This commit is contained in:
2025-12-11 21:06:25 +01:00
parent c49ea2598d
commit ec3ddc3a5c
227 changed files with 3496 additions and 2083 deletions

View File

@@ -93,8 +93,11 @@ export default function RacesPage() {
// Group races by date for calendar view
const racesByDate = useMemo(() => {
const grouped = new Map<string, RaceListItemViewModel[]>();
filteredRaces.forEach(race => {
const dateKey = new Date(race.scheduledAt).toISOString().split('T')[0];
filteredRaces.forEach((race) => {
if (typeof race.scheduledAt !== 'string') {
return;
}
const dateKey = race.scheduledAt.split('T')[0]!;
if (!grouped.has(dateKey)) {
grouped.set(dateKey, []);
}
@@ -108,23 +111,26 @@ export default function RacesPage() {
const recentResults = pageData?.recentResults ?? [];
const stats = pageData?.stats ?? { total: 0, scheduled: 0, running: 0, completed: 0 };
const formatDate = (date: Date) => {
return new Date(date).toLocaleDateString('en-US', {
const formatDate = (date: Date | string) => {
const d = typeof date === 'string' ? new Date(date) : date;
return d.toLocaleDateString('en-US', {
weekday: 'short',
month: 'short',
day: 'numeric',
});
};
const formatTime = (date: Date) => {
return new Date(date).toLocaleTimeString('en-US', {
const formatTime = (date: Date | string) => {
const d = typeof date === 'string' ? new Date(date) : date;
return d.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
});
};
const formatFullDate = (date: Date) => {
return new Date(date).toLocaleDateString('en-US', {
const formatFullDate = (date: Date | string) => {
const d = typeof date === 'string' ? new Date(date) : date;
return d.toLocaleDateString('en-US', {
weekday: 'long',
month: 'long',
day: 'numeric',
@@ -132,9 +138,10 @@ export default function RacesPage() {
});
};
const getRelativeTime = (date: Date) => {
const getRelativeTime = (date?: Date | string) => {
if (!date) return '';
const now = new Date();
const targetDate = new Date(date);
const targetDate = typeof date === 'string' ? new Date(date) : date;
const diffMs = targetDate.getTime() - now.getTime();
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
@@ -144,7 +151,7 @@ export default function RacesPage() {
if (diffHours < 24) return `In ${diffHours}h`;
if (diffDays === 1) return 'Tomorrow';
if (diffDays < 7) return `In ${diffDays} days`;
return formatDate(date);
return formatDate(targetDate);
};
const statusConfig = {
@@ -368,6 +375,9 @@ export default function RacesPage() {
{/* Races for this date */}
<div className="space-y-2">
{dayRaces.map((race) => {
if (!race.scheduledAt) {
return null;
}
const config = statusConfig[race.status];
const StatusIcon = config.icon;
@@ -385,9 +395,13 @@ export default function RacesPage() {
<div className="flex items-start gap-4">
{/* Time Column */}
<div className="flex-shrink-0 text-center min-w-[60px]">
<p className="text-lg font-bold text-white">{formatTime(new Date(race.scheduledAt))}</p>
<p className="text-lg font-bold text-white">
{formatTime(race.scheduledAt)}
</p>
<p className={`text-xs ${config.color}`}>
{race.status === 'running' ? 'LIVE' : getRelativeTime(new Date(race.scheduledAt))}
{race.status === 'running'
? 'LIVE'
: getRelativeTime(race.scheduledAt)}
</p>
</div>
@@ -427,7 +441,7 @@ export default function RacesPage() {
{/* League Link */}
<div className="mt-3 pt-3 border-t border-charcoal-outline/50">
<Link
href={`/leagues/${race.leagueId}`}
href={`/leagues/${race.leagueId ?? ''}`}
onClick={(e) => e.stopPropagation()}
className="inline-flex items-center gap-2 text-sm text-primary-blue hover:underline"
>
@@ -482,24 +496,30 @@ export default function RacesPage() {
</p>
) : (
<div className="space-y-3">
{upcomingRaces.map((race) => (
<div
key={race.id}
onClick={() => router.push(`/races/${race.id}`)}
className="flex items-center gap-3 p-2 rounded-lg hover:bg-deep-graphite cursor-pointer transition-colors"
>
<div className="flex-shrink-0 w-10 h-10 bg-primary-blue/10 rounded-lg flex items-center justify-center">
<span className="text-sm font-bold text-primary-blue">
{new Date(race.scheduledAt).getDate()}
</span>
{upcomingRaces.map((race) => {
if (!race.scheduledAt) {
return null;
}
const scheduledAtDate = new Date(race.scheduledAt);
return (
<div
key={race.id}
onClick={() => router.push(`/races/${race.id}`)}
className="flex items-center gap-3 p-2 rounded-lg hover:bg-deep-graphite cursor-pointer transition-colors"
>
<div className="flex-shrink-0 w-10 h-10 bg-primary-blue/10 rounded-lg flex items-center justify-center">
<span className="text-sm font-bold text-primary-blue">
{scheduledAtDate.getDate()}
</span>
</div>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium text-white truncate">{race.track}</p>
<p className="text-xs text-gray-500">{formatTime(scheduledAtDate)}</p>
</div>
<ChevronRight className="w-4 h-4 text-gray-500" />
</div>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium text-white truncate">{race.track}</p>
<p className="text-xs text-gray-500">{formatTime(new Date(race.scheduledAt))}</p>
</div>
<ChevronRight className="w-4 h-4 text-gray-500" />
</div>
))}
);
})}
</div>
)}
</Card>