mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-09-15 01:52:39 +00:00
fix: repair defects surfaced by gating CI on lint and typecheck
`npm run check` reported 15 errors and `npm run lint` 20, all pre-existing, so
neither gate could pass. Fixing them turned up three real bugs:
- SignOut destructured `{ error }` off `.then(() => {})`, which resolves to
undefined, so every sign-out threw a TypeError - after the signed-out event had
already been emitted. Sign-out also left the user on the protected page they
were on, still showing its content; it now returns them to the home page and
re-runs the server loads.
- SignUp passed `redirectTo`, which is not a signUp option and was silently
ignored, so the confirmation link has always used Supabase's configured site
URL. Documented rather than changed, since pointing it elsewhere needs an
absolute allow-listed URL.
- The Pokédex page tracked totalRecordsCreated but never passed it to the box
view, so the "Processed N entries so far" progress message never rendered.
The rest is typing and dead code: cookie callback parameters in hooks.server.ts
and +layout.ts, the untyped supabase props, a query-builder type that made
PostgREST rows untyped downstream, an unused session destructure, and
`while (true)` paging loops rewritten as `for (;;)`.
This commit is contained in:
+4
-3
@@ -1,21 +1,22 @@
|
|||||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
|
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
|
||||||
import { createServerClient } from '@supabase/ssr';
|
import { createServerClient } from '@supabase/ssr';
|
||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
import type { CookieSerializeOptions } from 'cookie';
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
||||||
cookies: {
|
cookies: {
|
||||||
get: (key) => event.cookies.get(key),
|
get: (key: string) => event.cookies.get(key),
|
||||||
/**
|
/**
|
||||||
* Note: You have to add the `path` variable to the
|
* Note: You have to add the `path` variable to the
|
||||||
* set and remove method due to sveltekit's cookie API
|
* set and remove method due to sveltekit's cookie API
|
||||||
* requiring this to be set, setting the path to an empty string
|
* requiring this to be set, setting the path to an empty string
|
||||||
* will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
|
* will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
|
||||||
*/
|
*/
|
||||||
set: (key, value, options) => {
|
set: (key: string, value: string, options: CookieSerializeOptions) => {
|
||||||
event.cookies.set(key, value, { ...options, path: '/' });
|
event.cookies.set(key, value, { ...options, path: '/' });
|
||||||
},
|
},
|
||||||
remove: (key, options) => {
|
remove: (key: string, options: CookieSerializeOptions) => {
|
||||||
event.cookies.delete(key, { ...options, path: '/' });
|
event.cookies.delete(key, { ...options, path: '/' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
currentPage = Math.max(currentPage - 1, 1);
|
currentPage = Math.max(currentPage - 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setItemsPerPage(event: any) {
|
function setItemsPerPage(event: Event) {
|
||||||
itemsPerPage = parseInt(event.target.value, 10);
|
itemsPerPage = parseInt((event.target as HTMLSelectElement).value, 10);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
let errorMessage = '';
|
let errorMessage = '';
|
||||||
|
|
||||||
// Access the supabase client from the layout data
|
// Access the supabase client from the layout data
|
||||||
export let supabase: any;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
async function signInWithEmail() {
|
async function signInWithEmail() {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
function emitSignedOutEvent() {
|
function emitSignedOutEvent() {
|
||||||
@@ -7,13 +9,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Access the supabase client from the layout data
|
// Access the supabase client from the layout data
|
||||||
export let supabase: any;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
async function signOut() {
|
async function signOut() {
|
||||||
// TODO use the error from the response
|
// `.then(() => {...})` resolved to undefined, so destructuring `error` off it threw a
|
||||||
const { error } = await supabase.auth.signOut().then(() => {
|
// TypeError on every sign-out - after the event had already been emitted.
|
||||||
emitSignedOutEvent();
|
const { error } = await supabase.auth.signOut();
|
||||||
});
|
if (error) console.error('Sign out failed', error);
|
||||||
|
emitSignedOutEvent();
|
||||||
|
// Signing out used to leave the user sitting on the protected page they were on, still
|
||||||
|
// showing its content. Send them to the public home page and re-run the server loads.
|
||||||
|
await goto('/', { invalidateAll: true });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
@@ -6,17 +7,16 @@
|
|||||||
let password = '';
|
let password = '';
|
||||||
|
|
||||||
// Access the supabase client from the layout data
|
// Access the supabase client from the layout data
|
||||||
export let supabase: any;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
async function signUpNewUser() {
|
async function signUpNewUser() {
|
||||||
try {
|
try {
|
||||||
|
// `redirectTo` is not a signUp option - it was silently ignored, so the confirmation
|
||||||
|
// link has always used Supabase's configured site URL. Sending the user to /welcome
|
||||||
|
// would need `emailRedirectTo` with an absolute, allow-listed URL.
|
||||||
const { data, error } = await supabase.auth.signUp({
|
const { data, error } = await supabase.auth.signUp({
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password
|
||||||
options: {
|
|
||||||
// Redirect URL after successful sign-up
|
|
||||||
redirectTo: '/welcome'
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
export let creatingRecords = false;
|
export let creatingRecords = false;
|
||||||
export let totalRecordsCreated = 0;
|
export let totalRecordsCreated = 0;
|
||||||
export let failedToLoad = false;
|
export let failedToLoad = false;
|
||||||
export let markBoxAsNotCaught = (boxNumber: number) => {};
|
export let markBoxAsNotCaught: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsCaught = (boxNumber: number) => {};
|
export let markBoxAsCaught: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsNeedsToEvolve = (boxNumber: number) => {};
|
export let markBoxAsNeedsToEvolve: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsInHome = (boxNumber: number) => {};
|
export let markBoxAsInHome: (boxNumber: number) => void = () => {};
|
||||||
export let markBoxAsNotInHome = (boxNumber: number) => {};
|
export let markBoxAsNotInHome: (boxNumber: number) => void = () => {};
|
||||||
export let createCatchRecords = () => {};
|
export let createCatchRecords = () => {};
|
||||||
export let onPokemonClick: (pokemon: CombinedData) => void = () => {};
|
export let onPokemonClick: (pokemon: CombinedData) => void = () => {};
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class CombinedDataRepository {
|
|||||||
let start = 0;
|
let start = 0;
|
||||||
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
||||||
|
|
||||||
while (true) {
|
for (;;) {
|
||||||
const end = start + maxRows - 1;
|
const end = start + maxRows - 1;
|
||||||
let query = this.supabase.from('pokedex_entries').select('*').not('form', 'is', null);
|
let query = this.supabase.from('pokedex_entries').select('*').not('form', 'is', null);
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ class CombinedDataRepository {
|
|||||||
let start = 0;
|
let start = 0;
|
||||||
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
||||||
|
|
||||||
while (true) {
|
for (;;) {
|
||||||
const end = start + maxRows - 1;
|
const end = start + maxRows - 1;
|
||||||
const { data, error } = await this.buildDexEntriesQuery(dexScopes, enableForms, region).range(
|
const { data, error } = await this.buildDexEntriesQuery(dexScopes, enableForms, region).range(
|
||||||
start,
|
start,
|
||||||
@@ -312,7 +312,7 @@ class CombinedDataRepository {
|
|||||||
let start = 0;
|
let start = 0;
|
||||||
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
const maxRows = CombinedDataRepository.MAX_ROWS_PER_REQUEST;
|
||||||
|
|
||||||
while (true) {
|
for (;;) {
|
||||||
const end = start + maxRows - 1;
|
const end = start + maxRows - 1;
|
||||||
const { data, error } = await this.buildEntriesQuery(enableForms, region, game).range(
|
const { data, error } = await this.buildEntriesQuery(enableForms, region, game).range(
|
||||||
start,
|
start,
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import type {
|
|||||||
PokedexExportIntegrationDB
|
PokedexExportIntegrationDB
|
||||||
} from '$lib/models/PokedexExportIntegration';
|
} from '$lib/models/PokedexExportIntegration';
|
||||||
|
|
||||||
|
// `from()` returns a table builder; only `select()` yields the filter builder that `eq`/`is`
|
||||||
|
// live on. Typing the scope helper with the table builder made `data` untyped downstream.
|
||||||
|
type IntegrationQuery = ReturnType<ReturnType<SupabaseClient['from']>['select']>;
|
||||||
|
|
||||||
class PokedexExportIntegrationRepository {
|
class PokedexExportIntegrationRepository {
|
||||||
constructor(
|
constructor(
|
||||||
private supabase: SupabaseClient,
|
private supabase: SupabaseClient,
|
||||||
@@ -34,7 +38,7 @@ class PokedexExportIntegrationRepository {
|
|||||||
return this.supabase.from('pokedex_export_integrations').select('*').eq('userId', this.userId);
|
return this.supabase.from('pokedex_export_integrations').select('*').eq('userId', this.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addPokedexScope(query: ReturnType<SupabaseClient['from']>) {
|
private addPokedexScope(query: IntegrationQuery): IntegrationQuery {
|
||||||
if (this.pokedexId) {
|
if (this.pokedexId) {
|
||||||
return query.eq('pokedexId', this.pokedexId);
|
return query.eq('pokedexId', this.pokedexId);
|
||||||
}
|
}
|
||||||
@@ -49,7 +53,8 @@ class PokedexExportIntegrationRepository {
|
|||||||
throw new Error(`Failed to load export integrations: ${error.message}`);
|
throw new Error(`Failed to load export integrations: ${error.message}`);
|
||||||
}
|
}
|
||||||
if (!data) return [];
|
if (!data) return [];
|
||||||
return data.map((row) => this.transform(row));
|
// PostgREST rows are untyped without generated database types.
|
||||||
|
return (data as PokedexExportIntegrationDB[]).map((row) => this.transform(row));
|
||||||
}
|
}
|
||||||
|
|
||||||
async listAll(): Promise<PokedexExportIntegration[]> {
|
async listAll(): Promise<PokedexExportIntegration[]> {
|
||||||
@@ -60,12 +65,12 @@ class PokedexExportIntegrationRepository {
|
|||||||
throw new Error(`Failed to load export integrations: ${error.message}`);
|
throw new Error(`Failed to load export integrations: ${error.message}`);
|
||||||
}
|
}
|
||||||
if (!data) return [];
|
if (!data) return [];
|
||||||
return data.map((row) => this.transform(row));
|
// PostgREST rows are untyped without generated database types.
|
||||||
|
return (data as PokedexExportIntegrationDB[]).map((row) => this.transform(row));
|
||||||
}
|
}
|
||||||
|
|
||||||
async upsert(
|
async upsert(
|
||||||
data: Partial<PokedexExportIntegrationDB> &
|
data: Partial<PokedexExportIntegrationDB> & Pick<PokedexExportIntegrationDB, 'provider'>
|
||||||
Pick<PokedexExportIntegrationDB, 'provider'>
|
|
||||||
): Promise<PokedexExportIntegration> {
|
): Promise<PokedexExportIntegration> {
|
||||||
const payload: Partial<PokedexExportIntegrationDB> = {
|
const payload: Partial<PokedexExportIntegrationDB> = {
|
||||||
userId: this.userId,
|
userId: this.userId,
|
||||||
@@ -105,7 +110,8 @@ class PokedexExportIntegrationRepository {
|
|||||||
throw new Error(`Failed to load export integrations: ${error.message}`);
|
throw new Error(`Failed to load export integrations: ${error.message}`);
|
||||||
}
|
}
|
||||||
if (!data) return [];
|
if (!data) return [];
|
||||||
return data.map((row) => this.transform(row));
|
// PostgREST rows are untyped without generated database types.
|
||||||
|
return (data as PokedexExportIntegrationDB[]).map((row) => this.transform(row));
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTokens(
|
async updateTokens(
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import { error } from '@sveltejs/kit';
|
|||||||
*/
|
*/
|
||||||
export async function requireAuth(event: RequestEvent): Promise<string> {
|
export async function requireAuth(event: RequestEvent): Promise<string> {
|
||||||
const { session, user } = await event.locals.safeGetSession();
|
const { session, user } = await event.locals.safeGetSession();
|
||||||
|
|
||||||
if (!session || !user) {
|
if (!session || !user) {
|
||||||
throw error(401, 'Authentication required');
|
throw error(401, 'Authentication required');
|
||||||
}
|
}
|
||||||
|
|
||||||
return user.id;
|
return user.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,9 +21,9 @@ export async function requireAuth(event: RequestEvent): Promise<string> {
|
|||||||
*/
|
*/
|
||||||
export async function getOptionalUserId(event: RequestEvent): Promise<string | null> {
|
export async function getOptionalUserId(event: RequestEvent): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
const { session, user } = await event.locals.safeGetSession();
|
const { user } = await event.locals.safeGetSession();
|
||||||
return user?.id || null;
|
return user?.id || null;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export function calculateBoxPlacement(index: number): {
|
|||||||
} {
|
} {
|
||||||
const POKEMON_PER_BOX = 30;
|
const POKEMON_PER_BOX = 30;
|
||||||
const COLUMNS_PER_BOX = 6;
|
const COLUMNS_PER_BOX = 6;
|
||||||
const ROWS_PER_BOX = 5;
|
|
||||||
|
|
||||||
// Calculate which box this Pokémon belongs to (1-indexed)
|
// Calculate which box this Pokémon belongs to (1-indexed)
|
||||||
const box = Math.floor(index / POKEMON_PER_BOX) + 1;
|
const box = Math.floor(index / POKEMON_PER_BOX) + 1;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const load: LayoutLoad = async ({ fetch, data, depends }) => {
|
|||||||
fetch
|
fetch
|
||||||
},
|
},
|
||||||
cookies: {
|
cookies: {
|
||||||
get(key) {
|
get(key: string) {
|
||||||
if (!isBrowser()) {
|
if (!isBrowser()) {
|
||||||
return JSON.stringify(data.session);
|
return JSON.stringify(data.session);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { user } from '$lib/stores/user.js';
|
|
||||||
import { type User } from '@supabase/auth-js';
|
|
||||||
import SignUp from '$lib/components/SignUp.svelte';
|
import SignUp from '$lib/components/SignUp.svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
@@ -9,12 +7,7 @@
|
|||||||
let { supabase, stats } = data;
|
let { supabase, stats } = data;
|
||||||
$: ({ supabase, stats } = data);
|
$: ({ supabase, stats } = data);
|
||||||
|
|
||||||
let localUser: User | null;
|
// Redirection is decided from the live session below, so the user store is not needed here.
|
||||||
const unsubscribe = user.subscribe((value) => {
|
|
||||||
localUser = value;
|
|
||||||
});
|
|
||||||
onDestroy(unsubscribe);
|
|
||||||
|
|
||||||
let isCheckingSession = true;
|
let isCheckingSession = true;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
import type { Pokedex } from '$lib/models/Pokedex';
|
import type { Pokedex } from '$lib/models/Pokedex';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
// Get pokédex from server load (guarded for transient undefined during navigation/HMR)
|
// Get pokédex from server load (guarded for transient undefined during navigation/HMR)
|
||||||
@@ -38,7 +37,10 @@
|
|||||||
// Box view requires the full dataset for correct box numbering/placement.
|
// Box view requires the full dataset for correct box numbering/placement.
|
||||||
// If/when a paginated list view is introduced, this can be lowered and paired with UI controls.
|
// If/when a paginated list view is introduced, this can be lowered and paired with UI controls.
|
||||||
let itemsPerPage = 9999 as number;
|
let itemsPerPage = 9999 as number;
|
||||||
let totalPages = 0 as number;
|
type CatchUpdateEvent = CustomEvent<{
|
||||||
|
catchRecord: CatchRecord;
|
||||||
|
source: 'toggle' | 'notes' | 'notes-blur';
|
||||||
|
}>;
|
||||||
let creatingRecords = false;
|
let creatingRecords = false;
|
||||||
let totalRecordsCreated = 0;
|
let totalRecordsCreated = 0;
|
||||||
let failedToLoad = false;
|
let failedToLoad = false;
|
||||||
@@ -128,7 +130,6 @@
|
|||||||
}, 250);
|
}, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Derive from pokedex config
|
// Derive from pokedex config
|
||||||
$: showOrigins = !!pokedex?.isOriginDex;
|
$: showOrigins = !!pokedex?.isOriginDex;
|
||||||
$: showShiny = !!pokedex?.isShinyDex;
|
$: showShiny = !!pokedex?.isShinyDex;
|
||||||
@@ -190,8 +191,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function applyOptimisticCatchRecordUpdate(next: CatchRecord) {
|
function applyOptimisticCatchRecordUpdate(next: CatchRecord) {
|
||||||
if (!combinedData) return;
|
if (!combinedData) return;
|
||||||
const idx = combinedData.findIndex((cd) => cd.pokedexEntry._id === next.pokemonId);
|
const idx = combinedData.findIndex((cd) => cd.pokedexEntry._id === next.pokemonId);
|
||||||
@@ -212,7 +211,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleModalCatchUpdate(event: any) {
|
async function handleModalCatchUpdate(event: CatchUpdateEvent) {
|
||||||
await updateACatch(event);
|
await updateACatch(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,20 +242,16 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
combinedData = fetchedData.combinedData;
|
combinedData = fetchedData.combinedData;
|
||||||
totalPages = fetchedData.totalPages || 0;
|
|
||||||
// Always extract box numbers for box view
|
// Always extract box numbers for box view
|
||||||
if (combinedData) {
|
if (combinedData) {
|
||||||
boxNumbers = calculateBoxNumbers(combinedData.length);
|
boxNumbers = calculateBoxNumbers(combinedData.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateACatch(event: any) {
|
async function updateACatch(event: CatchUpdateEvent) {
|
||||||
if (!pokedexId) return;
|
if (!pokedexId) return;
|
||||||
ensureCatchWriteQueue();
|
ensureCatchWriteQueue();
|
||||||
const { catchRecord, source } = event.detail as {
|
const { catchRecord, source } = event.detail;
|
||||||
catchRecord: CatchRecord;
|
|
||||||
source: 'toggle' | 'notes' | 'notes-blur';
|
|
||||||
};
|
|
||||||
// Enforce mutual exclusivity (should be impossible to have both true).
|
// Enforce mutual exclusivity (should be impossible to have both true).
|
||||||
const sanitizedCatchRecord: CatchRecord = { ...catchRecord };
|
const sanitizedCatchRecord: CatchRecord = { ...catchRecord };
|
||||||
if (sanitizedCatchRecord.caught) {
|
if (sanitizedCatchRecord.caught) {
|
||||||
@@ -464,7 +459,6 @@
|
|||||||
window.clearInterval(reconcileInterval);
|
window.clearInterval(reconcileInterval);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -599,7 +593,6 @@
|
|||||||
{#if pokedex.description}
|
{#if pokedex.description}
|
||||||
<p class="text-sm text-base-content/70 mt-3">{pokedex.description}</p>
|
<p class="text-sm text-base-content/70 mt-3">{pokedex.description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right side: Actions -->
|
<!-- Right side: Actions -->
|
||||||
@@ -637,6 +630,7 @@
|
|||||||
bind:combinedData
|
bind:combinedData
|
||||||
bind:boxNumbers
|
bind:boxNumbers
|
||||||
bind:creatingRecords
|
bind:creatingRecords
|
||||||
|
{totalRecordsCreated}
|
||||||
bind:failedToLoad
|
bind:failedToLoad
|
||||||
{markBoxAsNotCaught}
|
{markBoxAsNotCaught}
|
||||||
{markBoxAsCaught}
|
{markBoxAsCaught}
|
||||||
|
|||||||
Reference in New Issue
Block a user