115 lines
4.2 KiB
TypeScript
115 lines
4.2 KiB
TypeScript
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;
|
|
weekdays?: Weekday[];
|
|
monthlyOrdinal?: 1 | 2 | 3 | 4;
|
|
monthlyWeekday?: Weekday;
|
|
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 RacingApplicationError('seasonStartDate is required');
|
|
}
|
|
if (!dto.raceStartTime) {
|
|
throw new RacingApplicationError('raceStartTime is required');
|
|
}
|
|
if (!dto.timezoneId) {
|
|
throw new RacingApplicationError('timezoneId is required');
|
|
}
|
|
if (!dto.recurrenceStrategy) {
|
|
throw new RacingApplicationError('recurrenceStrategy is required');
|
|
}
|
|
if (!Number.isInteger(dto.plannedRounds) || dto.plannedRounds <= 0) {
|
|
throw new RacingApplicationError('plannedRounds must be a positive integer');
|
|
}
|
|
|
|
const startDate = new Date(dto.seasonStartDate);
|
|
if (Number.isNaN(startDate.getTime())) {
|
|
throw new RacingApplicationError(`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 RacingApplicationError('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 RacingApplicationError('weekdays are required for everyNWeeks recurrence');
|
|
}
|
|
if (dto.intervalWeeks == null) {
|
|
throw new RacingApplicationError('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 RacingApplicationError('monthlyOrdinal and monthlyWeekday are required for monthlyNthWeekday');
|
|
}
|
|
const pattern = new MonthlyRecurrencePattern(dto.monthlyOrdinal, dto.monthlyWeekday);
|
|
recurrence = RecurrenceStrategyFactory.monthlyNthWeekday(pattern);
|
|
} else {
|
|
throw new RacingApplicationError(`Unknown recurrenceStrategy "${dto.recurrenceStrategy}"`);
|
|
}
|
|
|
|
return new SeasonSchedule({
|
|
startDate,
|
|
timeOfDay,
|
|
timezone,
|
|
recurrence,
|
|
plannedRounds: dto.plannedRounds,
|
|
});
|
|
} |