refactor
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
import { Controller, Post, Body, Res, HttpStatus } from '@nestjs/common';
|
||||
import type { Response } from 'express';
|
||||
import type { RecordPageViewInput, RecordPageViewOutput, RecordEngagementInput, RecordEngagementOutput } from './dto/AnalyticsDto';
|
||||
import type { RecordPageViewInputDTO } from './dtos/RecordPageViewInputDTO';
|
||||
import type { RecordPageViewOutputDTO } from './dtos/RecordPageViewOutputDTO';
|
||||
import type { RecordEngagementInputDTO } from './dtos/RecordEngagementInputDTO';
|
||||
import type { RecordEngagementOutputDTO } from './dtos/RecordEngagementOutputDTO';
|
||||
import { AnalyticsService } from './AnalyticsService';
|
||||
|
||||
type RecordPageViewInput = RecordPageViewInputDTO;
|
||||
type RecordPageViewOutput = RecordPageViewOutputDTO;
|
||||
type RecordEngagementInput = RecordEngagementInputDTO;
|
||||
type RecordEngagementOutput = RecordEngagementOutputDTO;
|
||||
|
||||
@Controller('analytics')
|
||||
export class AnalyticsController {
|
||||
constructor(
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { RecordEngagementInput, RecordEngagementOutput, RecordPageViewInput, RecordPageViewOutput } from './dto/AnalyticsDto';
|
||||
import type { RecordPageViewInputDTO } from './dtos/RecordPageViewInputDTO';
|
||||
import type { RecordPageViewOutputDTO } from './dtos/RecordPageViewOutputDTO';
|
||||
import type { RecordEngagementInputDTO } from './dtos/RecordEngagementInputDTO';
|
||||
import type { RecordEngagementOutputDTO } from './dtos/RecordEngagementOutputDTO';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { RecordPageViewUseCase } from './use-cases/RecordPageViewUseCase';
|
||||
import { RecordEngagementUseCase } from './use-cases/RecordEngagementUseCase';
|
||||
|
||||
type RecordPageViewInput = RecordPageViewInputDTO;
|
||||
type RecordPageViewOutput = RecordPageViewOutputDTO;
|
||||
type RecordEngagementInput = RecordEngagementInputDTO;
|
||||
type RecordEngagementOutput = RecordEngagementOutputDTO;
|
||||
|
||||
const Logger_TOKEN = 'Logger_TOKEN';
|
||||
const RECORD_PAGE_VIEW_USE_CASE_TOKEN = 'RecordPageViewUseCase_TOKEN';
|
||||
const RECORD_ENGAGEMENT_USE_CASE_TOKEN = 'RecordEngagementUseCase_TOKEN';
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsOptional, IsEnum, IsBoolean, IsNumber, IsObject } from 'class-validator';
|
||||
|
||||
// From core/analytics/domain/types/PageView.ts
|
||||
export enum EntityType {
|
||||
LEAGUE = 'league',
|
||||
DRIVER = 'driver',
|
||||
TEAM = 'team',
|
||||
RACE = 'race',
|
||||
SPONSOR = 'sponsor',
|
||||
}
|
||||
|
||||
// From core/analytics/domain/types/PageView.ts
|
||||
export enum VisitorType {
|
||||
ANONYMOUS = 'anonymous',
|
||||
DRIVER = 'driver',
|
||||
SPONSOR = 'sponsor',
|
||||
}
|
||||
|
||||
export class RecordPageViewInput {
|
||||
@ApiProperty({ enum: EntityType })
|
||||
@IsEnum(EntityType)
|
||||
entityType!: EntityType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
entityId!: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
visitorId?: string;
|
||||
|
||||
@ApiProperty({ enum: VisitorType })
|
||||
@IsEnum(VisitorType)
|
||||
visitorType!: VisitorType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
sessionId!: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
referrer?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
userAgent?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
country?: string;
|
||||
}
|
||||
|
||||
export class RecordPageViewOutput {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
pageViewId!: string;
|
||||
}
|
||||
|
||||
// From core/analytics/domain/types/EngagementEvent.ts
|
||||
export enum EngagementAction {
|
||||
CLICK_SPONSOR_LOGO = 'click_sponsor_logo',
|
||||
CLICK_SPONSOR_URL = 'click_sponsor_url',
|
||||
DOWNLOAD_LIVERY_PACK = 'download_livery_pack',
|
||||
JOIN_LEAGUE = 'join_league',
|
||||
REGISTER_RACE = 'register_race',
|
||||
VIEW_STANDINGS = 'view_standings',
|
||||
VIEW_SCHEDULE = 'view_schedule',
|
||||
SHARE_SOCIAL = 'share_social',
|
||||
CONTACT_SPONSOR = 'contact_sponsor',
|
||||
}
|
||||
|
||||
// From core/analytics/domain/types/EngagementEvent.ts
|
||||
export enum EngagementEntityType {
|
||||
LEAGUE = 'league',
|
||||
DRIVER = 'driver',
|
||||
TEAM = 'team',
|
||||
RACE = 'race',
|
||||
SPONSOR = 'sponsor',
|
||||
SPONSORSHIP = 'sponsorship',
|
||||
}
|
||||
|
||||
export class RecordEngagementInput {
|
||||
@ApiProperty({ enum: EngagementAction })
|
||||
@IsEnum(EngagementAction)
|
||||
action!: EngagementAction;
|
||||
|
||||
@ApiProperty({ enum: EngagementEntityType })
|
||||
@IsEnum(EngagementEntityType)
|
||||
entityType!: EngagementEntityType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
entityId!: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
actorId?: string;
|
||||
|
||||
@ApiProperty({ enum: ['anonymous', 'driver', 'sponsor'] })
|
||||
@IsEnum(['anonymous', 'driver', 'sponsor'])
|
||||
actorType!: 'anonymous' | 'driver' | 'sponsor';
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
sessionId!: string;
|
||||
|
||||
@ApiProperty({ required: false, type: Object })
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
metadata?: Record<string, string | number | boolean>;
|
||||
}
|
||||
|
||||
export class RecordEngagementOutput {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
eventId!: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
engagementWeight!: number;
|
||||
}
|
||||
12
apps/api/src/domain/analytics/dtos/EngagementAction.ts
Normal file
12
apps/api/src/domain/analytics/dtos/EngagementAction.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// From core/analytics/domain/types/EngagementEvent.ts
|
||||
export enum EngagementAction {
|
||||
CLICK_SPONSOR_LOGO = 'click_sponsor_logo',
|
||||
CLICK_SPONSOR_URL = 'click_sponsor_url',
|
||||
DOWNLOAD_LIVERY_PACK = 'download_livery_pack',
|
||||
JOIN_LEAGUE = 'join_league',
|
||||
REGISTER_RACE = 'register_race',
|
||||
VIEW_STANDINGS = 'view_standings',
|
||||
VIEW_SCHEDULE = 'view_schedule',
|
||||
SHARE_SOCIAL = 'share_social',
|
||||
CONTACT_SPONSOR = 'contact_sponsor',
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// From core/analytics/domain/types/EngagementEvent.ts
|
||||
export enum EngagementEntityType {
|
||||
LEAGUE = 'league',
|
||||
DRIVER = 'driver',
|
||||
TEAM = 'team',
|
||||
RACE = 'race',
|
||||
SPONSOR = 'sponsor',
|
||||
SPONSORSHIP = 'sponsorship',
|
||||
}
|
||||
8
apps/api/src/domain/analytics/dtos/EntityType.ts
Normal file
8
apps/api/src/domain/analytics/dtos/EntityType.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// From core/analytics/domain/types/PageView.ts
|
||||
export enum EntityType {
|
||||
LEAGUE = 'league',
|
||||
DRIVER = 'driver',
|
||||
TEAM = 'team',
|
||||
RACE = 'race',
|
||||
SPONSOR = 'sponsor',
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsOptional, IsEnum, IsObject } from 'class-validator';
|
||||
import { EngagementAction } from './EngagementAction';
|
||||
import { EngagementEntityType } from './EngagementEntityType';
|
||||
|
||||
export class RecordEngagementInputDTO {
|
||||
@ApiProperty({ enum: EngagementAction })
|
||||
@IsEnum(EngagementAction)
|
||||
action!: EngagementAction;
|
||||
|
||||
@ApiProperty({ enum: EngagementEntityType })
|
||||
@IsEnum(EngagementEntityType)
|
||||
entityType!: EngagementEntityType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
entityId!: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
actorId?: string;
|
||||
|
||||
@ApiProperty({ enum: ['anonymous', 'driver', 'sponsor'] })
|
||||
@IsEnum(['anonymous', 'driver', 'sponsor'])
|
||||
actorType!: 'anonymous' | 'driver' | 'sponsor';
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
sessionId!: string;
|
||||
|
||||
@ApiProperty({ required: false, type: Object })
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
metadata?: Record<string, string | number | boolean>;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsNumber } from 'class-validator';
|
||||
|
||||
export class RecordEngagementOutputDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
eventId!: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
engagementWeight!: number;
|
||||
}
|
||||
42
apps/api/src/domain/analytics/dtos/RecordPageViewInputDTO.ts
Normal file
42
apps/api/src/domain/analytics/dtos/RecordPageViewInputDTO.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsOptional, IsEnum } from 'class-validator';
|
||||
import { EntityType } from './EntityType';
|
||||
import { VisitorType } from './VisitorType';
|
||||
|
||||
export class RecordPageViewInputDTO {
|
||||
@ApiProperty({ enum: EntityType })
|
||||
@IsEnum(EntityType)
|
||||
entityType!: EntityType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
entityId!: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
visitorId?: string;
|
||||
|
||||
@ApiProperty({ enum: VisitorType })
|
||||
@IsEnum(VisitorType)
|
||||
visitorType!: VisitorType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
sessionId!: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
referrer?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
userAgent?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
country?: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class RecordPageViewOutputDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
pageViewId!: string;
|
||||
}
|
||||
6
apps/api/src/domain/analytics/dtos/VisitorType.ts
Normal file
6
apps/api/src/domain/analytics/dtos/VisitorType.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// From core/analytics/domain/types/PageView.ts
|
||||
export enum VisitorType {
|
||||
ANONYMOUS = 'anonymous',
|
||||
DRIVER = 'driver',
|
||||
SPONSOR = 'sponsor',
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { RecordEngagementInput, RecordEngagementOutput } from '../dto/AnalyticsDto';
|
||||
import type { RecordEngagementInputDTO } from '../dtos/RecordEngagementInputDTO';
|
||||
import type { RecordEngagementOutputDTO } from '../dtos/RecordEngagementOutputDTO';
|
||||
import type { IEngagementRepository } from '@core/analytics/domain/repositories/IEngagementRepository';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { EngagementEvent } from '@core/analytics/domain/entities/EngagementEvent';
|
||||
|
||||
type RecordEngagementInput = RecordEngagementInputDTO;
|
||||
type RecordEngagementOutput = RecordEngagementOutputDTO;
|
||||
|
||||
const Logger_TOKEN = 'Logger_TOKEN';
|
||||
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { RecordPageViewInput, RecordPageViewOutput } from '../dto/AnalyticsDto';
|
||||
import type { RecordPageViewInputDTO } from '../dtos/RecordPageViewInputDTO';
|
||||
import type { RecordPageViewOutputDTO } from '../dtos/RecordPageViewOutputDTO';
|
||||
import type { IPageViewRepository } from '@core/analytics/application/repositories/IPageViewRepository';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { PageView } from '@core/analytics/domain/entities/PageView';
|
||||
|
||||
type RecordPageViewInput = RecordPageViewInputDTO;
|
||||
type RecordPageViewOutput = RecordPageViewOutputDTO;
|
||||
|
||||
const Logger_TOKEN = 'Logger_TOKEN';
|
||||
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@ import { Controller, Get, Post, Body, Req, Param } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
|
||||
import { DriverService } from './DriverService';
|
||||
import { DriversLeaderboardViewModel, DriverStatsDto, CompleteOnboardingInput, CompleteOnboardingOutput, GetDriverRegistrationStatusQuery, DriverRegistrationStatusViewModel } from './dto/DriverDto';
|
||||
import { DriversLeaderboardDTO } from './dtos/DriversLeaderboardDTO';
|
||||
import { DriverStatsDTO } from './dtos/DriverStatsDTO';
|
||||
import { CompleteOnboardingInputDTO } from './dtos/CompleteOnboardingInputDTO';
|
||||
import { CompleteOnboardingOutputDTO } from './dtos/CompleteOnboardingOutputDTO';
|
||||
import { GetDriverRegistrationStatusQueryDTO } from './dtos/GetDriverRegistrationStatusQueryDTO';
|
||||
import { DriverRegistrationStatusDTO } from './dtos/DriverRegistrationStatusDTO';
|
||||
import { DriverDTO } from './dtos/DriverDTO';
|
||||
|
||||
@ApiTags('drivers')
|
||||
@Controller('drivers')
|
||||
@@ -11,15 +17,15 @@ export class DriverController {
|
||||
|
||||
@Get('leaderboard')
|
||||
@ApiOperation({ summary: 'Get drivers leaderboard' })
|
||||
@ApiResponse({ status: 200, description: 'List of drivers for the leaderboard', type: DriversLeaderboardViewModel })
|
||||
async getDriversLeaderboard(): Promise<DriversLeaderboardViewModel> {
|
||||
@ApiResponse({ status: 200, description: 'List of drivers for the leaderboard', type: DriversLeaderboardDTO })
|
||||
async getDriversLeaderboard(): Promise<DriversLeaderboardDTO> {
|
||||
return this.driverService.getDriversLeaderboard();
|
||||
}
|
||||
|
||||
@Get('total-drivers')
|
||||
@ApiOperation({ summary: 'Get the total number of drivers' })
|
||||
@ApiResponse({ status: 200, description: 'Total number of drivers', type: DriverStatsDto })
|
||||
async getTotalDrivers(): Promise<DriverStatsDto> {
|
||||
@ApiResponse({ status: 200, description: 'Total number of drivers', type: DriverStatsDTO })
|
||||
async getTotalDrivers(): Promise<DriverStatsDTO> {
|
||||
return this.driverService.getTotalDrivers();
|
||||
}
|
||||
|
||||
@@ -38,11 +44,11 @@ export class DriverController {
|
||||
|
||||
@Post('complete-onboarding')
|
||||
@ApiOperation({ summary: 'Complete driver onboarding for a user' })
|
||||
@ApiResponse({ status: 200, description: 'Onboarding complete', type: CompleteOnboardingOutput })
|
||||
@ApiResponse({ status: 200, description: 'Onboarding complete', type: CompleteOnboardingOutputDTO })
|
||||
async completeOnboarding(
|
||||
@Body() input: CompleteOnboardingInput,
|
||||
@Body() input: CompleteOnboardingInputDTO,
|
||||
@Req() req: Request,
|
||||
): Promise<CompleteOnboardingOutput> {
|
||||
): Promise<CompleteOnboardingOutputDTO> {
|
||||
// Assuming userId is available from the request (e.g., via auth middleware)
|
||||
const userId = req['user'].userId; // Placeholder for actual user extraction
|
||||
return this.driverService.completeOnboarding(userId, input);
|
||||
@@ -50,13 +56,23 @@ export class DriverController {
|
||||
|
||||
@Get(':driverId/races/:raceId/registration-status')
|
||||
@ApiOperation({ summary: 'Get driver registration status for a specific race' })
|
||||
@ApiResponse({ status: 200, description: 'Driver registration status', type: DriverRegistrationStatusViewModel })
|
||||
@ApiResponse({ status: 200, description: 'Driver registration status', type: DriverRegistrationStatusDTO })
|
||||
async getDriverRegistrationStatus(
|
||||
@Param('driverId') driverId: string,
|
||||
@Param('raceId') raceId: string,
|
||||
): Promise<DriverRegistrationStatusViewModel> {
|
||||
): Promise<DriverRegistrationStatusDTO> {
|
||||
return this.driverService.getDriverRegistrationStatus({ driverId, raceId });
|
||||
}
|
||||
|
||||
@Put(':driverId/profile')
|
||||
@ApiOperation({ summary: 'Update driver profile' })
|
||||
@ApiResponse({ status: 200, description: 'Driver profile updated', type: DriverDTO })
|
||||
async updateDriverProfile(
|
||||
@Param('driverId') driverId: string,
|
||||
@Body() body: { bio?: string; country?: string },
|
||||
): Promise<DriverDTO | null> {
|
||||
return this.driverService.updateDriverProfile(driverId, body.bio, body.country);
|
||||
}
|
||||
|
||||
// Add other Driver endpoints here based on other presenters
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ import type { Logger } from "@gridpilot/core/shared/application";
|
||||
import { GetDriversLeaderboardUseCase } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase';
|
||||
import { GetTotalDriversUseCase } from '@core/racing/application/use-cases/GetTotalDriversUseCase';
|
||||
import { CompleteDriverOnboardingUseCase } from '@core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
|
||||
import { GetProfileOverviewUseCase } from '@core/racing/application/use-cases/GetProfileOverviewUseCase';
|
||||
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
|
||||
import { UpdateDriverProfileUseCase } from '@core/racing/application/use-cases/UpdateDriverProfileUseCase';
|
||||
|
||||
// Import concrete in-memory implementations
|
||||
import { InMemoryDriverRepository } from '../../..//racing/persistence/inmemory/InMemoryDriverRepository';
|
||||
@@ -41,6 +44,9 @@ export const GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN = 'GetDriversLeaderboardUseC
|
||||
export const GET_TOTAL_DRIVERS_USE_CASE_TOKEN = 'GetTotalDriversUseCase';
|
||||
export const COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN = 'CompleteDriverOnboardingUseCase';
|
||||
export const IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN = 'IsDriverRegisteredForRaceUseCase';
|
||||
export const GET_PROFILE_OVERVIEW_USE_CASE_TOKEN = 'GetProfileOverviewUseCase';
|
||||
export const GET_DRIVER_TEAM_USE_CASE_TOKEN = 'GetDriverTeamUseCase';
|
||||
export const UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN = 'UpdateDriverProfileUseCase';
|
||||
|
||||
export const DriverProviders: Provider[] = [
|
||||
DriverService, // Provide the service itself
|
||||
@@ -105,4 +111,9 @@ export const DriverProviders: Provider[] = [
|
||||
useFactory: (registrationRepo: IRaceRegistrationRepository) => new IsDriverRegisteredForRaceUseCase(registrationRepo),
|
||||
inject: [RACE_REGISTRATION_REPOSITORY_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN,
|
||||
useFactory: (driverRepo: IDriverRepository) => new UpdateDriverProfileUseCase(driverRepo),
|
||||
inject: [DRIVER_REPOSITORY_TOKEN],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { DriversLeaderboardViewModel, DriverStatsDto, CompleteOnboardingInput, CompleteOnboardingOutput, GetDriverRegistrationStatusQuery, DriverRegistrationStatusViewModel } from './dto/DriverDto';
|
||||
import { DriversLeaderboardDTO } from './dtos/DriversLeaderboardDTO';
|
||||
import { DriverStatsDTO } from './dtos/DriverStatsDTO';
|
||||
import { CompleteOnboardingInputDTO } from './dtos/CompleteOnboardingInputDTO';
|
||||
import { CompleteOnboardingOutputDTO } from './dtos/CompleteOnboardingOutputDTO';
|
||||
import { GetDriverRegistrationStatusQueryDTO } from './dtos/GetDriverRegistrationStatusQueryDTO';
|
||||
import { DriverRegistrationStatusDTO } from './dtos/DriverRegistrationStatusDTO';
|
||||
|
||||
// Use cases
|
||||
import { GetDriversLeaderboardUseCase } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase';
|
||||
@@ -14,8 +19,10 @@ import { CompleteOnboardingPresenter } from './presenters/CompleteOnboardingPres
|
||||
import { DriverRegistrationStatusPresenter } from './presenters/DriverRegistrationStatusPresenter';
|
||||
|
||||
// Tokens
|
||||
import { GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN, GET_TOTAL_DRIVERS_USE_CASE_TOKEN, COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN, IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN, LOGGER_TOKEN } from './DriverProviders';
|
||||
import { GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN, GET_TOTAL_DRIVERS_USE_CASE_TOKEN, COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN, IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN, UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN, LOGGER_TOKEN, DRIVER_REPOSITORY_TOKEN } from './DriverProviders';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
||||
import { UpdateDriverProfileUseCase } from '@core/racing/application/use-cases/UpdateDriverProfileUseCase';
|
||||
|
||||
@Injectable()
|
||||
export class DriverService {
|
||||
@@ -24,10 +31,12 @@ export class DriverService {
|
||||
@Inject(GET_TOTAL_DRIVERS_USE_CASE_TOKEN) private readonly getTotalDriversUseCase: GetTotalDriversUseCase,
|
||||
@Inject(COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN) private readonly completeDriverOnboardingUseCase: CompleteDriverOnboardingUseCase,
|
||||
@Inject(IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN) private readonly isDriverRegisteredForRaceUseCase: IsDriverRegisteredForRaceUseCase,
|
||||
@Inject(UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN) private readonly updateDriverProfileUseCase: UpdateDriverProfileUseCase,
|
||||
@Inject(DRIVER_REPOSITORY_TOKEN) private readonly driverRepository: IDriverRepository,
|
||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async getDriversLeaderboard(): Promise<DriversLeaderboardViewModel> {
|
||||
async getDriversLeaderboard(): Promise<DriversLeaderboardDTO> {
|
||||
this.logger.debug('[DriverService] Fetching drivers leaderboard.');
|
||||
|
||||
const presenter = new DriversLeaderboardPresenter();
|
||||
@@ -35,7 +44,7 @@ export class DriverService {
|
||||
return presenter.viewModel;
|
||||
}
|
||||
|
||||
async getTotalDrivers(): Promise<DriverStatsDto> {
|
||||
async getTotalDrivers(): Promise<DriverStatsDTO> {
|
||||
this.logger.debug('[DriverService] Fetching total drivers count.');
|
||||
|
||||
const presenter = new DriverStatsPresenter();
|
||||
@@ -43,7 +52,7 @@ export class DriverService {
|
||||
return presenter.viewModel;
|
||||
}
|
||||
|
||||
async completeOnboarding(userId: string, input: CompleteOnboardingInput): Promise<CompleteOnboardingOutput> {
|
||||
async completeOnboarding(userId: string, input: CompleteOnboardingInputDTO): Promise<CompleteOnboardingOutputDTO> {
|
||||
this.logger.debug('Completing onboarding for user:', userId);
|
||||
|
||||
const presenter = new CompleteOnboardingPresenter();
|
||||
@@ -59,11 +68,37 @@ export class DriverService {
|
||||
return presenter.viewModel;
|
||||
}
|
||||
|
||||
async getDriverRegistrationStatus(query: GetDriverRegistrationStatusQuery): Promise<DriverRegistrationStatusViewModel> {
|
||||
async getDriverRegistrationStatus(query: GetDriverRegistrationStatusQueryDTO): Promise<DriverRegistrationStatusDTO> {
|
||||
this.logger.debug('Checking driver registration status:', query);
|
||||
|
||||
const presenter = new DriverRegistrationStatusPresenter();
|
||||
await this.isDriverRegisteredForRaceUseCase.execute({ raceId: query.raceId, driverId: query.driverId }, presenter);
|
||||
return presenter.viewModel;
|
||||
}
|
||||
|
||||
async getCurrentDriver(userId: string): Promise<DriverDTO | null> {
|
||||
this.logger.debug(`[DriverService] Fetching current driver for userId: ${userId}`);
|
||||
|
||||
const driver = await this.driverRepository.findById(userId);
|
||||
if (!driver) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: driver.id,
|
||||
name: driver.name.value,
|
||||
};
|
||||
}
|
||||
|
||||
async updateDriverProfile(driverId: string, bio?: string, country?: string): Promise<DriverDTO | null> {
|
||||
this.logger.debug(`[DriverService] Updating driver profile for driverId: ${driverId}`);
|
||||
|
||||
const result = await this.updateDriverProfileUseCase.execute({ driverId, bio, country });
|
||||
if (result.isErr()) {
|
||||
this.logger.error(`Failed to update driver profile: ${result.error.code}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return result.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsNotEmpty, IsOptional, IsBoolean } from 'class-validator';
|
||||
|
||||
export class DriverLeaderboardItemViewModel {
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
rating: number;
|
||||
|
||||
@ApiProperty()
|
||||
skillLevel: string; // Assuming skillLevel is a string like 'Rookie', 'Pro', etc.
|
||||
|
||||
@ApiProperty()
|
||||
nationality: string;
|
||||
|
||||
@ApiProperty()
|
||||
racesCompleted: number;
|
||||
|
||||
@ApiProperty()
|
||||
wins: number;
|
||||
|
||||
@ApiProperty()
|
||||
podiums: number;
|
||||
|
||||
@ApiProperty()
|
||||
isActive: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
rank: number;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
avatarUrl?: string;
|
||||
}
|
||||
|
||||
export class DriversLeaderboardViewModel {
|
||||
@ApiProperty({ type: [DriverLeaderboardItemViewModel] })
|
||||
drivers: DriverLeaderboardItemViewModel[];
|
||||
|
||||
@ApiProperty()
|
||||
totalRaces: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalWins: number;
|
||||
|
||||
@ApiProperty()
|
||||
activeCount: number;
|
||||
}
|
||||
|
||||
export class DriverStatsDto {
|
||||
@ApiProperty()
|
||||
totalDrivers: number;
|
||||
}
|
||||
|
||||
export class CompleteOnboardingInput {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
displayName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
country: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
timezone?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
bio?: string;
|
||||
}
|
||||
|
||||
export class CompleteOnboardingOutput {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
driverId?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export class GetDriverRegistrationStatusQuery {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
raceId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
driverId: string;
|
||||
}
|
||||
|
||||
export class DriverRegistrationStatusViewModel {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
isRegistered: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
raceId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
driverId: string;
|
||||
}
|
||||
|
||||
export class DriverDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
name: string; // Display name or full name
|
||||
}
|
||||
|
||||
// Add other DTOs for driver-related logic as needed
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
|
||||
|
||||
export class CompleteOnboardingInputDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
displayName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
country: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
timezone?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
bio?: string;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsString } from 'class-validator';
|
||||
|
||||
export class CompleteOnboardingOutputDTO {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
driverId?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
errorMessage?: string;
|
||||
}
|
||||
36
apps/api/src/domain/driver/dtos/DriverLeaderboardItemDTO.ts
Normal file
36
apps/api/src/domain/driver/dtos/DriverLeaderboardItemDTO.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class DriverLeaderboardItemDTO {
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
rating: number;
|
||||
|
||||
@ApiProperty()
|
||||
skillLevel: string; // Assuming skillLevel is a string like 'Rookie', 'Pro', etc.
|
||||
|
||||
@ApiProperty()
|
||||
nationality: string;
|
||||
|
||||
@ApiProperty()
|
||||
racesCompleted: number;
|
||||
|
||||
@ApiProperty()
|
||||
wins: number;
|
||||
|
||||
@ApiProperty()
|
||||
podiums: number;
|
||||
|
||||
@ApiProperty()
|
||||
isActive: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
rank: number;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
avatarUrl?: string;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsString } from 'class-validator';
|
||||
|
||||
export class DriverRegistrationStatusDTO {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
isRegistered!: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
raceId!: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
driverId!: string;
|
||||
}
|
||||
6
apps/api/src/domain/driver/dtos/DriverStatsDTO.ts
Normal file
6
apps/api/src/domain/driver/dtos/DriverStatsDTO.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class DriverStatsDTO {
|
||||
@ApiProperty()
|
||||
totalDrivers: number;
|
||||
}
|
||||
16
apps/api/src/domain/driver/dtos/DriversLeaderboardDTO.ts
Normal file
16
apps/api/src/domain/driver/dtos/DriversLeaderboardDTO.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { DriverLeaderboardItemDTO } from './DriverLeaderboardItemDTO';
|
||||
|
||||
export class DriversLeaderboardDTO {
|
||||
@ApiProperty({ type: [DriverLeaderboardItemDTO] })
|
||||
drivers: DriverLeaderboardItemDTO[];
|
||||
|
||||
@ApiProperty()
|
||||
totalRaces: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalWins: number;
|
||||
|
||||
@ApiProperty()
|
||||
activeCount: number;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class GetDriverRegistrationStatusQueryDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
raceId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
driverId: string;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CompleteOnboardingOutput } from '../dto/DriverDto';
|
||||
import { CompleteOnboardingOutputDTO } from '../dtos/CompleteOnboardingOutputDTO';
|
||||
import type { ICompleteDriverOnboardingPresenter, CompleteDriverOnboardingResultDTO } from '../../../../../core/racing/application/presenters/ICompleteDriverOnboardingPresenter';
|
||||
|
||||
export class CompleteOnboardingPresenter implements ICompleteDriverOnboardingPresenter {
|
||||
private result: CompleteOnboardingOutput | null = null;
|
||||
private result: CompleteOnboardingOutputDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
@@ -16,7 +16,7 @@ export class CompleteOnboardingPresenter implements ICompleteDriverOnboardingPre
|
||||
};
|
||||
}
|
||||
|
||||
get viewModel(): CompleteOnboardingOutput {
|
||||
get viewModel(): CompleteOnboardingOutputDTO {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DriverRegistrationStatusViewModel } from '../dto/DriverDto';
|
||||
import { DriverRegistrationStatusDTO } from '../dtos/DriverRegistrationStatusDTO';
|
||||
import type { IDriverRegistrationStatusPresenter } from '../../../../../core/racing/application/presenters/IDriverRegistrationStatusPresenter';
|
||||
|
||||
export class DriverRegistrationStatusPresenter implements IDriverRegistrationStatusPresenter {
|
||||
private result: DriverRegistrationStatusViewModel | null = null;
|
||||
private result: DriverRegistrationStatusDTO | null = null;
|
||||
|
||||
present(isRegistered: boolean, raceId: string, driverId: string) {
|
||||
this.result = {
|
||||
@@ -12,7 +12,7 @@ export class DriverRegistrationStatusPresenter implements IDriverRegistrationSta
|
||||
};
|
||||
}
|
||||
|
||||
getViewModel(): DriverRegistrationStatusViewModel {
|
||||
getViewModel(): DriverRegistrationStatusDTO {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
return this.result;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export class DriverRegistrationStatusPresenter implements IDriverRegistrationSta
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
get viewModel(): DriverRegistrationStatusViewModel {
|
||||
get viewModel(): DriverRegistrationStatusDTO {
|
||||
return this.getViewModel();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DriverStatsDto } from '../dto/DriverDto';
|
||||
import { DriverStatsDTO } from '../dtos/DriverStatsDTO';
|
||||
import type { ITotalDriversPresenter, TotalDriversResultDTO } from '../../../../../core/racing/application/presenters/ITotalDriversPresenter';
|
||||
|
||||
export class DriverStatsPresenter implements ITotalDriversPresenter {
|
||||
private result: DriverStatsDto | null = null;
|
||||
private result: DriverStatsDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
@@ -14,7 +14,7 @@ export class DriverStatsPresenter implements ITotalDriversPresenter {
|
||||
};
|
||||
}
|
||||
|
||||
get viewModel(): DriverStatsDto {
|
||||
get viewModel(): DriverStatsDTO {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { DriversLeaderboardViewModel, DriverLeaderboardItemViewModel } from '../dto/DriverDto';
|
||||
import { DriversLeaderboardDTO, DriverLeaderboardItemDTO } from '../dtos/DriversLeaderboardDTO';
|
||||
import { DriverLeaderboardItemDTO } from '../dtos/DriverLeaderboardItemDTO';
|
||||
import type { IDriversLeaderboardPresenter, DriversLeaderboardResultDTO } from '../../../../../core/racing/application/presenters/IDriversLeaderboardPresenter';
|
||||
|
||||
export class DriversLeaderboardPresenter implements IDriversLeaderboardPresenter {
|
||||
private result: DriversLeaderboardViewModel | null = null;
|
||||
private result: DriversLeaderboardDTO | null = null;
|
||||
|
||||
reset() {
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
present(dto: DriversLeaderboardResultDTO) {
|
||||
const drivers: DriverLeaderboardItemViewModel[] = dto.drivers.map(driver => {
|
||||
const drivers: DriverLeaderboardItemDTO[] = dto.drivers.map(driver => {
|
||||
const ranking = dto.rankings.find(r => r.driverId === driver.id);
|
||||
const stats = dto.stats[driver.id];
|
||||
const avatarUrl = dto.avatarUrls[driver.id];
|
||||
@@ -42,7 +43,7 @@ export class DriversLeaderboardPresenter implements IDriversLeaderboardPresenter
|
||||
};
|
||||
}
|
||||
|
||||
get viewModel(): DriversLeaderboardViewModel {
|
||||
get viewModel(): DriversLeaderboardDTO {
|
||||
if (!this.result) throw new Error('Presenter not presented');
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
16
apps/api/src/domain/league/dtos/AllLeaguesWithCapacityDTO.ts
Normal file
16
apps/api/src/domain/league/dtos/AllLeaguesWithCapacityDTO.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
20
apps/api/src/domain/league/dtos/CreateLeagueInputDTO.ts
Normal file
20
apps/api/src/domain/league/dtos/CreateLeagueInputDTO.ts
Normal 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;
|
||||
}
|
||||
12
apps/api/src/domain/league/dtos/CreateLeagueOutputDTO.ts
Normal file
12
apps/api/src/domain/league/dtos/CreateLeagueOutputDTO.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class GetLeagueAdminConfigQueryDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
leagueId: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class GetLeagueJoinRequestsQueryDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
leagueId: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class GetLeagueProtestsQueryDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
leagueId: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class GetLeagueSeasonsQueryDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
leagueId: string;
|
||||
}
|
||||
12
apps/api/src/domain/league/dtos/LeagueAdminConfigDTO.ts
Normal file
12
apps/api/src/domain/league/dtos/LeagueAdminConfigDTO.ts
Normal 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;
|
||||
}
|
||||
38
apps/api/src/domain/league/dtos/LeagueAdminDTO.ts
Normal file
38
apps/api/src/domain/league/dtos/LeagueAdminDTO.ts
Normal 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[];
|
||||
}
|
||||
12
apps/api/src/domain/league/dtos/LeagueAdminPermissionsDTO.ts
Normal file
12
apps/api/src/domain/league/dtos/LeagueAdminPermissionsDTO.ts
Normal 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;
|
||||
}
|
||||
24
apps/api/src/domain/league/dtos/LeagueAdminProtestsDTO.ts
Normal file
24
apps/api/src/domain/league/dtos/LeagueAdminProtestsDTO.ts
Normal 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 };
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
49
apps/api/src/domain/league/dtos/LeagueConfigFormModelDTO.ts
Normal file
49
apps/api/src/domain/league/dtos/LeagueConfigFormModelDTO.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
34
apps/api/src/domain/league/dtos/LeagueJoinRequestDTO.ts
Normal file
34
apps/api/src/domain/league/dtos/LeagueJoinRequestDTO.ts
Normal 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;
|
||||
}
|
||||
24
apps/api/src/domain/league/dtos/LeagueMemberDTO.ts
Normal file
24
apps/api/src/domain/league/dtos/LeagueMemberDTO.ts
Normal 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;
|
||||
}
|
||||
12
apps/api/src/domain/league/dtos/LeagueMembershipsDTO.ts
Normal file
12
apps/api/src/domain/league/dtos/LeagueMembershipsDTO.ts
Normal 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[];
|
||||
}
|
||||
21
apps/api/src/domain/league/dtos/LeagueOwnerSummaryDTO.ts
Normal file
21
apps/api/src/domain/league/dtos/LeagueOwnerSummaryDTO.ts
Normal 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;
|
||||
}
|
||||
12
apps/api/src/domain/league/dtos/LeagueScheduleDTO.ts
Normal file
12
apps/api/src/domain/league/dtos/LeagueScheduleDTO.ts
Normal 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[];
|
||||
}
|
||||
37
apps/api/src/domain/league/dtos/LeagueSeasonSummaryDTO.ts
Normal file
37
apps/api/src/domain/league/dtos/LeagueSeasonSummaryDTO.ts
Normal 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;
|
||||
}
|
||||
10
apps/api/src/domain/league/dtos/LeagueSettingsDTO.ts
Normal file
10
apps/api/src/domain/league/dtos/LeagueSettingsDTO.ts
Normal 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
|
||||
}
|
||||
23
apps/api/src/domain/league/dtos/LeagueStandingDTO.ts
Normal file
23
apps/api/src/domain/league/dtos/LeagueStandingDTO.ts
Normal 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;
|
||||
}
|
||||
12
apps/api/src/domain/league/dtos/LeagueStandingsDTO.ts
Normal file
12
apps/api/src/domain/league/dtos/LeagueStandingsDTO.ts
Normal 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[];
|
||||
}
|
||||
16
apps/api/src/domain/league/dtos/LeagueStatsDTO.ts
Normal file
16
apps/api/src/domain/league/dtos/LeagueStatsDTO.ts
Normal 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;
|
||||
}
|
||||
58
apps/api/src/domain/league/dtos/LeagueSummaryDTO.ts
Normal file
58
apps/api/src/domain/league/dtos/LeagueSummaryDTO.ts
Normal 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;
|
||||
}
|
||||
45
apps/api/src/domain/league/dtos/LeagueWithCapacityDTO.ts
Normal file
45
apps/api/src/domain/league/dtos/LeagueWithCapacityDTO.ts
Normal 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;
|
||||
};
|
||||
}
|
||||
36
apps/api/src/domain/league/dtos/ProtestDTO.ts
Normal file
36
apps/api/src/domain/league/dtos/ProtestDTO.ts
Normal 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';
|
||||
}
|
||||
12
apps/api/src/domain/league/dtos/RejectJoinRequestInputDTO.ts
Normal file
12
apps/api/src/domain/league/dtos/RejectJoinRequestInputDTO.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean } from 'class-validator';
|
||||
|
||||
export class RemoveLeagueMemberOutputDTO {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
success: boolean;
|
||||
}
|
||||
42
apps/api/src/domain/league/dtos/SeasonDTO.ts
Normal file
42
apps/api/src/domain/league/dtos/SeasonDTO.ts
Normal 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;
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean } from 'class-validator';
|
||||
|
||||
export class UpdateLeagueMemberRoleOutputDTO {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
success: boolean;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -2,7 +2,11 @@ import { Controller, Post, Body, HttpStatus, Res } from '@nestjs/common';
|
||||
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
|
||||
import { Response } from 'express';
|
||||
import { MediaService } from './MediaService';
|
||||
import { RequestAvatarGenerationInput, RequestAvatarGenerationOutput } from './dto/MediaDto'; // Assuming these DTOs are defined
|
||||
import type { RequestAvatarGenerationInputDTO } from './dtos/RequestAvatarGenerationInputDTO';
|
||||
import type { RequestAvatarGenerationOutputDTO } from './dtos/RequestAvatarGenerationOutputDTO';
|
||||
|
||||
type RequestAvatarGenerationInput = RequestAvatarGenerationInputDTO;
|
||||
type RequestAvatarGenerationOutput = RequestAvatarGenerationOutputDTO;
|
||||
|
||||
@ApiTags('media')
|
||||
@Controller('media')
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Injectable, Inject } from '@nestjs/common';
|
||||
import { RequestAvatarGenerationInput, RequestAvatarGenerationOutput } from './dto/MediaDto';
|
||||
import type { RequestAvatarGenerationInputDTO } from './dtos/RequestAvatarGenerationInputDTO';
|
||||
import type { RequestAvatarGenerationOutputDTO } from './dtos/RequestAvatarGenerationOutputDTO';
|
||||
|
||||
type RequestAvatarGenerationInput = RequestAvatarGenerationInputDTO;
|
||||
type RequestAvatarGenerationOutput = RequestAvatarGenerationOutputDTO;
|
||||
|
||||
// Use cases
|
||||
import { RequestAvatarGenerationUseCase } from '@core/media/application/use-cases/RequestAvatarGenerationUseCase';
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsNotEmpty, IsBoolean } from 'class-validator';
|
||||
|
||||
export class RequestAvatarGenerationInput {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
userId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
facePhotoData: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
suitColor: string;
|
||||
}
|
||||
|
||||
export class RequestAvatarGenerationOutput {
|
||||
@ApiProperty({ type: Boolean })
|
||||
@IsBoolean()
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
requestId?: string;
|
||||
|
||||
@ApiProperty({ type: [String], required: false })
|
||||
avatarUrls?: string[];
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
// Assuming FacePhotoData and SuitColor are simple string types for DTO purposes
|
||||
export type FacePhotoData = string;
|
||||
export type SuitColor = string;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsNotEmpty } from 'class-validator';
|
||||
|
||||
export class RequestAvatarGenerationInputDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
userId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
facePhotoData: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
suitColor: string;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsString } from 'class-validator';
|
||||
|
||||
export class RequestAvatarGenerationOutputDTO {
|
||||
@ApiProperty({ type: Boolean })
|
||||
@IsBoolean()
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
requestId?: string;
|
||||
|
||||
@ApiProperty({ type: [String], required: false })
|
||||
avatarUrls?: string[];
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
errorMessage?: string;
|
||||
}
|
||||
2
apps/api/src/domain/media/types/FacePhotoData.ts
Normal file
2
apps/api/src/domain/media/types/FacePhotoData.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// Assuming FacePhotoData is a simple string type for DTO purposes
|
||||
export type FacePhotoData = string;
|
||||
2
apps/api/src/domain/media/types/SuitColor.ts
Normal file
2
apps/api/src/domain/media/types/SuitColor.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// Assuming SuitColor is a simple string type for DTO purposes
|
||||
export type SuitColor = string;
|
||||
31
apps/api/src/domain/payments/dtos/CreatePaymentInputDTO.ts
Normal file
31
apps/api/src/domain/payments/dtos/CreatePaymentInputDTO.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEnum, IsNumber, IsString, IsOptional } from 'class-validator';
|
||||
import { PaymentType } from './PaymentType';
|
||||
import { PayerType } from './PayerType';
|
||||
|
||||
export class CreatePaymentInputDTO {
|
||||
@ApiProperty({ enum: PaymentType })
|
||||
@IsEnum(PaymentType)
|
||||
type: PaymentType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
amount: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
payerId: string;
|
||||
|
||||
@ApiProperty({ enum: PayerType })
|
||||
@IsEnum(PayerType)
|
||||
payerType: PayerType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
leagueId: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
seasonId?: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaymentDTO } from './PaymentDTO';
|
||||
|
||||
export class CreatePaymentOutputDTO {
|
||||
@ApiProperty({ type: PaymentDTO })
|
||||
payment: PaymentDTO;
|
||||
}
|
||||
5
apps/api/src/domain/payments/dtos/MemberPaymentStatus.ts
Normal file
5
apps/api/src/domain/payments/dtos/MemberPaymentStatus.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum MemberPaymentStatus {
|
||||
PENDING = 'pending',
|
||||
PAID = 'paid',
|
||||
OVERDUE = 'overdue',
|
||||
}
|
||||
5
apps/api/src/domain/payments/dtos/MembershipFeeType.ts
Normal file
5
apps/api/src/domain/payments/dtos/MembershipFeeType.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum MembershipFeeType {
|
||||
SEASON = 'season',
|
||||
MONTHLY = 'monthly',
|
||||
PER_RACE = 'per_race',
|
||||
}
|
||||
4
apps/api/src/domain/payments/dtos/PayerType.ts
Normal file
4
apps/api/src/domain/payments/dtos/PayerType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum PayerType {
|
||||
SPONSOR = 'sponsor',
|
||||
DRIVER = 'driver',
|
||||
}
|
||||
57
apps/api/src/domain/payments/dtos/PaymentDTO.ts
Normal file
57
apps/api/src/domain/payments/dtos/PaymentDTO.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsNumber, IsEnum, IsOptional, IsDate } from 'class-validator';
|
||||
import { PaymentType } from './PaymentType';
|
||||
import { PayerType } from './PayerType';
|
||||
import { PaymentStatus } from './PaymentStatus';
|
||||
|
||||
export class PaymentDTO {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty({ enum: PaymentType })
|
||||
@IsEnum(PaymentType)
|
||||
type: PaymentType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
amount: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
platformFee: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsNumber()
|
||||
netAmount: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
payerId: string;
|
||||
|
||||
@ApiProperty({ enum: PayerType })
|
||||
@IsEnum(PayerType)
|
||||
payerType: PayerType;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
leagueId: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
seasonId?: string;
|
||||
|
||||
@ApiProperty({ enum: PaymentStatus })
|
||||
@IsEnum(PaymentStatus)
|
||||
status: PaymentStatus;
|
||||
|
||||
@ApiProperty()
|
||||
@IsDate()
|
||||
createdAt: Date;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
completedAt?: Date;
|
||||
}
|
||||
6
apps/api/src/domain/payments/dtos/PaymentStatus.ts
Normal file
6
apps/api/src/domain/payments/dtos/PaymentStatus.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum PaymentStatus {
|
||||
PENDING = 'pending',
|
||||
COMPLETED = 'completed',
|
||||
FAILED = 'failed',
|
||||
REFUNDED = 'refunded',
|
||||
}
|
||||
4
apps/api/src/domain/payments/dtos/PaymentType.ts
Normal file
4
apps/api/src/domain/payments/dtos/PaymentType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum PaymentType {
|
||||
SPONSORSHIP = 'sponsorship',
|
||||
MEMBERSHIP_FEE = 'membership_fee',
|
||||
}
|
||||
5
apps/api/src/domain/payments/dtos/PrizeType.ts
Normal file
5
apps/api/src/domain/payments/dtos/PrizeType.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum PrizeType {
|
||||
CASH = 'cash',
|
||||
MERCHANDISE = 'merchandise',
|
||||
OTHER = 'other',
|
||||
}
|
||||
5
apps/api/src/domain/payments/dtos/ReferenceType.ts
Normal file
5
apps/api/src/domain/payments/dtos/ReferenceType.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum ReferenceType {
|
||||
SPONSORSHIP = 'sponsorship',
|
||||
MEMBERSHIP_FEE = 'membership_fee',
|
||||
PRIZE = 'prize',
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user