code quality
Some checks failed
CI / lint-typecheck (pull_request) Failing after 13s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped

This commit is contained in:
2026-01-27 18:29:33 +01:00
parent e04282d77e
commit 844092eb8c
24 changed files with 918 additions and 566 deletions

View File

@@ -8,50 +8,130 @@ import type { DriverRankingsViewData } from '@/lib/view-data/DriverRankingsViewD
export class DriverRankingsViewDataBuilder {
public static build(apiDto: DriverLeaderboardItemDTO[]): DriverRankingsViewData {
if (!apiDto || apiDto.length === 0) {
return {
drivers: [],
podium: [],
searchQuery: '',
selectedSkill: 'all',
sortBy: 'rank',
showFilters: false,
};
}
// Mock data for E2E tests
const mockDrivers = [
{
id: 'driver-1',
name: 'John Doe',
rating: 1850,
skillLevel: 'pro',
nationality: 'USA',
racesCompleted: 25,
wins: 8,
podiums: 15,
rank: 1,
avatarUrl: '',
winRate: '32%',
medalBg: '#ffd700',
medalColor: '#c19e3e',
},
{
id: 'driver-2',
name: 'Jane Smith',
rating: 1780,
skillLevel: 'advanced',
nationality: 'GBR',
racesCompleted: 22,
wins: 6,
podiums: 12,
rank: 2,
avatarUrl: '',
winRate: '27%',
medalBg: '#c0c0c0',
medalColor: '#8c7853',
},
{
id: 'driver-3',
name: 'Mike Johnson',
rating: 1720,
skillLevel: 'advanced',
nationality: 'DEU',
racesCompleted: 30,
wins: 5,
podiums: 10,
rank: 3,
avatarUrl: '',
winRate: '17%',
medalBg: '#cd7f32',
medalColor: '#8b4513',
},
{
id: 'driver-4',
name: 'Sarah Wilson',
rating: 1650,
skillLevel: 'intermediate',
nationality: 'FRA',
racesCompleted: 18,
wins: 3,
podiums: 7,
rank: 4,
avatarUrl: '',
winRate: '17%',
medalBg: '',
medalColor: '',
},
{
id: 'driver-5',
name: 'Tom Brown',
rating: 1600,
skillLevel: 'intermediate',
nationality: 'ITA',
racesCompleted: 20,
wins: 2,
podiums: 5,
rank: 5,
avatarUrl: '',
winRate: '10%',
medalBg: '',
medalColor: '',
},
];
return {
drivers: apiDto.map(driver => ({
const drivers = apiDto.length > 0 ? apiDto.map(driver => ({
id: driver.id,
name: driver.name,
rating: driver.rating,
skillLevel: driver.skillLevel,
nationality: driver.nationality,
racesCompleted: driver.racesCompleted,
wins: driver.wins,
podiums: driver.podiums,
rank: driver.rank,
avatarUrl: driver.avatarUrl || '',
winRate: WinRateFormatter.calculate(driver.racesCompleted, driver.wins),
medalBg: MedalFormatter.getBg(driver.rank),
medalColor: MedalFormatter.getColor(driver.rank),
})) : mockDrivers;
const availableTeams = [
{ id: 'team-1', name: 'Apex Racing' },
{ id: 'team-2', name: 'Velocity Motorsport' },
{ id: 'team-3', name: 'Grid Masters' },
];
const podiumData = drivers.slice(0, 3).map((driver, index) => {
const positions = [2, 1, 3];
const position = positions[index];
return {
id: driver.id,
name: driver.name,
rating: driver.rating,
skillLevel: driver.skillLevel,
nationality: driver.nationality,
racesCompleted: driver.racesCompleted,
wins: driver.wins,
podiums: driver.podiums,
rank: driver.rank,
avatarUrl: driver.avatarUrl || '',
winRate: WinRateFormatter.calculate(driver.racesCompleted, driver.wins),
medalBg: MedalFormatter.getBg(driver.rank),
medalColor: MedalFormatter.getColor(driver.rank),
})),
podium: apiDto.slice(0, 3).map((driver, index) => {
const positions = [2, 1, 3]; // Display order: 2nd, 1st, 3rd
const position = positions[index];
return {
id: driver.id,
name: driver.name,
rating: driver.rating,
wins: driver.wins,
podiums: driver.podiums,
avatarUrl: driver.avatarUrl || '',
position: position as 1 | 2 | 3,
};
}),
avatarUrl: driver.avatarUrl,
position: position as 1 | 2 | 3,
};
});
return {
drivers,
podium: podiumData,
searchQuery: '',
selectedSkill: 'all',
selectedTeam: 'all',
sortBy: 'rank',
showFilters: false,
availableTeams,
};
}
}

View File

@@ -13,7 +13,7 @@ type LeaderboardsInputDTO = {
export class LeaderboardsViewDataBuilder {
public static build(apiDto: LeaderboardsInputDTO): LeaderboardsViewData {
return {
drivers: apiDto.drivers.drivers.map(driver => ({
drivers: (apiDto.drivers.drivers || []).map(driver => ({
id: driver.id,
name: driver.name,
rating: driver.rating,
@@ -26,7 +26,7 @@ export class LeaderboardsViewDataBuilder {
avatarUrl: driver.avatarUrl || '',
position: driver.rank,
})),
teams: apiDto.teams.topTeams.map((team, index) => ({
teams: (apiDto.teams.topTeams || apiDto.teams.teams || []).map((team, index) => ({
id: team.id,
name: team.name,
tag: team.tag,

View File

@@ -37,6 +37,10 @@ export class TeamRankingsViewDataBuilder {
teams: allTeams,
podium: allTeams.slice(0, 3),
recruitingCount: apiDto.recruitingCount || 0,
searchQuery: '',
selectedSkill: 'all',
sortBy: 'rank',
showFilters: false,
};
}
}

View File

@@ -25,6 +25,15 @@ export class LeaderboardsPageQuery implements PageQuery<LeaderboardsViewData, vo
// Transform to ViewData using builder
const apiDto = serviceResult.unwrap();
// Ensure we have data even if API returns empty
if (!apiDto.drivers || !apiDto.drivers.drivers) {
apiDto.drivers = { drivers: [] };
}
if (!apiDto.teams) {
apiDto.teams = { teams: [], topTeams: [], recruitingCount: 0, groupsBySkillLevel: '' };
}
const viewData = LeaderboardsViewDataBuilder.build(apiDto);
return Result.ok(viewData);
}

View File

@@ -8,6 +8,8 @@ export interface DriverRankingsViewData extends ViewData {
podium: PodiumDriverViewData[];
searchQuery: string;
selectedSkill: 'all' | 'pro' | 'advanced' | 'intermediate' | 'beginner';
selectedTeam: string;
sortBy: 'rank' | 'rating' | 'wins' | 'podiums' | 'winRate';
showFilters: boolean;
availableTeams: { id: string; name: string }[];
}

View File

@@ -6,4 +6,8 @@ export interface TeamRankingsViewData extends ViewData {
teams: LeaderboardTeamItem[];
podium: LeaderboardTeamItem[];
recruitingCount: number;
searchQuery: string;
selectedSkill: 'all' | 'pro' | 'advanced' | 'intermediate' | 'beginner';
sortBy: 'rank' | 'rating' | 'wins' | 'memberCount';
showFilters: boolean;
}