view data fixes
This commit is contained in:
@@ -1,33 +1,32 @@
|
||||
'use client';
|
||||
|
||||
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
|
||||
import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
|
||||
import type { RaceDTO } from '@/lib/types/generated/RaceDTO';
|
||||
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
||||
import type { LeagueScoringConfigDTO } from '@/lib/types/generated/LeagueScoringConfigDTO';
|
||||
import type { LeagueDetailViewData, LeagueInfoData, LiveRaceData, DriverSummaryData, SponsorInfo, NextRaceInfo, SeasonProgress, RecentResult } from '@/lib/view-data/LeagueDetailViewData';
|
||||
import type { ViewDataBuilder } from '@/lib/contracts/builders/ViewDataBuilder';
|
||||
|
||||
/**
|
||||
* LeagueDetailViewDataBuilder
|
||||
*
|
||||
* Transforms API DTOs into LeagueDetailViewData for server-side rendering.
|
||||
* Deterministic; side-effect free; no HTTP calls.
|
||||
*/
|
||||
import { ViewDataBuilder } from "../../contracts/builders/ViewDataBuilder";
|
||||
type LeagueDetailInputDTO = {
|
||||
league: LeagueWithCapacityAndScoringDTO;
|
||||
owner: GetDriverOutputDTO | null;
|
||||
scoringConfig: LeagueScoringConfigDTO | null;
|
||||
memberships: LeagueMembershipsDTO;
|
||||
races: RaceDTO[];
|
||||
sponsors: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
tier: string;
|
||||
logoUrl?: string;
|
||||
websiteUrl?: string;
|
||||
tagline?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
build(input: any): any {
|
||||
return LeagueDetailViewDataBuilder.build(input);
|
||||
}
|
||||
|
||||
static build(
|
||||
static build(input: {
|
||||
league: LeagueWithCapacityAndScoringDTO;
|
||||
owner: GetDriverOutputDTO | null;
|
||||
scoringConfig: LeagueScoringConfigDTO | null;
|
||||
memberships: LeagueMembershipsDTO;
|
||||
races: RaceDTO[];
|
||||
sponsors: any[];
|
||||
}): LeagueDetailViewData {
|
||||
const { league, owner, scoringConfig, memberships, races, sponsors } = input;
|
||||
export class LeagueDetailViewDataBuilder {
|
||||
public static build(apiDto: LeagueDetailInputDTO): LeagueDetailViewData {
|
||||
const { league, owner, scoringConfig, memberships, races, sponsors } = apiDto;
|
||||
|
||||
// Calculate running races - using available fields from RaceDTO
|
||||
const runningRaces: LiveRaceData[] = races
|
||||
@@ -44,31 +43,17 @@ export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
const membersCount = Array.isArray(memberships.members) ? memberships.members.length : 0;
|
||||
|
||||
// League overview wants total races, not just completed.
|
||||
// (In seed/demo data many races are `status: running`, which should still count.)
|
||||
const racesCount = races.length;
|
||||
|
||||
// Compute real avgSOF from races
|
||||
const racesWithSOF = races.filter(r => {
|
||||
const sof = (r as any).strengthOfField;
|
||||
const sof = (r as RaceDTO & { strengthOfField?: number }).strengthOfField;
|
||||
return typeof sof === 'number' && sof > 0;
|
||||
});
|
||||
const avgSOF = racesWithSOF.length > 0
|
||||
? Math.round(racesWithSOF.reduce((sum, r) => sum + ((r as any).strengthOfField || 0), 0) / racesWithSOF.length)
|
||||
? Math.round(racesWithSOF.reduce((sum, r) => sum + ((r as RaceDTO & { strengthOfField?: number }).strengthOfField || 0), 0) / racesWithSOF.length)
|
||||
: null;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const race0 = races.length > 0 ? races[0] : null;
|
||||
console.info(
|
||||
'[LeagueDetailViewDataBuilder] leagueId=%s members=%d races=%d racesWithSOF=%d avgSOF=%s race0=%o',
|
||||
league.id,
|
||||
membersCount,
|
||||
racesCount,
|
||||
racesWithSOF.length,
|
||||
String(avgSOF),
|
||||
race0,
|
||||
);
|
||||
}
|
||||
|
||||
const info: LeagueInfoData = {
|
||||
name: league.name,
|
||||
description: league.description || '',
|
||||
@@ -111,7 +96,7 @@ export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
.map(m => ({
|
||||
driverId: m.driverId,
|
||||
driverName: m.driver.name,
|
||||
avatarUrl: (m.driver as any).avatarUrl || null,
|
||||
avatarUrl: (m.driver as GetDriverOutputDTO & { avatarUrl?: string }).avatarUrl || null,
|
||||
rating: null,
|
||||
rank: null,
|
||||
roleBadgeText: 'Admin',
|
||||
@@ -124,7 +109,7 @@ export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
.map(m => ({
|
||||
driverId: m.driverId,
|
||||
driverName: m.driver.name,
|
||||
avatarUrl: (m.driver as any).avatarUrl || null,
|
||||
avatarUrl: (m.driver as GetDriverOutputDTO & { avatarUrl?: string }).avatarUrl || null,
|
||||
rating: null,
|
||||
rank: null,
|
||||
roleBadgeText: 'Steward',
|
||||
@@ -137,7 +122,7 @@ export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
.map(m => ({
|
||||
driverId: m.driverId,
|
||||
driverName: m.driver.name,
|
||||
avatarUrl: (m.driver as any).avatarUrl || null,
|
||||
avatarUrl: (m.driver as GetDriverOutputDTO & { avatarUrl?: string }).avatarUrl || null,
|
||||
rating: null,
|
||||
rank: null,
|
||||
roleBadgeText: 'Member',
|
||||
@@ -154,8 +139,8 @@ export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
date: r.date,
|
||||
track: (r as any).track,
|
||||
car: (r as any).car,
|
||||
track: (r as RaceDTO & { track?: string }).track || '',
|
||||
car: (r as RaceDTO & { car?: string }).car || '',
|
||||
}))[0];
|
||||
|
||||
// Calculate season progress (completed races vs total races)
|
||||
@@ -179,8 +164,8 @@ export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
.map(r => ({
|
||||
raceId: r.id,
|
||||
raceName: r.name,
|
||||
position: (r as any).position || 0,
|
||||
points: (r as any).points || 0,
|
||||
position: (r as RaceDTO & { position?: number }).position || 0,
|
||||
points: (r as RaceDTO & { points?: number }).points || 0,
|
||||
finishedAt: r.date,
|
||||
}));
|
||||
|
||||
@@ -196,13 +181,15 @@ export class LeagueDetailViewDataBuilder implements ViewDataBuilder<any, any> {
|
||||
adminSummaries,
|
||||
stewardSummaries,
|
||||
memberSummaries,
|
||||
sponsorInsights: null, // Only for sponsor mode
|
||||
sponsorInsights: null,
|
||||
nextRace,
|
||||
seasonProgress,
|
||||
recentResults,
|
||||
walletBalance: league.walletBalance,
|
||||
pendingProtestsCount: league.pendingProtestsCount,
|
||||
pendingJoinRequestsCount: league.pendingJoinRequestsCount,
|
||||
walletBalance: league.walletBalance ?? 0,
|
||||
pendingProtestsCount: league.pendingProtestsCount ?? 0,
|
||||
pendingJoinRequestsCount: league.pendingJoinRequestsCount ?? 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LeagueDetailViewDataBuilder satisfies ViewDataBuilder<LeagueDetailInputDTO, LeagueDetailViewData>;
|
||||
|
||||
Reference in New Issue
Block a user