complete handleSave for weapon modifiers, update key select to use slug
This commit is contained in:
parent
c5f6963ca7
commit
a251240331
3 changed files with 137 additions and 45 deletions
|
|
@ -10,6 +10,7 @@
|
||||||
import AxSkillSelect from './edit/AxSkillSelect.svelte'
|
import AxSkillSelect from './edit/AxSkillSelect.svelte'
|
||||||
import Button from '$lib/components/ui/Button.svelte'
|
import Button from '$lib/components/ui/Button.svelte'
|
||||||
import { getElementIcon } from '$lib/utils/images'
|
import { getElementIcon } from '$lib/utils/images'
|
||||||
|
import { seriesHasWeaponKeys, getSeriesSlug } from '$lib/utils/weaponSeries'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
weapon: GridWeapon
|
weapon: GridWeapon
|
||||||
|
|
@ -42,23 +43,24 @@
|
||||||
// Weapon data shortcuts
|
// Weapon data shortcuts
|
||||||
const weaponData = $derived(weapon.weapon)
|
const weaponData = $derived(weapon.weapon)
|
||||||
const canChangeElement = $derived(weaponData?.element === 0)
|
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)
|
const transcendenceStep = $derived(weapon.transcendenceStep ?? 0)
|
||||||
|
|
||||||
// Weapon key config keyed by WEAPON series
|
// Weapon key slot configuration by series slug
|
||||||
// Maps weapon.series → { slots, keySeries } where keySeries is what weapon_keys API expects
|
// Maps series slug → number of weapon key slots
|
||||||
const WEAPON_KEY_SERIES: Record<number, { name: string; slots: number; keySeries: number }> = {
|
const WEAPON_KEY_SLOTS: Record<string, number> = {
|
||||||
2: { name: 'Dark Opus', slots: 2, keySeries: 3 }, // Pendulum (slot 0) + Chain/Pendulum (slot 1)
|
'dark-opus': 2, // 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)
|
'ultima': 3, // Gauph Key (slot 0) + Ultima Key (slot 1) + Gate (slot 2)
|
||||||
17: { name: 'Draconic', slots: 2, keySeries: 27 }, // Teluma (slot 0) + Teluma (slot 1)
|
'draconic': 2, // Teluma (slot 0) + Teluma (slot 1)
|
||||||
22: { name: 'Astral', slots: 1, keySeries: 19 }, // Emblem (slot 0)
|
'draconic-providence': 2, // Same as Draconic
|
||||||
34: { name: 'Superlative', slots: 2, keySeries: 40 } // Teluma (slot 0) + Teluma (slot 1)
|
'superlative': 2, // Teluma (slot 0) + Teluma (slot 1)
|
||||||
|
// Add more as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
const weaponKeyConfig = $derived(WEAPON_KEY_SERIES[series])
|
// Check if series has weapon keys using the utility (handles both formats)
|
||||||
const hasWeaponKeys = $derived(!!weaponKeyConfig)
|
const hasWeaponKeys = $derived(seriesHasWeaponKeys(series))
|
||||||
const keySlotCount = $derived(weaponKeyConfig?.slots ?? 0)
|
const keySlotCount = $derived(seriesSlug ? (WEAPON_KEY_SLOTS[seriesSlug] ?? 2) : 0)
|
||||||
const keySeries = $derived(weaponKeyConfig?.keySeries ?? 0)
|
|
||||||
|
|
||||||
const hasAxSkills = $derived(weaponData?.ax === true)
|
const hasAxSkills = $derived(weaponData?.ax === true)
|
||||||
const axType = $derived(weaponData?.axType ?? 1)
|
const axType = $derived(weaponData?.axType ?? 1)
|
||||||
|
|
@ -95,16 +97,92 @@
|
||||||
return '—'
|
return '—'
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
// Build the update payload for the API
|
||||||
const updates: Partial<GridWeapon> = {}
|
// 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) {
|
if (canChangeElement && element !== weapon.element) {
|
||||||
updates.element = 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<GridWeapon>)
|
||||||
|
} else {
|
||||||
|
// No changes, just close the panel
|
||||||
|
onCancel?.()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancel() {
|
function handleCancel() {
|
||||||
|
|
@ -148,12 +226,12 @@
|
||||||
</DetailsSection>
|
</DetailsSection>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if hasWeaponKeys}
|
{#if hasWeaponKeys && seriesSlug}
|
||||||
<DetailsSection title="Weapon Keys">
|
<DetailsSection title="Weapon Keys">
|
||||||
<div class="key-selects">
|
<div class="key-selects">
|
||||||
{#if keySlotCount >= 1}
|
{#if keySlotCount >= 1}
|
||||||
<WeaponKeySelect
|
<WeaponKeySelect
|
||||||
series={keySeries}
|
{seriesSlug}
|
||||||
slot={0}
|
slot={0}
|
||||||
bind:value={weaponKey1}
|
bind:value={weaponKey1}
|
||||||
{transcendenceStep}
|
{transcendenceStep}
|
||||||
|
|
@ -161,7 +239,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{#if keySlotCount >= 2}
|
{#if keySlotCount >= 2}
|
||||||
<WeaponKeySelect
|
<WeaponKeySelect
|
||||||
series={keySeries}
|
{seriesSlug}
|
||||||
slot={1}
|
slot={1}
|
||||||
bind:value={weaponKey2}
|
bind:value={weaponKey2}
|
||||||
{transcendenceStep}
|
{transcendenceStep}
|
||||||
|
|
@ -169,7 +247,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
{#if keySlotCount >= 3}
|
{#if keySlotCount >= 3}
|
||||||
<WeaponKeySelect
|
<WeaponKeySelect
|
||||||
series={keySeries}
|
{seriesSlug}
|
||||||
slot={2}
|
slot={2}
|
||||||
bind:value={weaponKey3}
|
bind:value={weaponKey3}
|
||||||
{transcendenceStep}
|
{transcendenceStep}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createQuery } from '@tanstack/svelte-query'
|
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 type { WeaponKey } from '$lib/api/adapters/entity.adapter'
|
||||||
import Select from '$lib/components/ui/Select.svelte'
|
import Select from '$lib/components/ui/Select.svelte'
|
||||||
|
import { queryOptions } from '@tanstack/svelte-query'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** The weapon series (determines which keys are available) */
|
/** The weapon series slug (determines which keys are available) */
|
||||||
series: number
|
seriesSlug?: string
|
||||||
/** The slot number (1, 2, or 3) */
|
/** The slot number (0, 1, or 2) */
|
||||||
slot: number
|
slot: number
|
||||||
/** Currently selected weapon key ID */
|
/** Currently selected weapon key ID */
|
||||||
value?: string
|
value?: string
|
||||||
|
|
@ -17,42 +18,54 @@
|
||||||
transcendenceStep?: number
|
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)
|
// Key type names based on series slug and slot (0-based indexing)
|
||||||
const KEY_TYPE_NAMES: Record<number, Record<number, { en: string; ja: string }>> = {
|
const KEY_TYPE_NAMES: Record<string, Record<number, { en: string; ja: string }>> = {
|
||||||
// Dark Opus (series 3)
|
// Dark Opus
|
||||||
3: {
|
'dark-opus': {
|
||||||
0: { en: 'Pendulum', ja: 'ペンデュラム' },
|
0: { en: 'Pendulum', ja: 'ペンデュラム' },
|
||||||
1: { en: 'Pendulum/Chain', ja: 'ペンデュラム/チェイン' }
|
1: { en: 'Pendulum/Chain', ja: 'ペンデュラム/チェイン' }
|
||||||
},
|
},
|
||||||
// Draconic (series 27)
|
// Draconic
|
||||||
27: {
|
'draconic': {
|
||||||
0: { en: 'Teluma', ja: 'テルマ' },
|
0: { en: 'Teluma', ja: 'テルマ' },
|
||||||
1: { en: 'Teluma', ja: 'テルマ' }
|
1: { en: 'Teluma', ja: 'テルマ' }
|
||||||
},
|
},
|
||||||
// Ultima (series 13)
|
// Draconic Providence
|
||||||
13: {
|
'draconic-providence': {
|
||||||
|
0: { en: 'Teluma', ja: 'テルマ' },
|
||||||
|
1: { en: 'Teluma', ja: 'テルマ' }
|
||||||
|
},
|
||||||
|
// Ultima
|
||||||
|
'ultima': {
|
||||||
0: { en: 'Gauph Key', ja: 'ガフスキー' },
|
0: { en: 'Gauph Key', ja: 'ガフスキー' },
|
||||||
1: { en: 'Ultima Key', ja: 'ガフスキーΩ' },
|
1: { en: 'Ultima Key', ja: 'ガフスキーΩ' },
|
||||||
2: { en: 'Gate of Omnipotence', ja: 'ガフスキー' }
|
2: { en: 'Gate of Omnipotence', ja: 'ガフスキー' }
|
||||||
},
|
},
|
||||||
// Astral (series 19)
|
// Superlative
|
||||||
19: {
|
'superlative': {
|
||||||
0: { en: 'Emblem', ja: 'エンブレム' }
|
|
||||||
},
|
|
||||||
// Superlative (series 40)
|
|
||||||
40: {
|
|
||||||
0: { en: 'Teluma', ja: 'テルマ' },
|
0: { en: 'Teluma', ja: 'テルマ' },
|
||||||
1: { en: 'Teluma', ja: 'テルマ' }
|
1: { en: 'Teluma', ja: 'テルマ' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch weapon keys for this series and slot
|
// Fetch weapon keys for this series slug and slot
|
||||||
const weaponKeysQuery = createQuery(() => entityQueries.weaponKeys({ series, 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
|
// 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
|
// Group and sort weapon keys
|
||||||
const groupedOptions = $derived.by(() => {
|
const groupedOptions = $derived.by(() => {
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
import { getWeaponKeyImages } from '$lib/utils/modifiers'
|
import { getWeaponKeyImages } from '$lib/utils/modifiers'
|
||||||
import type { WeaponKey } from '$lib/types/api/entities'
|
import type { WeaponKey } from '$lib/types/api/entities'
|
||||||
import type { LocalizedName } from '$lib/types/api/entities'
|
import type { LocalizedName } from '$lib/types/api/entities'
|
||||||
|
import type { WeaponSeriesRef } from '$lib/types/api/weaponSeries'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
weaponKeys?: WeaponKey[]
|
weaponKeys?: WeaponKey[]
|
||||||
weaponData?: {
|
weaponData?: {
|
||||||
element?: number
|
element?: number
|
||||||
proficiency?: number | number[]
|
proficiency?: number | number[]
|
||||||
series?: number
|
series?: WeaponSeriesRef | null
|
||||||
name?: LocalizedName
|
name?: LocalizedName
|
||||||
}
|
}
|
||||||
layout?: 'list' | 'grid'
|
layout?: 'list' | 'grid'
|
||||||
|
|
@ -32,7 +33,7 @@
|
||||||
return key.slug || 'Weapon Key'
|
return key.slug || 'Weapon Key'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSlotLabel(slot: number, series?: number): string {
|
function getSlotLabel(slot: number, series?: WeaponSeriesRef | null): string {
|
||||||
return `Skill ${slot + 1}`
|
return `Skill ${slot + 1}`
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue