website refactor

This commit is contained in:
2026-01-14 02:02:24 +01:00
parent 8d7c709e0c
commit 4522d41aef
291 changed files with 12763 additions and 9309 deletions

View File

@@ -1,18 +1,24 @@
import React from 'react';
import Card from '@/components/ui/Card';
import { StandingEntryViewModel } from '@/lib/view-models/StandingEntryViewModel';
import { DriverViewModel } from '@/lib/view-models/DriverViewModel';
interface LeagueChampionshipStatsProps {
standings: StandingEntryViewModel[];
drivers: DriverViewModel[];
standings: Array<{
driverId: string;
position: number;
totalPoints: number;
racesFinished: number;
}>;
drivers: Array<{
id: string;
name: string;
}>;
}
export default function LeagueChampionshipStats({ standings, drivers }: LeagueChampionshipStatsProps) {
export function LeagueChampionshipStats({ standings, drivers }: LeagueChampionshipStatsProps) {
if (standings.length === 0) return null;
const leader = standings[0];
const totalRaces = Math.max(...standings.map(s => s.races), 0);
const totalRaces = Math.max(...standings.map(s => s.racesFinished), 0);
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
@@ -24,7 +30,7 @@ export default function LeagueChampionshipStats({ standings, drivers }: LeagueCh
<div>
<p className="text-xs text-gray-400 mb-1">Championship Leader</p>
<p className="font-bold text-white">{drivers.find(d => d.id === leader?.driverId)?.name || 'N/A'}</p>
<p className="text-sm text-yellow-400 font-medium">{leader?.points || 0} points</p>
<p className="text-sm text-yellow-400 font-medium">{leader?.totalPoints || 0} points</p>
</div>
</div>
</Card>

View File

@@ -3,14 +3,29 @@
import { useState, useRef, useEffect } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { Star } from 'lucide-react';
import type { DriverViewModel } from '@/lib/view-models/DriverViewModel';
import type { LeagueMembership } from '@/lib/types/LeagueMembership';
import { LeagueRoleDisplay } from '@/lib/display-objects/LeagueRoleDisplay';
import CountryFlag from '@/components/ui/CountryFlag';
import { getMediaUrl } from '@/lib/utilities/media';
import PlaceholderImage from '@/components/ui/PlaceholderImage';
// League role display data
const leagueRoleDisplay = {
owner: {
text: 'Owner',
badgeClasses: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/30',
},
admin: {
text: 'Admin',
badgeClasses: 'bg-purple-500/10 text-purple-400 border-purple-500/30',
},
steward: {
text: 'Steward',
badgeClasses: 'bg-blue-500/10 text-blue-400 border-blue-500/30',
},
member: {
text: 'Member',
badgeClasses: 'bg-primary-blue/10 text-primary-blue border-primary-blue/30',
},
} as const;
// Position background colors
const getPositionBgColor = (position: number): string => {
switch (position) {
@@ -23,7 +38,6 @@ const getPositionBgColor = (position: number): string => {
interface StandingsTableProps {
standings: Array<{
leagueId: string;
driverId: string;
position: number;
totalPoints: number;
@@ -34,19 +48,29 @@ interface StandingsTableProps {
bonusPoints: number;
teamName?: string;
}>;
drivers: DriverViewModel[];
leagueId: string;
memberships?: LeagueMembership[];
drivers: Array<{
id: string;
name: string;
avatarUrl: string | null;
iracingId?: string;
rating?: number;
country?: string;
}>;
memberships?: Array<{
driverId: string;
role: 'owner' | 'admin' | 'steward' | 'member';
joinedAt: string;
status: 'active' | 'pending' | 'banned';
}>;
currentDriverId?: string;
isAdmin?: boolean;
onRemoveMember?: (driverId: string) => void;
onUpdateRole?: (driverId: string, role: string) => void;
}
export default function StandingsTable({
export function StandingsTable({
standings,
drivers,
leagueId,
memberships = [],
currentDriverId,
isAdmin = false,
@@ -68,11 +92,11 @@ export default function StandingsTable({
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const getDriver = (driverId: string): DriverViewModel | undefined => {
const getDriver = (driverId: string) => {
return drivers.find((d) => d.id === driverId);
};
const getMembership = (driverId: string): LeagueMembership | undefined => {
const getMembership = (driverId: string) => {
return memberships.find((m) => m.driverId === driverId);
};
@@ -216,7 +240,7 @@ export default function StandingsTable({
);
};
const PointsActionMenu = ({ driverId }: { driverId: string }) => {
const PointsActionMenu = () => {
return (
<div
ref={menuRef}
@@ -280,10 +304,8 @@ export default function StandingsTable({
{standings.map((row) => {
const driver = getDriver(row.driverId);
const membership = getMembership(row.driverId);
const roleDisplay = membership ? LeagueRoleDisplay.getLeagueRoleDisplay(membership.role) : null;
const roleDisplay = membership ? leagueRoleDisplay[membership.role] : null;
const canModify = canModifyMember(row.driverId);
// TODO: Hook up real driver stats once API provides it
const driverStatsData: null = null;
const isRowHovered = hoveredRow === row.driverId;
const isMemberMenuOpen = activeMenu?.driverId === row.driverId && activeMenu?.type === 'member';
const isPointsMenuOpen = activeMenu?.driverId === row.driverId && activeMenu?.type === 'points';
@@ -292,7 +314,7 @@ export default function StandingsTable({
return (
<tr
key={`${row.leagueId}-${row.driverId}`}
key={row.driverId}
className={`border-b border-charcoal-outline/50 transition-all duration-200 ${getPositionBgColor(row.position)} ${isRowHovered ? 'bg-iron-gray/10' : ''} ${isMe ? 'ring-2 ring-primary-blue/50 ring-inset bg-primary-blue/5' : ''}`}
onMouseEnter={() => setHoveredRow(row.driverId)}
onMouseLeave={() => {
@@ -407,7 +429,7 @@ export default function StandingsTable({
</button>
)}
</div>
{isPointsMenuOpen && <PointsActionMenu driverId={row.driverId} />}
{isPointsMenuOpen && <PointsActionMenu />}
</td>
{/* Races (Finished/Started) */}