This commit is contained in:
2025-12-17 14:04:11 +01:00
parent 1ea9c9649f
commit daa4bb6576
238 changed files with 4263 additions and 1752 deletions

View File

@@ -1,8 +1,35 @@
import { Controller, Get, Post, Patch, Body, Param } from '@nestjs/common';
import { ApiTags, ApiResponse, ApiOperation, ApiBody } from '@nestjs/swagger';
import { LeagueService } from './LeagueService';
import { AllLeaguesWithCapacityViewModel, LeagueStatsDto, LeagueJoinRequestViewModel, ApproveJoinRequestInput, ApproveJoinRequestOutput, RejectJoinRequestInput, RejectJoinRequestOutput, LeagueAdminPermissionsViewModel, RemoveLeagueMemberInput, RemoveLeagueMemberOutput, UpdateLeagueMemberRoleInput, UpdateLeagueMemberRoleOutput, LeagueOwnerSummaryViewModel, LeagueConfigFormModelDto, LeagueAdminProtestsViewModel, LeagueSeasonSummaryViewModel, LeagueMembershipsViewModel, LeagueStandingsViewModel, LeagueScheduleViewModel, LeagueStatsViewModel, LeagueAdminViewModel, CreateLeagueInput, CreateLeagueOutput } from './dto/LeagueDto';
import { GetLeagueAdminPermissionsInput, GetLeagueJoinRequestsQuery, GetLeagueProtestsQuery, GetLeagueSeasonsQuery, GetLeagueAdminConfigQuery, GetLeagueOwnerSummaryQuery } from './dto/LeagueDto'; // Explicitly import queries
import { AllLeaguesWithCapacityDTO } from './dtos/AllLeaguesWithCapacityDTO';
import { LeagueStatsDTO } from './dtos/LeagueStatsDTO';
import { LeagueJoinRequestDTO } from './dtos/LeagueJoinRequestDTO';
import { ApproveJoinRequestInputDTO } from './dtos/ApproveJoinRequestInputDTO';
import { ApproveJoinRequestOutputDTO } from './dtos/ApproveJoinRequestOutputDTO';
import { RejectJoinRequestInputDTO } from './dtos/RejectJoinRequestInputDTO';
import { RejectJoinRequestOutputDTO } from './dtos/RejectJoinRequestOutputDTO';
import { LeagueAdminPermissionsDTO } from './dtos/LeagueAdminPermissionsDTO';
import { RemoveLeagueMemberInputDTO } from './dtos/RemoveLeagueMemberInputDTO';
import { RemoveLeagueMemberOutputDTO } from './dtos/RemoveLeagueMemberOutputDTO';
import { UpdateLeagueMemberRoleInputDTO } from './dtos/UpdateLeagueMemberRoleInputDTO';
import { UpdateLeagueMemberRoleOutputDTO } from './dtos/UpdateLeagueMemberRoleOutputDTO';
import { LeagueOwnerSummaryDTO } from './dtos/LeagueOwnerSummaryDTO';
import { LeagueConfigFormModelDTO } from './dtos/LeagueConfigFormModelDTO';
import { LeagueAdminProtestsDTO } from './dtos/LeagueAdminProtestsDTO';
import { LeagueSeasonSummaryDTO } from './dtos/LeagueSeasonSummaryDTO';
import { LeagueMembershipsDTO } from './dtos/LeagueMembershipsDTO';
import { LeagueStandingsDTO } from './dtos/LeagueStandingsDTO';
import { LeagueScheduleDTO } from './dtos/LeagueScheduleDTO';
import { LeagueStatsDTO } from './dtos/LeagueStatsDTO';
import { LeagueAdminDTO } from './dtos/LeagueAdminDTO';
import { CreateLeagueInputDTO } from './dtos/CreateLeagueInputDTO';
import { CreateLeagueOutputDTO } from './dtos/CreateLeagueOutputDTO';
import { GetLeagueAdminPermissionsInputDTO } from './dtos/GetLeagueAdminPermissionsInputDTO';
import { GetLeagueJoinRequestsQueryDTO } from './dtos/GetLeagueJoinRequestsQueryDTO';
import { GetLeagueProtestsQueryDTO } from './dtos/GetLeagueProtestsQueryDTO';
import { GetLeagueSeasonsQueryDTO } from './dtos/GetLeagueSeasonsQueryDTO';
import { GetLeagueAdminConfigQueryDTO } from './dtos/GetLeagueAdminConfigQueryDTO';
import { GetLeagueOwnerSummaryQueryDTO } from './dtos/GetLeagueOwnerSummaryQueryDTO';
@ApiTags('leagues')
@Controller('leagues')
@@ -11,169 +38,197 @@ export class LeagueController {
@Get('all-with-capacity')
@ApiOperation({ summary: 'Get all leagues with their capacity information' })
@ApiResponse({ status: 200, description: 'List of leagues with capacity', type: AllLeaguesWithCapacityViewModel })
async getAllLeaguesWithCapacity(): Promise<AllLeaguesWithCapacityViewModel> {
@ApiResponse({ status: 200, description: 'List of leagues with capacity', type: AllLeaguesWithCapacityDTO })
async getAllLeaguesWithCapacity(): Promise<AllLeaguesWithCapacityDTO> {
return this.leagueService.getAllLeaguesWithCapacity();
}
@Get('total-leagues')
@ApiOperation({ summary: 'Get the total number of leagues' })
@ApiResponse({ status: 200, description: 'Total number of leagues', type: LeagueStatsDto })
async getTotalLeagues(): Promise<LeagueStatsDto> {
@ApiResponse({ status: 200, description: 'Total number of leagues', type: LeagueStatsDTO })
async getTotalLeagues(): Promise<LeagueStatsDTO> {
return this.leagueService.getTotalLeagues();
}
@Get(':leagueId/join-requests')
@ApiOperation({ summary: 'Get all outstanding join requests for a league' })
@ApiResponse({ status: 200, description: 'List of join requests', type: [LeagueJoinRequestViewModel] })
async getJoinRequests(@Param('leagueId') leagueId: string): Promise<LeagueJoinRequestViewModel[]> {
@ApiResponse({ status: 200, description: 'List of join requests', type: [LeagueJoinRequestDTO] })
async getJoinRequests(@Param('leagueId') leagueId: string): Promise<LeagueJoinRequestDTO[]> {
// No specific query DTO needed for GET, leagueId from param
return this.leagueService.getLeagueJoinRequests(leagueId);
}
@Post(':leagueId/join-requests/approve')
@ApiOperation({ summary: 'Approve a league join request' })
@ApiBody({ type: ApproveJoinRequestInput }) // Explicitly define body type for Swagger
@ApiResponse({ status: 200, description: 'Join request approved', type: ApproveJoinRequestOutput })
@ApiBody({ type: ApproveJoinRequestInputDTO }) // Explicitly define body type for Swagger
@ApiResponse({ status: 200, description: 'Join request approved', type: ApproveJoinRequestOutputDTO })
@ApiResponse({ status: 404, description: 'Join request not found' })
async approveJoinRequest(
@Param('leagueId') leagueId: string,
@Body() input: ApproveJoinRequestInput,
): Promise<ApproveJoinRequestOutput> {
@Body() input: ApproveJoinRequestInputDTO,
): Promise<ApproveJoinRequestOutputDTO> {
return this.leagueService.approveLeagueJoinRequest({ ...input, leagueId });
}
@Post(':leagueId/join-requests/reject')
@ApiOperation({ summary: 'Reject a league join request' })
@ApiBody({ type: RejectJoinRequestInput })
@ApiResponse({ status: 200, description: 'Join request rejected', type: RejectJoinRequestOutput })
@ApiBody({ type: RejectJoinRequestInputDTO })
@ApiResponse({ status: 200, description: 'Join request rejected', type: RejectJoinRequestOutputDTO })
@ApiResponse({ status: 404, description: 'Join request not found' })
async rejectJoinRequest(
@Param('leagueId') leagueId: string,
@Body() input: RejectJoinRequestInput,
): Promise<RejectJoinRequestOutput> {
@Body() input: RejectJoinRequestInputDTO,
): Promise<RejectJoinRequestOutputDTO> {
return this.leagueService.rejectLeagueJoinRequest({ ...input, leagueId });
}
@Get(':leagueId/permissions/:performerDriverId')
@ApiOperation({ summary: 'Get league admin permissions for a performer' })
@ApiResponse({ status: 200, description: 'League admin permissions', type: LeagueAdminPermissionsViewModel })
@ApiResponse({ status: 200, description: 'League admin permissions', type: LeagueAdminPermissionsDTO })
async getLeagueAdminPermissions(
@Param('leagueId') leagueId: string,
@Param('performerDriverId') performerDriverId: string,
): Promise<LeagueAdminPermissionsViewModel> {
): Promise<LeagueAdminPermissionsDTO> {
// No specific input DTO needed for Get, parameters from path
return this.leagueService.getLeagueAdminPermissions({ leagueId, performerDriverId });
}
@Patch(':leagueId/members/:targetDriverId/remove')
@ApiOperation({ summary: 'Remove a member from the league' })
@ApiBody({ type: RemoveLeagueMemberInput }) // Explicitly define body type for Swagger
@ApiResponse({ status: 200, description: 'Member removed successfully', type: RemoveLeagueMemberOutput })
@ApiBody({ type: RemoveLeagueMemberInputDTO }) // Explicitly define body type for Swagger
@ApiResponse({ status: 200, description: 'Member removed successfully', type: RemoveLeagueMemberOutputDTO })
@ApiResponse({ status: 400, description: 'Cannot remove member' })
@ApiResponse({ status: 404, description: 'Member not found' })
async removeLeagueMember(
@Param('leagueId') leagueId: string,
@Param('performerDriverId') performerDriverId: string,
@Param('targetDriverId') targetDriverId: string,
@Body() input: RemoveLeagueMemberInput, // Body content for a patch often includes IDs
): Promise<RemoveLeagueMemberOutput> {
@Body() input: RemoveLeagueMemberInputDTO, // Body content for a patch often includes IDs
): Promise<RemoveLeagueMemberOutputDTO> {
return this.leagueService.removeLeagueMember({ leagueId, performerDriverId, targetDriverId });
}
@Patch(':leagueId/members/:targetDriverId/role')
@ApiOperation({ summary: "Update a member's role in the league" })
@ApiBody({ type: UpdateLeagueMemberRoleInput }) // Explicitly define body type for Swagger
@ApiResponse({ status: 200, description: 'Member role updated successfully', type: UpdateLeagueMemberRoleOutput })
@ApiBody({ type: UpdateLeagueMemberRoleInputDTO }) // Explicitly define body type for Swagger
@ApiResponse({ status: 200, description: 'Member role updated successfully', type: UpdateLeagueMemberRoleOutputDTO })
@ApiResponse({ status: 400, description: 'Cannot update role' })
@ApiResponse({ status: 404, description: 'Member not found' })
async updateLeagueMemberRole(
@Param('leagueId') leagueId: string,
@Param('performerDriverId') performerDriverId: string,
@Param('targetDriverId') targetDriverId: string,
@Body() input: UpdateLeagueMemberRoleInput, // Body includes newRole, other for swagger
): Promise<UpdateLeagueMemberRoleOutput> {
@Body() input: UpdateLeagueMemberRoleInputDTO, // Body includes newRole, other for swagger
): Promise<UpdateLeagueMemberRoleOutputDTO> {
return this.leagueService.updateLeagueMemberRole({ leagueId, performerDriverId, targetDriverId, newRole: input.newRole });
}
@Get(':leagueId/owner-summary/:ownerId')
@ApiOperation({ summary: 'Get owner summary for a league' })
@ApiResponse({ status: 200, description: 'League owner summary', type: LeagueOwnerSummaryViewModel })
@ApiResponse({ status: 200, description: 'League owner summary', type: LeagueOwnerSummaryDTO })
@ApiResponse({ status: 404, description: 'Owner or league not found' })
async getLeagueOwnerSummary(
@Param('leagueId') leagueId: string,
@Param('ownerId') ownerId: string,
): Promise<LeagueOwnerSummaryViewModel | null> {
): Promise<LeagueOwnerSummaryDTO | null> {
const query: GetLeagueOwnerSummaryQuery = { ownerId, leagueId };
return this.leagueService.getLeagueOwnerSummary(query);
}
@Get(':leagueId/config')
@ApiOperation({ summary: 'Get league full configuration' })
@ApiResponse({ status: 200, description: 'League configuration form model', type: LeagueConfigFormModelDto })
@ApiResponse({ status: 200, description: 'League configuration form model', type: LeagueConfigFormModelDTO })
async getLeagueFullConfig(
@Param('leagueId') leagueId: string,
): Promise<LeagueConfigFormModelDto | null> {
): Promise<LeagueConfigFormModelDTO | null> {
const query: GetLeagueAdminConfigQuery = { leagueId };
return this.leagueService.getLeagueFullConfig(query);
}
@Get(':leagueId/protests')
@ApiOperation({ summary: 'Get protests for a league' })
@ApiResponse({ status: 200, description: 'List of protests for the league', type: LeagueAdminProtestsViewModel })
async getLeagueProtests(@Param('leagueId') leagueId: string): Promise<LeagueAdminProtestsViewModel> {
@ApiResponse({ status: 200, description: 'List of protests for the league', type: LeagueAdminProtestsDTO })
async getLeagueProtests(@Param('leagueId') leagueId: string): Promise<LeagueAdminProtestsDTO> {
const query: GetLeagueProtestsQuery = { leagueId };
return this.leagueService.getLeagueProtests(query);
}
@Get(':leagueId/seasons')
@ApiOperation({ summary: 'Get seasons for a league' })
@ApiResponse({ status: 200, description: 'List of seasons for the league', type: [LeagueSeasonSummaryViewModel] })
async getLeagueSeasons(@Param('leagueId') leagueId: string): Promise<LeagueSeasonSummaryViewModel[]> {
@ApiResponse({ status: 200, description: 'List of seasons for the league', type: [LeagueSeasonSummaryDTO] })
async getLeagueSeasons(@Param('leagueId') leagueId: string): Promise<LeagueSeasonSummaryDTO[]> {
const query: GetLeagueSeasonsQuery = { leagueId };
return this.leagueService.getLeagueSeasons(query);
}
@Get(':leagueId/memberships')
@ApiOperation({ summary: 'Get league memberships' })
@ApiResponse({ status: 200, description: 'List of league members', type: LeagueMembershipsViewModel })
async getLeagueMemberships(@Param('leagueId') leagueId: string): Promise<LeagueMembershipsViewModel> {
@ApiResponse({ status: 200, description: 'List of league members', type: LeagueMembershipsDTO })
async getLeagueMemberships(@Param('leagueId') leagueId: string): Promise<LeagueMembershipsDTO> {
return this.leagueService.getLeagueMemberships(leagueId);
}
@Get(':leagueId/standings')
@ApiOperation({ summary: 'Get league standings' })
@ApiResponse({ status: 200, description: 'League standings', type: LeagueStandingsViewModel })
async getLeagueStandings(@Param('leagueId') leagueId: string): Promise<LeagueStandingsViewModel> {
@ApiResponse({ status: 200, description: 'League standings', type: LeagueStandingsDTO })
async getLeagueStandings(@Param('leagueId') leagueId: string): Promise<LeagueStandingsDTO> {
return this.leagueService.getLeagueStandings(leagueId);
}
@Get(':leagueId/schedule')
@ApiOperation({ summary: 'Get league schedule' })
@ApiResponse({ status: 200, description: 'League schedule', type: LeagueScheduleViewModel })
async getLeagueSchedule(@Param('leagueId') leagueId: string): Promise<LeagueScheduleViewModel> {
@ApiResponse({ status: 200, description: 'League schedule', type: LeagueScheduleDTO })
async getLeagueSchedule(@Param('leagueId') leagueId: string): Promise<LeagueScheduleDTO> {
return this.leagueService.getLeagueSchedule(leagueId);
}
@Get(':leagueId/stats')
@ApiOperation({ summary: 'Get league stats' })
@ApiResponse({ status: 200, description: 'League stats', type: LeagueStatsViewModel })
async getLeagueStats(@Param('leagueId') leagueId: string): Promise<LeagueStatsViewModel> {
@ApiResponse({ status: 200, description: 'League stats', type: LeagueStatsDTO })
async getLeagueStats(@Param('leagueId') leagueId: string): Promise<LeagueStatsDTO> {
return this.leagueService.getLeagueStats(leagueId);
}
@Get(':leagueId/admin')
@ApiOperation({ summary: 'Get league admin data' })
@ApiResponse({ status: 200, description: 'League admin data', type: LeagueAdminViewModel })
async getLeagueAdmin(@Param('leagueId') leagueId: string): Promise<LeagueAdminViewModel> {
@ApiResponse({ status: 200, description: 'League admin data', type: LeagueAdminDTO })
async getLeagueAdmin(@Param('leagueId') leagueId: string): Promise<LeagueAdminDTO> {
return this.leagueService.getLeagueAdmin(leagueId);
}
@Post()
@ApiOperation({ summary: 'Create a new league' })
@ApiBody({ type: CreateLeagueInput })
@ApiResponse({ status: 201, description: 'League created successfully', type: CreateLeagueOutput })
async createLeague(@Body() input: CreateLeagueInput): Promise<CreateLeagueOutput> {
@ApiBody({ type: CreateLeagueInputDTO })
@ApiResponse({ status: 201, description: 'League created successfully', type: CreateLeagueOutputDTO })
async createLeague(@Body() input: CreateLeagueInputDTO): Promise<CreateLeagueOutputDTO> {
return this.leagueService.createLeague(input);
}
@Get('scoring-presets')
@ApiOperation({ summary: 'Get league scoring presets' })
@ApiResponse({ status: 200, description: 'List of scoring presets' })
async getLeagueScoringPresets() {
return this.leagueService.listLeagueScoringPresets();
}
@Get(':leagueId/scoring-config')
@ApiOperation({ summary: 'Get league scoring config' })
@ApiResponse({ status: 200, description: 'League scoring config' })
async getLeagueScoringConfig(@Param('leagueId') leagueId: string) {
return this.leagueService.getLeagueScoringConfig(leagueId);
}
@Post(':leagueId/join')
@ApiOperation({ summary: 'Join a league' })
@ApiResponse({ status: 200, description: 'Joined league successfully' })
async joinLeague(@Param('leagueId') leagueId: string, @Body() body: { driverId: string }) {
return this.leagueService.joinLeague(leagueId, body.driverId);
}
@Post(':leagueId/transfer-ownership')
@ApiOperation({ summary: 'Transfer league ownership' })
@ApiResponse({ status: 200, description: 'Ownership transferred successfully' })
async transferLeagueOwnership(@Param('leagueId') leagueId: string, @Body() body: { currentOwnerId: string, newOwnerId: string }) {
return this.leagueService.transferLeagueOwnership(leagueId, body.currentOwnerId, body.newOwnerId);
}
}

View File

@@ -1,5 +1,32 @@
import { Injectable, Inject } from '@nestjs/common';
import { AllLeaguesWithCapacityViewModel, LeagueStatsDto, LeagueJoinRequestViewModel, ApproveJoinRequestInput, ApproveJoinRequestOutput, RejectJoinRequestInput, RejectJoinRequestOutput, LeagueAdminPermissionsViewModel, RemoveLeagueMemberInput, RemoveLeagueMemberOutput, UpdateLeagueMemberRoleInput, UpdateLeagueMemberRoleOutput, LeagueOwnerSummaryViewModel, LeagueConfigFormModelDto, LeagueAdminProtestsViewModel, LeagueSeasonSummaryViewModel, GetLeagueAdminPermissionsInput, GetLeagueProtestsQuery, GetLeagueSeasonsQuery, GetLeagueAdminConfigQuery, GetLeagueOwnerSummaryQuery, LeagueMembershipsViewModel, LeagueStandingsViewModel, LeagueScheduleViewModel, LeagueStatsViewModel, LeagueAdminViewModel, CreateLeagueInput, CreateLeagueOutput } from './dto/LeagueDto';
import { AllLeaguesWithCapacityDTO } from './dtos/AllLeaguesWithCapacityDTO';
import { LeagueStatsDTO } from './dtos/LeagueStatsDTO';
import { LeagueJoinRequestDTO } from './dtos/LeagueJoinRequestDTO';
import { ApproveJoinRequestInputDTO } from './dtos/ApproveJoinRequestInputDTO';
import { ApproveJoinRequestOutputDTO } from './dtos/ApproveJoinRequestOutputDTO';
import { RejectJoinRequestInputDTO } from './dtos/RejectJoinRequestInputDTO';
import { RejectJoinRequestOutputDTO } from './dtos/RejectJoinRequestOutputDTO';
import { LeagueAdminPermissionsDTO } from './dtos/LeagueAdminPermissionsDTO';
import { RemoveLeagueMemberInputDTO } from './dtos/RemoveLeagueMemberInputDTO';
import { RemoveLeagueMemberOutputDTO } from './dtos/RemoveLeagueMemberOutputDTO';
import { UpdateLeagueMemberRoleInputDTO } from './dtos/UpdateLeagueMemberRoleInputDTO';
import { UpdateLeagueMemberRoleOutputDTO } from './dtos/UpdateLeagueMemberRoleOutputDTO';
import { LeagueOwnerSummaryDTO } from './dtos/LeagueOwnerSummaryDTO';
import { LeagueConfigFormModelDTO } from './dtos/LeagueConfigFormModelDTO';
import { LeagueAdminProtestsDTO } from './dtos/LeagueAdminProtestsDTO';
import { LeagueSeasonSummaryDTO } from './dtos/LeagueSeasonSummaryDTO';
import { GetLeagueAdminPermissionsInputDTO } from './dtos/GetLeagueAdminPermissionsInputDTO';
import { GetLeagueProtestsQueryDTO } from './dtos/GetLeagueProtestsQueryDTO';
import { GetLeagueSeasonsQueryDTO } from './dtos/GetLeagueSeasonsQueryDTO';
import { GetLeagueAdminConfigQueryDTO } from './dtos/GetLeagueAdminConfigQueryDTO';
import { GetLeagueOwnerSummaryQueryDTO } from './dtos/GetLeagueOwnerSummaryQueryDTO';
import { LeagueMembershipsDTO } from './dtos/LeagueMembershipsDTO';
import { LeagueStandingsDTO } from './dtos/LeagueStandingsDTO';
import { LeagueScheduleDTO } from './dtos/LeagueScheduleDTO';
import { LeagueStatsDTO } from './dtos/LeagueStatsDTO';
import { LeagueAdminDTO } from './dtos/LeagueAdminDTO';
import { CreateLeagueInputDTO } from './dtos/CreateLeagueInputDTO';
import { CreateLeagueOutputDTO } from './dtos/CreateLeagueOutputDTO';
// Core imports
import type { Logger } from '@core/shared/application/Logger';
@@ -9,6 +36,10 @@ import { GetAllLeaguesWithCapacityUseCase } from '@core/racing/application/use-c
import { GetLeagueStandingsUseCase } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
import { GetLeagueFullConfigUseCase } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase';
import { GetLeagueScoringConfigUseCase } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase';
import { ListLeagueScoringPresetsUseCase } from '@core/racing/application/use-cases/ListLeagueScoringPresetsUseCase';
import { JoinLeagueUseCase } from '@core/racing/application/use-cases/JoinLeagueUseCase';
import { TransferLeagueOwnershipUseCase } from '@core/racing/application/use-cases/TransferLeagueOwnershipUseCase';
import { CreateLeagueWithSeasonAndScoringUseCase } from '@core/racing/application/use-cases/CreateLeagueWithSeasonAndScoringUseCase';
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
import { GetTotalLeaguesUseCase } from '@core/racing/application/use-cases/GetTotalLeaguesUseCase';
@@ -27,6 +58,8 @@ import { GetLeagueAdminPermissionsUseCase } from '@core/racing/application/use-c
// API Presenters
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
import { AllLeaguesWithCapacityPresenter } from './presenters/AllLeaguesWithCapacityPresenter';
import { LeagueScoringConfigPresenter } from './presenters/LeagueScoringConfigPresenter';
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
import { LeagueJoinRequestsPresenter } from './presenters/LeagueJoinRequestsPresenter';
import { ApproveLeagueJoinRequestPresenter } from './presenters/ApproveLeagueJoinRequestPresenter';
import { RejectLeagueJoinRequestPresenter } from './presenters/RejectLeagueJoinRequestPresenter';
@@ -52,6 +85,10 @@ export class LeagueService {
private readonly getLeagueStandingsUseCase: GetLeagueStandingsUseCase,
private readonly getLeagueStatsUseCase: GetLeagueStatsUseCase,
private readonly getLeagueFullConfigUseCase: GetLeagueFullConfigUseCase,
private readonly getLeagueScoringConfigUseCase: GetLeagueScoringConfigUseCase,
private readonly listLeagueScoringPresetsUseCase: ListLeagueScoringPresetsUseCase,
private readonly joinLeagueUseCase: JoinLeagueUseCase,
private readonly transferLeagueOwnershipUseCase: TransferLeagueOwnershipUseCase,
private readonly createLeagueWithSeasonAndScoringUseCase: CreateLeagueWithSeasonAndScoringUseCase,
private readonly getRaceProtestsUseCase: GetRaceProtestsUseCase,
private readonly getTotalLeaguesUseCase: GetTotalLeaguesUseCase,
@@ -234,4 +271,61 @@ export class LeagueService {
success: true,
};
}
async getLeagueScoringConfig(leagueId: string): Promise<LeagueScoringConfigViewModel | null> {
this.logger.debug('Getting league scoring config', { leagueId });
const presenter = new LeagueScoringConfigPresenter();
try {
const result = await this.getLeagueScoringConfigUseCase.execute({ leagueId });
if (result.isErr()) {
this.logger.error('Error getting league scoring config', result.error);
return null;
}
await presenter.present(result.value);
return presenter.getViewModel();
} catch (error) {
this.logger.error('Error getting league scoring config', error instanceof Error ? error : new Error(String(error)));
return null;
}
}
async listLeagueScoringPresets(): Promise<LeagueScoringPresetsViewModel> {
this.logger.debug('Listing league scoring presets');
const presenter = new LeagueScoringPresetsPresenter();
await this.listLeagueScoringPresetsUseCase.execute(undefined, presenter);
return presenter.getViewModel()!;
}
async joinLeague(leagueId: string, driverId: string): Promise<JoinLeagueOutput> {
this.logger.debug('Joining league', { leagueId, driverId });
const result = await this.joinLeagueUseCase.execute({ leagueId, driverId });
if (result.isErr()) {
return {
success: false,
error: result.error.code,
};
}
return {
success: true,
membershipId: result.value.id,
};
}
async transferLeagueOwnership(leagueId: string, currentOwnerId: string, newOwnerId: string): Promise<TransferLeagueOwnershipOutput> {
this.logger.debug('Transferring league ownership', { leagueId, currentOwnerId, newOwnerId });
const result = await this.transferLeagueOwnershipUseCase.execute({ leagueId, currentOwnerId, newOwnerId });
if (result.isErr()) {
return {
success: false,
error: result.error.code,
};
}
return {
success: true,
};
}
}

View File

@@ -1,666 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber, IsBoolean, IsDate, IsOptional, IsEnum, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { DriverDto } from '../../driver/dto/DriverDto';
import { RaceDto } from '../../race/dto/RaceDto';
export class LeagueSettingsDto {
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
maxDrivers?: number;
// Add other league settings properties as needed
}
export class LeagueWithCapacityViewModel {
@ApiProperty()
@IsString()
id: string;
@ApiProperty()
@IsString()
name: string;
// ... other properties of LeagueWithCapacityViewModel
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
description?: string;
@ApiProperty()
@IsString()
ownerId: string;
@ApiProperty({ type: () => LeagueSettingsDto })
@ValidateNested()
@Type(() => LeagueSettingsDto)
settings: LeagueSettingsDto;
@ApiProperty()
@IsString()
createdAt: string;
@ApiProperty()
@IsNumber()
usedSlots: number;
@ApiProperty({ type: () => Object, nullable: true }) // Using Object for generic social links
@IsOptional()
socialLinks?: {
discordUrl?: string;
youtubeUrl?: string;
websiteUrl?: string;
};
}
export class AllLeaguesWithCapacityViewModel {
@ApiProperty({ type: [LeagueWithCapacityViewModel] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueWithCapacityViewModel)
leagues: LeagueWithCapacityViewModel[];
@ApiProperty()
@IsNumber()
totalCount: number;
}
export class LeagueStatsDto {
@ApiProperty()
@IsNumber()
totalLeagues: number;
}
export class ProtestDto {
@ApiProperty()
@IsString()
id: string;
@ApiProperty()
@IsString()
raceId: string;
@ApiProperty()
@IsString()
protestingDriverId: string;
@ApiProperty()
@IsString()
accusedDriverId: string;
@ApiProperty()
@IsDate()
@Type(() => Date)
submittedAt: Date;
@ApiProperty()
@IsString()
description: string;
@ApiProperty({ enum: ['pending', 'accepted', 'rejected'] })
@IsEnum(['pending', 'accepted', 'rejected'])
status: 'pending' | 'accepted' | 'rejected';
}
export class SeasonDto {
@ApiProperty()
@IsString()
seasonId: string;
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
startDate?: Date;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
endDate?: Date;
@ApiProperty({ enum: ['planned', 'active', 'completed'] })
@IsEnum(['planned', 'active', 'completed'])
status: 'planned' | 'active' | 'completed';
@ApiProperty()
@IsBoolean()
isPrimary: boolean;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
seasonGroupId?: string;
}
export class LeagueJoinRequestViewModel {
@ApiProperty()
@IsString()
id: string;
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
driverId: string;
@ApiProperty()
@IsDate()
@Type(() => Date)
requestedAt: Date;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
message?: string;
@ApiProperty({ type: () => DriverDto, required: false })
@IsOptional()
@ValidateNested()
@Type(() => DriverDto)
driver?: DriverDto;
}
export class GetLeagueJoinRequestsQuery {
@ApiProperty()
@IsString()
leagueId: string;
}
export class ApproveJoinRequestInput {
@ApiProperty()
@IsString()
requestId: string;
@ApiProperty()
@IsString()
leagueId: string;
}
export class ApproveJoinRequestOutput {
@ApiProperty()
@IsBoolean()
success: boolean;
@ApiProperty({ required: false })
@IsString()
message?: string;
}
export class RejectJoinRequestInput {
@ApiProperty()
@IsString()
requestId: string;
@ApiProperty()
@IsString()
leagueId: string;
}
export class RejectJoinRequestOutput {
@ApiProperty()
@IsBoolean()
success: boolean;
@ApiProperty({ required: false })
@IsString()
message?: string;
}
export class GetLeagueAdminPermissionsInput {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
performerDriverId: string;
}
export class LeagueAdminPermissionsViewModel {
@ApiProperty()
@IsBoolean()
canRemoveMember: boolean;
@ApiProperty()
@IsBoolean()
canUpdateRoles: boolean;
}
export class RemoveLeagueMemberInput {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
performerDriverId: string;
@ApiProperty()
@IsString()
targetDriverId: string;
}
export class RemoveLeagueMemberOutput {
@ApiProperty()
@IsBoolean()
success: boolean;
}
export class UpdateLeagueMemberRoleInput {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
performerDriverId: string;
@ApiProperty()
@IsString()
targetDriverId: string;
@ApiProperty({ enum: ['owner', 'manager', 'member'] })
@IsEnum(['owner', 'manager', 'member'])
newRole: 'owner' | 'manager' | 'member';
}
export class UpdateLeagueMemberRoleOutput {
@ApiProperty()
@IsBoolean()
success: boolean;
}
export class GetLeagueOwnerSummaryQuery {
@ApiProperty()
@IsString()
ownerId: string;
@ApiProperty()
@IsString()
leagueId: string;
}
export class LeagueOwnerSummaryViewModel {
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
rating: number | null;
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
rank: number | null;
}
export class LeagueConfigFormModelBasicsDto {
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
description: string;
@ApiProperty({ enum: ['public', 'private'] })
@IsEnum(['public', 'private'])
visibility: 'public' | 'private';
}
export class LeagueConfigFormModelStructureDto {
@ApiProperty()
@IsString()
@IsEnum(['solo', 'team'])
mode: 'solo' | 'team';
}
export class LeagueConfigFormModelScoringDto {
@ApiProperty()
@IsString()
type: string;
@ApiProperty()
@IsNumber()
points: number;
}
export class LeagueConfigFormModelDropPolicyDto {
@ApiProperty({ enum: ['none', 'worst_n'] })
@IsEnum(['none', 'worst_n'])
strategy: 'none' | 'worst_n';
@ApiProperty({ required: false })
@IsOptional()
@IsNumber()
n?: number;
}
export class LeagueConfigFormModelStewardingDto {
@ApiProperty({ enum: ['single_steward', 'committee_vote'] })
@IsEnum(['single_steward', 'committee_vote'])
decisionMode: 'single_steward' | 'committee_vote';
@ApiProperty({ required: false })
@IsOptional()
@IsNumber()
requiredVotes?: number;
@ApiProperty()
@IsBoolean()
requireDefense: boolean;
@ApiProperty()
@IsNumber()
defenseTimeLimit: number;
@ApiProperty()
@IsNumber()
voteTimeLimit: number;
@ApiProperty()
@IsNumber()
protestDeadlineHours: number;
@ApiProperty()
@IsNumber()
stewardingClosesHours: number;
@ApiProperty()
@IsBoolean()
notifyAccusedOnProtest: boolean;
@ApiProperty()
@IsBoolean()
notifyOnVoteRequired: boolean;
}
export class LeagueConfigFormModelTimingsDto {
@ApiProperty()
@IsString()
raceDayOfWeek: string;
@ApiProperty()
@IsNumber()
raceTimeHour: number;
@ApiProperty()
@IsNumber()
raceTimeMinute: number;
}
export class LeagueConfigFormModelDto {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty({ type: LeagueConfigFormModelBasicsDto })
@ValidateNested()
@Type(() => LeagueConfigFormModelBasicsDto)
basics: LeagueConfigFormModelBasicsDto;
@ApiProperty({ type: LeagueConfigFormModelStructureDto })
@ValidateNested()
@Type(() => LeagueConfigFormModelStructureDto)
structure: LeagueConfigFormModelStructureDto;
@ApiProperty({ type: [Object] })
@IsArray()
championships: any[];
@ApiProperty({ type: LeagueConfigFormModelScoringDto })
@ValidateNested()
@Type(() => LeagueConfigFormModelScoringDto)
scoring: LeagueConfigFormModelScoringDto;
@ApiProperty({ type: LeagueConfigFormModelDropPolicyDto })
@ValidateNested()
@Type(() => LeagueConfigFormModelDropPolicyDto)
dropPolicy: LeagueConfigFormModelDropPolicyDto;
@ApiProperty({ type: LeagueConfigFormModelTimingsDto })
@ValidateNested()
@Type(() => LeagueConfigFormModelTimingsDto)
timings: LeagueConfigFormModelTimingsDto;
@ApiProperty({ type: LeagueConfigFormModelStewardingDto })
@ValidateNested()
@Type(() => LeagueConfigFormModelStewardingDto)
stewarding: LeagueConfigFormModelStewardingDto;
}
export class GetLeagueAdminConfigQuery {
@ApiProperty()
@IsString()
leagueId: string;
}
export class GetLeagueAdminConfigOutput {
@ApiProperty({ type: () => LeagueConfigFormModelDto, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDto)
form: LeagueConfigFormModelDto | null;
}
export class LeagueAdminConfigViewModel {
@ApiProperty({ type: () => LeagueConfigFormModelDto, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDto)
form: LeagueConfigFormModelDto | null;
}
export class GetLeagueProtestsQuery {
@ApiProperty()
@IsString()
leagueId: string;
}
export class LeagueAdminProtestsViewModel {
@ApiProperty({ type: [ProtestDto] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => ProtestDto)
protests: ProtestDto[];
@ApiProperty({ type: () => RaceDto })
@ValidateNested()
@Type(() => RaceDto)
racesById: { [raceId: string]: RaceDto };
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driversById: { [driverId: string]: DriverDto };
}
export class GetLeagueSeasonsQuery {
@ApiProperty()
@IsString()
leagueId: string;
}
export class LeagueSeasonSummaryViewModel {
@ApiProperty()
@IsString()
seasonId: string;
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
status: string;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
startDate?: Date;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
endDate?: Date;
@ApiProperty()
@IsBoolean()
isPrimary: boolean;
@ApiProperty()
@IsBoolean()
isParallelActive: boolean;
}
export class LeagueAdminViewModel {
@ApiProperty({ type: [LeagueJoinRequestViewModel] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueJoinRequestViewModel)
joinRequests: LeagueJoinRequestViewModel[];
@ApiProperty({ type: () => LeagueOwnerSummaryViewModel, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueOwnerSummaryViewModel)
ownerSummary: LeagueOwnerSummaryViewModel | null;
@ApiProperty({ type: () => LeagueAdminConfigViewModel })
@ValidateNested()
@Type(() => LeagueAdminConfigViewModel)
config: LeagueAdminConfigViewModel;
@ApiProperty({ type: () => LeagueAdminProtestsViewModel })
@ValidateNested()
@Type(() => LeagueAdminProtestsViewModel)
protests: LeagueAdminProtestsViewModel;
@ApiProperty({ type: [LeagueSeasonSummaryViewModel] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueSeasonSummaryViewModel)
seasons: LeagueSeasonSummaryViewModel[];
}
export class LeagueMemberDto {
@ApiProperty()
@IsString()
driverId: string;
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@ApiProperty({ enum: ['owner', 'manager', 'member'] })
@IsEnum(['owner', 'manager', 'member'])
role: 'owner' | 'manager' | 'member';
@ApiProperty()
@IsDate()
@Type(() => Date)
joinedAt: Date;
}
export class LeagueMembershipsViewModel {
@ApiProperty({ type: [LeagueMemberDto] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueMemberDto)
members: LeagueMemberDto[];
}
export class LeagueStandingDto {
@ApiProperty()
@IsString()
driverId: string;
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@ApiProperty()
@IsNumber()
points: number;
@ApiProperty()
@IsNumber()
rank: number;
}
export class LeagueStandingsViewModel {
@ApiProperty({ type: [LeagueStandingDto] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueStandingDto)
standings: LeagueStandingDto[];
}
export class LeagueScheduleViewModel {
@ApiProperty({ type: [RaceDto] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => RaceDto)
races: RaceDto[];
}
export class LeagueStatsViewModel {
@ApiProperty()
@IsNumber()
totalMembers: number;
@ApiProperty()
@IsNumber()
totalRaces: number;
@ApiProperty()
@IsNumber()
averageRating: number;
}
export class CreateLeagueInput {
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
description: string;
@ApiProperty({ enum: ['public', 'private'] })
@IsEnum(['public', 'private'])
visibility: 'public' | 'private';
@ApiProperty()
@IsString()
ownerId: string;
}
export class CreateLeagueOutput {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsBoolean()
success: boolean;
}

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueSummaryDTO } from './LeagueSummaryDTO';
export class AllLeaguesWithCapacityAndScoringDTO {
@ApiProperty({ type: [LeagueSummaryDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueSummaryDTO)
leagues: LeagueSummaryDTO[];
@ApiProperty()
@IsNumber()
totalCount: number;
}

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueWithCapacityDTO } from './LeagueWithCapacityDTO';
export class AllLeaguesWithCapacityDTO {
@ApiProperty({ type: [LeagueWithCapacityDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueWithCapacityDTO)
leagues: LeagueWithCapacityDTO[];
@ApiProperty()
@IsNumber()
totalCount: number;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class ApproveJoinRequestInputDTO {
@ApiProperty()
@IsString()
requestId: string;
@ApiProperty()
@IsString()
leagueId: string;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsBoolean } from 'class-validator';
export class ApproveJoinRequestOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
@ApiProperty({ required: false })
@IsString()
message?: string;
}

View File

@@ -0,0 +1,20 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsEnum } from 'class-validator';
export class CreateLeagueInputDTO {
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
description: string;
@ApiProperty({ enum: ['public', 'private'] })
@IsEnum(['public', 'private'])
visibility: 'public' | 'private';
@ApiProperty()
@IsString()
ownerId: string;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsBoolean } from 'class-validator';
export class CreateLeagueOutputDTO {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsBoolean()
success: boolean;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueConfigFormModelDTO } from './LeagueConfigFormModelDTO';
export class GetLeagueAdminConfigOutputDTO {
@ApiProperty({ type: () => LeagueConfigFormModelDTO, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDTO)
form: LeagueConfigFormModelDTO | null;
}

View File

@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class GetLeagueAdminConfigQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class GetLeagueAdminPermissionsInputDTO {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
performerDriverId: string;
}

View File

@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class GetLeagueJoinRequestsQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class GetLeagueOwnerSummaryQueryDTO {
@ApiProperty()
@IsString()
ownerId: string;
@ApiProperty()
@IsString()
leagueId: string;
}

View File

@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class GetLeagueProtestsQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
}

View File

@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class GetLeagueSeasonsQueryDTO {
@ApiProperty()
@IsString()
leagueId: string;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueConfigFormModelDTO } from './LeagueConfigFormModelDTO';
export class LeagueAdminConfigDTO {
@ApiProperty({ type: () => LeagueConfigFormModelDTO, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDTO)
form: LeagueConfigFormModelDTO | null;
}

View File

@@ -0,0 +1,38 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueJoinRequestDTO } from './LeagueJoinRequestDTO';
import { LeagueOwnerSummaryDTO } from './LeagueOwnerSummaryDTO';
import { LeagueAdminConfigDTO } from './LeagueAdminConfigDTO';
import { LeagueAdminProtestsDTO } from './LeagueAdminProtestsDTO';
import { LeagueSeasonSummaryDTO } from './LeagueSeasonSummaryDTO';
export class LeagueAdminDTO {
@ApiProperty({ type: [LeagueJoinRequestDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueJoinRequestDTO)
joinRequests: LeagueJoinRequestDTO[];
@ApiProperty({ type: () => LeagueOwnerSummaryDTO, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueOwnerSummaryDTO)
ownerSummary: LeagueOwnerSummaryDTO | null;
@ApiProperty({ type: () => LeagueAdminConfigDTO })
@ValidateNested()
@Type(() => LeagueAdminConfigDTO)
config: LeagueAdminConfigDTO;
@ApiProperty({ type: () => LeagueAdminProtestsDTO })
@ValidateNested()
@Type(() => LeagueAdminProtestsDTO)
protests: LeagueAdminProtestsDTO;
@ApiProperty({ type: [LeagueSeasonSummaryDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueSeasonSummaryDTO)
seasons: LeagueSeasonSummaryDTO[];
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsBoolean } from 'class-validator';
export class LeagueAdminPermissionsDTO {
@ApiProperty()
@IsBoolean()
canRemoveMember: boolean;
@ApiProperty()
@IsBoolean()
canUpdateRoles: boolean;
}

View File

@@ -0,0 +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 { ProtestDTO } from './ProtestDTO';
export class LeagueAdminProtestsDTO {
@ApiProperty({ type: [ProtestDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => ProtestDTO)
protests: ProtestDTO[];
@ApiProperty({ type: () => RaceDto })
@ValidateNested()
@Type(() => RaceDto)
racesById: { [raceId: string]: RaceDto };
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driversById: { [driverId: string]: DriverDto };
}

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsEnum } from 'class-validator';
export class LeagueConfigFormModelBasicsDTO {
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
description: string;
@ApiProperty({ enum: ['public', 'private'] })
@IsEnum(['public', 'private'])
visibility: 'public' | 'private';
}

View File

@@ -0,0 +1,49 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueConfigFormModelBasicsDTO } from './LeagueConfigFormModelBasicsDTO';
import { LeagueConfigFormModelStructureDTO } from './LeagueConfigFormModelStructureDTO';
import { LeagueConfigFormModelScoringDTO } from './LeagueConfigFormModelScoringDTO';
import { LeagueConfigFormModelDropPolicyDTO } from './LeagueConfigFormModelDropPolicyDTO';
import { LeagueConfigFormModelStewardingDTO } from './LeagueConfigFormModelStewardingDTO';
import { LeagueConfigFormModelTimingsDTO } from './LeagueConfigFormModelTimingsDTO';
export class LeagueConfigFormModelDTO {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty({ type: LeagueConfigFormModelBasicsDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelBasicsDTO)
basics: LeagueConfigFormModelBasicsDTO;
@ApiProperty({ type: LeagueConfigFormModelStructureDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelStructureDTO)
structure: LeagueConfigFormModelStructureDTO;
@ApiProperty({ type: [Object] })
@IsArray()
championships: any[];
@ApiProperty({ type: LeagueConfigFormModelScoringDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelScoringDTO)
scoring: LeagueConfigFormModelScoringDTO;
@ApiProperty({ type: LeagueConfigFormModelDropPolicyDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelDropPolicyDTO)
dropPolicy: LeagueConfigFormModelDropPolicyDTO;
@ApiProperty({ type: LeagueConfigFormModelTimingsDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelTimingsDTO)
timings: LeagueConfigFormModelTimingsDTO;
@ApiProperty({ type: LeagueConfigFormModelStewardingDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelStewardingDTO)
stewarding: LeagueConfigFormModelStewardingDTO;
}

View File

@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsOptional, IsEnum } from 'class-validator';
export class LeagueConfigFormModelDropPolicyDTO {
@ApiProperty({ enum: ['none', 'worst_n'] })
@IsEnum(['none', 'worst_n'])
strategy: 'none' | 'worst_n';
@ApiProperty({ required: false })
@IsOptional()
@IsNumber()
n?: number;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber } from 'class-validator';
export class LeagueConfigFormModelScoringDTO {
@ApiProperty()
@IsString()
type: string;
@ApiProperty()
@IsNumber()
points: number;
}

View File

@@ -0,0 +1,41 @@
import { ApiProperty } from '@nestjs/swagger';
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';
@ApiProperty({ required: false })
@IsOptional()
@IsNumber()
requiredVotes?: number;
@ApiProperty()
@IsBoolean()
requireDefense: boolean;
@ApiProperty()
@IsNumber()
defenseTimeLimit: number;
@ApiProperty()
@IsNumber()
voteTimeLimit: number;
@ApiProperty()
@IsNumber()
protestDeadlineHours: number;
@ApiProperty()
@IsNumber()
stewardingClosesHours: number;
@ApiProperty()
@IsBoolean()
notifyAccusedOnProtest: boolean;
@ApiProperty()
@IsBoolean()
notifyOnVoteRequired: boolean;
}

View File

@@ -0,0 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsEnum } from 'class-validator';
export class LeagueConfigFormModelStructureDTO {
@ApiProperty()
@IsString()
@IsEnum(['solo', 'team'])
mode: 'solo' | 'team';
}

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber } from 'class-validator';
export class LeagueConfigFormModelTimingsDTO {
@ApiProperty()
@IsString()
raceDayOfWeek: string;
@ApiProperty()
@IsNumber()
raceTimeHour: number;
@ApiProperty()
@IsNumber()
raceTimeMinute: number;
}

View File

@@ -0,0 +1,34 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsDate, IsOptional, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { DriverDto } from '../../driver/dto/DriverDto';
export class LeagueJoinRequestDTO {
@ApiProperty()
@IsString()
id: string;
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
driverId: string;
@ApiProperty()
@IsDate()
@Type(() => Date)
requestedAt: Date;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
message?: string;
@ApiProperty({ type: () => DriverDto, required: false })
@IsOptional()
@ValidateNested()
@Type(() => DriverDto)
driver?: DriverDto;
}

View File

@@ -0,0 +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';
export class LeagueMemberDTO {
@ApiProperty()
@IsString()
driverId: string;
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@ApiProperty({ enum: ['owner', 'manager', 'member'] })
@IsEnum(['owner', 'manager', 'member'])
role: 'owner' | 'manager' | 'member';
@ApiProperty()
@IsDate()
@Type(() => Date)
joinedAt: Date;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueMemberDTO } from './LeagueMemberDTO';
export class LeagueMembershipsDTO {
@ApiProperty({ type: [LeagueMemberDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueMemberDTO)
members: LeagueMemberDTO[];
}

View File

@@ -0,0 +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';
export class LeagueOwnerSummaryDTO {
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
rating: number | null;
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
rank: number | null;
}

View File

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

View File

@@ -0,0 +1,37 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsBoolean, IsDate, IsOptional } from 'class-validator';
import { Type } from 'class-transformer';
export class LeagueSeasonSummaryDTO {
@ApiProperty()
@IsString()
seasonId: string;
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
status: string;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
startDate?: Date;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
endDate?: Date;
@ApiProperty()
@IsBoolean()
isPrimary: boolean;
@ApiProperty()
@IsBoolean()
isParallelActive: boolean;
}

View File

@@ -0,0 +1,10 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsOptional } from 'class-validator';
export class LeagueSettingsDTO {
@ApiProperty({ nullable: true })
@IsOptional()
@IsNumber()
maxDrivers?: number;
// Add other league settings properties as needed
}

View File

@@ -0,0 +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';
export class LeagueStandingDTO {
@ApiProperty()
@IsString()
driverId: string;
@ApiProperty({ type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
@ApiProperty()
@IsNumber()
points: number;
@ApiProperty()
@IsNumber()
rank: number;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueStandingDTO } from './LeagueStandingDTO';
export class LeagueStandingsDTO {
@ApiProperty({ type: [LeagueStandingDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueStandingDTO)
standings: LeagueStandingDTO[];
}

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber } from 'class-validator';
export class LeagueStatsDTO {
@ApiProperty()
@IsNumber()
totalMembers: number;
@ApiProperty()
@IsNumber()
totalRaces: number;
@ApiProperty()
@IsNumber()
averageRating: number;
}

View File

@@ -0,0 +1,58 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber, IsBoolean, IsOptional } from 'class-validator';
export class LeagueSummaryDTO {
@ApiProperty()
@IsString()
id: string;
@ApiProperty()
@IsString()
name: string;
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
description?: string;
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
logoUrl?: string;
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
coverImage?: string;
@ApiProperty()
@IsNumber()
memberCount: number;
@ApiProperty()
@IsNumber()
maxMembers: number;
@ApiProperty()
@IsBoolean()
isPublic: boolean;
@ApiProperty()
@IsString()
ownerId: string;
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
ownerName?: string;
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
scoringType?: string;
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
status?: string;
}

View File

@@ -0,0 +1,45 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNumber, IsOptional, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { LeagueSettingsDTO } from './LeagueSettingsDTO';
export class LeagueWithCapacityDTO {
@ApiProperty()
@IsString()
id: string;
@ApiProperty()
@IsString()
name: string;
// ... other properties of LeagueWithCapacityDTO
@ApiProperty({ nullable: true })
@IsOptional()
@IsString()
description?: string;
@ApiProperty()
@IsString()
ownerId: string;
@ApiProperty({ type: () => LeagueSettingsDTO })
@ValidateNested()
@Type(() => LeagueSettingsDTO)
settings: LeagueSettingsDTO;
@ApiProperty()
@IsString()
createdAt: string;
@ApiProperty()
@IsNumber()
usedSlots: number;
@ApiProperty({ type: () => Object, nullable: true }) // Using Object for generic social links
@IsOptional()
socialLinks?: {
discordUrl?: string;
youtubeUrl?: string;
websiteUrl?: string;
};
}

View File

@@ -0,0 +1,36 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsDate, IsEnum } from 'class-validator';
import { Type } from 'class-transformer';
// TODO: protests are filed at race level but also managed on league level
export class ProtestDTO {
@ApiProperty()
@IsString()
id: string;
@ApiProperty()
@IsString()
raceId: string;
@ApiProperty()
@IsString()
protestingDriverId: string;
@ApiProperty()
@IsString()
accusedDriverId: string;
@ApiProperty()
@IsDate()
@Type(() => Date)
submittedAt: Date;
@ApiProperty()
@IsString()
description: string;
@ApiProperty({ enum: ['pending', 'accepted', 'rejected'] })
@IsEnum(['pending', 'accepted', 'rejected'])
status: 'pending' | 'accepted' | 'rejected';
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class RejectJoinRequestInputDTO {
@ApiProperty()
@IsString()
requestId: string;
@ApiProperty()
@IsString()
leagueId: string;
}

View File

@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsBoolean } from 'class-validator';
export class RejectJoinRequestOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
@ApiProperty({ required: false })
@IsString()
message?: string;
}

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class RemoveLeagueMemberInputDTO {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
performerDriverId: string;
@ApiProperty()
@IsString()
targetDriverId: string;
}

View File

@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsBoolean } from 'class-validator';
export class RemoveLeagueMemberOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
}

View File

@@ -0,0 +1,42 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsBoolean, IsDate, IsOptional, IsEnum } from 'class-validator';
import { Type } from 'class-transformer';
export class SeasonDTO {
@ApiProperty()
@IsString()
seasonId: string;
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
startDate?: Date;
@ApiProperty({ required: false })
@IsOptional()
@IsDate()
@Type(() => Date)
endDate?: Date;
@ApiProperty({ enum: ['planned', 'active', 'completed'] })
@IsEnum(['planned', 'active', 'completed'])
status: 'planned' | 'active' | 'completed';
@ApiProperty()
@IsBoolean()
isPrimary: boolean;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
seasonGroupId?: string;
}

View File

@@ -0,0 +1,20 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsEnum } from 'class-validator';
export class UpdateLeagueMemberRoleInputDTO {
@ApiProperty()
@IsString()
leagueId: string;
@ApiProperty()
@IsString()
performerDriverId: string;
@ApiProperty()
@IsString()
targetDriverId: string;
@ApiProperty({ enum: ['owner', 'manager', 'member'] })
@IsEnum(['owner', 'manager', 'member'])
newRole: 'owner' | 'manager' | 'member';
}

View File

@@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsBoolean } from 'class-validator';
export class UpdateLeagueMemberRoleOutputDTO {
@ApiProperty()
@IsBoolean()
success: boolean;
}

View File

@@ -1,5 +1,5 @@
import { IGetLeagueMembershipsPresenter, GetLeagueMembershipsResultDTO, GetLeagueMembershipsViewModel } from '@core/racing/application/presenters/IGetLeagueMembershipsPresenter';
import { LeagueMembershipsViewModel } from '../dto/LeagueDto';
import { LeagueMembershipsDTO } from '../dtos/LeagueMembershipsDTO';
export class GetLeagueMembershipsPresenter implements IGetLeagueMembershipsPresenter {
private result: GetLeagueMembershipsViewModel | null = null;
@@ -40,7 +40,7 @@ export class GetLeagueMembershipsPresenter implements IGetLeagueMembershipsPrese
}
// API-specific method
get apiViewModel(): LeagueMembershipsViewModel | null {
get apiViewModel(): LeagueMembershipsDTO | null {
if (!this.result?.memberships) return null;
return this.result.memberships as LeagueMembershipsViewModel;
}

View File

@@ -1,7 +1,7 @@
import { LeagueAdminViewModel } from '../dto/LeagueDto';
import { LeagueAdminDTO } from '../dtos/LeagueAdminDTO';
export class LeagueAdminPresenter {
private result: LeagueAdminViewModel | null = null;
private result: LeagueAdminDTO | null = null;
reset() {
this.result = null;
@@ -23,7 +23,7 @@ export class LeagueAdminPresenter {
};
}
getViewModel(): LeagueAdminViewModel {
getViewModel(): LeagueAdminDTO {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}

View File

@@ -1,5 +1,5 @@
import { ILeagueFullConfigPresenter, LeagueFullConfigData, LeagueConfigFormViewModel } from '@core/racing/application/presenters/ILeagueFullConfigPresenter';
import { LeagueConfigFormModelDto } from '../dto/LeagueDto';
import { LeagueConfigFormModelDTO } from '../dtos/LeagueConfigFormModelDTO';
export class LeagueConfigPresenter implements ILeagueFullConfigPresenter {
private result: LeagueConfigFormViewModel | null = null;
@@ -65,7 +65,7 @@ export class LeagueConfigPresenter implements ILeagueFullConfigPresenter {
}
// API-specific method to get the DTO
get viewModel(): LeagueConfigFormModelDto | null {
get viewModel(): LeagueConfigFormModelDTO | null {
if (!this.result) return null;
// Map from LeagueConfigFormViewModel to LeagueConfigFormModelDto

View File

@@ -0,0 +1,150 @@
import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
import type { BonusRule } from '@core/racing/domain/types/BonusRule';
import type {
ILeagueScoringConfigPresenter,
LeagueScoringConfigData,
LeagueScoringConfigViewModel,
LeagueScoringChampionshipViewModel,
} from '@core/racing/application/presenters/ILeagueScoringConfigPresenter';
export class LeagueScoringConfigPresenter implements ILeagueScoringConfigPresenter {
private viewModel: LeagueScoringConfigViewModel | null = null;
reset(): void {
this.viewModel = null;
}
present(data: LeagueScoringConfigData): LeagueScoringConfigViewModel {
const championships: LeagueScoringChampionshipViewModel[] =
data.championships.map((champ) => this.mapChampionship(champ));
const dropPolicySummary =
data.preset?.dropPolicySummary ??
this.deriveDropPolicyDescriptionFromChampionships(data.championships);
this.viewModel = {
leagueId: data.leagueId,
seasonId: data.seasonId,
gameId: data.gameId,
gameName: data.gameName,
scoringPresetId: data.scoringPresetId ?? 'custom',
scoringPresetName: data.preset?.name ?? 'Custom',
dropPolicySummary,
championships,
};
return this.viewModel;
}
getViewModel(): LeagueScoringConfigViewModel | null {
return this.viewModel;
}
private mapChampionship(championship: ChampionshipConfig): LeagueScoringChampionshipViewModel {
const sessionTypes = championship.sessionTypes.map((s) => s.toString());
const pointsPreview = this.buildPointsPreview(championship.pointsTableBySessionType);
const bonusSummary = this.buildBonusSummary(
championship.bonusRulesBySessionType ?? {},
);
const dropPolicyDescription = this.deriveDropPolicyDescription(
championship.dropScorePolicy,
);
return {
id: championship.id,
name: championship.name,
type: championship.type,
sessionTypes,
pointsPreview,
bonusSummary,
dropPolicyDescription,
};
}
private buildPointsPreview(
tables: Record<string, { getPointsForPosition: (position: number) => number }>,
): Array<{ sessionType: string; position: number; points: number }> {
const preview: Array<{
sessionType: string;
position: number;
points: number;
}> = [];
const maxPositions = 10;
for (const [sessionType, table] of Object.entries(tables)) {
for (let pos = 1; pos <= maxPositions; pos++) {
const points = table.getPointsForPosition(pos);
if (points && points !== 0) {
preview.push({
sessionType,
position: pos,
points,
});
}
}
}
return preview;
}
private buildBonusSummary(
bonusRulesBySessionType: Record<string, BonusRule[]>,
): string[] {
const summaries: string[] = [];
for (const [sessionType, rules] of Object.entries(bonusRulesBySessionType)) {
for (const rule of rules) {
if (rule.type === 'fastestLap') {
const base = `Fastest lap in ${sessionType}`;
if (rule.requiresFinishInTopN) {
summaries.push(
`${base} +${rule.points} points if finishing P${rule.requiresFinishInTopN} or better`,
);
} else {
summaries.push(`${base} +${rule.points} points`);
}
} else {
summaries.push(
`${rule.type} bonus in ${sessionType} worth ${rule.points} points`,
);
}
}
}
return summaries;
}
private deriveDropPolicyDescriptionFromChampionships(
championships: ChampionshipConfig[],
): string {
const first = championships[0];
if (!first) {
return 'All results count';
}
return this.deriveDropPolicyDescription(first.dropScorePolicy);
}
private deriveDropPolicyDescription(policy: {
strategy: string;
count?: number;
dropCount?: number;
}): string {
if (!policy || policy.strategy === 'none') {
return 'All results count';
}
if (policy.strategy === 'bestNResults' && typeof policy.count === 'number') {
return `Best ${policy.count} results count towards the championship`;
}
if (
policy.strategy === 'dropWorstN' &&
typeof policy.dropCount === 'number'
) {
return `Worst ${policy.dropCount} results are dropped from the championship total`;
}
return 'Custom drop score rules apply';
}
}

View File

@@ -0,0 +1,23 @@
import type {
ILeagueScoringPresetsPresenter,
LeagueScoringPresetsResultDTO,
LeagueScoringPresetsViewModel,
} from '@core/racing/application/presenters/ILeagueScoringPresetsPresenter';
export class LeagueScoringPresetsPresenter implements ILeagueScoringPresetsPresenter {
private viewModel: LeagueScoringPresetsViewModel | null = null;
reset(): void {
this.viewModel = null;
}
present(dto: LeagueScoringPresetsResultDTO): void {
this.viewModel = {
presets: dto.presets,
};
}
getViewModel(): LeagueScoringPresetsViewModel | null {
return this.viewModel;
}
}

View File

@@ -1,5 +1,5 @@
import { IGetTotalLeaguesPresenter, GetTotalLeaguesResultDTO, GetTotalLeaguesViewModel } from '@core/racing/application/presenters/IGetTotalLeaguesPresenter';
import { LeagueStatsDto } from '../dto/LeagueDto';
import { LeagueStatsDTO } from '../dtos/LeagueStatsDTO';
export class TotalLeaguesPresenter implements IGetTotalLeaguesPresenter {
private result: LeagueStatsDto | null = null;