website refactor
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import { Flag, Star, Trophy, Users } from 'lucide-react';
|
||||
import { Avatar } from '../../ui/Avatar';
|
||||
import { Badge } from '../../ui/Badge';
|
||||
import { Box } from '../../ui/Box';
|
||||
import { Heading } from '../../ui/Heading';
|
||||
import { Icon } from '../../ui/Icon';
|
||||
import { ProfileHero, ProfileAvatar, ProfileStatsGroup, ProfileStat } from '../../ui/ProfileHero';
|
||||
import { BadgeGroup } from '../../ui/BadgeGroup';
|
||||
import { QuickStatCard, QuickStatItem } from '../../ui/QuickStatCard';
|
||||
import { Stack } from '../../ui/Stack';
|
||||
import React from 'react';
|
||||
|
||||
interface DashboardHeroProps {
|
||||
@@ -27,7 +29,7 @@ export function DashboardHero({
|
||||
}: DashboardHeroProps) {
|
||||
return (
|
||||
<ProfileHero glowColor="aqua">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '2rem', flexWrap: 'wrap' }}>
|
||||
<Stack direction="row" align="center" gap={8} wrap>
|
||||
{/* Avatar Section */}
|
||||
<ProfileAvatar
|
||||
badge={<Icon icon={Star} size={5} intent="high" />}
|
||||
@@ -40,7 +42,7 @@ export function DashboardHero({
|
||||
</ProfileAvatar>
|
||||
|
||||
{/* Info Section */}
|
||||
<div style={{ flex: 1, minWidth: '200px' }}>
|
||||
<Box flex={1} minWidth="200px">
|
||||
<Heading level={1}>{driverName}</Heading>
|
||||
|
||||
<ProfileStatsGroup>
|
||||
@@ -60,14 +62,14 @@ export function DashboardHero({
|
||||
Team Redline
|
||||
</Badge>
|
||||
</BadgeGroup>
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<QuickStatCard>
|
||||
<QuickStatItem label="Podiums" value="12" />
|
||||
<QuickStatItem label="Wins" value="4" />
|
||||
</QuickStatCard>
|
||||
</div>
|
||||
</Stack>
|
||||
</ProfileHero>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { StatGrid } from '@/ui/StatGrid';
|
||||
import { Flag, Medal, Target, Trophy, User, Users } from 'lucide-react';
|
||||
import React from 'react';
|
||||
@@ -26,7 +27,7 @@ interface DashboardHeroProps {
|
||||
|
||||
export function DashboardHero({ currentDriver, activeLeaguesCount }: DashboardHeroProps) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
|
||||
<Stack gap={8}>
|
||||
<UiDashboardHero
|
||||
driverName={currentDriver.name}
|
||||
avatarUrl={currentDriver.avatarUrl}
|
||||
@@ -36,7 +37,7 @@ export function DashboardHero({ currentDriver, activeLeaguesCount }: DashboardHe
|
||||
winRate={Math.round((currentDriver.wins / currentDriver.totalRaces) * 100) || 0}
|
||||
/>
|
||||
|
||||
<div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
|
||||
<Stack direction="row" gap={4} wrap>
|
||||
<Link href={routes.public.leagues}>
|
||||
<Button variant="secondary" icon={<Icon icon={Flag} size={4} />}>
|
||||
Browse Leagues
|
||||
@@ -47,7 +48,7 @@ export function DashboardHero({ currentDriver, activeLeaguesCount }: DashboardHe
|
||||
View Profile
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
<StatGrid
|
||||
variant="box"
|
||||
@@ -59,6 +60,6 @@ export function DashboardHero({ currentDriver, activeLeaguesCount }: DashboardHe
|
||||
{ icon: Users, label: 'Active Leagues', value: activeLeaguesCount, intent: 'telemetry' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useNotifications } from '@/components/notifications/NotificationProvide
|
||||
import type { NotificationVariant } from '@/components/notifications/notificationTypes';
|
||||
import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId";
|
||||
import { ApiConnectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
|
||||
import { CircuitBreakerRegistry } from '@/lib/api/base/CircuitBreakerRegistry';
|
||||
import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler';
|
||||
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
|
||||
import { Activity, AlertTriangle, ChevronDown, ChevronUp, MessageSquare, Wrench, X } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
import { getGlobalReplaySystem } from '@/lib/infrastructure/ErrorReplay';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { IconButton } from '@/ui/IconButton';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Box, Clock, Copy, Download, Play, Trash2 } from 'lucide-react';
|
||||
import { Clock, Copy, Download, Play, Trash2 } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface ReplayEntry {
|
||||
|
||||
@@ -11,8 +11,7 @@ import { Group } from '@/ui/Group';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { ControlBar } from '@/ui/ControlBar';
|
||||
import { Trophy } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
interface RaceHistoryProps {
|
||||
driverId: string;
|
||||
|
||||
@@ -3,10 +3,12 @@ import { Badge } from '@/ui/Badge';
|
||||
|
||||
interface RatingBadgeProps {
|
||||
rating: number;
|
||||
size?: 'sm' | 'md';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
}
|
||||
|
||||
export function RatingBadge({ rating, size = 'md' }: RatingBadgeProps) {
|
||||
const badgeSize = size === 'lg' ? 'md' : size;
|
||||
|
||||
const getVariant = (val: number): 'warning' | 'primary' | 'success' | 'default' => {
|
||||
if (val >= 2500) return 'warning';
|
||||
if (val >= 2000) return 'primary'; // Simplified
|
||||
@@ -18,7 +20,7 @@ export function RatingBadge({ rating, size = 'md' }: RatingBadgeProps) {
|
||||
return (
|
||||
<Badge
|
||||
variant={getVariant(rating)}
|
||||
size={size}
|
||||
size={badgeSize}
|
||||
>
|
||||
{rating.toLocaleString()}
|
||||
</Badge>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function SafetyRatingBadge({ rating, size = 'md' }: SafetyRatingBadgeProp
|
||||
border
|
||||
bg={getBgColor(rating)}
|
||||
borderColor={getBorderColor(rating)}
|
||||
{...sizeProps[size]}
|
||||
{...(sizeProps[size] as any)}
|
||||
>
|
||||
<Shield size={iconSizes[size]} color={iconColors[colorClass as keyof typeof iconColors]} />
|
||||
<Text
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React, { Component, ReactNode, ErrorInfo, useState, version } from 'react';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
|
||||
import { DevErrorPanel } from './DevErrorPanel';
|
||||
import { DevErrorPanel } from '@/ui/DevErrorPanel';
|
||||
import { ErrorDisplay } from '@/ui/ErrorDisplay';
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
interface ActivityFeedListProps {
|
||||
@@ -6,8 +7,8 @@ interface ActivityFeedListProps {
|
||||
|
||||
export function ActivityFeedList({ children }: ActivityFeedListProps) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||
<Stack gap={4}>
|
||||
{children}
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FeedItemCard } from '@/components/feed/FeedItemCard';
|
||||
import { FeedEmptyState } from '@/ui/FeedEmptyState';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import React from 'react';
|
||||
|
||||
interface FeedItemData {
|
||||
@@ -23,10 +24,10 @@ export function FeedList({ items }: FeedListProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||
<Stack gap={4}>
|
||||
{items.map(item => (
|
||||
<FeedItemCard key={item.id} item={item} />
|
||||
))}
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Calendar, Home, Layout, Settings, Trophy, Users } from 'lucide-react';
|
||||
import { NavLink } from './NavLink';
|
||||
import { NavLink } from '@/ui/NavLink';
|
||||
|
||||
interface AuthedNavProps {
|
||||
pathname: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Calendar, Home, Layout, Trophy, Users } from 'lucide-react';
|
||||
import { NavLink } from './NavLink';
|
||||
import { NavLink } from '@/ui/NavLink';
|
||||
|
||||
interface PublicNavProps {
|
||||
pathname: string;
|
||||
|
||||
@@ -5,10 +5,12 @@ import React from 'react';
|
||||
|
||||
interface RankBadgeProps {
|
||||
rank: number;
|
||||
size?: 'sm' | 'md';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
}
|
||||
|
||||
export function RankBadge({ rank, size = 'md' }: RankBadgeProps) {
|
||||
const badgeSize = size === 'lg' ? 'md' : size;
|
||||
|
||||
const getVariant = (rank: number): 'warning' | 'primary' | 'info' | 'default' => {
|
||||
if (rank <= 3) return 'warning';
|
||||
if (rank <= 10) return 'primary';
|
||||
@@ -28,7 +30,7 @@ export function RankBadge({ rank, size = 'md' }: RankBadgeProps) {
|
||||
const medal = getMedalEmoji(rank);
|
||||
|
||||
return (
|
||||
<Badge variant={getVariant(rank)} size={size}>
|
||||
<Badge variant={getVariant(rank)} size={badgeSize}>
|
||||
<Group gap={1}>
|
||||
{medal && <Text size="xs">{medal}</Text>}
|
||||
<Text size="xs" weight="bold">#{rank}</Text>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DriverIdentity } from '@/ui/DriverIdentity';
|
||||
import { DriverViewModel } from '@/lib/view-models/DriverViewModel';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { TableCell, TableRow } from '@/ui/Table';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
|
||||
@@ -41,7 +42,7 @@ export function LeagueMemberRow({
|
||||
return (
|
||||
<TableRow variant={isTopPerformer ? 'highlight' : 'default'}>
|
||||
<TableCell>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
<Box display="flex" alignItems="center" gap={2}>
|
||||
{driver ? (
|
||||
<DriverIdentity
|
||||
driver={driver}
|
||||
@@ -59,7 +60,7 @@ export function LeagueMemberRow({
|
||||
{isTopPerformer && (
|
||||
<Text size="xs">⭐</Text>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text variant="primary" weight="medium">
|
||||
|
||||
@@ -17,8 +17,7 @@ import { Text } from '@/ui/Text';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Group } from '@/ui/Group';
|
||||
import { ControlBar } from '@/ui/ControlBar';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
interface LeagueMembersProps {
|
||||
leagueId: string;
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { LeagueScheduleRaceViewModel } from '@/lib/view-models/LeagueSchedu
|
||||
import { useState } from 'react';
|
||||
|
||||
// Shared state components
|
||||
import { StateContainer } from '@/ui/StateContainer';
|
||||
import { StateContainer } from '@/components/shared/state/StateContainer';
|
||||
import { useLeagueSchedule } from "@/hooks/league/useLeagueSchedule";
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { Text } from '@/ui/Text';
|
||||
import { useState } from 'react';
|
||||
import { MediaCard } from '@/ui/MediaCard';
|
||||
import { MediaFiltersBar } from './MediaFiltersBar';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
@@ -10,7 +9,7 @@ import { MediaViewerModal } from './MediaViewerModal';
|
||||
import { SectionHeader } from '@/ui/SectionHeader';
|
||||
import { EmptyState } from '@/ui/EmptyState';
|
||||
import { Search } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export interface MediaAsset {
|
||||
id: string;
|
||||
|
||||
@@ -235,7 +235,7 @@ function AnimatedRating({ shouldReduceMotion, value }: { shouldReduceMotion: boo
|
||||
|
||||
return (
|
||||
<Stack as={motion.span} weight="bold" color="text-primary-accent" font="mono" size={{ base: 'sm', sm: 'base' }}>
|
||||
{shouldReduceMotion ? value : <Stack as={motion.span}>{rounded}</Stack>}
|
||||
{shouldReduceMotion ? value : <motion.span>{rounded}</motion.span>}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -265,7 +265,7 @@ function AnimatedCounter({
|
||||
|
||||
return (
|
||||
<Stack weight="bold" color="text-white" font="mono" size={{ base: 'sm', sm: 'base', md: 'lg' }}>
|
||||
{shouldReduceMotion ? value : <Stack as={motion.span}>{rounded}</Stack>}{suffix}
|
||||
{shouldReduceMotion ? value : <motion.span>{rounded}</motion.span>}{suffix}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ function AnimatedPoints({
|
||||
weight="bold"
|
||||
color="text-white"
|
||||
>
|
||||
{shouldReduceMotion ? points : <Stack as={motion.span}>{spring}</Stack>}
|
||||
{shouldReduceMotion ? points : <motion.span>{spring}</motion.span>}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -6,6 +6,7 @@ interface Stat {
|
||||
label: string;
|
||||
value: string | number;
|
||||
intent?: 'primary' | 'telemetry' | 'success' | 'critical';
|
||||
color?: string;
|
||||
}
|
||||
|
||||
interface ProfileStatGridProps {
|
||||
@@ -17,6 +18,7 @@ export function ProfileStatGrid({ stats }: ProfileStatGridProps) {
|
||||
label: stat.label,
|
||||
value: stat.value,
|
||||
intent: stat.intent || 'primary',
|
||||
color: stat.color,
|
||||
icon: Bug // Default icon if none provided, but StatBox requires one
|
||||
}));
|
||||
|
||||
|
||||
@@ -190,7 +190,8 @@ export function UserPill() {
|
||||
|
||||
return (
|
||||
<Box position="relative" display="inline-flex" alignItems="center" data-user-pill>
|
||||
<button
|
||||
<Box
|
||||
as="button"
|
||||
type="button"
|
||||
onClick={() => setIsMenuOpen((open) => !open)}
|
||||
style={{
|
||||
@@ -237,7 +238,7 @@ export function UserPill() {
|
||||
|
||||
{/* Chevron */}
|
||||
<Icon icon={ChevronDown} size={3.5} intent="low" />
|
||||
</button>
|
||||
</Box>
|
||||
|
||||
<UserDropdown isOpen={isMenuOpen}>
|
||||
<UserDropdownHeader variant={isDemo ? 'demo' : 'default'}>
|
||||
|
||||
@@ -35,7 +35,7 @@ export function LatestResultsSidebar({ results }: LatestResultsSidebarProps) {
|
||||
<RaceSummaryItem
|
||||
track={result.track}
|
||||
meta={`${result.winnerName} • ${result.car}`}
|
||||
date={scheduledAt}
|
||||
dateLabel={scheduledAt.toLocaleDateString()}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -24,22 +24,22 @@ interface RaceListItemProps {
|
||||
export function RaceListItem({ race, onClick }: RaceListItemProps) {
|
||||
const statusConfig = {
|
||||
scheduled: {
|
||||
icon: Clock,
|
||||
iconName: 'Clock',
|
||||
variant: 'primary' as const,
|
||||
label: 'Scheduled',
|
||||
},
|
||||
running: {
|
||||
icon: PlayCircle,
|
||||
iconName: 'PlayCircle',
|
||||
variant: 'success' as const,
|
||||
label: 'LIVE',
|
||||
},
|
||||
completed: {
|
||||
icon: CheckCircle2,
|
||||
iconName: 'CheckCircle2',
|
||||
variant: 'default' as const,
|
||||
label: 'Completed',
|
||||
},
|
||||
cancelled: {
|
||||
icon: XCircle,
|
||||
iconName: 'XCircle',
|
||||
variant: 'warning' as const,
|
||||
label: 'Cancelled',
|
||||
},
|
||||
@@ -64,11 +64,13 @@ export function RaceListItem({ race, onClick }: RaceListItemProps) {
|
||||
dayLabel={date.getDate().toString()}
|
||||
timeLabel={formatTime(race.scheduledAt)}
|
||||
status={race.status}
|
||||
statusLabel={config.label}
|
||||
statusVariant={config.variant}
|
||||
statusIconName={config.iconName}
|
||||
leagueName={race.leagueName}
|
||||
leagueHref={race.leagueId ? routes.league.detail(race.leagueId) : undefined}
|
||||
strengthOfField={race.strengthOfField}
|
||||
onClick={() => onClick(race.id)}
|
||||
statusConfig={config}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Panel } from '@/ui/Panel';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { StatusDot } from '@/ui/StatusDot';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
@@ -30,16 +31,16 @@ export function SessionSummaryPanel({
|
||||
|
||||
return (
|
||||
<Panel title="Session Summary" className={className}>
|
||||
<Box display="flex" flexDirection="col" gap={3}>
|
||||
<Box display="flex" align="center" justify="between">
|
||||
<Stack gap={3}>
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Text weight="bold" size="lg">{title}</Text>
|
||||
<Box display="flex" align="center" gap={2}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<StatusDot color={statusColor} pulse={status === 'live'} size={2} />
|
||||
<Text size="xs" uppercase weight="bold" style={{ color: statusColor }}>
|
||||
{status}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Box display="grid" gridCols={2} gap={4} borderTop borderStyle="solid" borderColor="border-gray/10" pt={3}>
|
||||
{startTime && (
|
||||
@@ -61,7 +62,7 @@ export function SessionSummaryPanel({
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface TelemetryItem {
|
||||
@@ -25,7 +26,7 @@ export function TelemetryStrip({ items, className = '' }: TelemetryStripProps) {
|
||||
className={`bg-graphite-black/80 border-y border-border-gray/30 py-2 px-4 flex items-center gap-8 overflow-x-auto no-scrollbar ${className}`}
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
<Box key={index} display="flex" align="center" gap={2} className="whitespace-nowrap">
|
||||
<Stack key={index} direction="row" align="center" gap={2} className="whitespace-nowrap">
|
||||
<Text size="xs" color="text-gray-500" uppercase weight="bold" letterSpacing="widest">
|
||||
{item.label}
|
||||
</Text>
|
||||
@@ -42,7 +43,7 @@ export function TelemetryStrip({ items, className = '' }: TelemetryStripProps) {
|
||||
{item.trend === 'up' ? '↑' : item.trend === 'down' ? '↓' : '•'}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { Droplets, Sun, Thermometer, Wind, type LucideIcon } from 'lucide-react';
|
||||
|
||||
interface TrackConditionsPanelProps {
|
||||
@@ -26,7 +27,7 @@ export function TrackConditionsPanel({
|
||||
Track Conditions
|
||||
</Text>
|
||||
|
||||
<Stack display="grid" gridCols={2} mdCols={4} gap={4}>
|
||||
<Grid cols={2} mdCols={4} gap={4}>
|
||||
<ConditionItem
|
||||
icon={Thermometer}
|
||||
label="Air Temp"
|
||||
@@ -51,7 +52,7 @@ export function TrackConditionsPanel({
|
||||
value={windSpeed}
|
||||
color="text-gray-400"
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
|
||||
<Stack mt={4} pt={4} borderTop borderColor="border-outline-steel" bgOpacity={0.5} display="flex" alignItems="center" gap={3}>
|
||||
<Icon icon={Sun} size={4} color="#FFBE4D" />
|
||||
|
||||
@@ -42,7 +42,7 @@ export function UpcomingRacesSidebar({ races }: UpcomingRacesSidebarProps) {
|
||||
key={race.id}
|
||||
track={race.track}
|
||||
meta={race.car}
|
||||
date={scheduledAt}
|
||||
dateLabel={scheduledAt.toLocaleDateString()}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -130,7 +130,8 @@ export function RangeField({
|
||||
<Group justify="between" gap={3}>
|
||||
<Text as="label" size="xs" weight="medium" variant="low">{label}</Text>
|
||||
<Box display="flex" alignItems="center" gap={2} flex={1} maxWidth="200px">
|
||||
<div
|
||||
<Box
|
||||
as="div"
|
||||
ref={sliderRef}
|
||||
style={{
|
||||
position: 'relative',
|
||||
@@ -173,7 +174,7 @@ export function RangeField({
|
||||
boxShadow: isDragging ? '0 0 12px rgba(25, 140, 255, 0.5)' : undefined
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
<Box flexShrink={0}>
|
||||
<Group gap={1}>
|
||||
<Text size="sm" weight="semibold" variant="high" textAlign="right" width="2rem">{clampedValue}</Text>
|
||||
@@ -202,7 +203,8 @@ export function RangeField({
|
||||
)}
|
||||
|
||||
{/* Custom slider */}
|
||||
<div
|
||||
<Box
|
||||
as="div"
|
||||
ref={sliderRef}
|
||||
style={{
|
||||
position: 'relative',
|
||||
@@ -265,12 +267,13 @@ export function RangeField({
|
||||
boxShadow: isDragging ? '0 0 16px rgba(25, 140, 255, 0.6)' : '0 2px 8px rgba(0,0,0,0.3)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
{/* Value input and quick presets */}
|
||||
<Group justify="between" align="center" gap={3}>
|
||||
<Group gap={2}>
|
||||
<input
|
||||
<Box
|
||||
as="input"
|
||||
ref={inputRef}
|
||||
type="number"
|
||||
min={min}
|
||||
@@ -301,7 +304,8 @@ export function RangeField({
|
||||
{quickPresets.length > 0 && (
|
||||
<Group gap={1}>
|
||||
{quickPresets.slice(0, 3).map((preset) => (
|
||||
<button
|
||||
<Box
|
||||
as="button"
|
||||
key={preset}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -321,7 +325,7 @@ export function RangeField({
|
||||
}}
|
||||
>
|
||||
{preset}
|
||||
</button>
|
||||
</Box>
|
||||
))}
|
||||
</Group>
|
||||
)}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { LoadingSpinner } from '@/ui/LoadingSpinner';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
|
||||
import { ProgressLine } from '@/ui/ProgressLine';
|
||||
import { SharedEmptyState } from './SharedEmptyState';
|
||||
|
||||
export {
|
||||
Pagination as SharedPagination,
|
||||
@@ -38,5 +39,6 @@ export {
|
||||
Skeleton as SharedSkeleton,
|
||||
LoadingSpinner as SharedLoadingSpinner,
|
||||
Badge as SharedBadge,
|
||||
ProgressLine as SharedProgressLine
|
||||
ProgressLine as SharedProgressLine,
|
||||
SharedEmptyState
|
||||
};
|
||||
|
||||
@@ -84,8 +84,7 @@ export function TransactionTable({ transactions, onDownload }: TransactionTableP
|
||||
return (
|
||||
<Grid
|
||||
key={tx.id}
|
||||
cols={1}
|
||||
mdCols={12}
|
||||
cols={{ base: 1, md: 12 }}
|
||||
gap={4}
|
||||
p={4}
|
||||
alignItems="center"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useCreateTeam } from "@/hooks/team/useCreateTeam";
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { InfoBanner } from '@/ui/InfoBanner';
|
||||
import { Input } from '@/ui/Input';
|
||||
@@ -79,7 +80,7 @@ export function CreateTeamForm({ onCancel, onSuccess, onNavigate }: CreateTeamFo
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Box as="form" onSubmit={handleSubmit}>
|
||||
<Group direction="col" align="stretch" gap={6}>
|
||||
<Input
|
||||
label="Team Name *"
|
||||
@@ -142,6 +143,6 @@ export function CreateTeamForm({ onCancel, onSuccess, onNavigate }: CreateTeamFo
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
</form>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ import { SectionHeader } from '@/ui/SectionHeader';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Group } from '@/ui/Group';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { useState } from 'react';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface TeamAdminProps {
|
||||
team: {
|
||||
|
||||
Reference in New Issue
Block a user