mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-07-13 11:03:44 +00:00
feat(#67): Add support for multiple pokedexes per user
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import type { Pokedex } from '$lib/models/Pokedex';
|
||||
|
||||
export let pokedex: Pokedex;
|
||||
export let onEdit: () => void;
|
||||
export let onDelete: () => void;
|
||||
export let onView: () => void;
|
||||
|
||||
// Get active type badges
|
||||
$: typeBadges = [
|
||||
pokedex.isLivingDex && 'Living',
|
||||
pokedex.isShinyDex && 'Shiny',
|
||||
pokedex.isOriginDex && 'Origin',
|
||||
pokedex.isFormDex && 'Form'
|
||||
].filter(Boolean);
|
||||
</script>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
{pokedex.name}
|
||||
</h2>
|
||||
|
||||
{#if pokedex.description}
|
||||
<p class="text-sm">{pokedex.description}</p>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-wrap gap-2 my-2">
|
||||
{#each typeBadges as badge}
|
||||
<span class="badge badge-primary">{badge}</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if pokedex.gameScope}
|
||||
<div class="text-sm text-base-content/70">
|
||||
<span class="font-semibold">Game:</span>
|
||||
{pokedex.gameScope}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-sm text-base-content/70">
|
||||
<span class="font-semibold">Scope:</span> All Games
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="card-actions justify-end mt-4">
|
||||
<button class="btn btn-sm btn-ghost" on:click={onEdit}>Edit</button>
|
||||
<button class="btn btn-sm btn-error btn-outline" on:click={onDelete}>Delete</button>
|
||||
<button class="btn btn-sm btn-primary" on:click={onView}>View</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -10,6 +10,7 @@
|
||||
export let showForms: boolean;
|
||||
export let showShiny: boolean;
|
||||
export let userId: string | null = null;
|
||||
export let pokedexId: string;
|
||||
|
||||
// Create a default catch record if none exists
|
||||
$: if (!catchRecord) {
|
||||
@@ -17,6 +18,7 @@
|
||||
_id: '', // Empty string, not temp ID - will be created by server
|
||||
userId: userId || '',
|
||||
pokedexEntryId: pokedexEntry._id,
|
||||
pokedexId: pokedexId,
|
||||
haveToEvolve: false,
|
||||
caught: false,
|
||||
inHome: false,
|
||||
@@ -83,74 +85,76 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dex-column catch-record-container bg-white text-black rounded-lg p-4 mb-4 md:mb-0">
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">Caught:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.caught}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">Needs to evolve:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.haveToEvolve}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">In home:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.inHome}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{#if pokedexEntry.canGigantamax && showForms}
|
||||
{#if catchRecord}
|
||||
<div class="dex-column catch-record-container bg-white text-black rounded-lg p-4 mb-4 md:mb-0">
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">Has Gigantamaxed:</span>
|
||||
<span class="block font-bold mr-2">Caught:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.hasGigantamaxed}
|
||||
bind:checked={catchRecord.caught}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<p>
|
||||
<label
|
||||
class="block font-bold mb-1"
|
||||
for={`personalNotesInput-${catchRecord?._id || pokedexEntry._id}`}>Notes:</label
|
||||
>
|
||||
<textarea
|
||||
bind:value={catchRecord.personalNotes}
|
||||
id={`personalNotesInput-${catchRecord?._id || pokedexEntry._id}`}
|
||||
class="form-textarea w-full p-2 border rounded"
|
||||
on:change={updateCatchRecord}
|
||||
></textarea>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">Needs to evolve:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.haveToEvolve}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">In home:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.inHome}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{#if pokedexEntry.canGigantamax && showForms}
|
||||
<div class="flex items-center">
|
||||
<div class="form-control">
|
||||
<label class="cursor-pointer label">
|
||||
<span class="block font-bold mr-2">Has Gigantamaxed:</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={catchRecord.hasGigantamaxed}
|
||||
class="checkbox checkbox-primary border-black"
|
||||
on:change={updateCatchRecord}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<p>
|
||||
<label
|
||||
class="block font-bold mb-1"
|
||||
for={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}>Notes:</label
|
||||
>
|
||||
<textarea
|
||||
bind:value={catchRecord.personalNotes}
|
||||
id={`personalNotesInput-${catchRecord._id || pokedexEntry._id}`}
|
||||
class="form-textarea w-full p-2 border rounded"
|
||||
on:change={updateCatchRecord}
|
||||
></textarea>
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="dex-column additional-details-container">
|
||||
{#if showOrigins}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
import type { Pokedex } from '$lib/models/Pokedex';
|
||||
|
||||
export let pokedex: Partial<Pokedex> = {
|
||||
name: '',
|
||||
description: '',
|
||||
isLivingDex: false,
|
||||
isShinyDex: false,
|
||||
isOriginDex: false,
|
||||
isFormDex: false,
|
||||
gameScope: null
|
||||
};
|
||||
export let mode: 'create' | 'edit' = 'create';
|
||||
export let onSubmit: () => void;
|
||||
export let onCancel: () => void;
|
||||
|
||||
// Validation
|
||||
$: hasAtLeastOneType =
|
||||
pokedex.isLivingDex || pokedex.isShinyDex || pokedex.isOriginDex || pokedex.isFormDex;
|
||||
$: canSubmit = pokedex.name && pokedex.name.trim() !== '' && hasAtLeastOneType;
|
||||
|
||||
function handleSubmit() {
|
||||
if (canSubmit) {
|
||||
onSubmit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="pokedex-name">
|
||||
<span class="label-text">Name</span>
|
||||
</label>
|
||||
<input
|
||||
id="pokedex-name"
|
||||
type="text"
|
||||
placeholder="My Living Dex"
|
||||
class="input input-bordered w-full"
|
||||
bind:value={pokedex.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="pokedex-description">
|
||||
<span class="label-text">Description (optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="pokedex-description"
|
||||
class="textarea textarea-bordered h-24"
|
||||
placeholder="Describe your pokédex..."
|
||||
bind:value={pokedex.description}
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<label class="label">
|
||||
<span class="label-text">Type(s)</span>
|
||||
<span class="label-text-alt text-error"
|
||||
>{!hasAtLeastOneType ? 'At least one type required' : ''}</span
|
||||
>
|
||||
</label>
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isLivingDex} />
|
||||
<span class="label-text">Living Dex (one of each Pokémon)</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isShinyDex} />
|
||||
<span class="label-text">Shiny Dex (shiny variants only)</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isOriginDex} />
|
||||
<span class="label-text">Origin Dex (caught in native regions)</span>
|
||||
</label>
|
||||
<label class="label cursor-pointer justify-start gap-3">
|
||||
<input type="checkbox" class="checkbox" bind:checked={pokedex.isFormDex} />
|
||||
<span class="label-text">Form Dex (all forms included)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="game-scope">
|
||||
<span class="label-text">Game Scope</span>
|
||||
</label>
|
||||
<select id="game-scope" class="select select-bordered" bind:value={pokedex.gameScope}>
|
||||
<option value={null}>All Games</option>
|
||||
<option value="Scarlet">Scarlet</option>
|
||||
<option value="Violet">Violet>
|
||||
<option value="Sword">Sword</option>
|
||||
<option value="Shield">Shield</option>
|
||||
<option value="Brilliant Diamond">Brilliant Diamond</option>
|
||||
<option value="Shining Pearl">Shining Pearl</option>
|
||||
<option value="Legends: Arceus">Legends: Arceus</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn btn-ghost" type="button" on:click={onCancel}>Cancel</button>
|
||||
<button class="btn btn-primary" type="button" on:click={handleSubmit} disabled={!canSubmit}>
|
||||
{mode === 'create' ? 'Create' : 'Save'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import Pagination from '$lib/components/Pagination.svelte';
|
||||
import type { Pokedex } from '$lib/models/Pokedex';
|
||||
|
||||
export let pokedex: Pokedex | null = null;
|
||||
export let viewAsBoxes = false;
|
||||
export let currentPage = 1;
|
||||
export let itemsPerPage = 20;
|
||||
@@ -15,11 +17,36 @@
|
||||
export let toggleShiny = () => {};
|
||||
export let getData = () => {};
|
||||
export let toggleViewAsBoxes = () => {};
|
||||
|
||||
// Get active type badges
|
||||
$: typeBadges = pokedex
|
||||
? [
|
||||
pokedex.isLivingDex && 'Living',
|
||||
pokedex.isShinyDex && 'Shiny',
|
||||
pokedex.isOriginDex && 'Origin',
|
||||
pokedex.isFormDex && 'Form'
|
||||
].filter(Boolean)
|
||||
: [];
|
||||
</script>
|
||||
|
||||
<aside
|
||||
class="menu bg-gray-800 text-white min-h-full w-64 p-4 lg:fixed xs:top-0 lg:left-0 h-full lg:h-5/6"
|
||||
>
|
||||
<!-- Pokédex Info Section -->
|
||||
{#if pokedex}
|
||||
<div class="mb-6 pb-4 border-b border-gray-700">
|
||||
<h2 class="text-xl font-semibold mb-2">{pokedex.name}</h2>
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
{#each typeBadges as badge}
|
||||
<span class="badge badge-sm badge-primary">{badge}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{#if pokedex.gameScope}
|
||||
<p class="text-sm text-gray-400">Game: {pokedex.gameScope}</p>
|
||||
{/if}
|
||||
<a href="/my-pokedexes" class="btn btn-ghost btn-sm mt-2 w-full"> Manage Pokédexes </a>
|
||||
</div>
|
||||
{/if}
|
||||
<h2 class="text-2xl font-semibold mb-4">Views</h2>
|
||||
<ul>
|
||||
<li class="mb-2">
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
|
||||
export let showShiny = false;
|
||||
export let showForms = true;
|
||||
export let combinedData: CombinedData[] | null;
|
||||
export let boxNumbers = Array<number>;
|
||||
export let boxNumbers: number[] = [];
|
||||
export let currentPlacement = 'boxPlacementForms';
|
||||
export let creatingRecords = false;
|
||||
export let totalRecordsCreated = 0;
|
||||
@@ -33,7 +34,8 @@
|
||||
if (catchRecord?.caught || catchRecord?.haveToEvolve) {
|
||||
return '';
|
||||
} else {
|
||||
if (pokedexEntry[currentPlacement].column % 2 === 0) {
|
||||
const placement = showForms ? pokedexEntry.boxPlacementForms : pokedexEntry.boxPlacement;
|
||||
if (placement.column % 2 === 0) {
|
||||
return 'background-color: #ffffff';
|
||||
} else {
|
||||
return 'background-color: #f9f9f9;';
|
||||
@@ -67,11 +69,13 @@
|
||||
>
|
||||
<div class="grid grid-cols-6">
|
||||
{#each combinedData as { pokedexEntry, catchRecord }}
|
||||
{#if pokedexEntry[currentPlacement].box === boxNumber}
|
||||
{@const placement = showForms
|
||||
? pokedexEntry.boxPlacementForms
|
||||
: pokedexEntry.boxPlacement}
|
||||
{#if placement.box === boxNumber}
|
||||
<div
|
||||
class="pokemon-box {cellBackgroundColourClass(catchRecord)}"
|
||||
style="grid-column-start: {pokedexEntry[currentPlacement]
|
||||
.column}; grid-row-start: {pokedexEntry[currentPlacement].row};
|
||||
style="grid-column-start: {placement.column}; grid-row-start: {placement.row};
|
||||
{cellBackgroundColourStyle(pokedexEntry, catchRecord)}"
|
||||
>
|
||||
<Tooltip>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
export let updateACatch = (event: any) => {};
|
||||
export let createCatchRecords = () => {};
|
||||
export let userId: string | null = null;
|
||||
export let pokedexId: string;
|
||||
</script>
|
||||
|
||||
<main class="flex-1 p-4 w-full">
|
||||
@@ -24,6 +25,7 @@
|
||||
{showForms}
|
||||
{showShiny}
|
||||
{userId}
|
||||
{pokedexId}
|
||||
bind:catchRecord
|
||||
on:updateCatch={updateACatch}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user