import { db } from '$lib/server/db'; import { game, pokemonGameEntry, userEntry, userGame } from '$lib/server/db/schema'; import { eq, inArray } from 'drizzle-orm'; import type { Actions, PageServerLoad } from './$types'; import { availabilityValues } from '$lib/availability'; import { fail } from '@sveltejs/kit'; export type WireEntry = [id: number, gameId: number, availability: number, note?: string]; export const load: PageServerLoad = async ({ locals }) => { const pokemonRows = await db.query.pokemon.findMany({ with: { entry: true }, orderBy: (pokemon) => pokemon.number }); const pokemon = pokemonRows.map(({ entry, ...rest }) => ({ ...rest, entries: entry.map((e): WireEntry => { const code = availabilityValues.indexOf(e.availability); return e.note === null ? [e.id, e.gameId, code] : [e.id, e.gameId, code, e.note]; }) })); const games = await db.query.game.findMany({ orderBy: (game) => [game.generation, game.id] }); let caughtIds: number[] = []; let selectedGameIds: number[] = []; if (locals.user) { const caughtRows = await db .select({ id: userEntry.pokemonGameEntryId }) .from(userEntry) .where(eq(userEntry.userId, locals.user.id)); caughtIds = caughtRows.map((r) => r.id); const gameRows = await db .select({ id: userGame.gameId }) .from(userGame) .where(eq(userGame.userId, locals.user.id)); selectedGameIds = gameRows.map((r) => r.id); } return { pokemon, games, caughtIds, selectedGameIds }; }; export const actions = { togglePokemonCaught: async ({ request, locals }) => { if (!locals.user) return fail(401, { message: 'Log in to track your progress' }); const data = await request.formData(); const entryId = Number(data.get('entryId')); if (!Number.isInteger(entryId) || entryId <= 0) return fail(400, { message: 'Invalid entry' }); // only catchable entries can be toggled — the client disables these // checkboxes but we can't trust that const [entry] = await db .select({ availability: pokemonGameEntry.availability }) .from(pokemonGameEntry) .where(eq(pokemonGameEntry.id, entryId)); if (!entry) return fail(404, { message: 'Unknown entry' }); if (entry.availability !== 'catchable') { return fail(400, { message: 'This pokemon is not catchable in this game' }); } const caught = data.get('caught') === 'on'; if (caught) { await db .insert(userEntry) .values({ pokemonGameEntryId: entryId, userId: locals.user.id }) .onConflictDoNothing(); } else { await db .delete(userEntry) .where( and(eq(userEntry.pokemonGameEntryId, entryId), eq(userEntry.userId, locals.user.id)) ); } }, setGames: async ({ request, locals }) => { if (!locals.user) return fail(401, { message: 'Log in to set rules' }); const data = await request.formData(); const gameIds = data .getAll('gameIds') .map(Number) .filter((id) => Number.isInteger(id) && id > 0); if (gameIds.length > 0) { const validGames = await db .select({ id: game.id }) .from(game) .where(inArray(game.id, gameIds)); if (validGames.length !== gameIds.length) return fail(400, { message: 'Unknown game' }); } const userId = locals.user.id; await db.transaction(async (tx) => { await tx.delete(userGame).where(eq(userGame.userId, userId)); if (gameIds.length > 0) { await tx.insert(userGame).values(gameIds.map((gameId) => ({ userId, gameId }))); } }); } } satisfies Actions;