refactor driver module (wip)

This commit is contained in:
2025-12-22 10:24:40 +01:00
parent e7dbec4a85
commit 9da528d5bd
108 changed files with 842 additions and 947 deletions

View File

@@ -8,6 +8,8 @@ import type { ISeasonRepository } from '@core/racing/domain/repositories/ISeason
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
// Import concrete in-memory implementations
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
@@ -52,6 +54,7 @@ import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-ca
// Import presenters
import { AllLeaguesWithCapacityPresenter } from './presenters/AllLeaguesWithCapacityPresenter';
import { GetLeagueProtestsPresenter } from './presenters/GetLeagueProtestsPresenter';
// Define injection tokens
export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository';
@@ -145,6 +148,10 @@ export const LeagueProviders: Provider[] = [
provide: 'AllLeaguesWithCapacityPresenter',
useClass: AllLeaguesWithCapacityPresenter,
},
{
provide: 'GetLeagueProtestsPresenter',
useClass: GetLeagueProtestsPresenter,
},
// Use cases
{
provide: GetAllLeaguesWithCapacityUseCase,
@@ -167,7 +174,17 @@ export const LeagueProviders: Provider[] = [
RemoveLeagueMemberUseCase,
UpdateLeagueMemberRoleUseCase,
GetLeagueOwnerSummaryUseCase,
GetLeagueProtestsUseCase,
{
provide: GetLeagueProtestsUseCase,
useFactory: (
raceRepo: IRaceRepository,
protestRepo: IProtestRepository,
driverRepo: IDriverRepository,
leagueRepo: ILeagueRepository,
presenter: GetLeagueProtestsPresenter,
) => new GetLeagueProtestsUseCase(raceRepo, protestRepo, driverRepo, leagueRepo, presenter),
inject: [RACE_REPOSITORY_TOKEN, PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, 'GetLeagueProtestsPresenter'],
},
GetLeagueSeasonsUseCase,
GetLeagueMembershipsUseCase,
GetLeagueScheduleUseCase,

View File

@@ -257,9 +257,7 @@ export class LeagueService {
if (result.isErr()) {
throw new Error(result.unwrapErr().code);
}
const presenter = new GetLeagueProtestsPresenter();
presenter.present(result.unwrap());
return presenter.getViewModel()!;
return (this.getLeagueProtestsUseCase.outputPort as GetLeagueProtestsPresenter).getResponseModel()!;
}
async getLeagueSeasons(query: GetLeagueSeasonsQueryDTO): Promise<LeagueSeasonSummaryDTO[]> {
@@ -268,9 +266,7 @@ export class LeagueService {
if (result.isErr()) {
throw new Error(result.unwrapErr().code);
}
const presenter = new GetLeagueSeasonsPresenter();
presenter.present(result.unwrap());
return presenter.getViewModel()!;
return (this.getLeagueSeasonsUseCase.output as GetLeagueSeasonsPresenter).getResponseModel()!;
}
async getLeagueMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {

View File

@@ -8,9 +8,9 @@ export class AllLeaguesWithCapacityAndScoringDTO {
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueSummaryDTO)
leagues: LeagueSummaryDTO[];
leagues!: LeagueSummaryDTO[];
@ApiProperty()
@IsNumber()
totalCount: number;
totalCount!: number;
}

View File

@@ -1,5 +1,17 @@
import { ApiProperty } from '@nestjs/swagger';
export interface LeagueSettings {
maxDrivers: number;
sessionDuration?: number;
visibility?: string;
}
export interface SocialLinks {
discordUrl?: string;
youtubeUrl?: string;
websiteUrl?: string;
}
export class LeagueWithCapacityDTO {
@ApiProperty()
id!: string;
@@ -14,21 +26,13 @@ export class LeagueWithCapacityDTO {
ownerId!: string;
@ApiProperty()
settings!: {
maxDrivers: number;
sessionDuration?: number;
visibility?: string;
};
settings!: LeagueSettings;
@ApiProperty()
createdAt!: string;
@ApiProperty({ nullable: true })
socialLinks?: {
discordUrl?: string;
youtubeUrl?: string;
websiteUrl?: string;
};
socialLinks?: SocialLinks;
@ApiProperty()
usedSlots!: number;

View File

@@ -4,9 +4,9 @@ import { IsString } from 'class-validator';
export class ApproveJoinRequestInputDTO {
@ApiProperty()
@IsString()
requestId: string;
requestId!: string;
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
}

View File

@@ -4,7 +4,7 @@ import { IsString, IsBoolean } from 'class-validator';
export class ApproveJoinRequestOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
success!: boolean;
@ApiProperty({ required: false })
@IsString()

View File

@@ -4,17 +4,17 @@ import { IsString, IsEnum } from 'class-validator';
export class CreateLeagueInputDTO {
@ApiProperty()
@IsString()
name: string;
name!: string;
@ApiProperty()
@IsString()
description: string;
description!: string;
@ApiProperty({ enum: ['public', 'private'] })
@IsEnum(['public', 'private'])
visibility: 'public' | 'private';
visibility!: 'public' | 'private';
@ApiProperty()
@IsString()
ownerId: string;
ownerId!: string;
}

View File

@@ -4,9 +4,9 @@ import { IsString, IsBoolean } from 'class-validator';
export class CreateLeagueOutputDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty()
@IsBoolean()
success: boolean;
success!: boolean;
}

View File

@@ -8,5 +8,5 @@ export class GetLeagueAdminConfigOutputDTO {
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDTO)
form: LeagueConfigFormModelDTO | null;
form!: LeagueConfigFormModelDTO | null;
}

View File

@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
export class GetLeagueAdminConfigQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
}

View File

@@ -4,9 +4,9 @@ import { IsString } from 'class-validator';
export class GetLeagueAdminPermissionsInputDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty()
@IsString()
performerDriverId: string;
performerDriverId!: string;
}

View File

@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
export class GetLeagueJoinRequestsQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
}

