/** * Rulebook View Data Builder * * Transforms API DTO to ViewData for templates. */ import type { RulebookViewData } from '@/lib/view-data/RulebookViewData'; import { ViewDataBuilder } from "../../contracts/builders/ViewDataBuilder"; interface RulebookApiDto { leagueId: string; scoringConfig: { gameName: string; scoringPresetName: string; championships: Array<{ type: string; sessionTypes: string[]; pointsPreview: Array<{ sessionType: string; position: number; points: number; }>; bonusSummary: string[]; }>; dropPolicySummary: string; }; } export class RulebookViewDataBuilder { /** * Transform API DTO to ViewData * * @param apiDto - The DTO from the service * @returns ViewData for the rulebook page */ public static build(apiDto: RulebookApiDto): RulebookViewData { const primaryChampionship = apiDto.scoringConfig.championships.find(c => c.type === 'driver') ?? apiDto.scoringConfig.championships[0]; const positionPoints: { position: number; points: number }[] = (primaryChampionship?.pointsPreview || []) .filter((p: unknown): p is { sessionType: string; position: number; points: number } => { const point = p as { sessionType?: string; position?: number; points?: number }; return point.sessionType === primaryChampionship?.sessionTypes[0]; }) .map(p => ({ position: p.position, points: p.points })) .sort((a, b) => a.position - b.position); return { leagueId: apiDto.leagueId, gameName: apiDto.scoringConfig.gameName, scoringPresetName: apiDto.scoringConfig.scoringPresetName || 'Custom', championshipsCount: apiDto.scoringConfig.championships.length, sessionTypes: primaryChampionship?.sessionTypes.join(', ') || 'Main', dropPolicySummary: apiDto.scoringConfig.dropPolicySummary, hasActiveDropPolicy: !!apiDto.scoringConfig.dropPolicySummary && !apiDto.scoringConfig.dropPolicySummary.toLowerCase().includes('all'), positionPoints, bonusPoints: primaryChampionship?.bonusSummary || [], hasBonusPoints: (primaryChampionship?.bonusSummary.length || 0) > 0, }; } } RulebookViewDataBuilder satisfies ViewDataBuilder;