This commit is contained in:
2025-12-11 00:57:32 +01:00
parent 1303a14493
commit 6a427eab57
112 changed files with 6148 additions and 2272 deletions

View File

@@ -22,31 +22,19 @@ import {
import Button from '@/components/ui/Button';
import Input from '@/components/ui/Input';
import Heading from '@/components/ui/Heading';
import { getGetAllTeamsUseCase, getGetTeamMembersUseCase, getDriverStats } from '@/lib/di-container';
import type { Team } from '@gridpilot/racing';
import { getGetTeamsLeaderboardUseCase } from '@/lib/di-container';
import type {
TeamLeaderboardItemViewModel,
SkillLevel,
} from '@gridpilot/racing/application/presenters/ITeamsLeaderboardPresenter';
// ============================================================================
// TYPES
// ============================================================================
type SkillLevel = 'beginner' | 'intermediate' | 'advanced' | 'pro';
type SortBy = 'rating' | 'wins' | 'winRate' | 'races';
interface TeamDisplayData {
id: string;
name: string;
memberCount: number;
rating: number | null;
totalWins: number;
totalRaces: number;
performanceLevel: SkillLevel;
isRecruiting: boolean;
createdAt: Date;
description?: string;
specialization?: 'endurance' | 'sprint' | 'mixed';
region?: string;
languages?: string[];
}
type TeamDisplayData = TeamLeaderboardItemViewModel;
// ============================================================================
// SKILL LEVEL CONFIG
@@ -248,83 +236,29 @@ function TopThreePodium({ teams, onTeamClick }: TopThreePodiumProps) {
export default function TeamLeaderboardPage() {
const router = useRouter();
const [realTeams, setRealTeams] = useState<TeamDisplayData[]>([]);
const [teams, setTeams] = useState<TeamDisplayData[]>([]);
const [loading, setLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const [sortBy, setSortBy] = useState<SortBy>('rating');
const [filterLevel, setFilterLevel] = useState<SkillLevel | 'all'>('all');
useEffect(() => {
loadTeams();
const loadTeams = async () => {
try {
const useCase = getGetTeamsLeaderboardUseCase();
await useCase.execute();
const viewModel = useCase.presenter.getViewModel();
setTeams(viewModel.teams);
} catch (error) {
console.error('Failed to load teams:', error);
} finally {
setLoading(false);
}
};
void loadTeams();
}, []);
const loadTeams = async () => {
try {
const allTeamsUseCase = getGetAllTeamsUseCase();
const teamMembersUseCase = getGetTeamMembersUseCase();
await allTeamsUseCase.execute();
const allTeamsViewModel = allTeamsUseCase.presenter.getViewModel();
const allTeams = allTeamsViewModel.teams;
const teamData: TeamDisplayData[] = [];
await Promise.all(
allTeams.map(async (team: Team) => {
await teamMembersUseCase.execute({ teamId: team.id });
const membershipsViewModel = teamMembersUseCase.presenter.getViewModel();
const memberships = membershipsViewModel.members;
const memberCount = memberships.length;
let ratingSum = 0;
let ratingCount = 0;
let totalWins = 0;
let totalRaces = 0;
for (const membership of memberships) {
const stats = getDriverStats(membership.driverId);
if (!stats) continue;
if (typeof stats.rating === 'number') {
ratingSum += stats.rating;
ratingCount += 1;
}
totalWins += stats.wins ?? 0;
totalRaces += stats.totalRaces ?? 0;
}
const averageRating = ratingCount > 0 ? ratingSum / ratingCount : null;
let performanceLevel: TeamDisplayData['performanceLevel'] = 'beginner';
if (averageRating !== null) {
if (averageRating >= 4500) performanceLevel = 'pro';
else if (averageRating >= 3000) performanceLevel = 'advanced';
else if (averageRating >= 2000) performanceLevel = 'intermediate';
}
teamData.push({
id: team.id,
name: team.name,
memberCount,
rating: averageRating,
totalWins,
totalRaces,
performanceLevel,
isRecruiting: true,
createdAt: new Date(),
});
}),
);
setRealTeams(teamData);
} catch (error) {
console.error('Failed to load teams:', error);
} finally {
setLoading(false);
}
};
const teams = realTeams;
const handleTeamClick = (teamId: string) => {
if (teamId.startsWith('demo-team-')) {