- CollectionArtifactCard for grid view
- CollectionArtifactRow for list view
- CollectionArtifactPane for sidebar details
- artifacts collection page with filters and infinite scroll
- getArtifactImage util
- update collection layout for artifacts tab
The addWeapons/addSummons methods were using Promise.all with Array.fill()
which created arrays where all elements referenced the same object. This
caused the request deduplication logic in BaseAdapter to cancel previous
requests since they all had the same body/requestId.
Fix:
- Use Array.from() with spread to create unique object instances
- Execute requests sequentially to avoid deduplication conflicts
- Improve error handling in AddToCollectionModal to filter CancelledErrors
Types, adapter, queries, and mutations for managing user collections
(characters, weapons, summons, job accessories). Supports both private
collection management and public collection viewing with privacy.
Add updateCharacter(), updateSummon(), and updateWeapon() methods to
entity adapter. Each method uses PATCH and clears the entity cache
after successful update.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backfill nicknames support to character creation page:
- Add nicknamesEn and nicknamesJp fields to editData
- Import and use TagInput component for nickname entry
- Add nicknames section to form UI
- Update CreateCharacterPayload interface with nickname fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- new page with granblue_id validation
- role check (>= 7) on server
- API methods: validate, create, download images
- permanent edit mode with create button
The WeaponRep (and other rep components) weren't visually updating when
new items were added to the party. The issue was that Svelte's reactivity
wasn't properly propagating through the {#each} blocks.
Changes:
- PartySegmentedControl: Add derived values for party sub-properties to
ensure reactivity propagates through snippet boundaries
- WeaponRep: Pre-compute rows as explicit $derived value and use keyed
{#each} blocks for proper change detection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## 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>
## Summary
This PR establishes the foundation for migrating from custom Svelte 5
resource classes to TanStack Query v6 for server state management. It
adds:
**Query Options Factories** (in `src/lib/api/queries/`):
- `party.queries.ts` - Party fetching with infinite scroll support
- `job.queries.ts` - Job and skill queries with pagination
- `user.queries.ts` - User profile, parties, and favorites queries
**Mutation Configurations** (in `src/lib/api/mutations/`):
- `party.mutations.ts` - Party CRUD with cache invalidation
- `grid.mutations.ts` - Weapon/character/summon mutations with
optimistic updates
- `job.mutations.ts` - Job and skill update mutations
**Deprecation Notices**:
- Added `@deprecated` JSDoc to `search.resource.svelte.ts` and
`party.resource.svelte.ts` with migration examples
**SSR Integration** (Phase 4):
- Created `+layout.ts` to initialize QueryClient for SSR support
- Updated `+layout.svelte` to receive QueryClient from load function
- Added SSR utilities in `src/lib/query/ssr.ts`:
- `withInitialData()` - for pages using +page.server.ts
- `prefetchQuery()` / `prefetchInfiniteQuery()` - for pages using
+page.ts
- `setQueryData()` - for direct cache population
- Added documentation in `src/lib/query/README.md`
**Component Wiring Examples** (Phase 5):
- `JobSelectionSidebar.svelte` - Migrated from `createJobResource()` to
`createQuery(() => jobQueries.list())`. Demonstrates client-side query
pattern with automatic loading/error states.
- `teams/[id]/+page.svelte` - Added `withInitialData()` pattern for SSR
integration. Server-fetched party data is used as initial cache value
with background refetching support.
**Migration Guide**:
- Added `src/lib/query/MIGRATION.md` with follow-up prompts for
remaining component migrations (JobSkillSelectionSidebar, search modal,
user profile, teams explore, Party mutations, resource class removal)
## Updates Since Last Revision
Fixed TypeScript type errors in the TanStack Query integration:
- `party.queries.ts`: Made `total` and `perPage` optional in
`PartyPageResult` interface to match adapter return type
- `ssr.ts`: Fixed `withInitialData` to properly handle null values using
`NonNullable<TData>` return type
- `job.mutations.ts`: Fixed slot indexing by casting through `unknown`
to `keyof typeof updatedSkills`
Type checks now pass for all files modified in this PR (16 remaining
errors are pre-existing project issues unrelated to this PR - paraglide
modules not generated, hooks.ts implicit anys).
## Review & Testing Checklist for Human
- [ ] **Verify app loads correctly**: The `+layout.ts` and
`+layout.svelte` changes are critical path - confirm the app still
renders
- [ ] **Test JobSelectionSidebar**: Open job selection sidebar and
verify jobs load correctly, search/filter works, and retry button works
on error
- [ ] **Test teams/[id] page**: Navigate to a party detail page and
verify it renders without loading flash (SSR data should be immediate)
- [ ] **Review type casts**: Check `job.mutations.ts:135` - the `as
unknown as keyof typeof` cast for slot indexing is a workaround for
jobSkills having string literal keys ('0', '1', '2', '3') while slot is
a number
- [ ] **Verify withInitialData behavior**: The `NonNullable<TData>`
return type change in `ssr.ts` should work correctly with `data.party`
which can be `Party | null`
**Recommended test plan**:
1. Run `pnpm install` to ensure dependencies are up to date
2. Start dev server and verify the app loads without errors
3. Navigate to a party detail page (`/teams/[shortcode]`) - should
render immediately without loading state
4. Open job selection sidebar (click job icon on a party you can edit) -
verify jobs load and filtering works
5. Test error handling by temporarily breaking network - verify retry
button appears
### Notes
- Pre-existing project issues remain (paraglide modules not generated,
hooks.ts implicit anys) - these are unrelated to this PR
- Local testing could not run due to missing node_modules (vite not
found) - project setup issue
- TanStack Query devtools installation was skipped due to Storybook
version conflicts
- The existing `search.queries.ts` file was used as the pattern
reference for new query factories
- SSR approach uses hybrid pattern: existing `+page.server.ts` files
work with `withInitialData()`, while new pages can use `prefetchQuery()`
in `+page.ts`
- Migration guide includes 6 follow-up prompts for completing the
remaining component migrations
**Link to Devin run**:
https://app.devin.ai/sessions/33e97a98ae3e415aa4dc35378cad3a2b
**Requested by**: Justin Edmund (justin@jedmund.com) / @jedmund
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Justin Edmund <justin@jedmund.com>
Changed from numeric (0/1/2) to string literals ('public'/'private'/'unlisted')
- Created PartyVisibility type with const assertion
- Updated Party and PartyPreview interfaces
- Updated PartyUpdatePayload interface
- Updated Zod schemas for validation
- Updated test mocks to use string literals
- Added deprecated conversion helpers for backward compat
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- transform job skills pagination meta to include page/perPage
- fix UserInfo avatar type (optional properties -> required when present)
- add visibility number-to-string mapping in party service (0=public, 1=private, 2=unlisted)
- change mapToApiPayload return type from Partial<Party> to CreatePartyParams
- fix raid/job mapping to use IDs instead of nested objects
- add generic type argument to RestDataProvider<any>
- add type assertion in optionalProps (value as T[keyof T])
- remove recruits property from Character test mock (doesn't exist in type)
- add missing subaura property to Summon test mocks
- consolidate Awakening imports to use entities source
- make awakening type/level optional in GridWeapon/GridSummon
- fix null handling in AwakeningDisplay (null -> undefined)
Fixed remaining test mock data to match actual type schemas.
Changes:
1. entity.adapter.test.ts:
- Removed invalid maxLevel property from Character mock
- Character interface doesn't have maxLevel
2. grid.adapter.test.ts:
- Added missing required properties to Weapon mock:
- maxSkillLevel: 15
- maxAwakeningLevel: 5
- ax: true
- axType: 1
- Removed invalid series property from Summon mock
- Summon interface doesn't have series property
3. settings/+page.svelte:
- Fixed users.update call (removed extra fetch parameter)
All test mocks now match their corresponding type definitions from
entities.ts, ensuring tests can compile and run correctly.
Result: 24 → 22 errors (-2)
Overall progress: 53 → 22 errors (58% reduction)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>