services refactor

This commit is contained in:
2025-12-17 22:17:02 +01:00
parent 26f7a2b6aa
commit 055a7f67b5
93 changed files with 7434 additions and 659 deletions

View File

@@ -0,0 +1,8 @@
import { AnalyticsDashboardDto } from '../dtos';
import { AnalyticsDashboardViewModel } from '../view-models';
export class AnalyticsDashboardPresenter {
present(dto: AnalyticsDashboardDto): AnalyticsDashboardViewModel {
return new AnalyticsDashboardViewModel(dto);
}
}

View File

@@ -0,0 +1,8 @@
import { AnalyticsMetricsDto } from '../dtos';
import { AnalyticsMetricsViewModel } from '../view-models';
export class AnalyticsMetricsPresenter {
present(dto: AnalyticsMetricsDto): AnalyticsMetricsViewModel {
return new AnalyticsMetricsViewModel(dto);
}
}

View File

@@ -0,0 +1,39 @@
import type {
GetAvatarOutputDto,
RequestAvatarGenerationOutputDto,
UpdateAvatarOutputDto
} from '../dtos';
import type {
AvatarViewModel,
RequestAvatarGenerationViewModel,
UpdateAvatarViewModel
} from '../view-models';
/**
* Avatar Presenter
* Transforms avatar DTOs to ViewModels
*/
export class AvatarPresenter {
presentAvatar(dto: GetAvatarOutputDto): AvatarViewModel {
return {
driverId: dto.driverId,
avatarUrl: dto.avatarUrl,
hasAvatar: dto.hasAvatar,
};
}
presentRequestGeneration(dto: RequestAvatarGenerationOutputDto): RequestAvatarGenerationViewModel {
return {
success: dto.success,
avatarUrl: dto.avatarUrl,
error: dto.error,
};
}
presentUpdate(dto: UpdateAvatarOutputDto): UpdateAvatarViewModel {
return {
success: dto.success,
error: dto.error,
};
}
}

View File

@@ -0,0 +1,15 @@
import type { CompleteOnboardingOutputDto } from '../dtos';
import type { CompleteOnboardingViewModel } from '../view-models/CompleteOnboardingViewModel';
/**
* Complete Onboarding Presenter
* Transforms CompleteOnboardingOutputDto to CompleteOnboardingViewModel
*/
export class CompleteOnboardingPresenter {
present(dto: CompleteOnboardingOutputDto): CompleteOnboardingViewModel {
return {
driverId: dto.driverId,
success: dto.success,
};
}
}

View File

@@ -0,0 +1,18 @@
import type { DriverDto } from '../dtos';
import type { DriverViewModel } from '../view-models/DriverViewModel';
/**
* Driver Presenter
* Transforms DriverDto to DriverViewModel
*/
export class DriverPresenter {
present(dto: DriverDto): DriverViewModel {
return {
id: dto.id,
name: dto.name,
avatarUrl: dto.avatarUrl,
iracingId: dto.iracingId,
rating: dto.rating,
};
}
}

View File

@@ -1,6 +1,19 @@
import { DriverRegistrationStatusDto } from '../dtos';
import { DriverRegistrationStatusViewModel } from '../view-models';
import type { DriverRegistrationStatusDto } from '../dtos';
import type { DriverRegistrationStatusViewModel } from '../view-models';
import { DriverRegistrationStatusViewModel as ViewModel } from '../view-models';
/**
* Driver Registration Status Presenter
* Transforms DriverRegistrationStatusDto to DriverRegistrationStatusViewModel
*/
export class DriverRegistrationStatusPresenter {
present(dto: DriverRegistrationStatusDto): DriverRegistrationStatusViewModel {
return new ViewModel(dto);
}
}
// Legacy functional export for backward compatibility
export const presentDriverRegistrationStatus = (dto: DriverRegistrationStatusDto): DriverRegistrationStatusViewModel => {
return new DriverRegistrationStatusViewModel(dto);
const presenter = new DriverRegistrationStatusPresenter();
return presenter.present(dto);
};

View File

@@ -1,6 +1,18 @@
import { DriversLeaderboardDto, DriverLeaderboardItemDto } from '../dtos';
import { DriverLeaderboardViewModel } from '../view-models';
import type { DriversLeaderboardDto } from '../dtos';
import type { DriverLeaderboardViewModel } from '../view-models';
export const presentDriversLeaderboard = (dto: DriversLeaderboardDto, previousDrivers?: DriverLeaderboardItemDto[]): DriverLeaderboardViewModel => {
return new DriverLeaderboardViewModel(dto, previousDrivers);
/**
* Drivers Leaderboard Presenter
* Transforms DriversLeaderboardDto to DriverLeaderboardViewModel
*/
export class DriversLeaderboardPresenter {
present(dto: DriversLeaderboardDto, previousDrivers?: any): DriverLeaderboardViewModel {
return new DriverLeaderboardViewModel(dto as any, previousDrivers);
}
}
// Legacy functional export for backward compatibility
export const presentDriversLeaderboard = (dto: DriversLeaderboardDto, previousDrivers?: any): DriverLeaderboardViewModel => {
const presenter = new DriversLeaderboardPresenter();
return presenter.present(dto, previousDrivers);
};

View File

@@ -1,17 +1,21 @@
import type {
IImportRaceResultsPresenter,
ImportRaceResultsSummaryViewModel,
} from '@core/racing/application/presenters/IImportRaceResultsPresenter';
import type { ImportRaceResultsSummaryDto } from '../dtos/ImportRaceResultsSummaryDto';
export class ImportRaceResultsPresenter implements IImportRaceResultsPresenter {
private viewModel: ImportRaceResultsSummaryViewModel | null = null;
export interface ImportRaceResultsSummaryViewModel {
success: boolean;
raceId: string;
driversProcessed: number;
resultsRecorded: number;
errors?: string[];
}
present(viewModel: ImportRaceResultsSummaryViewModel): ImportRaceResultsSummaryViewModel {
this.viewModel = viewModel;
return this.viewModel;
}
getViewModel(): ImportRaceResultsSummaryViewModel | null {
return this.viewModel;
export class ImportRaceResultsPresenter {
present(dto: ImportRaceResultsSummaryDto): ImportRaceResultsSummaryViewModel {
return {
success: dto.success,
raceId: dto.raceId,
driversProcessed: dto.driversProcessed,
resultsRecorded: dto.resultsRecorded,
errors: dto.errors,
};
}
}

View File

@@ -0,0 +1,16 @@
import type { LeagueMembershipsDto } from '../dtos';
import { LeagueMemberViewModel } from '../view-models';
/**
* League Members Presenter
*
* Transforms league memberships DTO to view models for the UI.
*/
export class LeagueMembersPresenter {
/**
* Present league memberships with current user context
*/
present(dto: LeagueMembershipsDto, currentUserId: string): LeagueMemberViewModel[] {
return dto.members.map(member => new LeagueMemberViewModel(member, currentUserId));
}
}

View File

@@ -1,6 +1,18 @@
import { LeagueStandingsDto, StandingEntryDto } from '../dtos';
import type { LeagueStandingsDto, StandingEntryDto } from '../dtos';
import { LeagueStandingsViewModel } from '../view-models';
/**
* League Standings Presenter
* Transforms LeagueStandingsDto to LeagueStandingsViewModel
*/
export class LeagueStandingsPresenter {
present(dto: LeagueStandingsDto, currentUserId: string, previousStandings?: StandingEntryDto[]): LeagueStandingsViewModel {
return new LeagueStandingsViewModel(dto, currentUserId, previousStandings);
}
}
// Legacy functional export for backward compatibility
export const presentLeagueStandings = (dto: LeagueStandingsDto, currentUserId: string, previousStandings?: StandingEntryDto[]): LeagueStandingsViewModel => {
return new LeagueStandingsViewModel(dto, currentUserId, previousStandings);
const presenter = new LeagueStandingsPresenter();
return presenter.present(dto, currentUserId, previousStandings);
};

View File

@@ -1,10 +1,21 @@
import { LeagueSummaryDto } from '../dtos';
import type { AllLeaguesWithCapacityDto } from '../dtos';
import { LeagueSummaryViewModel } from '../view-models';
export const presentLeagueSummary = (dto: LeagueSummaryDto): LeagueSummaryViewModel => {
/**
* League Summary Presenter
* Transforms AllLeaguesWithCapacityDto to array of LeagueSummaryViewModel
*/
export class LeagueSummaryPresenter {
present(dto: AllLeaguesWithCapacityDto): LeagueSummaryViewModel[] {
return dto.leagues.map(league => new LeagueSummaryViewModel(league));
}
}
// Legacy functional exports for backward compatibility
export const presentLeagueSummary = (dto: any): LeagueSummaryViewModel => {
return new LeagueSummaryViewModel(dto);
};
export const presentLeagueSummaries = (dtos: LeagueSummaryDto[]): LeagueSummaryViewModel[] => {
export const presentLeagueSummaries = (dtos: any[]): LeagueSummaryViewModel[] => {
return dtos.map(presentLeagueSummary);
};

View File

@@ -0,0 +1,35 @@
import type { GetMediaOutputDto, UploadMediaOutputDto, DeleteMediaOutputDto } from '../dtos';
import type { MediaViewModel, UploadMediaViewModel, DeleteMediaViewModel } from '../view-models';
/**
* Media Presenter
* Transforms media DTOs to ViewModels
*/
export class MediaPresenter {
presentMedia(dto: GetMediaOutputDto): MediaViewModel {
return {
id: dto.id,
url: dto.url,
type: dto.type,
category: dto.category,
uploadedAt: new Date(dto.uploadedAt),
size: dto.size,
};
}
presentUpload(dto: UploadMediaOutputDto): UploadMediaViewModel {
return {
success: dto.success,
mediaId: dto.mediaId,
url: dto.url,
error: dto.error,
};
}
presentDelete(dto: DeleteMediaOutputDto): DeleteMediaViewModel {
return {
success: dto.success,
error: dto.error,
};
}
}

View File

@@ -0,0 +1,17 @@
import type { GetPaymentsOutputDto } from '../dtos';
import { PaymentViewModel } from '../view-models';
import { presentPayment } from './PaymentPresenter';
/**
* Payment List Presenter
*
* Transforms payment list DTOs into ViewModels for UI consumption.
*/
export class PaymentListPresenter {
/**
* Transform payment list DTO to ViewModels
*/
present(dto: GetPaymentsOutputDto): PaymentViewModel[] {
return dto.payments.map(payment => presentPayment(payment));
}
}

View File

@@ -1,25 +1,17 @@
import type {
IRaceWithSOFPresenter,
RaceWithSOFResultDTO,
RaceWithSOFViewModel,
} from '@core/racing/application/presenters/IRaceWithSOFPresenter';
import type { RaceWithSOFDto } from '../dtos/RaceWithSOFDto';
export class RaceWithSOFPresenter implements IRaceWithSOFPresenter {
present(dto: RaceWithSOFResultDTO): RaceWithSOFViewModel {
export interface RaceWithSOFViewModel {
id: string;
track: string;
strengthOfField: number | null;
}
export class RaceWithSOFPresenter {
present(dto: RaceWithSOFDto): RaceWithSOFViewModel {
return {
id: dto.raceId,
leagueId: dto.leagueId,
scheduledAt: dto.scheduledAt.toISOString(),
id: dto.id,
track: dto.track,
trackId: dto.trackId,
car: dto.car,
carId: dto.carId,
sessionType: dto.sessionType,
status: dto.status,
strengthOfField: dto.strengthOfField,
registeredCount: dto.registeredCount,
maxParticipants: dto.maxParticipants,
participantCount: dto.participantCount,
};
}
}

View File

@@ -0,0 +1,13 @@
import type { SessionDataDto } from '../dtos';
import { SessionViewModel } from '../view-models';
/**
* Session Presenter
* Transforms session DTOs to ViewModels
*/
export class SessionPresenter {
presentSession(dto: SessionDataDto | null): SessionViewModel | null {
if (!dto) return null;
return new SessionViewModel(dto);
}
}

View File

@@ -1,25 +1,13 @@
import type {
ISponsorDashboardPresenter,
SponsorDashboardViewModel,
} from '@core/racing/application/presenters/ISponsorDashboardPresenter';
import type { SponsorDashboardDTO } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
import type { SponsorDashboardDto } from '../dtos';
import { SponsorDashboardViewModel } from '../view-models/SponsorDashboardViewModel';
export class SponsorDashboardPresenter implements ISponsorDashboardPresenter {
private viewModel: SponsorDashboardViewModel = null;
reset(): void {
this.viewModel = null;
}
present(data: SponsorDashboardDTO | null): void {
this.viewModel = data;
}
getViewModel(): SponsorDashboardViewModel {
return this.viewModel;
}
getData(): SponsorDashboardDTO | null {
return this.viewModel;
/**
* Sponsor Dashboard Presenter
*
* Transforms sponsor dashboard DTOs into view models.
*/
export class SponsorDashboardPresenter {
present(dto: SponsorDashboardDto): SponsorDashboardViewModel {
return new SponsorDashboardViewModel(dto);
}
}

View File

@@ -0,0 +1,13 @@
import type { GetSponsorsOutputDto } from '../dtos';
import { SponsorViewModel } from '../view-models';
/**
* Sponsor List Presenter
*
* Transforms sponsor list DTOs into view models.
*/
export class SponsorListPresenter {
present(dto: GetSponsorsOutputDto): SponsorViewModel[] {
return dto.sponsors.map(sponsor => new SponsorViewModel(sponsor));
}
}

View File

@@ -1,6 +1,13 @@
import { SponsorDto } from '../dtos';
import type { SponsorDto } from '../dtos';
import { SponsorViewModel } from '../view-models';
export const presentSponsor = (dto: SponsorDto): SponsorViewModel => {
return new SponsorViewModel(dto);
};
/**
* Sponsor Presenter
*
* Transforms sponsor DTOs into view models.
*/
export class SponsorPresenter {
present(dto: SponsorDto): SponsorViewModel {
return new SponsorViewModel(dto);
}
}

View File

@@ -1,25 +1,13 @@
import type {
ISponsorSponsorshipsPresenter,
SponsorSponsorshipsViewModel,
} from '@core/racing/application/presenters/ISponsorSponsorshipsPresenter';
import type { SponsorSponsorshipsDTO } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
import type { SponsorSponsorshipsDto } from '../dtos';
import { SponsorSponsorshipsViewModel } from '../view-models/SponsorSponsorshipsViewModel';
export class SponsorSponsorshipsPresenter implements ISponsorSponsorshipsPresenter {
private viewModel: SponsorSponsorshipsViewModel = null;
reset(): void {
this.viewModel = null;
}
present(data: SponsorSponsorshipsDTO | null): void {
this.viewModel = data;
}
getViewModel(): SponsorSponsorshipsViewModel {
return this.viewModel;
}
getData(): SponsorSponsorshipsDTO | null {
return this.viewModel;
/**
* Sponsor Sponsorships Presenter
*
* Transforms sponsor sponsorships DTOs into view models.
*/
export class SponsorSponsorshipsPresenter {
present(dto: SponsorSponsorshipsDto): SponsorSponsorshipsViewModel {
return new SponsorSponsorshipsViewModel(dto);
}
}

View File

@@ -0,0 +1,13 @@
import type { GetEntitySponsorshipPricingResultDto } from '../dtos';
import { SponsorshipPricingViewModel } from '../view-models/SponsorshipPricingViewModel';
/**
* Sponsorship Pricing Presenter
*
* Transforms sponsorship pricing DTOs into view models.
*/
export class SponsorshipPricingPresenter {
present(dto: GetEntitySponsorshipPricingResultDto): SponsorshipPricingViewModel {
return new SponsorshipPricingViewModel(dto);
}
}

View File

@@ -1,6 +1,12 @@
import { TeamDetailsDto, TeamMemberDto } from '../dtos';
import type { TeamDetailsDto } from '../dtos';
import { TeamDetailsViewModel } from '../view-models';
export const presentTeamDetails = (dto: TeamDetailsDto, currentUserId: string): TeamDetailsViewModel => {
return new TeamDetailsViewModel(dto, currentUserId);
};
/**
* Team Details Presenter
* Transforms TeamDetailsDto to TeamDetailsViewModel
*/
export class TeamDetailsPresenter {
present(dto: TeamDetailsDto, currentUserId: string): TeamDetailsViewModel {
return new TeamDetailsViewModel(dto, currentUserId);
}
}

View File

@@ -1,6 +1,18 @@
import { TeamJoinRequestItemDto } from '../dtos';
import type { TeamJoinRequestItemDto } from '../dtos';
import { TeamJoinRequestViewModel } from '../view-models';
/**
* Team Join Request Presenter
* Transforms TeamJoinRequestItemDto to TeamJoinRequestViewModel
*/
export class TeamJoinRequestPresenter {
present(dto: TeamJoinRequestItemDto, currentUserId: string, isOwner: boolean): TeamJoinRequestViewModel {
return new TeamJoinRequestViewModel(dto, currentUserId, isOwner);
}
}
// Backward compatibility export (deprecated)
export const presentTeamJoinRequest = (dto: TeamJoinRequestItemDto, currentUserId: string, isOwner: boolean): TeamJoinRequestViewModel => {
return new TeamJoinRequestViewModel(dto, currentUserId, isOwner);
const presenter = new TeamJoinRequestPresenter();
return presenter.present(dto, currentUserId, isOwner);
};

View File

@@ -0,0 +1,12 @@
import type { AllTeamsDto } from '../dtos';
import { TeamSummaryViewModel } from '../view-models';
/**
* Team List Presenter
* Transforms AllTeamsDto to array of TeamSummaryViewModel
*/
export class TeamListPresenter {
present(dto: AllTeamsDto): TeamSummaryViewModel[] {
return dto.teams.map(team => new TeamSummaryViewModel(team));
}
}

View File

@@ -1,41 +1,12 @@
import type {
ITeamMembersPresenter,
TeamMemberViewModel,
TeamMembersViewModel,
TeamMembersResultDTO,
} from '@core/racing/application/presenters/ITeamMembersPresenter';
import type { TeamMembersDto } from '../dtos';
import { TeamMemberViewModel } from '../view-models';
export class TeamMembersPresenter implements ITeamMembersPresenter {
private viewModel: TeamMembersViewModel | null = null;
reset(): void {
this.viewModel = null;
}
present(input: TeamMembersResultDTO): void {
const members: TeamMemberViewModel[] = input.memberships.map((membership) => ({
driverId: membership.driverId,
driverName: input.driverNames[membership.driverId] ?? 'Unknown Driver',
role: membership.role === 'driver' ? 'member' : membership.role,
joinedAt: membership.joinedAt.toISOString(),
isActive: membership.status === 'active',
avatarUrl: input.avatarUrls[membership.driverId] ?? '',
}));
const ownerCount = members.filter((m) => m.role === 'owner').length;
const managerCount = members.filter((m) => m.role === 'manager').length;
const memberCount = members.filter((m) => m.role === 'member').length;
this.viewModel = {
members,
totalCount: members.length,
ownerCount,
managerCount,
memberCount,
};
}
getViewModel(): TeamMembersViewModel | null {
return this.viewModel;
/**
* Team Members Presenter
* Transforms TeamMembersDto to array of TeamMemberViewModel
*/
export class TeamMembersPresenter {
present(dto: TeamMembersDto, currentUserId: string, teamOwnerId: string): TeamMemberViewModel[] {
return dto.members.map(member => new TeamMemberViewModel(member, currentUserId, teamOwnerId));
}
}

View File

@@ -1,37 +0,0 @@
// Analytics Presenters
// Auth Presenters
// Driver Presenters
export { presentDriversLeaderboard } from './DriversLeaderboardPresenter';
export { presentDriverRegistrationStatus } from './DriverRegistrationStatusPresenter';
// League Presenters
export { presentLeagueMember } from './LeagueMemberPresenter';
export { presentLeagueStandings } from './LeagueStandingsPresenter';
export { presentLeagueSummaries, presentLeagueSummary } from './LeagueSummaryPresenter';
// Payments Presenters
export { presentMembershipFee } from './MembershipFeePresenter';
export { presentPayment } from './PaymentPresenter';
export { presentPrize } from './PrizePresenter';
export { presentWallet } from './WalletPresenter';
export { presentWalletTransaction } from './WalletTransactionPresenter';
// Race Presenters
export { presentRaceDetail } from './RaceDetailPresenter';
export { presentRaceListItem } from './RaceListItemPresenter';
export { presentRaceResult } from './RaceResultsPresenter';
export { presentRaceResultsDetail, RaceResultsDetailPresenter } from './RaceResultsDetailPresenter';
export { RaceWithSOFPresenter } from './RaceWithSOFPresenter';
export { ImportRaceResultsPresenter } from './ImportRaceResultsPresenter';
// Sponsor Presenters
export { presentSponsor } from './SponsorPresenter';
export { presentSponsorshipDetail } from './SponsorshipDetailPresenter';
// Team Presenters
export { presentTeamDetails } from './TeamDetailsPresenter';
export { presentTeamJoinRequest } from './TeamJoinRequestPresenter';
export { presentTeamMember } from './TeamMemberPresenter';
export { presentTeamSummary } from './TeamSummaryPresenter';