From 2cf3a03f8fbc5f24407d04d47d060c41ac498cd0 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Fri, 12 Jul 2024 21:33:02 +0100 Subject: [PATCH] feat(*): Add pagination to my dex --- src/lib/components/Pagination.svelte | 29 ++++ .../repositories/CombinedDataRepository.ts | 45 +++++ src/routes/api/combined-data/+server.ts | 23 +++ src/routes/mydex/+page.svelte | 154 +++--------------- 4 files changed, 124 insertions(+), 127 deletions(-) create mode 100644 src/lib/components/Pagination.svelte create mode 100644 src/lib/repositories/CombinedDataRepository.ts create mode 100644 src/routes/api/combined-data/+server.ts diff --git a/src/lib/components/Pagination.svelte b/src/lib/components/Pagination.svelte new file mode 100644 index 0000000..d99ad5a --- /dev/null +++ b/src/lib/components/Pagination.svelte @@ -0,0 +1,29 @@ + + +
+ + + + + +
diff --git a/src/lib/repositories/CombinedDataRepository.ts b/src/lib/repositories/CombinedDataRepository.ts new file mode 100644 index 0000000..adc033f --- /dev/null +++ b/src/lib/repositories/CombinedDataRepository.ts @@ -0,0 +1,45 @@ +import PokedexEntryModel, { type PokedexEntry } from '$lib/models/PokedexEntry'; +import CatchRecordModel, { type CatchRecord } from '$lib/models/CatchRecord'; +import { CombinedData } from '$lib/models/CombinedData'; + +class CombinedDataRepository { + async findCombinedData(page: number = 1, limit: number = 20): Promise { + const skip = (page - 1) * limit; + const combinedData: CombinedData[] = await PokedexEntryModel.aggregate([ + { + $lookup: { + from: 'catchrecords', + localField: '_id', + foreignField: 'pokedexEntryId', + as: 'catchRecord' + } + }, + { + $unwind: { + path: '$catchRecord', + preserveNullAndEmptyArrays: true + } + }, + { + $sort: { + 'boxPlacement.box': 1, + 'boxPlacement.row': 1, + 'boxPlacement.column': 1 + } + }, + { $skip: skip }, + { $limit: limit } + ]).exec(); + + return combinedData.map((data) => ({ + pokedexEntry: data as PokedexEntry, + catchRecord: data.catchRecord as CatchRecord + })); + } + + async countCombinedData(): Promise { + return PokedexEntryModel.countDocuments().exec(); + } +} + +export default CombinedDataRepository; diff --git a/src/routes/api/combined-data/+server.ts b/src/routes/api/combined-data/+server.ts new file mode 100644 index 0000000..cc8ace3 --- /dev/null +++ b/src/routes/api/combined-data/+server.ts @@ -0,0 +1,23 @@ +import { json } from '@sveltejs/kit'; +import { dbConnect, dbDisconnect } from '$lib/utils/db'; +import CombinedDataRepository from '$lib/repositories/CombinedDataRepository'; + +export const GET = async ({ url }) => { + const page = parseInt(url.searchParams.get('page') || '1', 10); + const limit = parseInt(url.searchParams.get('limit') || '20', 10); + + try { + await dbConnect(); + const repo = new CombinedDataRepository(); + const combinedData = await repo.findCombinedData(page, limit); + const totalCount = await repo.countCombinedData(); + const totalPages = Math.ceil(totalCount / limit); + + return json({ combinedData, totalPages }); + } catch (error) { + console.error(error); + return json({ error: 'Internal Server Error' }, { status: 500 }); + } finally { + dbDisconnect(); + } +}; diff --git a/src/routes/mydex/+page.svelte b/src/routes/mydex/+page.svelte index ace0aa5..6dd73f6 100644 --- a/src/routes/mydex/+page.svelte +++ b/src/routes/mydex/+page.svelte @@ -2,11 +2,16 @@ import { onMount, onDestroy } from 'svelte'; import { user } from '$lib/stores/user.js'; import { type User } from '@supabase/auth-js'; - import mongoose, { Types } from 'mongoose'; - import { type PokedexEntry } from '$lib/models/PokedexEntry'; - import CatchRecordModel, { type CatchRecord } from '$lib/models/CatchRecord'; + import { type CatchRecord } from '$lib/models/CatchRecord'; import { type CombinedData } from '$lib/models/CombinedData'; import PokedexEntryCatchRecord from '$lib/components/pokedex/PokedexEntryCatchRecord.svelte'; + import Pagination from '$lib/components/Pagination.svelte'; + import { browser } from '$app/environment'; + + let combinedData = null as CombinedData[] | null; + let currentPage = 1 as number; + let itemsPerPage = 20 as number; + let totalPages = 0 as number; let localUser: User | null; const unsubscribe = user.subscribe((value) => { @@ -14,13 +19,6 @@ }); onDestroy(unsubscribe); - let pokedexEntries = null as PokedexEntry[] | null; - let catchRecords = null as CatchRecord[] | null; - let combinedData = null as CombinedData[] | null; - - let creatingRecords = false; - let totalRecordsCreated = 0; - let showOrigins = true; let showForms = true; @@ -32,119 +30,24 @@ showForms = !showForms; } - async function fetchPokeDexEntries() { - console.log('Fetching updated Pokedex entry data'); - const response = await fetch('/api/pokedexentries'); - if (!response.ok) { - throw new Error('Failed to fetch Pokémon data'); - } + async function getData() { + const response = await fetch(`/api/combined-data?page=${currentPage}&limit=${itemsPerPage}`); + const data = await response.json(); - pokedexEntries = await response.json(); + combinedData = data.combinedData; + totalPages = data.totalPages; } - async function fetchCatchRecords() { - const response = await fetch('/api/catch-records'); - if (!response.ok) { - throw new Error('Failed to fetch catch record data'); - } - - catchRecords = await response.json(); - - // If there are no catch records, make one for each pokedex entry - if (catchRecords?.length === 0 && localUser?.id) { - creatingRecords = true; - const newCatchRecords = pokedexEntries.map((entry) => ({ - userId: localUser?.id, - pokedexEntryId: entry._id, - haveToEvolve: false, - caught: false, - inHome: false, - hasGigantamaxed: false, - personalNotes: '' - })); - - if (newCatchRecords.length === 0) { - alert('No pokedex entries to create catch records for'); - return; - } - - const batchSize = 500; - for (let i = 0; i < newCatchRecords.length; i += batchSize) { - const batch = newCatchRecords.slice(i, i + batchSize); - - const requestOptions = { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(batch) - }; - - try { - const response = await fetch('/api/catch-records', requestOptions); - if (!response.ok) { - throw new Error('Failed to create catch records'); - } - - const createdRecords = await response.json(); - totalRecordsCreated += createdRecords.length; - console.log(`Created ${createdRecords.length} catch records`); - } catch (error) { - console.error('Error creating catch records:', error); - } - } - - await fetchCatchRecords(); - console.log('Created catch records for each pokedex entry'); - creatingRecords = false; - } - } - - const fetchLastModifiedDate = async () => { - const response = await fetch('/api/pokedex-metadata'); - const { lastModified } = await response.json(); - return new Date(lastModified); - }; - - const cacheData = () => { - localStorage.setItem('pokeDexEntries', JSON.stringify(pokedexEntries)); - localStorage.setItem('pokeDexLastUpdated', new Date().toISOString()); - }; - - const checkForUpdates = async () => { - const cachedData = localStorage.getItem('pokeDexEntries'); - const cachedDate = localStorage.getItem('pokeDexLastUpdated'); - - if (cachedData && cachedDate) { - const lastModified = await fetchLastModifiedDate(); - const lastUpdated = new Date(cachedDate); - - if (lastModified <= lastUpdated) { - pokedexEntries = JSON.parse(cachedData); - return; - } - } - - await fetchPokeDexEntries(); - cacheData(); - }; - onMount(async () => { - await checkForUpdates() - .then(async () => { - await fetchCatchRecords(); - }) - .then(async () => { - combinedData = []; - - // Combine pokedex entries with their corresponding catch records - pokedexEntries.forEach((entry) => { - let catchRecord = catchRecords.find((record) => record.pokedexEntryId === entry._id); - if (catchRecord) { - combinedData.push({ pokedexEntry: entry, catchRecord }); - } - }); - }); + await getData(); }); + $: { + if (browser) { + currentPage, itemsPerPage, getData(); + } + } + async function updateACatch(catchRecord: CatchRecord) { alert('here'); // if (!localUser?.id) { @@ -179,9 +82,9 @@ - + + {currentPage} + {#each combinedData as { pokedexEntry, catchRecord }}
{/each} + + + {currentPage} {:else} -

Loading Pokédex

-

Please be patient, this can take some time on the first load.

- {#if totalRecordsCreated > 0} -

Processing {totalRecordsCreated}/{pokedexEntries?.length}

- {:else if creatingRecords} -

Processing...

- {/if} + {/if}