diff --git a/src/lib/components/sidebar/EditWeaponSidebar.svelte b/src/lib/components/sidebar/EditWeaponSidebar.svelte index 027eedd6..1b2e30a4 100644 --- a/src/lib/components/sidebar/EditWeaponSidebar.svelte +++ b/src/lib/components/sidebar/EditWeaponSidebar.svelte @@ -10,6 +10,7 @@ import AxSkillSelect from './edit/AxSkillSelect.svelte' import Button from '$lib/components/ui/Button.svelte' import { getElementIcon } from '$lib/utils/images' + import { seriesHasWeaponKeys, getSeriesSlug } from '$lib/utils/weaponSeries' interface Props { weapon: GridWeapon @@ -42,23 +43,24 @@ // Weapon data shortcuts const weaponData = $derived(weapon.weapon) const canChangeElement = $derived(weaponData?.element === 0) - const series = $derived(weaponData?.series ?? 0) + const series = $derived(weaponData?.series) + const seriesSlug = $derived(getSeriesSlug(series)) const transcendenceStep = $derived(weapon.transcendenceStep ?? 0) - // Weapon key config keyed by WEAPON series - // Maps weapon.series → { slots, keySeries } where keySeries is what weapon_keys API expects - const WEAPON_KEY_SERIES: Record = { - 2: { name: 'Dark Opus', slots: 2, keySeries: 3 }, // Pendulum (slot 0) + Chain/Pendulum (slot 1) - 3: { name: 'Ultima', slots: 3, keySeries: 13 }, // Gauph Key (slot 0) + Ultima Key (slot 1) + Gate (slot 2) - 17: { name: 'Draconic', slots: 2, keySeries: 27 }, // Teluma (slot 0) + Teluma (slot 1) - 22: { name: 'Astral', slots: 1, keySeries: 19 }, // Emblem (slot 0) - 34: { name: 'Superlative', slots: 2, keySeries: 40 } // Teluma (slot 0) + Teluma (slot 1) + // Weapon key slot configuration by series slug + // Maps series slug → number of weapon key slots + const WEAPON_KEY_SLOTS: Record = { + 'dark-opus': 2, // Pendulum (slot 0) + Chain/Pendulum (slot 1) + 'ultima': 3, // Gauph Key (slot 0) + Ultima Key (slot 1) + Gate (slot 2) + 'draconic': 2, // Teluma (slot 0) + Teluma (slot 1) + 'draconic-providence': 2, // Same as Draconic + 'superlative': 2, // Teluma (slot 0) + Teluma (slot 1) + // Add more as needed } - const weaponKeyConfig = $derived(WEAPON_KEY_SERIES[series]) - const hasWeaponKeys = $derived(!!weaponKeyConfig) - const keySlotCount = $derived(weaponKeyConfig?.slots ?? 0) - const keySeries = $derived(weaponKeyConfig?.keySeries ?? 0) + // Check if series has weapon keys using the utility (handles both formats) + const hasWeaponKeys = $derived(seriesHasWeaponKeys(series)) + const keySlotCount = $derived(seriesSlug ? (WEAPON_KEY_SLOTS[seriesSlug] ?? 2) : 0) const hasAxSkills = $derived(weaponData?.ax === true) const axType = $derived(weaponData?.axType ?? 1) @@ -95,16 +97,92 @@ return '—' } - function handleSave() { - const updates: Partial = {} + // Build the update payload for the API + // Uses flat key IDs rather than nested arrays, as expected by the API + interface WeaponUpdatePayload { + element?: number + weaponKey1Id?: string | null + weaponKey2Id?: string | null + weaponKey3Id?: string | null + awakeningId?: string | null + awakeningLevel?: number + axModifier1?: number | null + axStrength1?: number | null + axModifier2?: number | null + axStrength2?: number | null + } + function handleSave() { + const updates: WeaponUpdatePayload = {} + + // Element change (only for element-changeable weapons) if (canChangeElement && element !== weapon.element) { updates.element = element } - // TODO: Add weapon keys, AX skills, awakening updates + // Weapon keys - send individual key IDs + if (hasWeaponKeys) { + const originalKey1 = weapon.weaponKeys?.[0]?.id + const originalKey2 = weapon.weaponKeys?.[1]?.id + const originalKey3 = weapon.weaponKeys?.[2]?.id - onSave?.(updates) + if (weaponKey1 !== originalKey1) { + updates.weaponKey1Id = weaponKey1 ?? null + } + if (weaponKey2 !== originalKey2) { + updates.weaponKey2Id = weaponKey2 ?? null + } + if (weaponKey3 !== originalKey3) { + updates.weaponKey3Id = weaponKey3 ?? null + } + } + + // Awakening - send awakening ID and level + if (hasAwakening) { + const originalAwakeningId = weapon.awakening?.type?.id + const originalLevel = weapon.awakening?.level ?? 1 + + if (selectedAwakening?.id !== originalAwakeningId) { + updates.awakeningId = selectedAwakening?.id ?? null + } + if (awakeningLevel !== originalLevel) { + updates.awakeningLevel = awakeningLevel + } + } + + // AX skills - send modifier/strength pairs + if (hasAxSkills) { + const originalAx = weapon.ax ?? [ + { modifier: -1, strength: 0 }, + { modifier: -1, strength: 0 } + ] + + const ax1 = axSkills[0] + const ax2 = axSkills[1] + const origAx1 = originalAx[0] + const origAx2 = originalAx[1] + + if (ax1?.modifier !== origAx1?.modifier) { + updates.axModifier1 = ax1?.modifier ?? null + } + if (ax1?.strength !== origAx1?.strength) { + updates.axStrength1 = ax1?.strength ?? null + } + if (ax2?.modifier !== origAx2?.modifier) { + updates.axModifier2 = ax2?.modifier ?? null + } + if (ax2?.strength !== origAx2?.strength) { + updates.axStrength2 = ax2?.strength ?? null + } + } + + // Only call onSave if there are actual updates + if (Object.keys(updates).length > 0) { + onSave?.(updates as Partial) + } else { + // No changes, just close the panel + onCancel?.() + } } function handleCancel() { @@ -148,12 +226,12 @@ {/if} - {#if hasWeaponKeys} + {#if hasWeaponKeys && seriesSlug}
{#if keySlotCount >= 1} = 2} = 3} import { createQuery } from '@tanstack/svelte-query' - import { entityQueries } from '$lib/api/queries/entity.queries' + import { entityAdapter } from '$lib/api/adapters/entity.adapter' import type { WeaponKey } from '$lib/api/adapters/entity.adapter' import Select from '$lib/components/ui/Select.svelte' + import { queryOptions } from '@tanstack/svelte-query' interface Props { - /** The weapon series (determines which keys are available) */ - series: number - /** The slot number (1, 2, or 3) */ + /** The weapon series slug (determines which keys are available) */ + seriesSlug?: string + /** The slot number (0, 1, or 2) */ slot: number /** Currently selected weapon key ID */ value?: string @@ -17,42 +18,54 @@ transcendenceStep?: number } - let { series, slot, value = $bindable(), onchange, transcendenceStep = 0 }: Props = $props() + let { seriesSlug, slot, value = $bindable(), onchange, transcendenceStep = 0 }: Props = $props() - // Key type names based on series and slot (0-based indexing) - const KEY_TYPE_NAMES: Record> = { - // Dark Opus (series 3) - 3: { + // Key type names based on series slug and slot (0-based indexing) + const KEY_TYPE_NAMES: Record> = { + // Dark Opus + 'dark-opus': { 0: { en: 'Pendulum', ja: 'ペンデュラム' }, 1: { en: 'Pendulum/Chain', ja: 'ペンデュラム/チェイン' } }, - // Draconic (series 27) - 27: { + // Draconic + 'draconic': { 0: { en: 'Teluma', ja: 'テルマ' }, 1: { en: 'Teluma', ja: 'テルマ' } }, - // Ultima (series 13) - 13: { + // Draconic Providence + 'draconic-providence': { + 0: { en: 'Teluma', ja: 'テルマ' }, + 1: { en: 'Teluma', ja: 'テルマ' } + }, + // Ultima + 'ultima': { 0: { en: 'Gauph Key', ja: 'ガフスキー' }, 1: { en: 'Ultima Key', ja: 'ガフスキーΩ' }, 2: { en: 'Gate of Omnipotence', ja: 'ガフスキー' } }, - // Astral (series 19) - 19: { - 0: { en: 'Emblem', ja: 'エンブレム' } - }, - // Superlative (series 40) - 40: { + // Superlative + 'superlative': { 0: { en: 'Teluma', ja: 'テルマ' }, 1: { en: 'Teluma', ja: 'テルマ' } } } - // Fetch weapon keys for this series and slot - const weaponKeysQuery = createQuery(() => entityQueries.weaponKeys({ series, slot })) + // Fetch weapon keys for this series slug and slot + const weaponKeysQuery = createQuery(() => + queryOptions({ + queryKey: ['weaponKeys', 'slug', seriesSlug, slot] as const, + queryFn: async () => { + if (!seriesSlug) return [] + return entityAdapter.getWeaponKeys({ seriesSlug, slot }) + }, + enabled: !!seriesSlug, + staleTime: 1000 * 60 * 60, + gcTime: 1000 * 60 * 60 * 24 + }) + ) // Get the key type name for this series/slot - const keyTypeName = $derived(KEY_TYPE_NAMES[series]?.[slot]?.en ?? 'Key') + const keyTypeName = $derived(seriesSlug ? (KEY_TYPE_NAMES[seriesSlug]?.[slot]?.en ?? 'Key') : 'Key') // Group and sort weapon keys const groupedOptions = $derived.by(() => { diff --git a/src/lib/components/sidebar/modifications/WeaponKeysList.svelte b/src/lib/components/sidebar/modifications/WeaponKeysList.svelte index dde647f0..1b75ba10 100644 --- a/src/lib/components/sidebar/modifications/WeaponKeysList.svelte +++ b/src/lib/components/sidebar/modifications/WeaponKeysList.svelte @@ -2,13 +2,14 @@ import { getWeaponKeyImages } from '$lib/utils/modifiers' import type { WeaponKey } from '$lib/types/api/entities' import type { LocalizedName } from '$lib/types/api/entities' + import type { WeaponSeriesRef } from '$lib/types/api/weaponSeries' interface Props { weaponKeys?: WeaponKey[] weaponData?: { element?: number proficiency?: number | number[] - series?: number + series?: WeaponSeriesRef | null name?: LocalizedName } layout?: 'list' | 'grid' @@ -32,7 +33,7 @@ return key.slug || 'Weapon Key' } - function getSlotLabel(slot: number, series?: number): string { + function getSlotLabel(slot: number, series?: WeaponSeriesRef | null): string { return `Skill ${slot + 1}` }