Files
gridpilot.gg/apps/website/lib/view-models/LeagueStewardingViewModel.ts
2025-12-19 21:58:03 +01:00

74 lines
2.0 KiB
TypeScript

/**
* League Stewarding View Model
* Represents all data needed for league stewarding across all races
*/
export class LeagueStewardingViewModel {
constructor(
public readonly racesWithData: RaceWithProtests[],
public readonly driverMap: Record<string, { id: string; name: string; avatarUrl?: string; iracingId?: string; rating?: number }>
) {}
/** UI-specific: Total pending protests count */
get totalPending(): number {
return this.racesWithData.reduce((sum, r) => sum + r.pendingProtests.length, 0);
}
/** UI-specific: Total resolved protests count */
get totalResolved(): number {
return this.racesWithData.reduce((sum, r) => sum + r.resolvedProtests.length, 0);
}
/** UI-specific: Total penalties count */
get totalPenalties(): number {
return this.racesWithData.reduce((sum, r) => sum + r.penalties.length, 0);
}
/** UI-specific: Filtered races for pending tab */
get pendingRaces(): RaceWithProtests[] {
return this.racesWithData.filter(r => r.pendingProtests.length > 0);
}
/** UI-specific: Filtered races for history tab */
get historyRaces(): RaceWithProtests[] {
return this.racesWithData.filter(r => r.resolvedProtests.length > 0 || r.penalties.length > 0);
}
/** UI-specific: All drivers for quick penalty modal */
get allDrivers(): Array<{ id: string; name: string; avatarUrl?: string; iracingId?: string; rating?: number }> {
return Object.values(this.driverMap);
}
}
export interface RaceWithProtests {
race: {
id: string;
track: string;
scheduledAt: Date;
};
pendingProtests: Protest[];
resolvedProtests: Protest[];
penalties: Penalty[];
}
export interface Protest {
id: string;
protestingDriverId: string;
accusedDriverId: string;
incident: {
lap: number;
description: string;
};
filedAt: string;
status: string;
decisionNotes?: string;
proofVideoUrl?: string;
}
export interface Penalty {
id: string;
driverId: string;
type: string;
value: number;
reason: string;
notes?: string;
}