presenter refactoring

This commit is contained in:
2025-12-20 17:06:11 +01:00
parent 92be9d2e1b
commit e9d6f90bb2
109 changed files with 4159 additions and 1283 deletions

View File

@@ -0,0 +1,29 @@
import type { CompleteDriverOnboardingOutputPort } from '@core/racing/application/ports/output/CompleteDriverOnboardingOutputPort';
import type { CompleteOnboardingOutputDTO } from '../dtos/CompleteOnboardingOutputDTO';
export class CompleteOnboardingPresenter {
private result: CompleteOnboardingOutputDTO | null = null;
reset(): void {
this.result = null;
}
present(output: CompleteDriverOnboardingOutputPort): void {
this.result = {
success: true,
driverId: output.driverId,
};
}
presentError(errorCode: string): void {
this.result = {
success: false,
errorMessage: errorCode,
};
}
get viewModel(): CompleteOnboardingOutputDTO {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,30 @@
import type { Driver } from '@core/racing/domain/entities/Driver';
import type { GetDriverOutputDTO } from '../dtos/GetDriverOutputDTO';
export class DriverPresenter {
private result: GetDriverOutputDTO | null = null;
reset(): void {
this.result = null;
}
present(driver: Driver | null): void {
if (!driver) {
this.result = null;
return;
}
this.result = {
id: driver.id,
iracingId: driver.iracingId.toString(),
name: driver.name.toString(),
country: driver.country.toString(),
bio: driver.bio?.toString(),
joinedAt: driver.joinedAt.toDate().toISOString(),
};
}
get viewModel(): GetDriverOutputDTO | null {
return this.result;
}
}

View File

@@ -0,0 +1,47 @@
import type { ProfileOverviewOutputPort } from '@core/racing/application/ports/output/ProfileOverviewOutputPort';
import type { GetDriverProfileOutputDTO } from '../dtos/GetDriverProfileOutputDTO';
export class DriverProfilePresenter {
private result: GetDriverProfileOutputDTO | null = null;
reset(): void {
this.result = null;
}
present(output: ProfileOverviewOutputPort): void {
this.result = {
currentDriver: output.driver
? {
id: output.driver.id,
name: output.driver.name,
country: output.driver.country,
avatarUrl: output.driver.avatarUrl,
iracingId: output.driver.iracingId,
joinedAt: output.driver.joinedAt.toISOString(),
rating: output.driver.rating,
globalRank: output.driver.globalRank,
consistency: output.driver.consistency,
bio: output.driver.bio,
totalDrivers: output.driver.totalDrivers,
}
: null,
stats: output.stats,
finishDistribution: output.finishDistribution,
teamMemberships: output.teamMemberships.map(membership => ({
teamId: membership.teamId,
teamName: membership.teamName,
teamTag: membership.teamTag,
role: membership.role,
joinedAt: membership.joinedAt.toISOString(),
isCurrent: membership.isCurrent,
})),
socialSummary: output.socialSummary,
extendedProfile: output.extendedProfile,
};
}
get viewModel(): GetDriverProfileOutputDTO {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,25 @@
import { DriverRegistrationStatusDTO } from '../dtos/DriverRegistrationStatusDTO';
export class DriverRegistrationStatusPresenter {
private result: DriverRegistrationStatusDTO | null = null;
reset(): void {
this.result = null;
}
present(isRegistered: boolean, raceId: string, driverId: string): void {
this.result = {
isRegistered,
raceId,
driverId,
};
}
get viewModel(): DriverRegistrationStatusDTO {
if (!this.result) {
throw new Error('Presenter not presented');
}
return this.result;
}
}

View File

@@ -1,51 +1,31 @@
import { DriversLeaderboardDTO } from '../dtos/DriversLeaderboardDTO';
import { DriverLeaderboardItemDTO } from '../dtos/DriverLeaderboardItemDTO';
import type { IDriversLeaderboardPresenter, DriversLeaderboardResultDTO } from '../../../../../core/racing/application/presenters/IDriversLeaderboardPresenter';
import { SkillLevelService } from '../../../../../core/racing/domain/services/SkillLevelService';
import type { DriversLeaderboardOutputPort } from '../../../../../core/racing/application/ports/output/DriversLeaderboardOutputPort';
export class DriversLeaderboardPresenter implements IDriversLeaderboardPresenter {
export class DriversLeaderboardPresenter {
private result: DriversLeaderboardDTO | null = null;
reset() {
reset(): void {
this.result = null;
}
present(dto: DriversLeaderboardResultDTO) {
const drivers: DriverLeaderboardItemDTO[] = dto.drivers.map(driver => {
const ranking = dto.rankings.find(r => r.driverId === driver.id);
const stats = dto.stats[driver.id];
const avatarUrl = dto.avatarUrls[driver.id];
const rating = ranking?.rating ?? 0;
const racesCompleted = stats?.racesCompleted ?? 0;
return {
present(output: DriversLeaderboardOutputPort): void {
this.result = {
drivers: output.drivers.map(driver => ({
id: driver.id,
name: driver.name,
rating,
// Use core SkillLevelService to derive band from rating
skillLevel: SkillLevelService.getSkillLevel(rating),
nationality: driver.country,
racesCompleted,
wins: stats?.wins ?? 0,
podiums: stats?.podiums ?? 0,
// Consider a driver active if they have completed at least one race
isActive: racesCompleted > 0,
rank: ranking?.overallRank ?? 0,
avatarUrl,
};
});
// Calculate totals
const totalRaces = drivers.reduce((sum, d) => sum + (d.racesCompleted ?? 0), 0);
const totalWins = drivers.reduce((sum, d) => sum + (d.wins ?? 0), 0);
const activeCount = drivers.filter(d => d.isActive).length;
this.result = {
drivers: drivers.sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0)),
totalRaces,
totalWins,
activeCount,
rating: driver.rating,
skillLevel: driver.skillLevel,
nationality: driver.nationality,
racesCompleted: driver.racesCompleted,
wins: driver.wins,
podiums: driver.podiums,
isActive: driver.isActive,
rank: driver.rank,
avatarUrl: driver.avatarUrl,
})),
totalRaces: output.totalRaces,
totalWins: output.totalWins,
activeCount: output.activeCount,
};
}