website refactor
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import type { LeagueStandingsViewData, StandingEntryData, DriverData, LeagueMembershipData } from '@/lib/view-data/LeagueStandingsViewData';
|
||||
import type { LeagueStandingDTO } from '@/lib/types/generated/LeagueStandingDTO';
|
||||
import type { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';
|
||||
|
||||
interface LeagueStandingsApiDto {
|
||||
standings: LeagueStandingDTO[];
|
||||
}
|
||||
|
||||
interface LeagueMembershipsApiDto {
|
||||
members: LeagueMemberDTO[];
|
||||
}
|
||||
|
||||
/**
|
||||
* LeagueStandingsViewDataBuilder
|
||||
*
|
||||
* Transforms API DTOs into LeagueStandingsViewData for server-side rendering.
|
||||
* Deterministic; side-effect free; no HTTP calls.
|
||||
*/
|
||||
export class LeagueStandingsViewDataBuilder {
|
||||
static build(
|
||||
standingsDto: LeagueStandingsApiDto,
|
||||
membershipsDto: LeagueMembershipsApiDto,
|
||||
leagueId: string
|
||||
): LeagueStandingsViewData {
|
||||
const standings = standingsDto.standings || [];
|
||||
const members = membershipsDto.members || [];
|
||||
|
||||
// Convert LeagueStandingDTO to StandingEntryData
|
||||
const standingData: StandingEntryData[] = standings.map(standing => ({
|
||||
driverId: standing.driverId,
|
||||
position: standing.position,
|
||||
totalPoints: standing.points,
|
||||
racesFinished: standing.races,
|
||||
racesStarted: standing.races,
|
||||
avgFinish: null, // Not in DTO
|
||||
penaltyPoints: 0, // Not in DTO
|
||||
bonusPoints: 0, // Not in DTO
|
||||
}));
|
||||
|
||||
// Extract unique drivers from standings
|
||||
const driverMap = new Map<string, DriverData>();
|
||||
standings.forEach(standing => {
|
||||
if (standing.driver && !driverMap.has(standing.driver.id)) {
|
||||
const driver = standing.driver;
|
||||
driverMap.set(driver.id, {
|
||||
id: driver.id,
|
||||
name: driver.name,
|
||||
avatarUrl: null, // DTO may not have this
|
||||
iracingId: driver.iracingId,
|
||||
rating: undefined,
|
||||
country: driver.country,
|
||||
});
|
||||
}
|
||||
});
|
||||
const driverData: DriverData[] = Array.from(driverMap.values());
|
||||
|
||||
// Convert LeagueMemberDTO to LeagueMembershipData
|
||||
const membershipData: LeagueMembershipData[] = members.map(member => ({
|
||||
driverId: member.driverId,
|
||||
leagueId: leagueId,
|
||||
role: (member.role as LeagueMembershipData['role']) || 'member',
|
||||
joinedAt: member.joinedAt,
|
||||
status: 'active' as const,
|
||||
}));
|
||||
|
||||
return {
|
||||
standings: standingData,
|
||||
drivers: driverData,
|
||||
memberships: membershipData,
|
||||
leagueId,
|
||||
currentDriverId: null, // Would need to get from auth
|
||||
isAdmin: false, // Would need to check permissions
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ProtestDetailViewData } from '@/lib/view-data/leagues/ProtestDetailViewData';
|
||||
|
||||
interface ProtestDetailApiDto {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
status: string;
|
||||
submittedAt: string;
|
||||
incident: {
|
||||
lap: number;
|
||||
description: string;
|
||||
};
|
||||
protestingDriver: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
accusedDriver: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
race: {
|
||||
id: string;
|
||||
name: string;
|
||||
scheduledAt: string;
|
||||
};
|
||||
penaltyTypes: Array<{
|
||||
type: string;
|
||||
label: string;
|
||||
description: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export class ProtestDetailViewDataBuilder {
|
||||
static build(apiDto: ProtestDetailApiDto): ProtestDetailViewData {
|
||||
return {
|
||||
protestId: apiDto.id,
|
||||
leagueId: apiDto.leagueId,
|
||||
status: apiDto.status,
|
||||
submittedAt: apiDto.submittedAt,
|
||||
incident: apiDto.incident,
|
||||
protestingDriver: apiDto.protestingDriver,
|
||||
accusedDriver: apiDto.accusedDriver,
|
||||
race: apiDto.race,
|
||||
penaltyTypes: apiDto.penaltyTypes,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { RulebookViewData } from '@/lib/view-data/leagues/RulebookViewData';
|
||||
import { RulebookApiDto } from '@/lib/types/tbd/RulebookApiDto';
|
||||
|
||||
export class RulebookViewDataBuilder {
|
||||
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): p is { sessionType: string; position: number; points: number } => p.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,
|
||||
championshipsCount: apiDto.scoringConfig.championships.length,
|
||||
sessionTypes: primaryChampionship?.sessionTypes.join(', ') || 'Main',
|
||||
dropPolicySummary: apiDto.scoringConfig.dropPolicySummary,
|
||||
hasActiveDropPolicy: !apiDto.scoringConfig.dropPolicySummary.includes('All'),
|
||||
positionPoints,
|
||||
bonusPoints: primaryChampionship?.bonusSummary || [],
|
||||
hasBonusPoints: (primaryChampionship?.bonusSummary.length || 0) > 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { StewardingApiDto } from '@/lib/types/tbd/StewardingApiDto';
|
||||
import { StewardingViewData } from '@/lib/view-data/leagues/StewardingViewData';
|
||||
|
||||
|
||||
export class StewardingViewDataBuilder {
|
||||
static build(apiDto: StewardingApiDto): StewardingViewData {
|
||||
return {
|
||||
leagueId: apiDto.leagueId,
|
||||
totalPending: apiDto.totalPending || 0,
|
||||
totalResolved: apiDto.totalResolved || 0,
|
||||
totalPenalties: apiDto.totalPenalties || 0,
|
||||
races: (apiDto.races || []).map((race) => ({
|
||||
id: race.id,
|
||||
track: race.track,
|
||||
scheduledAt: race.scheduledAt,
|
||||
pendingProtests: race.pendingProtests || [],
|
||||
resolvedProtests: race.resolvedProtests || [],
|
||||
penalties: race.penalties || [],
|
||||
})),
|
||||
drivers: (apiDto.drivers || []).map((driver) => ({
|
||||
id: driver.id,
|
||||
name: driver.name,
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user