112 lines
3.6 KiB
TypeScript
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';
|
|
}
|
|
} |