fix issues in core

This commit is contained in:
2025-12-23 16:16:12 +01:00
parent 120d3bb1a1
commit d04a21fe02
40 changed files with 280 additions and 841 deletions

View File

@@ -7,8 +7,8 @@ import {
CreateSeasonForLeagueUseCase,
type CreateSeasonForLeagueInput,
type CreateSeasonForLeagueResult,
type LeagueConfigFormModel,
} from '@core/racing/application/use-cases/CreateSeasonForLeagueUseCase';
import type { LeagueConfigFormModel } from '@core/racing/application/dto/LeagueConfigFormDTO';
import type { UseCaseOutputPort } from '@core/shared/application';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Result } from '@core/shared/application/Result';

View File

@@ -2,7 +2,6 @@ import { Season } from '../../domain/entities/season/Season';
import { League } from '../../domain/entities/League';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { LeagueConfigFormModel } from '@core/racing/application/dto/LeagueConfigFormDTO';
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { SeasonScoringConfig } from '../../domain/value-objects/SeasonScoringConfig';
import { SeasonDropPolicy, type SeasonDropStrategy } from '../../domain/value-objects/SeasonDropPolicy';
@@ -19,6 +18,58 @@ import type { UseCaseOutputPort } from '@core/shared/application';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
export type LeagueConfigFormModel = {
basics?: {
name?: string;
description?: string;
visibility?: string;
gameId?: string;
};
structure?: {
mode?: string;
maxDrivers?: number;
};
championships?: {
enableDriverChampionship?: boolean;
enableTeamChampionship?: boolean;
enableNationsChampionship?: boolean;
enableTrophyChampionship?: boolean;
};
scoring?: {
patternId?: string;
customScoringEnabled?: boolean;
};
dropPolicy?: {
strategy?: string;
n?: number;
};
timings?: {
qualifyingMinutes?: number;
mainRaceMinutes?: number;
sessionCount?: number;
roundsPlanned?: number;
seasonStartDate?: string;
raceStartTime?: string;
timezoneId?: string;
recurrenceStrategy?: string;
weekdays?: string[];
intervalWeeks?: number;
monthlyOrdinal?: number;
monthlyWeekday?: string;
};
stewarding?: {
decisionMode?: string;
requiredVotes?: number;
requireDefense?: boolean;
defenseTimeLimit?: number;
voteTimeLimit?: number;
protestDeadlineHours?: number;
stewardingClosesHours?: number;
notifyAccusedOnProtest?: boolean;
notifyOnVoteRequired?: boolean;
};
};
export type CreateSeasonForLeagueInput = {
leagueId: string;
name: string;

View File

@@ -71,10 +71,8 @@ export class GetDriversLeaderboardUseCase implements UseCase<GetDriversLeaderboa
avatarUrls[driver.id] = await this.getDriverAvatar(driver.id);
}
// TODO maps way too much data, should just create Domain Objects
const items: DriverLeaderboardItem[] = drivers.map(driver => {
const ranking = rankings.find(r => r.driverId === driver.id);
const items: DriverLeaderboardItem[] = drivers.map((driver) => {
const ranking = rankings.find((r) => r.driverId === driver.id);
const stats = this.driverStatsService.getDriverStats(driver.id);
const rating = ranking?.rating ?? 0;
const racesCompleted = stats?.totalRaces ?? 0;

View File

@@ -1,15 +1,71 @@
import { SeasonScheduleGenerator } from '../../domain/services/SeasonScheduleGenerator';
import {
scheduleDTOToSeasonSchedule,
type SeasonScheduleConfigDTO,
} from '../dto/LeagueScheduleDTO';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { RaceTimeOfDay } from '../../domain/value-objects/RaceTimeOfDay';
import { LeagueTimezone } from '../../domain/value-objects/LeagueTimezone';
import { RecurrenceStrategyFactory } from '../../domain/value-objects/RecurrenceStrategy';
import { WeekdaySet } from '../../domain/value-objects/WeekdaySet';
import { MonthlyRecurrencePattern } from '../../domain/value-objects/MonthlyRecurrencePattern';
import { ALL_WEEKDAYS, type Weekday } from '../../domain/types/Weekday';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
export type PreviewLeagueScheduleSeasonConfig = SeasonScheduleConfigDTO;
export type SeasonScheduleConfig = {
seasonStartDate: string;
recurrenceStrategy: 'weekly' | 'everyNWeeks' | 'monthlyNthWeekday';
weekdays?: string[];
raceStartTime: string;
timezoneId: string;
plannedRounds: number;
intervalWeeks?: number;
monthlyOrdinal?: 1 | 2 | 3 | 4;
monthlyWeekday?: string;
};
function toWeekdaySet(values: string[] | undefined): WeekdaySet {
const weekdays = (values ?? []).filter((v): v is Weekday =>
ALL_WEEKDAYS.includes(v as Weekday),
);
return WeekdaySet.fromArray(weekdays.length > 0 ? weekdays : ['Mon']);
}
function scheduleConfigToSeasonSchedule(dto: SeasonScheduleConfig): SeasonSchedule {
const startDate = new Date(dto.seasonStartDate);
const timeOfDay = RaceTimeOfDay.fromString(dto.raceStartTime);
const timezone = LeagueTimezone.create(dto.timezoneId);
const recurrence = (() => {
switch (dto.recurrenceStrategy) {
case 'everyNWeeks':
return RecurrenceStrategyFactory.everyNWeeks(
dto.intervalWeeks ?? 2,
toWeekdaySet(dto.weekdays),
);
case 'monthlyNthWeekday': {
const pattern = MonthlyRecurrencePattern.create(
dto.monthlyOrdinal ?? 1,
((dto.monthlyWeekday ?? 'Mon') as Weekday),
);
return RecurrenceStrategyFactory.monthlyNthWeekday(pattern);
}
case 'weekly':
default:
return RecurrenceStrategyFactory.weekly(toWeekdaySet(dto.weekdays));
}
})();
return new SeasonSchedule({
startDate,
timeOfDay,
timezone,
recurrence,
plannedRounds: dto.plannedRounds,
});
}
export type PreviewLeagueScheduleSeasonConfig = SeasonScheduleConfig;
export type PreviewLeagueScheduleInput = {
schedule: PreviewLeagueScheduleSeasonConfig;
@@ -54,7 +110,7 @@ export class PreviewLeagueScheduleUseCase {
try {
let seasonSchedule: SeasonSchedule;
try {
seasonSchedule = scheduleDTOToSeasonSchedule(params.schedule);
seasonSchedule = scheduleConfigToSeasonSchedule(params.schedule);
} catch (error) {
this.logger.warn('Invalid schedule data provided', {
schedule: params.schedule,

View File

@@ -19,8 +19,8 @@ import {
type ListSeasonsForLeagueErrorCode,
type GetSeasonDetailsErrorCode,
type ManageSeasonLifecycleErrorCode,
type LeagueConfigFormModel,
} from '@core/racing/application/use-cases/SeasonUseCases';
import type { LeagueConfigFormModel } from '@core/racing/application/dto/LeagueConfigFormDTO';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

View File

@@ -1,7 +1,6 @@
import { Season } from '../../domain/entities/season/Season';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { LeagueConfigFormModel } from '../dto/LeagueConfigFormDTO';
import { SeasonSchedule } from '../../domain/value-objects/SeasonSchedule';
import { SeasonScoringConfig } from '../../domain/value-objects/SeasonScoringConfig';
import { SeasonDropPolicy } from '../../domain/value-objects/SeasonDropPolicy';
@@ -21,6 +20,58 @@ import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPo
* Input, result and error models shared across Season-focused use cases.
*/
export type LeagueConfigFormModel = {
basics?: {
name?: string;
description?: string;
visibility?: string;
gameId?: string;
};
structure?: {
mode?: string;
maxDrivers?: number;
};
championships?: {
enableDriverChampionship?: boolean;
enableTeamChampionship?: boolean;
enableNationsChampionship?: boolean;
enableTrophyChampionship?: boolean;
};
scoring?: {
patternId?: string;
customScoringEnabled?: boolean;
};
dropPolicy?: {
strategy?: string;
n?: number;
};
timings?: {
qualifyingMinutes?: number;
mainRaceMinutes?: number;
sessionCount?: number;
roundsPlanned?: number;
seasonStartDate?: string;
raceStartTime?: string;
timezoneId?: string;
recurrenceStrategy?: string;
weekdays?: string[];
intervalWeeks?: number;
monthlyOrdinal?: number;
monthlyWeekday?: string;
};
stewarding?: {
decisionMode?: string;
requiredVotes?: number;
requireDefense?: boolean;
defenseTimeLimit?: number;
voteTimeLimit?: number;
protestDeadlineHours?: number;
stewardingClosesHours?: number;
notifyAccusedOnProtest?: boolean;
notifyOnVoteRequired?: boolean;
};
};
export interface CreateSeasonForLeagueInput {
leagueId: string;
name: string;