View File

@@ -4,9 +4,9 @@ import { IsString } from 'class-validator';
export class GetLeagueOwnerSummaryQueryDTO {
@ApiProperty()
@IsString()
ownerId: string;
ownerId!: string;
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
}

View File

@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
export class GetLeagueProtestsQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
}

View File

@@ -3,5 +3,5 @@ import { RaceDTO } from '../../race/dtos/RaceDTO';
export class GetLeagueRacesOutputDTO {
@ApiProperty({ type: [RaceDTO] })
races: RaceDTO[];
races!: RaceDTO[];
}

View File

@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
export class GetLeagueSeasonsQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
}

View File

@@ -2,28 +2,28 @@ import { ApiProperty } from '@nestjs/swagger';
export class WalletTransactionDTO {
@ApiProperty()
id: string;
id!: string;
@ApiProperty({ enum: ['sponsorship', 'membership', 'withdrawal', 'prize'] })
type: 'sponsorship' | 'membership' | 'withdrawal' | 'prize';
type!: 'sponsorship' | 'membership' | 'withdrawal' | 'prize';
@ApiProperty()
description: string;
description!: string;
@ApiProperty()
amount: number;
amount!: number;
@ApiProperty()
fee: number;
fee!: number;
@ApiProperty()
netAmount: number;
netAmount!: number;
@ApiProperty()
date: string;
date!: string;
@ApiProperty({ enum: ['completed', 'pending', 'failed'] })
status: 'completed' | 'pending' | 'failed';
status!: 'completed' | 'pending' | 'failed';
@ApiProperty({ required: false })
reference?: string;
@@ -31,29 +31,29 @@ export class WalletTransactionDTO {
export class GetLeagueWalletOutputDTO {
@ApiProperty()
balance: number;
balance!: number;
@ApiProperty()
currency: string;
currency!: string;
@ApiProperty()
totalRevenue: number;
totalRevenue!: number;
@ApiProperty()
totalFees: number;
totalFees!: number;
@ApiProperty()
totalWithdrawals: number;
totalWithdrawals!: number;
@ApiProperty()
pendingPayouts: number;
pendingPayouts!: number;
@ApiProperty()
canWithdraw: boolean;
canWithdraw!: boolean;
@ApiProperty({ required: false })
withdrawalBlockReason?: string;
@ApiProperty({ type: [WalletTransactionDTO] })
transactions: WalletTransactionDTO[];
transactions!: WalletTransactionDTO[];
}

View File

@@ -3,5 +3,5 @@ import { SponsorshipDetailDTO } from '../../sponsor/dtos/SponsorshipDetailDTO';
export class GetSeasonSponsorshipsOutputDTO {
@ApiProperty({ type: [SponsorshipDetailDTO] })
sponsorships: SponsorshipDetailDTO[];
sponsorships!: SponsorshipDetailDTO[];
}

View File

@@ -8,5 +8,5 @@ export class LeagueAdminConfigDTO {
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDTO)
form: LeagueConfigFormModelDTO | null;
form!: LeagueConfigFormModelDTO | null;
}

View File

@@ -12,27 +12,27 @@ export class LeagueAdminDTO {
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueJoinRequestDTO)
joinRequests: LeagueJoinRequestDTO[];
joinRequests!: LeagueJoinRequestDTO[];
@ApiProperty({ type: () => LeagueOwnerSummaryDTO, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueOwnerSummaryDTO)
ownerSummary: LeagueOwnerSummaryDTO | null;
ownerSummary!: LeagueOwnerSummaryDTO | null;
@ApiProperty({ type: () => LeagueAdminConfigDTO })
@ValidateNested()
@Type(() => LeagueAdminConfigDTO)
config: LeagueAdminConfigDTO;
config!: LeagueAdminConfigDTO;
@ApiProperty({ type: () => LeagueAdminProtestsDTO })
@ValidateNested()
@Type(() => LeagueAdminProtestsDTO)
protests: LeagueAdminProtestsDTO;
protests!: LeagueAdminProtestsDTO;
@ApiProperty({ type: [LeagueSeasonSummaryDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueSeasonSummaryDTO)
seasons: LeagueSeasonSummaryDTO[];
seasons!: LeagueSeasonSummaryDTO[];
}

View File

@@ -4,9 +4,9 @@ import { IsBoolean } from 'class-validator';
export class LeagueAdminPermissionsDTO {
@ApiProperty()
@IsBoolean()
canRemoveMember: boolean;
canRemoveMember!: boolean;
@ApiProperty()
@IsBoolean()
canUpdateRoles: boolean;
canUpdateRoles!: boolean;
}

View File

@@ -1,24 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { DriverDto } from '../../driver/dto/DriverDto';
import { RaceDto } from '../../race/dto/RaceDto';
import { DriverDTO } from '../../driver/dtos/DriverDTO';
import { RaceDTO } from '../../race/dtos/RaceDTO';
import { ProtestDTO } from './ProtestDTO';
export class LeagueAdminProtestsDTO {
@ApiProperty({ type: [ProtestDTO] })
@ApiProperty({ type: [ProtestDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => ProtestDTO)
protests: ProtestDTO[];
protests!: ProtestDTO[];
@ApiProperty({ type: () => RaceDto })
@ApiProperty({ type: () => RaceDTO })
@ValidateNested()
@Type(() => RaceDto)
racesById: { [raceId: string]: RaceDto };
@Type(() => RaceDTO)
racesById!: { [raceId: string]: RaceDTO };
@ApiProperty({ type: () => DriverDto })
@ApiProperty({ type: () => DriverDTO })
@ValidateNested()
@Type(() => DriverDto)
driversById: { [driverId: string]: DriverDto };
@Type(() => DriverDTO)
driversById!: { [driverId: string]: DriverDTO };
}

View File

@@ -4,13 +4,13 @@ import { IsString, IsEnum } from 'class-validator';
export class LeagueConfigFormModelBasicsDTO {
@ApiProperty()
@IsString()
name: string;
name!: string;
@ApiProperty()
@IsString()
description: string;
description!: string;
@ApiProperty({ enum: ['public', 'private'] })
@IsEnum(['public', 'private'])
visibility: 'public' | 'private';
visibility!: 'public' | 'private';
}

View File

@@ -11,39 +11,39 @@ import { LeagueConfigFormModelTimingsDTO } from './LeagueConfigFormModelTimingsD
export class LeagueConfigFormModelDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty({ type: LeagueConfigFormModelBasicsDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelBasicsDTO)
basics: LeagueConfigFormModelBasicsDTO;
basics!: LeagueConfigFormModelBasicsDTO;
@ApiProperty({ type: LeagueConfigFormModelStructureDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelStructureDTO)
structure: LeagueConfigFormModelStructureDTO;
structure!: LeagueConfigFormModelStructureDTO;
@ApiProperty({ type: [Object] })
@IsArray()
championships: Object[];
championships!: Object[];
@ApiProperty({ type: LeagueConfigFormModelScoringDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelScoringDTO)
scoring: LeagueConfigFormModelScoringDTO;
scoring!: LeagueConfigFormModelScoringDTO;
@ApiProperty({ type: LeagueConfigFormModelDropPolicyDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelDropPolicyDTO)
dropPolicy: LeagueConfigFormModelDropPolicyDTO;
dropPolicy!: LeagueConfigFormModelDropPolicyDTO;
@ApiProperty({ type: LeagueConfigFormModelTimingsDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelTimingsDTO)
timings: LeagueConfigFormModelTimingsDTO;
timings!: LeagueConfigFormModelTimingsDTO;
@ApiProperty({ type: LeagueConfigFormModelStewardingDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelStewardingDTO)
stewarding: LeagueConfigFormModelStewardingDTO;
stewarding!: LeagueConfigFormModelStewardingDTO;
}

View File

@@ -4,7 +4,7 @@ import { IsNumber, IsOptional, IsEnum } from 'class-validator';
export class LeagueConfigFormModelDropPolicyDTO {
@ApiProperty({ enum: ['none', 'worst_n'] })
@IsEnum(['none', 'worst_n'])
strategy: 'none' | 'worst_n';
strategy!: 'none' | 'worst_n';
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -4,9 +4,9 @@ import { IsString, IsNumber } from 'class-validator';
export class LeagueConfigFormModelScoringDTO {
@ApiProperty()
@IsString()
type: string;
type!: string;
@ApiProperty()
@IsNumber()
points: number;
points!: number;
}

View File

@@ -4,7 +4,7 @@ import { IsNumber, IsBoolean, IsOptional, IsEnum } from 'class-validator';
export class LeagueConfigFormModelStewardingDTO {
@ApiProperty({ enum: ['single_steward', 'committee_vote'] })
@IsEnum(['single_steward', 'committee_vote'])
decisionMode: 'single_steward' | 'committee_vote';
decisionMode!: 'single_steward' | 'committee_vote';
@ApiProperty({ required: false })
@IsOptional()
@@ -13,29 +13,29 @@ export class LeagueConfigFormModelStewardingDTO {
@ApiProperty()
@IsBoolean()
requireDefense: boolean;
requireDefense!: boolean;
@ApiProperty()
@IsNumber()
defenseTimeLimit: number;
defenseTimeLimit!: number;
@ApiProperty()
@IsNumber()
voteTimeLimit: number;
voteTimeLimit!: number;
@ApiProperty()
@IsNumber()
protestDeadlineHours: number;
protestDeadlineHours!: number;
@ApiProperty()
@IsNumber()
stewardingClosesHours: number;
stewardingClosesHours!: number;
@ApiProperty()
@IsBoolean()
notifyAccusedOnProtest: boolean;
notifyAccusedOnProtest!: boolean;
@ApiProperty()
@IsBoolean()
notifyOnVoteRequired: boolean;
notifyOnVoteRequired!: boolean;
}

View File

@@ -5,5 +5,5 @@ export class LeagueConfigFormModelStructureDTO {
@ApiProperty()
@IsString()
@IsEnum(['solo', 'team'])
mode: 'solo' | 'team';
mode!: 'solo' | 'team';
}

View File

@@ -4,13 +4,13 @@ import { IsString, IsNumber } from 'class-validator';
export class LeagueConfigFormModelTimingsDTO {
@ApiProperty()
@IsString()
raceDayOfWeek: string;
raceDayOfWeek!: string;
@ApiProperty()
@IsNumber()
raceTimeHour: number;
raceTimeHour!: number;
@ApiProperty()
@IsNumber()
raceTimeMinute: number;
raceTimeMinute!: number;
}

View File

@@ -2,23 +2,28 @@ import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsDate, IsOptional } from 'class-validator';
import { Type } from 'class-transformer';
export interface DriverInfo {
id: string;
name: string;
}
export class LeagueJoinRequestDTO {
@ApiProperty()
@IsString()
id: string;
id!: string;
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty()
@IsString()
driverId: string;
driverId!: string;
@ApiProperty()
@IsDate()
@Type(() => Date)
requestedAt: Date;
requestedAt!: Date;
@ApiProperty({ required: false })
@IsOptional()
@@ -30,8 +35,5 @@ export class LeagueJoinRequestDTO {
type: () => Object,
})
@IsOptional()
driver?: {
id: string;
name: string;
};
driver?: DriverInfo;
}

View File

@@ -1,24 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsDate, IsEnum, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { DriverDto } from '../../driver/dto/DriverDto';
import { DriverDTO } from '../../driver/dtos/DriverDTO';
export class LeagueMemberDTO {
@ApiProperty()
@IsString()
driverId: string;
driverId!: string;
@ApiProperty({ type: () => DriverDto })
@ApiProperty({ type: () => DriverDTO })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@Type(() => DriverDTO)
driver!: DriverDTO;
@ApiProperty({ enum: ['owner', 'manager', 'member'] })
@IsEnum(['owner', 'manager', 'member'])
role: 'owner' | 'manager' | 'member';
role!: 'owner' | 'manager' | 'member';
@ApiProperty()
@IsDate()
@Type(() => Date)
joinedAt: Date;
joinedAt!: Date;
}

View File

@@ -4,25 +4,25 @@ import { IsString, IsEnum } from 'class-validator';
export class LeagueMembershipDTO {
@ApiProperty()
@IsString()
id: string;
id!: string;
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty()
@IsString()
driverId: string;
driverId!: string;
@ApiProperty({ enum: ['owner', 'admin', 'steward', 'member'] })
@IsEnum(['owner', 'admin', 'steward', 'member'])
role: 'owner' | 'admin' | 'steward' | 'member';
role!: 'owner' | 'admin' | 'steward' | 'member';
@ApiProperty({ enum: ['active', 'inactive', 'pending'] })
@IsEnum(['active', 'inactive', 'pending'])
status: 'active' | 'inactive' | 'pending';
status!: 'active' | 'inactive' | 'pending';
@ApiProperty()
@IsString()
joinedAt: string;
joinedAt!: string;
}

View File

@@ -8,5 +8,5 @@ export class LeagueMembershipsDTO {
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueMemberDTO)
members: LeagueMemberDTO[];
members!: LeagueMemberDTO[];
}

View File

@@ -1,21 +1,21 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsOptional, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { DriverDto } from '../../driver/dto/DriverDto';
import { DriverDTO } from '../../driver/dtos/DriverDTO';
export class LeagueOwnerSummaryDTO {
@ApiProperty({ type: () => DriverDto })
@ApiProperty({ type: () => DriverDTO })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@Type(() => DriverDTO)
driver!: DriverDTO;
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
rating: number | null;
rating!: number | null;
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
rank: number | null;
rank!: number | null;
}

View File

@@ -4,5 +4,5 @@ import { IsEnum } from 'class-validator';
export class LeagueRoleDTO {
@ApiProperty({ enum: ['owner', 'admin', 'steward', 'member'] })
@IsEnum(['owner', 'admin', 'steward', 'member'])
value: 'owner' | 'admin' | 'steward' | 'member';
value!: 'owner' | 'admin' | 'steward' | 'member';
}

View File

@@ -1,12 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { RaceDto } from '../../race/dto/RaceDto';
import { RaceDTO } from '../../race/dtos/RaceDTO';
export class LeagueScheduleDTO {
@ApiProperty({ type: [RaceDto] })
@ApiProperty({ type: [RaceDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => RaceDto)
races: RaceDto[];
@Type(() => RaceDTO)
races!: RaceDTO[];
}

View File

@@ -4,29 +4,29 @@ import { IsString, IsEnum } from 'class-validator';
export class LeagueScoringPresetDTO {
@ApiProperty()
@IsString()
id: string;
id!: string;
@ApiProperty()
@IsString()
name: string;
name!: string;
@ApiProperty()
@IsString()
description: string;
description!: string;
@ApiProperty({ enum: ['driver', 'team', 'nations', 'trophy'] })
@IsEnum(['driver', 'team', 'nations', 'trophy'])
primaryChampionshipType: 'driver' | 'team' | 'nations' | 'trophy';
primaryChampionshipType!: 'driver' | 'team' | 'nations' | 'trophy';
@ApiProperty()
@IsString()
sessionSummary: string;
sessionSummary!: string;
@ApiProperty()
@IsString()
bonusSummary: string;
bonusSummary!: string;
@ApiProperty()
@IsString()
dropPolicySummary: string;
dropPolicySummary!: string;
}

View File

@@ -5,15 +5,15 @@ import { Type } from 'class-transformer';
export class LeagueSeasonSummaryDTO {
@ApiProperty()
@IsString()
seasonId: string;
seasonId!: string;
@ApiProperty()
@IsString()
name: string;
name!: string;
@ApiProperty()
@IsString()
status: string;
status!: string;
@ApiProperty({ required: false })
@IsOptional()
@@ -29,9 +29,9 @@ export class LeagueSeasonSummaryDTO {
@ApiProperty()
@IsBoolean()
isPrimary: boolean;
isPrimary!: boolean;
@ApiProperty()
@IsBoolean()
isParallelActive: boolean;
isParallelActive!: boolean;
}

View File

@@ -1,23 +1,23 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { DriverDto } from '../../driver/dto/DriverDto';
import { DriverDTO } from '../../driver/dtos/DriverDTO';
export class LeagueStandingDTO {
@ApiProperty()
@IsString()
driverId: string;
driverId!: string;
@ApiProperty({ type: () => DriverDto })
@ApiProperty({ type: () => DriverDTO })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@Type(() => DriverDTO)
driver!: DriverDTO;
@ApiProperty()
@IsNumber()
points: number;
points!: number;
@ApiProperty()
@IsNumber()
rank: number;
rank!: number;
}

View File

@@ -8,5 +8,5 @@ export class LeagueStandingsDTO {
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueStandingDTO)
standings: LeagueStandingDTO[];
standings!: LeagueStandingDTO[];
}

View File

@@ -4,13 +4,13 @@ import { IsNumber } from 'class-validator';
export class LeagueStatsDTO {
@ApiProperty()
@IsNumber()
totalMembers: number;
totalMembers!: number;
@ApiProperty()
@IsNumber()
totalRaces: number;
totalRaces!: number;
@ApiProperty()
@IsNumber()
averageRating: number;
averageRating!: number;
}

View File

@@ -4,11 +4,11 @@ import { IsString, IsNumber, IsBoolean, IsOptional } from 'class-validator';
export class LeagueSummaryDTO {
@ApiProperty()
@IsString()
id: string;
id!: string;
@ApiProperty()
@IsString()
name: string;
name!: string;
@ApiProperty({ nullable: true })
@IsOptional()
@@ -27,19 +27,19 @@ export class LeagueSummaryDTO {
@ApiProperty()
@IsNumber()
memberCount: number;
memberCount!: number;
@ApiProperty()
@IsNumber()
maxMembers: number;
maxMembers!: number;
@ApiProperty()
@IsBoolean()
isPublic: boolean;
isPublic!: boolean;
@ApiProperty()
@IsString()
ownerId: string;
ownerId!: string;
@ApiProperty({ nullable: true })
@IsOptional()

View File

@@ -6,11 +6,11 @@ import { LeagueSettingsDTO } from './LeagueSettingsDTO';
export class LeagueWithCapacityDTO {
@ApiProperty()
@IsString()
id: string;
id!: string;
@ApiProperty()
@IsString()
name: string;
name!: string;
// ... other properties of LeagueWithCapacityDTO
@ApiProperty({ nullable: true })
@@ -20,20 +20,20 @@ export class LeagueWithCapacityDTO {
@ApiProperty()
@IsString()
ownerId: string;
ownerId!: string;
@ApiProperty({ type: () => LeagueSettingsDTO })
@ValidateNested()
@Type(() => LeagueSettingsDTO)
settings: LeagueSettingsDTO;
settings!: LeagueSettingsDTO;
@ApiProperty()
@IsString()
createdAt: string;
createdAt!: string;
@ApiProperty()
@IsNumber()
usedSlots: number;
usedSlots!: number;
@ApiProperty({ type: () => Object, nullable: true }) // Using Object for generic social links
@IsOptional()

View File

@@ -4,5 +4,5 @@ import { IsEnum } from 'class-validator';
export class MembershipRoleDTO {
@ApiProperty({ enum: ['owner', 'admin', 'steward', 'member'] })
@IsEnum(['owner', 'admin', 'steward', 'member'])
value: 'owner' | 'admin' | 'steward' | 'member';
value!: 'owner' | 'admin' | 'steward' | 'member';
}

View File

@@ -4,5 +4,5 @@ import { IsEnum } from 'class-validator';
export class MembershipStatusDTO {
@ApiProperty({ enum: ['active', 'inactive', 'pending'] })
@IsEnum(['active', 'inactive', 'pending'])
value: 'active' | 'inactive' | 'pending';
value!: 'active' | 'inactive' | 'pending';
}

View File

@@ -13,34 +13,34 @@ import { Type } from 'class-transformer';
export class ProtestDTO {
@ApiProperty()
@IsString()
id: string;
id!: string;
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty()
@IsString()
raceId: string;
raceId!: string;
@ApiProperty()
@IsString()
protestingDriverId: string;
protestingDriverId!: string;
@ApiProperty()
@IsString()
accusedDriverId: string;
accusedDriverId!: string;
@ApiProperty()
@IsDate()
@Type(() => Date)
submittedAt: Date;
submittedAt!: Date;
@ApiProperty()
@IsString()
description: string;
description!: string;
@ApiProperty({ enum: ['pending', 'accepted', 'rejected'] })
@IsEnum(['pending', 'accepted', 'rejected'])
status: 'pending' | 'accepted' | 'rejected';
status!: 'pending' | 'accepted' | 'rejected';
}

View File

@@ -4,9 +4,9 @@ import { IsString } from 'class-validator';
export class RejectJoinRequestInputDTO {
@ApiProperty()
@IsString()
requestId: string;
requestId!: string;
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
}

View File

@@ -4,7 +4,7 @@ import { IsString, IsBoolean } from 'class-validator';
export class RejectJoinRequestOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
success!: boolean;
@ApiProperty({ required: false })
@IsString()

View File

@@ -4,13 +4,13 @@ import { IsString } from 'class-validator';
export class RemoveLeagueMemberInputDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty()
@IsString()
performerDriverId: string;
performerDriverId!: string;
@ApiProperty()
@IsString()
targetDriverId: string;
targetDriverId!: string;
}

View File

@@ -4,7 +4,7 @@ import { IsBoolean, IsOptional, IsString } from 'class-validator';
export class RemoveLeagueMemberOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
success!: boolean;
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -5,15 +5,15 @@ import { Type } from 'class-transformer';
export class SeasonDTO {
@ApiProperty()
@IsString()
seasonId: string;
seasonId!: string;
@ApiProperty()
@IsString()
name: string;
name!: string;
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty({ required: false })
@IsOptional()
@@ -29,11 +29,11 @@ export class SeasonDTO {
@ApiProperty({ enum: ['planned', 'active', 'completed'] })
@IsEnum(['planned', 'active', 'completed'])
status: 'planned' | 'active' | 'completed';
status!: 'planned' | 'active' | 'completed';
@ApiProperty()
@IsBoolean()
isPrimary: boolean;
isPrimary!: boolean;
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -4,5 +4,5 @@ import { IsNumber } from 'class-validator';
export class TotalLeaguesDTO {
@ApiProperty()
@IsNumber()
totalLeagues: number;
totalLeagues!: number;
}

View File

@@ -4,17 +4,17 @@ import { IsString, IsEnum } from 'class-validator';
export class UpdateLeagueMemberRoleInputDTO {
@ApiProperty()
@IsString()
leagueId: string;
leagueId!: string;
@ApiProperty()
@IsString()
performerDriverId: string;
performerDriverId!: string;
@ApiProperty()
@IsString()
targetDriverId: string;
targetDriverId!: string;
@ApiProperty({ enum: ['owner', 'manager', 'member'] })
@IsEnum(['owner', 'manager', 'member'])
newRole: 'owner' | 'manager' | 'member';
newRole!: 'owner' | 'manager' | 'member';
}

View File

@@ -4,7 +4,7 @@ import { IsBoolean, IsOptional, IsString } from 'class-validator';
export class UpdateLeagueMemberRoleOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
success!: boolean;
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -2,14 +2,14 @@ import { ApiProperty } from '@nestjs/swagger';
export class WithdrawFromLeagueWalletInputDTO {
@ApiProperty()
amount: number;
amount!: number;
@ApiProperty()
currency: string;
currency!: string;
@ApiProperty()
seasonId: string;
seasonId!: string;
@ApiProperty()
destinationAccount: string;
destinationAccount!: string;
}

View File

@@ -2,7 +2,7 @@ import { ApiProperty } from '@nestjs/swagger';
export class WithdrawFromLeagueWalletOutputDTO {
@ApiProperty()
success: boolean;
success!: boolean;
@ApiProperty({ required: false })
message?: string;

View File

@@ -4,5 +4,5 @@ import { IsEnum } from 'class-validator';
export class WizardStepDTO {
@ApiProperty({ enum: [1, 2, 3, 4, 5, 6, 7] })
@IsEnum([1, 2, 3, 4, 5, 6, 7])
value: 1 | 2 | 3 | 4 | 5 | 6 | 7;
value!: 1 | 2 | 3 | 4 | 5 | 6 | 7;
}

View File

@@ -1,30 +1,26 @@
import { GetLeagueOwnerSummaryOutputPort } from '@core/racing/application/ports/output/GetLeagueOwnerSummaryOutputPort';
import type { UseCaseOutputPort } from '@core/shared/application';
import type { GetLeagueOwnerSummaryResult } from '@core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase';
import { LeagueOwnerSummaryDTO } from '../dtos/LeagueOwnerSummaryDTO';
export class GetLeagueOwnerSummaryPresenter {
export class GetLeagueOwnerSummaryPresenter implements UseCaseOutputPort<GetLeagueOwnerSummaryResult> {
private result: LeagueOwnerSummaryDTO | null = null;
reset() {
this.result = null;
}
present(output: GetLeagueOwnerSummaryOutputPort) {
if (!output.summary) {
this.result = null;
return;
}
present(output: GetLeagueOwnerSummaryResult) {
this.result = {
driver: {
id: output.summary.driver.id,
iracingId: output.summary.driver.iracingId,
name: output.summary.driver.name,
country: output.summary.driver.country,
bio: output.summary.driver.bio,
joinedAt: output.summary.driver.joinedAt,
id: output.owner.id,
iracingId: output.owner.iracingId.toString(),
name: output.owner.name.toString(),
country: output.owner.country.toString(),
joinedAt: output.owner.joinedAt.toDate().toISOString(),
...(output.owner.bio ? { bio: output.owner.bio.toString() } : {}),
},
rating: output.summary.rating,
rank: output.summary.rank,
rating: output.rating,
rank: output.rank,
};
}

View File

@@ -1,10 +1,11 @@
import { GetLeagueProtestsOutputPort, type ProtestOutputPort } from '@core/racing/application/ports/output/GetLeagueProtestsOutputPort';
import { Presenter } from '@core/shared/presentation';
import type { GetLeagueProtestsResult } from '@core/racing/application/use-cases/GetLeagueProtestsUseCase';
import { LeagueAdminProtestsDTO } from '../dtos/LeagueAdminProtestsDTO';
import { ProtestDTO } from '../dtos/ProtestDTO';
import { RaceDTO } from '../../race/dtos/RaceDTO';
import { DriverDTO } from '../../driver/dtos/DriverDTO';
function mapProtestStatus(status: ProtestOutputPort['status']): ProtestDTO['status'] {
function mapProtestStatus(status: string): ProtestDTO['status'] {
switch (status) {
case 'pending':
case 'awaiting_defense':
@@ -20,53 +21,63 @@ function mapProtestStatus(status: ProtestOutputPort['status']): ProtestDTO['stat
}
}
export class GetLeagueProtestsPresenter {
export class GetLeagueProtestsPresenter implements Presenter<GetLeagueProtestsResult, LeagueAdminProtestsDTO> {
private result: LeagueAdminProtestsDTO | null = null;
reset() {
this.result = null;
}
present(output: GetLeagueProtestsOutputPort, leagueName?: string) {
const protests: ProtestDTO[] = output.protests.map((protest) => {
const race = output.racesById[protest.raceId];
present(input: GetLeagueProtestsResult) {
const protests: ProtestDTO[] = input.protests.map((protestWithEntities) => {
const { protest, race } = protestWithEntities;
return {
id: protest.id,
leagueId: race?.leagueId || '',
raceId: protest.raceId,
protestingDriverId: protest.protestingDriverId,
accusedDriverId: protest.accusedDriverId,
id: protest.id.toString(),
leagueId: race?.leagueId.toString() || '',
raceId: protest.raceId.toString(),
protestingDriverId: protest.protestingDriverId.toString(),
accusedDriverId: protest.accusedDriverId.toString(),
submittedAt: new Date(protest.filedAt),
description: protest.incident.description,
status: mapProtestStatus(protest.status),
description: protest.incident.description.toString(),
status: mapProtestStatus(protest.status.toString()),
};
});
const racesById: { [raceId: string]: RaceDTO } = {};
for (const raceId in output.racesById) {
const race = output.racesById[raceId];
for (const protestWithEntities of input.protests) {
const { race } = protestWithEntities;
if (race) {
racesById[raceId] = {
id: race.id,
name: race.track,
racesById[race.id.toString()] = {
id: race.id.toString(),
name: race.track.toString(),
date: race.scheduledAt.toISOString(),
leagueName,
leagueName: input.league.name.toString(),
};
}
}
const driversById: { [driverId: string]: DriverDTO } = {};
for (const driverId in output.driversById) {
const driver = output.driversById[driverId];
if (driver) {
driversById[driverId] = {
id: driver.id,
iracingId: driver.iracingId,
name: driver.name,
country: driver.country,
bio: driver.bio,
joinedAt: driver.joinedAt,
for (const protestWithEntities of input.protests) {
const { protestingDriver, accusedDriver } = protestWithEntities;
if (protestingDriver) {
driversById[protestingDriver.id.toString()] = {
id: protestingDriver.id.toString(),
iracingId: protestingDriver.iracingId.toString(),
name: protestingDriver.name.toString(),
country: protestingDriver.country.toString(),
bio: protestingDriver.bio?.toString(),
joinedAt: protestingDriver.joinedAt.toDate().toISOString(),
};
}
if (accusedDriver) {
driversById[accusedDriver.id.toString()] = {
id: accusedDriver.id.toString(),
iracingId: accusedDriver.iracingId.toString(),
name: accusedDriver.name.toString(),
country: accusedDriver.country.toString(),
bio: accusedDriver.bio?.toString(),
joinedAt: accusedDriver.joinedAt.toISOString(),
};
}
}
@@ -78,7 +89,7 @@ export class GetLeagueProtestsPresenter {
};
}
getViewModel(): LeagueAdminProtestsDTO | null {
getResponseModel(): LeagueAdminProtestsDTO | null {
return this.result;
}
}

View File

@@ -1,26 +1,27 @@
import { GetLeagueSeasonsOutputPort } from '@core/racing/application/ports/output/GetLeagueSeasonsOutputPort';
import { Presenter } from '@core/shared/presentation';
import type { GetLeagueSeasonsResult } from '@core/racing/application/use-cases/GetLeagueSeasonsUseCase';
import { LeagueSeasonSummaryDTO } from '../dtos/LeagueSeasonSummaryDTO';
export class GetLeagueSeasonsPresenter {
export class GetLeagueSeasonsPresenter implements Presenter<GetLeagueSeasonsResult, LeagueSeasonSummaryDTO[]> {
private result: LeagueSeasonSummaryDTO[] | null = null;
reset() {
this.result = null;
}
present(output: GetLeagueSeasonsOutputPort) {
this.result = output.seasons.map(season => ({
seasonId: season.seasonId,
name: season.name,
status: season.status,
startDate: season.startDate,
endDate: season.endDate,
isPrimary: season.isPrimary,
isParallelActive: season.isParallelActive,
present(input: GetLeagueSeasonsResult) {
this.result = input.seasons.map(seasonSummary => ({
seasonId: seasonSummary.season.id.toString(),
name: seasonSummary.season.name.toString(),
status: seasonSummary.season.status.toString(),
startDate: seasonSummary.season.startDate.toISOString(),
endDate: seasonSummary.season.endDate?.toISOString(),
isPrimary: seasonSummary.isPrimary,
isParallelActive: seasonSummary.isParallelActive,
}));
}
getViewModel(): LeagueSeasonSummaryDTO[] | null {
getResponseModel(): LeagueSeasonSummaryDTO[] | null {
return this.result;
}
}