diff --git a/apps/website/app/races/all/page.tsx b/apps/website/app/races/all/page.tsx index 0b536610e..10d711950 100644 --- a/apps/website/app/races/all/page.tsx +++ b/apps/website/app/races/all/page.tsx @@ -122,7 +122,9 @@ export default function RacesAllPage() { retry={fetchData} Template={({ data: _data }) => ( ('all'); + const [availabilityFilter, setAvailabilityFilter] = useState('all'); + const [sortBy, setSortBy] = useState('rating'); + + const filteredLeagues = useMemo(() => { + if (!data?.leagues) return []; + return data.leagues + .filter((league: any) => { + if (searchQuery && !league.name.toLowerCase().includes(searchQuery.toLowerCase())) { + return false; + } + if (tierFilter !== 'all' && league.tier !== tierFilter) { + return false; + } + if (availabilityFilter === 'main' && !league.mainSponsorSlot.available) { + return false; + } + if (availabilityFilter === 'secondary' && league.secondarySlots.available === 0) { + return false; + } + return true; + }) + .sort((a: any, b: any) => { + switch (sortBy) { + case 'rating': return b.rating - a.rating; + case 'drivers': return b.drivers - a.drivers; + case 'price': return a.mainSponsorSlot.price - b.mainSponsorSlot.price; + case 'views': return b.avgViewsPerRace - a.avgViewsPerRace; + default: return 0; + } + }); + }, [data?.leagues, searchQuery, tierFilter, availabilityFilter, sortBy]); + + return ( + + ); +} diff --git a/apps/website/app/sponsor/leagues/[id]/SponsorLeagueDetailPageClient.tsx b/apps/website/app/sponsor/leagues/[id]/SponsorLeagueDetailPageClient.tsx new file mode 100644 index 000000000..ec861d1e2 --- /dev/null +++ b/apps/website/app/sponsor/leagues/[id]/SponsorLeagueDetailPageClient.tsx @@ -0,0 +1,19 @@ +'use client'; + +import React, { useState } from 'react'; +import { SponsorLeagueDetailTemplate } from '@/templates/SponsorLeagueDetailTemplate'; + +export default function SponsorLeagueDetailPageClient({ data }: { data: any }) { + const [activeTab, setActiveTab] = useState<'overview' | 'drivers' | 'races' | 'sponsor'>('overview'); + const [selectedTier, setSelectedTier] = useState<'main' | 'secondary'>('main'); + + return ( + + ); +} diff --git a/apps/website/app/sponsor/leagues/[id]/page.tsx b/apps/website/app/sponsor/leagues/[id]/page.tsx index 845e6e601..c82836a60 100644 --- a/apps/website/app/sponsor/leagues/[id]/page.tsx +++ b/apps/website/app/sponsor/leagues/[id]/page.tsx @@ -1,6 +1,6 @@ import { notFound } from 'next/navigation'; import { PageWrapper } from '@/components/shared/state/PageWrapper'; -import { SponsorLeagueDetailTemplate } from '@/templates/SponsorLeagueDetailTemplate'; +import SponsorLeagueDetailPageClient from './SponsorLeagueDetailPageClient'; import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient'; import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter'; import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger'; @@ -25,5 +25,5 @@ export default async function Page({ params }: { params: { id: string } }) { if (!data) notFound(); // Data is already in the right format from API client - return ; + return ; } \ No newline at end of file diff --git a/apps/website/app/sponsor/leagues/page.tsx b/apps/website/app/sponsor/leagues/page.tsx index a06e5e52e..294c0c8bb 100644 --- a/apps/website/app/sponsor/leagues/page.tsx +++ b/apps/website/app/sponsor/leagues/page.tsx @@ -1,5 +1,5 @@ import { PageWrapper } from '@/components/shared/state/PageWrapper'; -import { SponsorLeaguesTemplate } from '@/templates/SponsorLeaguesTemplate'; +import SponsorLeaguesPageClient from './SponsorLeaguesPageClient'; import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient'; import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter'; import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger'; @@ -23,7 +23,7 @@ export default async function Page() { // Process data - move business logic to template if (!leaguesData) { - return ; + return ; } // Calculate summary stats (business logic moved from view model) @@ -42,5 +42,5 @@ export default async function Page() { stats, }; - return ; + return ; } \ No newline at end of file diff --git a/apps/website/lib/view-data/LeagueRulebookViewData.ts b/apps/website/lib/view-data/LeagueRulebookViewData.ts index 5c137483f..65fbd1ac5 100644 --- a/apps/website/lib/view-data/LeagueRulebookViewData.ts +++ b/apps/website/lib/view-data/LeagueRulebookViewData.ts @@ -16,4 +16,5 @@ export interface RulebookScoringConfig { export interface LeagueRulebookViewData { scoringConfig: RulebookScoringConfig | null; + positionPoints: Array<{ position: number; points: number }>; } diff --git a/apps/website/lib/view-data/TeamLeaderboardViewData.ts b/apps/website/lib/view-data/TeamLeaderboardViewData.ts new file mode 100644 index 000000000..1c314aa8c --- /dev/null +++ b/apps/website/lib/view-data/TeamLeaderboardViewData.ts @@ -0,0 +1,12 @@ +import type { TeamSummaryViewModel } from '../view-models/TeamSummaryViewModel'; + +export type SkillLevel = 'pro' | 'advanced' | 'intermediate' | 'beginner'; +export type SortBy = 'rating' | 'wins' | 'winRate' | 'races'; + +export interface TeamLeaderboardViewData { + teams: TeamSummaryViewModel[]; + searchQuery: string; + filterLevel: SkillLevel | 'all'; + sortBy: SortBy; + filteredAndSortedTeams: TeamSummaryViewModel[]; +} diff --git a/apps/website/lib/view-data/leagues/LeagueSponsorshipsViewData.ts b/apps/website/lib/view-data/leagues/LeagueSponsorshipsViewData.ts index 7e198d966..ac32581f0 100644 --- a/apps/website/lib/view-data/leagues/LeagueSponsorshipsViewData.ts +++ b/apps/website/lib/view-data/leagues/LeagueSponsorshipsViewData.ts @@ -1,5 +1,7 @@ export interface LeagueSponsorshipsViewData { leagueId: string; + activeTab: 'overview' | 'editor'; + onTabChange: (tab: 'overview' | 'editor') => void; league: { id: string; name: string; diff --git a/apps/website/templates/AdminDashboardTemplate.tsx b/apps/website/templates/AdminDashboardTemplate.tsx index 62c7f86af..e2e4a6f0e 100644 --- a/apps/website/templates/AdminDashboardTemplate.tsx +++ b/apps/website/templates/AdminDashboardTemplate.tsx @@ -53,7 +53,7 @@ export function AdminDashboardTemplate({ onClick={onRefresh} disabled={isLoading} variant="secondary" - icon={} + icon={} > Refresh diff --git a/apps/website/templates/AdminUsersTemplate.tsx b/apps/website/templates/AdminUsersTemplate.tsx index 3390a2992..351694bb3 100644 --- a/apps/website/templates/AdminUsersTemplate.tsx +++ b/apps/website/templates/AdminUsersTemplate.tsx @@ -65,11 +65,11 @@ export function AdminUsersTemplate({ } }; - const getRoleBadgeStyle = (role: string) => { + const getRoleBadgeProps = (role: string): { bg: string; color: string; borderColor: string } => { switch (role) { - case 'owner': return { backgroundColor: 'rgba(168, 85, 247, 0.2)', color: '#d8b4fe', border: '1px solid rgba(168, 85, 247, 0.3)' }; - case 'admin': return { backgroundColor: 'rgba(59, 130, 246, 0.2)', color: '#93c5fd', border: '1px solid rgba(59, 130, 246, 0.3)' }; - default: return { backgroundColor: 'rgba(115, 115, 115, 0.2)', color: '#d1d5db', border: '1px solid rgba(115, 115, 115, 0.3)' }; + case 'owner': return { bg: 'bg-purple-500/20', color: '#d8b4fe', borderColor: 'border-purple-500/30' }; + case 'admin': return { bg: 'bg-blue-500/20', color: '#93c5fd', borderColor: 'border-blue-500/30' }; + default: return { bg: 'bg-neutral-500/20', color: '#d1d5db', borderColor: 'border-neutral-500/30' }; } }; @@ -86,7 +86,7 @@ export function AdminUsersTemplate({ onClick={onRefresh} disabled={loading} variant="secondary" - icon={} + icon={} > Refresh @@ -117,7 +117,7 @@ export function AdminUsersTemplate({ {loading ? ( - + Loading users... ) : !viewData.users || viewData.users.length === 0 ? ( @@ -149,7 +149,7 @@ export function AdminUsersTemplate({ - + @@ -167,21 +167,18 @@ export function AdminUsersTemplate({ {user.roles.map((role, idx) => { - const style = getRoleBadgeStyle(role); + const badgeProps = getRoleBadgeProps(role); return ( {role.charAt(0).toUpperCase() + role.slice(1)} diff --git a/apps/website/templates/DriverRankingsTemplate.tsx b/apps/website/templates/DriverRankingsTemplate.tsx index 8c4c960f0..298185cac 100644 --- a/apps/website/templates/DriverRankingsTemplate.tsx +++ b/apps/website/templates/DriverRankingsTemplate.tsx @@ -43,7 +43,7 @@ export function DriverRankingsTemplate({ )} - + diff --git a/apps/website/templates/DriversTemplate.tsx b/apps/website/templates/DriversTemplate.tsx index 175f7ae1f..471bef8fa 100644 --- a/apps/website/templates/DriversTemplate.tsx +++ b/apps/website/templates/DriversTemplate.tsx @@ -4,6 +4,8 @@ import React from 'react'; import { Search, Crown, + Users, + Trophy, } from 'lucide-react'; import { Heading } from '@/ui/Heading'; import { Box } from '@/ui/Box'; @@ -20,7 +22,6 @@ import { CategoryDistribution } from '@/ui/CategoryDistribution'; import { LeaderboardPreview } from '@/ui/LeaderboardPreview'; import { RecentActivity } from '@/ui/RecentActivity'; import { PageHero } from '@/ui/PageHero'; -import { Users, Trophy } from 'lucide-react'; import { DriversSearch } from '@/ui/DriversSearch'; import { EmptyState } from '@/ui/EmptyState'; import type { DriversViewData } from '@/lib/types/view-data/DriversViewData'; diff --git a/apps/website/templates/HomeTemplate.tsx b/apps/website/templates/HomeTemplate.tsx index c31811243..0cc60d83b 100644 --- a/apps/website/templates/HomeTemplate.tsx +++ b/apps/website/templates/HomeTemplate.tsx @@ -11,7 +11,6 @@ import { CareerProgressionMockup } from '@/components/mockups/CareerProgressionM import { RaceHistoryMockup } from '@/components/mockups/RaceHistoryMockup'; import { CompanionAutomationMockup } from '@/components/mockups/CompanionAutomationMockup'; import { SimPlatformMockup } from '@/components/mockups/SimPlatformMockup'; -import { MockupStack } from '@/ui/MockupStack'; import { Card } from '@/ui/Card'; import { Button } from '@/ui/Button'; import { Box } from '@/ui/Box'; @@ -65,7 +64,7 @@ export function HomeTemplate({ viewData }: HomeTemplateProps) { description={ - Your races, your seasons, your progress — finally in one place. + Your races, your seasons, your progress — finally in one place. @@ -93,7 +92,7 @@ export function HomeTemplate({ viewData }: HomeTemplateProps) { Every race you run stays with you. - + @@ -102,7 +101,7 @@ export function HomeTemplate({ viewData }: HomeTemplateProps) { } - mockup={} + mockup={} layout="text-right" /> @@ -112,7 +111,7 @@ export function HomeTemplate({ viewData }: HomeTemplateProps) { description={ - Setting up league races used to mean clicking through iRacing's wizard 20 times. + Setting up league races used to mean clicking through iRacing's wizard 20 times. @@ -135,10 +134,10 @@ export function HomeTemplate({ viewData }: HomeTemplateProps) { description={ - Right now, we're focused on making iRacing league racing better. + Right now, we're focused on making iRacing league racing better. - But sims come and go. Your leagues, your teams, your rating — those stay. + But sims come and go. Your leagues, your teams, your rating — those stay. GridPilot is built to outlast any single platform. @@ -168,7 +167,7 @@ export function HomeTemplate({ viewData }: HomeTemplateProps) { - Featured leagues + Featured leagues - + {isEditing ? 'Edit race' : 'Add race'} @@ -156,7 +152,7 @@ export function LeagueAdminScheduleTemplate({ - + Races diff --git a/apps/website/templates/LeagueRulebookTemplate.tsx b/apps/website/templates/LeagueRulebookTemplate.tsx index 200338683..852802831 100644 --- a/apps/website/templates/LeagueRulebookTemplate.tsx +++ b/apps/website/templates/LeagueRulebookTemplate.tsx @@ -50,10 +50,7 @@ export function LeagueRulebookTemplate({ const { scoringConfig } = viewData; const primaryChampionship = scoringConfig.championships.find(c => c.type === 'driver') ?? scoringConfig.championships[0]; - const positionPoints: { position: number; points: number }[] = primaryChampionship?.pointsPreview - .filter((p) => p.sessionType === primaryChampionship.sessionTypes[0]) - .map(p => ({ position: p.position, points: p.points })) - .sort((a, b) => a.position - b.position) || []; + const positionPoints = viewData.positionPoints; return ( @@ -86,24 +83,24 @@ export function LeagueRulebookTemplate({ - + Weekend Structure & Timings - Practice + PRACTICE 20 min - Qualifying + QUALIFYING 30 min - Sprint + SPRINT - Main Race + MAIN RACE 40 min @@ -128,7 +125,7 @@ export function LeagueRulebookTemplate({ padding={3} > - + + {bonus} @@ -222,9 +219,9 @@ export function LeagueRulebookTemplate({ function StatItem({ label, value }: { label: string, value: string | number }) { return ( - - {label} - {value} + + {label.toUpperCase()} + {value} ); } @@ -245,7 +242,7 @@ function PenaltyRow({ infraction, penalty, color }: { infraction: string, penalt {infraction} - {penalty} + {penalty} ); diff --git a/apps/website/templates/LeagueSettingsTemplate.tsx b/apps/website/templates/LeagueSettingsTemplate.tsx index 4961e2e2a..cb22b22e6 100644 --- a/apps/website/templates/LeagueSettingsTemplate.tsx +++ b/apps/website/templates/LeagueSettingsTemplate.tsx @@ -10,7 +10,7 @@ import { Grid } from '@/ui/Grid'; import { GridItem } from '@/ui/GridItem'; import { Icon } from '@/ui/Icon'; import { Surface } from '@/ui/Surface'; -import { Settings, Users, Trophy, Shield, Clock } from 'lucide-react'; +import { Settings, Users, Trophy, Shield, Clock, LucideIcon } from 'lucide-react'; import type { LeagueSettingsViewData } from '@/lib/view-data/leagues/LeagueSettingsViewData'; interface LeagueSettingsTemplateProps { @@ -32,7 +32,7 @@ export function LeagueSettingsTemplate({ viewData }: LeagueSettingsTemplateProps - + @@ -48,7 +48,7 @@ export function LeagueSettingsTemplate({ viewData }: LeagueSettingsTemplateProps - + @@ -57,7 +57,7 @@ export function LeagueSettingsTemplate({ viewData }: LeagueSettingsTemplateProps - + @@ -70,7 +70,7 @@ export function LeagueSettingsTemplate({ viewData }: LeagueSettingsTemplateProps - + @@ -78,10 +78,10 @@ export function LeagueSettingsTemplate({ viewData }: LeagueSettingsTemplateProps {/* Note about forms */} - + - + Settings Management Form-based editing and ownership transfer functionality will be implemented in future updates. @@ -94,22 +94,22 @@ export function LeagueSettingsTemplate({ viewData }: LeagueSettingsTemplateProps ); } -function InfoItem({ label, value, capitalize, mono }: { label: string, value: string, capitalize?: boolean, mono?: boolean }) { +function InfoItem({ label, value, capitalize }: { label: string, value: string, capitalize?: boolean }) { return ( {label} - {value} + {capitalize ? value.toUpperCase() : value} ); } -function ConfigItem({ icon, label, value, mono }: { icon: React.ElementType, label: string, value: string | number, mono?: boolean }) { +function ConfigItem({ icon, label, value }: { icon: LucideIcon, label: string, value: string | number }) { return ( - + {label} - {value} + {value} ); diff --git a/apps/website/templates/LeagueSponsorshipsTemplate.tsx b/apps/website/templates/LeagueSponsorshipsTemplate.tsx index c1034aeb2..875d4e9fb 100644 --- a/apps/website/templates/LeagueSponsorshipsTemplate.tsx +++ b/apps/website/templates/LeagueSponsorshipsTemplate.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useState } from 'react'; +import React from 'react'; import { Card } from '@/ui/Card'; import { Box } from '@/ui/Box'; import { Stack } from '@/ui/Stack'; @@ -20,7 +20,7 @@ interface LeagueSponsorshipsTemplateProps { } export function LeagueSponsorshipsTemplate({ viewData }: LeagueSponsorshipsTemplateProps) { - const [activeTab, setActiveTab] = useState<'overview' | 'editor'>('overview'); + const activeTab = viewData.activeTab; return ( @@ -32,26 +32,36 @@ export function LeagueSponsorshipsTemplate({ viewData }: LeagueSponsorshipsTempl - - + @@ -61,7 +71,7 @@ export function LeagueSponsorshipsTemplate({ viewData }: LeagueSponsorshipsTempl - + @@ -89,7 +99,7 @@ export function LeagueSponsorshipsTemplate({ viewData }: LeagueSponsorshipsTempl - + @@ -112,7 +122,6 @@ export function LeagueSponsorshipsTemplate({ viewData }: LeagueSponsorshipsTempl key={request.id} request={{ ...request, - status: request.status as any, slotName: slot?.name || 'Unknown slot' }} /> @@ -127,7 +136,7 @@ export function LeagueSponsorshipsTemplate({ viewData }: LeagueSponsorshipsTempl - + diff --git a/apps/website/templates/LeagueStandingsTemplate.tsx b/apps/website/templates/LeagueStandingsTemplate.tsx index 923259e10..941a457b2 100644 --- a/apps/website/templates/LeagueStandingsTemplate.tsx +++ b/apps/website/templates/LeagueStandingsTemplate.tsx @@ -4,7 +4,6 @@ import React from 'react'; import { LeagueChampionshipStats } from '@/components/leagues/LeagueChampionshipStats'; import { StandingsTable } from '@/components/leagues/StandingsTable'; import { Card } from '@/ui/Card'; -import { Box } from '@/ui/Box'; import { Stack } from '@/ui/Stack'; import { Text } from '@/ui/Text'; import { Heading } from '@/ui/Heading'; diff --git a/apps/website/templates/LeagueWalletTemplate.tsx b/apps/website/templates/LeagueWalletTemplate.tsx index 5bf0c90f9..f74b40f75 100644 --- a/apps/website/templates/LeagueWalletTemplate.tsx +++ b/apps/website/templates/LeagueWalletTemplate.tsx @@ -31,7 +31,7 @@ export function LeagueWalletTemplate({ viewData }: LeagueWalletTemplateProps) { {/* Balance Card */} - + @@ -47,7 +47,7 @@ export function LeagueWalletTemplate({ viewData }: LeagueWalletTemplateProps) { - + @@ -75,7 +75,7 @@ export function LeagueWalletTemplate({ viewData }: LeagueWalletTemplateProps) { - + @@ -90,10 +90,10 @@ export function LeagueWalletTemplate({ viewData }: LeagueWalletTemplateProps) { {/* Note about features */} - + - + Wallet Management Interactive withdrawal and export features will be implemented in future updates. diff --git a/apps/website/templates/ProfileLeaguesTemplate.tsx b/apps/website/templates/ProfileLeaguesTemplate.tsx index 9dd9c8551..ebad7940b 100644 --- a/apps/website/templates/ProfileLeaguesTemplate.tsx +++ b/apps/website/templates/ProfileLeaguesTemplate.tsx @@ -39,7 +39,7 @@ export function ProfileLeaguesTemplate({ viewData }: ProfileLeaguesTemplateProps {viewData.ownedLeagues.length === 0 ? ( - You don't own any leagues yet in this session. + You don't own any leagues yet in this session. ) : ( @@ -55,7 +55,7 @@ export function ProfileLeaguesTemplate({ viewData }: ProfileLeaguesTemplateProps - Leagues you're in + Leagues you're in {viewData.memberLeagues.length > 0 && ( {viewData.memberLeagues.length} {viewData.memberLeagues.length === 1 ? 'league' : 'leagues'} @@ -65,7 +65,7 @@ export function ProfileLeaguesTemplate({ viewData }: ProfileLeaguesTemplateProps {viewData.memberLeagues.length === 0 ? ( - You're not a member of any other leagues yet. + You're not a member of any other leagues yet. ) : ( diff --git a/apps/website/templates/ProfileTemplate.tsx b/apps/website/templates/ProfileTemplate.tsx index 6a244f6f4..b88730482 100644 --- a/apps/website/templates/ProfileTemplate.tsx +++ b/apps/website/templates/ProfileTemplate.tsx @@ -7,7 +7,7 @@ import { ProfileSettings } from '@/components/drivers/ProfileSettings'; import { AchievementGrid } from '@/ui/AchievementGrid'; import { ProfileHero } from '@/ui/ProfileHero'; import { ProfileStatGrid } from '@/ui/ProfileStatGrid'; -import { ProfileTabs } from '@/ui/ProfileTabs'; +import { ProfileTabs, type ProfileTab as ProfileTabsType } from '@/ui/ProfileTabs'; import { TeamMembershipGrid } from '@/ui/TeamMembershipGrid'; import type { ProfileViewData } from '@/lib/view-data/ProfileViewData'; import { Box } from '@/ui/Box'; @@ -74,7 +74,7 @@ export function ProfileTemplate({ Create your driver profile to join leagues, compete in races, and connect with other drivers. - + {}} isPending={false} /> @@ -150,7 +150,7 @@ export function ProfileTemplate({ stats={viewData.stats ? { rating: Number(viewData.stats.ratingLabel) || 0 } : null} globalRank={Number(viewData.stats?.globalRankLabel) || 0} timezone={viewData.extendedProfile?.timezone || 'UTC'} - socialHandles={viewData.extendedProfile?.socialHandles.map(s => ({ ...s, platform: s.platformLabel as any })) || []} + socialHandles={viewData.extendedProfile?.socialHandles.map(s => ({ ...s, platform: s.platformLabel })) || []} onAddFriend={onFriendRequestSend} friendRequestSent={friendRequestSent} /> @@ -176,7 +176,7 @@ export function ProfileTemplate({ /> )} - + void} /> {activeTab === 'history' && ( diff --git a/apps/website/templates/RaceResultsTemplate.tsx b/apps/website/templates/RaceResultsTemplate.tsx index 70310eab0..453929b91 100644 --- a/apps/website/templates/RaceResultsTemplate.tsx +++ b/apps/website/templates/RaceResultsTemplate.tsx @@ -12,20 +12,19 @@ import { Container } from '@/ui/Container'; import { Grid } from '@/ui/Grid'; import { Icon } from '@/ui/Icon'; import { Surface } from '@/ui/Surface'; -import { ArrowLeft, Trophy, Zap } from 'lucide-react'; +import { ArrowLeft, Trophy, Zap, type LucideIcon } from 'lucide-react'; import type { RaceResultsViewData } from '@/lib/view-data/races/RaceResultsViewData'; import { RaceResultRow } from '@/ui/RaceResultRow'; import { RacePenaltyRow } from '@/ui/RacePenaltyRowWrapper'; export interface RaceResultsTemplateProps { viewData: RaceResultsViewData; - currentDriverId: string; isAdmin: boolean; isLoading: boolean; error?: Error | null; // Actions onBack: () => void; - onImportResults: (results: any[]) => void; + onImportResults: (results: unknown[]) => void; onPenaltyClick: (driver: { id: string; name: string }) => void; // UI State importing: boolean; @@ -37,7 +36,6 @@ export interface RaceResultsTemplateProps { export function RaceResultsTemplate({ viewData, - currentDriverId, isLoading, error, onBack, @@ -114,9 +112,9 @@ export function RaceResultsTemplate({ {/* Header */} - + - + @@ -131,20 +129,20 @@ export function RaceResultsTemplate({ - - + + {importSuccess && ( - + Success! Results imported and standings updated. )} {importError && ( - + Error: {importError} @@ -158,7 +156,7 @@ export function RaceResultsTemplate({ {viewData.results.map((result) => ( ))} @@ -166,13 +164,13 @@ export function RaceResultsTemplate({ {/* Penalties Section */} {viewData.penalties.length > 0 && ( - + Penalties {viewData.penalties.map((penalty, index) => ( - + ))} @@ -215,13 +213,13 @@ export function RaceResultsTemplate({ ); } -function StatItem({ label, value, icon, color = 'text-white' }: { label: string, value: string | number, icon?: any, color?: string }) { +function StatItem({ label, value, icon, color = 'text-white' }: { label: string, value: string | number, icon?: LucideIcon, color?: string }) { return ( - + {label} {icon && } - {value} + {value} ); diff --git a/apps/website/templates/RaceStewardingTemplate.tsx b/apps/website/templates/RaceStewardingTemplate.tsx index 2ea3bf11e..421a0df5b 100644 --- a/apps/website/templates/RaceStewardingTemplate.tsx +++ b/apps/website/templates/RaceStewardingTemplate.tsx @@ -44,7 +44,6 @@ interface RaceStewardingTemplateProps { export function RaceStewardingTemplate({ viewData, isLoading, - error, onBack, onReviewProtest, isAdmin, @@ -77,9 +76,9 @@ export function RaceStewardingTemplate({ - + Race not found - The race you're looking for doesn't exist. + The race you're looking for doesn't exist. @@ -173,20 +181,19 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem {/* Tabs */} - + {(['overview', 'drivers', 'races', 'sponsor'] as const).map((tab) => ( setActiveTab(tab)} pb={3} - style={{ - cursor: 'pointer', - borderBottom: activeTab === tab ? '2px solid #3b82f6' : '2px solid transparent', - color: activeTab === tab ? '#3b82f6' : '#9ca3af' - }} + cursor="pointer" + borderBottom={activeTab === tab} + borderColor={activeTab === tab ? 'border-primary-blue' : 'border-transparent'} + color={activeTab === tab ? 'text-primary-blue' : 'text-gray-400'} > - + {tab === 'sponsor' ? '🎯 Become a Sponsor' : tab} @@ -235,10 +242,10 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem Next Race - + - + @@ -259,16 +266,16 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem {activeTab === 'drivers' && ( - + Championship Standings Top drivers carrying sponsor branding {viewData.drivers.map((driver, index) => ( - + - + {driver.position} @@ -277,11 +284,11 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem - + {driver.races} races - + {driver.formattedImpressions} views @@ -295,16 +302,16 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem {activeTab === 'races' && ( - + Race Calendar Season schedule with view statistics {viewData.races.map((race, index) => ( - + - + {race.name} {race.formattedDate} @@ -312,7 +319,7 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem {race.status === 'completed' ? ( - + {race.views.toLocaleString()} views @@ -361,7 +368,7 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem - + Total (excl. VAT) @@ -391,11 +398,11 @@ export function SponsorLeagueDetailTemplate({ viewData }: SponsorLeagueDetailTem ); } -function StatCard({ icon, label, value, color }: { icon: any, label: string, value: string | number, color: string }) { +function StatCard({ icon, label, value, color }: { icon: LucideIcon, label: string, value: string | number, color: string }) { return ( - + @@ -409,10 +416,10 @@ function StatCard({ icon, label, value, color }: { icon: any, label: string, val function InfoRow({ label, value, color = 'text-white', last }: { label: string, value: string | number, color?: string, last?: boolean }) { return ( - + {label} - {value} + {value} ); diff --git a/apps/website/templates/SponsorLeaguesTemplate.tsx b/apps/website/templates/SponsorLeaguesTemplate.tsx index b9ca1de17..40e165917 100644 --- a/apps/website/templates/SponsorLeaguesTemplate.tsx +++ b/apps/website/templates/SponsorLeaguesTemplate.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useState, useMemo } from 'react'; +import React from 'react'; import { Card } from '@/ui/Card'; import { Button } from '@/ui/Button'; import { Heading } from '@/ui/Heading'; @@ -17,13 +17,13 @@ import { Trophy, Users, Search, - ChevronRight, Car, Megaphone, } from 'lucide-react'; import { siteConfig } from '@/lib/siteConfig'; import { routes } from '@/lib/routing/RouteConfig'; import { AvailableLeagueCard } from '@/components/sponsors/AvailableLeagueCard'; +import { Input } from '@/ui/Input'; interface AvailableLeague { id: string; @@ -43,58 +43,34 @@ interface AvailableLeague { cpm: number; } -type SortOption = 'rating' | 'drivers' | 'price' | 'views'; -type TierFilter = 'all' | 'premium' | 'standard' | 'starter'; -type AvailabilityFilter = 'all' | 'main' | 'secondary'; +export type SortOption = 'rating' | 'drivers' | 'price' | 'views'; +export type TierFilter = 'all' | 'premium' | 'standard' | 'starter'; +export type AvailabilityFilter = 'all' | 'main' | 'secondary'; -interface SponsorLeaguesTemplateProps { - viewData: { - leagues: AvailableLeague[]; - stats: { - total: number; - mainAvailable: number; - secondaryAvailable: number; - totalDrivers: number; - avgCpm: number; - }; +interface SponsorLeaguesViewData { + leagues: AvailableLeague[]; + stats: { + total: number; + mainAvailable: number; + secondaryAvailable: number; + totalDrivers: number; + avgCpm: number; }; } -export function SponsorLeaguesTemplate({ viewData }: SponsorLeaguesTemplateProps) { - const [searchQuery, setSearchQuery] = useState(''); - const [tierFilter, setTierFilter] = useState('all'); - const [availabilityFilter, setAvailabilityFilter] = useState('all'); - const [sortBy, setSortBy] = useState('rating'); - - // Filter and sort leagues - const filteredLeagues = useMemo(() => { - return viewData.leagues - .filter((league) => { - if (searchQuery && !league.name.toLowerCase().includes(searchQuery.toLowerCase())) { - return false; - } - if (tierFilter !== 'all' && league.tier !== tierFilter) { - return false; - } - if (availabilityFilter === 'main' && !league.mainSponsorSlot.available) { - return false; - } - if (availabilityFilter === 'secondary' && league.secondarySlots.available === 0) { - return false; - } - return true; - }) - .sort((a, b) => { - switch (sortBy) { - case 'rating': return b.rating - a.rating; - case 'drivers': return b.drivers - a.drivers; - case 'price': return a.mainSponsorSlot.price - b.mainSponsorSlot.price; - case 'views': return b.avgViewsPerRace - a.avgViewsPerRace; - default: return 0; - } - }); - }, [viewData.leagues, searchQuery, tierFilter, availabilityFilter, sortBy]); +interface SponsorLeaguesTemplateProps { + viewData: SponsorLeaguesViewData; + filteredLeagues: AvailableLeague[]; + searchQuery: string; + setSearchQuery: (query: string) => void; +} +export function SponsorLeaguesTemplate({ + viewData, + filteredLeagues, + searchQuery, + setSearchQuery, +}: SponsorLeaguesTemplateProps) { const stats = viewData.stats; return ( @@ -138,12 +114,11 @@ export function SponsorLeaguesTemplate({ viewData }: SponsorLeaguesTemplateProps - setSearchQuery(e.target.value)} - className="w-full px-4 py-2 rounded-lg border border-charcoal-outline bg-iron-gray text-white placeholder-gray-500 focus:border-primary-blue focus:outline-none" + icon={} /> {/* Selects would go here, using standard Select UI if available */} @@ -175,7 +150,7 @@ export function SponsorLeaguesTemplate({ viewData }: SponsorLeaguesTemplateProps {filteredLeagues.map((league) => ( - + ))} @@ -185,14 +160,12 @@ export function SponsorLeaguesTemplate({ viewData }: SponsorLeaguesTemplateProps - + No leagues found Try adjusting your filters to see more results @@ -201,7 +174,7 @@ export function SponsorLeaguesTemplate({ viewData }: SponsorLeaguesTemplateProps )} {/* Platform Fee Notice */} - + @@ -220,8 +193,8 @@ export function SponsorLeaguesTemplate({ viewData }: SponsorLeaguesTemplateProps function StatCard({ label, value, color = 'text-white' }: { label: string, value: string | number, color?: string }) { return ( - - {value} + + {value} {label} diff --git a/apps/website/templates/SponsorshipRequestsTemplate.tsx b/apps/website/templates/SponsorshipRequestsTemplate.tsx index 8d8c7871f..6a5c2abec 100644 --- a/apps/website/templates/SponsorshipRequestsTemplate.tsx +++ b/apps/website/templates/SponsorshipRequestsTemplate.tsx @@ -55,7 +55,7 @@ export function SponsorshipRequestsTemplate({ padding={4} > - + {request.sponsorName} {request.message && ( {request.message} diff --git a/apps/website/templates/StewardingTemplate.tsx b/apps/website/templates/StewardingTemplate.tsx index a408968e2..b1f0dffb4 100644 --- a/apps/website/templates/StewardingTemplate.tsx +++ b/apps/website/templates/StewardingTemplate.tsx @@ -30,9 +30,9 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { {/* Stats summary */} - - - + + + {/* Content */} @@ -41,7 +41,7 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { - + All Clear! No protests or penalties to review. @@ -54,10 +54,11 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { variant="muted" rounded="lg" border - style={{ overflow: 'hidden', borderColor: '#262626' }} + borderColor="border-neutral-800" + overflow="hidden" > {/* Race Header */} - + @@ -67,7 +68,7 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { {new Date(race.scheduledAt).toLocaleDateString()} - + {race.pendingProtests.length} pending @@ -77,7 +78,7 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { {race.pendingProtests.length === 0 && race.resolvedProtests.length === 0 && race.penalties.length === 0 ? ( - No items to display + No items to display ) : ( @@ -92,16 +93,17 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { rounded="lg" border padding={4} - style={{ backgroundColor: 'rgba(38, 38, 38, 0.3)', borderColor: '#262626' }} + bg="bg-neutral-800/30" + borderColor="border-neutral-800" > - + {protester?.name || 'Unknown'} vs {accused?.name || 'Unknown'} - + Pending @@ -127,27 +129,28 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { rounded="lg" border padding={4} - style={{ backgroundColor: 'rgba(38, 38, 38, 0.3)', borderColor: '#262626' }} + bg="bg-neutral-800/30" + borderColor="border-neutral-800" > - + {driver?.name || 'Unknown'} - - - {penalty.type.replace('_', ' ')} + + + {penalty.type.replace('_', ' ').toUpperCase()} {penalty.reason} - - + + {penalty.type === 'time_penalty' && `+${penalty.value}s`} {penalty.type === 'grid_penalty' && `+${penalty.value} grid`} {penalty.type === 'points_deduction' && `-${penalty.value} pts`} @@ -175,9 +178,11 @@ export function StewardingTemplate({ viewData }: StewardingTemplateProps) { function StatItem({ label, value, color }: { label: string, value: string | number, color: string }) { return ( - - {value} - {label} + + + {value} + {label} + ); } diff --git a/apps/website/templates/TeamDetailTemplate.tsx b/apps/website/templates/TeamDetailTemplate.tsx index c1607e3ea..fc7ea8d5d 100644 --- a/apps/website/templates/TeamDetailTemplate.tsx +++ b/apps/website/templates/TeamDetailTemplate.tsx @@ -49,6 +49,8 @@ export function TeamDetailTemplate({ }: TeamDetailTemplateProps) { const isSponsorMode = useSponsorMode(); + const team = viewData.team; + // Show loading state if (loading) { return ( @@ -61,7 +63,7 @@ export function TeamDetailTemplate({ } // Show not found state - if (!viewData.team) { + if (!team) { return ( @@ -69,7 +71,7 @@ export function TeamDetailTemplate({ Team Not Found - The team you're looking for doesn't exist or has been disbanded. + The team you're looking for doesn't exist or has been disbanded. {/* Back to Login */} - - + + Back to Login @@ -119,10 +119,10 @@ export function ForgotPasswordTemplate({ viewData, formActions, mutationState }: - + ) : ( - - + + @@ -130,8 +130,8 @@ export function ForgotPasswordTemplate({ viewData, formActions, mutationState }: {viewData.magicLink && ( Development Mode - Magic Link: - - {viewData.magicLink} + + {viewData.magicLink} In production, this would be sent via email @@ -167,7 +167,7 @@ export function ForgotPasswordTemplate({ viewData, formActions, mutationState }: {/* Footer */} - + Need help?{' '} diff --git a/apps/website/templates/auth/LoginTemplate.tsx b/apps/website/templates/auth/LoginTemplate.tsx index a268504a2..be307676f 100644 --- a/apps/website/templates/auth/LoginTemplate.tsx +++ b/apps/website/templates/auth/LoginTemplate.tsx @@ -25,6 +25,7 @@ import { LoadingSpinner } from '@/ui/LoadingSpinner'; import { EnhancedFormError } from '@/components/errors/EnhancedFormError'; import { UserRolesPreview } from '@/components/auth/UserRolesPreview'; import { AuthWorkflowMockup } from '@/components/auth/AuthWorkflowMockup'; +import { routes } from '@/lib/routing/RouteConfig'; import { LoginViewData } from '@/lib/builders/view-data/types/LoginViewData'; import { FormState } from '@/lib/builders/view-data/types/FormState'; @@ -45,16 +46,16 @@ interface LoginTemplateProps { export function LoginTemplate({ viewData, formActions, mutationState }: LoginTemplateProps) { return ( - + {/* Background Pattern */} - + {/* Left Side - Info Panel (Hidden on mobile) */} - - + + {/* Logo */} - + GridPilot @@ -90,11 +91,11 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem {/* Right Side - Login Form */} - - + + {/* Mobile Logo/Header */} - - + + Welcome Back @@ -104,26 +105,26 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem {/* Desktop Header */} - + Welcome Back Sign in to access your racing dashboard - + {/* Background accent */} - + -
- + + {/* Email */} Email Address - + @@ -152,12 +152,12 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem Password - + Forgot password? - + formActions.setShowPassword(!viewData.showPassword)} - style={{ position: 'absolute', right: '0.75rem', top: '50%', transform: 'translateY(-50%)', zIndex: 10, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }} + position="absolute" + right="3" + top="50%" + zIndex={10} + bg="transparent" + borderStyle="none" + cursor="pointer" > @@ -190,27 +195,27 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem {/* Remember Me */} - Keep me signed in {/* Insufficient Permissions Message */} {viewData.hasInsufficientPermissions && ( - + Insufficient Permissions - You don't have permission to access that page. Please log in with an account that has the required role. + You don't have permission to access that page. Please log in with an account that has the required role. @@ -239,24 +244,24 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem {mutationState.isPending || viewData.formState.isSubmitting ? 'Signing in...' : 'Sign In'} -
+
{/* Divider */} - - - + + + - - + + or continue with {/* Sign Up Link */} - + - Don't have an account?{' '} + Don't have an account?{' '} @@ -268,18 +273,18 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem {/* Name Immutability Notice */} - + - Note: Your display name cannot be changed after signup. Please ensure it's correct when creating your account. + Note: Your display name cannot be changed after signup. Please ensure it's correct when creating your account. {/* Footer */} - + By signing in, you agree to our{' '} @@ -293,7 +298,7 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem {/* Mobile Role Info */} - + diff --git a/apps/website/templates/auth/ResetPasswordTemplate.tsx b/apps/website/templates/auth/ResetPasswordTemplate.tsx index 5319cca39..317cca2cc 100644 --- a/apps/website/templates/auth/ResetPasswordTemplate.tsx +++ b/apps/website/templates/auth/ResetPasswordTemplate.tsx @@ -22,9 +22,11 @@ import { Link } from '@/ui/Link'; import { Surface } from '@/ui/Surface'; import { Icon } from '@/ui/Icon'; import { LoadingSpinner } from '@/ui/LoadingSpinner'; +import { routes } from '@/lib/routing/RouteConfig'; import { ResetPasswordViewData } from '@/lib/builders/view-data/types/ResetPasswordViewData'; -interface ResetPasswordTemplateProps extends ResetPasswordViewData { +interface ResetPasswordTemplateProps { + viewData: ResetPasswordViewData; formActions: { handleChange: (e: React.ChangeEvent) => void; handleSubmit: (e: React.FormEvent) => Promise; @@ -42,18 +44,21 @@ interface ResetPasswordTemplateProps extends ResetPasswordViewData { }; } -export function ResetPasswordTemplate(props: ResetPasswordTemplateProps) { - const { formActions, uiState, mutationState, ...viewData } = props; - +export function ResetPasswordTemplate({ + viewData, + formActions, + uiState, + mutationState, +}: ResetPasswordTemplateProps) { return ( - + {/* Background Pattern */} - + - + {/* Header */} - - + + Reset Password @@ -62,20 +67,20 @@ export function ResetPasswordTemplate(props: ResetPasswordTemplateProps) { - + {/* Background accent */} - + {!viewData.showSuccess ? ( -
- + + {/* New Password */} New Password - + formActions.setShowPassword(!uiState.showPassword)} - style={{ position: 'absolute', right: '0.75rem', top: '50%', transform: 'translateY(-50%)', zIndex: 10, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }} + position="absolute" + right="3" + top="50%" + zIndex={10} + bg="transparent" + borderStyle="none" + cursor="pointer" > @@ -112,7 +122,7 @@ export function ResetPasswordTemplate(props: ResetPasswordTemplateProps) { Confirm Password - + formActions.setShowConfirmPassword(!uiState.showConfirmPassword)} - style={{ position: 'absolute', right: '0.75rem', top: '50%', transform: 'translateY(-50%)', zIndex: 10, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }} + position="absolute" + right="3" + top="50%" + zIndex={10} + bg="transparent" + borderStyle="none" + cursor="pointer" > @@ -145,7 +160,7 @@ export function ResetPasswordTemplate(props: ResetPasswordTemplateProps) { {/* Error Message */} {mutationState.error && ( - + {mutationState.error} @@ -165,8 +180,8 @@ export function ResetPasswordTemplate(props: ResetPasswordTemplateProps) { {/* Back to Login */} - - + + Back to Login @@ -174,10 +189,10 @@ export function ResetPasswordTemplate(props: ResetPasswordTemplateProps) { - + ) : ( - - + + @@ -214,7 +229,7 @@ export function ResetPasswordTemplate(props: ResetPasswordTemplateProps) { {/* Footer */} - + Need help?{' '} diff --git a/apps/website/templates/auth/SignupTemplate.tsx b/apps/website/templates/auth/SignupTemplate.tsx index ab05ee497..7ee2400e2 100644 --- a/apps/website/templates/auth/SignupTemplate.tsx +++ b/apps/website/templates/auth/SignupTemplate.tsx @@ -56,18 +56,21 @@ const USER_ROLES = [ title: 'Driver', description: 'Race, track stats, join teams', color: '#3b82f6', + bg: 'bg-blue-500/10', }, { icon: Trophy, title: 'League Admin', description: 'Organize leagues and events', color: '#10b981', + bg: 'bg-green-500/10', }, { icon: Users, title: 'Team Manager', description: 'Manage team and drivers', color: '#a855f7', + bg: 'bg-purple-500/10', }, ]; @@ -91,16 +94,16 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } ]; return ( - + {/* Background Pattern */} - + {/* Left Side - Info Panel (Hidden on mobile) */} - - + + {/* Logo */} - + GridPilot @@ -123,10 +126,11 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } rounded="xl" border padding={4} - style={{ backgroundColor: 'rgba(38, 38, 38, 0.3)', borderColor: '#262626' }} + bg="bg-neutral-800/30" + borderColor="border-neutral-800" > - + @@ -140,7 +144,7 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } {/* Features List */} - + What you'll get @@ -168,11 +172,11 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } {/* Right Side - Signup Form */} - - + + {/* Mobile Logo/Header */} - - + + Join GridPilot @@ -182,26 +186,26 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } {/* Desktop Header */} - + Create Account Get started with your free account - + {/* Background accent */} - + -
- + + {/* First Name */} First Name - + @@ -230,7 +233,7 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } Last Name - + @@ -255,11 +257,11 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } {/* Name Immutability Warning */} - + - Important: Your name cannot be changed after signup. Please ensure it's correct. + Important: Your name cannot be changed after signup. Please ensure it's correct. @@ -270,7 +272,7 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } Email Address - + @@ -299,7 +300,7 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } Password - + formActions.setShowPassword(!uiState.showPassword)} - style={{ position: 'absolute', right: '0.75rem', top: '50%', transform: 'translateY(-50%)', zIndex: 10, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }} + position="absolute" + right="3" + top="50%" + zIndex={10} + bg="transparent" + borderStyle="none" + cursor="pointer" > @@ -333,14 +339,14 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } {viewData.formState.fields.password.value && ( - - + + - + {passwordStrength.label} - + {passwordRequirements.map((req, index) => ( @@ -360,7 +366,7 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } Confirm Password - + formActions.setShowConfirmPassword(!uiState.showConfirmPassword)} - style={{ position: 'absolute', right: '0.75rem', top: '50%', transform: 'translateY(-50%)', zIndex: 10, backgroundColor: 'transparent', border: 'none', cursor: 'pointer' }} + position="absolute" + right="3" + top="50%" + zIndex={10} + bg="transparent" + borderStyle="none" + cursor="pointer" > @@ -408,22 +419,22 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } {mutationState.isPending ? 'Creating account...' : 'Create Account'} - + {/* Divider */} - - - + + + - - + + or continue with {/* Login Link */} - + Already have an account?{' '} {/* Footer */} - + By creating an account, you agree to our{' '} @@ -450,12 +461,12 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState } {/* Mobile Role Info */} - - One account for all roles + + One account for all roles {USER_ROLES.map((role) => ( - + {role.title} diff --git a/apps/website/ui/RaceListItemWrapper.tsx b/apps/website/ui/RaceListItemWrapper.tsx index 1269ea9fe..82f04e3a9 100644 --- a/apps/website/ui/RaceListItemWrapper.tsx +++ b/apps/website/ui/RaceListItemWrapper.tsx @@ -11,8 +11,8 @@ interface Race { scheduledAt: string; status: 'scheduled' | 'running' | 'completed' | 'cancelled'; sessionType: string; - leagueId?: string; - leagueName?: string; + leagueId?: string | null; + leagueName?: string | null; strengthOfField?: number | null; }