147 lines
5.7 KiB
TypeScript
147 lines
5.7 KiB
TypeScript
|
|
|
|
import { DashboardKpiRow } from '@/components/dashboard/DashboardKpiRow';
|
|
import { RecentActivityTable, type ActivityItem } from '@/components/dashboard/RecentActivityTable';
|
|
import { TelemetryPanel } from '@/components/dashboard/TelemetryPanel';
|
|
import type { DashboardViewData } from '@/lib/view-data/DashboardViewData';
|
|
import { Box } from '@/ui/Box';
|
|
import { Button } from '@/ui/Button';
|
|
import { Grid } from '@/ui/Grid';
|
|
import { Stack } from '@/ui/Stack';
|
|
import { Text } from '@/ui/Text';
|
|
|
|
interface DashboardTemplateProps {
|
|
viewData: DashboardViewData;
|
|
onNavigateToRaces: () => void;
|
|
}
|
|
|
|
/**
|
|
* DashboardTemplate
|
|
*
|
|
* Redesigned as a "Telemetry Workspace" following the Precision Racing Minimal theme.
|
|
* Composes semantic dashboard components into a high-density data environment.
|
|
* Complies with architectural constraints by using UI primitives.
|
|
*/
|
|
export function DashboardTemplate({
|
|
viewData,
|
|
onNavigateToRaces,
|
|
}: DashboardTemplateProps) {
|
|
const {
|
|
currentDriver,
|
|
nextRace,
|
|
upcomingRaces,
|
|
leagueStandings,
|
|
feedItems,
|
|
activeLeaguesCount,
|
|
hasLeagueStandings,
|
|
hasFeedItems,
|
|
} = viewData;
|
|
|
|
const kpiItems = [
|
|
{ label: 'Rating', value: currentDriver.rating, intent: 'primary' as const },
|
|
{ label: 'Rank', value: `#${currentDriver.rank}`, intent: 'warning' as const },
|
|
{ label: 'Starts', value: currentDriver.totalRaces },
|
|
{ label: 'Wins', value: currentDriver.wins, intent: 'success' as const },
|
|
{ label: 'Podiums', value: currentDriver.podiums, intent: 'warning' as const },
|
|
{ label: 'Leagues', value: activeLeaguesCount },
|
|
];
|
|
|
|
const activityItems: ActivityItem[] = feedItems.map(item => ({
|
|
id: item.id,
|
|
type: item.type.toUpperCase(),
|
|
description: item.headline,
|
|
timestamp: item.formattedTime,
|
|
status: item.type === 'race_result' ? 'success' : 'info'
|
|
}));
|
|
|
|
return (
|
|
<Stack gap={6}>
|
|
{/* KPI Overview */}
|
|
<DashboardKpiRow items={kpiItems} />
|
|
|
|
<Grid responsiveGridCols={{ base: 1, lg: 12 }} gap={6}>
|
|
{/* Main Content Column */}
|
|
<Box responsiveColSpan={{ base: 1, lg: 8 }}>
|
|
<Stack direction="col" gap={6}>
|
|
{nextRace && (
|
|
<TelemetryPanel title="Active Session">
|
|
<Box display="flex" alignItems="center" justifyContent="between">
|
|
<Box>
|
|
<Text size="xs" variant="low" mb={1} block>Next Event</Text>
|
|
<Text size="lg" weight="bold" block>{nextRace.track}</Text>
|
|
<Text size="xs" variant="primary" font="mono" block>{nextRace.car}</Text>
|
|
</Box>
|
|
<Box textAlign="right">
|
|
<Text size="xs" variant="low" mb={1} block>Starts In</Text>
|
|
<Text size="xl" font="mono" weight="bold" variant="warning" block>{nextRace.timeUntil}</Text>
|
|
<Text size="xs" variant="low" block>{nextRace.formattedDate} @ {nextRace.formattedTime}</Text>
|
|
</Box>
|
|
</Box>
|
|
</TelemetryPanel>
|
|
)}
|
|
|
|
<TelemetryPanel title="Recent Activity">
|
|
{hasFeedItems ? (
|
|
<RecentActivityTable items={activityItems} />
|
|
) : (
|
|
<Box py={8} textAlign="center">
|
|
<Text italic variant="low">No recent activity recorded.</Text>
|
|
</Box>
|
|
)}
|
|
</TelemetryPanel>
|
|
</Stack>
|
|
</Box>
|
|
|
|
{/* Sidebar Column */}
|
|
<Box responsiveColSpan={{ base: 1, lg: 4 }}>
|
|
<Stack direction="col" gap={6}>
|
|
<TelemetryPanel title="Championship Standings">
|
|
{hasLeagueStandings ? (
|
|
<Stack direction="col" gap={3}>
|
|
{leagueStandings.map((standing) => (
|
|
<Box key={standing.leagueId} display="flex" alignItems="center" justifyContent="between" borderBottom borderColor="var(--ui-color-border-muted)" pb={2}>
|
|
<Box>
|
|
<Text size="xs" weight="bold" truncate block maxWidth="180px">{standing.leagueName}</Text>
|
|
<Text size="xs" variant="low" block>Pos: {standing.position} / {standing.totalDrivers}</Text>
|
|
</Box>
|
|
<Text size="sm" font="mono" weight="bold" variant="primary">{standing.points} PTS</Text>
|
|
</Box>
|
|
))}
|
|
</Stack>
|
|
) : (
|
|
<Box py={4} textAlign="center">
|
|
<Text italic variant="low">No active championships.</Text>
|
|
</Box>
|
|
)}
|
|
</TelemetryPanel>
|
|
|
|
<TelemetryPanel title="Upcoming Schedule">
|
|
<Stack direction="col" gap={4}>
|
|
{upcomingRaces.slice(0, 3).map((race) => (
|
|
<Box key={race.id} cursor="pointer">
|
|
<Box display="flex" justifyContent="between" alignItems="start" mb={1}>
|
|
<Text size="xs" weight="bold">{race.track}</Text>
|
|
<Text size="xs" font="mono" variant="low">{race.timeUntil}</Text>
|
|
</Box>
|
|
<Box display="flex" justifyContent="between">
|
|
<Text size="xs" variant="low">{race.car}</Text>
|
|
<Text size="xs" variant="low">{race.formattedDate}</Text>
|
|
</Box>
|
|
</Box>
|
|
))}
|
|
<Button
|
|
variant="secondary"
|
|
fullWidth
|
|
onClick={onNavigateToRaces}
|
|
>
|
|
<Text size="xs" weight="bold" uppercase letterSpacing="widest">View Full Schedule</Text>
|
|
</Button>
|
|
</Stack>
|
|
</TelemetryPanel>
|
|
</Stack>
|
|
</Box>
|
|
</Grid>
|
|
</Stack>
|
|
);
|
|
}
|