wip league admin tools
This commit is contained in:
@@ -3,15 +3,20 @@ import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import type { League } from '../../domain/entities/League';
|
||||
import type { Race } from '../../domain/entities/Race';
|
||||
import type { Season } from '../../domain/entities/season/Season';
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
||||
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
||||
import { SeasonScheduleGenerator } from '../../domain/services/SeasonScheduleGenerator';
|
||||
|
||||
export type GetLeagueScheduleErrorCode =
|
||||
| 'LEAGUE_NOT_FOUND'
|
||||
| 'SEASON_NOT_FOUND'
|
||||
| 'REPOSITORY_ERROR';
|
||||
|
||||
export interface GetLeagueScheduleInput {
|
||||
leagueId: string;
|
||||
seasonId?: string;
|
||||
}
|
||||
|
||||
export interface LeagueScheduledRace {
|
||||
@@ -20,17 +25,88 @@ export interface LeagueScheduledRace {
|
||||
|
||||
export interface GetLeagueScheduleResult {
|
||||
league: League;
|
||||
seasonId: string;
|
||||
published: boolean;
|
||||
races: LeagueScheduledRace[];
|
||||
}
|
||||
|
||||
export class GetLeagueScheduleUseCase {
|
||||
constructor(
|
||||
private readonly leagueRepository: ILeagueRepository,
|
||||
private readonly seasonRepository: ISeasonRepository,
|
||||
private readonly raceRepository: IRaceRepository,
|
||||
private readonly logger: Logger,
|
||||
private readonly output: UseCaseOutputPort<GetLeagueScheduleResult>,
|
||||
) {}
|
||||
|
||||
private async resolveSeasonForSchedule(params: {
|
||||
leagueId: string;
|
||||
requestedSeasonId?: string;
|
||||
}): Promise<Result<Season, ApplicationErrorCode<GetLeagueScheduleErrorCode, { message: string }>>> {
|
||||
if (params.requestedSeasonId) {
|
||||
const season = await this.seasonRepository.findById(params.requestedSeasonId);
|
||||
if (!season || season.leagueId !== params.leagueId) {
|
||||
return Result.err({
|
||||
code: 'SEASON_NOT_FOUND',
|
||||
details: { message: 'Season not found for league' },
|
||||
});
|
||||
}
|
||||
return Result.ok(season);
|
||||
}
|
||||
|
||||
const seasons = await this.seasonRepository.findByLeagueId(params.leagueId);
|
||||
const activeSeason = seasons.find(s => s.status.isActive()) ?? seasons[0];
|
||||
if (!activeSeason) {
|
||||
return Result.err({
|
||||
code: 'SEASON_NOT_FOUND',
|
||||
details: { message: 'No seasons found for league' },
|
||||
});
|
||||
}
|
||||
|
||||
return Result.ok(activeSeason);
|
||||
}
|
||||
|
||||
private getSeasonDateWindow(season: Season): { start?: Date; endInclusive?: Date } {
|
||||
const start = season.startDate ?? season.schedule?.startDate;
|
||||
const window: { start?: Date; endInclusive?: Date } = {};
|
||||
|
||||
if (start) {
|
||||
window.start = start;
|
||||
}
|
||||
|
||||
if (season.endDate) {
|
||||
window.endInclusive = season.endDate;
|
||||
return window;
|
||||
}
|
||||
|
||||
if (season.schedule) {
|
||||
const slots = SeasonScheduleGenerator.generateSlotsUpTo(
|
||||
season.schedule,
|
||||
season.schedule.plannedRounds,
|
||||
);
|
||||
const last = slots.at(-1);
|
||||
if (last?.scheduledAt) {
|
||||
window.endInclusive = last.scheduledAt;
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
private filterRacesBySeasonWindow(season: Season, races: Race[]): Race[] {
|
||||
const { start, endInclusive } = this.getSeasonDateWindow(season);
|
||||
|
||||
if (!start && !endInclusive) return races;
|
||||
|
||||
return races.filter(race => {
|
||||
const t = race.scheduledAt.getTime();
|
||||
if (start && t < start.getTime()) return false;
|
||||
if (endInclusive && t > endInclusive.getTime()) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
async execute(
|
||||
input: GetLeagueScheduleInput,
|
||||
): Promise<
|
||||
@@ -49,14 +125,26 @@ export class GetLeagueScheduleUseCase {
|
||||
});
|
||||
}
|
||||
|
||||
const races = await this.raceRepository.findByLeagueId(leagueId);
|
||||
const seasonResult = await this.resolveSeasonForSchedule({
|
||||
leagueId,
|
||||
...(input.seasonId ? { requestedSeasonId: input.seasonId } : {}),
|
||||
});
|
||||
if (seasonResult.isErr()) {
|
||||
return Result.err(seasonResult.unwrapErr());
|
||||
}
|
||||
const season = seasonResult.unwrap();
|
||||
|
||||
const scheduledRaces: LeagueScheduledRace[] = races.map(race => ({
|
||||
const races = await this.raceRepository.findByLeagueId(leagueId);
|
||||
const seasonRaces = this.filterRacesBySeasonWindow(season, races);
|
||||
|
||||
const scheduledRaces: LeagueScheduledRace[] = seasonRaces.map(race => ({
|
||||
race,
|
||||
}));
|
||||
|
||||
const result: GetLeagueScheduleResult = {
|
||||
league,
|
||||
seasonId: season.id,
|
||||
published: season.schedulePublished ?? false,
|
||||
races: scheduledRaces,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user