Files
gridpilot.gg/apps/website/lib/presenters/TeamRosterPresenter.ts
2025-12-11 21:06:25 +01:00

59 lines
1.7 KiB
TypeScript

import type { TeamMembership, TeamRole } from '@gridpilot/racing/domain/types/TeamMembership';
import type { DriverDTO } from '@gridpilot/racing/application/dto/DriverDTO';
import { EntityMappers } from '@gridpilot/racing/application/mappers/EntityMappers';
import { getDriverRepository, getDriverStats } from '@/lib/di-container';
export interface TeamRosterMemberViewModel {
driver: DriverDTO;
role: TeamRole;
joinedAt: string;
rating: number | null;
overallRank: number | null;
}
export interface TeamRosterViewModel {
members: TeamRosterMemberViewModel[];
averageRating: number;
}
/**
* Presenter/facade for team roster.
* Encapsulates repository and stats access so the TeamRoster component can remain a pure view.
*/
export async function getTeamRosterViewModel(
memberships: TeamMembership[]
): Promise<TeamRosterViewModel> {
const driverRepo = getDriverRepository();
const allDrivers = await driverRepo.findAll();
const members: TeamRosterMemberViewModel[] = [];
for (const membership of memberships) {
const driver = allDrivers.find((d) => d.id === membership.driverId);
if (!driver) continue;
const dto = EntityMappers.toDriverDTO(driver);
if (!dto) continue;
const stats = getDriverStats(membership.driverId);
members.push({
driver: dto,
role: membership.role,
joinedAt: membership.joinedAt.toISOString(),
rating: stats?.rating ?? null,
overallRank: typeof stats?.overallRank === 'number' ? stats.overallRank : null,
});
}
const averageRating =
members.length > 0
? Math.round(
members.reduce((sum, m) => sum + (m.rating ?? 0), 0) / members.length
)
: 0;
return {
members,
averageRating,
};
}