90 lines
2.4 KiB
TypeScript
90 lines
2.4 KiB
TypeScript
/**
|
|
* League Stewarding View Model
|
|
* Represents all data needed for league stewarding across all races
|
|
*/
|
|
import { ViewModel } from "../contracts/view-models/ViewModel";
|
|
|
|
/**
|
|
* ViewData for LeagueStewarding
|
|
* This is the JSON-serializable input for the Template.
|
|
*/
|
|
export interface LeagueStewardingViewData {
|
|
racesWithData: RaceWithProtests[];
|
|
driverMap: Record<string, { id: string; name: string; avatarUrl?: string; iracingId?: string; rating?: number }>;
|
|
}
|
|
|
|
export class LeagueStewardingViewModel extends ViewModel {
|
|
private readonly data: LeagueStewardingViewData;
|
|
|
|
constructor(data: LeagueStewardingViewData) {
|
|
super();
|
|
this.data = data;
|
|
}
|
|
|
|
get racesWithData(): RaceWithProtests[] { return this.data.racesWithData; }
|
|
get driverMap() { return this.data.driverMap; }
|
|
|
|
/** 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;
|
|
} |