This commit is contained in:
2025-12-04 23:31:55 +01:00
parent 9fa21a488a
commit fb509607c1
96 changed files with 5839 additions and 1609 deletions

View File

@@ -1,15 +1,16 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import Link from 'next/link';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
import DriverIdentity from '@/components/drivers/DriverIdentity';
import type { DriverDTO } from '@gridpilot/racing/application/dto/DriverDTO';
import { EntityMappers } from '@gridpilot/racing/application/mappers/EntityMappers';
import { getDriverRepository, getDriverStats } from '@/lib/di-container';
import {
getLeagueMembers,
getCurrentDriverId,
type LeagueMembership,
type MembershipRole,
} from '@/lib/racingLegacyFacade';
} from '@/lib/leagueMembership';
import { useEffectiveDriverId } from '@/lib/currentDriver';
interface LeagueMembersProps {
leagueId: string;
@@ -18,17 +19,17 @@ interface LeagueMembersProps {
showActions?: boolean;
}
export default function LeagueMembers({
leagueId,
onRemoveMember,
export default function LeagueMembers({
leagueId,
onRemoveMember,
onUpdateRole,
showActions = false
showActions = false
}: LeagueMembersProps) {
const [members, setMembers] = useState<LeagueMembership[]>([]);
const [drivers, setDrivers] = useState<Driver[]>([]);
const [driversById, setDriversById] = useState<Record<string, DriverDTO>>({});
const [loading, setLoading] = useState(true);
const [sortBy, setSortBy] = useState<'role' | 'name' | 'date' | 'rating' | 'points' | 'wins'>('rating');
const currentDriverId = getCurrentDriverId();
const currentDriverId = useEffectiveDriverId();
const loadMembers = useCallback(async () => {
setLoading(true);
@@ -37,10 +38,18 @@ export default function LeagueMembers({
setMembers(membershipData);
const driverRepo = getDriverRepository();
const driverData = await Promise.all(
membershipData.map(m => driverRepo.findById(m.driverId))
const driverEntities = await Promise.all(
membershipData.map((m) => driverRepo.findById(m.driverId))
);
setDrivers(driverData.filter((d): d is Driver => d !== null));
const driverDtos = driverEntities
.map((driver) => (driver ? EntityMappers.toDriverDTO(driver) : null))
.filter((dto): dto is DriverDTO => dto !== null);
const byId: Record<string, DriverDTO> = {};
for (const dto of driverDtos) {
byId[dto.id] = dto;
}
setDriversById(byId);
} catch (error) {
console.error('Failed to load members:', error);
} finally {
@@ -53,7 +62,7 @@ export default function LeagueMembers({
}, [loadMembers]);
const getDriverName = (driverId: string): string => {
const driver = drivers.find(d => d.id === driverId);
const driver = driversById[driverId];
return driver?.name || 'Unknown Driver';
};
@@ -160,6 +169,13 @@ export default function LeagueMembers({
const cannotModify = member.role === 'owner';
const driverStats = getDriverStats(member.driverId);
const isTopPerformer = index < 3 && sortBy === 'rating';
const driver = driversById[member.driverId];
const roleLabel =
member.role.charAt(0).toUpperCase() + member.role.slice(1);
const ratingAndWinsMeta =
driverStats && typeof driverStats.rating === 'number'
? `Rating ${driverStats.rating}${driverStats.wins ?? 0} wins`
: null;
return (
<tr
@@ -168,12 +184,17 @@ export default function LeagueMembers({
>
<td className="py-3 px-4">
<div className="flex items-center gap-2">
<Link
href={`/drivers/${member.driverId}?from=league&leagueId=${leagueId}`}
className="text-white font-medium hover:text-primary-blue transition-colors"
>
{getDriverName(member.driverId)}
</Link>
{driver ? (
<DriverIdentity
driver={driver}
href={`/drivers/${member.driverId}?from=league-members&leagueId=${leagueId}`}
contextLabel={roleLabel}
meta={ratingAndWinsMeta}
size="md"
/>
) : (
<span className="text-white">Unknown Driver</span>
)}
{isCurrentUser && (
<span className="text-xs text-gray-500">(You)</span>
)}