Files
gridpilot.gg/apps/website/lib/presenters/AllLeaguesWithCapacityAndScoringPresenter.ts
2025-12-10 18:28:32 +01:00

112 lines
3.6 KiB
TypeScript

import type { League } from '@gridpilot/racing/domain/entities/League';
import type {
IAllLeaguesWithCapacityAndScoringPresenter,
LeagueEnrichedData,
LeagueSummaryViewModel,
AllLeaguesWithCapacityAndScoringViewModel,
} from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityAndScoringPresenter';
export class AllLeaguesWithCapacityAndScoringPresenter implements IAllLeaguesWithCapacityAndScoringPresenter {
private viewModel: AllLeaguesWithCapacityAndScoringViewModel | null = null;
present(enrichedLeagues: LeagueEnrichedData[]): AllLeaguesWithCapacityAndScoringViewModel {
const leagueItems: LeagueSummaryViewModel[] = enrichedLeagues.map((data) => {
const { league, usedDriverSlots, season, scoringConfig, game, preset } = data;
const configuredMaxDrivers = league.settings.maxDrivers ?? usedDriverSlots;
const safeMaxDrivers = Math.max(configuredMaxDrivers, usedDriverSlots);
const structureSummary = `Solo • ${safeMaxDrivers} drivers`;
const qualifyingMinutes = 30;
const mainRaceMinutes =
typeof league.settings.sessionDuration === 'number'
? league.settings.sessionDuration
: 40;
const timingSummary = `${qualifyingMinutes} min Quali • ${mainRaceMinutes} min Race`;
let scoringSummary: LeagueSummaryViewModel['scoring'] | undefined;
let scoringPatternSummary: string | undefined;
if (season && scoringConfig && game) {
const dropPolicySummary =
preset?.dropPolicySummary ?? this.deriveDropPolicySummary(scoringConfig);
const primaryChampionshipType =
preset?.primaryChampionshipType ??
(scoringConfig.championships[0]?.type ?? 'driver');
const scoringPresetName = preset?.name ?? 'Custom';
scoringPatternSummary = `${scoringPresetName}${dropPolicySummary}`;
scoringSummary = {
gameId: game.id,
gameName: game.name,
primaryChampionshipType,
scoringPresetId: scoringConfig.scoringPresetId ?? 'custom',
scoringPresetName,
dropPolicySummary,
scoringPatternSummary,
};
}
return {
id: league.id,
name: league.name,
description: league.description,
ownerId: league.ownerId,
createdAt: league.createdAt,
maxDrivers: safeMaxDrivers,
usedDriverSlots,
maxTeams: undefined,
usedTeamSlots: undefined,
structureSummary,
scoringPatternSummary,
timingSummary,
scoring: scoringSummary,
};
});
this.viewModel = {
leagues: leagueItems,
totalCount: leagueItems.length,
};
return this.viewModel;
}
getViewModel(): AllLeaguesWithCapacityAndScoringViewModel {
if (!this.viewModel) {
throw new Error('Presenter has not been called yet');
}
return this.viewModel;
}
private deriveDropPolicySummary(config: {
championships: Array<{
dropScorePolicy: { strategy: string; count?: number; dropCount?: number };
}>;
}): string {
const championship = config.championships[0];
if (!championship) {
return 'All results count';
}
const policy = championship.dropScorePolicy;
if (!policy || policy.strategy === 'none') {
return 'All results count';
}
if (policy.strategy === 'bestNResults' && typeof policy.count === 'number') {
return `Best ${policy.count} results count`;
}
if (
policy.strategy === 'dropWorstN' &&
typeof policy.dropCount === 'number'
) {
return `Worst ${policy.dropCount} results are dropped`;
}
return 'Custom drop score rules';
}
}