feat: wire up example components with TanStack Query v6
Phase 5 implementation - Component wiring examples: 1. JobSelectionSidebar.svelte: - Replaced createJobResource() with createQuery(() => jobQueries.list()) - Removed manual loading/error state management - Uses TanStack Query's isLoading, isError, refetch() for state - Demonstrates client-side query pattern 2. teams/[id]/+page.svelte: - Added createQuery with withInitialData() for SSR integration - Server-fetched party data used as initial cache value - Query can refetch in background when data becomes stale - Demonstrates SSR + TanStack Query hybrid pattern 3. Added MIGRATION.md with follow-up prompts for: - JobSkillSelectionSidebar migration - Search modal migration - User profile page migration - Teams explore page migration - Party component mutations - Resource class removal This completes Phase 5 of the TanStack Query integration. Co-Authored-By: Justin Edmund <justin@jedmund.com>
This commit is contained in:
parent
e1c330f376
commit
cce93e367b
3 changed files with 258 additions and 33 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte'
|
import { createQuery } from '@tanstack/svelte-query'
|
||||||
import type { Job } from '$lib/types/api/entities'
|
import type { Job } from '$lib/types/api/entities'
|
||||||
import { createJobResource } from '$lib/api/adapters/resources/job.resource.svelte'
|
import { jobQueries } from '$lib/api/queries/job.queries'
|
||||||
import { getJobTierName, getJobTierOrder } from '$lib/utils/jobUtils'
|
import { getJobTierName, getJobTierOrder } from '$lib/utils/jobUtils'
|
||||||
import JobItem from '../job/JobItem.svelte'
|
import JobItem from '../job/JobItem.svelte'
|
||||||
import JobTierSelector from '../job/JobTierSelector.svelte'
|
import JobTierSelector from '../job/JobTierSelector.svelte'
|
||||||
|
|
@ -17,14 +17,13 @@
|
||||||
|
|
||||||
let { currentJobId, onSelectJob }: Props = $props()
|
let { currentJobId, onSelectJob }: Props = $props()
|
||||||
|
|
||||||
// Create job resource
|
// TanStack Query v6: Use createQuery with thunk pattern for reactivity
|
||||||
const jobResource = createJobResource()
|
// Jobs are cached for 30 minutes and shared across all components
|
||||||
|
const jobsQuery = createQuery(() => jobQueries.list())
|
||||||
|
|
||||||
// State
|
// State for filtering (local UI state, not server state)
|
||||||
let searchQuery = $state('')
|
let searchQuery = $state('')
|
||||||
let selectedTiers = $state<Set<string>>(new Set(['4', '5', 'ex2', 'o1'])) // Default to IV, V, EXII, OI
|
let selectedTiers = $state<Set<string>>(new Set(['4', '5', 'ex2', 'o1'])) // Default to IV, V, EXII, OI
|
||||||
let loading = $state(false)
|
|
||||||
let error = $state<string | undefined>()
|
|
||||||
|
|
||||||
// Available tiers with short labels for display
|
// Available tiers with short labels for display
|
||||||
const tiers = [
|
const tiers = [
|
||||||
|
|
@ -48,29 +47,11 @@
|
||||||
selectedTiers = newSet
|
selectedTiers = newSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch jobs on mount
|
|
||||||
onMount(() => {
|
|
||||||
loadJobs()
|
|
||||||
})
|
|
||||||
|
|
||||||
async function loadJobs() {
|
|
||||||
loading = true
|
|
||||||
error = undefined
|
|
||||||
|
|
||||||
try {
|
|
||||||
await jobResource.fetchJobs()
|
|
||||||
} catch (e: any) {
|
|
||||||
error = e.message || 'Failed to load jobs'
|
|
||||||
console.error('Error loading jobs:', e)
|
|
||||||
} finally {
|
|
||||||
loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter jobs based on search and filters
|
// Filter jobs based on search and filters
|
||||||
|
// TanStack Query handles loading/error states, we just filter the data
|
||||||
const filteredJobs = $derived(
|
const filteredJobs = $derived(
|
||||||
(() => {
|
(() => {
|
||||||
let jobs = jobResource.jobs.data || []
|
let jobs = jobsQuery.data || []
|
||||||
|
|
||||||
// Filter by search query
|
// Filter by search query
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
|
|
@ -135,16 +116,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="jobs-container">
|
<div class="jobs-container">
|
||||||
{#if loading}
|
{#if jobsQuery.isLoading}
|
||||||
<div class="loading-state">
|
<div class="loading-state">
|
||||||
<Icon name="loader-2" size={32} />
|
<Icon name="loader-2" size={32} />
|
||||||
<p>Loading jobs...</p>
|
<p>Loading jobs...</p>
|
||||||
</div>
|
</div>
|
||||||
{:else if error}
|
{:else if jobsQuery.isError}
|
||||||
<div class="error-state">
|
<div class="error-state">
|
||||||
<Icon name="alert-circle" size={32} />
|
<Icon name="alert-circle" size={32} />
|
||||||
<p>{error}</p>
|
<p>{jobsQuery.error?.message || 'Failed to load jobs'}</p>
|
||||||
<Button size="small" onclick={loadJobs}>Retry</Button>
|
<Button size="small" onclick={() => jobsQuery.refetch()}>Retry</Button>
|
||||||
</div>
|
</div>
|
||||||
{:else if Object.keys(filteredJobs).length === 0}
|
{:else if Object.keys(filteredJobs).length === 0}
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
|
|
|
||||||
217
src/lib/query/MIGRATION.md
Normal file
217
src/lib/query/MIGRATION.md
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
# TanStack Query Migration Guide
|
||||||
|
|
||||||
|
This document contains follow-up prompts for migrating remaining components to TanStack Query v6.
|
||||||
|
|
||||||
|
## Migration Status
|
||||||
|
|
||||||
|
### Completed (PR #441)
|
||||||
|
- Query options factories: `party.queries.ts`, `job.queries.ts`, `user.queries.ts`
|
||||||
|
- Mutation configurations: `party.mutations.ts`, `grid.mutations.ts`, `job.mutations.ts`
|
||||||
|
- SSR utilities: `withInitialData`, `prefetchQuery`, `prefetchInfiniteQuery`
|
||||||
|
- Example components: `JobSelectionSidebar.svelte`, `teams/[id]/+page.svelte`
|
||||||
|
|
||||||
|
### Pending Migration
|
||||||
|
|
||||||
|
The following components still use direct adapter calls or resource classes and should be migrated in future PRs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Follow-Up Prompt 1: Job Skill Selection Sidebar
|
||||||
|
|
||||||
|
**Scope**: Migrate `JobSkillSelectionSidebar.svelte` to use TanStack Query
|
||||||
|
|
||||||
|
**Prompt**:
|
||||||
|
```
|
||||||
|
Migrate the JobSkillSelectionSidebar component to use TanStack Query v6.
|
||||||
|
|
||||||
|
The component currently uses InfiniteScrollResource for paginated skill loading.
|
||||||
|
Replace it with createInfiniteQuery using jobQueries.skills().
|
||||||
|
|
||||||
|
Files to modify:
|
||||||
|
- src/lib/components/sidebar/JobSkillSelectionSidebar.svelte
|
||||||
|
|
||||||
|
Reference implementation:
|
||||||
|
- src/lib/components/sidebar/JobSelectionSidebar.svelte (already migrated)
|
||||||
|
- src/lib/api/queries/job.queries.ts (jobQueries.skills for infinite query)
|
||||||
|
|
||||||
|
Key changes:
|
||||||
|
1. Replace InfiniteScrollResource with createInfiniteQuery
|
||||||
|
2. Use jobQueries.skills(jobId, { query: searchTerm }) for the query options
|
||||||
|
3. Handle pagination with query.fetchNextPage() and query.hasNextPage
|
||||||
|
4. Update loading/error states to use query.isLoading, query.isError
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Follow-Up Prompt 2: Search Modal Migration
|
||||||
|
|
||||||
|
**Scope**: Migrate search functionality to use TanStack Query
|
||||||
|
|
||||||
|
**Prompt**:
|
||||||
|
```
|
||||||
|
Migrate the search functionality to use TanStack Query v6.
|
||||||
|
|
||||||
|
The existing search.queries.ts has infinite query options for weapons, characters,
|
||||||
|
summons, and job skills. Wire these up to the actual search components.
|
||||||
|
|
||||||
|
Files to modify:
|
||||||
|
- src/lib/components/search/SearchPanel.svelte (or equivalent)
|
||||||
|
- src/lib/features/search/openSearchSidebar.svelte.ts
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
- src/lib/api/queries/search.queries.ts (existing query options)
|
||||||
|
- src/lib/components/InfiniteScrollQuery.svelte (existing TanStack Query component)
|
||||||
|
|
||||||
|
Key changes:
|
||||||
|
1. Use createInfiniteQuery with searchQueries.weapons/characters/summons
|
||||||
|
2. Implement debounced search input (debounce the value, not the query)
|
||||||
|
3. Use InfiniteScrollQuery component for rendering results
|
||||||
|
4. Remove dependency on SearchResource class
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Follow-Up Prompt 3: User Profile Page
|
||||||
|
|
||||||
|
**Scope**: Migrate user profile page to use TanStack Query with SSR
|
||||||
|
|
||||||
|
**Prompt**:
|
||||||
|
```
|
||||||
|
Migrate the [username]/+page.svelte to use TanStack Query v6 with SSR.
|
||||||
|
|
||||||
|
The page currently fetches user profile and parties in +page.server.ts.
|
||||||
|
Add TanStack Query integration using the withInitialData pattern.
|
||||||
|
|
||||||
|
Files to modify:
|
||||||
|
- src/routes/[username]/+page.svelte
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
- src/routes/teams/[id]/+page.svelte (already migrated with withInitialData)
|
||||||
|
- src/lib/api/queries/user.queries.ts (userQueries.profile, userQueries.parties)
|
||||||
|
- src/lib/query/ssr.ts (withInitialData helper)
|
||||||
|
|
||||||
|
Key changes:
|
||||||
|
1. Add createQuery for user profile with withInitialData
|
||||||
|
2. Add createInfiniteQuery for user parties with initialData from server
|
||||||
|
3. Use $derived to prefer query data over server data
|
||||||
|
4. Enable background refetching for fresh data
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Follow-Up Prompt 4: Teams Explore Page
|
||||||
|
|
||||||
|
**Scope**: Migrate teams explore page to use TanStack Query
|
||||||
|
|
||||||
|
**Prompt**:
|
||||||
|
```
|
||||||
|
Migrate the teams/explore page to use TanStack Query v6 for party listing.
|
||||||
|
|
||||||
|
The page displays a paginated list of public parties with filtering.
|
||||||
|
|
||||||
|
Files to modify:
|
||||||
|
- src/routes/teams/explore/+page.svelte
|
||||||
|
- src/routes/teams/explore/+page.server.ts (if needed)
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
- src/lib/api/queries/party.queries.ts (partyQueries.list for infinite query)
|
||||||
|
- src/lib/query/ssr.ts (prefetchInfiniteQuery for SSR)
|
||||||
|
|
||||||
|
Key changes:
|
||||||
|
1. Use createInfiniteQuery with partyQueries.list(filters)
|
||||||
|
2. Implement filter state that triggers query refetch
|
||||||
|
3. Use infinite scroll or "Load More" button with query.fetchNextPage()
|
||||||
|
4. Consider prefetching first page in +page.ts for faster initial load
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Follow-Up Prompt 5: Party Component Mutations
|
||||||
|
|
||||||
|
**Scope**: Wire up Party component to use TanStack Query mutations
|
||||||
|
|
||||||
|
**Prompt**:
|
||||||
|
```
|
||||||
|
Migrate the Party component to use TanStack Query mutations for CRUD operations.
|
||||||
|
|
||||||
|
The Party component currently uses PartyService and GridService directly.
|
||||||
|
Replace these with TanStack Query mutations for automatic cache invalidation.
|
||||||
|
|
||||||
|
Files to modify:
|
||||||
|
- src/lib/components/party/Party.svelte
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
- src/lib/api/mutations/party.mutations.ts (useUpdateParty, useDeleteParty, etc.)
|
||||||
|
- src/lib/api/mutations/grid.mutations.ts (useUpdateGridWeapon, etc.)
|
||||||
|
- src/lib/api/mutations/job.mutations.ts (useUpdateJob, useUpdateSkills)
|
||||||
|
|
||||||
|
Key changes:
|
||||||
|
1. Import and use mutation hooks (useUpdateParty, useDeleteParty, etc.)
|
||||||
|
2. Replace direct service calls with mutation.mutate()
|
||||||
|
3. Leverage optimistic updates for immediate UI feedback
|
||||||
|
4. Use mutation.isPending for loading states
|
||||||
|
5. Use mutation.error for error handling
|
||||||
|
|
||||||
|
Note: This is a larger refactor. Consider breaking it into sub-tasks:
|
||||||
|
- 5a: Party metadata mutations (name, description, visibility)
|
||||||
|
- 5b: Grid weapon mutations
|
||||||
|
- 5c: Grid character mutations
|
||||||
|
- 5d: Grid summon mutations
|
||||||
|
- 5e: Job and skill mutations
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Follow-Up Prompt 6: Remove Deprecated Resource Classes
|
||||||
|
|
||||||
|
**Scope**: Remove deprecated resource classes after migration is complete
|
||||||
|
|
||||||
|
**Prompt**:
|
||||||
|
```
|
||||||
|
Remove the deprecated resource classes now that TanStack Query migration is complete.
|
||||||
|
|
||||||
|
Files to delete:
|
||||||
|
- src/lib/api/adapters/resources/search.resource.svelte.ts
|
||||||
|
- src/lib/api/adapters/resources/party.resource.svelte.ts
|
||||||
|
- src/lib/api/adapters/resources/job.resource.svelte.ts
|
||||||
|
- src/lib/api/adapters/resources/infiniteScroll.resource.svelte.ts
|
||||||
|
|
||||||
|
Pre-requisites:
|
||||||
|
1. Verify no components import these files (use grep)
|
||||||
|
2. Ensure all functionality has been migrated to TanStack Query
|
||||||
|
3. Run build to confirm no import errors
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
1. Search for any remaining imports of resource classes
|
||||||
|
2. Migrate any remaining usages to TanStack Query
|
||||||
|
3. Delete the resource files
|
||||||
|
4. Update any barrel exports (index.ts files)
|
||||||
|
5. Run build and tests to verify
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Checklist
|
||||||
|
|
||||||
|
Use this checklist to track overall migration progress:
|
||||||
|
|
||||||
|
- [x] Create query options factories
|
||||||
|
- [x] Create mutation configurations
|
||||||
|
- [x] Add SSR utilities
|
||||||
|
- [x] Migrate JobSelectionSidebar (example)
|
||||||
|
- [x] Migrate teams/[id] page (SSR example)
|
||||||
|
- [ ] Migrate JobSkillSelectionSidebar
|
||||||
|
- [ ] Migrate search functionality
|
||||||
|
- [ ] Migrate user profile page
|
||||||
|
- [ ] Migrate teams explore page
|
||||||
|
- [ ] Migrate Party component mutations
|
||||||
|
- [ ] Remove deprecated resource classes
|
||||||
|
- [ ] Add TanStack Query devtools (optional)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Always test locally before pushing changes
|
||||||
|
- Run `npm run build` to verify TypeScript compilation
|
||||||
|
- The existing adapters remain unchanged - TanStack Query wraps them
|
||||||
|
- Cache invalidation is handled automatically by mutations
|
||||||
|
- SSR uses hybrid approach: existing +page.server.ts + withInitialData pattern
|
||||||
|
|
@ -1,12 +1,39 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PageData } from './$types'
|
import type { PageData } from './$types'
|
||||||
import Party from '$lib/components/party/Party.svelte'
|
import Party from '$lib/components/party/Party.svelte'
|
||||||
|
import { createQuery } from '@tanstack/svelte-query'
|
||||||
|
import { partyQueries } from '$lib/api/queries/party.queries'
|
||||||
|
import { withInitialData } from '$lib/query/ssr'
|
||||||
|
|
||||||
let { data }: { data: PageData } = $props()
|
let { data }: { data: PageData } = $props()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TanStack Query v6 SSR Integration Example
|
||||||
|
*
|
||||||
|
* This demonstrates the `withInitialData` pattern for pages using +page.server.ts.
|
||||||
|
* The server-fetched party data is used as initial data for the query, which means:
|
||||||
|
*
|
||||||
|
* 1. No loading state on initial render (data is already available)
|
||||||
|
* 2. The query can refetch in the background when data becomes stale
|
||||||
|
* 3. The data is cached and shared across components using the same query key
|
||||||
|
*
|
||||||
|
* Note: The Party component currently manages its own state, so we pass the
|
||||||
|
* server data directly. In a future refactor, the Party component could use
|
||||||
|
* this query directly for automatic cache updates and background refetching.
|
||||||
|
*/
|
||||||
|
const partyQuery = createQuery(() => ({
|
||||||
|
...partyQueries.byShortcode(data.party?.shortcode ?? ''),
|
||||||
|
...withInitialData(data.party),
|
||||||
|
enabled: !!data.party?.shortcode
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Use query data if available, fall back to server data
|
||||||
|
// This allows the query to refetch and update the UI automatically
|
||||||
|
const party = $derived(partyQuery.data ?? data.party)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if data?.party}
|
{#if party}
|
||||||
<Party party={data.party} canEdit={data.canEdit || false} authUserId={data.authUserId} />
|
<Party party={party} canEdit={data.canEdit || false} authUserId={data.authUserId} />
|
||||||
{:else}
|
{:else}
|
||||||
<div>
|
<div>
|
||||||
<h1>Party not found</h1>
|
<h1>Party not found</h1>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue