import { db } from '$lib/server/db'; import { character, devilFruit, arc, characterOverride } from '$lib/server/db/schema'; import { eq } from 'drizzle-orm'; import { fail } from '@sveltejs/kit'; import type { PageServerLoad, Actions } from './$types'; export const load: PageServerLoad = async () => { const [charactersData, devilFruits, arcs, overrides] = await Promise.all([ db .select({ id: character.id, name: character.name, gender: character.gender, age: character.age, affiliations: character.affiliations, devilFruitId: character.devilFruitId, hakiObservation: character.hakiObservation, hakiArmament: character.hakiArmament, hakiConqueror: character.hakiConqueror, bounty: character.bounty, height: character.height, origin: character.origin, firstAppearance: character.firstAppearance, pictureUrl: character.pictureUrl, epithets: character.epithets, status: character.status, url: character.url, arcId: character.arcId, isInDailyMode: character.isInDailyMode, arcName: arc.name, devilFruitName: devilFruit.name, devilFruitType: devilFruit.type }) .from(character) .leftJoin(arc, eq(character.arcId, arc.id)) .leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id)) .orderBy(character.name), db.select().from(devilFruit).orderBy(devilFruit.name), db.select().from(arc).orderBy(arc.name), db.select().from(characterOverride) ]); // Create a map of overrides by characterId for easy lookup const overridesMap = new Map(overrides.map((o) => [o.characterId, o])); // Merge character data with overrides const charactersWithOverrides = charactersData.map((char) => { const override = overridesMap.get(char.id); // Build displayValues by only applying non-null override fields const displayValues = { ...char } as any; if (override) { Object.keys(override).forEach((key) => { if (override[key as keyof typeof override] !== null && key !== 'characterId') { displayValues[key as keyof typeof displayValues] = override[key as keyof typeof override]; } }); } return { ...char, override, displayValues }; }); return { characters: charactersWithOverrides, devilFruits, arcs }; }; export const actions: Actions = { update: async ({ request, locals }) => { if (!locals.user?.isAdmin) { return fail(401, { error: 'Unauthorized' }); } const formData = await request.formData(); const id = formData.get('id') as string; if (!id) { return fail(400, { error: 'Character ID is required' }); } try { const [originalCharacter] = await db .select({ hakiObservation: character.hakiObservation, hakiArmament: character.hakiArmament, hakiConqueror: character.hakiConqueror }) .from(character) .where(eq(character.id, id)) .limit(1); if (!originalCharacter) { return fail(404, { error: 'Character not found' }); } const updates: Record = {}; formData.forEach((value, key) => { if (key !== 'id') { // Handle integers (age, bounty, height, devilFruitId, arcId) if (key === 'age' || key === 'bounty' || key === 'height' || key === 'devilFruitId' || key === 'arcId') { const strValue = value as string; updates[key] = strValue && strValue !== '' ? parseInt(strValue) : null; } // Handle checkboxes (haki fields) after parsing all form data else if (key === 'hakiObservation' || key === 'hakiArmament' || key === 'hakiConqueror') { return; } // Handle strings (name, gender, status, origin, affiliations, epithets, pictureUrl, url, firstAppearance) else { updates[key] = value || null; } } }); const submittedHakiObservation = formData.has('hakiObservation'); const submittedHakiArmament = formData.has('hakiArmament'); const submittedHakiConqueror = formData.has('hakiConqueror'); updates.hakiObservation = submittedHakiObservation === originalCharacter.hakiObservation ? null : submittedHakiObservation; updates.hakiArmament = submittedHakiArmament === originalCharacter.hakiArmament ? null : submittedHakiArmament; updates.hakiConqueror = submittedHakiConqueror === originalCharacter.hakiConqueror ? null : submittedHakiConqueror; // Update or insert into characterOverride table await db .insert(characterOverride) .values({ characterId: id, ...updates }) .onConflictDoUpdate({ target: characterOverride.characterId, set: updates }); return { success: true }; } catch (error) { console.error('Character update error:', error); return fail(500, { error: 'Failed to update character' }); } }, delete: async ({ request, locals }) => { if (!locals.user?.isAdmin) { return fail(401, { error: 'Unauthorized' }); } const formData = await request.formData(); const id = formData.get('id') as string; if (!id) { return fail(400, { error: 'Character ID is required' }); } try { await db.delete(character).where(eq(character.id, id)); return { success: true }; } catch (error) { console.error('Character delete error:', error); return fail(500, { error: 'Failed to delete character' }); } }, toggleDailyMode: async ({ request, locals }) => { if (!locals.user?.isAdmin) { return fail(401, { error: 'Unauthorized' }); } const formData = await request.formData(); const id = formData.get('id') as string; const isInDailyMode = formData.get('isInDailyMode') === 'true'; if (!id) { return fail(400, { error: 'Character ID is required' }); } try { await db.update(character) .set({ isInDailyMode }) .where(eq(character.id, id)); return { success: true }; } catch (error) { console.error('Toggle daily mode error:', error); return fail(500, { error: 'Failed to toggle daily mode' }); } } };