use granblueId in database URLs instead of UUID

- rename route folders from [id] to [granblueId]
- update all server load functions to use params.granblueId
- update detail/edit/new pages to navigate with granblueId
- update cross-entity links (recruits, recruitedBy)
- update TanStack Query keys to use granblueId

backend's IdResolvable already supports fetching by granblueId
This commit is contained in:
Justin Edmund 2025-12-15 11:26:52 -08:00
parent 4d7d2c563e
commit 6ba89dc216
18 changed files with 57 additions and 57 deletions

View file

@ -21,25 +21,25 @@ import { entityAdapter, type WeaponKeyQueryParams } from '$lib/api/adapters/enti
* import { createQuery } from '@tanstack/svelte-query'
* import { entityQueries } from '$lib/api/queries/entity.queries'
*
* // Single weapon by ID
* const weapon = createQuery(() => entityQueries.weapon(id))
* // Single weapon by granblueId
* const weapon = createQuery(() => entityQueries.weapon(granblueId))
*
* // Single character by ID
* const character = createQuery(() => entityQueries.character(id))
* // Single character by granblueId
* const character = createQuery(() => entityQueries.character(granblueId))
* ```
*/
export const entityQueries = {
/**
* Single weapon query options
*
* @param id - Weapon ID
* @param granblueId - Weapon granblueId (e.g., "1040001000")
* @returns Query options for fetching a single weapon
*/
weapon: (id: string) =>
weapon: (granblueId: string) =>
queryOptions({
queryKey: ['weapon', id] as const,
queryFn: () => entityAdapter.getWeapon(id),
enabled: !!id,
queryKey: ['weapon', granblueId] as const,
queryFn: () => entityAdapter.getWeapon(granblueId),
enabled: !!granblueId,
staleTime: 1000 * 60 * 60, // 1 hour - canonical data rarely changes
gcTime: 1000 * 60 * 60 * 24 // 24 hours
}),
@ -47,14 +47,14 @@ export const entityQueries = {
/**
* Single character query options
*
* @param id - Character ID
* @param granblueId - Character granblueId (e.g., "3040001000")
* @returns Query options for fetching a single character
*/
character: (id: string) =>
character: (granblueId: string) =>
queryOptions({
queryKey: ['character', id] as const,
queryFn: () => entityAdapter.getCharacter(id),
enabled: !!id,
queryKey: ['character', granblueId] as const,
queryFn: () => entityAdapter.getCharacter(granblueId),
enabled: !!granblueId,
staleTime: 1000 * 60 * 60, // 1 hour - canonical data rarely changes
gcTime: 1000 * 60 * 60 * 24 // 24 hours
}),
@ -62,14 +62,14 @@ export const entityQueries = {
/**
* Single summon query options
*
* @param id - Summon ID
* @param granblueId - Summon granblueId (e.g., "2040001000")
* @returns Query options for fetching a single summon
*/
summon: (id: string) =>
summon: (granblueId: string) =>
queryOptions({
queryKey: ['summon', id] as const,
queryFn: () => entityAdapter.getSummon(id),
enabled: !!id,
queryKey: ['summon', granblueId] as const,
queryFn: () => entityAdapter.getSummon(granblueId),
enabled: !!granblueId,
staleTime: 1000 * 60 * 60, // 1 hour - canonical data rarely changes
gcTime: 1000 * 60 * 60 * 24 // 24 hours
}),
@ -186,8 +186,8 @@ export const entityQueries = {
*
* const queryClient = useQueryClient()
*
* // Invalidate a specific weapon
* queryClient.invalidateQueries({ queryKey: entityKeys.weapon('abc123') })
* // Invalidate a specific weapon by granblueId
* queryClient.invalidateQueries({ queryKey: entityKeys.weapon('1040001000') })
*
* // Invalidate all weapons
* queryClient.invalidateQueries({ queryKey: entityKeys.weapons() })
@ -195,11 +195,11 @@ export const entityQueries = {
*/
export const entityKeys = {
weapons: () => ['weapon'] as const,
weapon: (id: string) => [...entityKeys.weapons(), id] as const,
weapon: (granblueId: string) => [...entityKeys.weapons(), granblueId] as const,
characters: () => ['character'] as const,
character: (id: string) => [...entityKeys.characters(), id] as const,
character: (granblueId: string) => [...entityKeys.characters(), granblueId] as const,
summons: () => ['summon'] as const,
summon: (id: string) => [...entityKeys.summons(), id] as const,
summon: (granblueId: string) => [...entityKeys.summons(), granblueId] as const,
weaponKeys: (params?: WeaponKeyQueryParams) =>
['weaponKeys', params?.seriesSlug, params?.slot, params?.group] as const,
weaponSeriesList: () => ['weaponSeries', 'list'] as const,

View file

@ -101,7 +101,7 @@
{/if}
{#if character.recruitedBy}
<DetailItem label="Recruited By">
<a href="/database/weapons/{character.recruitedBy.id}" class="recruited-by-link">
<a href="/database/weapons/{character.recruitedBy.granblueId}" class="recruited-by-link">
<img
src={getWeaponImage(character.recruitedBy.granblueId, 'square')}
alt={character.recruitedBy.name.en || 'Recruiting weapon'}

View file

@ -63,7 +63,7 @@
</DetailItem>
{#if weapon.recruits}
<DetailItem label="Recruits">
<a href="/database/characters/{weapon.recruits.id}" class="recruits-link">
<a href="/database/characters/{weapon.recruits.granblueId}" class="recruits-link">
<img
src={getCharacterImage(weapon.recruits.granblueId, 'square', '01')}
alt={weapon.recruits.name.en || 'Recruited character'}

View file

@ -7,7 +7,7 @@ export const load: PageServerLoad = async ({ params, parent }) => {
// Get parent data to access role
const parentData = await parent()
const character = await entityAdapter.getCharacter(params.id)
const character = await entityAdapter.getCharacter(params.granblueId)
if (!character) {
throw error(404, 'Character not found')

View file

@ -50,7 +50,7 @@
// Use TanStack Query with SSR initial data
const characterQuery = createQuery(() => ({
...entityQueries.character(data.character?.id ?? ''),
...entityQueries.character(data.character?.granblueId ?? ''),
...withInitialData(data.character)
}))
@ -60,7 +60,7 @@
const canEdit = $derived(userRole >= 7)
// Edit URL for navigation
const editUrl = $derived(character?.id ? `/database/characters/${character.id}/edit` : undefined)
const editUrl = $derived(character?.granblueId ? `/database/characters/${character.granblueId}/edit` : undefined)
// Query for related characters (same character_id)
const relatedQuery = createQuery(() => ({
@ -260,7 +260,7 @@
<DetailsContainer title="Related Units">
<div class="related-units">
{#each relatedQuery.data as related}
<a href="/database/characters/{related.id}" class="related-unit">
<a href="/database/characters/{related.granblueId}" class="related-unit">
<img
src={getCharacterImage(related.granblueId, 'grid', '01')}
alt={related.name.en}

View file

@ -8,11 +8,11 @@ export const load: PageServerLoad = async ({ params, parent }) => {
// Role check - must be editor level (>= 7) to edit
if (!parentData.role || parentData.role < 7) {
throw redirect(303, `/database/characters/${params.id}`)
throw redirect(303, `/database/characters/${params.granblueId}`)
}
try {
const character = await entityAdapter.getCharacter(params.id)
const character = await entityAdapter.getCharacter(params.granblueId)
if (!character) {
throw error(404, 'Character not found')

View file

@ -34,7 +34,7 @@
// Use TanStack Query with SSR initial data
const characterQuery = createQuery(() => ({
...entityQueries.character(data.character?.id ?? ''),
...entityQueries.character(data.character?.granblueId ?? ''),
...withInitialData(data.character)
}))
@ -142,10 +142,10 @@
await entityAdapter.updateCharacter(character.id, payload)
// Invalidate TanStack Query cache to refetch fresh data
await queryClient.invalidateQueries({ queryKey: ['character', character.id] })
await queryClient.invalidateQueries({ queryKey: ['character', character.granblueId] })
// Navigate back to detail page
goto(`/database/characters/${character.id}`)
goto(`/database/characters/${character.granblueId}`)
} catch (error) {
saveError = 'Failed to save changes. Please try again.'
console.error('Save error:', error)
@ -155,7 +155,7 @@
}
function handleCancel() {
goto(`/database/characters/${character?.id}`)
goto(`/database/characters/${character?.granblueId}`)
}
// Helper function for character grid image

View file

@ -223,7 +223,7 @@
const newCharacter = await entityAdapter.createCharacter(payload)
// Trigger image download in background (don't await - it queues a job)
entityAdapter.downloadCharacterImages(newCharacter.id).catch(console.error)
await goto(`/database/characters/${newCharacter.id}`)
await goto(`/database/characters/${newCharacter.granblueId}`)
} catch (error) {
saveError = 'Failed to create character. Please try again.'
console.error('Create error:', error)

View file

@ -7,7 +7,7 @@ export const load: PageServerLoad = async ({ params, parent }) => {
// Get parent data to access role
const parentData = await parent()
const summon = await entityAdapter.getSummon(params.id)
const summon = await entityAdapter.getSummon(params.granblueId)
if (!summon) {
throw error(404, 'Summon not found')

View file

@ -49,7 +49,7 @@
// Use TanStack Query with SSR initial data
const summonQuery = createQuery(() => ({
...entityQueries.summon(data.summon?.id ?? ''),
...entityQueries.summon(data.summon?.granblueId ?? ''),
...withInitialData(data.summon)
}))
@ -59,7 +59,7 @@
const canEdit = $derived(userRole >= 7)
// Edit URL for navigation
const editUrl = $derived(summon?.id ? `/database/summons/${summon.id}/edit` : undefined)
const editUrl = $derived(summon?.granblueId ? `/database/summons/${summon.granblueId}/edit` : undefined)
// Query for raw data (only when on raw tab)
const rawDataQuery = createQuery(() => ({

View file

@ -8,11 +8,11 @@ export const load: PageServerLoad = async ({ params, parent }) => {
// Role check - must be editor level (>= 7) to edit
if (!parentData.role || parentData.role < 7) {
throw redirect(303, `/database/summons/${params.id}`)
throw redirect(303, `/database/summons/${params.granblueId}`)
}
try {
const summon = await entityAdapter.getSummon(params.id)
const summon = await entityAdapter.getSummon(params.granblueId)
if (!summon) {
throw error(404, 'Summon not found')

View file

@ -30,7 +30,7 @@
// Use TanStack Query with SSR initial data
const summonQuery = createQuery(() => ({
...entityQueries.summon(data.summon?.id ?? ''),
...entityQueries.summon(data.summon?.granblueId ?? ''),
...withInitialData(data.summon)
}))
@ -168,10 +168,10 @@
await entityAdapter.updateSummon(summon.id, payload)
// Invalidate TanStack Query cache to refetch fresh data
await queryClient.invalidateQueries({ queryKey: ['summon', summon.id] })
await queryClient.invalidateQueries({ queryKey: ['summon', summon.granblueId] })
// Navigate back to detail page
goto(`/database/summons/${summon.id}`)
goto(`/database/summons/${summon.granblueId}`)
} catch (error) {
saveError = 'Failed to save changes. Please try again.'
console.error('Save error:', error)
@ -181,7 +181,7 @@
}
function handleCancel() {
goto(`/database/summons/${summon?.id}`)
goto(`/database/summons/${summon?.granblueId}`)
}
// Helper function for summon grid image

View file

@ -203,7 +203,7 @@
const newSummon = await entityAdapter.createSummon(payload)
// Trigger image download in background (don't await - it queues a job)
entityAdapter.downloadSummonImages(newSummon.id).catch(console.error)
await goto(`/database/summons/${newSummon.id}`)
await goto(`/database/summons/${newSummon.granblueId}`)
} catch (error) {
saveError = 'Failed to create summon. Please try again.'
console.error('Create error:', error)

View file

@ -7,7 +7,7 @@ export const load: PageServerLoad = async ({ params, parent }) => {
// Get parent data to access role
const parentData = await parent()
const weapon = await entityAdapter.getWeapon(params.id)
const weapon = await entityAdapter.getWeapon(params.granblueId)
if (!weapon) {
throw error(404, 'Weapon not found')

View file

@ -50,7 +50,7 @@
// Use TanStack Query with SSR initial data
const weaponQuery = createQuery(() => ({
...entityQueries.weapon(data.weapon?.id ?? ''),
...entityQueries.weapon(data.weapon?.granblueId ?? ''),
...withInitialData(data.weapon)
}))
@ -60,7 +60,7 @@
const canEdit = $derived(userRole >= 7)
// Edit URL for navigation
const editUrl = $derived(weapon?.id ? `/database/weapons/${weapon.id}/edit` : undefined)
const editUrl = $derived(weapon?.granblueId ? `/database/weapons/${weapon.granblueId}/edit` : undefined)
// Query for raw data (only when on raw tab)
const rawDataQuery = createQuery(() => ({

View file

@ -8,11 +8,11 @@ export const load: PageServerLoad = async ({ params, parent }) => {
// Role check - must be editor level (>= 7) to edit
if (!parentData.role || parentData.role < 7) {
throw redirect(303, `/database/weapons/${params.id}`)
throw redirect(303, `/database/weapons/${params.granblueId}`)
}
try {
const weapon = await entityAdapter.getWeapon(params.id)
const weapon = await entityAdapter.getWeapon(params.granblueId)
if (!weapon) {
throw error(404, 'Weapon not found')

View file

@ -31,7 +31,7 @@
// Use TanStack Query with SSR initial data
const weaponQuery = createQuery(() => ({
...entityQueries.weapon(data.weapon?.id ?? ''),
...entityQueries.weapon(data.weapon?.granblueId ?? ''),
...withInitialData(data.weapon)
}))
@ -179,10 +179,10 @@
await entityAdapter.updateWeapon(weapon.id, payload)
// Invalidate TanStack Query cache to refetch fresh data
await queryClient.invalidateQueries({ queryKey: ['weapon', weapon.id] })
await queryClient.invalidateQueries({ queryKey: ['weapon', weapon.granblueId] })
// Navigate back to detail page
goto(`/database/weapons/${weapon.id}`)
goto(`/database/weapons/${weapon.granblueId}`)
} catch (error) {
saveError = 'Failed to save changes. Please try again.'
console.error('Save error:', error)
@ -192,7 +192,7 @@
}
function handleCancel() {
goto(`/database/weapons/${weapon?.id}`)
goto(`/database/weapons/${weapon?.granblueId}`)
}
// Helper function for weapon grid image

View file

@ -210,7 +210,7 @@
const newWeapon = await entityAdapter.createWeapon(payload)
// Trigger image download in background (don't await - it queues a job)
entityAdapter.downloadWeaponImages(newWeapon.id).catch(console.error)
await goto(`/database/weapons/${newWeapon.id}`)
await goto(`/database/weapons/${newWeapon.granblueId}`)
} catch (error) {
saveError = 'Failed to create weapon. Please try again.'
console.error('Create error:', error)