diff --git a/src/lib/components/database/DatabaseGrid.svelte b/src/lib/components/database/DatabaseGrid.svelte new file mode 100644 index 00000000..e5f766da --- /dev/null +++ b/src/lib/components/database/DatabaseGrid.svelte @@ -0,0 +1,325 @@ + + + + + +
+
+ + +
+ + +
+
+ +
+ {#if loading} +
+
Loading...
+
+ {/if} + + +
+ + +
+ + \ No newline at end of file diff --git a/src/lib/components/database/cells/CharacterImageCell.svelte b/src/lib/components/database/cells/CharacterImageCell.svelte new file mode 100644 index 00000000..55c78c42 --- /dev/null +++ b/src/lib/components/database/cells/CharacterImageCell.svelte @@ -0,0 +1,35 @@ + + + + +
+ +
+ + \ No newline at end of file diff --git a/src/lib/components/database/cells/SummonImageCell.svelte b/src/lib/components/database/cells/SummonImageCell.svelte new file mode 100644 index 00000000..0baac6e8 --- /dev/null +++ b/src/lib/components/database/cells/SummonImageCell.svelte @@ -0,0 +1,35 @@ + + + + +
+ +
+ + \ No newline at end of file diff --git a/src/lib/components/database/cells/WeaponImageCell.svelte b/src/lib/components/database/cells/WeaponImageCell.svelte new file mode 100644 index 00000000..5d15be8f --- /dev/null +++ b/src/lib/components/database/cells/WeaponImageCell.svelte @@ -0,0 +1,35 @@ + + + + +
+ +
+ + \ No newline at end of file diff --git a/src/lib/utils/database.ts b/src/lib/utils/database.ts new file mode 100644 index 00000000..9048711a --- /dev/null +++ b/src/lib/utils/database.ts @@ -0,0 +1,67 @@ +import { TeamElement } from '$lib/types/enums' + +export function elementLabel(n?: number): string { + switch (n) { + case TeamElement.Wind: + return 'Wind' + case TeamElement.Fire: + return 'Fire' + case TeamElement.Water: + return 'Water' + case TeamElement.Earth: + return 'Earth' + case TeamElement.Dark: + return 'Dark' + case TeamElement.Light: + return 'Light' + case TeamElement.Null: + return 'Null' + default: + return '—' + } +} + +export function elementClass(n?: number): string { + switch (n) { + case TeamElement.Wind: + return 'element-wind' + case TeamElement.Fire: + return 'element-fire' + case TeamElement.Water: + return 'element-water' + case TeamElement.Earth: + return 'element-earth' + case TeamElement.Dark: + return 'element-dark' + case TeamElement.Light: + return 'element-light' + default: + return '' + } +} + +export function getCharacterImageUrl(gbid?: string | number): string { + if (!gbid) return '/images/placeholders/placeholder-character-grid.png' + return `https://prd-game-a1-granbluefantasy.akamaized.net/assets/img/sp/assets/npc/m/${gbid}_01.jpg` +} + +export function getWeaponImageUrl(gbid?: string | number): string { + if (!gbid) return '/images/placeholders/placeholder-weapon-grid.png' + return `https://prd-game-a1-granbluefantasy.akamaized.net/assets/img/sp/assets/weapon/m/${gbid}.jpg` +} + +export function getSummonImageUrl(gbid?: string | number): string { + if (!gbid) return '/images/placeholders/placeholder-summon-main.png' + return `https://prd-game-a1-granbluefantasy.akamaized.net/assets/img/sp/assets/summon/m/${gbid}.jpg` +} + +export function getItemName(item: { name?: string | { en?: string; ja?: string } }): string { + const name = item.name + + // Handle name object + if (!name) return '—' + if (typeof name === 'string') return name + + // Handle name.en/name.ja structure (API returns { en: "...", ja: "..." }) + return name.en || name.ja || '—' +} \ No newline at end of file diff --git a/src/routes/database/+layout.server.ts b/src/routes/database/+layout.server.ts new file mode 100644 index 00000000..b12e8629 --- /dev/null +++ b/src/routes/database/+layout.server.ts @@ -0,0 +1,21 @@ +import { redirect } from '@sveltejs/kit' +import type { LayoutServerLoad } from './$types' + +export const load: LayoutServerLoad = async ({ locals, url }) => { + // Check authentication first + if (!locals.session.isAuthenticated) { + throw redirect(302, '/login') + } + + // Check role authorization + const role = locals.session.account?.role ?? 0 + if (role < 7) { + // Redirect to home with no indication of why (security best practice) + throw redirect(302, '/') + } + + return { + role + } +} + diff --git a/src/routes/database/+layout.svelte b/src/routes/database/+layout.svelte new file mode 100644 index 00000000..627e7bf5 --- /dev/null +++ b/src/routes/database/+layout.svelte @@ -0,0 +1,43 @@ + + +
+ +
+ + + + + diff --git a/src/routes/database/+page.server.ts b/src/routes/database/+page.server.ts new file mode 100644 index 00000000..9fa3bb74 --- /dev/null +++ b/src/routes/database/+page.server.ts @@ -0,0 +1,16 @@ +import { redirect } from '@sveltejs/kit' +import type { PageServerLoad } from './$types' + +export const load: PageServerLoad = async ({ locals }) => { + // Double-check authorization at page level + if (!locals.session.isAuthenticated) { + throw redirect(302, '/login') + } + + const role = locals.session.account?.role ?? 0 + if (role < 7) { + throw redirect(302, '/') + } + + return {} +} \ No newline at end of file diff --git a/src/routes/database/+page.ts b/src/routes/database/+page.ts new file mode 100644 index 00000000..547d4f51 --- /dev/null +++ b/src/routes/database/+page.ts @@ -0,0 +1,7 @@ +import { redirect } from '@sveltejs/kit' +import type { PageLoad } from './$types' + +export const load: PageLoad = async () => { + throw redirect(302, '/database/weapons') +} + diff --git a/src/routes/database/characters/+page.server.ts b/src/routes/database/characters/+page.server.ts new file mode 100644 index 00000000..48582bb4 --- /dev/null +++ b/src/routes/database/characters/+page.server.ts @@ -0,0 +1,16 @@ +import { redirect } from '@sveltejs/kit' +import type { PageServerLoad } from './$types' + +export const load: PageServerLoad = async ({ locals }) => { + // Enforce authorization at individual page level + if (!locals.session.isAuthenticated) { + throw redirect(302, '/login') + } + + const role = locals.session.account?.role ?? 0 + if (role < 7) { + throw redirect(302, '/') + } + + return {} +} \ No newline at end of file diff --git a/src/routes/database/characters/+page.svelte b/src/routes/database/characters/+page.svelte new file mode 100644 index 00000000..faaea6eb --- /dev/null +++ b/src/routes/database/characters/+page.svelte @@ -0,0 +1,127 @@ + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/src/routes/database/characters/+page.ts b/src/routes/database/characters/+page.ts new file mode 100644 index 00000000..4c883757 --- /dev/null +++ b/src/routes/database/characters/+page.ts @@ -0,0 +1,18 @@ +import type { PageLoad } from './$types' +import { searchCharacters } from '$lib/api/resources/search' + +export const load: PageLoad = async ({ fetch, url }) => { + const page = Number(url.searchParams.get('page') || '1') || 1 + const pageSize = Number(url.searchParams.get('pageSize') || '20') || 20 + + const search = await searchCharacters({ page, per: pageSize }, undefined, fetch) + + return { + items: search.results || [], + page: search.meta?.page || page, + totalPages: search.meta?.total_pages || 1, + total: search.meta?.count || 0, + pageSize: search.meta?.per_page || pageSize + } +} + diff --git a/src/routes/database/summons/+page.server.ts b/src/routes/database/summons/+page.server.ts new file mode 100644 index 00000000..48582bb4 --- /dev/null +++ b/src/routes/database/summons/+page.server.ts @@ -0,0 +1,16 @@ +import { redirect } from '@sveltejs/kit' +import type { PageServerLoad } from './$types' + +export const load: PageServerLoad = async ({ locals }) => { + // Enforce authorization at individual page level + if (!locals.session.isAuthenticated) { + throw redirect(302, '/login') + } + + const role = locals.session.account?.role ?? 0 + if (role < 7) { + throw redirect(302, '/') + } + + return {} +} \ No newline at end of file diff --git a/src/routes/database/summons/+page.svelte b/src/routes/database/summons/+page.svelte new file mode 100644 index 00000000..94d7adfe --- /dev/null +++ b/src/routes/database/summons/+page.svelte @@ -0,0 +1,127 @@ + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/src/routes/database/summons/+page.ts b/src/routes/database/summons/+page.ts new file mode 100644 index 00000000..25f69ec4 --- /dev/null +++ b/src/routes/database/summons/+page.ts @@ -0,0 +1,18 @@ +import type { PageLoad } from './$types' +import { searchSummons } from '$lib/api/resources/search' + +export const load: PageLoad = async ({ fetch, url }) => { + const page = Number(url.searchParams.get('page') || '1') || 1 + const pageSize = Number(url.searchParams.get('pageSize') || '20') || 20 + + const search = await searchSummons({ page, per: pageSize }, undefined, fetch) + + return { + items: search.results || [], + page: search.meta?.page || page, + totalPages: search.meta?.total_pages || 1, + total: search.meta?.count || 0, + pageSize: search.meta?.per_page || pageSize + } +} + diff --git a/src/routes/database/weapons/+page.server.ts b/src/routes/database/weapons/+page.server.ts new file mode 100644 index 00000000..48582bb4 --- /dev/null +++ b/src/routes/database/weapons/+page.server.ts @@ -0,0 +1,16 @@ +import { redirect } from '@sveltejs/kit' +import type { PageServerLoad } from './$types' + +export const load: PageServerLoad = async ({ locals }) => { + // Enforce authorization at individual page level + if (!locals.session.isAuthenticated) { + throw redirect(302, '/login') + } + + const role = locals.session.account?.role ?? 0 + if (role < 7) { + throw redirect(302, '/') + } + + return {} +} \ No newline at end of file diff --git a/src/routes/database/weapons/+page.svelte b/src/routes/database/weapons/+page.svelte new file mode 100644 index 00000000..92aaaa2d --- /dev/null +++ b/src/routes/database/weapons/+page.svelte @@ -0,0 +1,131 @@ + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/src/routes/database/weapons/+page.ts b/src/routes/database/weapons/+page.ts new file mode 100644 index 00000000..e94318eb --- /dev/null +++ b/src/routes/database/weapons/+page.ts @@ -0,0 +1,29 @@ +import type { PageLoad } from './$types' +import { searchWeapons } from '$lib/api/resources/search' + +export const load: PageLoad = async ({ fetch, url }) => { + const page = Number(url.searchParams.get('page') || '1') || 1 + const pageSize = Number(url.searchParams.get('pageSize') || '20') || 20 + + console.log('[Database Weapons] Loading page:', page, 'pageSize:', pageSize) + + const search = await searchWeapons({ page, per: pageSize }, undefined, fetch) + + console.log('[Database Weapons] API Response:', search) + console.log('[Database Weapons] Meta:', search.meta) + console.log('[Database Weapons] Results count:', search.results?.length || 0) + + // Extract data from meta object + const result = { + items: search.results || [], + page: search.meta?.page || page, + totalPages: search.meta?.total_pages || 1, + total: search.meta?.count || 0, + pageSize: search.meta?.per_page || pageSize + } + + console.log('[Database Weapons] Returning to component:', result) + + return result +} +