website refactor

This commit is contained in:
2026-01-19 18:34:01 +01:00
parent 61b5cf3b64
commit 41e21e6595
24 changed files with 643 additions and 717 deletions

View File

@@ -6,7 +6,7 @@ import { AdminUsersTemplate } from '@/templates/AdminUsersTemplate';
import { AdminUsersViewData } from '@/lib/view-data/AdminUsersViewData'; import { AdminUsersViewData } from '@/lib/view-data/AdminUsersViewData';
import { updateUserStatus, deleteUser } from '@/app/actions/adminActions'; import { updateUserStatus, deleteUser } from '@/app/actions/adminActions';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { SharedConfirmDialog } from '@/components/shared/UIComponents'; import { ConfirmDialog } from '@/components/shared/ConfirmDialog';
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts'; import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
export function AdminUsersWrapper({ viewData }: ClientWrapperProps<AdminUsersViewData>) { export function AdminUsersWrapper({ viewData }: ClientWrapperProps<AdminUsersViewData>) {
@@ -148,7 +148,7 @@ export function AdminUsersWrapper({ viewData }: ClientWrapperProps<AdminUsersVie
onSelectAll={handleSelectAll} onSelectAll={handleSelectAll}
onClearSelection={handleClearSelection} onClearSelection={handleClearSelection}
/> />
<SharedConfirmDialog <ConfirmDialog
isOpen={!!userToDelete} isOpen={!!userToDelete}
onClose={() => setUserToDelete(null)} onClose={() => setUserToDelete(null)}
onConfirm={confirmDeleteUser} onConfirm={confirmDeleteUser}

View File

@@ -8,7 +8,12 @@ import {
updateRaceAction updateRaceAction
} from '@/app/actions/leagueScheduleActions'; } from '@/app/actions/leagueScheduleActions';
import { StatefulPageWrapper } from '@/components/shared/state/StatefulPageWrapper'; import { StatefulPageWrapper } from '@/components/shared/state/StatefulPageWrapper';
import { SharedConfirmDialog, SharedStack, SharedCard, SharedBox, SharedText, SharedHeading } from '@/components/shared/UIComponents'; import { ConfirmDialog } from '@/components/shared/ConfirmDialog';
import { Stack } from '@/ui/Stack';
import { Card } from '@/ui/Card';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { Heading } from '@/ui/Heading';
import { import {
useLeagueAdminSchedule, useLeagueAdminSchedule,
useLeagueAdminStatus, useLeagueAdminStatus,
@@ -172,16 +177,16 @@ export function LeagueAdminSchedulePageClient() {
// Render admin access required if not admin // Render admin access required if not admin
if (!isLoading && !isAdmin) { if (!isLoading && !isAdmin) {
return ( return (
<SharedStack gap={6}> <Stack gap={6}>
<SharedCard> <Card>
<SharedBox p={6} textAlign="center"> <Box p={6} textAlign="center">
<SharedHeading level={3}>Admin Access Required</SharedHeading> <Heading level={3}>Admin Access Required</Heading>
<SharedBox mt={2}> <Box mt={2}>
<SharedText size="sm" color="text-gray-400">Only league admins can manage the schedule.</SharedText> <Text size="sm" color="text-gray-400">Only league admins can manage the schedule.</Text>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedCard> </Card>
</SharedStack> </Stack>
); );
} }
@@ -221,7 +226,7 @@ export function LeagueAdminSchedulePageClient() {
setForm(new RaceScheduleCommandModel(form.toCommand())); setForm(new RaceScheduleCommandModel(form.toCommand()));
}} }}
/> />
<SharedConfirmDialog <ConfirmDialog
isOpen={!!raceToDelete} isOpen={!!raceToDelete}
onClose={() => setRaceToDelete(null)} onClose={() => setRaceToDelete(null)}
onConfirm={confirmDelete} onConfirm={confirmDelete}

View File

@@ -3,13 +3,11 @@
import type { Result } from '@/lib/contracts/Result'; import type { Result } from '@/lib/contracts/Result';
import type { ProfileViewData } from '@/lib/view-data/ProfileViewData'; import type { ProfileViewData } from '@/lib/view-data/ProfileViewData';
import { ProfileSettingsTemplate } from '@/templates/ProfileSettingsTemplate'; import { ProfileSettingsTemplate } from '@/templates/ProfileSettingsTemplate';
import { import { Box } from '@/ui/Box';
SharedBox, import { Stack } from '@/ui/Stack';
SharedStack, import { Text } from '@/ui/Text';
SharedText, import { Icon } from '@/ui/Icon';
SharedIcon, import { ProgressLine } from '@/components/shared/ProgressLine';
SharedProgressLine
} from '@/components/shared/UIComponents';
import { ShieldAlert } from 'lucide-react'; import { ShieldAlert } from 'lucide-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -46,19 +44,19 @@ export function ProfileSettingsPageClient({ viewData, onSave }: ProfileSettingsP
return ( return (
<> <>
<SharedProgressLine isLoading={isSaving} /> <ProgressLine isLoading={isSaving} />
{error && ( {error && (
<SharedBox position="fixed" top={4} right={4} zIndex={50} maxWidth="md"> <Box position="fixed" top={4} right={4} zIndex={50} maxWidth="md">
<SharedBox bg="bg-error-red/10" p={4} rounded="md" border borderColor="border-error-red/20"> <Box bg="var(--ui-color-bg-surface)" p={4} rounded="md" border borderColor="var(--ui-color-intent-critical)">
<SharedStack direction="row" align="center" gap={3}> <Stack direction="row" align="center" gap={3}>
<SharedIcon icon={ShieldAlert} size={5} color="text-error-red" /> <Icon icon={ShieldAlert} size={5} color="var(--ui-color-intent-critical)" />
<SharedBox> <Box>
<SharedText weight="bold" color="text-error-red">Update Failed</SharedText> <Text weight="bold" variant="critical">Update Failed</Text>
<SharedText size="sm" color="text-error-red/80">{error}</SharedText> <Text size="sm" variant="low">{error}</Text>
</SharedBox> </Box>
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedBox> </Box>
)} )}
<ProfileSettingsTemplate <ProfileSettingsTemplate
viewData={viewData} viewData={viewData}

View File

@@ -1,12 +1,10 @@
'use client'; 'use client';
import { import { Box } from '@/ui/Box';
SharedBox, import { Stack } from '@/ui/Stack';
SharedStack, import { Text } from '@/ui/Text';
SharedText, import { Icon } from '@/ui/Icon';
SharedIcon, import { ProgressLine } from '@/components/shared/ProgressLine';
SharedProgressLine
} from '@/components/shared/UIComponents';
import { ShieldAlert } from 'lucide-react'; import { ShieldAlert } from 'lucide-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -53,19 +51,19 @@ export function SponsorshipRequestsClient({ viewData, onAccept, onReject }: Spon
return ( return (
<> <>
<SharedProgressLine isLoading={!!isProcessing} /> <ProgressLine isLoading={!!isProcessing} />
{error && ( {error && (
<SharedBox position="fixed" top={4} right={4} zIndex={50} maxWidth="md"> <Box position="fixed" top={4} right={4} zIndex={50} maxWidth="md">
<SharedBox bg="bg-error-red/10" p={4} rounded="md" border borderColor="border-error-red/20"> <Box bg="bg-error-red/10" p={4} rounded="md" border borderColor="border-error-red/20">
<SharedStack direction="row" align="center" gap={3}> <Stack direction="row" align="center" gap={3}>
<SharedIcon icon={ShieldAlert} size={5} color="text-error-red" /> <Icon icon={ShieldAlert} size={5} color="text-error-red" />
<SharedBox> <Box>
<SharedText weight="bold" color="text-error-red">Action Failed</SharedText> <Text weight="bold" color="text-error-red">Action Failed</Text>
<SharedText size="sm" color="text-error-red/80">{error}</SharedText> <Text size="sm" color="text-error-red/80">{error}</Text>
</SharedBox> </Box>
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedBox> </Box>
)} )}
<SponsorshipRequestsTemplate <SponsorshipRequestsTemplate
viewData={viewData} viewData={viewData}

View File

@@ -24,7 +24,7 @@ export function AppFooter({ children }: AppFooterProps) {
variant="muted" variant="muted"
paddingY={8} paddingY={8}
marginTop="auto" marginTop="auto"
style={{ borderTop: '1px solid var(--ui-color-border-default)' }} borderTop={true}
> >
<Container size="xl"> <Container size="xl">
{children ? ( {children ? (

View File

@@ -1,7 +1,9 @@
'use client'; 'use client';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { SharedBox, SharedStack, SharedContainer } from '@/components/shared/UIComponents'; import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Container } from '@/ui/Container';
interface CreateLeagueWizardLayoutProps { interface CreateLeagueWizardLayoutProps {
children: ReactNode; children: ReactNode;
@@ -13,12 +15,12 @@ interface CreateLeagueWizardLayoutProps {
export function CreateLeagueWizardLayout({ children, header, progress, navigation, footer }: CreateLeagueWizardLayoutProps) { export function CreateLeagueWizardLayout({ children, header, progress, navigation, footer }: CreateLeagueWizardLayoutProps) {
return ( return (
<SharedBox as="main" maxWidth="4xl" mx="auto" pb={8}> <Box as="main" maxWidth="4xl" mx="auto" pb={8}>
{header} {header}
{progress} {progress}
{children} {children}
{navigation} {navigation}
{footer} {footer}
</SharedBox> </Box>
); );
} }

View File

@@ -1,26 +0,0 @@
'use client';
import { LucideIcon } from 'lucide-react';
import { EmptyState } from '@/ui/EmptyState';
interface SharedEmptyStateProps {
icon: LucideIcon;
title: string;
description?: string;
action?: {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'race-final' | 'discord';
};
}
export function SharedEmptyState({ icon, title, description, action }: SharedEmptyStateProps) {
return (
<EmptyState
icon={icon}
title={title}
description={description}
action={action}
/>
);
}

View File

@@ -1,44 +0,0 @@
import { Pagination } from '@/ui/Pagination';
import { Text } from '@/ui/Text';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Container } from '@/ui/Container';
import { ConfirmDialog } from '@/components/shared/ConfirmDialog';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading';
import { Grid } from '@/ui/Grid';
import { GridItem } from '@/ui/GridItem';
import { Surface } from '@/ui/Surface';
import { Input } from '@/ui/Input';
import { Link } from '@/ui/Link';
import { Skeleton } from '@/ui/Skeleton';
import { LoadingSpinner } from '@/ui/LoadingSpinner';
import { Badge } from '@/ui/Badge';
import { ProgressLine } from '@/components/shared/ProgressLine';
import { SharedEmptyState } from './SharedEmptyState';
export {
Pagination as SharedPagination,
Text as SharedText,
Box as SharedBox,
Stack as SharedStack,
Container as SharedContainer,
ConfirmDialog as SharedConfirmDialog,
Button as SharedButton,
Icon as SharedIcon,
Card as SharedCard,
Heading as SharedHeading,
Grid as SharedGrid,
GridItem as SharedGridItem,
Surface as SharedSurface,
Input as SharedInput,
Link as SharedLink,
Skeleton as SharedSkeleton,
LoadingSpinner as SharedLoadingSpinner,
Badge as SharedBadge,
ProgressLine as SharedProgressLine,
SharedEmptyState
};

View File

@@ -8,7 +8,13 @@ import { AdminUsersTable } from '@/components/admin/AdminUsersTable';
import { BulkActionBar } from '@/components/admin/BulkActionBar'; import { BulkActionBar } from '@/components/admin/BulkActionBar';
import { UserFilters } from '@/components/admin/UserFilters'; import { UserFilters } from '@/components/admin/UserFilters';
import { AdminUsersViewData } from '@/lib/view-data/AdminUsersViewData'; import { AdminUsersViewData } from '@/lib/view-data/AdminUsersViewData';
import { SharedButton, SharedContainer, SharedIcon, SharedStack, SharedBox, SharedText } from '@/components/shared/UIComponents'; import { Button } from '@/ui/Button';
import { Container } from '@/ui/Container';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/Stack';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { ErrorBanner } from '@/ui/ErrorBanner';
import { RefreshCw, ShieldAlert, Users } from 'lucide-react'; import { RefreshCw, ShieldAlert, Users } from 'lucide-react';
import { TemplateProps } from '@/lib/contracts/components/ComponentContracts'; import { TemplateProps } from '@/lib/contracts/components/ComponentContracts';
@@ -95,37 +101,29 @@ export function AdminUsersTemplate({
]; ];
return ( return (
<SharedContainer size="lg"> <Container size="lg">
<SharedBox paddingY={8}> <Box paddingY={8}>
<SharedStack gap={8}> <Stack gap={8}>
<AdminHeaderPanel <AdminHeaderPanel
title="User Management" title="User Management"
description="Monitor and control system access" description="Monitor and control system access"
isLoading={loading} isLoading={loading}
actions={ actions={
<SharedButton <Button
onClick={onRefresh} onClick={onRefresh}
disabled={loading} disabled={loading}
variant="secondary" variant="secondary"
size="sm" size="sm"
icon={<SharedIcon icon={RefreshCw} size={3} animate={loading ? 'spin' : 'none'} />} icon={<Icon icon={RefreshCw} size={3} animate={loading ? 'spin' : 'none'} />}
> >
Refresh Data Refresh Data
</SharedButton> </Button>
} }
/> />
{/* error notice should be a component */} {/* error notice should be a component */}
{error && ( {error && (
<SharedBox bg="bg-error-red/10" p={4} rounded="md" border borderColor="border-error-red/20"> <ErrorBanner title="Operation Failed" message={error} />
<SharedStack direction="row" align="center" gap={3}>
<SharedIcon icon={ShieldAlert} size={5} color="text-error-red" />
<SharedBox>
<SharedText weight="bold" color="text-error-red">Operation Failed</SharedText>
<SharedText size="sm" color="text-error-red/80">{error}</SharedText>
</SharedBox>
</SharedStack>
</SharedBox>
)} )}
<AdminStatsPanel stats={stats} /> <AdminStatsPanel stats={stats} />
@@ -147,9 +145,9 @@ export function AdminUsersTemplate({
title="No users found" title="No users found"
description="Try adjusting your filters or search query" description="Try adjusting your filters or search query"
action={ action={
<SharedButton variant="secondary" size="sm" onClick={onClearFilters}> <Button variant="secondary" size="sm" onClick={onClearFilters}>
Clear All Filters Clear All Filters
</SharedButton> </Button>
} }
/> />
) : ( ) : (
@@ -170,8 +168,8 @@ export function AdminUsersTemplate({
actions={bulkActions} actions={bulkActions}
onClearSelection={onClearSelection} onClearSelection={onClearSelection}
/> />
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedContainer> </Container>
); );
} }

View File

@@ -67,14 +67,14 @@ export function DashboardTemplate({
<TelemetryPanel title="Active Session"> <TelemetryPanel title="Active Session">
<Box display="flex" alignItems="center" justifyContent="between"> <Box display="flex" alignItems="center" justifyContent="between">
<Box> <Box>
<Text size="xs" color="text-gray-500" mb={1} block>Next Event</Text> <Text size="xs" variant="low" mb={1} block>Next Event</Text>
<Text size="lg" weight="bold" block>{nextRace.track}</Text> <Text size="lg" weight="bold" block>{nextRace.track}</Text>
<Text size="xs" color="primary-accent" font="mono" block>{nextRace.car}</Text> <Text size="xs" variant="primary" font="mono" block>{nextRace.car}</Text>
</Box> </Box>
<Box textAlign="right"> <Box textAlign="right">
<Text size="xs" color="text-gray-500" mb={1} block>Starts In</Text> <Text size="xs" variant="low" mb={1} block>Starts In</Text>
<Text size="xl" font="mono" weight="bold" color="warning-amber" block>{nextRace.timeUntil}</Text> <Text size="xl" font="mono" weight="bold" variant="warning" block>{nextRace.timeUntil}</Text>
<Text size="xs" color="text-gray-500" block>{nextRace.formattedDate} @ {nextRace.formattedTime}</Text> <Text size="xs" variant="low" block>{nextRace.formattedDate} @ {nextRace.formattedTime}</Text>
</Box> </Box>
</Box> </Box>
</TelemetryPanel> </TelemetryPanel>
@@ -85,7 +85,7 @@ export function DashboardTemplate({
<RecentActivityTable items={activityItems} /> <RecentActivityTable items={activityItems} />
) : ( ) : (
<Box py={8} textAlign="center"> <Box py={8} textAlign="center">
<Text italic color="text-gray-500">No recent activity recorded.</Text> <Text italic variant="low">No recent activity recorded.</Text>
</Box> </Box>
)} )}
</TelemetryPanel> </TelemetryPanel>
@@ -99,18 +99,18 @@ export function DashboardTemplate({
{hasLeagueStandings ? ( {hasLeagueStandings ? (
<Stack direction="col" gap={3}> <Stack direction="col" gap={3}>
{leagueStandings.map((standing) => ( {leagueStandings.map((standing) => (
<Box key={standing.leagueId} display="flex" alignItems="center" justifyContent="between" borderBottom borderColor="rgba(255, 255, 255, 0.1)" pb={2}> <Box key={standing.leagueId} display="flex" alignItems="center" justifyContent="between" borderBottom borderColor="var(--ui-color-border-muted)" pb={2}>
<Box> <Box>
<Text size="xs" weight="bold" truncate block maxWidth="180px">{standing.leagueName}</Text> <Text size="xs" weight="bold" truncate block maxWidth="180px">{standing.leagueName}</Text>
<Text size="xs" color="text-gray-500" block>Pos: {standing.position} / {standing.totalDrivers}</Text> <Text size="xs" variant="low" block>Pos: {standing.position} / {standing.totalDrivers}</Text>
</Box> </Box>
<Text size="sm" font="mono" weight="bold" color="primary-accent">{standing.points} PTS</Text> <Text size="sm" font="mono" weight="bold" variant="primary">{standing.points} PTS</Text>
</Box> </Box>
))} ))}
</Stack> </Stack>
) : ( ) : (
<Box py={4} textAlign="center"> <Box py={4} textAlign="center">
<Text italic color="text-gray-500">No active championships.</Text> <Text italic variant="low">No active championships.</Text>
</Box> </Box>
)} )}
</TelemetryPanel> </TelemetryPanel>
@@ -121,11 +121,11 @@ export function DashboardTemplate({
<Box key={race.id} cursor="pointer"> <Box key={race.id} cursor="pointer">
<Box display="flex" justifyContent="between" alignItems="start" mb={1}> <Box display="flex" justifyContent="between" alignItems="start" mb={1}>
<Text size="xs" weight="bold">{race.track}</Text> <Text size="xs" weight="bold">{race.track}</Text>
<Text size="xs" font="mono" color="text-gray-500">{race.timeUntil}</Text> <Text size="xs" font="mono" variant="low">{race.timeUntil}</Text>
</Box> </Box>
<Box display="flex" justifyContent="between"> <Box display="flex" justifyContent="between">
<Text size="xs" color="text-gray-500">{race.car}</Text> <Text size="xs" variant="low">{race.car}</Text>
<Text size="xs" color="text-gray-500">{race.formattedDate}</Text> <Text size="xs" variant="low">{race.formattedDate}</Text>
</Box> </Box>
</Box> </Box>
))} ))}

View File

@@ -3,35 +3,33 @@
import { LeagueCard } from '@/components/leagues/LeagueCardWrapper'; import { LeagueCard } from '@/components/leagues/LeagueCardWrapper';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import type { LeagueDetailViewData } from '@/lib/view-data/LeagueDetailViewData'; import type { LeagueDetailViewData } from '@/lib/view-data/LeagueDetailViewData';
import { import { Box } from '@/ui/Box';
SharedBox, import { Link } from '@/ui/Link';
SharedLink, import { Text } from '@/ui/Text';
SharedText, import { Stack } from '@/ui/Stack';
SharedStack, import { Container } from '@/ui/Container';
SharedContainer, import { Icon } from '@/ui/Icon';
SharedIcon
} from '@/components/shared/UIComponents';
import { ChevronRight } from 'lucide-react'; import { ChevronRight } from 'lucide-react';
import { TemplateProps } from '@/lib/contracts/components/ComponentContracts'; import { TemplateProps } from '@/lib/contracts/components/ComponentContracts';
export function LeagueDetailTemplate({ viewData, children, tabs }: TemplateProps<LeagueDetailViewData> & { children?: React.ReactNode, tabs?: any[] }) { export function LeagueDetailTemplate({ viewData, children, tabs }: TemplateProps<LeagueDetailViewData> & { children?: React.ReactNode, tabs?: any[] }) {
return ( return (
<SharedContainer size="lg"> <Container size="lg">
<SharedBox paddingY={8}> <Box paddingY={8}>
<SharedStack gap={8}> <Stack gap={8}>
<SharedBox> <Box>
<SharedStack direction="row" align="center" gap={2}> <Stack direction="row" align="center" gap={2}>
<SharedLink href={routes.public.leagues}> <Link href={routes.public.leagues}>
<SharedText size="sm" color="text-gray-400">Leagues</SharedText> <Text size="sm" color="text-gray-400">Leagues</Text>
</SharedLink> </Link>
<SharedIcon icon={ChevronRight} size={3} color="text-gray-500" /> <Icon icon={ChevronRight} size={3} color="text-gray-500" />
<SharedText size="sm" color="text-white">{viewData.name}</SharedText> <Text size="sm" color="text-white">{viewData.name}</Text>
</SharedStack> </Stack>
</SharedBox> </Box>
{children} {children}
{/* ... rest of the template ... */} {/* ... rest of the template ... */}
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedContainer> </Container>
); );
} }

View File

@@ -2,24 +2,25 @@
import { MediaGallery } from '@/components/media/MediaGallery'; import { MediaGallery } from '@/components/media/MediaGallery';
import { MediaViewData } from '@/lib/view-data/MediaViewData'; import { MediaViewData } from '@/lib/view-data/MediaViewData';
import { SharedBox, SharedContainer } from '@/components/shared/UIComponents'; import { Box } from '@/ui/Box';
import { Container } from '@/ui/Container';
import { TemplateProps } from '@/lib/contracts/components/ComponentContracts'; import { TemplateProps } from '@/lib/contracts/components/ComponentContracts';
export function MediaTemplate({ viewData }: TemplateProps<MediaViewData>) { export function MediaTemplate({ viewData }: TemplateProps<MediaViewData>) {
const { assets, categories, title, description } = viewData; const { assets, categories, title, description } = viewData;
return ( return (
<SharedContainer> <Container>
<SharedBox paddingY={8}> <Box paddingY={8}>
<SharedBox display="flex" flexDirection="col" gap={8}> <Box display="flex" flexDirection="col" gap={8}>
<MediaGallery <MediaGallery
assets={assets} assets={assets}
categories={categories} categories={categories}
title={title} title={title}
description={description} description={description}
/> />
</SharedBox> </Box>
</SharedBox> </Box>
</SharedContainer> </Container>
); );
} }

View File

@@ -2,14 +2,12 @@
import { UploadDropzone } from '@/components/shared/UploadDropzone'; import { UploadDropzone } from '@/components/shared/UploadDropzone';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { import { Box } from '@/ui/Box';
SharedBox, import { Button } from '@/ui/Button';
SharedButton, import { Stack } from '@/ui/Stack';
SharedStack, import { Text } from '@/ui/Text';
SharedText, import { Container } from '@/ui/Container';
SharedContainer, import { Card } from '@/ui/Card';
SharedCard
} from '@/components/shared/UIComponents';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { MediaMetaPanel, mapMediaMetadata } from '@/ui/MediaMetaPanel'; import { MediaMetaPanel, mapMediaMetadata } from '@/ui/MediaMetaPanel';
import { MediaPreviewCard } from '@/ui/MediaPreviewCard'; import { MediaPreviewCard } from '@/ui/MediaPreviewCard';
@@ -33,18 +31,18 @@ export function ProfileLiveryUploadTemplate({
onUpload, onUpload,
}: ProfileLiveryUploadTemplateProps) { }: ProfileLiveryUploadTemplateProps) {
return ( return (
<SharedContainer size="md"> <Container size="md">
<SharedBox paddingY={8}> <Box paddingY={8}>
<SharedBox mb={6}> <Box mb={6}>
<Heading level={1}>Upload livery</Heading> <Heading level={1}>Upload livery</Heading>
<SharedText color="text-gray-500"> <Text color="text-gray-500">
Upload your custom car livery. Supported formats: .png, .jpg, .tga Upload your custom car livery. Supported formats: .png, .jpg, .tga
</SharedText> </Text>
</SharedBox> </Box>
<SharedBox display="grid" responsiveGridCols={{ base: 1, md: 2 }} gap={6}> <Box display="grid" responsiveGridCols={{ base: 1, md: 2 }} gap={6}>
<SharedBox> <Box>
<SharedCard> <Card>
<UploadDropzone <UploadDropzone
onFilesSelected={onFilesSelected} onFilesSelected={onFilesSelected}
accept=".png,.jpg,.jpeg,.tga" accept=".png,.jpg,.jpeg,.tga"
@@ -52,25 +50,25 @@ export function ProfileLiveryUploadTemplate({
isLoading={isUploading} isLoading={isUploading}
/> />
<SharedBox mt={6} display="flex" justifyContent="end" gap={3}> <Box mt={6} display="flex" justifyContent="end" gap={3}>
<Link href={routes.protected.profileLiveries}> <Link href={routes.protected.profileLiveries}>
<SharedButton variant="ghost">Cancel</SharedButton> <Button variant="ghost">Cancel</Button>
</Link> </Link>
<SharedButton <Button
variant="primary" variant="primary"
disabled={!selectedFile || isUploading} disabled={!selectedFile || isUploading}
onClick={onUpload} onClick={onUpload}
isLoading={isUploading} isLoading={isUploading}
> >
Upload Livery Upload Livery
</SharedButton> </Button>
</SharedBox> </Box>
</SharedCard> </Card>
</SharedBox> </Box>
<SharedBox> <Box>
{previewUrl ? ( {previewUrl ? (
<SharedBox display="flex" flexDirection="col" gap={6}> <Box display="flex" flexDirection="col" gap={6}>
<MediaPreviewCard <MediaPreviewCard
type="image" type="image"
src={previewUrl} src={previewUrl}
@@ -88,17 +86,17 @@ export function ProfileLiveryUploadTemplate({
createdAt: new Date(), createdAt: new Date(),
})} })}
/> />
</SharedBox> </Box>
) : ( ) : (
<SharedCard center p={12}> <Card center p={12}>
<SharedText color="text-gray-500" align="center"> <Text color="text-gray-500" align="center">
Select a file to see preview and details Select a file to see preview and details
</SharedText> </Text>
</SharedCard> </Card>
)} )}
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedContainer> </Container>
); );
} }

View File

@@ -39,11 +39,11 @@ export function ProfileTemplate({
return ( return (
<Stack align="center" gap={4} mb={8}> <Stack align="center" gap={4} mb={8}>
<Surface variant="muted" rounded="xl" border padding={4}> <Surface variant="muted" rounded="xl" border padding={4}>
<Icon icon={User} size={8} color="var(--color-primary)" /> <Icon icon={User} size={8} color="var(--ui-color-intent-primary)" />
</Surface> </Surface>
<Box> <Box>
<Heading level={1}>Create Your Driver Profile</Heading> <Heading level={1}>Create Your Driver Profile</Heading>
<Text color="text-gray-400">Join the GridPilot community and start your racing journey</Text> <Text variant="low">Join the GridPilot community and start your racing journey</Text>
</Box> </Box>
<Box maxWidth="42rem" mx="auto" width="100%"> <Box maxWidth="42rem" mx="auto" width="100%">
@@ -51,7 +51,7 @@ export function ProfileTemplate({
<Stack gap={6}> <Stack gap={6}>
<Box> <Box>
<Heading level={2}>Get Started</Heading> <Heading level={2}>Get Started</Heading>
<Text size="sm" color="text-gray-400"> <Text size="sm" variant="low">
Create your driver profile to join leagues, compete in races, and connect with other drivers. Create your driver profile to join leagues, compete in races, and connect with other drivers.
</Text> </Text>
</Box> </Box>
@@ -110,7 +110,7 @@ export function ProfileTemplate({
<Stack gap={4}> <Stack gap={4}>
<Stack direction="row" justify="between" align="center"> <Stack direction="row" justify="between" align="center">
<Heading level={3} id="achievements-heading">Achievements</Heading> <Heading level={3} id="achievements-heading">Achievements</Heading>
<Text size="sm" color="text-gray-500">{viewData.extendedProfile.achievements.length} earned</Text> <Text size="sm" variant="low">{viewData.extendedProfile.achievements.length} earned</Text>
</Stack> </Stack>
<AchievementGrid <AchievementGrid
achievements={viewData.extendedProfile.achievements.map(a => ({ achievements={viewData.extendedProfile.achievements.map(a => ({

View File

@@ -1,14 +1,12 @@
'use client'; 'use client';
import { import { Box } from '@/ui/Box';
SharedBox, import { Button } from '@/ui/Button';
SharedButton, import { Stack } from '@/ui/Stack';
SharedStack, import { Text } from '@/ui/Text';
SharedText, import { Icon } from '@/ui/Icon';
SharedIcon, import { Card } from '@/ui/Card';
SharedCard, import { Container } from '@/ui/Container';
SharedContainer
} from '@/components/shared/UIComponents';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Link as UILink } from '@/ui/Link'; import { Link as UILink } from '@/ui/Link';
import { Grid } from '@/ui/Grid'; import { Grid } from '@/ui/Grid';
@@ -99,114 +97,114 @@ export function ProtestDetailTemplate({
const daysSinceFiled = Math.floor((Date.now() - new Date(submittedAt).getTime()) / (1000 * 60 * 60 * 24)); const daysSinceFiled = Math.floor((Date.now() - new Date(submittedAt).getTime()) / (1000 * 60 * 60 * 24));
return ( return (
<SharedBox minHeight="100vh"> <Box minHeight="100vh">
<SharedContainer size="lg"> <Container size="lg">
<SharedBox paddingY={8}> <Box paddingY={8}>
{/* Compact Header */} {/* Compact Header */}
<SharedBox mb={6}> <Box mb={6}>
<SharedStack direction="row" align="center" gap={3} mb={4}> <Stack direction="row" align="center" gap={3} mb={4}>
<UILink href={routes.league.stewarding(leagueId)}> <UILink href={routes.league.stewarding(leagueId)}>
<SharedIcon icon={ArrowLeft} size={5} color="text-gray-400" /> <Icon icon={ArrowLeft} size={5} color="text-gray-400" />
</UILink> </UILink>
<SharedStack direction="row" align="center" gap={3} flexGrow={1}> <Stack direction="row" align="center" gap={3} flexGrow={1}>
<Heading level={1}>Protest Review</Heading> <Heading level={1}>Protest Review</Heading>
<SharedBox display="flex" alignItems="center" gap={1.5} px={2.5} py={1} rounded="full" fontSize="0.75rem" weight="medium" border bg={statusConfig.bg} color={statusConfig.color} borderColor={statusConfig.borderColor}> <Box display="flex" alignItems="center" gap={1.5} px={2.5} py={1} rounded="full" fontSize="0.75rem" weight="medium" border bg={statusConfig.bg} color={statusConfig.color} borderColor={statusConfig.borderColor}>
<SharedIcon icon={StatusIcon} size={3} /> <Icon icon={StatusIcon} size={3} />
<SharedText>{statusConfig.label}</SharedText> <Text>{statusConfig.label}</Text>
</SharedBox> </Box>
{daysSinceFiled > 2 && isPending && ( {daysSinceFiled > 2 && isPending && (
<SharedBox display="flex" alignItems="center" gap={1} px={2} py={0.5} fontSize="0.75rem" weight="medium" bg="bg-red-500/20" color="text-red-400" rounded="full"> <Box display="flex" alignItems="center" gap={1} px={2} py={0.5} fontSize="0.75rem" weight="medium" bg="bg-red-500/20" color="text-red-400" rounded="full">
<SharedIcon icon={AlertTriangle} size={3} /> <Icon icon={AlertTriangle} size={3} />
<SharedText>{daysSinceFiled}d old</SharedText> <Text>{daysSinceFiled}d old</Text>
</SharedBox> </Box>
)} )}
</SharedStack> </Stack>
</SharedStack> </Stack>
</SharedBox> </Box>
{/* Main Layout: Feed + Sidebar */} {/* Main Layout: Feed + Sidebar */}
<Grid cols={12} gap={6}> <Grid cols={12} gap={6}>
{/* Left Sidebar - Incident Info */} {/* Left Sidebar - Incident Info */}
<GridItem colSpan={{ base: 12, lg: 3 }}> <GridItem colSpan={{ base: 12, lg: 3 }}>
<SharedStack gap={4}> <Stack gap={4}>
{/* Drivers Involved */} {/* Drivers Involved */}
<SharedCard> <Card>
<SharedBox p={4}> <Box p={4}>
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Parties Involved</Heading> <Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Parties Involved</Heading>
<SharedStack gap={3}> <Stack gap={3}>
{/* Protesting Driver */} {/* Protesting Driver */}
<UILink href={routes.driver.detail(protestingDriver?.id || '')} block> <UILink href={routes.driver.detail(protestingDriver?.id || '')} block>
<SharedBox display="flex" alignItems="center" gap={3} p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-blue-500/50" hoverBg="bg-blue-500/5" transition cursor="pointer"> <Box display="flex" alignItems="center" gap={3} p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-blue-500/50" hoverBg="bg-blue-500/5" transition cursor="pointer">
<SharedBox w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}> <Box w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
<SharedIcon icon={User} size={5} color="text-blue-400" /> <Icon icon={User} size={5} color="text-blue-400" />
</SharedBox> </Box>
<SharedBox flexGrow={1} minWidth="0"> <Box flexGrow={1} minWidth="0">
<SharedText size="xs" color="text-blue-400" weight="medium" block>Protesting</SharedText> <Text size="xs" color="text-blue-400" weight="medium" block>Protesting</Text>
<SharedText size="sm" weight="semibold" color="text-white" truncate block>{protestingDriver?.name || 'Unknown'}</SharedText> <Text size="sm" weight="semibold" color="text-white" truncate block>{protestingDriver?.name || 'Unknown'}</Text>
</SharedBox> </Box>
<SharedIcon icon={ExternalLink} size={3} color="text-gray-500" /> <Icon icon={ExternalLink} size={3} color="text-gray-500" />
</SharedBox> </Box>
</UILink> </UILink>
{/* Accused Driver */} {/* Accused Driver */}
<UILink href={routes.driver.detail(accusedDriver?.id || '')} block> <UILink href={routes.driver.detail(accusedDriver?.id || '')} block>
<SharedBox display="flex" alignItems="center" gap={3} p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-orange-500/50" hoverBg="bg-orange-500/5" transition cursor="pointer"> <Box display="flex" alignItems="center" gap={3} p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-orange-500/50" hoverBg="bg-orange-500/5" transition cursor="pointer">
<SharedBox w={10} h={10} rounded="full" bg="bg-orange-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}> <Box w={10} h={10} rounded="full" bg="bg-orange-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
<SharedIcon icon={User} size={5} color="text-orange-400" /> <Icon icon={User} size={5} color="text-orange-400" />
</SharedBox> </Box>
<SharedBox flexGrow={1} minWidth="0"> <Box flexGrow={1} minWidth="0">
<SharedText size="xs" color="text-orange-400" weight="medium" block>Accused</SharedText> <Text size="xs" color="text-orange-400" weight="medium" block>Accused</Text>
<SharedText size="sm" weight="semibold" color="text-white" truncate block>{accusedDriver?.name || 'Unknown'}</SharedText> <Text size="sm" weight="semibold" color="text-white" truncate block>{accusedDriver?.name || 'Unknown'}</Text>
</SharedBox> </Box>
<SharedIcon icon={ExternalLink} size={3} color="text-gray-500" /> <Icon icon={ExternalLink} size={3} color="text-gray-500" />
</SharedBox> </Box>
</UILink> </UILink>
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedCard> </Card>
{/* Race Info */} {/* Race Info */}
<SharedCard> <Card>
<SharedBox p={4}> <Box p={4}>
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Race Details</Heading> <Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Race Details</Heading>
<SharedBox marginBottom={3}> <Box marginBottom={3}>
<UILink <UILink
href={routes.race.detail(race?.id || '')} href={routes.race.detail(race?.id || '')}
block block
> >
<SharedBox p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-primary-blue/50" hoverBg="bg-primary-blue/5" transition> <Box p={3} rounded="lg" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" hoverBorderColor="border-primary-blue/50" hoverBg="bg-primary-blue/5" transition>
<SharedBox display="flex" alignItems="center" justifyContent="between"> <Box display="flex" alignItems="center" justifyContent="between">
<SharedText size="sm" weight="medium" color="text-white">{race?.name || 'Unknown Race'}</SharedText> <Text size="sm" weight="medium" color="text-white">{race?.name || 'Unknown Race'}</Text>
<SharedIcon icon={ExternalLink} size={3} color="text-gray-500" /> <Icon icon={ExternalLink} size={3} color="text-gray-500" />
</SharedBox> </Box>
</SharedBox> </Box>
</UILink> </UILink>
</SharedBox> </Box>
<SharedStack gap={2}> <Stack gap={2}>
<SharedBox display="flex" alignItems="center" gap={2}> <Box display="flex" alignItems="center" gap={2}>
<SharedIcon icon={MapPin} size={4} color="text-gray-500" /> <Icon icon={MapPin} size={4} color="text-gray-500" />
<SharedText size="sm" color="text-gray-300">{race?.name || 'Unknown Track'}</SharedText> <Text size="sm" color="text-gray-300">{race?.name || 'Unknown Track'}</Text>
</SharedBox> </Box>
<SharedBox display="flex" alignItems="center" gap={2}> <Box display="flex" alignItems="center" gap={2}>
<SharedIcon icon={Calendar} size={4} color="text-gray-500" /> <Icon icon={Calendar} size={4} color="text-gray-500" />
<SharedText size="sm" color="text-gray-300">{race?.formattedDate || (race?.scheduledAt ? new Date(race.scheduledAt).toLocaleDateString() : 'Unknown Date')}</SharedText> <Text size="sm" color="text-gray-300">{race?.formattedDate || (race?.scheduledAt ? new Date(race.scheduledAt).toLocaleDateString() : 'Unknown Date')}</Text>
</SharedBox> </Box>
{protest.incident?.lap && ( {protest.incident?.lap && (
<SharedBox display="flex" alignItems="center" gap={2}> <Box display="flex" alignItems="center" gap={2}>
<SharedIcon icon={Flag} size={4} color="text-gray-500" /> <Icon icon={Flag} size={4} color="text-gray-500" />
<SharedText size="sm" color="text-gray-300">Lap {protest.incident.lap}</SharedText> <Text size="sm" color="text-gray-300">Lap {protest.incident.lap}</Text>
</SharedBox> </Box>
)} )}
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedCard> </Card>
{protest.proofVideoUrl && ( {protest.proofVideoUrl && (
<SharedCard> <Card>
<SharedBox p={4}> <Box p={4}>
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Evidence</Heading> <Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Evidence</Heading>
<UILink <UILink
href={protest.proofVideoUrl} href={protest.proofVideoUrl}
@@ -214,133 +212,133 @@ export function ProtestDetailTemplate({
rel="noopener noreferrer" rel="noopener noreferrer"
block block
> >
<SharedBox display="flex" alignItems="center" gap={2} p={3} rounded="lg" bg="bg-primary-blue/10" border borderColor="border-primary-blue/20" color="text-primary-blue" hoverBg="bg-primary-blue/20" transition> <Box display="flex" alignItems="center" gap={2} p={3} rounded="lg" bg="bg-primary-blue/10" border borderColor="border-primary-blue/20" color="text-primary-blue" hoverBg="bg-primary-blue/20" transition>
<SharedIcon icon={Video} size={4} /> <Icon icon={Video} size={4} />
<SharedText size="sm" weight="medium" flexGrow={1}>Watch Video</SharedText> <Text size="sm" weight="medium" flexGrow={1}>Watch Video</Text>
<SharedIcon icon={ExternalLink} size={3} /> <Icon icon={ExternalLink} size={3} />
</SharedBox> </Box>
</UILink> </UILink>
</SharedBox> </Box>
</SharedCard> </Card>
)} )}
{/* Quick Stats */} {/* Quick Stats */}
<SharedCard> <Card>
<SharedBox p={4}> <Box p={4}>
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Timeline</Heading> <Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Timeline</Heading>
<SharedStack gap={2}> <Stack gap={2}>
<SharedBox display="flex" justifyContent="between"> <Box display="flex" justifyContent="between">
<SharedText size="sm" color="text-gray-500">Filed</SharedText> <Text size="sm" color="text-gray-500">Filed</Text>
<SharedText size="sm" color="text-gray-300">{new Date(submittedAt).toLocaleDateString()}</SharedText> <Text size="sm" color="text-gray-300">{new Date(submittedAt).toLocaleDateString()}</Text>
</SharedBox> </Box>
<SharedBox display="flex" justifyContent="between"> <Box display="flex" justifyContent="between">
<SharedText size="sm" color="text-gray-500">Age</SharedText> <Text size="sm" color="text-gray-500">Age</Text>
<SharedText size="sm" color={daysSinceFiled > 2 ? 'text-red-400' : 'text-gray-300'}>{daysSinceFiled} days</SharedText> <Text size="sm" color={daysSinceFiled > 2 ? 'text-red-400' : 'text-gray-300'}>{daysSinceFiled} days</Text>
</SharedBox> </Box>
{protest.reviewedAt && ( {protest.reviewedAt && (
<SharedBox display="flex" justifyContent="between"> <Box display="flex" justifyContent="between">
<SharedText size="sm" color="text-gray-500">Resolved</SharedText> <Text size="sm" color="text-gray-500">Resolved</Text>
<SharedText size="sm" color="text-gray-300">{new Date(protest.reviewedAt).toLocaleDateString()}</SharedText> <Text size="sm" color="text-gray-300">{new Date(protest.reviewedAt).toLocaleDateString()}</Text>
</SharedBox> </Box>
)} )}
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedCard> </Card>
</SharedStack> </Stack>
</GridItem> </GridItem>
{/* Center - Discussion Feed */} {/* Center - Discussion Feed */}
<GridItem colSpan={12} lgSpan={6}> <GridItem colSpan={12} lgSpan={6}>
<SharedStack gap={4}> <Stack gap={4}>
{/* Timeline / Feed */} {/* Timeline / Feed */}
<SharedCard> <Card>
<SharedBox borderBottom borderColor="border-charcoal-outline" bg="bg-iron-gray/30" p={4}> <Box borderBottom borderColor="border-charcoal-outline" bg="bg-iron-gray/30" p={4}>
<Heading level={2}>Discussion</Heading> <Heading level={2}>Discussion</Heading>
</SharedBox> </Box>
<SharedStack gap={0}> <Stack gap={0}>
{/* Initial Protest Filing */} {/* Initial Protest Filing */}
<SharedBox p={4}> <Box p={4}>
<SharedBox display="flex" gap={3}> <Box display="flex" gap={3}>
<SharedBox w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}> <Box w={10} h={10} rounded="full" bg="bg-blue-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
<SharedIcon icon={AlertCircle} size={5} color="text-blue-400" /> <Icon icon={AlertCircle} size={5} color="text-blue-400" />
</SharedBox> </Box>
<SharedBox flexGrow={1} minWidth="0"> <Box flexGrow={1} minWidth="0">
<SharedBox display="flex" alignItems="center" gap={2} mb={1}> <Box display="flex" alignItems="center" gap={2} mb={1}>
<SharedText weight="semibold" color="text-white" size="sm">{protestingDriver?.name || 'Unknown'}</SharedText> <Text weight="semibold" color="text-white" size="sm">{protestingDriver?.name || 'Unknown'}</Text>
<SharedText size="xs" color="text-blue-400" weight="medium">filed protest</SharedText> <Text size="xs" color="text-blue-400" weight="medium">filed protest</Text>
<SharedText size="xs" color="text-gray-500"></SharedText> <Text size="xs" color="text-gray-500"></Text>
<SharedText size="xs" color="text-gray-500">{new Date(submittedAt).toLocaleString()}</SharedText> <Text size="xs" color="text-gray-500">{new Date(submittedAt).toLocaleString()}</Text>
</SharedBox> </Box>
<SharedBox bg="bg-deep-graphite" rounded="lg" p={4} border borderColor="border-charcoal-outline"> <Box bg="bg-deep-graphite" rounded="lg" p={4} border borderColor="border-charcoal-outline">
<SharedText size="sm" color="text-gray-300" block mb={3}>{protest.description || protestDetail.incident?.description}</SharedText> <Text size="sm" color="text-gray-300" block mb={3}>{protest.description || protestDetail.incident?.description}</Text>
{(protest.comment || protestDetail.comment) && ( {(protest.comment || protestDetail.comment) && (
<SharedBox mt={3} pt={3} borderTop borderColor="border-charcoal-outline/50"> <Box mt={3} pt={3} borderTop borderColor="border-charcoal-outline/50">
<SharedText size="xs" color="text-gray-500" block mb={1}>Additional details:</SharedText> <Text size="xs" color="text-gray-500" block mb={1}>Additional details:</Text>
<SharedText size="sm" color="text-gray-400">{protest.comment || protestDetail.comment}</SharedText> <Text size="sm" color="text-gray-400">{protest.comment || protestDetail.comment}</Text>
</SharedBox> </Box>
)} )}
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
{/* Defense placeholder */} {/* Defense placeholder */}
{protest.status === 'awaiting_defense' && ( {protest.status === 'awaiting_defense' && (
<SharedBox p={4} bg="bg-purple-500/5"> <Box p={4} bg="bg-purple-500/5">
<SharedBox display="flex" gap={3}> <Box display="flex" gap={3}>
<SharedBox w={10} h={10} rounded="full" bg="bg-purple-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}> <Box w={10} h={10} rounded="full" bg="bg-purple-500/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
<SharedIcon icon={MessageCircle} size={5} color="text-purple-400" /> <Icon icon={MessageCircle} size={5} color="text-purple-400" />
</SharedBox> </Box>
<SharedBox flexGrow={1}> <Box flexGrow={1}>
<SharedText size="sm" color="text-purple-400" weight="medium" block mb={1}>Defense Requested</SharedText> <Text size="sm" color="text-purple-400" weight="medium" block mb={1}>Defense Requested</Text>
<SharedText size="sm" color="text-gray-400">Waiting for {accusedDriver?.name || 'the accused driver'} to submit their defense...</SharedText> <Text size="sm" color="text-gray-400">Waiting for {accusedDriver?.name || 'the accused driver'} to submit their defense...</Text>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
)} )}
{/* Decision (if resolved) */} {/* Decision (if resolved) */}
{(protest.status === 'upheld' || protest.status === 'dismissed') && protest.decisionNotes && ( {(protest.status === 'upheld' || protest.status === 'dismissed') && protest.decisionNotes && (
<SharedBox p={4} bg={protest.status === 'upheld' ? 'bg-red-500/5' : 'bg-gray-500/5'}> <Box p={4} bg={protest.status === 'upheld' ? 'bg-red-500/5' : 'bg-gray-500/5'}>
<SharedBox display="flex" gap={3}> <Box display="flex" gap={3}>
<SharedBox w={10} h={10} rounded="full" display="flex" alignItems="center" justifyContent="center" flexShrink={0} bg={protest.status === 'upheld' ? 'bg-red-500/20' : 'bg-gray-500/20'}> <Box w={10} h={10} rounded="full" display="flex" alignItems="center" justifyContent="center" flexShrink={0} bg={protest.status === 'upheld' ? 'bg-red-500/20' : 'bg-gray-500/20'}>
<SharedIcon icon={Gavel} size={5} color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'} /> <Icon icon={Gavel} size={5} color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'} />
</SharedBox> </Box>
<SharedBox flexGrow={1} minWidth="0"> <Box flexGrow={1} minWidth="0">
<SharedBox display="flex" alignItems="center" gap={2} mb={1}> <Box display="flex" alignItems="center" gap={2} mb={1}>
<SharedText weight="semibold" color="text-white" size="sm">Steward Decision</SharedText> <Text weight="semibold" color="text-white" size="sm">Steward Decision</Text>
<SharedText size="xs" weight="medium" color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'}> <Text size="xs" weight="medium" color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'}>
{protest.status === 'upheld' ? 'Protest Upheld' : 'Protest Dismissed'} {protest.status === 'upheld' ? 'Protest Upheld' : 'Protest Dismissed'}
</SharedText> </Text>
{protest.reviewedAt && ( {protest.reviewedAt && (
<> <>
<SharedText size="xs" color="text-gray-500"></SharedText> <Text size="xs" color="text-gray-500"></Text>
<SharedText size="xs" color="text-gray-500">{new Date(protest.reviewedAt).toLocaleString()}</SharedText> <Text size="xs" color="text-gray-500">{new Date(protest.reviewedAt).toLocaleString()}</Text>
</> </>
)} )}
</SharedBox> </Box>
<SharedBox rounded="lg" p={4} border bg={protest.status === 'upheld' ? 'bg-red-500/10' : 'bg-gray-500/10'} borderColor={protest.status === 'upheld' ? 'border-red-500/20' : 'border-gray-500/20'}> <Box rounded="lg" p={4} border bg={protest.status === 'upheld' ? 'bg-red-500/10' : 'bg-gray-500/10'} borderColor={protest.status === 'upheld' ? 'border-red-500/20' : 'border-gray-500/20'}>
<SharedText size="sm" color="text-gray-300">{protest.decisionNotes}</SharedText> <Text size="sm" color="text-gray-300">{protest.decisionNotes}</Text>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
)} )}
</SharedStack> </Stack>
{/* Add Comment */} {/* Add Comment */}
{isPending && ( {isPending && (
<SharedBox p={4} borderTop borderColor="border-charcoal-outline" bg="bg-iron-gray/20"> <Box p={4} borderTop borderColor="border-charcoal-outline" bg="bg-iron-gray/20">
<SharedBox display="flex" gap={3}> <Box display="flex" gap={3}>
<SharedBox w={10} h={10} rounded="full" bg="bg-iron-gray" display="flex" alignItems="center" justifyContent="center" flexShrink={0}> <Box w={10} h={10} rounded="full" bg="bg-iron-gray" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
<SharedIcon icon={User} size={5} color="text-gray-500" /> <Icon icon={User} size={5} color="text-gray-500" />
</SharedBox> </Box>
<SharedBox flexGrow={1}> <Box flexGrow={1}>
<SharedBox as="textarea" <Box as="textarea"
value={newComment} value={newComment}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setNewComment(e.target.value)} onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setNewComment(e.target.value)}
placeholder="Add a comment or request more information..." placeholder="Add a comment or request more information..."
@@ -355,102 +353,102 @@ export function ProtestDetailTemplate({
color="text-white" color="text-white"
fontSize="sm" fontSize="sm"
/> />
<SharedBox display="flex" justifyContent="end" mt={2}> <Box display="flex" justifyContent="end" mt={2}>
<SharedButton variant="secondary" disabled={!newComment.trim()}> <Button variant="secondary" disabled={!newComment.trim()}>
<SharedIcon icon={Send} size={3} style={{ marginRight: '0.25rem' }} /> <Icon icon={Send} size={3} style={{ marginRight: '0.25rem' }} />
Comment Comment
</SharedButton> </Button>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedBox> </Box>
)} )}
</SharedCard> </Card>
</SharedStack> </Stack>
</GridItem> </GridItem>
{/* Right Sidebar - Actions */} {/* Right Sidebar - Actions */}
<GridItem colSpan={12} lgSpan={3}> <GridItem colSpan={12} lgSpan={3}>
<SharedStack gap={4}> <Stack gap={4}>
{isPending && ( {isPending && (
<> <>
{/* Quick Actions */} {/* Quick Actions */}
<SharedCard> <Card>
<SharedBox p={4}> <Box p={4}>
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Actions</Heading> <Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Actions</Heading>
<SharedStack gap={2}> <Stack gap={2}>
<SharedButton <Button
variant="secondary" variant="secondary"
fullWidth fullWidth
onClick={onRequestDefense} onClick={onRequestDefense}
> >
<SharedStack direction="row" align="center" gap={2}> <Stack direction="row" align="center" gap={2}>
<SharedIcon icon={MessageCircle} size={4} /> <Icon icon={MessageCircle} size={4} />
<SharedText>Request Defense</SharedText> <Text>Request Defense</Text>
</SharedStack> </Stack>
</SharedButton> </Button>
<SharedButton <Button
variant="primary" variant="primary"
fullWidth fullWidth
onClick={() => setShowDecisionPanel(!showDecisionPanel)} onClick={() => setShowDecisionPanel(!showDecisionPanel)}
> >
<SharedStack direction="row" align="center" gap={2} fullWidth> <Stack direction="row" align="center" gap={2} fullWidth>
<SharedIcon icon={Gavel} size={4} /> <Icon icon={Gavel} size={4} />
<SharedText>Make Decision</SharedText> <Text>Make Decision</Text>
<SharedBox ml="auto" transition style={{ transform: showDecisionPanel ? 'rotate(180deg)' : 'none' }}> <Box ml="auto" transition style={{ transform: showDecisionPanel ? 'rotate(180deg)' : 'none' }}>
<SharedIcon icon={ChevronDown} size={4} /> <Icon icon={ChevronDown} size={4} />
</SharedBox> </Box>
</SharedStack> </Stack>
</SharedButton> </Button>
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedCard> </Card>
{/* Decision Panel */} {/* Decision Panel */}
{showDecisionPanel && ( {showDecisionPanel && (
<SharedCard> <Card>
<SharedBox p={4}> <Box p={4}>
<Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Stewarding Decision</Heading> <Heading level={3} fontSize="xs" weight="semibold" color="text-gray-500" mb={3}>Stewarding Decision</Heading>
{/* Decision Selection */} {/* Decision Selection */}
<Grid cols={2} gap={2} mb={4}> <Grid cols={2} gap={2} mb={4}>
<SharedBox padding={3} border borderColor={decision === 'uphold' ? 'border-racing-red' : 'border-charcoal-outline'} bg={decision === 'uphold' ? 'bg-racing-red/10' : 'transparent'} rounded="lg"> <Box padding={3} border borderColor={decision === 'uphold' ? 'border-racing-red' : 'border-charcoal-outline'} bg={decision === 'uphold' ? 'bg-racing-red/10' : 'transparent'} rounded="lg">
<SharedButton <Button
variant="ghost" variant="ghost"
onClick={() => setDecision('uphold')} onClick={() => setDecision('uphold')}
fullWidth fullWidth
> >
<SharedStack align="center" gap={1}> <Stack align="center" gap={1}>
<SharedIcon icon={CheckCircle} size={5} color={decision === 'uphold' ? 'text-red-400' : 'text-gray-500'} /> <Icon icon={CheckCircle} size={5} color={decision === 'uphold' ? 'text-red-400' : 'text-gray-500'} />
<SharedText size="xs" weight="medium" color={decision === 'uphold' ? 'text-red-400' : 'text-gray-400'}>Uphold</SharedText> <Text size="xs" weight="medium" color={decision === 'uphold' ? 'text-red-400' : 'text-gray-400'}>Uphold</Text>
</SharedStack> </Stack>
</SharedButton> </Button>
</SharedBox> </Box>
<SharedBox padding={3} border borderColor={decision === 'dismiss' ? 'border-gray-500' : 'border-charcoal-outline'} bg={decision === 'dismiss' ? 'bg-gray-500/10' : 'transparent'} rounded="lg"> <Box padding={3} border borderColor={decision === 'dismiss' ? 'border-gray-500' : 'border-charcoal-outline'} bg={decision === 'dismiss' ? 'bg-gray-500/10' : 'transparent'} rounded="lg">
<SharedButton <Button
variant="ghost" variant="ghost"
onClick={() => setDecision('dismiss')} onClick={() => setDecision('dismiss')}
fullWidth fullWidth
> >
<SharedStack align="center" gap={1}> <Stack align="center" gap={1}>
<SharedIcon icon={XCircle} size={5} color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-500'} /> <Icon icon={XCircle} size={5} color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-500'} />
<SharedText size="xs" weight="medium" color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-400'}>Dismiss</SharedText> <Text size="xs" weight="medium" color={decision === 'dismiss' ? 'text-gray-300' : 'text-gray-400'}>Dismiss</Text>
</SharedStack> </Stack>
</SharedButton> </Button>
</SharedBox> </Box>
</Grid> </Grid>
{/* Penalty Selection (if upholding) */} {/* Penalty Selection (if upholding) */}
{decision === 'uphold' && ( {decision === 'uphold' && (
<SharedBox mb={4}> <Box mb={4}>
<SharedText as="label" size="xs" weight="medium" color="text-gray-400" block mb={2}>Penalty Type</SharedText> <Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={2}>Penalty Type</Text>
{penaltyTypes.length === 0 ? ( {penaltyTypes.length === 0 ? (
<SharedText size="xs" color="text-gray-500"> <Text size="xs" color="text-gray-500">
Loading penalty types... Loading penalty types...
</SharedText> </Text>
) : ( ) : (
<> <>
<Grid cols={2} gap={2}> <Grid cols={2} gap={2}>
@@ -458,8 +456,8 @@ export function ProtestDetailTemplate({
const Icon = penalty.icon; const Icon = penalty.icon;
const isSelected = penaltyType === penalty.type; const isSelected = penaltyType === penalty.type;
return ( return (
<SharedBox key={penalty.type} padding={2} border borderColor={isSelected ? undefined : 'border-charcoal-outline'} bg={isSelected ? undefined : 'bg-iron-gray/30'} rounded="lg"> <Box key={penalty.type} padding={2} border borderColor={isSelected ? undefined : 'border-charcoal-outline'} bg={isSelected ? undefined : 'bg-iron-gray/30'} rounded="lg">
<SharedButton <Button
variant="ghost" variant="ghost"
onClick={() => { onClick={() => {
setPenaltyType(penalty.type); setPenaltyType(penalty.type);
@@ -468,24 +466,24 @@ export function ProtestDetailTemplate({
fullWidth fullWidth
title={penalty.description} title={penalty.description}
> >
<SharedStack align="start" gap={0.5}> <Stack align="start" gap={0.5}>
<SharedIcon icon={Icon} size={3.5} color={isSelected ? penalty.color : 'text-gray-500'} /> <Icon icon={Icon} size={3.5} color={isSelected ? penalty.color : 'text-gray-500'} />
<SharedText size="xs" weight="medium" fontSize="10px" color={isSelected ? penalty.color : 'text-gray-500'}> <Text size="xs" weight="medium" fontSize="10px" color={isSelected ? penalty.color : 'text-gray-500'}>
{penalty.label} {penalty.label}
</SharedText> </Text>
</SharedStack> </Stack>
</SharedButton> </Button>
</SharedBox> </Box>
); );
})} })}
</Grid> </Grid>
{selectedPenalty?.requiresValue && ( {selectedPenalty?.requiresValue && (
<SharedBox mt={3}> <Box mt={3}>
<SharedText as="label" size="xs" weight="medium" color="text-gray-400" block mb={1}> <Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={1}>
Value ({selectedPenalty.valueLabel}) Value ({selectedPenalty.valueLabel})
</SharedText> </Text>
<SharedBox as="input" <Box as="input"
type="number" type="number"
value={penaltyValue} value={penaltyValue}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPenaltyValue(Number(e.target.value))} onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPenaltyValue(Number(e.target.value))}
@@ -500,17 +498,17 @@ export function ProtestDetailTemplate({
color="text-white" color="text-white"
fontSize="sm" fontSize="sm"
/> />
</SharedBox> </Box>
)} )}
</> </>
)} )}
</SharedBox> </Box>
)} )}
{/* Steward Notes */} {/* Steward Notes */}
<SharedBox mb={4}> <Box mb={4}>
<SharedText as="label" size="xs" weight="medium" color="text-gray-400" block mb={1}>Decision Reasoning *</SharedText> <Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={1}>Decision Reasoning *</Text>
<SharedBox as="textarea" <Box as="textarea"
value={stewardNotes} value={stewardNotes}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setStewardNotes(e.target.value)} onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setStewardNotes(e.target.value)}
placeholder="Explain your decision..." placeholder="Explain your decision..."
@@ -525,42 +523,42 @@ export function ProtestDetailTemplate({
color="text-white" color="text-white"
fontSize="sm" fontSize="sm"
/> />
</SharedBox> </Box>
{/* Submit */} {/* Submit */}
<SharedButton <Button
variant="primary" variant="primary"
fullWidth fullWidth
onClick={onSubmitDecision} onClick={onSubmitDecision}
disabled={!decision || !stewardNotes.trim() || submitting} disabled={!decision || !stewardNotes.trim() || submitting}
> >
{submitting ? 'Submitting...' : 'Submit Decision'} {submitting ? 'Submitting...' : 'Submit Decision'}
</SharedButton> </Button>
</SharedBox> </Box>
</SharedCard> </Card>
)} )}
</> </>
)} )}
{/* Already Resolved Info */} {/* Already Resolved Info */}
{!isPending && ( {!isPending && (
<SharedCard> <Card>
<SharedBox p={4} textAlign="center"> <Box p={4} textAlign="center">
<SharedBox py={4} color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'}> <Box py={4} color={protest.status === 'upheld' ? 'text-red-400' : 'text-gray-400'}>
<SharedIcon icon={Gavel} size={8} style={{ margin: '0 auto 0.5rem auto' }} /> <Icon icon={Gavel} size={8} style={{ margin: '0 auto 0.5rem auto' }} />
<SharedText weight="semibold" block>Case Closed</SharedText> <Text weight="semibold" block>Case Closed</Text>
<SharedText size="xs" color="text-gray-500" block mt={1}> <Text size="xs" color="text-gray-500" block mt={1}>
{protest.status === 'upheld' ? 'Protest was upheld' : 'Protest was dismissed'} {protest.status === 'upheld' ? 'Protest was upheld' : 'Protest was dismissed'}
</SharedText> </Text>
</SharedBox> </Box>
</SharedBox> </Box>
</SharedCard> </Card>
)} )}
</SharedStack> </Stack>
</GridItem> </GridItem>
</Grid> </Grid>
</SharedBox> </Box>
</SharedContainer> </Container>
</SharedBox> </Box>
); );
} }

View File

@@ -7,7 +7,9 @@ import { RacesAllLayout, RacesAllStats } from '@/components/races/RacesAllLayout
import { RaceScheduleSection } from '@/components/races/RacesLayout'; import { RaceScheduleSection } from '@/components/races/RacesLayout';
import type { SessionStatus } from '@/components/races/SessionStatusBadge'; import type { SessionStatus } from '@/components/races/SessionStatusBadge';
import type { RacesViewData } from '@/lib/view-data/RacesViewData'; import type { RacesViewData } from '@/lib/view-data/RacesViewData';
import { SharedPagination, SharedText, SharedBox } from '@/components/shared/UIComponents'; import { Pagination } from '@/ui/Pagination';
import { Text } from '@/ui/Text';
import { Box } from '@/ui/Box';
import { TemplateProps } from '@/lib/contracts/components/ComponentContracts'; import { TemplateProps } from '@/lib/contracts/components/ComponentContracts';
export type StatusFilter = 'scheduled' | 'running' | 'completed' | 'cancelled' | 'all'; export type StatusFilter = 'scheduled' | 'running' | 'completed' | 'cancelled' | 'all';
@@ -76,7 +78,7 @@ export function RacesAllTemplate({
/> />
} }
pagination={ pagination={
<SharedPagination <Pagination
currentPage={currentPage} currentPage={currentPage}
totalPages={totalPages} totalPages={totalPages}
onPageChange={onPageChange} onPageChange={onPageChange}
@@ -85,9 +87,9 @@ export function RacesAllTemplate({
> >
<RaceScheduleSection title="Race Schedule"> <RaceScheduleSection title="Race Schedule">
{races.length === 0 ? ( {races.length === 0 ? (
<SharedBox p={12} textAlign="center"> <Box p={12} textAlign="center">
<SharedText color="text-gray-500">No races found matching your criteria.</SharedText> <Text color="text-gray-500">No races found matching your criteria.</Text>
</SharedBox> </Box>
) : ( ) : (
<RaceScheduleTable <RaceScheduleTable
races={races.map(race => ({ races={races.map(race => ({

View File

@@ -7,15 +7,13 @@ import { SponsorDashboardHeader } from '@/components/sponsors/SponsorDashboardHe
import { SponsorStatusChip } from '@/components/sponsors/SponsorStatusChip'; import { SponsorStatusChip } from '@/components/sponsors/SponsorStatusChip';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { siteConfig } from '@/lib/siteConfig'; import { siteConfig } from '@/lib/siteConfig';
import { import { Box } from '@/ui/Box';
SharedBox, import { Button } from '@/ui/Button';
SharedButton, import { Stack } from '@/ui/Stack';
SharedStack, import { Text } from '@/ui/Text';
SharedText, import { Icon } from '@/ui/Icon';
SharedIcon, import { Card } from '@/ui/Card';
SharedCard, import { Container } from '@/ui/Container';
SharedContainer
} from '@/components/shared/UIComponents';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Link } from '@/ui/Link'; import { Link } from '@/ui/Link';
import { Grid } from '@/ui/Grid'; import { Grid } from '@/ui/Grid';
@@ -187,23 +185,23 @@ export function SponsorLeagueDetailTemplate({
]; ];
return ( return (
<SharedContainer size="lg"> <Container size="lg">
<SharedBox paddingY={8}> <Box paddingY={8}>
<SharedStack gap={8}> <Stack gap={8}>
{/* Breadcrumb */} {/* Breadcrumb */}
<SharedBox> <Box>
<SharedStack direction="row" align="center" gap={2}> <Stack direction="row" align="center" gap={2}>
<Link href={routes.sponsor.dashboard}> <Link href={routes.sponsor.dashboard}>
<SharedText size="sm" color="text-gray-400">Dashboard</SharedText> <Text size="sm" color="text-gray-400">Dashboard</Text>
</Link> </Link>
<SharedText size="sm" color="text-gray-500">/</SharedText> <Text size="sm" color="text-gray-500">/</Text>
<Link href={routes.sponsor.leagues}> <Link href={routes.sponsor.leagues}>
<SharedText size="sm" color="text-gray-400">Leagues</SharedText> <Text size="sm" color="text-gray-400">Leagues</Text>
</Link> </Link>
<SharedText size="sm" color="text-gray-500">/</SharedText> <Text size="sm" color="text-gray-500">/</Text>
<SharedText size="sm" color="text-white">{league.name}</SharedText> <Text size="sm" color="text-white">{league.name}</Text>
</SharedStack> </Stack>
</SharedBox> </Box>
{/* Header */} {/* Header */}
<SponsorDashboardHeader <SponsorDashboardHeader
@@ -215,10 +213,10 @@ export function SponsorLeagueDetailTemplate({
<BillingSummaryPanel stats={billingStats} /> <BillingSummaryPanel stats={billingStats} />
{/* Tabs */} {/* Tabs */}
<SharedBox borderBottom borderColor="border-neutral-800"> <Box borderBottom borderColor="border-neutral-800">
<SharedStack direction="row" gap={6}> <Stack direction="row" gap={6}>
{(['overview', 'drivers', 'races', 'sponsor'] as const).map((tab) => ( {(['overview', 'drivers', 'races', 'sponsor'] as const).map((tab) => (
<SharedBox <Box
key={tab} key={tab}
onClick={() => setActiveTab(tab)} onClick={() => setActiveTab(tab)}
style={{ paddingBottom: '0.75rem' }} style={{ paddingBottom: '0.75rem' }}
@@ -227,148 +225,148 @@ export function SponsorLeagueDetailTemplate({
borderColor={activeTab === tab ? 'border-primary-blue' : 'border-transparent'} borderColor={activeTab === tab ? 'border-primary-blue' : 'border-transparent'}
color={activeTab === tab ? 'text-primary-blue' : 'text-gray-400'} color={activeTab === tab ? 'text-primary-blue' : 'text-gray-400'}
> >
<SharedText size="sm" weight="medium" className="uppercase"> <Text size="sm" weight="medium" className="uppercase">
{tab === 'sponsor' ? '🎯 Become a Sponsor' : tab} {tab === 'sponsor' ? '🎯 Become a Sponsor' : tab}
</SharedText> </Text>
</SharedBox> </Box>
))} ))}
</SharedStack> </Stack>
</SharedBox> </Box>
{/* Tab Content */} {/* Tab Content */}
{activeTab === 'overview' && ( {activeTab === 'overview' && (
<Grid cols={2} gap={6}> <Grid cols={2} gap={6}>
<SharedCard> <Card>
<SharedBox mb={4}> <Box mb={4}>
<SharedStack direction="row" align="center" gap={3}> <Stack direction="row" align="center" gap={3}>
<SharedIcon icon={Trophy} size={5} color="#3b82f6" /> <Icon icon={Trophy} size={5} color="#3b82f6" />
<Heading level={2}> <Heading level={2}>
League Information League Information
</Heading> </Heading>
</SharedStack> </Stack>
</SharedBox> </Box>
<SharedStack gap={3}> <Stack gap={3}>
<InfoRow label="Platform" value={league.game} /> <InfoRow label="Platform" value={league.game} />
<InfoRow label="Season" value={league.season} /> <InfoRow label="Season" value={league.season} />
<InfoRow label="Duration" value="Oct 2025 - Feb 2026" /> <InfoRow label="Duration" value="Oct 2025 - Feb 2026" />
<InfoRow label="Drivers" value={league.formattedDrivers} /> <InfoRow label="Drivers" value={league.formattedDrivers} />
<InfoRow label="Races" value={league.formattedRaces} last /> <InfoRow label="Races" value={league.formattedRaces} last />
</SharedStack> </Stack>
</SharedCard> </Card>
<SharedCard> <Card>
<SharedBox mb={4}> <Box mb={4}>
<Heading level={2} icon={<SharedIcon icon={TrendingUp} size={5} color="#10b981" />}> <Heading level={2} icon={<Icon icon={TrendingUp} size={5} color="#10b981" />}>
Sponsorship Value Sponsorship Value
</Heading> </Heading>
</SharedBox> </Box>
<SharedStack gap={3}> <Stack gap={3}>
<InfoRow label="Total Season Views" value={league.formattedTotalImpressions} /> <InfoRow label="Total Season Views" value={league.formattedTotalImpressions} />
<InfoRow label="Projected Total" value={league.formattedProjectedTotal} /> <InfoRow label="Projected Total" value={league.formattedProjectedTotal} />
<InfoRow label="Main Sponsor CPM" value={league.formattedMainSponsorCpm} color="text-performance-green" /> <InfoRow label="Main Sponsor CPM" value={league.formattedMainSponsorCpm} color="text-performance-green" />
<InfoRow label="Engagement Rate" value={league.formattedEngagement} /> <InfoRow label="Engagement Rate" value={league.formattedEngagement} />
<InfoRow label="League Rating" value={league.formattedRating} last /> <InfoRow label="League Rating" value={league.formattedRating} last />
</SharedStack> </Stack>
</SharedCard> </Card>
{league.nextRace && ( {league.nextRace && (
<GridItem colSpan={2}> <GridItem colSpan={2}>
<SharedCard> <Card>
<SharedBox mb={4}> <Box mb={4}>
<Heading level={2} icon={<SharedIcon icon={Flag} size={5} color="#f59e0b" />}> <Heading level={2} icon={<Icon icon={Flag} size={5} color="#f59e0b" />}>
Next Race Next Race
</Heading> </Heading>
</SharedBox> </Box>
<Surface variant="muted" rounded="lg" border padding={4} style={{ background: 'rgba(245, 158, 11, 0.05)', borderColor: 'rgba(245, 158, 11, 0.2)' }}> <Surface variant="muted" rounded="lg" border padding={4} style={{ background: 'rgba(245, 158, 11, 0.05)', borderColor: 'rgba(245, 158, 11, 0.2)' }}>
<SharedStack direction="row" align="center" justify="between"> <Stack direction="row" align="center" justify="between">
<SharedStack direction="row" align="center" gap={4}> <Stack direction="row" align="center" gap={4}>
<Surface variant="muted" rounded="lg" padding={3} style={{ background: 'rgba(245, 158, 11, 0.1)' }}> <Surface variant="muted" rounded="lg" padding={3} style={{ background: 'rgba(245, 158, 11, 0.1)' }}>
<SharedIcon icon={Flag} size={6} color="#f59e0b" /> <Icon icon={Flag} size={6} color="#f59e0b" />
</Surface> </Surface>
<SharedBox> <Box>
<SharedText size="lg" weight="semibold" color="text-white" block>{league.nextRace.name}</SharedText> <Text size="lg" weight="semibold" color="text-white" block>{league.nextRace.name}</Text>
<SharedText size="sm" color="text-gray-400" block mt={1}>{league.nextRace.formattedDate}</SharedText> <Text size="sm" color="text-gray-400" block mt={1}>{league.nextRace.formattedDate}</Text>
</SharedBox> </Box>
</SharedStack> </Stack>
<SharedButton variant="secondary"> <Button variant="secondary">
View Schedule View Schedule
</SharedButton> </Button>
</SharedStack> </Stack>
</Surface> </Surface>
</SharedCard> </Card>
</GridItem> </GridItem>
)} )}
</Grid> </Grid>
)} )}
{activeTab === 'drivers' && ( {activeTab === 'drivers' && (
<SharedCard p={0}> <Card p={0}>
<SharedBox p={4} borderBottom borderColor="border-neutral-800"> <Box p={4} borderBottom borderColor="border-neutral-800">
<Heading level={2}>Championship Standings</Heading> <Heading level={2}>Championship Standings</Heading>
<SharedText size="sm" color="text-gray-400" block mt={1}>Top drivers carrying sponsor branding</SharedText> <Text size="sm" color="text-gray-400" block mt={1}>Top drivers carrying sponsor branding</Text>
</SharedBox> </Box>
<SharedStack gap={0}> <Stack gap={0}>
{viewData.drivers.map((driver, index) => ( {viewData.drivers.map((driver, index) => (
<SharedBox key={driver.id} p={4} borderBottom={index < viewData.drivers.length - 1} borderColor="border-neutral-800/50"> <Box key={driver.id} p={4} borderBottom={index < viewData.drivers.length - 1} borderColor="border-neutral-800/50">
<SharedStack direction="row" align="center" justify="between"> <Stack direction="row" align="center" justify="between">
<SharedStack direction="row" align="center" gap={4}> <Stack direction="row" align="center" gap={4}>
<Surface variant="muted" rounded="full" padding={1} style={{ width: '2.5rem', height: '2.5rem', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#262626' }}> <Surface variant="muted" rounded="full" padding={1} style={{ width: '2.5rem', height: '2.5rem', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#262626' }}>
<SharedText weight="bold" color="text-white">{driver.positionLabel}</SharedText> <Text weight="bold" color="text-white">{driver.positionLabel}</Text>
</Surface> </Surface>
<SharedBox> <Box>
<SharedText weight="medium" color="text-white" block>{driver.name}</SharedText> <Text weight="medium" color="text-white" block>{driver.name}</Text>
<SharedText size="sm" color="text-gray-500" block mt={1}>{driver.team} {driver.country}</SharedText> <Text size="sm" color="text-gray-500" block mt={1}>{driver.team} {driver.country}</Text>
</SharedBox> </Box>
</SharedStack> </Stack>
<SharedStack direction="row" align="center" gap={8}> <Stack direction="row" align="center" gap={8}>
<SharedBox textAlign="right"> <Box textAlign="right">
<SharedText weight="medium" color="text-white" block>{driver.formattedRaces}</SharedText> <Text weight="medium" color="text-white" block>{driver.formattedRaces}</Text>
<SharedText size="xs" color="text-gray-500">races</SharedText> <Text size="xs" color="text-gray-500">races</Text>
</SharedBox> </Box>
<SharedBox textAlign="right"> <Box textAlign="right">
<SharedText weight="semibold" color="text-white" block>{driver.formattedImpressions}</SharedText> <Text weight="semibold" color="text-white" block>{driver.formattedImpressions}</Text>
<SharedText size="xs" color="text-gray-500">views</SharedText> <Text size="xs" color="text-gray-500">views</Text>
</SharedBox> </Box>
</SharedStack> </Stack>
</SharedStack> </Stack>
</SharedBox> </Box>
))} ))}
</SharedStack> </Stack>
</SharedCard> </Card>
)} )}
{activeTab === 'races' && ( {activeTab === 'races' && (
<SharedCard p={0}> <Card p={0}>
<SharedBox p={4} borderBottom borderColor="border-neutral-800"> <Box p={4} borderBottom borderColor="border-neutral-800">
<Heading level={2}>Race Calendar</Heading> <Heading level={2}>Race Calendar</Heading>
<SharedText size="sm" color="text-gray-400" block mt={1}>Season schedule with view statistics</SharedText> <Text size="sm" color="text-gray-400" block mt={1}>Season schedule with view statistics</Text>
</SharedBox> </Box>
<SharedStack gap={0}> <Stack gap={0}>
{viewData.races.map((race, index) => ( {viewData.races.map((race, index) => (
<SharedBox key={race.id} p={4} borderBottom={index < viewData.races.length - 1} borderColor="border-neutral-800/50"> <Box key={race.id} p={4} borderBottom={index < viewData.races.length - 1} borderColor="border-neutral-800/50">
<SharedStack direction="row" align="center" justify="between"> <Stack direction="row" align="center" justify="between">
<SharedStack direction="row" align="center" gap={4}> <Stack direction="row" align="center" gap={4}>
<SharedBox w="3" h="3" rounded="full" bg={race.status === 'completed' ? 'bg-performance-green' : 'bg-warning-amber'} /> <Box w="3" h="3" rounded="full" bg={race.status === 'completed' ? 'bg-performance-green' : 'bg-warning-amber'} />
<SharedBox> <Box>
<SharedText weight="medium" color="text-white" block>{race.name}</SharedText> <Text weight="medium" color="text-white" block>{race.name}</Text>
<SharedText size="sm" color="text-gray-500" block mt={1}>{race.formattedDate}</SharedText> <Text size="sm" color="text-gray-500" block mt={1}>{race.formattedDate}</Text>
</SharedBox> </Box>
</SharedStack> </Stack>
<SharedBox> <Box>
{race.status === 'completed' ? ( {race.status === 'completed' ? (
<SharedBox textAlign="right"> <Box textAlign="right">
<SharedText weight="semibold" color="text-white" block>{race.formattedViews}</SharedText> <Text weight="semibold" color="text-white" block>{race.formattedViews}</Text>
<SharedText size="xs" color="text-gray-500">views</SharedText> <Text size="xs" color="text-gray-500">views</Text>
</SharedBox> </Box>
) : ( ) : (
<SponsorStatusChip status="pending" label="Upcoming" /> <SponsorStatusChip status="pending" label="Upcoming" />
)} )}
</SharedBox> </Box>
</SharedStack> </Stack>
</SharedBox> </Box>
))} ))}
</SharedStack> </Stack>
</SharedCard> </Card>
)} )}
{activeTab === 'sponsor' && ( {activeTab === 'sponsor' && (
@@ -383,62 +381,62 @@ export function SponsorLeagueDetailTemplate({
</GridItem> </GridItem>
<GridItem colSpan={{ base: 12, lg: 4 }}> <GridItem colSpan={{ base: 12, lg: 4 }}>
<SharedStack gap={6}> <Stack gap={6}>
<SponsorBrandingPreview <SponsorBrandingPreview
name="Your Brand" name="Your Brand"
/> />
<SharedCard> <Card>
<SharedBox mb={4}> <Box mb={4}>
<Heading level={2} icon={<SharedIcon icon={CreditCard} size={5} color="#3b82f6" />}> <Heading level={2} icon={<Icon icon={CreditCard} size={5} color="#3b82f6" />}>
Sponsorship Summary Sponsorship Summary
</Heading> </Heading>
</SharedBox> </Box>
<SharedStack gap={3} mb={6}> <Stack gap={3} mb={6}>
<InfoRow label="Selected Tier" value={`${selectedTier.charAt(0).toUpperCase() + selectedTier.slice(1)} Sponsor`} /> <InfoRow label="Selected Tier" value={`${selectedTier.charAt(0).toUpperCase() + selectedTier.slice(1)} Sponsor`} />
<InfoRow label="Season Price" value={selectedTier === 'main' ? league.sponsorSlots.main.priceLabel : league.sponsorSlots.secondary.priceLabel} /> <InfoRow label="Season Price" value={selectedTier === 'main' ? league.sponsorSlots.main.priceLabel : league.sponsorSlots.secondary.priceLabel} />
<InfoRow label={`Platform Fee (${siteConfig.fees.platformFeePercent}%)`} value={`$${((selectedTier === 'main' ? league.sponsorSlots.main.price : league.sponsorSlots.secondary.price) * siteConfig.fees.platformFeePercent / 100).toFixed(2)}`} /> <InfoRow label={`Platform Fee (${siteConfig.fees.platformFeePercent}%)`} value={`$${((selectedTier === 'main' ? league.sponsorSlots.main.price : league.sponsorSlots.secondary.price) * siteConfig.fees.platformFeePercent / 100).toFixed(2)}`} />
<SharedBox pt={4} borderTop borderColor="border-neutral-800"> <Box pt={4} borderTop borderColor="border-neutral-800">
<SharedStack direction="row" align="center" justify="between"> <Stack direction="row" align="center" justify="between">
<SharedText weight="semibold" color="text-white">Total (excl. VAT)</SharedText> <Text weight="semibold" color="text-white">Total (excl. VAT)</Text>
<SharedText size="xl" weight="bold" color="text-white"> <Text size="xl" weight="bold" color="text-white">
${((selectedTier === 'main' ? league.sponsorSlots.main.price : league.sponsorSlots.secondary.price) * (1 + siteConfig.fees.platformFeePercent / 100)).toFixed(2)} ${((selectedTier === 'main' ? league.sponsorSlots.main.price : league.sponsorSlots.secondary.price) * (1 + siteConfig.fees.platformFeePercent / 100)).toFixed(2)}
</SharedText> </Text>
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedStack> </Stack>
<SharedText size="xs" color="text-gray-500" block mb={4}> <Text size="xs" color="text-gray-500" block mb={4}>
{siteConfig.vat.notice} {siteConfig.vat.notice}
</SharedText> </Text>
<SharedStack direction="row" gap={3}> <Stack direction="row" gap={3}>
<SharedButton variant="primary" fullWidth icon={<SharedIcon icon={Megaphone} size={4} />}> <Button variant="primary" fullWidth icon={<Icon icon={Megaphone} size={4} />}>
Request Sponsorship Request Sponsorship
</SharedButton> </Button>
<SharedButton variant="secondary" icon={<SharedIcon icon={FileText} size={4} />}> <Button variant="secondary" icon={<Icon icon={FileText} size={4} />}>
Download Info Pack Download Info Pack
</SharedButton> </Button>
</SharedStack> </Stack>
</SharedCard> </Card>
</SharedStack> </Stack>
</GridItem> </GridItem>
</Grid> </Grid>
)} )}
</SharedStack> </Stack>
</SharedBox> </Box>
</SharedContainer> </Container>
); );
} }
function InfoRow({ label, value, color = 'text-white', last }: { label: string, value: string | number, color?: string, last?: boolean }) { function InfoRow({ label, value, color = 'text-white', last }: { label: string, value: string | number, color?: string, last?: boolean }) {
return ( return (
<SharedBox py={2} borderBottom={!last} borderColor="border-neutral-800/50"> <Box py={2} borderBottom={!last} borderColor="border-neutral-800/50">
<SharedStack direction="row" align="center" justify="between"> <Stack direction="row" align="center" justify="between">
<SharedText color="text-gray-400">{label}</SharedText> <Text color="text-gray-400">{label}</Text>
<SharedText weight="medium" color={color}>{value}</SharedText> <Text weight="medium" color={color}>{value}</Text>
</SharedStack> </Stack>
</SharedBox> </Box>
); );
} }

View File

@@ -7,13 +7,11 @@ import { StewardingQueuePanel } from '@/components/leagues/StewardingQueuePanel'
import { StewardingStats } from '@/components/leagues/StewardingStats'; import { StewardingStats } from '@/components/leagues/StewardingStats';
import { PenaltyFAB } from '@/components/races/PenaltyFAB'; import { PenaltyFAB } from '@/components/races/PenaltyFAB';
import type { StewardingViewData } from '@/lib/view-data/leagues/StewardingViewData'; import type { StewardingViewData } from '@/lib/view-data/leagues/StewardingViewData';
import { import { Box } from '@/ui/Box';
SharedBox, import { Button } from '@/ui/Button';
SharedButton, import { Stack } from '@/ui/Stack';
SharedStack, import { Text } from '@/ui/Text';
SharedText, import { Card } from '@/ui/Card';
SharedCard
} from '@/components/shared/UIComponents';
import { TemplateProps } from '@/lib/contracts/components/ComponentContracts'; import { TemplateProps } from '@/lib/contracts/components/ComponentContracts';
interface StewardingTemplateProps extends TemplateProps<StewardingViewData> { interface StewardingTemplateProps extends TemplateProps<StewardingViewData> {
@@ -51,7 +49,7 @@ export function StewardingTemplate({
currentDriverId, currentDriverId,
}: StewardingTemplateProps) { }: StewardingTemplateProps) {
return ( return (
<SharedStack gap={6}> <Stack gap={6}>
<StewardingStats <StewardingStats
totalPending={viewData.totalPending} totalPending={viewData.totalPending}
totalResolved={viewData.totalResolved} totalResolved={viewData.totalResolved}
@@ -59,41 +57,41 @@ export function StewardingTemplate({
/> />
{/* Tab navigation */} {/* Tab navigation */}
<SharedBox borderBottom borderColor="border-charcoal-outline"> <Box borderBottom borderColor="border-charcoal-outline">
<SharedStack direction="row" gap={4}> <Stack direction="row" gap={4}>
<SharedBox <Box
borderBottom={activeTab === 'pending'} borderBottom={activeTab === 'pending'}
borderColor={activeTab === 'pending' ? 'border-primary-blue' : undefined} borderColor={activeTab === 'pending' ? 'border-primary-blue' : undefined}
> >
<SharedButton <Button
variant="ghost" variant="ghost"
onClick={() => onTabChange('pending')} onClick={() => onTabChange('pending')}
rounded={false} rounded={false}
> >
<SharedStack direction="row" align="center" gap={2}> <Stack direction="row" align="center" gap={2}>
<SharedText weight="medium" color={activeTab === 'pending' ? 'text-primary-blue' : undefined}>Pending Protests</SharedText> <Text weight="medium" color={activeTab === 'pending' ? 'text-primary-blue' : undefined}>Pending Protests</Text>
{viewData.totalPending > 0 && ( {viewData.totalPending > 0 && (
<SharedBox px={2} py={0.5} fontSize="0.75rem" bg="bg-warning-amber/20" color="text-warning-amber" rounded="full"> <Box px={2} py={0.5} fontSize="0.75rem" bg="bg-warning-amber/20" color="text-warning-amber" rounded="full">
{viewData.totalPending} {viewData.totalPending}
</SharedBox> </Box>
)} )}
</SharedStack> </Stack>
</SharedButton> </Button>
</SharedBox> </Box>
<SharedBox <Box
borderBottom={activeTab === 'history'} borderBottom={activeTab === 'history'}
borderColor={activeTab === 'history' ? 'border-primary-blue' : undefined} borderColor={activeTab === 'history' ? 'border-primary-blue' : undefined}
> >
<SharedButton <Button
variant="ghost" variant="ghost"
onClick={() => onTabChange('history')} onClick={() => onTabChange('history')}
rounded={false} rounded={false}
> >
<SharedText weight="medium" color={activeTab === 'history' ? 'text-primary-blue' : undefined}>History</SharedText> <Text weight="medium" color={activeTab === 'history' ? 'text-primary-blue' : undefined}>History</Text>
</SharedButton> </Button>
</SharedBox> </Box>
</SharedStack> </Stack>
</SharedBox> </Box>
{/* Content */} {/* Content */}
{activeTab === 'pending' ? ( {activeTab === 'pending' ? (
@@ -102,15 +100,15 @@ export function StewardingTemplate({
onReview={onReviewProtest} onReview={onReviewProtest}
/> />
) : ( ) : (
<SharedCard> <Card>
<SharedBox p={6}> <Box p={6}>
<PenaltyHistoryList <PenaltyHistoryList
protests={allResolvedProtests} protests={allResolvedProtests}
races={racesMap} races={racesMap}
drivers={driverMap} drivers={driverMap}
/> />
</SharedBox> </Box>
</SharedCard> </Card>
)} )}
{activeTab === 'history' && ( {activeTab === 'history' && (
@@ -141,6 +139,6 @@ export function StewardingTemplate({
races={viewData.races.map(r => ({ id: r.id, track: r.track, scheduledAt: new Date(r.scheduledAt) }))} races={viewData.races.map(r => ({ id: r.id, track: r.track, scheduledAt: new Date(r.scheduledAt) }))}
/> />
)} )}
</SharedStack> </Stack>
); );
} }

View File

@@ -74,7 +74,7 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem
/> />
<Group justify="end" fullWidth> <Group justify="end" fullWidth>
<Link href={routes.auth.forgotPassword}> <Link href={routes.auth.forgotPassword}>
<Text size="xs" color="text-primary-accent"> <Text size="xs" variant="primary">
Forgot password? Forgot password?
</Text> </Text>
</Link> </Link>
@@ -101,10 +101,10 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem
{viewData.hasInsufficientPermissions && ( {viewData.hasInsufficientPermissions && (
<Group direction="row" align="start" gap={3} fullWidth> <Group direction="row" align="start" gap={3} fullWidth>
<Icon icon={AlertCircle} size={5} color="var(--color-warning)" /> <Icon icon={AlertCircle} size={5} color="var(--ui-color-intent-warning)" />
<Group direction="column" gap={1}> <Group direction="column" gap={1}>
<Text weight="bold" color="text-warning-amber" block size="sm">Insufficient Permissions</Text> <Text weight="bold" variant="warning" block size="sm">Insufficient Permissions</Text>
<Text size="xs" color="text-gray-400" block> <Text size="xs" variant="low" block>
Please log in with an account that has the required role. Please log in with an account that has the required role.
</Text> </Text>
</Group> </Group>
@@ -133,17 +133,17 @@ export function LoginTemplate({ viewData, formActions, mutationState }: LoginTem
</AuthForm> </AuthForm>
<AuthFooterLinks> <AuthFooterLinks>
<Text size="sm" color="text-gray-400"> <Text size="sm" variant="low">
Don&apos;t have an account?{' '} Don&apos;t have an account?{' '}
<Link <Link
href={viewData.returnTo && viewData.returnTo !== '/dashboard' ? `/auth/signup?returnTo=${encodeURIComponent(viewData.returnTo)}` : '/auth/signup'} href={viewData.returnTo && viewData.returnTo !== '/dashboard' ? `/auth/signup?returnTo=${encodeURIComponent(viewData.returnTo)}` : '/auth/signup'}
> >
<Text as="span" color="text-primary-accent" weight="bold">Create one</Text> <Text as="span" variant="primary" weight="bold">Create one</Text>
</Link> </Link>
</Text> </Text>
<Group direction="column" gap={1} align="center" fullWidth> <Group direction="column" gap={1} align="center" fullWidth>
<Text size="xs" color="text-gray-600"> <Text size="xs" variant="low">
By signing in, you agree to our{' '} By signing in, you agree to our{' '}
<Link href="/terms">Terms</Link> <Link href="/terms">Terms</Link>
{' '}and{' '} {' '}and{' '}

View File

@@ -62,7 +62,7 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
<AuthForm onSubmit={formActions.handleSubmit}> <AuthForm onSubmit={formActions.handleSubmit}>
<Group direction="column" gap={6} fullWidth> <Group direction="column" gap={6} fullWidth>
<Group direction="column" gap={4} fullWidth> <Group direction="column" gap={4} fullWidth>
<Text size="xs" weight="bold" color="text-low" uppercase letterSpacing="wide" block>Personal Information</Text> <Text size="xs" weight="bold" variant="low" uppercase letterSpacing="wide" block>Personal Information</Text>
<Grid cols={{ base: 1, md: 2 }} gap={4}> <Grid cols={{ base: 1, md: 2 }} gap={4}>
<Input <Input
label="First Name" label="First Name"
@@ -91,9 +91,9 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
</Grid> </Grid>
<Group direction="row" align="start" gap={2} fullWidth> <Group direction="row" align="start" gap={2} fullWidth>
<Icon icon={AlertCircle} size={3.5} color="var(--color-warning)" /> <Icon icon={AlertCircle} size={3.5} color="var(--ui-color-intent-warning)" />
<Text size="xs" color="text-med"> <Text size="xs" variant="low">
<Text weight="bold" color="text-warning-amber">Note:</Text> Your name cannot be changed after signup. <Text weight="bold" variant="warning">Note:</Text> Your name cannot be changed after signup.
</Text> </Text>
</Group> </Group>
@@ -113,7 +113,7 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
</Group> </Group>
<Group direction="column" gap={4} fullWidth> <Group direction="column" gap={4} fullWidth>
<Text size="xs" weight="bold" color="text-low" uppercase letterSpacing="wide" block>Security</Text> <Text size="xs" weight="bold" variant="low" uppercase letterSpacing="wide" block>Security</Text>
<PasswordField <PasswordField
label="Password" label="Password"
id="password" id="password"
@@ -138,15 +138,15 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
size="sm" size="sm"
/> />
</Group> </Group>
<Text size="xs" weight="bold" color="text-low" uppercase> <Text size="xs" weight="bold" variant="low" uppercase>
{passwordStrength.label} {passwordStrength.label}
</Text> </Text>
</Group> </Group>
<Grid cols={2} gap={2}> <Grid cols={2} gap={2}>
{passwordRequirements.map((req, index) => ( {passwordRequirements.map((req, index) => (
<Group key={index} direction="row" align="center" gap={1.5}> <Group key={index} direction="row" align="center" gap={1.5}>
<Icon icon={req.met ? Check : X} size={3} color={req.met ? 'var(--color-success)' : 'var(--color-text-low)'} /> <Icon icon={req.met ? Check : X} size={3} color={req.met ? 'var(--ui-color-intent-success)' : 'var(--ui-color-text-low)'} />
<Text size="xs" color={req.met ? 'text-med' : 'text-low'}> <Text size="xs" variant={req.met ? 'med' : 'low'}>
{req.label} {req.label}
</Text> </Text>
</Group> </Group>
@@ -173,8 +173,8 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
{mutationState.error && ( {mutationState.error && (
<Group direction="row" align="start" gap={3} fullWidth> <Group direction="row" align="start" gap={3} fullWidth>
<Icon icon={AlertCircle} size={4.5} color="var(--color-critical)" /> <Icon icon={AlertCircle} size={4.5} color="var(--ui-color-intent-critical)" />
<Text size="sm" color="text-critical-red">{mutationState.error}</Text> <Text size="sm" variant="critical">{mutationState.error}</Text>
</Group> </Group>
)} )}
@@ -190,17 +190,17 @@ export function SignupTemplate({ viewData, formActions, uiState, mutationState }
</AuthForm> </AuthForm>
<AuthFooterLinks> <AuthFooterLinks>
<Text size="sm" color="text-gray-400"> <Text size="sm" variant="low">
Already have an account?{' '} Already have an account?{' '}
<Link <Link
href={viewData.returnTo && viewData.returnTo !== '/onboarding' ? `/auth/login?returnTo=${encodeURIComponent(viewData.returnTo)}` : '/auth/login'} href={viewData.returnTo && viewData.returnTo !== '/onboarding' ? `/auth/login?returnTo=${encodeURIComponent(viewData.returnTo)}` : '/auth/login'}
> >
<Text color="text-primary-accent" weight="bold">Sign in</Text> <Text variant="primary" weight="bold">Sign in</Text>
</Link> </Link>
</Text> </Text>
<Group direction="column" gap={1} align="center" fullWidth> <Group direction="column" gap={1} align="center" fullWidth>
<Text size="xs" color="text-gray-600"> <Text size="xs" variant="low">
By creating an account, you agree to our{' '} By creating an account, you agree to our{' '}
<Link href="/terms">Terms</Link> <Link href="/terms">Terms</Link>
{' '}and{' '} {' '}and{' '}

View File

@@ -15,8 +15,8 @@ export function GlobalFooterTemplate(_props: GlobalFooterViewData) {
<Stack colSpan={{ base: 1, md: 2 }} gap={6}> <Stack colSpan={{ base: 1, md: 2 }} gap={6}>
<Stack direction="row" align="center" gap={4}> <Stack direction="row" align="center" gap={4}>
<BrandMark /> <BrandMark />
<Box display={{ base: 'none', sm: 'flex' }} alignItems="center" gap={2} borderLeft borderColor="[#23272B]" pl={4}> <Box display={{ base: 'none', sm: 'flex' }} alignItems="center" gap={2} borderLeft borderColor="var(--ui-color-border-default)" pl={4}>
<Box w="4px" h="4px" rounded="full" bg="primary-accent" animate="pulse" /> <Box w="4px" h="4px" rounded="full" bg="var(--ui-color-intent-primary)" animate="pulse" />
<Text size="xs" variant="low" weight="bold" font="mono" letterSpacing="0.1em"> <Text size="xs" variant="low" weight="bold" font="mono" letterSpacing="0.1em">
INFRASTRUCTURE INFRASTRUCTURE
</Text> </Text>

View File

@@ -21,7 +21,7 @@ export function GlobalSidebarTemplate(_props: GlobalSidebarViewData) {
<DashboardRail> <DashboardRail>
<Box py={6} fullWidth> <Box py={6} fullWidth>
<Box px={6} mb={8}> <Box px={6} mb={8}>
<Text size="xs" color="text-gray-500" weight="bold" font="mono" letterSpacing="0.2em"> <Text size="xs" variant="low" weight="bold" font="mono" letterSpacing="0.2em">
NAVIGATION NAVIGATION
</Text> </Text>
</Box> </Box>

View File

@@ -20,9 +20,9 @@ export function HeaderContentTemplate(_props: HeaderContentViewData) {
<> <>
<Stack direction="row" align="center" gap={6}> <Stack direction="row" align="center" gap={6}>
<BrandMark href={homeHref} priority /> <BrandMark href={homeHref} priority />
<Box display={{ base: 'none', sm: 'flex' }} alignItems="center" gap={2} borderLeft borderColor="[#23272B]" pl={6}> <Box display={{ base: 'none', sm: 'flex' }} alignItems="center" gap={2} borderLeft borderColor="var(--ui-color-border-default)" pl={6}>
<Box w="6px" h="6px" rounded="full" bg="primary-accent" animate="pulse" /> <Box w="6px" h="6px" rounded="full" bg="var(--ui-color-intent-primary)" animate="pulse" />
<Text size="xs" color="text-gray-500" weight="bold" font="mono" letterSpacing="0.2em"> <Text size="xs" variant="low" weight="bold" font="mono" letterSpacing="0.2em">
MOTORSPORT INFRASTRUCTURE MOTORSPORT INFRASTRUCTURE
</Text> </Text>
</Box> </Box>
@@ -35,9 +35,9 @@ export function HeaderContentTemplate(_props: HeaderContentViewData) {
)} )}
<Box display="flex" alignItems="center" gap={4}> <Box display="flex" alignItems="center" gap={4}>
<Stack direction="row" display={{ base: 'none', md: 'flex' }} align="center" gap={1} px={3} py={1} border borderColor="[#23272B]" bg="[#141619]/20"> <Stack direction="row" display={{ base: 'none', md: 'flex' }} align="center" gap={1} px={3} py={1} border borderColor="var(--ui-color-border-default)" bg="var(--ui-color-bg-surface-muted)">
<Text size="xs" color="text-gray-600" weight="bold" font="mono">STATUS:</Text> <Text size="xs" variant="low" weight="bold" font="mono">STATUS:</Text>
<Text size="xs" color="text-success-green" weight="bold" font="mono">OPERATIONAL</Text> <Text size="xs" variant="success" weight="bold" font="mono">OPERATIONAL</Text>
</Stack> </Stack>
<HeaderActions isAuthenticated={isAuthenticated} /> <HeaderActions isAuthenticated={isAuthenticated} />
</Box> </Box>

View File

@@ -1,6 +1,8 @@
'use client'; 'use client';
import { SharedContainer, SharedStack, SharedText } from '@/components/shared/UIComponents'; import { Container } from '@/ui/Container';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { TemplateProps } from '@/lib/contracts/components/ComponentContracts'; import { TemplateProps } from '@/lib/contracts/components/ComponentContracts';
import { ViewData } from '@/lib/contracts/view-data/ViewData'; import { ViewData } from '@/lib/contracts/view-data/ViewData';
@@ -11,12 +13,12 @@ interface ErrorTemplateProps extends TemplateProps<ViewData> {
export function ErrorTemplate({ message = "An error occurred", description = "Please try again later" }: ErrorTemplateProps) { export function ErrorTemplate({ message = "An error occurred", description = "Please try again later" }: ErrorTemplateProps) {
return ( return (
<SharedContainer size="lg"> <Container size="lg">
<SharedStack align="center" gap={4} py={12}> <Stack align="center" gap={4} py={12}>
<SharedText color="text-red-400">{message}</SharedText> <Text color="text-red-400">{message}</Text>
<SharedText color="text-gray-400">{description}</SharedText> <Text color="text-gray-400">{description}</Text>
</SharedStack> </Stack>
</SharedContainer> </Container>
); );
} }
@@ -27,11 +29,11 @@ interface EmptyTemplateProps extends TemplateProps<ViewData> {
export function EmptyTemplate({ title, description }: EmptyTemplateProps) { export function EmptyTemplate({ title, description }: EmptyTemplateProps) {
return ( return (
<SharedContainer size="lg"> <Container size="lg">
<SharedStack align="center" gap={2} py={12}> <Stack align="center" gap={2} py={12}>
<SharedText size="xl" weight="semibold" color="text-white">{title}</SharedText> <Text size="xl" weight="semibold" color="text-white">{title}</Text>
<SharedText color="text-gray-400">{description}</SharedText> <Text color="text-gray-400">{description}</Text>
</SharedStack> </Stack>
</SharedContainer> </Container>
); );
} }