Files
gridpilot.gg/apps/website/lib/services/leagues/LeagueStewardingService.ts
Marc Mintel 1b0a1f4aee
Some checks failed
Contract Testing / contract-tests (pull_request) Failing after 7m11s
Contract Testing / contract-snapshot (pull_request) Has been skipped
view data fixes
2026-01-24 23:29:55 +01:00

118 lines
4.4 KiB
TypeScript

import { injectable, unmanaged } from 'inversify';
import { Result } from '@/lib/contracts/Result';
import { Service, type DomainError } from '@/lib/contracts/services/Service';
import { type StewardingApiDto } from '@/lib/types/tbd/StewardingApiDto';
import { RaceService } from '../races/RaceService';
import { ProtestService } from '../protests/ProtestService';
import { PenaltyService } from '../penalties/PenaltyService';
import { DriverService } from '../drivers/DriverService';
import { LeagueMembershipService } from './LeagueMembershipService';
import { LeagueStewardingViewModel } from '@/lib/view-models/LeagueStewardingViewModel';
@injectable()
export class LeagueStewardingService implements Service {
constructor(
@unmanaged() private readonly raceService?: RaceService,
@unmanaged() private readonly protestService?: ProtestService,
@unmanaged() private readonly penaltyService?: PenaltyService,
@unmanaged() private readonly driverService?: DriverService,
@unmanaged() private readonly leagueMembershipService?: LeagueMembershipService,
) {}
async getLeagueStewardingData(leagueId: string): Promise<LeagueStewardingViewModel> {
if (!this.raceService || !this.protestService || !this.penaltyService || !this.driverService) {
return new LeagueStewardingViewModel({ racesWithData: [], driverMap: {} });
}
const racesRes = await this.raceService.findByLeagueId(leagueId);
const races = (racesRes as any).value || racesRes;
const racesWithData = await Promise.all(
races.map(async (race: any) => {
const [protestsRes, penaltiesRes] = await Promise.all([
this.protestService!.findByRaceId(race.id),
this.penaltyService!.findByRaceId(race.id),
]);
const protests = (protestsRes as any).value || protestsRes;
const penalties = (penaltiesRes as any).value || penaltiesRes;
return {
race: {
id: race.id,
track: race.track,
scheduledAt: new Date(race.scheduledAt),
},
pendingProtests: protests.filter((p: any) => p.status === 'pending' || p.status === 'under_review'),
resolvedProtests: protests.filter((p: any) => p.status !== 'pending' && p.status !== 'under_review'),
penalties: penalties,
};
}),
);
const driverIds = new Set<string>();
racesWithData.forEach((r: any) => {
r.pendingProtests.forEach((p: any) => {
driverIds.add(p.protestingDriverId);
driverIds.add(p.accusedDriverId);
});
r.resolvedProtests.forEach((p: any) => {
driverIds.add(p.protestingDriverId);
driverIds.add(p.accusedDriverId);
});
r.penalties.forEach((p: any) => driverIds.add(p.driverId));
});
const driversRes = await this.driverService.findByIds(Array.from(driverIds));
const drivers = (driversRes as any).value || driversRes;
const driverMap: Record<string, any> = {};
drivers.forEach((d: any) => {
driverMap[d.id] = d;
});
return new LeagueStewardingViewModel({ racesWithData: racesWithData as any, driverMap });
}
async reviewProtest(input: any): Promise<void> {
if (this.protestService) {
await this.protestService.reviewProtest(input);
}
}
async applyPenalty(input: any): Promise<void> {
if (this.penaltyService) {
await this.penaltyService.applyPenalty(input);
}
}
async getStewardingData(leagueId: string): Promise<Result<StewardingApiDto, DomainError>> {
// Mock data since backend not implemented
const mockData: StewardingApiDto = {
leagueId,
totalPending: 0,
totalResolved: 0,
totalPenalties: 0,
races: [],
drivers: []
};
return Result.ok(mockData);
}
async getProtestDetailViewModel(leagueId: string, protestId: string): Promise<any> {
if (!this.protestService || !this.penaltyService) return null;
const [protestRes, penaltyTypesRes] = await Promise.all([
this.protestService.getProtestById(leagueId, protestId),
this.penaltyService.getPenaltyTypesReference(),
]);
const protestData = (protestRes as any).value || protestRes;
const penaltyTypesData = (penaltyTypesRes as any).value || penaltyTypesRes;
return {
...protestData,
penaltyTypes: penaltyTypesData.penaltyTypes,
defaultReasons: penaltyTypesData.defaultReasons,
initialPenaltyType: penaltyTypesData.penaltyTypes[0]?.type,
};
}
}