59 lines
1.7 KiB
TypeScript
59 lines
1.7 KiB
TypeScript
import type { TeamMembership, TeamRole } from '@gridpilot/racing';
|
|
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,
|
|
};
|
|
} |