migrate /teams/new to query-based architecture
This commit is contained in:
parent
d0f7b3b89a
commit
f66b31fea8
1 changed files with 108 additions and 149 deletions
|
|
@ -13,13 +13,22 @@
|
||||||
import { partyAdapter, gridAdapter } from '$lib/api/adapters'
|
import { partyAdapter, gridAdapter } from '$lib/api/adapters'
|
||||||
import { getLocalId } from '$lib/utils/localId'
|
import { getLocalId } from '$lib/utils/localId'
|
||||||
import { storeEditKey } from '$lib/utils/editKeys'
|
import { storeEditKey } from '$lib/utils/editKeys'
|
||||||
|
import type { Party } from '$lib/types/api/party'
|
||||||
|
|
||||||
|
// TanStack Query
|
||||||
|
import { createQuery, useQueryClient } from '@tanstack/svelte-query'
|
||||||
|
import { partyQueries } from '$lib/api/queries/party.queries'
|
||||||
|
import { partyKeys } from '$lib/api/queries/party.queries'
|
||||||
|
|
||||||
// TanStack Query mutations
|
// TanStack Query mutations
|
||||||
import { useCreateParty } from '$lib/api/mutations/party.mutations'
|
import { useCreateParty } from '$lib/api/mutations/party.mutations'
|
||||||
import {
|
import {
|
||||||
useCreateGridWeapon,
|
useCreateGridWeapon,
|
||||||
useCreateGridCharacter,
|
useCreateGridCharacter,
|
||||||
useCreateGridSummon
|
useCreateGridSummon,
|
||||||
|
useDeleteGridWeapon,
|
||||||
|
useDeleteGridCharacter,
|
||||||
|
useDeleteGridSummon
|
||||||
} from '$lib/api/mutations/grid.mutations'
|
} from '$lib/api/mutations/grid.mutations'
|
||||||
import { Dialog } from 'bits-ui'
|
import { Dialog } from 'bits-ui'
|
||||||
import { replaceState } from '$app/navigation'
|
import { replaceState } from '$app/navigation'
|
||||||
|
|
@ -73,19 +82,42 @@
|
||||||
return characters.length >= 5
|
return characters.length >= 5
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grid state
|
|
||||||
let weapons = $state<any[]>([])
|
|
||||||
let summons = $state<any[]>([])
|
|
||||||
let characters = $state<any[]>([])
|
|
||||||
let selectedSlot = $state<number | null>(null)
|
|
||||||
let isFirstItemForSlot = false // Track if this is the first item after clicking empty cell
|
|
||||||
|
|
||||||
// Party state
|
// Party state
|
||||||
let partyId = $state<string | null>(null)
|
let partyId = $state<string | null>(null)
|
||||||
let shortcode = $state<string | null>(null)
|
let shortcode = $state<string | null>(null)
|
||||||
let editKey = $state<string | null>(null)
|
let editKey = $state<string | null>(null)
|
||||||
let isCreatingParty = $state(false)
|
let isCreatingParty = $state(false)
|
||||||
|
|
||||||
|
// Placeholder party for 'new' route
|
||||||
|
const placeholderParty: Party = {
|
||||||
|
id: 'new',
|
||||||
|
shortcode: 'new',
|
||||||
|
name: 'New Team',
|
||||||
|
description: '',
|
||||||
|
weapons: [],
|
||||||
|
summons: [],
|
||||||
|
characters: [],
|
||||||
|
element: 0,
|
||||||
|
visibility: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create query with placeholder data
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const partyQuery = createQuery(() => ({
|
||||||
|
...partyQueries.byShortcode(shortcode || 'new'),
|
||||||
|
initialData: placeholderParty,
|
||||||
|
enabled: false // Disable automatic fetching for 'new' party
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Derive state from query
|
||||||
|
const party = $derived(partyQuery.data ?? placeholderParty)
|
||||||
|
const weapons = $derived(party.weapons ?? [])
|
||||||
|
const summons = $derived(party.summons ?? [])
|
||||||
|
const characters = $derived(party.characters ?? [])
|
||||||
|
|
||||||
|
let selectedSlot = $state<number | null>(null)
|
||||||
|
let isFirstItemForSlot = false // Track if this is the first item after clicking empty cell
|
||||||
|
|
||||||
// Error dialog state
|
// Error dialog state
|
||||||
let errorDialogOpen = $state(false)
|
let errorDialogOpen = $state(false)
|
||||||
let errorMessage = $state('')
|
let errorMessage = $state('')
|
||||||
|
|
@ -96,6 +128,22 @@
|
||||||
const createWeaponMutation = useCreateGridWeapon()
|
const createWeaponMutation = useCreateGridWeapon()
|
||||||
const createCharacterMutation = useCreateGridCharacter()
|
const createCharacterMutation = useCreateGridCharacter()
|
||||||
const createSummonMutation = useCreateGridSummon()
|
const createSummonMutation = useCreateGridSummon()
|
||||||
|
const deleteWeapon = useDeleteGridWeapon()
|
||||||
|
const deleteCharacter = useDeleteGridCharacter()
|
||||||
|
const deleteSummon = useDeleteGridSummon()
|
||||||
|
|
||||||
|
// Helper to add item to cache
|
||||||
|
function addItemToCache(itemType: 'weapons' | 'summons' | 'characters', item: any) {
|
||||||
|
const cacheKey = partyKeys.detail(shortcode || 'new')
|
||||||
|
|
||||||
|
queryClient.setQueryData(cacheKey, (old: Party | undefined) => {
|
||||||
|
if (!old) return placeholderParty
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
[itemType]: [...(old[itemType] ?? []), item]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate if grids are full
|
// Calculate if grids are full
|
||||||
let isWeaponGridFull = $derived(weapons.length >= 10) // 1 mainhand + 9 grid slots
|
let isWeaponGridFull = $derived(weapons.length >= 10) // 1 mainhand + 9 grid slots
|
||||||
|
|
@ -156,6 +204,12 @@
|
||||||
throw new Error('Party creation did not return ID or shortcode')
|
throw new Error('Party creation did not return ID or shortcode')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the query cache with the created party
|
||||||
|
queryClient.setQueryData(
|
||||||
|
partyKeys.detail(createdParty.shortcode),
|
||||||
|
createdParty
|
||||||
|
)
|
||||||
|
|
||||||
// Step 2: Add the first item to the party
|
// Step 2: Add the first item to the party
|
||||||
let position = selectedSlot !== null ? selectedSlot : -1 // Use selectedSlot if available
|
let position = selectedSlot !== null ? selectedSlot : -1 // Use selectedSlot if available
|
||||||
let itemAdded = false
|
let itemAdded = false
|
||||||
|
|
@ -174,8 +228,8 @@
|
||||||
console.log('Weapon added:', addResult)
|
console.log('Weapon added:', addResult)
|
||||||
itemAdded = true
|
itemAdded = true
|
||||||
|
|
||||||
// Update local state with the added weapon
|
// Update cache with the added weapon
|
||||||
weapons = [...weapons, addResult]
|
addItemToCache('weapons', addResult)
|
||||||
} else if (activeTab === GridType.Summon) {
|
} else if (activeTab === GridType.Summon) {
|
||||||
// Use selectedSlot if available, otherwise default to main summon
|
// Use selectedSlot if available, otherwise default to main summon
|
||||||
if (selectedSlot === null) position = -1
|
if (selectedSlot === null) position = -1
|
||||||
|
|
@ -189,8 +243,8 @@
|
||||||
console.log('Summon added:', addResult)
|
console.log('Summon added:', addResult)
|
||||||
itemAdded = true
|
itemAdded = true
|
||||||
|
|
||||||
// Update local state with the added summon
|
// Update cache with the added summon
|
||||||
summons = [...summons, addResult]
|
addItemToCache('summons', addResult)
|
||||||
} else if (activeTab === GridType.Character) {
|
} else if (activeTab === GridType.Character) {
|
||||||
// Use selectedSlot if available, otherwise default to first slot
|
// Use selectedSlot if available, otherwise default to first slot
|
||||||
if (selectedSlot === null) position = 0
|
if (selectedSlot === null) position = 0
|
||||||
|
|
@ -202,8 +256,8 @@
|
||||||
console.log('Character added:', addResult)
|
console.log('Character added:', addResult)
|
||||||
itemAdded = true
|
itemAdded = true
|
||||||
|
|
||||||
// Update local state with the added character
|
// Update cache with the added character
|
||||||
characters = [...characters, addResult]
|
addItemToCache('characters', addResult)
|
||||||
}
|
}
|
||||||
selectedSlot = null // Reset after using
|
selectedSlot = null // Reset after using
|
||||||
|
|
||||||
|
|
@ -290,8 +344,8 @@
|
||||||
mainhand: position === -1
|
mainhand: position === -1
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add to local state
|
// Add to cache
|
||||||
weapons = [...weapons, response]
|
addItemToCache('weapons', response)
|
||||||
} else if (activeTab === GridType.Summon) {
|
} else if (activeTab === GridType.Summon) {
|
||||||
// Use selectedSlot for first item if available
|
// Use selectedSlot for first item if available
|
||||||
if (i === 0 && selectedSlot !== null && !summons.find(s => s.position === selectedSlot)) {
|
if (i === 0 && selectedSlot !== null && !summons.find(s => s.position === selectedSlot)) {
|
||||||
|
|
@ -314,8 +368,8 @@
|
||||||
friend: position === 6
|
friend: position === 6
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add to local state
|
// Add to cache
|
||||||
summons = [...summons, response]
|
addItemToCache('summons', response)
|
||||||
} else if (activeTab === GridType.Character) {
|
} else if (activeTab === GridType.Character) {
|
||||||
// Use selectedSlot for first item if available
|
// Use selectedSlot for first item if available
|
||||||
if (i === 0 && selectedSlot !== null && !characters.find(c => c.position === selectedSlot)) {
|
if (i === 0 && selectedSlot !== null && !characters.find(c => c.position === selectedSlot)) {
|
||||||
|
|
@ -336,8 +390,8 @@
|
||||||
position
|
position
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add to local state
|
// Add to cache
|
||||||
characters = [...characters, response]
|
addItemToCache('characters', response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
@ -348,143 +402,48 @@
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Original local-only adding logic (before party creation)
|
|
||||||
if (activeTab === GridType.Weapon) {
|
|
||||||
// Add weapons to empty slots
|
|
||||||
const emptySlots = Array.from({ length: 10 }, (_, i) => i - 1) // -1 for mainhand, 0-8 for grid
|
|
||||||
.filter(i => !weapons.find(w => w.position === i))
|
|
||||||
|
|
||||||
items.forEach((item, index) => {
|
|
||||||
let position: number
|
|
||||||
// Use selectedSlot for first item if available
|
|
||||||
if (index === 0 && selectedSlot !== null && !weapons.find(w => w.position === selectedSlot)) {
|
|
||||||
position = selectedSlot
|
|
||||||
selectedSlot = null // Reset after using
|
|
||||||
} else {
|
|
||||||
// Find next empty slot
|
|
||||||
const availableSlots = emptySlots.filter(s => !weapons.find(w => w.position === s))
|
|
||||||
if (availableSlots.length === 0) return
|
|
||||||
position = availableSlots[0]!
|
|
||||||
}
|
|
||||||
|
|
||||||
const newWeapon = {
|
|
||||||
id: `temp-${Date.now()}-${index}`,
|
|
||||||
position,
|
|
||||||
object: {
|
|
||||||
granblueId: item.granblueId,
|
|
||||||
name: item.name,
|
|
||||||
element: item.element
|
|
||||||
},
|
|
||||||
mainhand: position === -1
|
|
||||||
}
|
|
||||||
console.log('Adding weapon:', newWeapon)
|
|
||||||
weapons = [...weapons, newWeapon]
|
|
||||||
})
|
|
||||||
console.log('Updated weapons array:', weapons)
|
|
||||||
} else if (activeTab === GridType.Summon) {
|
|
||||||
// Add summons to empty slots
|
|
||||||
const emptySlots = [-1, 0, 1, 2, 3, 6] // main, 4 grid slots, friend
|
|
||||||
.filter(i => !summons.find(s => s.position === i))
|
|
||||||
|
|
||||||
items.forEach((item, index) => {
|
|
||||||
let position: number
|
|
||||||
// Use selectedSlot for first item if available
|
|
||||||
if (index === 0 && selectedSlot !== null && !summons.find(s => s.position === selectedSlot)) {
|
|
||||||
position = selectedSlot
|
|
||||||
selectedSlot = null // Reset after using
|
|
||||||
} else {
|
|
||||||
// Find next empty slot
|
|
||||||
const availableSlots = emptySlots.filter(s => !summons.find(sum => sum.position === s))
|
|
||||||
if (availableSlots.length === 0) return
|
|
||||||
position = availableSlots[0]!
|
|
||||||
}
|
|
||||||
|
|
||||||
summons = [...summons, {
|
|
||||||
id: `temp-${Date.now()}-${index}`,
|
|
||||||
position,
|
|
||||||
object: {
|
|
||||||
granblueId: item.granblueId,
|
|
||||||
name: item.name,
|
|
||||||
element: item.element
|
|
||||||
},
|
|
||||||
main: position === -1,
|
|
||||||
friend: position === 6
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
} else if (activeTab === GridType.Character) {
|
|
||||||
// Add characters to empty slots
|
|
||||||
const emptySlots = Array.from({ length: 5 }, (_, i) => i)
|
|
||||||
.filter(i => !characters.find(c => c.position === i))
|
|
||||||
|
|
||||||
items.forEach((item, index) => {
|
|
||||||
let position: number
|
|
||||||
// Use selectedSlot for first item if available
|
|
||||||
if (index === 0 && selectedSlot !== null && !characters.find(c => c.position === selectedSlot)) {
|
|
||||||
position = selectedSlot
|
|
||||||
selectedSlot = null // Reset after using
|
|
||||||
} else {
|
|
||||||
// Find next empty slot
|
|
||||||
const availableSlots = emptySlots.filter(s => !characters.find(c => c.position === s))
|
|
||||||
if (availableSlots.length === 0) return
|
|
||||||
position = availableSlots[0]!
|
|
||||||
}
|
|
||||||
|
|
||||||
characters = [...characters, {
|
|
||||||
id: `temp-${Date.now()}-${index}`,
|
|
||||||
position,
|
|
||||||
object: {
|
|
||||||
granblueId: item.granblueId,
|
|
||||||
name: item.name,
|
|
||||||
element: item.element
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove functions
|
|
||||||
function removeWeapon(itemId: string) {
|
|
||||||
console.log('Removing weapon:', itemId)
|
|
||||||
weapons = weapons.filter(w => w.id !== itemId)
|
|
||||||
return Promise.resolve({ id: 'new', shortcode: 'new', weapons, summons, characters })
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeSummon(itemId: string) {
|
// Provide party context using query data
|
||||||
console.log('Removing summon:', itemId)
|
|
||||||
summons = summons.filter(s => s.id !== itemId)
|
|
||||||
return Promise.resolve({ id: 'new', shortcode: 'new', weapons, summons, characters })
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeCharacter(itemId: string) {
|
|
||||||
console.log('Removing character:', itemId)
|
|
||||||
characters = characters.filter(c => c.id !== itemId)
|
|
||||||
return Promise.resolve({ id: 'new', shortcode: 'new', weapons, summons, characters })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provide a minimal party context so Unit components can render safely.
|
|
||||||
setContext('party', {
|
setContext('party', {
|
||||||
getParty: () => ({ id: 'new', shortcode: 'new', weapons, summons, characters }),
|
getParty: () => party,
|
||||||
updateParty: (updatedParty: any) => {
|
updateParty: (p: Party) => {
|
||||||
// Update the local state when party is updated
|
// Update cache instead of local state
|
||||||
if (updatedParty.weapons) weapons = updatedParty.weapons
|
queryClient.setQueryData(partyKeys.detail(shortcode || 'new'), p)
|
||||||
if (updatedParty.summons) summons = updatedParty.summons
|
|
||||||
if (updatedParty.characters) characters = updatedParty.characters
|
|
||||||
},
|
},
|
||||||
canEdit: () => true,
|
canEdit: () => true,
|
||||||
|
getEditKey: () => editKey,
|
||||||
services: {
|
services: {
|
||||||
gridService: {
|
gridService: {
|
||||||
removeWeapon: (partyId: string, itemId: string) => removeWeapon(itemId),
|
removeWeapon: async (partyId: string, itemId: string) => {
|
||||||
removeSummon: (partyId: string, itemId: string) => removeSummon(itemId),
|
if (!partyId || partyId === 'new') return party
|
||||||
removeCharacter: (partyId: string, itemId: string) => removeCharacter(itemId),
|
await deleteWeapon.mutateAsync({
|
||||||
addWeapon: () => Promise.resolve({ party: { id: 'new', shortcode: 'new', weapons, summons, characters } }),
|
id: itemId,
|
||||||
addSummon: () => Promise.resolve({ party: { id: 'new', shortcode: 'new', weapons, summons, characters } }),
|
partyId,
|
||||||
addCharacter: () => Promise.resolve({ party: { id: 'new', shortcode: 'new', weapons, summons, characters } }),
|
partyShortcode: shortcode || 'new'
|
||||||
replaceWeapon: () => Promise.resolve({ party: { id: 'new', shortcode: 'new', weapons, summons, characters } }),
|
})
|
||||||
replaceSummon: () => Promise.resolve({ party: { id: 'new', shortcode: 'new', weapons, summons, characters } }),
|
return party
|
||||||
replaceCharacter: () => Promise.resolve({ party: { id: 'new', shortcode: 'new', weapons, summons, characters } })
|
},
|
||||||
},
|
removeSummon: async (partyId: string, itemId: string) => {
|
||||||
partyService: { getEditKey: () => null }
|
if (!partyId || partyId === 'new') return party
|
||||||
|
await deleteSummon.mutateAsync({
|
||||||
|
id: itemId,
|
||||||
|
partyId,
|
||||||
|
partyShortcode: shortcode || 'new'
|
||||||
|
})
|
||||||
|
return party
|
||||||
|
},
|
||||||
|
removeCharacter: async (partyId: string, itemId: string) => {
|
||||||
|
if (!partyId || partyId === 'new') return party
|
||||||
|
await deleteCharacter.mutateAsync({
|
||||||
|
id: itemId,
|
||||||
|
partyId,
|
||||||
|
partyShortcode: shortcode || 'new'
|
||||||
|
})
|
||||||
|
return party
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
openPicker: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => {
|
openPicker: (opts: { type: 'weapon' | 'summon' | 'character'; position: number; item?: any }) => {
|
||||||
selectedSlot = opts.position
|
selectedSlot = opts.position
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue