This commit is contained in:
2025-12-16 15:42:38 +01:00
parent 29410708c8
commit 362894d1a5
147 changed files with 780 additions and 375 deletions

View File

@@ -0,0 +1,30 @@
import { IAllLeaguesWithCapacityPresenter, AllLeaguesWithCapacityResultDTO, AllLeaguesWithCapacityViewModel } from '@core/racing/application/presenters/IAllLeaguesWithCapacityPresenter';
export class AllLeaguesWithCapacityPresenter implements IAllLeaguesWithCapacityPresenter {
private result: AllLeaguesWithCapacityViewModel | null = null;
reset() {
this.result = null;
}
present(dto: AllLeaguesWithCapacityResultDTO) {
const leagues = dto.leagues.map(league => ({
id: league.id,
name: league.name,
description: league.description,
ownerId: league.ownerId,
settings: { maxDrivers: league.settings.maxDrivers || 0 },
createdAt: league.createdAt.toISOString(),
usedSlots: dto.memberCounts.get(league.id) || 0,
socialLinks: league.socialLinks,
}));
this.result = {
leagues,
totalCount: leagues.length,
};
}
getViewModel(): AllLeaguesWithCapacityViewModel | null {
return this.result;
}
}

View File

@@ -0,0 +1,18 @@
import { IApproveLeagueJoinRequestPresenter, ApproveLeagueJoinRequestResultDTO, ApproveLeagueJoinRequestViewModel } from '@core/racing/application/presenters/IApproveLeagueJoinRequestPresenter';
export class ApproveLeagueJoinRequestPresenter implements IApproveLeagueJoinRequestPresenter {
private result: ApproveLeagueJoinRequestViewModel | null = null;
reset() {
this.result = null;
}
present(dto: ApproveLeagueJoinRequestResultDTO) {
this.result = dto;
}
getViewModel(): ApproveLeagueJoinRequestViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,17 @@
import { IGetLeagueAdminPermissionsPresenter, GetLeagueAdminPermissionsResultDTO, GetLeagueAdminPermissionsViewModel } from '@core/racing/application/presenters/IGetLeagueAdminPermissionsPresenter';
export class GetLeagueAdminPermissionsPresenter implements IGetLeagueAdminPermissionsPresenter {
private result: GetLeagueAdminPermissionsViewModel | null = null;
reset() {
this.result = null;
}
present(dto: GetLeagueAdminPermissionsResultDTO) {
this.result = dto;
}
getViewModel(): GetLeagueAdminPermissionsViewModel | null {
return this.result;
}
}

View File

@@ -0,0 +1,47 @@
import { IGetLeagueMembershipsPresenter, GetLeagueMembershipsResultDTO, GetLeagueMembershipsViewModel } from '@core/racing/application/presenters/IGetLeagueMembershipsPresenter';
import { LeagueMembershipsViewModel } from '../dto/LeagueDto';
export class GetLeagueMembershipsPresenter implements IGetLeagueMembershipsPresenter {
private result: GetLeagueMembershipsViewModel | null = null;
reset() {
this.result = null;
}
present(dto: GetLeagueMembershipsResultDTO) {
const driverMap = new Map(dto.drivers.map(d => [d.id, d]));
const members = dto.memberships.map(membership => ({
driverId: membership.driverId,
driver: driverMap.get(membership.driverId)!,
role: this.mapRole(membership.role) as 'owner' | 'manager' | 'member',
joinedAt: membership.joinedAt,
}));
this.result = { memberships: { members } };
}
private mapRole(role: string): 'owner' | 'manager' | 'member' {
switch (role) {
case 'owner':
return 'owner';
case 'admin':
return 'manager'; // Map admin to manager for API
case 'steward':
return 'member'; // Map steward to member for API
case 'member':
return 'member';
default:
return 'member';
}
}
getViewModel(): GetLeagueMembershipsViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
// API-specific method
get apiViewModel(): LeagueMembershipsViewModel | null {
if (!this.result?.memberships) return null;
return this.result.memberships as LeagueMembershipsViewModel;
}
}

View File

@@ -0,0 +1,18 @@
import { IGetLeagueOwnerSummaryPresenter, GetLeagueOwnerSummaryResultDTO, GetLeagueOwnerSummaryViewModel } from '@core/racing/application/presenters/IGetLeagueOwnerSummaryPresenter';
export class GetLeagueOwnerSummaryPresenter implements IGetLeagueOwnerSummaryPresenter {
private result: GetLeagueOwnerSummaryViewModel | null = null;
reset() {
this.result = null;
}
present(dto: GetLeagueOwnerSummaryResultDTO) {
this.result = { summary: dto.summary };
}
getViewModel(): GetLeagueOwnerSummaryViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,30 @@
import { IGetLeagueProtestsPresenter, GetLeagueProtestsResultDTO, GetLeagueProtestsViewModel } from '@core/racing/application/presenters/IGetLeagueProtestsPresenter';
export class GetLeagueProtestsPresenter implements IGetLeagueProtestsPresenter {
private result: GetLeagueProtestsViewModel | null = null;
reset() {
this.result = null;
}
present(dto: GetLeagueProtestsResultDTO) {
const racesById = {};
dto.races.forEach(race => {
racesById[race.id] = race;
});
const driversById = {};
dto.drivers.forEach(driver => {
driversById[driver.id] = driver;
});
this.result = {
protests: dto.protests,
racesById,
driversById,
};
}
getViewModel(): GetLeagueProtestsViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,27 @@
import { IGetLeagueSeasonsPresenter, GetLeagueSeasonsResultDTO, GetLeagueSeasonsViewModel } from '@core/racing/application/presenters/IGetLeagueSeasonsPresenter';
export class GetLeagueSeasonsPresenter implements IGetLeagueSeasonsPresenter {
private result: GetLeagueSeasonsViewModel | null = null;
reset() {
this.result = null;
}
present(dto: GetLeagueSeasonsResultDTO) {
const seasons = dto.seasons.map(season => ({
seasonId: season.id,
name: season.name,
status: season.status,
startDate: season.startDate,
endDate: season.endDate,
isPrimary: season.isPrimary,
isParallelActive: season.isParallelActive,
}));
this.result = { seasons };
}
getViewModel(): GetLeagueSeasonsViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,30 @@
import { LeagueAdminViewModel } from '../dto/LeagueDto';
export class LeagueAdminPresenter {
private result: LeagueAdminViewModel | null = null;
reset() {
this.result = null;
}
present(data: {
joinRequests: any[];
ownerSummary: any;
config: any;
protests: any;
seasons: any[];
}) {
this.result = {
joinRequests: data.joinRequests,
ownerSummary: data.ownerSummary,
config: { form: data.config },
protests: data.protests,
seasons: data.seasons,
};
}
getViewModel(): LeagueAdminViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,109 @@
import { ILeagueFullConfigPresenter, LeagueFullConfigData, LeagueConfigFormViewModel } from '@core/racing/application/presenters/ILeagueFullConfigPresenter';
import { LeagueConfigFormModelDto } from '../dto/LeagueDto';
export class LeagueConfigPresenter implements ILeagueFullConfigPresenter {
private result: LeagueConfigFormViewModel | null = null;
reset() {
this.result = null;
}
present(dto: LeagueFullConfigData) {
// Map from LeagueFullConfigData to LeagueConfigFormViewModel
const league = dto.league;
const settings = league.settings;
const stewarding = settings.stewarding;
this.result = {
leagueId: league.id,
basics: {
name: league.name,
description: league.description,
visibility: 'public', // TODO: Map visibility from league
gameId: 'iracing', // TODO: Map from game
},
structure: {
mode: 'solo', // TODO: Map from league settings
maxDrivers: settings.maxDrivers || 32,
multiClassEnabled: false, // TODO: Map
},
championships: {
enableDriverChampionship: true, // TODO: Map
enableTeamChampionship: false,
enableNationsChampionship: false,
enableTrophyChampionship: false,
},
scoring: {
customScoringEnabled: false, // TODO: Map
},
dropPolicy: {
strategy: 'none', // TODO: Map
},
timings: {
practiceMinutes: 30, // TODO: Map
qualifyingMinutes: 15,
mainRaceMinutes: 60,
sessionCount: 1,
roundsPlanned: 10, // TODO: Map
},
stewarding: {
decisionMode: stewarding?.decisionMode || 'admin_only',
requireDefense: stewarding?.requireDefense || false,
defenseTimeLimit: stewarding?.defenseTimeLimit || 48,
voteTimeLimit: stewarding?.voteTimeLimit || 72,
protestDeadlineHours: stewarding?.protestDeadlineHours || 48,
stewardingClosesHours: stewarding?.stewardingClosesHours || 168,
notifyAccusedOnProtest: stewarding?.notifyAccusedOnProtest || true,
notifyOnVoteRequired: stewarding?.notifyOnVoteRequired || true,
requiredVotes: stewarding?.requiredVotes,
},
};
}
getViewModel(): LeagueConfigFormViewModel | null {
return this.result;
}
// API-specific method to get the DTO
get viewModel(): LeagueConfigFormModelDto | null {
if (!this.result) return null;
// Map from LeagueConfigFormViewModel to LeagueConfigFormModelDto
return {
leagueId: this.result.leagueId,
basics: {
name: this.result.basics.name,
description: this.result.basics.description,
visibility: this.result.basics.visibility as 'public' | 'private',
},
structure: {
mode: this.result.structure.mode as 'solo' | 'team',
},
championships: [], // TODO: Map championships
scoring: {
type: 'standard', // TODO: Map scoring type
points: 25, // TODO: Map points
},
dropPolicy: {
strategy: this.result.dropPolicy.strategy as 'none' | 'worst_n',
n: this.result.dropPolicy.n,
},
timings: {
raceDayOfWeek: 'sunday', // TODO: Map from timings
raceTimeHour: 20,
raceTimeMinute: 0,
},
stewarding: {
decisionMode: this.result.stewarding.decisionMode === 'steward_vote' ? 'committee_vote' : 'single_steward',
requireDefense: this.result.stewarding.requireDefense,
defenseTimeLimit: this.result.stewarding.defenseTimeLimit,
voteTimeLimit: this.result.stewarding.voteTimeLimit,
protestDeadlineHours: this.result.stewarding.protestDeadlineHours,
stewardingClosesHours: this.result.stewarding.stewardingClosesHours,
notifyAccusedOnProtest: this.result.stewarding.notifyAccusedOnProtest,
notifyOnVoteRequired: this.result.stewarding.notifyOnVoteRequired,
requiredVotes: this.result.stewarding.requiredVotes,
},
};
}
}

View File

@@ -0,0 +1,27 @@
import { IGetLeagueJoinRequestsPresenter, GetLeagueJoinRequestsResultDTO, GetLeagueJoinRequestsViewModel } from '@core/racing/application/presenters/IGetLeagueJoinRequestsPresenter';
export class LeagueJoinRequestsPresenter implements IGetLeagueJoinRequestsPresenter {
private result: GetLeagueJoinRequestsViewModel | null = null;
reset() {
this.result = null;
}
present(dto: GetLeagueJoinRequestsResultDTO) {
const driverMap = new Map(dto.drivers.map(d => [d.id, d]));
const joinRequests = dto.joinRequests.map(request => ({
id: request.id,
leagueId: request.leagueId,
driverId: request.driverId,
requestedAt: request.requestedAt,
message: request.message,
driver: driverMap.get(request.driverId) || null,
}));
this.result = { joinRequests };
}
getViewModel(): GetLeagueJoinRequestsViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,23 @@
import { IGetLeagueSchedulePresenter, GetLeagueScheduleResultDTO, LeagueScheduleViewModel } from '@core/racing/application/presenters/IGetLeagueSchedulePresenter';
export class LeagueSchedulePresenter implements IGetLeagueSchedulePresenter {
private result: LeagueScheduleViewModel | null = null;
reset() {
this.result = null;
}
present(dto: GetLeagueScheduleResultDTO) {
this.result = {
races: dto.races.map(race => ({
id: race.id,
name: race.name,
date: race.scheduledAt.toISOString(),
})),
};
}
getViewModel(): LeagueScheduleViewModel | null {
return this.result;
}
}

View File

@@ -0,0 +1,27 @@
import { ILeagueStandingsPresenter, LeagueStandingsResultDTO, LeagueStandingsViewModel } from '@core/racing/application/presenters/ILeagueStandingsPresenter';
export class LeagueStandingsPresenter implements ILeagueStandingsPresenter {
private result: LeagueStandingsViewModel | null = null;
reset() {
this.result = null;
}
present(dto: LeagueStandingsResultDTO) {
const driverMap = new Map(dto.drivers.map(d => [d.id, { id: d.id, name: d.name }]));
const standings = dto.standings
.sort((a, b) => a.position - b.position)
.map(standing => ({
driverId: standing.driverId,
driver: driverMap.get(standing.driverId)!,
points: standing.points,
rank: standing.position,
}));
this.result = { standings };
}
getViewModel(): LeagueStandingsViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,17 @@
import { ILeagueStatsPresenter, LeagueStatsResultDTO, LeagueStatsViewModel } from '@core/racing/application/presenters/ILeagueStatsPresenter';
export class LeagueStatsPresenter implements ILeagueStatsPresenter {
private result: LeagueStatsViewModel | null = null;
reset() {
this.result = null;
}
present(dto: LeagueStatsResultDTO) {
this.result = dto;
}
getViewModel(): LeagueStatsViewModel | null {
return this.result;
}
}

View File

@@ -0,0 +1,18 @@
import { IRejectLeagueJoinRequestPresenter, RejectLeagueJoinRequestResultDTO, RejectLeagueJoinRequestViewModel } from '@core/racing/application/presenters/IRejectLeagueJoinRequestPresenter';
export class RejectLeagueJoinRequestPresenter implements IRejectLeagueJoinRequestPresenter {
private result: RejectLeagueJoinRequestViewModel | null = null;
reset() {
this.result = null;
}
present(dto: RejectLeagueJoinRequestResultDTO) {
this.result = dto;
}
getViewModel(): RejectLeagueJoinRequestViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,18 @@
import { IRemoveLeagueMemberPresenter, RemoveLeagueMemberResultDTO, RemoveLeagueMemberViewModel } from '@core/racing/application/presenters/IRemoveLeagueMemberPresenter';
export class RemoveLeagueMemberPresenter implements IRemoveLeagueMemberPresenter {
private result: RemoveLeagueMemberViewModel | null = null;
reset() {
this.result = null;
}
present(dto: RemoveLeagueMemberResultDTO) {
this.result = dto;
}
getViewModel(): RemoveLeagueMemberViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,20 @@
import { IGetTotalLeaguesPresenter, GetTotalLeaguesResultDTO, GetTotalLeaguesViewModel } from '@core/racing/application/presenters/IGetTotalLeaguesPresenter';
import { LeagueStatsDto } from '../dto/LeagueDto';
export class TotalLeaguesPresenter implements IGetTotalLeaguesPresenter {
private result: LeagueStatsDto | null = null;
reset() {
this.result = null;
}
present(dto: GetTotalLeaguesResultDTO) {
this.result = {
totalLeagues: dto.totalLeagues,
};
}
getViewModel(): LeagueStatsDto | null {
return this.result;
}
}

View File

@@ -0,0 +1,18 @@
import { IUpdateLeagueMemberRolePresenter, UpdateLeagueMemberRoleResultDTO, UpdateLeagueMemberRoleViewModel } from '@core/racing/application/presenters/IUpdateLeagueMemberRolePresenter';
export class UpdateLeagueMemberRolePresenter implements IUpdateLeagueMemberRolePresenter {
private result: UpdateLeagueMemberRoleViewModel | null = null;
reset() {
this.result = null;
}
present(dto: UpdateLeagueMemberRoleResultDTO) {
this.result = dto;
}
getViewModel(): UpdateLeagueMemberRoleViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}