96 lines
3.2 KiB
TypeScript
96 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import type {
|
|
LeagueMembership as DomainLeagueMembership,
|
|
MembershipRole,
|
|
MembershipStatus,
|
|
} from '@gridpilot/racing/domain/entities/LeagueMembership';
|
|
|
|
/**
|
|
* Lightweight league membership model mirroring the domain type but with
|
|
* a stringified joinedAt for easier UI formatting.
|
|
*/
|
|
export interface LeagueMembership extends Omit<DomainLeagueMembership, 'joinedAt'> {
|
|
joinedAt: string;
|
|
}
|
|
|
|
const leagueMemberships = new Map<string, LeagueMembership[]>();
|
|
|
|
/**
|
|
* Initialize league memberships once from the in-memory league membership repository
|
|
* that is seeded via the static racing seed in the DI container.
|
|
*
|
|
* This avoids depending on raw testing-support seed exports and keeps all demo
|
|
* membership data flowing through the same in-memory repositories used elsewhere.
|
|
*/
|
|
(async function initializeLeagueMembershipsFromRepository() {
|
|
if (leagueMemberships.size > 0) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const { getLeagueRepository, getLeagueMembershipRepository } = await import('./di-container');
|
|
const leagueRepo = getLeagueRepository();
|
|
const membershipRepo = getLeagueMembershipRepository();
|
|
|
|
const allLeagues = await leagueRepo.findAll();
|
|
const byLeague = new Map<string, LeagueMembership[]>();
|
|
|
|
for (const league of allLeagues) {
|
|
const memberships = await membershipRepo.getLeagueMembers(league.id);
|
|
|
|
const mapped: LeagueMembership[] = memberships.map((membership) => ({
|
|
id: membership.id,
|
|
leagueId: membership.leagueId,
|
|
driverId: membership.driverId,
|
|
role: membership.role,
|
|
status: membership.status,
|
|
joinedAt:
|
|
membership.joinedAt instanceof Date
|
|
? membership.joinedAt.toISOString()
|
|
: new Date().toISOString(),
|
|
}));
|
|
|
|
byLeague.set(league.id, mapped);
|
|
}
|
|
|
|
for (const [leagueId, list] of byLeague.entries()) {
|
|
leagueMemberships.set(leagueId, list);
|
|
}
|
|
} catch (error) {
|
|
// In alpha/demo mode we tolerate failures here; callers will see empty memberships.
|
|
// eslint-disable-next-line no-console
|
|
console.error('Failed to initialize league memberships from repository', error);
|
|
}
|
|
})();
|
|
|
|
export function getMembership(leagueId: string, driverId: string): LeagueMembership | null {
|
|
const list = leagueMemberships.get(leagueId);
|
|
if (!list) return null;
|
|
return list.find((m) => m.driverId === driverId) ?? null;
|
|
}
|
|
|
|
export function getLeagueMembers(leagueId: string): LeagueMembership[] {
|
|
return [...(leagueMemberships.get(leagueId) ?? [])];
|
|
}
|
|
|
|
/**
|
|
* Derive a driver's primary league from in-memory league memberships.
|
|
* Prefers any active membership and returns the first matching league.
|
|
*/
|
|
export function getPrimaryLeagueIdForDriver(driverId: string): string | null {
|
|
for (const [leagueId, members] of leagueMemberships.entries()) {
|
|
if (members.some((m) => m.driverId === driverId && m.status === 'active')) {
|
|
return leagueId;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function isOwnerOrAdmin(leagueId: string, driverId: string): boolean {
|
|
const membership = getMembership(leagueId, driverId);
|
|
if (!membership) return false;
|
|
return membership.role === 'owner' || membership.role === 'admin';
|
|
}
|
|
|
|
export type { MembershipRole, MembershipStatus }; |