58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import type { League } from '@gridpilot/racing/domain/entities/League';
|
|
import type {
|
|
IAllLeaguesWithCapacityPresenter,
|
|
LeagueWithCapacityViewModel,
|
|
AllLeaguesWithCapacityViewModel,
|
|
} from '@gridpilot/racing/application/presenters/IAllLeaguesWithCapacityPresenter';
|
|
|
|
export class AllLeaguesWithCapacityPresenter implements IAllLeaguesWithCapacityPresenter {
|
|
private viewModel: AllLeaguesWithCapacityViewModel | null = null;
|
|
|
|
present(
|
|
leagues: League[],
|
|
memberCounts: Map<string, number>
|
|
): AllLeaguesWithCapacityViewModel {
|
|
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);
|
|
|
|
return {
|
|
id: league.id,
|
|
name: league.name,
|
|
description: league.description,
|
|
ownerId: league.ownerId,
|
|
settings: {
|
|
...league.settings,
|
|
maxDrivers: safeMaxDrivers,
|
|
},
|
|
createdAt: league.createdAt.toISOString(),
|
|
socialLinks: league.socialLinks
|
|
? {
|
|
discordUrl: league.socialLinks.discordUrl,
|
|
youtubeUrl: league.socialLinks.youtubeUrl,
|
|
websiteUrl: league.socialLinks.websiteUrl,
|
|
}
|
|
: undefined,
|
|
usedSlots,
|
|
};
|
|
});
|
|
|
|
this.viewModel = {
|
|
leagues: leagueItems,
|
|
totalCount: leagueItems.length,
|
|
};
|
|
|
|
return this.viewModel;
|
|
}
|
|
|
|
getViewModel(): AllLeaguesWithCapacityViewModel {
|
|
if (!this.viewModel) {
|
|
throw new Error('Presenter has not been called yet');
|
|
}
|
|
return this.viewModel;
|
|
}
|
|
} |