Files
gridpilot.gg/apps/website/lib/presenters/AllLeaguesWithCapacityPresenter.ts
2025-12-12 01:11:36 +01:00

73 lines
2.1 KiB
TypeScript

import type {
IAllLeaguesWithCapacityPresenter,
LeagueWithCapacityViewModel,
AllLeaguesWithCapacityViewModel,
AllLeaguesWithCapacityResultDTO,
} from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityPresenter';
export class AllLeaguesWithCapacityPresenter implements IAllLeaguesWithCapacityPresenter {
private viewModel: AllLeaguesWithCapacityViewModel | null = null;
reset(): void {
this.viewModel = null;
}
present(input: AllLeaguesWithCapacityResultDTO): void {
const { leagues, memberCounts } = input;
const leagueItems: LeagueWithCapacityViewModel[] = leagues.map((league) => {
const usedSlots = memberCounts.get(league.id) ?? 0;
// Ensure we never expose an impossible state like 26/24:
// clamp maxDrivers to at least usedSlots at the application boundary.
const configuredMax = league.settings.maxDrivers ?? usedSlots;
const safeMaxDrivers = Math.max(configuredMax, usedSlots);
const base: LeagueWithCapacityViewModel = {
id: league.id,
name: league.name,
description: league.description,
ownerId: league.ownerId,
settings: {
...league.settings,
maxDrivers: safeMaxDrivers,
},
createdAt: league.createdAt.toISOString(),
usedSlots,
};
if (!league.socialLinks) {
return base;
}
const socialLinks: NonNullable<LeagueWithCapacityViewModel['socialLinks']> = {};
if (league.socialLinks.discordUrl) {
socialLinks.discordUrl = league.socialLinks.discordUrl;
}
if (league.socialLinks.youtubeUrl) {
socialLinks.youtubeUrl = league.socialLinks.youtubeUrl;
}
if (league.socialLinks.websiteUrl) {
socialLinks.websiteUrl = league.socialLinks.websiteUrl;
}
if (Object.keys(socialLinks).length === 0) {
return base;
}
return {
...base,
socialLinks,
};
});
this.viewModel = {
leagues: leagueItems,
totalCount: leagueItems.length,
};
}
getViewModel(): AllLeaguesWithCapacityViewModel | null {
return this.viewModel;
}
}