website refactor

This commit is contained in:
2026-01-18 22:55:55 +01:00
parent b43a23a48c
commit aeaa43f4d3
179 changed files with 4736 additions and 6832 deletions

View File

@@ -1,6 +1,8 @@
'use client';
import { Panel } from '@/ui/Panel';
import { Stack } from '@/ui/primitives/Stack';
import { ActivityFeed } from '../feed/ActivityFeed';
import React from 'react';
interface FeedItem {
id: string;
@@ -25,10 +27,8 @@ interface ActivityFeedPanelProps {
*/
export function ActivityFeedPanel({ items, hasItems }: ActivityFeedPanelProps) {
return (
<Panel title="Activity Feed" padding={0}>
<Stack px={6} pb={6}>
<ActivityFeed items={items} hasItems={hasItems} />
</Stack>
<Panel title="Activity Feed">
<ActivityFeed items={items} hasItems={hasItems} />
</Panel>
);
}

View File

@@ -1,5 +1,7 @@
'use client';
import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack';
import { ControlBar } from '@/ui/ControlBar';
import React from 'react';
interface DashboardControlBarProps {
@@ -11,17 +13,17 @@ interface DashboardControlBarProps {
* DashboardControlBar
*
* The top header bar for page-level controls and context.
* Uses UI primitives to comply with architectural constraints.
*/
export function DashboardControlBar({ title, actions }: DashboardControlBarProps) {
return (
<Stack direction="row" h="full" align="center" justify="between" px={6}>
<Heading level={6} weight="bold">
{title}
</Heading>
<Stack direction="row" align="center" gap={4}>
{actions}
</Stack>
</Stack>
<ControlBar
leftContent={
<Heading level={6} weight="bold">
{title}
</Heading>
}
>
{actions}
</ControlBar>
);
}

View File

@@ -3,9 +3,10 @@ import { Avatar } from '../../ui/Avatar';
import { Badge } from '../../ui/Badge';
import { Heading } from '../../ui/Heading';
import { Icon } from '../../ui/Icon';
import { Box } from '../../ui/primitives/Box';
import { Stack } from '../../ui/primitives/Stack';
import { Text } from '../../ui/Text';
import { ProfileHero, ProfileAvatar, ProfileStatsGroup, ProfileStat } from '../../ui/ProfileHero';
import { BadgeGroup } from '../../ui/BadgeGroup';
import { QuickStatCard, QuickStatItem } from '../../ui/QuickStatCard';
import React from 'react';
interface DashboardHeroProps {
driverName: string;
@@ -14,7 +15,6 @@ interface DashboardHeroProps {
rank: number;
totalRaces: number;
winRate: number;
className?: string;
}
export function DashboardHero({
@@ -24,123 +24,50 @@ export function DashboardHero({
rank,
totalRaces,
winRate,
className = '',
}: DashboardHeroProps) {
return (
<Box
position="relative"
bg="bg-[#0C0D0F]"
borderBottom
borderColor="border-[#23272B]"
overflow="hidden"
className={className}
>
{/* Background Glow */}
<Box
position="absolute"
top={-100}
right={-100}
w="500px"
h="500px"
bg="bg-primary-blue/10"
rounded="full"
blur="3xl"
/>
<ProfileHero glowColor="aqua">
<div style={{ display: 'flex', alignItems: 'center', gap: '2rem', flexWrap: 'wrap' }}>
{/* Avatar Section */}
<ProfileAvatar
badge={<Icon icon={Star} size={5} intent="high" />}
>
<Avatar
src={avatarUrl || undefined}
alt={driverName}
size="xl"
/>
</ProfileAvatar>
<Box p={{ base: 6, md: 10 }} position="relative" zIndex={10}>
<Stack direction={{ base: 'col', md: 'row' }} align="center" gap={8}>
{/* Avatar Section */}
<Box position="relative">
<Box
p={1}
rounded="2xl"
bg="bg-[#141619]"
border
borderColor="border-[#23272B]"
>
<Avatar
src={avatarUrl}
alt={driverName}
size={120}
className="rounded-xl"
/>
</Box>
<Box
position="absolute"
bottom={-2}
right={-2}
w="10"
h="10"
rounded="xl"
bg="bg-[#4ED4E0]"
borderWidth="2px"
borderStyle="solid"
borderColor="border-[#0C0D0F]"
display="flex"
center
>
<Icon icon={Star} size={5} color="#0C0D0F" />
</Box>
</Box>
{/* Info Section */}
<div style={{ flex: 1, minWidth: '200px' }}>
<Heading level={1}>{driverName}</Heading>
<ProfileStatsGroup>
<ProfileStat label="Rating" value={rating} intent="telemetry" />
<ProfileStat label="Rank" value={`#${rank}`} intent="warning" />
<ProfileStat label="Starts" value={totalRaces} intent="low" />
</ProfileStatsGroup>
{/* Info Section */}
<Stack flex={1} align={{ base: 'center', md: 'start' }} gap={4}>
<Box>
<Heading level={1} uppercase letterSpacing="tighter" mb={2}>
{driverName}
</Heading>
<Stack direction="row" gap={4}>
<Stack gap={0.5}>
<Text size="xs" color="text-gray-500" uppercase>Rating</Text>
<Text size="sm" weight="bold" color="text-[#4ED4E0]" font="mono">{rating}</Text>
</Stack>
<Stack gap={0.5}>
<Text size="xs" color="text-gray-500" uppercase>Rank</Text>
<Text size="sm" weight="bold" color="text-[#FFBE4D]" font="mono">#{rank}</Text>
</Stack>
<Stack gap={0.5}>
<Text size="xs" color="text-gray-500" uppercase>Starts</Text>
<Text size="sm" weight="bold" color="text-gray-300" font="mono">{totalRaces}</Text>
</Stack>
</Stack>
</Box>
<BadgeGroup>
<Badge variant="primary" icon={Trophy}>
{winRate}% Win Rate
</Badge>
<Badge variant="info" icon={Flag}>
Pro License
</Badge>
<Badge variant="default" icon={Users}>
Team Redline
</Badge>
</BadgeGroup>
</div>
<Stack direction="row" gap={3} wrap>
<Badge variant="primary" rounded="lg" icon={Trophy}>
{winRate}% Win Rate
</Badge>
<Badge variant="info" rounded="lg" icon={Flag}>
Pro License
</Badge>
<Badge variant="default" rounded="lg" icon={Users}>
Team Redline
</Badge>
</Stack>
</Stack>
{/* Quick Stats */}
<Stack
direction="row"
gap={4}
p={6}
bg="bg-white/5"
rounded="2xl"
border
borderColor="border-white/10"
className="backdrop-blur-md"
>
<Stack align="center" px={4}>
<Text size="2xl" weight="bold" color="text-white">12</Text>
<Text size="xs" color="text-gray-500" uppercase>Podiums</Text>
</Stack>
<Box w="1px" h="10" bg="bg-white/10" />
<Stack align="center" px={4}>
<Text size="2xl" weight="bold" color="text-white">4</Text>
<Text size="xs" color="text-gray-500" uppercase>Wins</Text>
</Stack>
</Stack>
</Stack>
</Box>
</Box>
{/* Quick Stats */}
<QuickStatCard>
<QuickStatItem label="Podiums" value="12" />
<QuickStatItem label="Wins" value="4" />
</QuickStatCard>
</div>
</ProfileHero>
);
}

View File

@@ -1,57 +1,64 @@
'use client';
import { DashboardHero as UiDashboardHero } from '@/components/dashboard/DashboardHero';
import { routes } from '@/lib/routing/RouteConfig';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Link } from '@/ui/Link';
import { StatBox } from '@/ui/StatBox';
import { StatGrid } from '@/ui/StatGrid';
import { Flag, Medal, Target, Trophy, User, Users } from 'lucide-react';
import React from 'react';
interface DashboardHeroProps {
currentDriver: {
name: string;
avatarUrl: string;
country: string;
rating: string | number;
rank: string | number;
totalRaces: string | number;
wins: string | number;
podiums: string | number;
rating: number;
rank: number;
totalRaces: number;
wins: number;
podiums: number;
consistency: string;
};
activeLeaguesCount: string | number;
activeLeaguesCount: number;
}
export function DashboardHero({ currentDriver, activeLeaguesCount }: DashboardHeroProps) {
return (
<UiDashboardHero
driverName={currentDriver.name}
avatarUrl={currentDriver.avatarUrl}
country={currentDriver.country}
rating={currentDriver.rating}
rank={currentDriver.rank}
totalRaces={currentDriver.totalRaces}
actions={
<>
<Link href={routes.public.leagues} variant="ghost">
<Button variant="secondary" icon={<Icon icon={Flag} size={4} />}>
Browse Leagues
</Button>
</Link>
<Link href={routes.protected.profile} variant="ghost">
<Button variant="primary" icon={<Icon icon={User} size={4} />}>
View Profile
</Button>
</Link>
</>
}
stats={
<>
<StatBox icon={Trophy} label="Wins" value={currentDriver.wins} color="#10b981" />
<StatBox icon={Medal} label="Podiums" value={currentDriver.podiums} color="#FFBE4D" />
<StatBox icon={Target} label="Consistency" value={currentDriver.consistency} color="#198CFF" />
<StatBox icon={Users} label="Active Leagues" value={activeLeaguesCount} color="#a855f7" />
</>
}
/>
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<UiDashboardHero
driverName={currentDriver.name}
avatarUrl={currentDriver.avatarUrl}
rating={currentDriver.rating}
rank={currentDriver.rank}
totalRaces={currentDriver.totalRaces}
winRate={Math.round((currentDriver.wins / currentDriver.totalRaces) * 100) || 0}
/>
<div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
<Link href={routes.public.leagues}>
<Button variant="secondary" icon={<Icon icon={Flag} size={4} />}>
Browse Leagues
</Button>
</Link>
<Link href={routes.protected.profile}>
<Button variant="primary" icon={<Icon icon={User} size={4} />}>
View Profile
</Button>
</Link>
</div>
<StatGrid
variant="box"
columns={{ base: 2, md: 4 }}
stats={[
{ icon: Trophy, label: 'Wins', value: currentDriver.wins, intent: 'success' },
{ icon: Medal, label: 'Podiums', value: currentDriver.podiums, intent: 'warning' },
{ icon: Target, label: 'Consistency', value: currentDriver.consistency, intent: 'primary' },
{ icon: Users, label: 'Active Leagues', value: activeLeaguesCount, intent: 'telemetry' },
]}
/>
</div>
);
}

View File

@@ -1,4 +1,7 @@
import { Stack } from '@/ui/primitives/Stack';
import { Sidebar } from '@/ui/Sidebar';
import { Header } from '@/ui/Header';
import { MainContent } from '@/ui/MainContent';
import { Box } from '@/ui/primitives/Box';
import React from 'react';
interface DashboardShellProps {
@@ -12,28 +15,25 @@ interface DashboardShellProps {
*
* The primary layout container for the Telemetry Workspace.
* Orchestrates the sidebar rail, top control bar, and main content area.
* Uses UI primitives to comply with architectural constraints.
*/
export function DashboardShell({ children, rail, controlBar }: DashboardShellProps) {
return (
<Stack direction="row" h="screen" overflow="hidden" bg="base-black" color="white">
<Box display="flex" height="100vh" style={{ overflow: 'hidden', backgroundColor: 'var(--ui-color-bg-base)' }}>
{rail && (
<Stack as="aside" w="16" flexShrink={0} borderRight bg="surface-charcoal" borderColor="var(--color-outline)">
<Sidebar>
{rail}
</Stack>
</Sidebar>
)}
<Stack flexGrow={1} overflow="hidden">
<Box display="flex" flexDirection="col" flex={1} style={{ overflow: 'hidden' }}>
{controlBar && (
<Stack as="header" h="14" borderBottom bg="surface-charcoal" borderColor="var(--color-outline)">
<Header>
{controlBar}
</Stack>
</Header>
)}
<Stack as="main" flexGrow={1} overflow="auto" p={6}>
<Stack maxWidth="7xl" mx="auto" gap={6} fullWidth>
{children}
</Stack>
</Stack>
</Stack>
</Stack>
<MainContent maxWidth="7xl">
{children}
</MainContent>
</Box>
</Box>
);
}

View File

@@ -1,3 +1,5 @@
'use client';
import React from 'react';
import { Text } from '@/ui/Text';
import { StatusDot } from '@/ui/StatusDot';
@@ -19,33 +21,23 @@ interface RecentActivityTableProps {
* RecentActivityTable
*
* A high-density table for displaying recent events and telemetry logs.
* Uses UI primitives to comply with architectural constraints.
*/
export function RecentActivityTable({ items }: RecentActivityTableProps) {
const getStatusColor = (status?: string) => {
switch (status) {
case 'success': return 'var(--color-success)';
case 'warning': return 'var(--color-warning)';
case 'critical': return 'var(--color-critical)';
default: return 'var(--color-primary)';
}
};
return (
<Table>
<TableHead>
<TableRow>
<TableHeader>
<Text size="xs" weight="medium" uppercase letterSpacing="wider" color="var(--color-text-low)">Type</Text>
<Text size="xs" weight="bold" uppercase variant="low">Type</Text>
</TableHeader>
<TableHeader>
<Text size="xs" weight="medium" uppercase letterSpacing="wider" color="var(--color-text-low)">Description</Text>
<Text size="xs" weight="bold" uppercase variant="low">Description</Text>
</TableHeader>
<TableHeader>
<Text size="xs" weight="medium" uppercase letterSpacing="wider" color="var(--color-text-low)">Time</Text>
<Text size="xs" weight="bold" uppercase variant="low">Time</Text>
</TableHeader>
<TableHeader>
<Text size="xs" weight="medium" uppercase letterSpacing="wider" color="var(--color-text-low)">Status</Text>
<Text size="xs" weight="bold" uppercase variant="low">Status</Text>
</TableHeader>
</TableRow>
</TableHead>
@@ -53,16 +45,16 @@ export function RecentActivityTable({ items }: RecentActivityTableProps) {
{items.map((item) => (
<TableRow key={item.id}>
<TableCell>
<Text font="mono" color="var(--color-telemetry)" size="xs">{item.type}</Text>
<Text font="mono" variant="telemetry" size="xs">{item.type}</Text>
</TableCell>
<TableCell>
<Text color="var(--color-text-med)" size="xs">{item.description}</Text>
<Text variant="med" size="xs">{item.description}</Text>
</TableCell>
<TableCell>
<Text color="var(--color-text-low)" size="xs">{item.timestamp}</Text>
<Text variant="low" size="xs">{item.timestamp}</Text>
</TableCell>
<TableCell>
<StatusDot color={getStatusColor(item.status)} size={1.5} />
<StatusDot intent={item.status === 'info' ? 'primary' : item.status} size="sm" />
</TableCell>
</TableRow>
))}

View File

@@ -1,5 +1,5 @@
import { Panel } from '@/ui/Panel';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text';
import React from 'react';
interface TelemetryPanelProps {
@@ -11,14 +11,13 @@ interface TelemetryPanelProps {
* TelemetryPanel
*
* A dense, instrument-grade panel for displaying data and controls.
* Uses UI primitives to comply with architectural constraints.
*/
export function TelemetryPanel({ title, children }: TelemetryPanelProps) {
return (
<Panel title={title} variant="dark" padding={4}>
<Stack fontSize="sm">
<Text size="sm" variant="med">
{children}
</Stack>
</Text>
</Panel>
);
}