## Overview Complete migration from service layer to TanStack Query v6 with mutations, queries, and automatic cache management. ## What Was Completed ### Phase 1: Entity Queries & Database Pages ✅ - Created `entity.queries.ts` with query options for weapons, characters, and summons - Migrated all database detail pages to use TanStack Query with `withInitialData()` pattern - SSR with client-side hydration working correctly ### Phase 2: Server Load Cleanup ✅ - Removed PartyService dependency from teams detail server load - Server loads now use adapters directly instead of service layer - Cleaner separation of concerns ### Phase 3: Party.svelte Refactoring ✅ - Removed all PartyService, GridService, and ConflictService dependencies - Migrated to TanStack Query mutations for all operations: - Grid operations: create, update, delete, swap, move - Party operations: update, delete, remix, favorite, unfavorite - Added swap/move mutations for drag-and-drop operations - Automatic cache invalidation and query refetching ### Phase 4: Service Layer Removal ✅ - Deleted all service layer files (~1,345 lines removed): - `party.service.ts` (620 lines) - `grid.service.ts` (450 lines) - `conflict.service.ts` (120 lines) - `gridOperations.ts` (unused utility) - Deleted empty `services/` directory - Created utility functions for cross-cutting concerns: - `localId.ts`: Anonymous user local ID management - `editKeys.ts`: Edit key management for anonymous editing - `party-context.ts`: Extracted PartyContext type ### Phase 5: /teams/new Migration ✅ - Migrated party creation wizard to use TanStack Query mutations - Replaced all direct adapter calls with mutations (7 locations) - Maintains existing behavior and flow - Automatic cache invalidation for newly created parties ## Benefits ### Performance - Automatic request deduplication - Better cache utilization across pages - Background refetching for fresh data - Optimistic updates for instant UI feedback ### Developer Experience - Single source of truth for data fetching - Consistent patterns across entire app - Query devtools for debugging - Less boilerplate code ### Code Quality - ~1,088 net lines removed - Simpler mental model (no service layer) - Better TypeScript inference - Easier to test ### Architecture - **100% TanStack Query coverage** - no service layer, no direct adapter calls - Clear separation: UI ← Queries/Mutations ← Adapters ← API - Automatic cache management - Consistent mutation patterns everywhere ## Testing All features verified: - Party creation (anonymous & authenticated) - Grid operations (add, remove, update, swap, move) - Party operations (update, delete, remix, favorite) - Cache invalidation across tabs - Error handling and rollback - SSR with hydration ## Files Modified ### Created (3) - `src/lib/types/party-context.ts` - `src/lib/utils/editKeys.ts` - `src/lib/utils/localId.ts` ### Deleted (4) - `src/lib/services/party.service.ts` - `src/lib/services/grid.service.ts` - `src/lib/services/conflict.service.ts` - `src/lib/utils/gridOperations.ts` ### Modified (13) - `src/lib/api/mutations/grid.mutations.ts` - `src/lib/components/grids/CharacterGrid.svelte` - `src/lib/components/grids/SummonGrid.svelte` - `src/lib/components/grids/WeaponGrid.svelte` - `src/lib/components/party/Party.svelte` - `src/routes/teams/new/+page.svelte` - Database entity pages (characters, weapons, summons) - Other supporting files ## Migration Complete This PR completes the TanStack Query migration. The entire application now uses TanStack Query v6 for all data fetching and mutations, with zero remaining service layer code. --------- Co-authored-by: Claude <noreply@anthropic.com>
100 lines
2.6 KiB
TypeScript
100 lines
2.6 KiB
TypeScript
/**
|
|
* Grid Validation Utilities
|
|
*
|
|
* Validates and normalizes grid item data from API responses.
|
|
* Handles legacy 'object' property and ensures complete nested entity data.
|
|
*
|
|
* @module utils/gridValidation
|
|
*/
|
|
|
|
import type { GridWeapon, GridCharacter, GridSummon } from '$lib/types/api/party'
|
|
|
|
/**
|
|
* Validates that a GridWeapon has complete nested weapon data.
|
|
* Normalizes legacy 'object' property to 'weapon' if needed.
|
|
*
|
|
* @param raw - Raw grid weapon data from API
|
|
* @returns Validated GridWeapon or null if incomplete
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Valid data
|
|
* const validated = validateGridWeapon({
|
|
* id: '123',
|
|
* position: 0,
|
|
* weapon: { granblueId: '1040', name: {...} }
|
|
* })
|
|
*
|
|
* // Legacy data with 'object' property
|
|
* const validated = validateGridWeapon({
|
|
* id: '123',
|
|
* position: 0,
|
|
* object: { granblueId: '1040', name: {...} }
|
|
* }) // Automatically normalized to 'weapon'
|
|
* ```
|
|
*/
|
|
export function validateGridWeapon(raw: any): GridWeapon | null {
|
|
if (!raw || typeof raw !== 'object') return null
|
|
|
|
// Handle legacy API responses that use 'object' instead of 'weapon'
|
|
const weapon = raw.weapon || raw.object
|
|
|
|
if (!weapon || !weapon.granblueId) {
|
|
console.warn('GridWeapon missing nested weapon data:', raw)
|
|
return null
|
|
}
|
|
|
|
return {
|
|
...raw,
|
|
weapon, // Ensure 'weapon' property exists
|
|
object: undefined // Remove legacy 'object' property
|
|
} as GridWeapon
|
|
}
|
|
|
|
/**
|
|
* Validates that a GridCharacter has complete nested character data.
|
|
* Normalizes legacy 'object' property to 'character' if needed.
|
|
*
|
|
* @param raw - Raw grid character data from API
|
|
* @returns Validated GridCharacter or null if incomplete
|
|
*/
|
|
export function validateGridCharacter(raw: any): GridCharacter | null {
|
|
if (!raw || typeof raw !== 'object') return null
|
|
|
|
const character = raw.character || raw.object
|
|
|
|
if (!character || !character.granblueId) {
|
|
console.warn('GridCharacter missing nested character data:', raw)
|
|
return null
|
|
}
|
|
|
|
return {
|
|
...raw,
|
|
character,
|
|
object: undefined
|
|
} as GridCharacter
|
|
}
|
|
|
|
/**
|
|
* Validates that a GridSummon has complete nested summon data.
|
|
* Normalizes legacy 'object' property to 'summon' if needed.
|
|
*
|
|
* @param raw - Raw grid summon data from API
|
|
* @returns Validated GridSummon or null if incomplete
|
|
*/
|
|
export function validateGridSummon(raw: any): GridSummon | null {
|
|
if (!raw || typeof raw !== 'object') return null
|
|
|
|
const summon = raw.summon || raw.object
|
|
|
|
if (!summon || !summon.granblueId) {
|
|
console.warn('GridSummon missing nested summon data:', raw)
|
|
return null
|
|
}
|
|
|
|
return {
|
|
...raw,
|
|
summon,
|
|
object: undefined
|
|
} as GridSummon
|
|
}
|