diff --git a/src/lib/api/adapters/entity.adapter.ts b/src/lib/api/adapters/entity.adapter.ts index d5fb5478..ae5ca78c 100644 --- a/src/lib/api/adapters/entity.adapter.ts +++ b/src/lib/api/adapters/entity.adapter.ts @@ -550,6 +550,8 @@ export interface CharacterSuggestions { maxAtkFlb?: number flb?: boolean ulb?: boolean + /** Series array (e.g., [2] for Grand, [3] for Zodiac) */ + series?: number[] releaseDate?: string flbDate?: string ulbDate?: string @@ -570,11 +572,15 @@ export interface WeaponSuggestions { minHp?: number maxHp?: number maxHpFlb?: number + maxHpUlb?: number minAtk?: number maxAtk?: number maxAtkFlb?: number + maxAtkUlb?: number flb?: boolean ulb?: boolean + /** Series name (e.g., "Revenant", "Optimus") */ + series?: string releaseDate?: string flbDate?: string ulbDate?: string @@ -596,11 +602,16 @@ export interface SummonSuggestions { minHp?: number maxHp?: number maxHpFlb?: number + maxHpUlb?: number minAtk?: number maxAtk?: number maxAtkFlb?: number + maxAtkUlb?: number flb?: boolean ulb?: boolean + transcendence?: boolean + /** Series name (e.g., "Optimus", "Arcarum") */ + series?: string subaura?: boolean releaseDate?: string flbDate?: string diff --git a/src/lib/components/ui/Select.svelte b/src/lib/components/ui/Select.svelte index 6b5178e1..1143c2d0 100644 --- a/src/lib/components/ui/Select.svelte +++ b/src/lib/components/ui/Select.svelte @@ -64,11 +64,16 @@ const selected = $derived(options.find((opt) => opt.value === value)) + // Local string value for Bits UI (which manages its own internal state) + let internalValue = $state( + value !== undefined && value !== null ? String(value) : undefined + ) + + // Sync external value changes to internal state $effect(() => { - console.log('[Select] value:', value, typeof value) - console.log('[Select] options:', options.map(o => ({ value: o.value, type: typeof o.value }))) - console.log('[Select] selected:', selected) + internalValue = value !== undefined && value !== null ? String(value) : undefined }) + const hasWrapper = $derived(label || error) const fieldsetClasses = $derived( @@ -106,7 +111,7 @@ - {#if editMode} - onAcceptSuggestion?.('element', suggestions?.element)} - onDismissSuggestion={() => onDismissSuggestion?.('element')} - /> - onAcceptSuggestion?.('race1', suggestions?.race1)} - onDismissSuggestion={() => onDismissSuggestion?.('race1')} - /> - onAcceptSuggestion?.('race2', suggestions?.race2)} - onDismissSuggestion={() => onDismissSuggestion?.('race2')} - /> - onAcceptSuggestion?.('gender', suggestions?.gender)} - onDismissSuggestion={() => onDismissSuggestion?.('gender')} - /> - onAcceptSuggestion?.('proficiency1', suggestions?.proficiency1)} - onDismissSuggestion={() => onDismissSuggestion?.('proficiency1')} - /> - onAcceptSuggestion?.('proficiency2', suggestions?.proficiency2)} - onDismissSuggestion={() => onDismissSuggestion?.('proficiency2')} - /> - - - - {:else} - - - - - - - - - - - - - - - - {/if} + {#if editMode} + onAcceptSuggestion?.('element', suggestions?.element)} + onDismissSuggestion={() => onDismissSuggestion?.('element')} + /> + onAcceptSuggestion?.('race1', suggestions?.race1)} + onDismissSuggestion={() => onDismissSuggestion?.('race1')} + /> + onAcceptSuggestion?.('race2', suggestions?.race2)} + onDismissSuggestion={() => onDismissSuggestion?.('race2')} + /> + onAcceptSuggestion?.('gender', suggestions?.gender)} + onDismissSuggestion={() => onDismissSuggestion?.('gender')} + /> + onAcceptSuggestion?.('proficiency1', suggestions?.proficiency1)} + onDismissSuggestion={() => onDismissSuggestion?.('proficiency1')} + /> + onAcceptSuggestion?.('proficiency2', suggestions?.proficiency2)} + onDismissSuggestion={() => onDismissSuggestion?.('proficiency2')} + /> + + + + + + + {:else} + + + + + + + + + + + + + + + + + {/if} - diff --git a/src/lib/features/database/characters/sections/CharacterUncapSection.svelte b/src/lib/features/database/characters/sections/CharacterUncapSection.svelte index f26ffe20..8e74035a 100644 --- a/src/lib/features/database/characters/sections/CharacterUncapSection.svelte +++ b/src/lib/features/database/characters/sections/CharacterUncapSection.svelte @@ -20,6 +20,8 @@ dismissedSuggestions?: Set onAcceptSuggestion?: (field: string, value: any) => void onDismissSuggestion?: (field: string) => void + // Callback when editData is modified (for triggering reactivity in parent) + onDataChange?: () => void } let { @@ -29,7 +31,8 @@ suggestions, dismissedSuggestions, onAcceptSuggestion, - onDismissSuggestion + onDismissSuggestion, + onDataChange }: Props = $props() const uncap = $derived( @@ -60,6 +63,7 @@ editData.ulb = false editData.transcendence = false } + onDataChange?.() } function handleUlbChange(checked: boolean) { @@ -70,6 +74,7 @@ // Unchecking ULB should also uncheck Transcendence editData.transcendence = false } + onDataChange?.() } function handleTranscendenceChange(checked: boolean) { @@ -78,6 +83,7 @@ if (!editData.ulb) editData.ulb = true if (!editData.flb) editData.flb = true } + onDataChange?.() } function handleSpecialChange(checked: boolean) { @@ -87,6 +93,7 @@ editData.ulb = false editData.transcendence = false } + onDataChange?.() } diff --git a/src/lib/features/database/summons/sections/SummonTaxonomySection.svelte b/src/lib/features/database/summons/sections/SummonTaxonomySection.svelte index 674ed880..1395ae31 100644 --- a/src/lib/features/database/summons/sections/SummonTaxonomySection.svelte +++ b/src/lib/features/database/summons/sections/SummonTaxonomySection.svelte @@ -1,12 +1,16 @@ diff --git a/src/lib/features/database/weapons/sections/WeaponTaxonomySection.svelte b/src/lib/features/database/weapons/sections/WeaponTaxonomySection.svelte index 75853d3d..0c004397 100644 --- a/src/lib/features/database/weapons/sections/WeaponTaxonomySection.svelte +++ b/src/lib/features/database/weapons/sections/WeaponTaxonomySection.svelte @@ -7,6 +7,7 @@ import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte' import DetailItem from '$lib/components/ui/DetailItem.svelte' import SuggestionDetailItem from '$lib/components/ui/SuggestionDetailItem.svelte' + import MultiSelect from '$lib/components/ui/MultiSelect.svelte' import ElementLabel from '$lib/components/labels/ElementLabel.svelte' import ProficiencyLabel from '$lib/components/labels/ProficiencyLabel.svelte' import { getElementLabel, getElementOptions } from '$lib/utils/element' @@ -138,15 +139,15 @@ type="checkbox" element={elementName} /> - + + + {:else} @@ -158,9 +159,21 @@ /> - - + + - + {/if} diff --git a/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte b/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte index e6aa6d97..cde07d2c 100644 --- a/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte +++ b/src/lib/features/database/weapons/sections/WeaponUncapSection.svelte @@ -19,6 +19,8 @@ dismissedSuggestions?: Set onAcceptSuggestion?: (field: string, value: any) => void onDismissSuggestion?: (field: string) => void + // Callback when editData is modified (for triggering reactivity in parent) + onDataChange?: () => void } let { @@ -28,7 +30,8 @@ suggestions, dismissedSuggestions, onAcceptSuggestion, - onDismissSuggestion + onDismissSuggestion, + onDataChange }: Props = $props() const uncap = $derived( @@ -57,6 +60,7 @@ editData.ulb = false editData.transcendence = false } + onDataChange?.() } function handleUlbChange(checked: boolean) { @@ -67,6 +71,7 @@ // Unchecking ULB should also uncheck Transcendence editData.transcendence = false } + onDataChange?.() } function handleTranscendenceChange(checked: boolean) { @@ -75,6 +80,7 @@ if (!editData.ulb) editData.ulb = true if (!editData.flb) editData.flb = true } + onDataChange?.() } diff --git a/src/lib/utils/images.ts b/src/lib/utils/images.ts index 85fa70c2..b8382aa4 100644 --- a/src/lib/utils/images.ts +++ b/src/lib/utils/images.ts @@ -389,6 +389,41 @@ export function getArtifactImage( return `${getBasePath()}/${directory}/${granblueId}.jpg` } +// ===== Game CDN Images ===== +// For new items not yet in our AWS CDN (used in batch import) + +const GAME_CDN_BASE = 'https://prd-game-a-granbluefantasy.akamaized.net/assets_en/img/sp/assets' + +/** + * Get character image from the game CDN + * Used for batch imports where images aren't yet in AWS + */ +export function getGameCdnCharacterImage( + id: string | number | null | undefined, + pose: string = '01' +): string { + if (!id) return getPlaceholderImage('character', 'square') + return `${GAME_CDN_BASE}/npc/s/${id}_${pose}.jpg` +} + +/** + * Get weapon image from the game CDN + * Used for batch imports where images aren't yet in AWS + */ +export function getGameCdnWeaponImage(id: string | number | null | undefined): string { + if (!id) return getPlaceholderImage('weapon', 'square') + return `${GAME_CDN_BASE}/weapon/s/${id}.jpg` +} + +/** + * Get summon image from the game CDN + * Used for batch imports where images aren't yet in AWS + */ +export function getGameCdnSummonImage(id: string | number | null | undefined): string { + if (!id) return getPlaceholderImage('summon', 'square') + return `${GAME_CDN_BASE}/summon/s/${id}.jpg` +} + // ===== Other Game Images ===== /** diff --git a/src/routes/(app)/database/summons/[id]/edit/+page.svelte b/src/routes/(app)/database/summons/[id]/edit/+page.svelte index 14a1abc7..9126f2f6 100644 --- a/src/routes/(app)/database/summons/[id]/edit/+page.svelte +++ b/src/routes/(app)/database/summons/[id]/edit/+page.svelte @@ -77,7 +77,8 @@ gamewith: '', kamigame: '', nicknamesEn: [] as string[], - nicknamesJp: [] as string[] + nicknamesJp: [] as string[], + promotions: [] as number[] }) // Populate edit data when summon loads @@ -89,7 +90,7 @@ granblueId: summon.granblueId || '', rarity: summon.rarity || 3, element: summon.element || 0, - series: summon.series?.toString() ?? '', + series: summon.series?.id || '', minHp: summon.hp?.minHp || 0, maxHp: summon.hp?.maxHp || 0, maxHpFlb: summon.hp?.maxHpFlb || 0, @@ -115,7 +116,8 @@ gamewith: summon.gamewith || '', kamigame: summon.kamigame || '', nicknamesEn: summon.nicknames?.en || [], - nicknamesJp: summon.nicknames?.ja || [] + nicknamesJp: summon.nicknames?.ja || [], + promotions: summon.promotions || [] } } }) @@ -159,7 +161,8 @@ gamewith: editData.gamewith, kamigame: editData.kamigame, nicknames_en: editData.nicknamesEn, - nicknames_jp: editData.nicknamesJp + nicknames_jp: editData.nicknamesJp, + promotions: editData.promotions } await entityAdapter.updateSummon(summon.id, payload) diff --git a/src/routes/(app)/database/summons/new/+page.svelte b/src/routes/(app)/database/summons/new/+page.svelte index f8fc2370..978da1f9 100644 --- a/src/routes/(app)/database/summons/new/+page.svelte +++ b/src/routes/(app)/database/summons/new/+page.svelte @@ -157,6 +157,7 @@ // Taxonomy element: editData.element, series: editData.series || undefined, + promotions: editData.promotions, // Stats - note: transcendence maps to xlb min_hp: editData.minHp,