diff --git a/src/lib/api/adapters/gw.adapter.ts b/src/lib/api/adapters/gw.adapter.ts index afcb69be..6f223f1b 100644 --- a/src/lib/api/adapters/gw.adapter.ts +++ b/src/lib/api/adapters/gw.adapter.ts @@ -12,7 +12,9 @@ import type { CreateCrewScoreInput, UpdateCrewScoreInput, CreateIndividualScoreInput, - BatchIndividualScoresInput + BatchIndividualScoresInput, + MemberGwScores, + PhantomGwScores } from '$lib/types/api/gw' /** @@ -335,6 +337,30 @@ export class GwAdapter extends BaseAdapter { this.clearCache('/crew/gw_participations') return response.individualScores } + + // ==================== Member/Phantom Score History ==================== + + /** + * Get all GW scores for a specific crew member + */ + async getMemberGwScores(membershipId: string, options?: RequestOptions): Promise { + const response = await this.request( + `/crew/memberships/${membershipId}/gw_scores`, + options + ) + return response + } + + /** + * Get all GW scores for a specific phantom player + */ + async getPhantomGwScores(phantomId: string, options?: RequestOptions): Promise { + const response = await this.request( + `/crew/phantom_players/${phantomId}/gw_scores`, + options + ) + return response + } } export const gwAdapter = new GwAdapter(DEFAULT_ADAPTER_CONFIG) diff --git a/src/lib/api/queries/gw.queries.ts b/src/lib/api/queries/gw.queries.ts index a96fb714..d0ff5dca 100644 --- a/src/lib/api/queries/gw.queries.ts +++ b/src/lib/api/queries/gw.queries.ts @@ -80,6 +80,34 @@ export const gwQueries = { enabled: !!participationId, staleTime: 1000 * 60 * 2, // 2 minutes - scores change during event gcTime: 1000 * 60 * 15 // 15 minutes + }), + + /** + * Member's GW scores history query options + * + * @param membershipId - Crew membership ID + */ + memberGwScores: (membershipId: string) => + queryOptions({ + queryKey: ['crew', 'member', membershipId, 'gw_scores'] as const, + queryFn: () => gwAdapter.getMemberGwScores(membershipId), + enabled: !!membershipId, + staleTime: 1000 * 60 * 5, // 5 minutes + gcTime: 1000 * 60 * 30 // 30 minutes + }), + + /** + * Phantom's GW scores history query options + * + * @param phantomId - Phantom player ID + */ + phantomGwScores: (phantomId: string) => + queryOptions({ + queryKey: ['crew', 'phantom', phantomId, 'gw_scores'] as const, + queryFn: () => gwAdapter.getPhantomGwScores(phantomId), + enabled: !!phantomId, + staleTime: 1000 * 60 * 5, // 5 minutes + gcTime: 1000 * 60 * 30 // 30 minutes }) } @@ -105,5 +133,7 @@ export const gwKeys = { events: () => [...gwKeys.all, 'events'] as const, event: (eventId: string) => [...gwKeys.all, 'events', eventId] as const, participationsAll: () => [...gwKeys.all, 'participations'] as const, - participation: (participationId: string) => [...gwKeys.all, 'participations', participationId] as const + participation: (participationId: string) => [...gwKeys.all, 'participations', participationId] as const, + memberGwScores: (membershipId: string) => ['crew', 'member', membershipId, 'gw_scores'] as const, + phantomGwScores: (phantomId: string) => ['crew', 'phantom', phantomId, 'gw_scores'] as const } diff --git a/src/lib/types/api/gw.ts b/src/lib/types/api/gw.ts index af7c945b..68d3db26 100644 --- a/src/lib/types/api/gw.ts +++ b/src/lib/types/api/gw.ts @@ -148,6 +148,33 @@ export interface BatchIndividualScoresInput { scores: BatchScoreEntry[] } +// Member/Phantom GW score history + +export interface GwEventMinimal { + id: string + element: number + eventNumber: number + startDate: string + endDate: string +} + +export interface EventScoreSummary { + gwEvent: GwEventMinimal + totalScore: number +} + +export interface MemberGwScores { + member: CrewMembership + eventScores: EventScoreSummary[] + grandTotal: number +} + +export interface PhantomGwScores { + phantom: PhantomPlayer + eventScores: EventScoreSummary[] + grandTotal: number +} + // Aggregated data for visualization export interface GwLeaderboardEntry { diff --git a/src/routes/(app)/crew/members/[membershipId]/+page.svelte b/src/routes/(app)/crew/members/[membershipId]/+page.svelte new file mode 100644 index 00000000..887c32e3 --- /dev/null +++ b/src/routes/(app)/crew/members/[membershipId]/+page.svelte @@ -0,0 +1,179 @@ + + + + +
+ + + {#if scoresQuery.isLoading} +
Loading scores...
+ {:else if scoresQuery.isError} +
Failed to load scores
+ {:else if scoresQuery.data} + {@const data = scoresQuery.data} + +
+
+ Total Honors + {formatScore(data.grandTotal)} +
+
+ Events + {data.eventScores.length} +
+
+ + {#if data.eventScores.length === 0} +
No GW scores recorded yet.
+ {:else} +
    + {#each data.eventScores as eventScore (eventScore.gwEvent.id)} +
  • + +
  • + {/each} +
+ {/if} + {/if} +
+ + diff --git a/src/routes/(app)/crew/phantoms/[phantomId]/+page.svelte b/src/routes/(app)/crew/phantoms/[phantomId]/+page.svelte new file mode 100644 index 00000000..0a451fe4 --- /dev/null +++ b/src/routes/(app)/crew/phantoms/[phantomId]/+page.svelte @@ -0,0 +1,187 @@ + + + + +
+ + + {#if scoresQuery.isLoading} +
Loading scores...
+ {:else if scoresQuery.isError} +
Failed to load scores
+ {:else if scoresQuery.data} + {@const data = scoresQuery.data} + +
+
+ Total Honors + {formatScore(data.grandTotal)} +
+
+ Events + {data.eventScores.length} +
+
+ + {#if data.eventScores.length === 0} +
No GW scores recorded yet.
+ {:else} +
    + {#each data.eventScores as eventScore (eventScore.gwEvent.id)} +
  • + +
  • + {/each} +
+ {/if} + {/if} +
+ +