101 lines
2.4 KiB
TypeScript
101 lines
2.4 KiB
TypeScript
// DTO interfaces matching the API responses
|
|
interface RaceDetailDTO {
|
|
race: {
|
|
id: string;
|
|
track: string;
|
|
scheduledAt: string;
|
|
status: string;
|
|
} | null;
|
|
league: {
|
|
id: string;
|
|
name: string;
|
|
} | null;
|
|
}
|
|
|
|
interface RaceProtestsDTO {
|
|
protests: Array<{
|
|
id: string;
|
|
protestingDriverId: string;
|
|
accusedDriverId: string;
|
|
incident: {
|
|
lap: number;
|
|
description: string;
|
|
};
|
|
filedAt: string;
|
|
status: string;
|
|
decisionNotes?: string;
|
|
proofVideoUrl?: string;
|
|
}>;
|
|
driverMap: Record<string, { id: string; name: string }>;
|
|
}
|
|
|
|
interface RacePenaltiesDTO {
|
|
penalties: Array<{
|
|
id: string;
|
|
driverId: string;
|
|
type: string;
|
|
value: number;
|
|
reason: string;
|
|
notes?: string;
|
|
}>;
|
|
driverMap: Record<string, { id: string; name: string }>;
|
|
}
|
|
|
|
interface RaceStewardingDTO {
|
|
raceDetail: RaceDetailDTO;
|
|
protests: RaceProtestsDTO;
|
|
penalties: RacePenaltiesDTO;
|
|
}
|
|
|
|
/**
|
|
* Race Stewarding View Model
|
|
* Represents all data needed for race stewarding (protests, penalties, race info)
|
|
*/
|
|
import { ViewModel } from "../contracts/view-models/ViewModel";
|
|
|
|
export class RaceStewardingViewModel extends ViewModel {
|
|
race: RaceDetailDTO['race'];
|
|
league: RaceDetailDTO['league'];
|
|
protests: RaceProtestsDTO['protests'];
|
|
penalties: RacePenaltiesDTO['penalties'];
|
|
driverMap: Record<string, { id: string; name: string }>;
|
|
|
|
constructor(dto: RaceStewardingDTO) {
|
|
this.race = dto.raceDetail.race;
|
|
this.league = dto.raceDetail.league;
|
|
this.protests = dto.protests.protests;
|
|
this.penalties = dto.penalties.penalties;
|
|
|
|
// Merge driver maps from protests and penalties
|
|
this.driverMap = { ...dto.protests.driverMap, ...dto.penalties.driverMap };
|
|
}
|
|
|
|
/** UI-specific: Pending protests */
|
|
get pendingProtests() {
|
|
return this.protests.filter(p => p.status === 'pending' || p.status === 'under_review');
|
|
}
|
|
|
|
/** UI-specific: Resolved protests */
|
|
get resolvedProtests() {
|
|
return this.protests.filter(p =>
|
|
p.status === 'upheld' ||
|
|
p.status === 'dismissed' ||
|
|
p.status === 'withdrawn'
|
|
);
|
|
}
|
|
|
|
/** UI-specific: Total pending protests count */
|
|
get pendingCount(): number {
|
|
return this.pendingProtests.length;
|
|
}
|
|
|
|
/** UI-specific: Total resolved protests count */
|
|
get resolvedCount(): number {
|
|
return this.resolvedProtests.length;
|
|
}
|
|
|
|
/** UI-specific: Total penalties count */
|
|
get penaltiesCount(): number {
|
|
return this.penalties.length;
|
|
}
|
|
} |