diff --git a/src/lib/api/adapters/gw.adapter.ts b/src/lib/api/adapters/gw.adapter.ts index 9ad65425..afcb69be 100644 --- a/src/lib/api/adapters/gw.adapter.ts +++ b/src/lib/api/adapters/gw.adapter.ts @@ -194,6 +194,24 @@ export class GwAdapter extends BaseAdapter { return response.crewScore } + /** + * Delete a crew score + */ + async deleteCrewScore( + participationId: string, + scoreId: string, + options?: RequestOptions + ): Promise { + await this.request( + `/crew/gw_participations/${participationId}/crew_scores/${scoreId}`, + { + ...options, + method: 'DELETE' + } + ) + this.clearCache(`/crew/gw_participations/${participationId}`) + } + // ==================== Individual Score Operations ==================== /** diff --git a/src/lib/components/crew/EditCrewScoreModal.svelte b/src/lib/components/crew/EditCrewScoreModal.svelte new file mode 100644 index 00000000..49b9e807 --- /dev/null +++ b/src/lib/components/crew/EditCrewScoreModal.svelte @@ -0,0 +1,267 @@ + + + + + + + + +
+ + + + + + + + + {#if error} +
+

{error}

+
+ {/if} +
+
+ + + {#snippet left()} + {#if existingScore} + + {/if} + {/snippet} + +
+ + diff --git a/src/lib/components/crew/EditScoreModal.svelte b/src/lib/components/crew/EditScoreModal.svelte new file mode 100644 index 00000000..8f24bded --- /dev/null +++ b/src/lib/components/crew/EditScoreModal.svelte @@ -0,0 +1,366 @@ + + + + + + + + +
+ {#if scores.length === 0} +

No scores to edit

+ {:else} +
+ {#each scores as score (score.id)} + {#if editValues[score.id] !== undefined} +
+ +
+ + {#if excusedValues[score.id]} + + {/if} +
+
+ {/if} + {/each} +
+ {/if} + + {#if error} +
+

{error}

+
+ {/if} +
+
+ + + {#snippet left()} + {#if scores.length === 1} + + {:else if scores.length > 1} + + {/if} + {/snippet} + +
+ + diff --git a/src/lib/types/api/gw.ts b/src/lib/types/api/gw.ts index 85b9e966..34802efe 100644 --- a/src/lib/types/api/gw.ts +++ b/src/lib/types/api/gw.ts @@ -72,6 +72,8 @@ export interface GwIndividualScore { round: GwRound score: number isCumulative: boolean + excused: boolean + excuseReason?: string // Only returned to crew officers playerName: string playerType: PlayerType createdAt?: string @@ -125,6 +127,8 @@ export interface CreateIndividualScoreInput { round: GwRound score: number isCumulative?: boolean + excused?: boolean + excuseReason?: string } // Batch score entry @@ -135,6 +139,8 @@ export interface BatchScoreEntry { round: GwRound score: number isCumulative?: boolean + excused?: boolean + excuseReason?: string } export interface BatchIndividualScoresInput { diff --git a/src/routes/(app)/crew/events/[eventNumber]/+page.svelte b/src/routes/(app)/crew/events/[eventNumber]/+page.svelte index dd6707ac..944a1754 100644 --- a/src/routes/(app)/crew/events/[eventNumber]/+page.svelte +++ b/src/routes/(app)/crew/events/[eventNumber]/+page.svelte @@ -1,7 +1,7 @@ @@ -343,7 +398,7 @@ {:else} - + {#snippet belowTitle()}
@@ -353,9 +408,15 @@ {formatDate(gwEvent.startDate)} – {formatDate(gwEvent.endDate)}
+
+ handleTabChange(v as 'individual' | 'crew')} size="small" variant="background" grow> + Individual + Crew + +
{/snippet} {#snippet actions()} - {#if crewStore.isOfficer && gwEvent.status !== 'upcoming'} + {#if crewStore.isOfficer && gwEvent.status !== 'upcoming' && activeTab === 'individual'} {/if} {/snippet} @@ -369,8 +430,8 @@ {/if} {:else} - - {#if participation.totalScore !== undefined} + + {#if activeTab === 'crew' && participation.totalScore !== undefined}
{formatScore(participation.totalScore)} @@ -387,52 +448,142 @@
{/if} - -
- Individual Scores -
+ + {#if activeTab === 'individual'} +
+ Individual Scores +
- {#if playerScores.length > 0} -
    - {#each playerScores as player, index} -
  • -
    - {index + 1} - {player.name} - {#if player.isRetired} - Retired + {#if playerScores.length > 0} +
      + {#each playerScores as player, index} +
    • +
      + {index + 1} + {player.name} + {#if player.isRetired} + Retired + {/if} + {#if player.scores.some((s) => s.excused)} + Excused + {/if} +
      + {#if player.type === 'phantom'} + Phantom + {/if} +
      + {formatScore(player.totalScore)} + {#if crewStore.isOfficer && player.scores.length > 0} + + {#snippet trigger({ props })} +
      +
    • + {/each} +
    + {:else} +

    No scores recorded yet

    + {/if} + {/if} + + + {#if activeTab === 'crew'} +
    +
    + Round + Our Score + Their Score + +
    + {#each [2, 3, 4, 5] as round (round)} + {@const score = crewScores.find((s) => s.round === round)} +
    +
    + {GW_ROUND_LABELS[round as GwRound]} + {#if score?.victory !== undefined && score.victory !== null} + + {score.victory ? 'Win' : 'Loss'} + {/if}
    - {#if player.type === 'phantom'} - Phantom - {/if} - {formatScore(player.totalScore)} - {#if crewStore.isOfficer && player.scores.length > 0} - - {#snippet trigger({ props })} -
  • +
    + {#if score} + {formatScore(score.crewScore)} + {:else} + + {/if} +
    +
    + {#if score} + {#if score.opponentScore !== null} + {formatScore(score.opponentScore)} + {/if} + {#if score.opponentName} + ({score.opponentName}) + {/if} + {:else} + + {/if} +
    +
    + {#if score} + {#if crewStore.isOfficer} + + {#snippet trigger({ props })} + + {/if} + {/if} +
    +
{/each} - - {:else} -

No scores recorded yet

+ {/if} {/if} {/if} @@ -485,6 +636,21 @@ {/if} +
+ + {#if isExcused} + + {/if} +
+ {#if addScoreMutation.isError}

{addScoreMutation.error?.message ?? 'Failed to add score'} @@ -505,6 +671,28 @@ /> + +{#if participation?.id && editingPlayer} + +{/if} + + +{#if participation?.id && editingCrewScoreRound !== null} + +{/if} +