mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-07-12 18:43:45 +00:00
refactor(*): Implement CSV-based seeding, dynamic box placement, and enforce forms from pokedex configuration
This commit is contained in:
@@ -60,17 +60,6 @@
|
||||
{/if}
|
||||
|
||||
<div class="bg-white text-black rounded-lg p-4">
|
||||
{#if showForms}
|
||||
<p><strong>Box:</strong> {pokedexEntry.boxPlacementForms.box}</p>
|
||||
<p><strong>Row:</strong> {pokedexEntry.boxPlacementForms.row}</p>
|
||||
<p><strong>Column:</strong> {pokedexEntry.boxPlacementForms.column}</p>
|
||||
<p><strong>Can Gigantamax:</strong> {pokedexEntry.canGigantamax ? 'Yes' : 'No'}</p>
|
||||
{:else}
|
||||
<p><strong>Box:</strong> {pokedexEntry.boxPlacement.box}</p>
|
||||
<p><strong>Row:</strong> {pokedexEntry.boxPlacement.row}</p>
|
||||
<p><strong>Column:</strong> {pokedexEntry.boxPlacement.column}</p>
|
||||
{/if}
|
||||
|
||||
{#if pokedexEntry.evolutionInformation}
|
||||
<p><strong>How to evolve: </strong>{pokedexEntry.evolutionInformation}</p>
|
||||
{:else}
|
||||
|
||||
@@ -7,12 +7,10 @@
|
||||
export let currentPage = 1;
|
||||
export let itemsPerPage = 20;
|
||||
export let totalPages = 0;
|
||||
export let showForms = true;
|
||||
export let showOrigins = true;
|
||||
export let showShiny = false;
|
||||
export let catchRegion = '';
|
||||
export let catchGame = '';
|
||||
export let toggleForms = () => {};
|
||||
export let toggleOrigins = () => {};
|
||||
export let toggleShiny = () => {};
|
||||
export let getData = () => {};
|
||||
@@ -57,11 +55,6 @@
|
||||
</ul>
|
||||
<h2 class="text-2xl font-semibold mb-4">Filters</h2>
|
||||
<ul>
|
||||
<li class="mb-2">
|
||||
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleForms}>
|
||||
Toggle Forms (Currently {showForms ? 'On' : 'Off'})
|
||||
</button>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<button class="block p-2 hover:bg-gray-700 rounded" on:click={toggleOrigins}>
|
||||
Toggle Origins (Currently {showOrigins ? 'On' : 'Off'})
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
export let showForms = true;
|
||||
export let combinedData: CombinedData[] | null;
|
||||
export let boxNumbers: number[] = [];
|
||||
export let currentPlacement = 'boxPlacementForms';
|
||||
export let creatingRecords = false;
|
||||
export let totalRecordsCreated = 0;
|
||||
export let failedToLoad = false;
|
||||
|
||||
@@ -43,12 +43,6 @@ export interface PokedexEntryDB {
|
||||
regionToEvolveIn: string | null;
|
||||
evolutionInformation: string | null;
|
||||
catchInformation: string[] | null;
|
||||
boxPlacementFormsBox: number | null;
|
||||
boxPlacementFormsRow: number | null;
|
||||
boxPlacementFormsColumn: number | null;
|
||||
boxPlacementBox: number | null;
|
||||
boxPlacementRow: number | null;
|
||||
boxPlacementColumn: number | null;
|
||||
// Regional dex numbers - snake_case to match database
|
||||
kanto_dex_number: number | null;
|
||||
johto_dex_number: number | null;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { type CatchRecord, type CatchRecordDB } from '$lib/models/CatchRecord';
|
||||
import { type CombinedData } from '$lib/models/CombinedData';
|
||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { getRegionalDexColumnName } from '$lib/utils/regionalDexMapping';
|
||||
import { calculateBoxPlacement } from '$lib/utils/boxPlacement';
|
||||
|
||||
class CombinedDataRepository {
|
||||
constructor(
|
||||
@@ -24,16 +25,9 @@ class CombinedDataRepository {
|
||||
regionToEvolveIn: entry.regionToEvolveIn || '',
|
||||
evolutionInformation: entry.evolutionInformation || '',
|
||||
catchInformation: entry.catchInformation || [],
|
||||
boxPlacementForms: {
|
||||
box: entry.boxPlacementFormsBox || 0,
|
||||
row: entry.boxPlacementFormsRow || 0,
|
||||
column: entry.boxPlacementFormsColumn || 0
|
||||
},
|
||||
boxPlacement: {
|
||||
box: entry.boxPlacementBox || 0,
|
||||
row: entry.boxPlacementRow || 0,
|
||||
column: entry.boxPlacementColumn || 0
|
||||
},
|
||||
// Box placement will be calculated dynamically after sorting
|
||||
boxPlacementForms: { box: 0, row: 0, column: 0 },
|
||||
boxPlacement: { box: 0, row: 0, column: 0 },
|
||||
kantoDexNumber: entry.kanto_dex_number ?? undefined,
|
||||
johtoDexNumber: entry.johto_dex_number ?? undefined,
|
||||
hoennDexNumber: entry.hoenn_dex_number ?? undefined,
|
||||
@@ -77,7 +71,8 @@ class CombinedDataRepository {
|
||||
|
||||
// Apply filters
|
||||
if (!enableForms) {
|
||||
query = query.not('boxPlacementBox', 'is', null);
|
||||
// Filter to only base forms (form is NULL)
|
||||
query = query.is('form', null);
|
||||
}
|
||||
|
||||
if (region) {
|
||||
@@ -101,18 +96,8 @@ class CombinedDataRepository {
|
||||
query = query.order('pokedexNumber', { ascending: true });
|
||||
}
|
||||
} else {
|
||||
// No game filter - use pre-calculated box placement (national dex order)
|
||||
if (enableForms) {
|
||||
query = query
|
||||
.order('boxPlacementFormsBox', { ascending: true })
|
||||
.order('boxPlacementFormsRow', { ascending: true })
|
||||
.order('boxPlacementFormsColumn', { ascending: true });
|
||||
} else {
|
||||
query = query
|
||||
.order('boxPlacementBox', { ascending: true })
|
||||
.order('boxPlacementRow', { ascending: true })
|
||||
.order('boxPlacementColumn', { ascending: true });
|
||||
}
|
||||
// No game filter - order by national dex number
|
||||
query = query.order('pokedexNumber', { ascending: true });
|
||||
}
|
||||
|
||||
const { data: entries, error: entriesError } = await query;
|
||||
@@ -158,11 +143,8 @@ class CombinedDataRepository {
|
||||
};
|
||||
});
|
||||
|
||||
// Recalculate box placement if using regional dex ordering
|
||||
if (game && getRegionalDexColumnName(game)) {
|
||||
return this.recalculateBoxPlacement(combinedData, enableForms);
|
||||
}
|
||||
return combinedData;
|
||||
// Always recalculate box placement based on current sort order
|
||||
return this.recalculateBoxPlacement(combinedData, enableForms);
|
||||
}
|
||||
|
||||
async findCombinedData(
|
||||
@@ -180,7 +162,8 @@ class CombinedDataRepository {
|
||||
|
||||
// Apply filters
|
||||
if (!enableForms) {
|
||||
query = query.not('boxPlacementBox', 'is', null);
|
||||
// Filter to only base forms (form is NULL)
|
||||
query = query.is('form', null);
|
||||
}
|
||||
|
||||
if (region) {
|
||||
@@ -204,18 +187,8 @@ class CombinedDataRepository {
|
||||
query = query.order('pokedexNumber', { ascending: true });
|
||||
}
|
||||
} else {
|
||||
// No game filter - use pre-calculated box placement (national dex order)
|
||||
if (enableForms) {
|
||||
query = query
|
||||
.order('boxPlacementFormsBox', { ascending: true })
|
||||
.order('boxPlacementFormsRow', { ascending: true })
|
||||
.order('boxPlacementFormsColumn', { ascending: true });
|
||||
} else {
|
||||
query = query
|
||||
.order('boxPlacementBox', { ascending: true })
|
||||
.order('boxPlacementRow', { ascending: true })
|
||||
.order('boxPlacementColumn', { ascending: true });
|
||||
}
|
||||
// No game filter - order by national dex number
|
||||
query = query.order('pokedexNumber', { ascending: true });
|
||||
}
|
||||
|
||||
const { data: entries, error: entriesError } = await query;
|
||||
@@ -261,11 +234,8 @@ class CombinedDataRepository {
|
||||
};
|
||||
});
|
||||
|
||||
// Recalculate box placement if using regional dex ordering
|
||||
if (game && getRegionalDexColumnName(game)) {
|
||||
return this.recalculateBoxPlacement(combinedData, enableForms);
|
||||
}
|
||||
return combinedData;
|
||||
// Always recalculate box placement based on current sort order
|
||||
return this.recalculateBoxPlacement(combinedData, enableForms);
|
||||
}
|
||||
|
||||
async countCombinedData(
|
||||
@@ -277,7 +247,8 @@ class CombinedDataRepository {
|
||||
|
||||
// Apply same filters as in findCombinedData
|
||||
if (!enableForms) {
|
||||
query = query.not('boxPlacementBox', 'is', null);
|
||||
// Filter to only base forms (form is NULL)
|
||||
query = query.is('form', null);
|
||||
}
|
||||
|
||||
if (region) {
|
||||
|
||||
@@ -17,16 +17,9 @@ class PokedexEntryRepository {
|
||||
regionToEvolveIn: entry.regionToEvolveIn || '',
|
||||
evolutionInformation: entry.evolutionInformation || '',
|
||||
catchInformation: entry.catchInformation || [],
|
||||
boxPlacementForms: {
|
||||
box: entry.boxPlacementFormsBox || 0,
|
||||
row: entry.boxPlacementFormsRow || 0,
|
||||
column: entry.boxPlacementFormsColumn || 0
|
||||
},
|
||||
boxPlacement: {
|
||||
box: entry.boxPlacementBox || 0,
|
||||
row: entry.boxPlacementRow || 0,
|
||||
column: entry.boxPlacementColumn || 0
|
||||
},
|
||||
// Box placement is calculated dynamically - not stored in database
|
||||
boxPlacementForms: { box: 0, row: 0, column: 0 },
|
||||
boxPlacement: { box: 0, row: 0, column: 0 },
|
||||
kantoDexNumber: entry.kanto_dex_number ?? undefined,
|
||||
johtoDexNumber: entry.johto_dex_number ?? undefined,
|
||||
hoennDexNumber: entry.hoenn_dex_number ?? undefined,
|
||||
@@ -80,17 +73,7 @@ class PokedexEntryRepository {
|
||||
if (data.evolutionInformation !== undefined)
|
||||
dbData.evolutionInformation = data.evolutionInformation;
|
||||
if (data.catchInformation !== undefined) dbData.catchInformation = data.catchInformation;
|
||||
if (data.boxPlacementForms?.box !== undefined)
|
||||
dbData.boxPlacementFormsBox = data.boxPlacementForms.box;
|
||||
if (data.boxPlacementForms?.row !== undefined)
|
||||
dbData.boxPlacementFormsRow = data.boxPlacementForms.row;
|
||||
if (data.boxPlacementForms?.column !== undefined)
|
||||
dbData.boxPlacementFormsColumn = data.boxPlacementForms.column;
|
||||
if (data.boxPlacement?.box !== undefined) dbData.boxPlacementBox = data.boxPlacement.box;
|
||||
if (data.boxPlacement?.row !== undefined) dbData.boxPlacementRow = data.boxPlacement.row;
|
||||
if (data.boxPlacement?.column !== undefined)
|
||||
dbData.boxPlacementColumn = data.boxPlacement.column;
|
||||
if (data.regionalDexNumbers !== undefined) dbData.regionalDexNumbers = data.regionalDexNumbers;
|
||||
// Box placement is calculated dynamically - not stored in database
|
||||
|
||||
const { data: result, error } = await this.supabase
|
||||
.from('pokedex_entries')
|
||||
@@ -115,17 +98,7 @@ class PokedexEntryRepository {
|
||||
if (data.evolutionInformation !== undefined)
|
||||
dbData.evolutionInformation = data.evolutionInformation;
|
||||
if (data.catchInformation !== undefined) dbData.catchInformation = data.catchInformation;
|
||||
if (data.boxPlacementForms?.box !== undefined)
|
||||
dbData.boxPlacementFormsBox = data.boxPlacementForms.box;
|
||||
if (data.boxPlacementForms?.row !== undefined)
|
||||
dbData.boxPlacementFormsRow = data.boxPlacementForms.row;
|
||||
if (data.boxPlacementForms?.column !== undefined)
|
||||
dbData.boxPlacementFormsColumn = data.boxPlacementForms.column;
|
||||
if (data.boxPlacement?.box !== undefined) dbData.boxPlacementBox = data.boxPlacement.box;
|
||||
if (data.boxPlacement?.row !== undefined) dbData.boxPlacementRow = data.boxPlacement.row;
|
||||
if (data.boxPlacement?.column !== undefined)
|
||||
dbData.boxPlacementColumn = data.boxPlacement.column;
|
||||
if (data.regionalDexNumbers !== undefined) dbData.regionalDexNumbers = data.regionalDexNumbers;
|
||||
// Box placement is calculated dynamically - not stored in database
|
||||
|
||||
const { data: result, error } = await this.supabase
|
||||
.from('pokedex_entries')
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Calculate box placement for Pokémon based on their position in a sorted list
|
||||
*
|
||||
* Box layout: 6 columns × 5 rows = 30 Pokémon per box
|
||||
*
|
||||
* @param index - Zero-based index in the sorted list
|
||||
* @returns Box placement with box number, row, and column (1-indexed)
|
||||
*/
|
||||
export function calculateBoxPlacement(index: number): {
|
||||
box: number;
|
||||
row: number;
|
||||
column: number;
|
||||
} {
|
||||
const POKEMON_PER_BOX = 30;
|
||||
const COLUMNS_PER_BOX = 6;
|
||||
const ROWS_PER_BOX = 5;
|
||||
|
||||
// Calculate which box this Pokémon belongs to (1-indexed)
|
||||
const box = Math.floor(index / POKEMON_PER_BOX) + 1;
|
||||
|
||||
// Calculate position within the box (0-indexed)
|
||||
const positionInBox = index % POKEMON_PER_BOX;
|
||||
|
||||
// Calculate row and column (1-indexed)
|
||||
const row = Math.floor(positionInBox / COLUMNS_PER_BOX) + 1;
|
||||
const column = (positionInBox % COLUMNS_PER_BOX) + 1;
|
||||
|
||||
return { box, row, column };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate all unique box numbers needed for a list of Pokémon
|
||||
*
|
||||
* @param count - Total number of Pokémon
|
||||
* @returns Array of box numbers (1-indexed)
|
||||
*/
|
||||
export function calculateBoxNumbers(count: number): number[] {
|
||||
const POKEMON_PER_BOX = 30;
|
||||
const totalBoxes = Math.ceil(count / POKEMON_PER_BOX);
|
||||
return Array.from({ length: totalBoxes }, (_, i) => i + 1);
|
||||
}
|
||||
@@ -27,11 +27,9 @@
|
||||
let catchGame = '';
|
||||
let localUser: User | null;
|
||||
let showOrigins = true;
|
||||
let showForms = true;
|
||||
let showShiny = false;
|
||||
let drawerOpen = false;
|
||||
let viewAsBoxes = false;
|
||||
let currentPlacement = 'boxPlacementForms';
|
||||
let boxNumbers: number[] = [];
|
||||
|
||||
const unsubscribe = user.subscribe((value) => {
|
||||
@@ -43,17 +41,8 @@
|
||||
showOrigins = !showOrigins;
|
||||
}
|
||||
|
||||
async function toggleForms() {
|
||||
showForms = !showForms;
|
||||
await getData();
|
||||
}
|
||||
|
||||
async function toggleShiny() {
|
||||
showShiny = !showShiny;
|
||||
if (showShiny) {
|
||||
showForms = false;
|
||||
}
|
||||
|
||||
await getData();
|
||||
}
|
||||
|
||||
@@ -67,7 +56,7 @@
|
||||
combinedData = null;
|
||||
}
|
||||
// Use new pokédex-scoped endpoint
|
||||
let endpoint = `/api/pokedexes/${pokedexId}/combined-data?page=${currentPage}&limit=${viewAsBoxes ? 9999 : itemsPerPage}&enableForms=${showForms}®ion=${catchRegion}&game=${catchGame}`;
|
||||
let endpoint = `/api/pokedexes/${pokedexId}/combined-data?page=${currentPage}&limit=${viewAsBoxes ? 9999 : itemsPerPage}&enableForms=${pokedex.isFormDex}®ion=${catchRegion}&game=${catchGame}`;
|
||||
|
||||
const response = await fetch(endpoint);
|
||||
const fetchedData = await response.json();
|
||||
@@ -81,7 +70,7 @@
|
||||
boxNumbers = [
|
||||
...new Set(
|
||||
combinedData.map(({ pokedexEntry }) =>
|
||||
showForms ? pokedexEntry.boxPlacementForms.box : pokedexEntry.boxPlacement.box
|
||||
pokedex.isFormDex ? pokedexEntry.boxPlacementForms.box : pokedexEntry.boxPlacement.box
|
||||
)
|
||||
)
|
||||
].filter(Boolean);
|
||||
@@ -129,7 +118,7 @@
|
||||
) {
|
||||
let catchRecordsToUpdate = combinedData
|
||||
.filter(({ pokedexEntry }) =>
|
||||
(showForms ? pokedexEntry.boxPlacementForms.box : pokedexEntry.boxPlacement.box) ===
|
||||
(pokedex.isFormDex ? pokedexEntry.boxPlacementForms.box : pokedexEntry.boxPlacement.box) ===
|
||||
boxNumber
|
||||
)
|
||||
.map(({ pokedexEntry, catchRecord }) => {
|
||||
@@ -262,8 +251,6 @@
|
||||
currentPage, itemsPerPage, getData();
|
||||
}
|
||||
}
|
||||
|
||||
$: currentPlacement = showForms ? 'boxPlacementForms' : 'boxPlacement';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -286,10 +273,9 @@
|
||||
{#if viewAsBoxes}
|
||||
<PokedexViewBoxes
|
||||
bind:showShiny
|
||||
bind:showForms
|
||||
showForms={pokedex.isFormDex}
|
||||
bind:combinedData
|
||||
bind:boxNumbers
|
||||
bind:currentPlacement
|
||||
bind:creatingRecords
|
||||
bind:failedToLoad
|
||||
{markBoxAsNotCaught}
|
||||
@@ -301,7 +287,7 @@
|
||||
{:else}
|
||||
<PokedexViewList
|
||||
bind:showOrigins
|
||||
bind:showForms
|
||||
showForms={pokedex.isFormDex}
|
||||
bind:showShiny
|
||||
bind:combinedData
|
||||
bind:creatingRecords
|
||||
@@ -322,13 +308,11 @@
|
||||
bind:currentPage
|
||||
bind:itemsPerPage
|
||||
bind:totalPages
|
||||
bind:showForms
|
||||
bind:showOrigins
|
||||
bind:showShiny
|
||||
bind:catchRegion
|
||||
bind:catchGame
|
||||
{getData}
|
||||
{toggleForms}
|
||||
{toggleOrigins}
|
||||
{toggleShiny}
|
||||
{toggleViewAsBoxes}
|
||||
|
||||
Reference in New Issue
Block a user