fix issues in core

This commit is contained in:
2025-12-23 11:25:08 +01:00
parent 1efd971032
commit 2854ae3c5c
113 changed files with 1142 additions and 458 deletions

View File

@@ -1,5 +1,5 @@
import { v4 as uuidv4 } from 'uuid';
import { Season } from '../../domain/entities/Season';
import { Season } from '../../domain/entities/season/Season';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { Weekday } from '../../domain/types/Weekday';
@@ -32,7 +32,7 @@ export interface SeasonSummaryDTO {
seasonId: string;
leagueId: string;
name: string;
status: import('../../domain/entities/Season').SeasonStatus;
status: 'planned' | 'active' | 'completed' | 'archived' | 'cancelled';
startDate?: Date;
endDate?: Date;
isPrimary: boolean;
@@ -56,7 +56,7 @@ export interface SeasonDetailsDTO {
leagueId: string;
gameId: string;
name: string;
status: import('../../domain/entities/Season').SeasonStatus;
status: 'planned' | 'active' | 'completed' | 'archived' | 'cancelled';
startDate?: Date;
endDate?: Date;
maxDrivers?: number;
@@ -69,11 +69,11 @@ export interface SeasonDetailsDTO {
customScoringEnabled: boolean;
};
dropPolicy?: {
strategy: import('../../domain/value-objects/SeasonDropPolicy').SeasonDropStrategy;
strategy: string;
n?: number;
};
stewarding?: {
decisionMode: import('../../domain/entities/League').StewardingDecisionMode;
decisionMode: string;
requiredVotes?: number;
requireDefense: boolean;
defenseTimeLimit: number;
@@ -95,7 +95,7 @@ export interface ManageSeasonLifecycleCommand {
export interface ManageSeasonLifecycleResultDTO {
seasonId: string;
status: import('../../domain/entities/Season').SeasonStatus;
status: 'planned' | 'active' | 'completed' | 'archived' | 'cancelled';
startDate?: Date;
endDate?: Date;
}
@@ -140,7 +140,7 @@ export class SeasonApplicationService {
const season = Season.create({
id: seasonId,
leagueId: league.id,
leagueId: league.id.toString(),
gameId: command.gameId,
name: command.name,
year: new Date().getFullYear(),
@@ -163,7 +163,7 @@ export class SeasonApplicationService {
throw new Error(`League not found: ${query.leagueId}`);
}
const seasons = await this.seasonRepository.listByLeague(league.id);
const seasons = await this.seasonRepository.listByLeague(league.id.toString());
const items: SeasonSummaryDTO[] = seasons.map((s) => ({
seasonId: s.id,
leagueId: s.leagueId,
@@ -184,7 +184,7 @@ export class SeasonApplicationService {
}
const season = await this.seasonRepository.findById(query.seasonId);
if (!season || season.leagueId !== league.id) {
if (!season || season.leagueId !== league.id.toString()) {
throw new Error(`Season ${query.seasonId} does not belong to league ${league.id}`);
}
@@ -248,7 +248,7 @@ export class SeasonApplicationService {
}
const season = await this.seasonRepository.findById(command.seasonId);
if (!season || season.leagueId !== league.id) {
if (!season || season.leagueId !== league.id.toString()) {
throw new Error(`Season ${command.seasonId} does not belong to league ${league.id}`);
}
@@ -288,29 +288,38 @@ export class SeasonApplicationService {
maxDrivers?: number;
} {
const schedule = this.buildScheduleFromTimings(config);
const scoringConfig = new SeasonScoringConfig({
scoringPresetId: config.scoring.patternId ?? 'custom',
customScoringEnabled: config.scoring.customScoringEnabled ?? false,
});
const dropPolicy = new SeasonDropPolicy({
strategy: config.dropPolicy.strategy,
...(config.dropPolicy.n !== undefined ? { n: config.dropPolicy.n } : {}),
});
const stewardingConfig = new SeasonStewardingConfig({
decisionMode: config.stewarding.decisionMode,
...(config.stewarding.requiredVotes !== undefined
? { requiredVotes: config.stewarding.requiredVotes }
: {}),
requireDefense: config.stewarding.requireDefense,
defenseTimeLimit: config.stewarding.defenseTimeLimit,
voteTimeLimit: config.stewarding.voteTimeLimit,
protestDeadlineHours: config.stewarding.protestDeadlineHours,
stewardingClosesHours: config.stewarding.stewardingClosesHours,
notifyAccusedOnProtest: config.stewarding.notifyAccusedOnProtest,
notifyOnVoteRequired: config.stewarding.notifyOnVoteRequired,
});
const scoringConfig = config.scoring
? new SeasonScoringConfig({
scoringPresetId: config.scoring.patternId ?? 'custom',
customScoringEnabled: config.scoring.customScoringEnabled ?? false,
})
: undefined;
const structure = config.structure;
const dropPolicy = config.dropPolicy
? new SeasonDropPolicy({
strategy: config.dropPolicy.strategy as any,
...(config.dropPolicy.n !== undefined ? { n: config.dropPolicy.n } : {}),
})
: undefined;
const stewardingConfig = config.stewarding
? new SeasonStewardingConfig({
decisionMode: config.stewarding.decisionMode as any,
...(config.stewarding.requiredVotes !== undefined
? { requiredVotes: config.stewarding.requiredVotes }
: {}),
requireDefense: config.stewarding.requireDefense ?? false,
defenseTimeLimit: config.stewarding.defenseTimeLimit ?? 48,
voteTimeLimit: config.stewarding.voteTimeLimit ?? 72,
protestDeadlineHours: config.stewarding.protestDeadlineHours ?? 48,
stewardingClosesHours: config.stewarding.stewardingClosesHours ?? 168,
notifyAccusedOnProtest: config.stewarding.notifyAccusedOnProtest ?? true,
notifyOnVoteRequired: config.stewarding.notifyOnVoteRequired ?? true,
})
: undefined;
const structure = config.structure ?? {};
const maxDrivers =
typeof structure.maxDrivers === 'number' && structure.maxDrivers > 0
? structure.maxDrivers
@@ -318,28 +327,28 @@ export class SeasonApplicationService {
return {
...(schedule !== undefined ? { schedule } : {}),
scoringConfig,
dropPolicy,
stewardingConfig,
...(scoringConfig !== undefined ? { scoringConfig } : {}),
...(dropPolicy !== undefined ? { dropPolicy } : {}),
...(stewardingConfig !== undefined ? { stewardingConfig } : {}),
...(maxDrivers !== undefined ? { maxDrivers } : {}),
};
}
private buildScheduleFromTimings(config: LeagueConfigFormModel): SeasonSchedule | undefined {
const { timings } = config;
if (!timings.seasonStartDate || !timings.raceStartTime) {
if (!timings || !timings.seasonStartDate || !timings.raceStartTime) {
return undefined;
}
const startDate = new Date(timings.seasonStartDate);
const timeOfDay = RaceTimeOfDay.fromString(timings.raceStartTime);
const timezoneId = timings.timezoneId ?? 'UTC';
const timezone = new LeagueTimezone(timezoneId);
const timezone = LeagueTimezone.create(timezoneId);
const plannedRounds =
typeof timings.roundsPlanned === 'number' && timings.roundsPlanned > 0
? timings.roundsPlanned
: timings.sessionCount;
: timings.sessionCount ?? 0;
const recurrence = (() => {
const weekdays: WeekdaySet =
@@ -353,10 +362,10 @@ export class SeasonApplicationService {
weekdays,
);
case 'monthlyNthWeekday': {
const pattern = new MonthlyRecurrencePattern({
ordinal: (timings.monthlyOrdinal ?? 1) as 1 | 2 | 3 | 4,
weekday: (timings.monthlyWeekday ?? 'Mon') as Weekday,
});
const pattern = MonthlyRecurrencePattern.create(
(timings.monthlyOrdinal ?? 1) as 1 | 2 | 3 | 4,
(timings.monthlyWeekday ?? 'Mon') as Weekday,
);
return RecurrenceStrategyFactory.monthlyNthWeekday(pattern);
}
case 'weekly':