refactor dtos to ports
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { IApproveLeagueJoinRequestPresenter, ApproveLeagueJoinRequestResultDTO, ApproveLeagueJoinRequestViewModel } from '@core/racing/application/presenters/IApproveLeagueJoinRequestPresenter';
|
import { IApproveLeagueJoinRequestPresenter, ApproveLeagueJoinRequestResultPort, ApproveLeagueJoinRequestViewModel } from '@core/racing/application/presenters/IApproveLeagueJoinRequestPresenter';
|
||||||
|
|
||||||
export class ApproveLeagueJoinRequestPresenter implements IApproveLeagueJoinRequestPresenter {
|
export class ApproveLeagueJoinRequestPresenter implements IApproveLeagueJoinRequestPresenter {
|
||||||
private result: ApproveLeagueJoinRequestViewModel | null = null;
|
private result: ApproveLeagueJoinRequestViewModel | null = null;
|
||||||
@@ -7,7 +7,7 @@ export class ApproveLeagueJoinRequestPresenter implements IApproveLeagueJoinRequ
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(dto: ApproveLeagueJoinRequestResultDTO) {
|
present(dto: ApproveLeagueJoinRequestResultPort) {
|
||||||
this.result = dto;
|
this.result = dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { GetSponsorOutputDTO } from './dtos/GetSponsorOutputDTO';
|
|||||||
import { GetPendingSponsorshipRequestsOutputDTO } from './dtos/GetPendingSponsorshipRequestsOutputDTO';
|
import { GetPendingSponsorshipRequestsOutputDTO } from './dtos/GetPendingSponsorshipRequestsOutputDTO';
|
||||||
import { AcceptSponsorshipRequestInputDTO } from './dtos/AcceptSponsorshipRequestInputDTO';
|
import { AcceptSponsorshipRequestInputDTO } from './dtos/AcceptSponsorshipRequestInputDTO';
|
||||||
import { RejectSponsorshipRequestInputDTO } from './dtos/RejectSponsorshipRequestInputDTO';
|
import { RejectSponsorshipRequestInputDTO } from './dtos/RejectSponsorshipRequestInputDTO';
|
||||||
import type { AcceptSponsorshipRequestResultDTO } from '@core/racing/application/dtos/AcceptSponsorshipRequestResultDTO';
|
import type { AcceptSponsorshipRequestResultPort } from '@core/racing/application/ports/output/AcceptSponsorshipRequestResultPort';
|
||||||
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
|
|
||||||
@ApiTags('sponsors')
|
@ApiTags('sponsors')
|
||||||
@@ -80,7 +80,7 @@ export class SponsorController {
|
|||||||
@ApiResponse({ status: 200, description: 'Sponsorship request accepted' })
|
@ApiResponse({ status: 200, description: 'Sponsorship request accepted' })
|
||||||
@ApiResponse({ status: 400, description: 'Invalid request' })
|
@ApiResponse({ status: 400, description: 'Invalid request' })
|
||||||
@ApiResponse({ status: 404, description: 'Request not found' })
|
@ApiResponse({ status: 404, description: 'Request not found' })
|
||||||
async acceptSponsorshipRequest(@Param('requestId') requestId: string, @Body() input: AcceptSponsorshipRequestInputDTO): Promise<AcceptSponsorshipRequestResultDTO | null> {
|
async acceptSponsorshipRequest(@Param('requestId') requestId: string, @Body() input: AcceptSponsorshipRequestInputDTO): Promise<AcceptSponsorshipRequestResultPort | null> {
|
||||||
return this.sponsorService.acceptSponsorshipRequest(requestId, input.respondedBy);
|
return this.sponsorService.acceptSponsorshipRequest(requestId, input.respondedBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { GetPendingSponsorshipRequestsUseCase, GetPendingSponsorshipRequestsDTO
|
|||||||
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
||||||
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
import type { SponsorableEntityType } from '@core/racing/domain/entities/SponsorshipRequest';
|
import type { SponsorableEntityType } from '@core/racing/domain/entities/SponsorshipRequest';
|
||||||
import type { AcceptSponsorshipRequestResultDTO } from '@core/racing/application/dtos/AcceptSponsorshipRequestResultDTO';
|
import type { AcceptSponsorshipRequestResultPort } from '@core/racing/application/ports/output/AcceptSponsorshipRequestResultPort';
|
||||||
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
|
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ export class SponsorService {
|
|||||||
return result.value as GetPendingSponsorshipRequestsOutputDTO;
|
return result.value as GetPendingSponsorshipRequestsOutputDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
async acceptSponsorshipRequest(requestId: string, respondedBy: string): Promise<AcceptSponsorshipRequestResultDTO | null> {
|
async acceptSponsorshipRequest(requestId: string, respondedBy: string): Promise<AcceptSponsorshipRequestResultPort | null> {
|
||||||
this.logger.debug('[SponsorService] Accepting sponsorship request.', { requestId, respondedBy });
|
this.logger.debug('[SponsorService] Accepting sponsorship request.', { requestId, respondedBy });
|
||||||
|
|
||||||
const result = await this.acceptSponsorshipRequestUseCase.execute({ requestId, respondedBy });
|
const result = await this.acceptSponsorshipRequestUseCase.execute({ requestId, respondedBy });
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CreateSponsorViewModel, CreateSponsorResultDTO, ICreateSponsorPresenter } from '@core/racing/application/presenters/ICreateSponsorPresenter';
|
import { CreateSponsorViewModel, CreateSponsorOutputPort, ICreateSponsorPresenter } from '@core/racing/application/presenters/ICreateSponsorPresenter';
|
||||||
|
|
||||||
export class CreateSponsorPresenter implements ICreateSponsorPresenter {
|
export class CreateSponsorPresenter implements ICreateSponsorPresenter {
|
||||||
private result: CreateSponsorViewModel | null = null;
|
private result: CreateSponsorViewModel | null = null;
|
||||||
@@ -7,7 +7,7 @@ export class CreateSponsorPresenter implements ICreateSponsorPresenter {
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(dto: CreateSponsorResultDTO) {
|
present(dto: CreateSponsorOutputPort) {
|
||||||
this.result = dto;
|
this.result = dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest';
|
|
||||||
import type { SponsorshipTier } from '../../domain/entities/SeasonSponsorship';
|
|
||||||
import type { Currency } from '../../domain/value-objects/Money';
|
|
||||||
|
|
||||||
export interface ApplyForSponsorshipDTO {
|
|
||||||
sponsorId: string;
|
|
||||||
entityType: SponsorableEntityType;
|
|
||||||
entityId: string;
|
|
||||||
tier: SponsorshipTier;
|
|
||||||
offeredAmount: number; // in cents
|
|
||||||
currency?: Currency;
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import type { PenaltyType } from '../../domain/entities/Penalty';
|
|
||||||
|
|
||||||
export interface ApplyPenaltyCommand {
|
|
||||||
raceId: string;
|
|
||||||
driverId: string;
|
|
||||||
stewardId: string;
|
|
||||||
type: PenaltyType;
|
|
||||||
value?: number;
|
|
||||||
reason: string;
|
|
||||||
protestId?: string;
|
|
||||||
notes?: string;
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export interface ApproveLeagueJoinRequestResultDTO {
|
|
||||||
success: boolean;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import type { Team } from '../../domain/entities/Team';
|
|
||||||
|
|
||||||
export interface CreateTeamResultDTO {
|
|
||||||
team: Team;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import type { Team } from '../../domain/entities/Team';
|
|
||||||
|
|
||||||
export type GetAllTeamsQueryResultDTO = Team[];
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import type { Team } from '../../domain/entities/Team';
|
|
||||||
import type { TeamMembership } from '../../domain/types/TeamMembership';
|
|
||||||
|
|
||||||
export interface GetDriverTeamQueryResultDTO {
|
|
||||||
team: Team;
|
|
||||||
membership: TeamMembership;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest';
|
|
||||||
|
|
||||||
export interface GetEntitySponsorshipPricingDTO {
|
|
||||||
entityType: SponsorableEntityType;
|
|
||||||
entityId: string;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest';
|
|
||||||
import type { SponsorshipSlotDTO } from './SponsorshipSlotDTO';
|
|
||||||
|
|
||||||
export interface GetEntitySponsorshipPricingResultDTO {
|
|
||||||
entityType: SponsorableEntityType;
|
|
||||||
entityId: string;
|
|
||||||
acceptingApplications: boolean;
|
|
||||||
customRequirements?: string;
|
|
||||||
mainSlot?: SponsorshipSlotDTO;
|
|
||||||
secondarySlot?: SponsorshipSlotDTO;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import type { LeagueMembership } from '../../domain/entities/LeagueMembership';
|
|
||||||
|
|
||||||
export interface GetLeagueMembershipsResultDTO {
|
|
||||||
memberships: LeagueMembership[];
|
|
||||||
drivers: { id: string; name: string }[];
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
export interface ProtestDTO {
|
|
||||||
id: string;
|
|
||||||
raceId: string;
|
|
||||||
protestingDriverId: string;
|
|
||||||
accusedDriverId: string;
|
|
||||||
submittedAt: Date;
|
|
||||||
description: string;
|
|
||||||
status: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RaceDTO {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
date: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DriverDTO {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetLeagueProtestsResultDTO {
|
|
||||||
protests: ProtestDTO[];
|
|
||||||
races: RaceDTO[];
|
|
||||||
drivers: DriverDTO[];
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import type { Team } from '../../domain/entities/Team';
|
|
||||||
import type { TeamMembership } from '../../domain/types/TeamMembership';
|
|
||||||
|
|
||||||
export interface GetTeamDetailsQueryResultDTO {
|
|
||||||
team: Team;
|
|
||||||
membership: TeamMembership | null;
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
import type { StewardingDecisionMode } from '../../domain/entities/League';
|
|
||||||
import type { LeagueVisibilityType } from '../../domain/value-objects/LeagueVisibility';
|
|
||||||
|
|
||||||
export type LeagueStructureMode = 'solo' | 'fixedTeams';
|
|
||||||
|
|
||||||
// TODO this is way too much for a DTO. it must be pure InputPort or OutputPort
|
|
||||||
|
|
||||||
/**
|
|
||||||
* League visibility determines public visibility and ranking status.
|
|
||||||
* - 'ranked': Public, competitive, affects driver ratings. Requires min 10 drivers.
|
|
||||||
* - 'unranked': Private, casual with friends. No rating impact. Any number of drivers.
|
|
||||||
*
|
|
||||||
* For backward compatibility, 'public'/'private' are also supported in the form,
|
|
||||||
* but the domain uses 'ranked'/'unranked'.
|
|
||||||
*/
|
|
||||||
export type LeagueVisibilityFormValue = LeagueVisibilityType | 'public' | 'private';
|
|
||||||
|
|
||||||
export interface LeagueStructureFormDTO {
|
|
||||||
mode: LeagueStructureMode;
|
|
||||||
maxDrivers: number;
|
|
||||||
maxTeams?: number;
|
|
||||||
driversPerTeam?: number;
|
|
||||||
multiClassEnabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LeagueChampionshipsFormDTO {
|
|
||||||
enableDriverChampionship: boolean;
|
|
||||||
enableTeamChampionship: boolean;
|
|
||||||
enableNationsChampionship: boolean;
|
|
||||||
enableTrophyChampionship: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LeagueScoringFormDTO {
|
|
||||||
patternId?: string; // e.g. 'sprint-main-driver', 'club-ladder-solo'
|
|
||||||
// For now, keep customScoring optional and simple:
|
|
||||||
customScoringEnabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LeagueDropPolicyFormDTO {
|
|
||||||
strategy: 'none' | 'bestNResults' | 'dropWorstN';
|
|
||||||
n?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LeagueTimingsFormDTO {
|
|
||||||
practiceMinutes?: number;
|
|
||||||
qualifyingMinutes: number;
|
|
||||||
sprintRaceMinutes?: number;
|
|
||||||
mainRaceMinutes: number;
|
|
||||||
sessionCount: number;
|
|
||||||
roundsPlanned?: number;
|
|
||||||
|
|
||||||
seasonStartDate?: string; // ISO date YYYY-MM-DD
|
|
||||||
seasonEndDate?: string; // ISO date YYYY-MM-DD
|
|
||||||
raceStartTime?: string; // "HH:MM" 24h
|
|
||||||
timezoneId?: string; // IANA ID, e.g. "Europe/Berlin", or "track" for track local time
|
|
||||||
recurrenceStrategy?: 'weekly' | 'everyNWeeks' | 'monthlyNthWeekday';
|
|
||||||
intervalWeeks?: number;
|
|
||||||
weekdays?: import('../../domain/types/Weekday').Weekday[];
|
|
||||||
monthlyOrdinal?: 1 | 2 | 3 | 4;
|
|
||||||
monthlyWeekday?: import('../../domain/types/Weekday').Weekday;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stewarding configuration for protests and penalties.
|
|
||||||
*/
|
|
||||||
export interface LeagueStewardingFormDTO {
|
|
||||||
/**
|
|
||||||
* How protest decisions are made
|
|
||||||
*/
|
|
||||||
decisionMode: StewardingDecisionMode;
|
|
||||||
/**
|
|
||||||
* Number of votes required to uphold/reject a protest
|
|
||||||
* Used with steward_vote, member_vote, steward_veto, member_veto modes
|
|
||||||
*/
|
|
||||||
requiredVotes?: number;
|
|
||||||
/**
|
|
||||||
* Whether to require a defense from the accused before deciding
|
|
||||||
*/
|
|
||||||
requireDefense: boolean;
|
|
||||||
/**
|
|
||||||
* Time limit (hours) for accused to submit defense
|
|
||||||
*/
|
|
||||||
defenseTimeLimit: number;
|
|
||||||
/**
|
|
||||||
* Time limit (hours) for voting to complete
|
|
||||||
*/
|
|
||||||
voteTimeLimit: number;
|
|
||||||
/**
|
|
||||||
* Time limit (hours) after race ends when protests can be filed
|
|
||||||
*/
|
|
||||||
protestDeadlineHours: number;
|
|
||||||
/**
|
|
||||||
* Time limit (hours) after race ends when stewarding is closed
|
|
||||||
*/
|
|
||||||
stewardingClosesHours: number;
|
|
||||||
/**
|
|
||||||
* Whether to notify the accused when a protest is filed
|
|
||||||
*/
|
|
||||||
notifyAccusedOnProtest: boolean;
|
|
||||||
/**
|
|
||||||
* Whether to notify eligible voters when a vote is required
|
|
||||||
*/
|
|
||||||
notifyOnVoteRequired: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LeagueConfigFormModel {
|
|
||||||
leagueId?: string; // present for admin, omitted for create
|
|
||||||
basics: {
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
/**
|
|
||||||
* League visibility/ranking mode.
|
|
||||||
* - 'ranked' (or legacy 'public'): Competitive, public, affects ratings. Min 10 drivers.
|
|
||||||
* - 'unranked' (or legacy 'private'): Casual with friends, no rating impact.
|
|
||||||
*/
|
|
||||||
visibility: LeagueVisibilityFormValue;
|
|
||||||
gameId: string;
|
|
||||||
/**
|
|
||||||
* League logo as base64 data URL (optional).
|
|
||||||
* Format: data:image/png;base64,... or data:image/jpeg;base64,...
|
|
||||||
*/
|
|
||||||
logoDataUrl?: string;
|
|
||||||
};
|
|
||||||
structure: LeagueStructureFormDTO;
|
|
||||||
championships: LeagueChampionshipsFormDTO;
|
|
||||||
scoring: LeagueScoringFormDTO;
|
|
||||||
dropPolicy: LeagueDropPolicyFormDTO;
|
|
||||||
timings: LeagueTimingsFormDTO;
|
|
||||||
stewarding: LeagueStewardingFormDTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to normalize visibility values to new terminology.
|
|
||||||
* Maps 'public' -> 'ranked' and 'private' -> 'unranked'.
|
|
||||||
*/
|
|
||||||
export function normalizeVisibility(value: LeagueVisibilityFormValue): LeagueVisibilityType {
|
|
||||||
if (value === 'public' || value === 'ranked') return 'ranked';
|
|
||||||
return 'unranked';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to convert new terminology to legacy for backward compatibility.
|
|
||||||
* Maps 'ranked' -> 'public' and 'unranked' -> 'private'.
|
|
||||||
*/
|
|
||||||
export function toLegacyVisibility(value: LeagueVisibilityFormValue): 'public' | 'private' {
|
|
||||||
if (value === 'ranked' || value === 'public') return 'public';
|
|
||||||
return 'private';
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
import type { LeagueTimingsFormDTO } from './LeagueConfigFormDTO';
|
|
||||||
import type { Weekday } from '../../domain/types/Weekday';
|
|
||||||
import { RaceTimeOfDay } from '../../domain/value-objects/RaceTimeOfDay';
|
|
||||||
import { LeagueTimezone } from '../../domain/value-objects/LeagueTimezone';
|
|
||||||
import { WeekdaySet } from '../../domain/value-objects/WeekdaySet';
|
|
||||||
import { MonthlyRecurrencePattern } from '../../domain/value-objects/MonthlyRecurrencePattern';
|
|
||||||
import type { RecurrenceStrategy } from '../../domain/value-objects/RecurrenceStrategy';
|
|
||||||
import { RecurrenceStrategyFactory } from '../../domain/value-objects/RecurrenceStrategy';
|
|
||||||
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
|
|
||||||
import { BusinessRuleViolationError } from '../errors/RacingApplicationError';
|
|
||||||
|
|
||||||
export interface LeagueScheduleDTO {
|
|
||||||
seasonStartDate: string;
|
|
||||||
raceStartTime: string;
|
|
||||||
timezoneId: string;
|
|
||||||
recurrenceStrategy: 'weekly' | 'everyNWeeks' | 'monthlyNthWeekday';
|
|
||||||
intervalWeeks?: number | undefined;
|
|
||||||
weekdays?: Weekday[] | undefined;
|
|
||||||
monthlyOrdinal?: 1 | 2 | 3 | 4 | undefined;
|
|
||||||
monthlyWeekday?: Weekday | undefined;
|
|
||||||
plannedRounds: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LeagueSchedulePreviewDTO {
|
|
||||||
rounds: Array<{ roundNumber: number; scheduledAt: string; timezoneId: string }>;
|
|
||||||
summary: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function leagueTimingsToScheduleDTO(
|
|
||||||
timings: LeagueTimingsFormDTO,
|
|
||||||
): LeagueScheduleDTO | null {
|
|
||||||
if (
|
|
||||||
!timings.seasonStartDate ||
|
|
||||||
!timings.raceStartTime ||
|
|
||||||
!timings.timezoneId ||
|
|
||||||
!timings.recurrenceStrategy ||
|
|
||||||
!timings.roundsPlanned
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
seasonStartDate: timings.seasonStartDate,
|
|
||||||
raceStartTime: timings.raceStartTime,
|
|
||||||
timezoneId: timings.timezoneId,
|
|
||||||
recurrenceStrategy: timings.recurrenceStrategy,
|
|
||||||
intervalWeeks: timings.intervalWeeks,
|
|
||||||
weekdays: timings.weekdays,
|
|
||||||
monthlyOrdinal: timings.monthlyOrdinal,
|
|
||||||
monthlyWeekday: timings.monthlyWeekday,
|
|
||||||
plannedRounds: timings.roundsPlanned,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function scheduleDTOToSeasonSchedule(dto: LeagueScheduleDTO): SeasonSchedule {
|
|
||||||
if (!dto.seasonStartDate) {
|
|
||||||
throw new BusinessRuleViolationError('seasonStartDate is required');
|
|
||||||
}
|
|
||||||
if (!dto.raceStartTime) {
|
|
||||||
throw new BusinessRuleViolationError('raceStartTime is required');
|
|
||||||
}
|
|
||||||
if (!dto.timezoneId) {
|
|
||||||
throw new BusinessRuleViolationError('timezoneId is required');
|
|
||||||
}
|
|
||||||
if (!dto.recurrenceStrategy) {
|
|
||||||
throw new BusinessRuleViolationError('recurrenceStrategy is required');
|
|
||||||
}
|
|
||||||
if (!Number.isInteger(dto.plannedRounds) || dto.plannedRounds <= 0) {
|
|
||||||
throw new BusinessRuleViolationError('plannedRounds must be a positive integer');
|
|
||||||
}
|
|
||||||
|
|
||||||
const startDate = new Date(dto.seasonStartDate);
|
|
||||||
if (Number.isNaN(startDate.getTime())) {
|
|
||||||
throw new BusinessRuleViolationError(
|
|
||||||
`seasonStartDate must be a valid date, got "${dto.seasonStartDate}"`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeOfDay = RaceTimeOfDay.fromString(dto.raceStartTime);
|
|
||||||
const timezone = new LeagueTimezone(dto.timezoneId);
|
|
||||||
|
|
||||||
let recurrence: RecurrenceStrategy;
|
|
||||||
|
|
||||||
if (dto.recurrenceStrategy === 'weekly') {
|
|
||||||
if (!dto.weekdays || dto.weekdays.length === 0) {
|
|
||||||
throw new BusinessRuleViolationError('weekdays are required for weekly recurrence');
|
|
||||||
}
|
|
||||||
recurrence = RecurrenceStrategyFactory.weekly(new WeekdaySet(dto.weekdays));
|
|
||||||
} else if (dto.recurrenceStrategy === 'everyNWeeks') {
|
|
||||||
if (!dto.weekdays || dto.weekdays.length === 0) {
|
|
||||||
throw new BusinessRuleViolationError('weekdays are required for everyNWeeks recurrence');
|
|
||||||
}
|
|
||||||
if (dto.intervalWeeks == null) {
|
|
||||||
throw new BusinessRuleViolationError(
|
|
||||||
'intervalWeeks is required for everyNWeeks recurrence',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
recurrence = RecurrenceStrategyFactory.everyNWeeks(
|
|
||||||
dto.intervalWeeks,
|
|
||||||
new WeekdaySet(dto.weekdays),
|
|
||||||
);
|
|
||||||
} else if (dto.recurrenceStrategy === 'monthlyNthWeekday') {
|
|
||||||
if (!dto.monthlyOrdinal || !dto.monthlyWeekday) {
|
|
||||||
throw new BusinessRuleViolationError(
|
|
||||||
'monthlyOrdinal and monthlyWeekday are required for monthlyNthWeekday',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const pattern = new MonthlyRecurrencePattern(dto.monthlyOrdinal, dto.monthlyWeekday);
|
|
||||||
recurrence = RecurrenceStrategyFactory.monthlyNthWeekday(pattern);
|
|
||||||
} else {
|
|
||||||
throw new BusinessRuleViolationError(`Unknown recurrenceStrategy "${dto.recurrenceStrategy}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SeasonSchedule({
|
|
||||||
startDate,
|
|
||||||
timeOfDay,
|
|
||||||
timezone,
|
|
||||||
recurrence,
|
|
||||||
plannedRounds: dto.plannedRounds,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
export interface LeagueSummaryScoringDTO {
|
|
||||||
gameId: string;
|
|
||||||
gameName: string;
|
|
||||||
primaryChampionshipType: 'driver' | 'team' | 'nations' | 'trophy';
|
|
||||||
scoringPresetId: string;
|
|
||||||
scoringPresetName: string;
|
|
||||||
dropPolicySummary: string;
|
|
||||||
/**
|
|
||||||
* Human-readable scoring pattern summary combining preset name and drop policy,
|
|
||||||
* e.g. "Sprint + Main • Best 6 results of 8 count towards the championship."
|
|
||||||
*/
|
|
||||||
scoringPatternSummary: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LeagueSummaryDTO {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
createdAt: Date;
|
|
||||||
ownerId: string;
|
|
||||||
maxDrivers?: number;
|
|
||||||
usedDriverSlots?: number;
|
|
||||||
maxTeams?: number;
|
|
||||||
usedTeamSlots?: number;
|
|
||||||
/**
|
|
||||||
* Human-readable structure summary derived from capacity and (future) team settings,
|
|
||||||
* e.g. "Solo • 24 drivers" or "Teams • 12 × 2 drivers".
|
|
||||||
*/
|
|
||||||
structureSummary?: string;
|
|
||||||
/**
|
|
||||||
* Human-readable scoring pattern summary for list views,
|
|
||||||
* e.g. "Sprint + Main • Best 6 results of 8 count towards the championship."
|
|
||||||
*/
|
|
||||||
scoringPatternSummary?: string;
|
|
||||||
/**
|
|
||||||
* Human-readable timing summary for list views,
|
|
||||||
* e.g. "30 min Quali • 40 min Race".
|
|
||||||
*/
|
|
||||||
timingSummary?: string;
|
|
||||||
scoring?: LeagueSummaryScoringDTO;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
export interface IsDriverRegisteredForRaceQueryParamsDTO {
|
|
||||||
raceId: string;
|
|
||||||
driverId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetRaceRegistrationsQueryParamsDTO {
|
|
||||||
raceId: string;
|
|
||||||
}
|
|
||||||
@@ -66,7 +66,6 @@ export type {
|
|||||||
TeamMembershipStatus,
|
TeamMembershipStatus,
|
||||||
} from '../domain/types/TeamMembership';
|
} from '../domain/types/TeamMembership';
|
||||||
|
|
||||||
export type { DriverDTO } from './dto/DriverDTO';
|
|
||||||
export type { LeagueDTO } from './dto/LeagueDTO';
|
export type { LeagueDTO } from './dto/LeagueDTO';
|
||||||
export type { RaceDTO } from './dto/RaceDTO';
|
export type { RaceDTO } from './dto/RaceDTO';
|
||||||
export type { ResultDTO } from './dto/ResultDTO';
|
export type { ResultDTO } from './dto/ResultDTO';
|
||||||
@@ -76,10 +75,8 @@ export type {
|
|||||||
LeagueScheduleDTO,
|
LeagueScheduleDTO,
|
||||||
LeagueSchedulePreviewDTO,
|
LeagueSchedulePreviewDTO,
|
||||||
} from './dto/LeagueScheduleDTO';
|
} from './dto/LeagueScheduleDTO';
|
||||||
export type {
|
export type { ChampionshipStandingsOutputPort } from './ports/output/ChampionshipStandingsOutputPort';
|
||||||
ChampionshipStandingsDTO,
|
export type { ChampionshipStandingsRowOutputPort } from './ports/output/ChampionshipStandingsRowOutputPort';
|
||||||
ChampionshipStandingsRowDTO,
|
|
||||||
} from './dto/ChampionshipStandingsDTO';
|
|
||||||
export type {
|
export type {
|
||||||
LeagueConfigFormModel,
|
LeagueConfigFormModel,
|
||||||
LeagueStructureFormDTO,
|
LeagueStructureFormDTO,
|
||||||
|
|||||||
@@ -1,207 +0,0 @@
|
|||||||
/**
|
|
||||||
* Application Layer: Entity to DTO Mappers
|
|
||||||
*
|
|
||||||
* Transforms domain entities to plain objects for crossing architectural boundaries.
|
|
||||||
* These mappers handle the Server Component -> Client Component boundary in Next.js 15.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Driver } from '../../domain/entities/Driver';
|
|
||||||
import { League } from '../../domain/entities/League';
|
|
||||||
import { Race } from '../../domain/entities/Race';
|
|
||||||
import { Result } from '../../domain/entities/Result';
|
|
||||||
import { Standing } from '../../domain/entities/Standing';
|
|
||||||
import type { DriverDTO } from '../dto/DriverDTO';
|
|
||||||
import type { LeagueDTO } from '../dto/LeagueDTO';
|
|
||||||
import type { RaceDTO } from '../dto/RaceDTO';
|
|
||||||
import type { ResultDTO } from '../dto/ResultDTO';
|
|
||||||
import type { StandingDTO } from '../dto/StandingDTO';
|
|
||||||
|
|
||||||
export class EntityMappers {
|
|
||||||
static toDriverDTO(driver: Driver | null): DriverDTO | null {
|
|
||||||
if (!driver) return null;
|
|
||||||
return {
|
|
||||||
id: driver.id,
|
|
||||||
iracingId: driver.iracingId,
|
|
||||||
name: driver.name,
|
|
||||||
country: driver.country,
|
|
||||||
bio: driver.bio ?? '',
|
|
||||||
joinedAt: driver.joinedAt.toISOString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static toLeagueDTO(league: League | null): LeagueDTO | null {
|
|
||||||
if (!league) return null;
|
|
||||||
|
|
||||||
const socialLinks =
|
|
||||||
league.socialLinks !== undefined
|
|
||||||
? {
|
|
||||||
...(league.socialLinks.discordUrl !== undefined
|
|
||||||
? { discordUrl: league.socialLinks.discordUrl }
|
|
||||||
: {}),
|
|
||||||
...(league.socialLinks.youtubeUrl !== undefined
|
|
||||||
? { youtubeUrl: league.socialLinks.youtubeUrl }
|
|
||||||
: {}),
|
|
||||||
...(league.socialLinks.websiteUrl !== undefined
|
|
||||||
? { websiteUrl: league.socialLinks.websiteUrl }
|
|
||||||
: {}),
|
|
||||||
}
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: league.id,
|
|
||||||
name: league.name,
|
|
||||||
description: league.description,
|
|
||||||
ownerId: league.ownerId,
|
|
||||||
settings: league.settings,
|
|
||||||
createdAt: league.createdAt.toISOString(),
|
|
||||||
...(socialLinks !== undefined ? { socialLinks } : {}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static toLeagueDTOs(leagues: League[]): LeagueDTO[] {
|
|
||||||
return leagues.map((league) => {
|
|
||||||
const socialLinks =
|
|
||||||
league.socialLinks !== undefined
|
|
||||||
? {
|
|
||||||
...(league.socialLinks.discordUrl !== undefined
|
|
||||||
? { discordUrl: league.socialLinks.discordUrl }
|
|
||||||
: {}),
|
|
||||||
...(league.socialLinks.youtubeUrl !== undefined
|
|
||||||
? { youtubeUrl: league.socialLinks.youtubeUrl }
|
|
||||||
: {}),
|
|
||||||
...(league.socialLinks.websiteUrl !== undefined
|
|
||||||
? { websiteUrl: league.socialLinks.websiteUrl }
|
|
||||||
: {}),
|
|
||||||
}
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: league.id,
|
|
||||||
name: league.name,
|
|
||||||
description: league.description,
|
|
||||||
ownerId: league.ownerId,
|
|
||||||
settings: league.settings,
|
|
||||||
createdAt: league.createdAt.toISOString(),
|
|
||||||
...(socialLinks !== undefined ? { socialLinks } : {}),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static toRaceDTO(race: Race | null): RaceDTO | null {
|
|
||||||
if (!race) return null;
|
|
||||||
|
|
||||||
const sessionTypeMap = {
|
|
||||||
practice: 'practice' as const,
|
|
||||||
qualifying: 'qualifying' as const,
|
|
||||||
q1: 'qualifying' as const,
|
|
||||||
q2: 'qualifying' as const,
|
|
||||||
q3: 'qualifying' as const,
|
|
||||||
sprint: 'race' as const,
|
|
||||||
main: 'race' as const,
|
|
||||||
timeTrial: 'practice' as const,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: race.id,
|
|
||||||
leagueId: race.leagueId,
|
|
||||||
scheduledAt: race.scheduledAt.toISOString(),
|
|
||||||
track: race.track,
|
|
||||||
trackId: race.trackId ?? '',
|
|
||||||
car: race.car,
|
|
||||||
carId: race.carId ?? '',
|
|
||||||
sessionType: sessionTypeMap[race.sessionType.value],
|
|
||||||
status: race.status,
|
|
||||||
...(race.strengthOfField !== undefined
|
|
||||||
? { strengthOfField: race.strengthOfField }
|
|
||||||
: {}),
|
|
||||||
...(race.registeredCount !== undefined
|
|
||||||
? { registeredCount: race.registeredCount }
|
|
||||||
: {}),
|
|
||||||
...(race.maxParticipants !== undefined
|
|
||||||
? { maxParticipants: race.maxParticipants }
|
|
||||||
: {}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static toRaceDTOs(races: Race[]): RaceDTO[] {
|
|
||||||
const sessionTypeMap = {
|
|
||||||
practice: 'practice' as const,
|
|
||||||
qualifying: 'qualifying' as const,
|
|
||||||
q1: 'qualifying' as const,
|
|
||||||
q2: 'qualifying' as const,
|
|
||||||
q3: 'qualifying' as const,
|
|
||||||
sprint: 'race' as const,
|
|
||||||
main: 'race' as const,
|
|
||||||
timeTrial: 'practice' as const,
|
|
||||||
};
|
|
||||||
|
|
||||||
return races.map((race) => ({
|
|
||||||
id: race.id,
|
|
||||||
leagueId: race.leagueId,
|
|
||||||
scheduledAt: race.scheduledAt.toISOString(),
|
|
||||||
track: race.track,
|
|
||||||
trackId: race.trackId ?? '',
|
|
||||||
car: race.car,
|
|
||||||
carId: race.carId ?? '',
|
|
||||||
sessionType: sessionTypeMap[race.sessionType.value],
|
|
||||||
status: race.status,
|
|
||||||
...(race.strengthOfField !== undefined
|
|
||||||
? { strengthOfField: race.strengthOfField }
|
|
||||||
: {}),
|
|
||||||
...(race.registeredCount !== undefined
|
|
||||||
? { registeredCount: race.registeredCount }
|
|
||||||
: {}),
|
|
||||||
...(race.maxParticipants !== undefined
|
|
||||||
? { maxParticipants: race.maxParticipants }
|
|
||||||
: {}),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
static toResultDTO(result: Result | null): ResultDTO | null {
|
|
||||||
if (!result) return null;
|
|
||||||
return {
|
|
||||||
id: result.id,
|
|
||||||
raceId: result.raceId,
|
|
||||||
driverId: result.driverId,
|
|
||||||
position: result.position,
|
|
||||||
fastestLap: result.fastestLap,
|
|
||||||
incidents: result.incidents,
|
|
||||||
startPosition: result.startPosition,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static toResultDTOs(results: Result[]): ResultDTO[] {
|
|
||||||
return results.map(result => ({
|
|
||||||
id: result.id,
|
|
||||||
raceId: result.raceId,
|
|
||||||
driverId: result.driverId,
|
|
||||||
position: result.position,
|
|
||||||
fastestLap: result.fastestLap,
|
|
||||||
incidents: result.incidents,
|
|
||||||
startPosition: result.startPosition,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
static toStandingDTO(standing: Standing | null): StandingDTO | null {
|
|
||||||
if (!standing) return null;
|
|
||||||
return {
|
|
||||||
leagueId: standing.leagueId,
|
|
||||||
driverId: standing.driverId,
|
|
||||||
points: standing.points,
|
|
||||||
wins: standing.wins,
|
|
||||||
position: standing.position,
|
|
||||||
racesCompleted: standing.racesCompleted,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static toStandingDTOs(standings: Standing[]): StandingDTO[] {
|
|
||||||
return standings.map(standing => ({
|
|
||||||
leagueId: standing.leagueId,
|
|
||||||
driverId: standing.driverId,
|
|
||||||
points: standing.points,
|
|
||||||
wins: standing.wins,
|
|
||||||
position: standing.position,
|
|
||||||
racesCompleted: standing.racesCompleted,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// Mappers for converting between domain entities and DTOs
|
|
||||||
// Example: driverToDTO, leagueToDTO, etc.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export interface DriverRatingPort {
|
|
||||||
getRating(driverId: string): { rating: number | null; ratingChange: number | null };
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Application Port: DriverRatingProvider
|
|
||||||
*
|
|
||||||
* Port for looking up driver ratings.
|
|
||||||
* Implemented by infrastructure adapters that connect to rating systems.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface DriverRatingProvider {
|
|
||||||
/**
|
|
||||||
* Get the rating for a single driver
|
|
||||||
* Returns null if driver has no rating
|
|
||||||
*/
|
|
||||||
getRating(driverId: string): number | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get ratings for multiple drivers
|
|
||||||
* Returns a map of driverId -> rating
|
|
||||||
*/
|
|
||||||
getRatings(driverIds: string[]): Map<string, number>;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* Application Port: IImageServicePort
|
|
||||||
*
|
|
||||||
* Abstraction used by racing application use cases to obtain image URLs
|
|
||||||
* for drivers, teams and leagues without depending on UI/media layers.
|
|
||||||
*/
|
|
||||||
export interface IImageServicePort {
|
|
||||||
getDriverAvatar(driverId: string): string;
|
|
||||||
getTeamLogo(teamId: string): string;
|
|
||||||
getLeagueCover(leagueId: string): string;
|
|
||||||
getLeagueLogo(leagueId: string): string;
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Application Port: ILiveryCompositor
|
|
||||||
*
|
|
||||||
* Defines interface for livery image composition.
|
|
||||||
* Infrastructure will provide image processing implementation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { LiveryDecal } from '../../domain/value-objects/LiveryDecal';
|
|
||||||
|
|
||||||
export interface CompositionResult {
|
|
||||||
success: boolean;
|
|
||||||
composedImageUrl?: string;
|
|
||||||
error?: string;
|
|
||||||
timestamp: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ILiveryCompositor {
|
|
||||||
/**
|
|
||||||
* Composite a livery by layering decals on base image
|
|
||||||
*/
|
|
||||||
composeLivery(
|
|
||||||
baseImageUrl: string,
|
|
||||||
decals: LiveryDecal[]
|
|
||||||
): Promise<CompositionResult>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a livery pack (.zip) for all drivers in a season
|
|
||||||
*/
|
|
||||||
generateLiveryPack(
|
|
||||||
seasonId: string,
|
|
||||||
liveryData: Array<{
|
|
||||||
driverId: string;
|
|
||||||
driverName: string;
|
|
||||||
carId: string;
|
|
||||||
composedImageUrl: string;
|
|
||||||
}>
|
|
||||||
): Promise<Buffer>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate livery image (check for logos/text)
|
|
||||||
*/
|
|
||||||
validateLiveryImage(imageUrl: string): Promise<{
|
|
||||||
isValid: boolean;
|
|
||||||
violations?: string[];
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Application Port: ILiveryStorage
|
|
||||||
*
|
|
||||||
* Defines interface for livery image storage.
|
|
||||||
* Infrastructure will provide cloud storage adapter.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface UploadResult {
|
|
||||||
success: boolean;
|
|
||||||
imageUrl?: string;
|
|
||||||
error?: string;
|
|
||||||
timestamp: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ILiveryStorage {
|
|
||||||
/**
|
|
||||||
* Upload a livery image
|
|
||||||
*/
|
|
||||||
upload(
|
|
||||||
imageData: Buffer | string,
|
|
||||||
fileName: string,
|
|
||||||
metadata?: Record<string, unknown>
|
|
||||||
): Promise<UploadResult>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download a livery image
|
|
||||||
*/
|
|
||||||
download(imageUrl: string): Promise<Buffer>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a livery image
|
|
||||||
*/
|
|
||||||
delete(imageUrl: string): Promise<void>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a signed URL for temporary access
|
|
||||||
*/
|
|
||||||
generateSignedUrl(imageUrl: string, expiresInSeconds: number): Promise<string>;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* Application Port: IPaymentGateway
|
|
||||||
*
|
|
||||||
* Defines interface for payment processing.
|
|
||||||
* Infrastructure will provide mock or real implementation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { Money } from '../../domain/value-objects/Money';
|
|
||||||
|
|
||||||
export interface PaymentResult {
|
|
||||||
success: boolean;
|
|
||||||
transactionId?: string;
|
|
||||||
error?: string;
|
|
||||||
timestamp: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RefundResult {
|
|
||||||
success: boolean;
|
|
||||||
refundId?: string;
|
|
||||||
error?: string;
|
|
||||||
timestamp: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPaymentGateway {
|
|
||||||
/**
|
|
||||||
* Process a payment
|
|
||||||
*/
|
|
||||||
processPayment(
|
|
||||||
amount: Money,
|
|
||||||
payerId: string,
|
|
||||||
description: string,
|
|
||||||
metadata?: Record<string, unknown>
|
|
||||||
): Promise<PaymentResult>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refund a payment
|
|
||||||
*/
|
|
||||||
refund(
|
|
||||||
originalTransactionId: string,
|
|
||||||
amount: Money,
|
|
||||||
reason: string
|
|
||||||
): Promise<RefundResult>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify payment status
|
|
||||||
*/
|
|
||||||
verifyPayment(transactionId: string): Promise<PaymentResult>;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
|
||||||
|
|
||||||
export type LeagueScoringPresetPrimaryChampionshipType =
|
|
||||||
| 'driver'
|
|
||||||
| 'team'
|
|
||||||
| 'nations'
|
|
||||||
| 'trophy';
|
|
||||||
|
|
||||||
export interface LeagueScoringPresetDTO {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
primaryChampionshipType: LeagueScoringPresetPrimaryChampionshipType;
|
|
||||||
sessionSummary: string;
|
|
||||||
bonusSummary: string;
|
|
||||||
dropPolicySummary: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider abstraction for league scoring presets used by application-layer queries.
|
|
||||||
*
|
|
||||||
* In-memory implementation is backed by the preset registry in
|
|
||||||
* InMemoryScoringRepositories.
|
|
||||||
*/
|
|
||||||
export interface LeagueScoringPresetProvider {
|
|
||||||
listPresets(): LeagueScoringPresetDTO[];
|
|
||||||
getPresetById(id: string): LeagueScoringPresetDTO | undefined;
|
|
||||||
createScoringConfigFromPreset(presetId: string, seasonId: string): LeagueScoringConfig;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetDriverAvatarInputPort {
|
||||||
|
driverId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetDriverRatingInputPort {
|
||||||
|
driverId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { SponsorableEntityType } from '../../../domain/entities/SponsorshipRequest';
|
||||||
|
|
||||||
|
export interface GetEntitySponsorshipPricingInputPort {
|
||||||
|
entityType: SponsorableEntityType;
|
||||||
|
entityId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetLeagueCoverInputPort {
|
||||||
|
leagueId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetLeagueLogoInputPort {
|
||||||
|
leagueId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetLeagueScoringPresetByIdInputPort {
|
||||||
|
presetId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetTeamLogoInputPort {
|
||||||
|
teamId: string;
|
||||||
|
}
|
||||||
@@ -3,4 +3,4 @@
|
|||||||
* - 'ranked' (or legacy 'public'): Competitive, public, affects driver ratings. Min 10 drivers.
|
* - 'ranked' (or legacy 'public'): Competitive, public, affects driver ratings. Min 10 drivers.
|
||||||
* - 'unranked' (or legacy 'private'): Casual with friends, no rating impact.
|
* - 'unranked' (or legacy 'private'): Casual with friends, no rating impact.
|
||||||
*/
|
*/
|
||||||
export type LeagueVisibilityInput = 'ranked' | 'unranked' | 'public' | 'private';
|
export type LeagueVisibilityInputPort = 'ranked' | 'unranked' | 'public' | 'private';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface ListLeagueScoringPresetsInputPort {
|
||||||
|
// Empty interface for query with no parameters
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface ProcessPaymentInputPort {
|
||||||
|
amount: number; // in cents
|
||||||
|
payerId: string;
|
||||||
|
description: string;
|
||||||
|
metadata?: Record<string, unknown>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export interface IsDriverRegisteredForRaceInputPort {
|
||||||
|
raceId: string;
|
||||||
|
driverId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetRaceRegistrationsInputPort {
|
||||||
|
raceId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Money } from '../../domain/value-objects/Money';
|
||||||
|
|
||||||
|
export interface RefundPaymentInputPort {
|
||||||
|
originalTransactionId: string;
|
||||||
|
amount: Money;
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface VerifyPaymentInputPort {
|
||||||
|
transactionId: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface AcceptSponsorshipRequestResultDTO {
|
export interface AcceptSponsorshipOutputPort {
|
||||||
requestId: string;
|
requestId: string;
|
||||||
sponsorshipId: string;
|
sponsorshipId: string;
|
||||||
status: 'accepted';
|
status: 'accepted';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface ApplyForSponsorshipResultDTO {
|
export interface ApplyForSponsorshipResultPort {
|
||||||
requestId: string;
|
requestId: string;
|
||||||
status: 'pending';
|
status: 'pending';
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface ApproveLeagueJoinRequestResultPort {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { ChampionshipStandingsRowOutputPort } from './ChampionshipStandingsRowOutputPort';
|
||||||
|
|
||||||
|
export interface ChampionshipStandingsOutputPort {
|
||||||
|
seasonId: string;
|
||||||
|
championshipId: string;
|
||||||
|
championshipName: string;
|
||||||
|
rows: ChampionshipStandingsRowOutputPort[];
|
||||||
|
}
|
||||||
@@ -1,16 +1,9 @@
|
|||||||
import type { ParticipantRef } from '@core/racing/domain/types/ParticipantRef';
|
import type { ParticipantRef } from '@core/racing/domain/types/ParticipantRef';
|
||||||
|
|
||||||
export interface ChampionshipStandingsRowDTO {
|
export interface ChampionshipStandingsRowOutputPort {
|
||||||
participant: ParticipantRef;
|
participant: ParticipantRef;
|
||||||
position: number;
|
position: number;
|
||||||
totalPoints: number;
|
totalPoints: number;
|
||||||
resultsCounted: number;
|
resultsCounted: number;
|
||||||
resultsDropped: number;
|
resultsDropped: number;
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChampionshipStandingsDTO {
|
|
||||||
seasonId: string;
|
|
||||||
championshipId: string;
|
|
||||||
championshipName: string;
|
|
||||||
rows: ChampionshipStandingsRowDTO[];
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface CreateLeagueWithSeasonAndScoringResultDTO {
|
export interface CreateLeagueWithSeasonAndScoringOutputPort {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
seasonId: string;
|
seasonId: string;
|
||||||
scoringPresetId?: string;
|
scoringPresetId?: string;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface CreateSponsorResultDTO {
|
export interface CreateSponsorOutputPort {
|
||||||
sponsor: {
|
sponsor: {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import type { Team } from '../../../domain/entities/Team';
|
||||||
|
|
||||||
|
export interface CreateTeamOutputPort {
|
||||||
|
team: Team;
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
export type DriverDTO = {
|
export interface DriverOutputPort {
|
||||||
id: string;
|
id: string;
|
||||||
iracingId: string;
|
iracingId: string;
|
||||||
name: string;
|
name: string;
|
||||||
country: string;
|
country: string;
|
||||||
bio?: string;
|
bio?: string;
|
||||||
joinedAt: string;
|
joinedAt: string;
|
||||||
};
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import type { Team } from '../../../domain/entities/Team';
|
||||||
|
|
||||||
|
export type GetAllTeamsOutputPort = Team[];
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetDriverAvatarOutputPort {
|
||||||
|
avatarUrl: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface GetDriverRatingOutputPort {
|
||||||
|
rating: number | null;
|
||||||
|
ratingChange: number | null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Team } from '../../../domain/entities/Team';
|
||||||
|
import type { TeamMembership } from '../../../domain/types/TeamMembership';
|
||||||
|
|
||||||
|
export interface GetDriverTeamOutputPort {
|
||||||
|
team: Team;
|
||||||
|
membership: TeamMembership;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import type { SponsorableEntityType } from '../../../domain/entities/SponsorshipRequest';
|
||||||
|
import type { SponsorshipSlotDTO } from './SponsorshipSlotOutputPort';
|
||||||
|
|
||||||
|
export interface GetEntitySponsorshipPricingOutputPort {
|
||||||
|
entityType: SponsorableEntityType;
|
||||||
|
entityId: string;
|
||||||
|
acceptingApplications: boolean;
|
||||||
|
customRequirements?: string;
|
||||||
|
mainSlot?: SponsorshipSlotDTO;
|
||||||
|
secondarySlot?: SponsorshipSlotDTO;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface GetLeagueAdminResultDTO {
|
export interface GetLeagueAdminOutputPort {
|
||||||
league: {
|
league: {
|
||||||
id: string;
|
id: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface GetLeagueAdminPermissionsResultDTO {
|
export interface GetLeagueAdminPermissionsOutputPort {
|
||||||
canRemoveMember: boolean;
|
canRemoveMember: boolean;
|
||||||
canUpdateRoles: boolean;
|
canUpdateRoles: boolean;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetLeagueCoverOutputPort {
|
||||||
|
coverUrl: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface GetLeagueJoinRequestsResultDTO {
|
export interface GetLeagueJoinRequestsOutputPort {
|
||||||
joinRequests: Array<{
|
joinRequests: Array<{
|
||||||
id: string;
|
id: string;
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetLeagueLogoOutputPort {
|
||||||
|
logoUrl: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { LeagueMembership } from '../../../domain/entities/LeagueMembership';
|
||||||
|
|
||||||
|
export interface GetLeagueMembershipsOutputPort {
|
||||||
|
memberships: LeagueMembership[];
|
||||||
|
drivers: { id: string; name: string }[];
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export interface GetLeagueOwnerSummaryResultDTO {
|
export interface GetLeagueOwnerSummaryOutputPort {
|
||||||
summary: { driver: { id: string; name: string }; rating: number; rank: number } | null;
|
summary: { driver: { id: string; name: string }; rating: number; rank: number } | null;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type { ProtestOutputPort } from './ProtestOutputPort';
|
||||||
|
|
||||||
|
export interface RaceOutputPort {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DriverOutputPort {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetLeagueProtestsOutputPort {
|
||||||
|
protests: ProtestOutputPort[];
|
||||||
|
races: RaceOutputPort[];
|
||||||
|
drivers: DriverOutputPort[];
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface GetLeagueScheduleResultDTO {
|
export interface GetLeagueScheduleOutputPort {
|
||||||
races: Array<{
|
races: Array<{
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Team } from '../../../domain/entities/Team';
|
||||||
|
import type { TeamMembership } from '../../../domain/types/TeamMembership';
|
||||||
|
|
||||||
|
export interface GetTeamDetailsOutputPort {
|
||||||
|
team: Team;
|
||||||
|
membership: TeamMembership | null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface GetTeamLogoOutputPort {
|
||||||
|
logoUrl: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export type LeagueDriverSeasonStatsDTO = {
|
export interface LeagueDriverSeasonStatsOutputPort {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
driverId: string;
|
driverId: string;
|
||||||
position: number;
|
position: number;
|
||||||
@@ -17,4 +17,4 @@ export type LeagueDriverSeasonStatsDTO = {
|
|||||||
avgFinish: number | null;
|
avgFinish: number | null;
|
||||||
rating: number | null;
|
rating: number | null;
|
||||||
ratingChange: number | null;
|
ratingChange: number | null;
|
||||||
};
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export type LeagueDTO = {
|
export interface LeagueOutputPort {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -16,9 +16,5 @@ export type LeagueDTO = {
|
|||||||
youtubeUrl?: string;
|
youtubeUrl?: string;
|
||||||
websiteUrl?: string;
|
websiteUrl?: string;
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* Number of active driver slots currently used in this league.
|
|
||||||
* Populated by capacity-aware queries such as GetAllLeaguesWithCapacityQuery.
|
|
||||||
*/
|
|
||||||
usedSlots?: number;
|
usedSlots?: number;
|
||||||
};
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type { Weekday } from '../../../domain/types/Weekday';
|
||||||
|
|
||||||
|
export interface LeagueScheduleOutputPort {
|
||||||
|
seasonStartDate: string;
|
||||||
|
raceStartTime: string;
|
||||||
|
timezoneId: string;
|
||||||
|
recurrenceStrategy: 'weekly' | 'everyNWeeks' | 'monthlyNthWeekday';
|
||||||
|
intervalWeeks?: number;
|
||||||
|
weekdays?: Weekday[];
|
||||||
|
monthlyOrdinal?: 1 | 2 | 3 | 4;
|
||||||
|
monthlyWeekday?: Weekday;
|
||||||
|
plannedRounds: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LeagueSchedulePreviewOutputPort {
|
||||||
|
rounds: Array<{ roundNumber: number; scheduledAt: string; timezoneId: string }>;
|
||||||
|
summary: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface LeagueScoringChampionshipDTO {
|
export interface LeagueScoringChampionshipOutputPort {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: 'driver' | 'team' | 'nations' | 'trophy';
|
type: 'driver' | 'team' | 'nations' | 'trophy';
|
||||||
@@ -8,7 +8,7 @@ export interface LeagueScoringChampionshipDTO {
|
|||||||
dropPolicyDescription: string;
|
dropPolicyDescription: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LeagueScoringConfigDTO {
|
export interface LeagueScoringConfigOutputPort {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
seasonId: string;
|
seasonId: string;
|
||||||
gameId: string;
|
gameId: string;
|
||||||
@@ -16,5 +16,5 @@ export interface LeagueScoringConfigDTO {
|
|||||||
scoringPresetId?: string;
|
scoringPresetId?: string;
|
||||||
scoringPresetName?: string;
|
scoringPresetName?: string;
|
||||||
dropPolicySummary: string;
|
dropPolicySummary: string;
|
||||||
championships: LeagueScoringChampionshipDTO[];
|
championships: LeagueScoringChampionshipOutputPort[];
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
export type LeagueScoringPresetPrimaryChampionshipType =
|
||||||
|
| 'driver'
|
||||||
|
| 'team'
|
||||||
|
| 'nations'
|
||||||
|
| 'trophy';
|
||||||
|
|
||||||
|
export interface LeagueScoringPresetOutputPort {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
primaryChampionshipType: LeagueScoringPresetPrimaryChampionshipType;
|
||||||
|
sessionSummary: string;
|
||||||
|
bonusSummary: string;
|
||||||
|
dropPolicySummary: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export interface LeagueSummaryScoringOutputPort {
|
||||||
|
gameId: string;
|
||||||
|
gameName: string;
|
||||||
|
primaryChampionshipType: 'driver' | 'team' | 'nations' | 'trophy';
|
||||||
|
scoringPresetId: string;
|
||||||
|
scoringPresetName: string;
|
||||||
|
dropPolicySummary: string;
|
||||||
|
scoringPatternSummary: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LeagueSummaryOutputPort {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
createdAt: Date;
|
||||||
|
ownerId: string;
|
||||||
|
maxDrivers?: number;
|
||||||
|
usedDriverSlots?: number;
|
||||||
|
maxTeams?: number;
|
||||||
|
usedTeamSlots?: number;
|
||||||
|
structureSummary?: string;
|
||||||
|
scoringPatternSummary?: string;
|
||||||
|
timingSummary?: string;
|
||||||
|
scoring?: LeagueSummaryScoringOutputPort;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface ProcessPaymentOutputPort {
|
||||||
|
success: boolean;
|
||||||
|
transactionId?: string;
|
||||||
|
error?: string;
|
||||||
|
timestamp: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export interface ProtestOutputPort {
|
||||||
|
id: string;
|
||||||
|
raceId: string;
|
||||||
|
protestingDriverId: string;
|
||||||
|
accusedDriverId: string;
|
||||||
|
submittedAt: Date;
|
||||||
|
description: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export type RaceDTO = {
|
export interface RaceOutputPort {
|
||||||
id: string;
|
id: string;
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
scheduledAt: string;
|
scheduledAt: string;
|
||||||
@@ -11,4 +11,4 @@ export type RaceDTO = {
|
|||||||
strengthOfField?: number;
|
strengthOfField?: number;
|
||||||
registeredCount?: number;
|
registeredCount?: number;
|
||||||
maxParticipants?: number;
|
maxParticipants?: number;
|
||||||
};
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface RefundPaymentOutputPort {
|
||||||
|
success: boolean;
|
||||||
|
refundId?: string;
|
||||||
|
error?: string;
|
||||||
|
timestamp: Date;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export type ResultDTO = {
|
export interface ResultOutputPort {
|
||||||
id: string;
|
id: string;
|
||||||
raceId: string;
|
raceId: string;
|
||||||
driverId: string;
|
driverId: string;
|
||||||
@@ -6,4 +6,4 @@ export type ResultDTO = {
|
|||||||
fastestLap: number;
|
fastestLap: number;
|
||||||
incidents: number;
|
incidents: number;
|
||||||
startPosition: number;
|
startPosition: number;
|
||||||
};
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { SponsorshipTier } from '../../domain/entities/SeasonSponsorship';
|
import type { SponsorshipTier } from '../../../domain/entities/SeasonSponsorship';
|
||||||
|
|
||||||
export interface SponsorshipSlotDTO {
|
export interface SponsorshipSlotOutputPort {
|
||||||
tier: SponsorshipTier;
|
tier: SponsorshipTier;
|
||||||
price: number;
|
price: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
export type StandingDTO = {
|
export interface StandingOutputPort {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
driverId: string;
|
driverId: string;
|
||||||
points: number;
|
points: number;
|
||||||
wins: number;
|
wins: number;
|
||||||
position: number;
|
position: number;
|
||||||
racesCompleted: number;
|
racesCompleted: number;
|
||||||
};
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface VerifyPaymentOutputPort {
|
||||||
|
success: boolean;
|
||||||
|
transactionId?: string;
|
||||||
|
error?: string;
|
||||||
|
timestamp: Date;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import type { League } from '../../domain/entities/League';
|
|||||||
import type { Season } from '../../domain/entities/Season';
|
import type { Season } from '../../domain/entities/Season';
|
||||||
import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
||||||
import type { Game } from '../../domain/entities/Game';
|
import type { Game } from '../../domain/entities/Game';
|
||||||
import type { LeagueScoringPresetDTO } from '../ports/LeagueScoringPresetProvider';
|
import type { LeagueScoringPresetOutputPort } from '../ports/output/LeagueScoringPresetOutputPort';
|
||||||
import type { Presenter } from '@core/shared/presentation';
|
import type { Presenter } from '@core/shared/presentation';
|
||||||
|
|
||||||
export interface LeagueSummaryViewModel {
|
export interface LeagueSummaryViewModel {
|
||||||
@@ -40,7 +40,7 @@ export interface LeagueEnrichedData {
|
|||||||
season?: Season;
|
season?: Season;
|
||||||
scoringConfig?: LeagueScoringConfig;
|
scoringConfig?: LeagueScoringConfig;
|
||||||
game?: Game;
|
game?: Game;
|
||||||
preset?: LeagueScoringPresetDTO;
|
preset?: LeagueScoringPresetOutputPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAllLeaguesWithCapacityAndScoringPresenter
|
export interface IAllLeaguesWithCapacityAndScoringPresenter
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ export interface ApproveLeagueJoinRequestViewModel {
|
|||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApproveLeagueJoinRequestResultDTO {
|
export interface ApproveLeagueJoinRequestResultPort {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IApproveLeagueJoinRequestPresenter extends Presenter<ApproveLeagueJoinRequestResultDTO, ApproveLeagueJoinRequestViewModel> {}
|
export interface IApproveLeagueJoinRequestPresenter extends Presenter<ApproveLeagueJoinRequestResultPort, ApproveLeagueJoinRequestViewModel> {}
|
||||||
@@ -13,8 +13,8 @@ export interface CreateSponsorViewModel {
|
|||||||
sponsor: SponsorDto;
|
sponsor: SponsorDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateSponsorResultDTO {
|
export interface CreateSponsorOutputPort {
|
||||||
sponsor: SponsorDto;
|
sponsor: SponsorDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICreateSponsorPresenter extends Presenter<CreateSponsorResultDTO, CreateSponsorViewModel> {}
|
export interface ICreateSponsorPresenter extends Presenter<CreateSponsorOutputPort, CreateSponsorViewModel> {}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ChampionshipConfig } from '../../domain/types/ChampionshipConfig';
|
import type { ChampionshipConfig } from '../../domain/types/ChampionshipConfig';
|
||||||
import type { LeagueScoringPresetDTO } from '../ports/LeagueScoringPresetProvider';
|
import type { LeagueScoringPresetOutputPort } from '../ports/output/LeagueScoringPresetOutputPort';
|
||||||
import type { Presenter } from '@core/shared/presentation';
|
import type { Presenter } from '@core/shared/presentation';
|
||||||
|
|
||||||
export interface LeagueScoringChampionshipViewModel {
|
export interface LeagueScoringChampionshipViewModel {
|
||||||
@@ -29,7 +29,7 @@ export interface LeagueScoringConfigData {
|
|||||||
gameId: string;
|
gameId: string;
|
||||||
gameName: string;
|
gameName: string;
|
||||||
scoringPresetId?: string;
|
scoringPresetId?: string;
|
||||||
preset?: LeagueScoringPresetDTO;
|
preset?: LeagueScoringPresetOutputPort;
|
||||||
championships: ChampionshipConfig[];
|
championships: ChampionshipConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import type { LeagueScoringPresetDTO } from '../ports/LeagueScoringPresetProvider';
|
import type { LeagueScoringPresetOutputPort } from '../ports/output/LeagueScoringPresetOutputPort';
|
||||||
import type { Presenter } from '@core/shared/presentation';
|
import type { Presenter } from '@core/shared/presentation';
|
||||||
|
|
||||||
export interface LeagueScoringPresetsViewModel {
|
export interface LeagueScoringPresetsViewModel {
|
||||||
presets: LeagueScoringPresetDTO[];
|
presets: LeagueScoringPresetOutputPort[];
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LeagueScoringPresetsResultDTO {
|
export interface LeagueScoringPresetsResultDTO {
|
||||||
presets: LeagueScoringPresetDTO[];
|
presets: LeagueScoringPresetOutputPort[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILeagueScoringPresetsPresenter
|
export interface ILeagueScoringPresetsPresenter
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import type { ISponsorshipRequestRepository } from '../../domain/repositories/IS
|
|||||||
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
|
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
|
||||||
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
||||||
import type { INotificationService } from '@core/notifications/application/ports/INotificationService';
|
import type { INotificationService } from '@core/notifications/application/ports/INotificationService';
|
||||||
import type { IPaymentGateway } from '../ports/IPaymentGateway';
|
|
||||||
import type { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
|
import type { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
|
||||||
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
||||||
import { SeasonSponsorship } from '../../domain/entities/SeasonSponsorship';
|
import { SeasonSponsorship } from '../../domain/entities/SeasonSponsorship';
|
||||||
@@ -18,22 +17,24 @@ import type { AsyncUseCase } from '@core/shared/application';
|
|||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { AcceptSponsorshipRequestDTO } from '../dto/AcceptSponsorshipRequestDTO';
|
import type { AcceptSponsorshipRequestDTO } from '../dto/AcceptSponsorshipRequestDTO';
|
||||||
import type { AcceptSponsorshipRequestResultDTO } from '../dto/AcceptSponsorshipRequestResultDTO';
|
import type { AcceptSponsorshipOutputPort } from '../ports/output/AcceptSponsorshipOutputPort';
|
||||||
|
import type { ProcessPaymentInputPort } from '../ports/input/ProcessPaymentInputPort';
|
||||||
|
import type { ProcessPaymentOutputPort } from '../ports/output/ProcessPaymentOutputPort';
|
||||||
|
|
||||||
export class AcceptSponsorshipRequestUseCase
|
export class AcceptSponsorshipRequestUseCase
|
||||||
implements AsyncUseCase<AcceptSponsorshipRequestDTO, AcceptSponsorshipRequestResultDTO, string> {
|
implements AsyncUseCase<AcceptSponsorshipRequestDTO, AcceptSponsorshipOutputPort, string> {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||||
private readonly seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
private readonly seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
||||||
private readonly seasonRepository: ISeasonRepository,
|
private readonly seasonRepository: ISeasonRepository,
|
||||||
private readonly notificationService: INotificationService,
|
private readonly notificationService: INotificationService,
|
||||||
private readonly paymentGateway: IPaymentGateway,
|
private readonly paymentProcessor: (input: ProcessPaymentInputPort) => Promise<ProcessPaymentOutputPort>,
|
||||||
private readonly walletRepository: IWalletRepository,
|
private readonly walletRepository: IWalletRepository,
|
||||||
private readonly leagueWalletRepository: ILeagueWalletRepository,
|
private readonly leagueWalletRepository: ILeagueWalletRepository,
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: AcceptSponsorshipRequestDTO): Promise<Result<AcceptSponsorshipRequestResultDTO, ApplicationErrorCode<string>>> {
|
async execute(dto: AcceptSponsorshipRequestDTO): Promise<Result<AcceptSponsorshipOutputPort, ApplicationErrorCode<string>>> {
|
||||||
this.logger.debug(`Attempting to accept sponsorship request: ${dto.requestId}`, { requestId: dto.requestId, respondedBy: dto.respondedBy });
|
this.logger.debug(`Attempting to accept sponsorship request: ${dto.requestId}`, { requestId: dto.requestId, respondedBy: dto.respondedBy });
|
||||||
|
|
||||||
// Find the request
|
// Find the request
|
||||||
@@ -92,13 +93,15 @@ export class AcceptSponsorshipRequestUseCase
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process payment
|
// Process payment using clean input/output ports with primitive types
|
||||||
const paymentResult = await this.paymentGateway.processPayment(
|
const paymentInput: ProcessPaymentInputPort = {
|
||||||
request.offeredAmount,
|
amount: request.offeredAmount.amount, // Extract primitive number from value object
|
||||||
request.sponsorId,
|
payerId: request.sponsorId,
|
||||||
`Sponsorship payment for ${request.entityType} ${request.entityId}`,
|
description: `Sponsorship payment for ${request.entityType} ${request.entityId}`,
|
||||||
{ requestId: request.id }
|
metadata: { requestId: request.id }
|
||||||
);
|
};
|
||||||
|
|
||||||
|
const paymentResult = await this.paymentProcessor(paymentInput);
|
||||||
if (!paymentResult.success) {
|
if (!paymentResult.success) {
|
||||||
this.logger.error(`Payment failed for sponsorship request ${request.id}: ${paymentResult.error}`, undefined, { requestId: request.id });
|
this.logger.error(`Payment failed for sponsorship request ${request.id}: ${paymentResult.error}`, undefined, { requestId: request.id });
|
||||||
return Result.err({ code: 'PAYMENT_PROCESSING_FAILED' });
|
return Result.err({ code: 'PAYMENT_PROCESSING_FAILED' });
|
||||||
@@ -142,4 +145,4 @@ export class AcceptSponsorshipRequestUseCase
|
|||||||
netAmount: acceptedRequest.getNetAmount().amount,
|
netAmount: acceptedRequest.getNetAmount().amount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,11 +14,11 @@ import type { AsyncUseCase } from '@core/shared/application';
|
|||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { Logger } from '@core/shared/application';
|
import type { Logger } from '@core/shared/application';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { ApplyForSponsorshipDTO } from '../dto/ApplyForSponsorshipDTO';
|
import type { ApplyForSponsorshipPort } from '../ports/input/ApplyForSponsorshipPort';
|
||||||
import type { ApplyForSponsorshipResultDTO } from '../dto/ApplyForSponsorshipResultDTO';
|
import type { ApplyForSponsorshipResultPort } from '../ports/output/ApplyForSponsorshipResultPort';
|
||||||
|
|
||||||
export class ApplyForSponsorshipUseCase
|
export class ApplyForSponsorshipUseCase
|
||||||
implements AsyncUseCase<ApplyForSponsorshipDTO, ApplyForSponsorshipResultDTO, string>
|
implements AsyncUseCase<ApplyForSponsorshipPort, ApplyForSponsorshipResultPort, string>
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||||
@@ -27,7 +27,7 @@ export class ApplyForSponsorshipUseCase
|
|||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: ApplyForSponsorshipDTO): Promise<Result<ApplyForSponsorshipResultDTO, ApplicationErrorCode<string>>> {
|
async execute(dto: ApplyForSponsorshipPort): Promise<Result<ApplyForSponsorshipResultPort, ApplicationErrorCode<string>>> {
|
||||||
this.logger.debug('Attempting to apply for sponsorship', { dto });
|
this.logger.debug('Attempting to apply for sponsorship', { dto });
|
||||||
|
|
||||||
// Validate sponsor exists
|
// Validate sponsor exists
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import type { AsyncUseCase } from '@core/shared/application';
|
|||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { Logger } from '@core/shared/application';
|
import type { Logger } from '@core/shared/application';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { ApplyPenaltyCommand } from '../dto/ApplyPenaltyCommand';
|
import type { ApplyPenaltyCommandPort } from '../ports/input/ApplyPenaltyCommandPort';
|
||||||
|
|
||||||
export class ApplyPenaltyUseCase
|
export class ApplyPenaltyUseCase
|
||||||
implements AsyncUseCase<ApplyPenaltyCommand, { penaltyId: string }, string> {
|
implements AsyncUseCase<ApplyPenaltyCommandPort, { penaltyId: string }, string> {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly penaltyRepository: IPenaltyRepository,
|
private readonly penaltyRepository: IPenaltyRepository,
|
||||||
private readonly protestRepository: IProtestRepository,
|
private readonly protestRepository: IProtestRepository,
|
||||||
@@ -27,7 +27,7 @@ export class ApplyPenaltyUseCase
|
|||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(command: ApplyPenaltyCommand): Promise<Result<{ penaltyId: string }, ApplicationErrorCode<string>>> {
|
async execute(command: ApplyPenaltyCommandPort): Promise<Result<{ penaltyId: string }, ApplicationErrorCode<string>>> {
|
||||||
this.logger.debug('ApplyPenaltyUseCase: Executing with command', command);
|
this.logger.debug('ApplyPenaltyUseCase: Executing with command', command);
|
||||||
|
|
||||||
// Validate race exists
|
// Validate race exists
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
|
|||||||
import type { AsyncUseCase } from '@core/shared/application';
|
import type { AsyncUseCase } from '@core/shared/application';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import type { ApproveLeagueJoinRequestUseCaseParams } from '../dto/ApproveLeagueJoinRequestUseCaseParams';
|
import type { ApproveLeagueJoinRequestUseCaseParams } from '../dto/ApproveLeagueJoinRequestUseCaseParams';
|
||||||
import type { ApproveLeagueJoinRequestResultDTO } from '../dto/ApproveLeagueJoinRequestResultDTO';
|
import type { ApproveLeagueJoinRequestResultPort } from '../ports/output/ApproveLeagueJoinRequestResultPort';
|
||||||
import { JoinedAt } from '../../domain/value-objects/JoinedAt';
|
import { JoinedAt } from '../../domain/value-objects/JoinedAt';
|
||||||
|
|
||||||
export class ApproveLeagueJoinRequestUseCase implements AsyncUseCase<ApproveLeagueJoinRequestUseCaseParams, ApproveLeagueJoinRequestResultDTO, string> {
|
export class ApproveLeagueJoinRequestUseCase implements AsyncUseCase<ApproveLeagueJoinRequestUseCaseParams, ApproveLeagueJoinRequestResultPort, string> {
|
||||||
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
|
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
|
||||||
|
|
||||||
async execute(params: ApproveLeagueJoinRequestUseCaseParams): Promise<Result<ApproveLeagueJoinRequestResultDTO, ApplicationErrorCode<string>>> {
|
async execute(params: ApproveLeagueJoinRequestUseCaseParams): Promise<Result<ApproveLeagueJoinRequestResultPort, ApplicationErrorCode<string>>> {
|
||||||
const requests = await this.leagueMembershipRepository.getJoinRequests(params.leagueId);
|
const requests = await this.leagueMembershipRepository.getJoinRequests(params.leagueId);
|
||||||
const request = requests.find(r => r.id === params.requestId);
|
const request = requests.find(r => r.id === params.requestId);
|
||||||
if (!request) {
|
if (!request) {
|
||||||
@@ -25,7 +25,7 @@ export class ApproveLeagueJoinRequestUseCase implements AsyncUseCase<ApproveLeag
|
|||||||
status: 'active',
|
status: 'active',
|
||||||
joinedAt: JoinedAt.create(new Date()),
|
joinedAt: JoinedAt.create(new Date()),
|
||||||
});
|
});
|
||||||
const dto: ApproveLeagueJoinRequestResultDTO = { success: true, message: 'Join request approved.' };
|
const dto: ApproveLeagueJoinRequestResultPort = { success: true, message: 'Join request approved.' };
|
||||||
return Result.ok(dto);
|
return Result.ok(dto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { LeagueVisibilityInput } from '../dto/LeagueVisibilityInput';
|
import type { LeagueVisibilityInput } from '../dto/LeagueVisibilityInput';
|
||||||
import type { CreateLeagueWithSeasonAndScoringResultDTO } from '../dto/CreateLeagueWithSeasonAndScoringResultDTO';
|
import type { CreateLeagueWithSeasonAndScoringOutputPort } from '../ports/output/CreateLeagueWithSeasonAndScoringOutputPort';
|
||||||
|
|
||||||
export interface CreateLeagueWithSeasonAndScoringCommand {
|
export interface CreateLeagueWithSeasonAndScoringCommand {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -40,7 +40,7 @@ export interface CreateLeagueWithSeasonAndScoringCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CreateLeagueWithSeasonAndScoringUseCase
|
export class CreateLeagueWithSeasonAndScoringUseCase
|
||||||
implements AsyncUseCase<CreateLeagueWithSeasonAndScoringCommand, CreateLeagueWithSeasonAndScoringResultDTO, 'VALIDATION_ERROR' | 'UNKNOWN_PRESET' | 'REPOSITORY_ERROR'> {
|
implements AsyncUseCase<CreateLeagueWithSeasonAndScoringCommand, CreateLeagueWithSeasonAndScoringOutputPort, 'VALIDATION_ERROR' | 'UNKNOWN_PRESET' | 'REPOSITORY_ERROR'> {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly leagueRepository: ILeagueRepository,
|
private readonly leagueRepository: ILeagueRepository,
|
||||||
private readonly seasonRepository: ISeasonRepository,
|
private readonly seasonRepository: ISeasonRepository,
|
||||||
@@ -51,7 +51,7 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
|||||||
|
|
||||||
async execute(
|
async execute(
|
||||||
command: CreateLeagueWithSeasonAndScoringCommand,
|
command: CreateLeagueWithSeasonAndScoringCommand,
|
||||||
): Promise<Result<CreateLeagueWithSeasonAndScoringResultDTO, ApplicationErrorCode<'VALIDATION_ERROR' | 'UNKNOWN_PRESET' | 'REPOSITORY_ERROR', { message: string }>>> {
|
): Promise<Result<CreateLeagueWithSeasonAndScoringOutputPort, ApplicationErrorCode<'VALIDATION_ERROR' | 'UNKNOWN_PRESET' | 'REPOSITORY_ERROR', { message: string }>>> {
|
||||||
this.logger.debug('Executing CreateLeagueWithSeasonAndScoringUseCase', { command });
|
this.logger.debug('Executing CreateLeagueWithSeasonAndScoringUseCase', { command });
|
||||||
const validation = this.validate(command);
|
const validation = this.validate(command);
|
||||||
if (validation.isErr()) {
|
if (validation.isErr()) {
|
||||||
@@ -112,7 +112,7 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
|||||||
await this.leagueScoringConfigRepository.save(finalConfig);
|
await this.leagueScoringConfigRepository.save(finalConfig);
|
||||||
this.logger.info(`Scoring configuration saved for season ${seasonId}.`);
|
this.logger.info(`Scoring configuration saved for season ${seasonId}.`);
|
||||||
|
|
||||||
const result: CreateLeagueWithSeasonAndScoringResultDTO = {
|
const result: CreateLeagueWithSeasonAndScoringOutputPort = {
|
||||||
leagueId: league.id.toString(),
|
leagueId: league.id.toString(),
|
||||||
seasonId,
|
seasonId,
|
||||||
scoringPresetId: preset.id,
|
scoringPresetId: preset.id,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type { AsyncUseCase } from '@core/shared/application';
|
|||||||
import type { Logger } from '@core/shared/application';
|
import type { Logger } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { CreateSponsorResultDTO } from '../dto/CreateSponsorResultDTO';
|
import type { CreateSponsorOutputPort } from '../ports/output/CreateSponsorOutputPort';
|
||||||
|
|
||||||
export interface CreateSponsorCommand {
|
export interface CreateSponsorCommand {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -20,7 +20,7 @@ export interface CreateSponsorCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CreateSponsorUseCase
|
export class CreateSponsorUseCase
|
||||||
implements AsyncUseCase<CreateSponsorCommand, CreateSponsorResultDTO, 'VALIDATION_ERROR' | 'REPOSITORY_ERROR'>
|
implements AsyncUseCase<CreateSponsorCommand, CreateSponsorOutputPort, 'VALIDATION_ERROR' | 'REPOSITORY_ERROR'>
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sponsorRepository: ISponsorRepository,
|
private readonly sponsorRepository: ISponsorRepository,
|
||||||
@@ -29,7 +29,7 @@ export class CreateSponsorUseCase
|
|||||||
|
|
||||||
async execute(
|
async execute(
|
||||||
command: CreateSponsorCommand,
|
command: CreateSponsorCommand,
|
||||||
): Promise<Result<CreateSponsorResultDTO, ApplicationErrorCode<'VALIDATION_ERROR' | 'REPOSITORY_ERROR', { message: string }>>> {
|
): Promise<Result<CreateSponsorOutputPort, ApplicationErrorCode<'VALIDATION_ERROR' | 'REPOSITORY_ERROR', { message: string }>>> {
|
||||||
this.logger.debug('Executing CreateSponsorUseCase', { command });
|
this.logger.debug('Executing CreateSponsorUseCase', { command });
|
||||||
const validation = this.validate(command);
|
const validation = this.validate(command);
|
||||||
if (validation.isErr()) {
|
if (validation.isErr()) {
|
||||||
@@ -51,7 +51,7 @@ export class CreateSponsorUseCase
|
|||||||
await this.sponsorRepository.create(sponsor);
|
await this.sponsorRepository.create(sponsor);
|
||||||
this.logger.info(`Sponsor ${sponsor.name} (${sponsor.id}) created successfully.`);
|
this.logger.info(`Sponsor ${sponsor.name} (${sponsor.id}) created successfully.`);
|
||||||
|
|
||||||
const result: CreateSponsorResultDTO = {
|
const result: CreateSponsorOutputPort = {
|
||||||
sponsor: {
|
sponsor: {
|
||||||
id: sponsor.id,
|
id: sponsor.id,
|
||||||
name: sponsor.name,
|
name: sponsor.name,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type { AsyncUseCase } from '@core/shared/application';
|
|||||||
import type { Logger } from '@core/shared/application';
|
import type { Logger } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import type { CreateTeamOutputPort } from '../ports/output/CreateTeamOutputPort';
|
||||||
|
|
||||||
export interface CreateTeamCommandDTO {
|
export interface CreateTeamCommandDTO {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -25,12 +26,8 @@ export interface CreateTeamCommandDTO {
|
|||||||
leagues: string[];
|
leagues: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateTeamResultDTO {
|
|
||||||
team: Team;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CreateTeamUseCase
|
export class CreateTeamUseCase
|
||||||
implements AsyncUseCase<CreateTeamCommandDTO, CreateTeamResultDTO, 'ALREADY_IN_TEAM' | 'REPOSITORY_ERROR'>
|
implements AsyncUseCase<CreateTeamCommandDTO, CreateTeamOutputPort, 'ALREADY_IN_TEAM' | 'REPOSITORY_ERROR'>
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly teamRepository: ITeamRepository,
|
private readonly teamRepository: ITeamRepository,
|
||||||
@@ -40,7 +37,7 @@ export class CreateTeamUseCase
|
|||||||
|
|
||||||
async execute(
|
async execute(
|
||||||
command: CreateTeamCommandDTO,
|
command: CreateTeamCommandDTO,
|
||||||
): Promise<Result<CreateTeamResultDTO, ApplicationErrorCode<'ALREADY_IN_TEAM' | 'REPOSITORY_ERROR', { message: string }>>> {
|
): Promise<Result<CreateTeamOutputPort, ApplicationErrorCode<'ALREADY_IN_TEAM' | 'REPOSITORY_ERROR', { message: string }>>> {
|
||||||
this.logger.debug('Executing CreateTeamUseCase', { command });
|
this.logger.debug('Executing CreateTeamUseCase', { command });
|
||||||
const { name, tag, description, ownerId, leagues } = command;
|
const { name, tag, description, ownerId, leagues } = command;
|
||||||
|
|
||||||
@@ -80,7 +77,7 @@ export class CreateTeamUseCase
|
|||||||
await this.membershipRepository.saveMembership(membership);
|
await this.membershipRepository.saveMembership(membership);
|
||||||
this.logger.debug('Team membership created successfully.');
|
this.logger.debug('Team membership created successfully.');
|
||||||
|
|
||||||
const result: CreateTeamResultDTO = { team: createdTeam };
|
const result: CreateTeamOutputPort = { team: createdTeam };
|
||||||
this.logger.debug('CreateTeamUseCase completed successfully.', { result });
|
this.logger.debug('CreateTeamUseCase completed successfully.', { result });
|
||||||
return Result.ok(result);
|
return Result.ok(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISe
|
|||||||
import type { AsyncUseCase, Logger } from '@core/shared/application';
|
import type { AsyncUseCase, Logger } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { GetEntitySponsorshipPricingDTO } from '../dto/GetEntitySponsorshipPricingDTO';
|
import type { GetEntitySponsorshipPricingInputPort } from '../ports/input/GetEntitySponsorshipPricingInputPort';
|
||||||
import type { GetEntitySponsorshipPricingResultDTO } from '../dto/GetEntitySponsorshipPricingResultDTO';
|
import type { GetEntitySponsorshipPricingOutputPort } from '../ports/output/GetEntitySponsorshipPricingOutputPort';
|
||||||
|
|
||||||
export class GetEntitySponsorshipPricingUseCase
|
export class GetEntitySponsorshipPricingUseCase
|
||||||
implements AsyncUseCase<GetEntitySponsorshipPricingDTO, GetEntitySponsorshipPricingResultDTO | null, 'REPOSITORY_ERROR'>
|
implements AsyncUseCase<GetEntitySponsorshipPricingInputPort, GetEntitySponsorshipPricingOutputPort | null, 'REPOSITORY_ERROR'>
|
||||||
{
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private readonly sponsorshipPricingRepo: ISponsorshipPricingRepository,
|
private readonly sponsorshipPricingRepo: ISponsorshipPricingRepository,
|
||||||
@@ -24,7 +24,7 @@ export class GetEntitySponsorshipPricingUseCase
|
|||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: GetEntitySponsorshipPricingDTO): Promise<Result<GetEntitySponsorshipPricingResultDTO | null, ApplicationErrorCode<'REPOSITORY_ERROR', { message: string }>>> {
|
async execute(dto: GetEntitySponsorshipPricingInputPort): Promise<Result<GetEntitySponsorshipPricingOutputPort | null, ApplicationErrorCode<'REPOSITORY_ERROR', { message: string }>>> {
|
||||||
this.logger.debug(`Executing GetEntitySponsorshipPricingUseCase for entityType: ${dto.entityType}, entityId: ${dto.entityId}`);
|
this.logger.debug(`Executing GetEntitySponsorshipPricingUseCase for entityType: ${dto.entityType}, entityId: ${dto.entityId}`);
|
||||||
try {
|
try {
|
||||||
const pricing = await this.sponsorshipPricingRepo.findByEntity(dto.entityType, dto.entityId);
|
const pricing = await this.sponsorshipPricingRepo.findByEntity(dto.entityType, dto.entityId);
|
||||||
@@ -53,7 +53,7 @@ export class GetEntitySponsorshipPricingUseCase
|
|||||||
filledSecondarySlots = activeSponsorships.filter(s => s.tier === 'secondary').length;
|
filledSecondarySlots = activeSponsorships.filter(s => s.tier === 'secondary').length;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: GetEntitySponsorshipPricingResultDTO = {
|
const result: GetEntitySponsorshipPricingOutputPort = {
|
||||||
entityType: dto.entityType,
|
entityType: dto.entityType,
|
||||||
entityId: dto.entityId,
|
entityId: dto.entityId,
|
||||||
acceptingApplications: pricing.acceptingApplications,
|
acceptingApplications: pricing.acceptingApplications,
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ import type { ILeagueRepository } from '../../domain/repositories/ILeagueReposit
|
|||||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||||
import type { AsyncUseCase } from '@core/shared/application';
|
import type { AsyncUseCase } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { GetLeagueAdminPermissionsResultDTO } from '../dto/GetLeagueAdminPermissionsResultDTO';
|
import type { GetLeagueAdminPermissionsOutputPort } from '../ports/output/GetLeagueAdminPermissionsOutputPort';
|
||||||
|
|
||||||
export class GetLeagueAdminPermissionsUseCase implements AsyncUseCase<{ leagueId: string; performerDriverId: string }, GetLeagueAdminPermissionsResultDTO, 'NO_ERROR'> {
|
export class GetLeagueAdminPermissionsUseCase implements AsyncUseCase<{ leagueId: string; performerDriverId: string }, GetLeagueAdminPermissionsOutputPort, 'NO_ERROR'> {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly leagueRepository: ILeagueRepository,
|
private readonly leagueRepository: ILeagueRepository,
|
||||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(params: { leagueId: string; performerDriverId: string }): Promise<Result<GetLeagueAdminPermissionsResultDTO, never>> {
|
async execute(params: { leagueId: string; performerDriverId: string }): Promise<Result<GetLeagueAdminPermissionsOutputPort, never>> {
|
||||||
const league = await this.leagueRepository.findById(params.leagueId);
|
const league = await this.leagueRepository.findById(params.leagueId);
|
||||||
if (!league) {
|
if (!league) {
|
||||||
return Result.ok({ canRemoveMember: false, canUpdateRoles: false });
|
return Result.ok({ canRemoveMember: false, canUpdateRoles: false });
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ import type { ILeagueRepository } from '../../domain/repositories/ILeagueReposit
|
|||||||
import type { AsyncUseCase } from '@core/shared/application';
|
import type { AsyncUseCase } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { GetLeagueAdminResultDTO } from '../dto/GetLeagueAdminResultDTO';
|
import type { GetLeagueAdminOutputPort } from '../ports/output/GetLeagueAdminOutputPort';
|
||||||
|
|
||||||
export class GetLeagueAdminUseCase implements AsyncUseCase<{ leagueId: string }, GetLeagueAdminResultDTO, 'LEAGUE_NOT_FOUND'> {
|
export class GetLeagueAdminUseCase implements AsyncUseCase<{ leagueId: string }, GetLeagueAdminOutputPort, 'LEAGUE_NOT_FOUND'> {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly leagueRepository: ILeagueRepository,
|
private readonly leagueRepository: ILeagueRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(params: { leagueId: string }): Promise<Result<GetLeagueAdminResultDTO, ApplicationErrorCode<'LEAGUE_NOT_FOUND', { message: string }>>> {
|
async execute(params: { leagueId: string }): Promise<Result<GetLeagueAdminOutputPort, ApplicationErrorCode<'LEAGUE_NOT_FOUND', { message: string }>>> {
|
||||||
const league = await this.leagueRepository.findById(params.leagueId);
|
const league = await this.leagueRepository.findById(params.leagueId);
|
||||||
if (!league) {
|
if (!league) {
|
||||||
return Result.err({ code: 'LEAGUE_NOT_FOUND', details: { message: 'League not found' } });
|
return Result.err({ code: 'LEAGUE_NOT_FOUND', details: { message: 'League not found' } });
|
||||||
}
|
}
|
||||||
|
|
||||||
const dto: GetLeagueAdminResultDTO = {
|
const dto: GetLeagueAdminOutputPort = {
|
||||||
league: {
|
league: {
|
||||||
id: league.id,
|
id: league.id,
|
||||||
ownerId: league.ownerId,
|
ownerId: league.ownerId,
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import type { IDriverRepository } from '../../domain/repositories/IDriverReposit
|
|||||||
import type { AsyncUseCase } from '@core/shared/application';
|
import type { AsyncUseCase } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { GetLeagueJoinRequestsResultDTO } from '../dto/GetLeagueJoinRequestsResultDTO';
|
import type { GetLeagueJoinRequestsOutputPort } from '../ports/output/GetLeagueJoinRequestsOutputPort';
|
||||||
|
|
||||||
export interface GetLeagueJoinRequestsUseCaseParams {
|
export interface GetLeagueJoinRequestsUseCaseParams {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetLeagueJoinRequestsUseCase implements AsyncUseCase<GetLeagueJoinRequestsUseCaseParams, GetLeagueJoinRequestsResultDTO, 'NO_ERROR'> {
|
export class GetLeagueJoinRequestsUseCase implements AsyncUseCase<GetLeagueJoinRequestsUseCaseParams, GetLeagueJoinRequestsOutputPort, 'NO_ERROR'> {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||||
private readonly driverRepository: IDriverRepository,
|
private readonly driverRepository: IDriverRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(params: GetLeagueJoinRequestsUseCaseParams): Promise<Result<GetLeagueJoinRequestsResultDTO, ApplicationErrorCode<'NO_ERROR'>>> {
|
async execute(params: GetLeagueJoinRequestsUseCaseParams): Promise<Result<GetLeagueJoinRequestsOutputPort, ApplicationErrorCode<'NO_ERROR'>>> {
|
||||||
const joinRequests = await this.leagueMembershipRepository.getJoinRequests(params.leagueId);
|
const joinRequests = await this.leagueMembershipRepository.getJoinRequests(params.leagueId);
|
||||||
const driverIds = [...new Set(joinRequests.map(r => r.driverId))];
|
const driverIds = [...new Set(joinRequests.map(r => r.driverId))];
|
||||||
const drivers = await Promise.all(driverIds.map(id => this.driverRepository.findById(id)));
|
const drivers = await Promise.all(driverIds.map(id => this.driverRepository.findById(id)));
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import type { IDriverRepository } from '../../domain/repositories/IDriverReposit
|
|||||||
import type { AsyncUseCase } from '@core/shared/application';
|
import type { AsyncUseCase } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { GetLeagueMembershipsResultDTO } from '../dto/GetLeagueMembershipsResultDTO';
|
import type { GetLeagueMembershipsOutputPort } from '../ports/output/GetLeagueMembershipsOutputPort';
|
||||||
|
|
||||||
export interface GetLeagueMembershipsUseCaseParams {
|
export interface GetLeagueMembershipsUseCaseParams {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetLeagueMembershipsUseCase implements AsyncUseCase<GetLeagueMembershipsUseCaseParams, GetLeagueMembershipsResultDTO, 'NO_ERROR'> {
|
export class GetLeagueMembershipsUseCase implements AsyncUseCase<GetLeagueMembershipsUseCaseParams, GetLeagueMembershipsOutputPort, 'NO_ERROR'> {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||||
private readonly driverRepository: IDriverRepository,
|
private readonly driverRepository: IDriverRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(params: GetLeagueMembershipsUseCaseParams): Promise<Result<GetLeagueMembershipsResultDTO, ApplicationErrorCode<'NO_ERROR'>>> {
|
async execute(params: GetLeagueMembershipsUseCaseParams): Promise<Result<GetLeagueMembershipsOutputPort, ApplicationErrorCode<'NO_ERROR'>>> {
|
||||||
const memberships = await this.leagueMembershipRepository.getLeagueMembers(params.leagueId);
|
const memberships = await this.leagueMembershipRepository.getLeagueMembers(params.leagueId);
|
||||||
const drivers: { id: string; name: string }[] = [];
|
const drivers: { id: string; name: string }[] = [];
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export class GetLeagueMembershipsUseCase implements AsyncUseCase<GetLeagueMember
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dto: GetLeagueMembershipsResultDTO = {
|
const dto: GetLeagueMembershipsOutputPort = {
|
||||||
memberships,
|
memberships,
|
||||||
drivers,
|
drivers,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ import type { IDriverRepository } from '../../domain/repositories/IDriverReposit
|
|||||||
import type { AsyncUseCase } from '@core/shared/application';
|
import type { AsyncUseCase } from '@core/shared/application';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { GetLeagueOwnerSummaryResultDTO } from '../dto/GetLeagueOwnerSummaryResultDTO';
|
import type { GetLeagueOwnerSummaryOutputPort } from '../ports/output/GetLeagueOwnerSummaryOutputPort';
|
||||||
|
|
||||||
export interface GetLeagueOwnerSummaryUseCaseParams {
|
export interface GetLeagueOwnerSummaryUseCaseParams {
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetLeagueOwnerSummaryUseCase implements AsyncUseCase<GetLeagueOwnerSummaryUseCaseParams, GetLeagueOwnerSummaryResultDTO, 'NO_ERROR'> {
|
export class GetLeagueOwnerSummaryUseCase implements AsyncUseCase<GetLeagueOwnerSummaryUseCaseParams, GetLeagueOwnerSummaryOutputPort, 'NO_ERROR'> {
|
||||||
constructor(private readonly driverRepository: IDriverRepository) {}
|
constructor(private readonly driverRepository: IDriverRepository) {}
|
||||||
|
|
||||||
async execute(params: GetLeagueOwnerSummaryUseCaseParams): Promise<Result<GetLeagueOwnerSummaryResultDTO, ApplicationErrorCode<'NO_ERROR'>>> {
|
async execute(params: GetLeagueOwnerSummaryUseCaseParams): Promise<Result<GetLeagueOwnerSummaryOutputPort, ApplicationErrorCode<'NO_ERROR'>>> {
|
||||||
const driver = await this.driverRepository.findById(params.ownerId);
|
const driver = await this.driverRepository.findById(params.ownerId);
|
||||||
const summary = driver ? { driver: { id: driver.id, name: driver.name }, rating: 0, rank: 0 } : null;
|
const summary = driver ? { driver: { id: driver.id, name: driver.name }, rating: 0, rank: 0 } : null;
|
||||||
return Result.ok({ summary });
|
return Result.ok({ summary });
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import type { IDriverRepository } from '../../domain/repositories/IDriverReposit
|
|||||||
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
|
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
|
||||||
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
|
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
|
||||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||||
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
|
import type { GetDriverRatingInputPort } from '../ports/input/GetDriverRatingInputPort';
|
||||||
import type { IImageServicePort } from '../ports/IImageServicePort';
|
import type { GetDriverRatingOutputPort } from '../ports/output/GetDriverRatingOutputPort';
|
||||||
|
import type { GetDriverAvatarInputPort } from '../ports/input/GetDriverAvatarInputPort';
|
||||||
|
import type { GetDriverAvatarOutputPort } from '../ports/output/GetDriverAvatarOutputPort';
|
||||||
import type {
|
import type {
|
||||||
RaceDetailViewModel,
|
RaceDetailViewModel,
|
||||||
RaceDetailRaceViewModel,
|
RaceDetailRaceViewModel,
|
||||||
@@ -44,8 +46,8 @@ export class GetRaceDetailUseCase
|
|||||||
private readonly raceRegistrationRepository: IRaceRegistrationRepository,
|
private readonly raceRegistrationRepository: IRaceRegistrationRepository,
|
||||||
private readonly resultRepository: IResultRepository,
|
private readonly resultRepository: IResultRepository,
|
||||||
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
private readonly leagueMembershipRepository: ILeagueMembershipRepository,
|
||||||
private readonly driverRatingProvider: DriverRatingProvider,
|
private readonly getDriverRating: (input: GetDriverRatingInputPort) => Promise<GetDriverRatingOutputPort>,
|
||||||
private readonly imageService: IImageServicePort,
|
private readonly getDriverAvatar: (input: GetDriverAvatarInputPort) => Promise<GetDriverAvatarOutputPort>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(params: GetRaceDetailQueryParams): Promise<Result<RaceDetailViewModel, ApplicationErrorCode<GetRaceDetailErrorCode>>> {
|
async execute(params: GetRaceDetailQueryParams): Promise<Result<RaceDetailViewModel, ApplicationErrorCode<GetRaceDetailErrorCode>>> {
|
||||||
@@ -62,22 +64,26 @@ export class GetRaceDetailUseCase
|
|||||||
this.leagueMembershipRepository.getMembership(race.leagueId, driverId),
|
this.leagueMembershipRepository.getMembership(race.leagueId, driverId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const ratings = this.driverRatingProvider.getRatings(registeredDriverIds);
|
|
||||||
|
|
||||||
const drivers = await Promise.all(
|
const drivers = await Promise.all(
|
||||||
registeredDriverIds.map(id => this.driverRepository.findById(id)),
|
registeredDriverIds.map(id => this.driverRepository.findById(id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const entryList: RaceDetailEntryViewModel[] = drivers
|
const entryList: RaceDetailEntryViewModel[] = [];
|
||||||
.filter((d): d is NonNullable<typeof d> => d !== null)
|
for (const driver of drivers) {
|
||||||
.map(driver => ({
|
if (driver) {
|
||||||
id: driver.id,
|
const ratingResult = await this.getDriverRating({ driverId: driver.id });
|
||||||
name: driver.name,
|
const avatarResult = await this.getDriverAvatar({ driverId: driver.id });
|
||||||
country: driver.country,
|
|
||||||
avatarUrl: this.imageService.getDriverAvatar(driver.id),
|
entryList.push({
|
||||||
rating: ratings.get(driver.id) ?? null,
|
id: driver.id,
|
||||||
isCurrentUser: driver.id === driverId,
|
name: driver.name,
|
||||||
}));
|
country: driver.country,
|
||||||
|
avatarUrl: avatarResult.avatarUrl,
|
||||||
|
rating: ratingResult.rating,
|
||||||
|
isCurrentUser: driver.id === driverId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isUserRegistered = registeredDriverIds.includes(driverId);
|
const isUserRegistered = registeredDriverIds.includes(driverId);
|
||||||
const isUpcoming = race.status === 'scheduled' && race.scheduledAt > new Date();
|
const isUpcoming = race.status === 'scheduled' && race.scheduledAt > new Date();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user