website refactor
This commit is contained in:
@@ -14,31 +14,31 @@ interface RaceCardProps {
|
||||
track: string;
|
||||
car: string;
|
||||
scheduledAt: string;
|
||||
scheduledAtLabel: string;
|
||||
timeLabel: string;
|
||||
status: string;
|
||||
statusLabel: string;
|
||||
statusVariant: 'primary' | 'success' | 'warning' | 'critical' | 'default' | 'secondary' | 'info' | 'danger';
|
||||
leagueName: string;
|
||||
leagueId?: string;
|
||||
strengthOfField?: number | null;
|
||||
onClick?: () => void;
|
||||
statusConfig: {
|
||||
intent: 'primary' | 'success' | 'warning' | 'critical' | 'default' | 'secondary' | 'info' | 'danger';
|
||||
icon: LucideIcon | null;
|
||||
label: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function RaceCard({
|
||||
track,
|
||||
car,
|
||||
scheduledAt,
|
||||
scheduledAtLabel,
|
||||
timeLabel,
|
||||
status,
|
||||
statusLabel,
|
||||
statusVariant,
|
||||
leagueName,
|
||||
leagueId,
|
||||
strengthOfField,
|
||||
onClick,
|
||||
statusConfig,
|
||||
}: RaceCardProps) {
|
||||
const scheduledAtDate = new Date(scheduledAt);
|
||||
|
||||
return (
|
||||
<Card
|
||||
variant="dark"
|
||||
@@ -48,10 +48,10 @@ export function RaceCard({
|
||||
{/* Time Column */}
|
||||
<Stack align="center" gap={1}>
|
||||
<Text size="lg" weight="bold" variant="high">
|
||||
{scheduledAtDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
{timeLabel}
|
||||
</Text>
|
||||
<Text size="xs" variant={statusConfig.intent === 'default' ? 'low' : (statusConfig.intent as any)}>
|
||||
{status === 'running' ? 'LIVE' : scheduledAtDate.toLocaleDateString()}
|
||||
<Text size="xs" variant={statusVariant === 'default' ? 'low' : (statusVariant as any)}>
|
||||
{status === 'running' ? 'LIVE' : scheduledAtLabel}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
@@ -83,8 +83,8 @@ export function RaceCard({
|
||||
</Stack>
|
||||
|
||||
{/* Status Badge */}
|
||||
<Badge variant={statusConfig.intent}>
|
||||
{statusConfig.label}
|
||||
<Badge variant={statusVariant}>
|
||||
{statusLabel}
|
||||
</Badge>
|
||||
</Stack>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
import { raceStatusConfig } from '@/lib/utilities/raceStatus';
|
||||
import { RaceStatusDisplay } from '@/lib/display-objects/RaceStatusDisplay';
|
||||
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
|
||||
import { RaceCard as UiRaceCard } from './RaceCard';
|
||||
|
||||
interface RaceCardProps {
|
||||
@@ -18,28 +18,20 @@ interface RaceCardProps {
|
||||
}
|
||||
|
||||
export function RaceCard({ race, onClick }: RaceCardProps) {
|
||||
const config = raceStatusConfig[race.status as keyof typeof raceStatusConfig] || {
|
||||
border: 'border-charcoal-outline',
|
||||
bg: 'bg-charcoal-outline',
|
||||
color: 'text-gray-400',
|
||||
icon: null,
|
||||
label: 'Scheduled',
|
||||
};
|
||||
|
||||
return (
|
||||
<UiRaceCard
|
||||
track={race.track}
|
||||
car={race.car}
|
||||
scheduledAt={race.scheduledAt}
|
||||
scheduledAtLabel={DateDisplay.formatShort(race.scheduledAt)}
|
||||
timeLabel={DateDisplay.formatTime(race.scheduledAt)}
|
||||
status={race.status}
|
||||
statusLabel={RaceStatusDisplay.getLabel(race.status)}
|
||||
statusVariant={RaceStatusDisplay.getVariant(race.status) as any}
|
||||
leagueName={race.leagueName}
|
||||
leagueId={race.leagueId}
|
||||
strengthOfField={race.strengthOfField}
|
||||
onClick={onClick}
|
||||
statusConfig={{
|
||||
...config,
|
||||
icon: config.icon as LucideIcon | null,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,29 +19,6 @@ interface RaceListProps {
|
||||
}
|
||||
|
||||
export function RaceList({ racesByDate, totalCount, onRaceClick }: RaceListProps) {
|
||||
const statusConfig = {
|
||||
scheduled: {
|
||||
icon: Clock,
|
||||
variant: 'primary' as const,
|
||||
label: 'Scheduled',
|
||||
},
|
||||
running: {
|
||||
icon: PlayCircle,
|
||||
variant: 'success' as const,
|
||||
label: 'LIVE',
|
||||
},
|
||||
completed: {
|
||||
icon: CheckCircle2,
|
||||
variant: 'default' as const,
|
||||
label: 'Completed',
|
||||
},
|
||||
cancelled: {
|
||||
icon: XCircle,
|
||||
variant: 'warning' as const,
|
||||
label: 'Cancelled',
|
||||
},
|
||||
};
|
||||
|
||||
if (racesByDate.length === 0) {
|
||||
return (
|
||||
<EmptyState
|
||||
@@ -62,8 +39,6 @@ export function RaceList({ racesByDate, totalCount, onRaceClick }: RaceListProps
|
||||
|
||||
<Stack gap={2}>
|
||||
{group.races.map((race) => {
|
||||
const config = statusConfig[race.status as keyof typeof statusConfig] || statusConfig.scheduled;
|
||||
|
||||
return (
|
||||
<RaceListItem
|
||||
key={race.id}
|
||||
@@ -76,7 +51,9 @@ export function RaceList({ racesByDate, totalCount, onRaceClick }: RaceListProps
|
||||
leagueHref={routes.league.detail(race.leagueId ?? '')}
|
||||
strengthOfField={race.strengthOfField}
|
||||
onClick={() => onRaceClick(race.id)}
|
||||
statusConfig={config}
|
||||
statusLabel={race.statusLabel}
|
||||
statusVariant={race.statusVariant as any}
|
||||
statusIconName={race.statusIconName}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -5,9 +5,16 @@ import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { RaceCard, RaceTimeColumn, RaceInfo } from '@/ui/RaceCard';
|
||||
import { Car, Trophy, Zap, ArrowRight } from 'lucide-react';
|
||||
import { Car, Trophy, Zap, ArrowRight, Clock, PlayCircle, CheckCircle2, XCircle, HelpCircle } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
const ICON_MAP = {
|
||||
Clock,
|
||||
PlayCircle,
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
HelpCircle,
|
||||
};
|
||||
|
||||
interface RaceListItemProps {
|
||||
track: string;
|
||||
@@ -17,15 +24,13 @@ interface RaceListItemProps {
|
||||
dateLabel?: string;
|
||||
dayLabel?: string;
|
||||
status: string;
|
||||
statusLabel: string;
|
||||
statusVariant: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
||||
statusIconName: string;
|
||||
leagueName?: string | null;
|
||||
leagueHref?: string;
|
||||
strengthOfField?: number | null;
|
||||
onClick: () => void;
|
||||
statusConfig: {
|
||||
icon: LucideIcon;
|
||||
variant: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
||||
label: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function RaceListItem({
|
||||
@@ -36,13 +41,16 @@ export function RaceListItem({
|
||||
dateLabel,
|
||||
dayLabel,
|
||||
status,
|
||||
statusLabel,
|
||||
statusVariant,
|
||||
statusIconName,
|
||||
leagueName,
|
||||
leagueHref,
|
||||
strengthOfField,
|
||||
onClick,
|
||||
statusConfig,
|
||||
}: RaceListItemProps) {
|
||||
const isLive = status === 'running';
|
||||
const StatusIcon = ICON_MAP[statusIconName as keyof typeof ICON_MAP] || HelpCircle;
|
||||
|
||||
return (
|
||||
<RaceCard onClick={onClick} isLive={isLive}>
|
||||
@@ -59,9 +67,9 @@ export function RaceListItem({
|
||||
title={track}
|
||||
subtitle={car}
|
||||
badge={
|
||||
<Badge variant={statusConfig.variant}>
|
||||
<Icon icon={statusConfig.icon} size={3.5} />
|
||||
{statusConfig.label}
|
||||
<Badge variant={statusVariant}>
|
||||
<Icon icon={StatusIcon} size={3.5} />
|
||||
{statusLabel}
|
||||
</Badge>
|
||||
}
|
||||
meta={
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { RaceSummary } from '@/ui/RaceSummary';
|
||||
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
|
||||
import React from 'react';
|
||||
|
||||
interface RaceSummaryItemProps {
|
||||
track: string;
|
||||
meta: string;
|
||||
date: Date;
|
||||
dateLabel: string;
|
||||
}
|
||||
|
||||
export function RaceSummaryItem({ track, meta, date }: RaceSummaryItemProps) {
|
||||
export function RaceSummaryItem({ track, meta, dateLabel }: RaceSummaryItemProps) {
|
||||
return (
|
||||
<RaceSummary
|
||||
track={track}
|
||||
meta={meta}
|
||||
date={DateDisplay.formatShort(date)}
|
||||
date={dateLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ interface TeamDetailsHeaderProps {
|
||||
description?: string;
|
||||
logoUrl?: string;
|
||||
memberCount: number;
|
||||
memberCountLabel?: string;
|
||||
foundedDate?: string;
|
||||
foundedDateLabel?: string;
|
||||
isAdmin?: boolean;
|
||||
onAdminClick?: () => void;
|
||||
}
|
||||
@@ -25,7 +27,9 @@ export function TeamDetailsHeader({
|
||||
description,
|
||||
logoUrl,
|
||||
memberCount,
|
||||
memberCountLabel,
|
||||
foundedDate,
|
||||
foundedDateLabel,
|
||||
isAdmin,
|
||||
onAdminClick,
|
||||
}: TeamDetailsHeaderProps) {
|
||||
@@ -38,6 +42,9 @@ export function TeamDetailsHeader({
|
||||
</div>
|
||||
}
|
||||
description={description || 'No mission statement provided.'}
|
||||
memberCount={memberCount}
|
||||
memberCountLabel={memberCountLabel}
|
||||
foundedDateLabel={foundedDateLabel}
|
||||
sideContent={
|
||||
<div className="w-32 h-32 bg-[var(--ui-color-bg-surface-muted)] border border-[var(--ui-color-border-default)] flex items-center justify-center overflow-hidden rounded-lg">
|
||||
{logoUrl ? (
|
||||
@@ -58,7 +65,7 @@ export function TeamDetailsHeader({
|
||||
},
|
||||
{
|
||||
label: 'Established',
|
||||
value: foundedDate ? new Date(foundedDate).toLocaleDateString() : 'Unknown',
|
||||
value: foundedDateLabel || 'Unknown',
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -17,13 +17,16 @@ interface TeamHeroProps {
|
||||
description?: string;
|
||||
category?: string | null;
|
||||
createdAt?: string;
|
||||
foundedDateLabel?: string;
|
||||
leagues: { id: string }[];
|
||||
};
|
||||
memberCount: number;
|
||||
memberCountLabel?: string;
|
||||
leagueCountLabel?: string;
|
||||
onUpdate: () => void;
|
||||
}
|
||||
|
||||
export function TeamHero({ team, memberCount, onUpdate }: TeamHeroProps) {
|
||||
export function TeamHero({ team, memberCount, memberCountLabel, leagueCountLabel, onUpdate }: TeamHeroProps) {
|
||||
return (
|
||||
<Card>
|
||||
<Group align="start" justify="between" wrap gap={6}>
|
||||
@@ -44,20 +47,20 @@ export function TeamHero({ team, memberCount, onUpdate }: TeamHeroProps) {
|
||||
stats={[
|
||||
{
|
||||
label: 'Personnel',
|
||||
value: `${memberCount} ${memberCount === 1 ? 'member' : 'members'}`,
|
||||
value: memberCountLabel || 'Unknown',
|
||||
},
|
||||
...(team.category ? [{
|
||||
label: 'Category',
|
||||
value: team.category,
|
||||
intent: 'primary' as const,
|
||||
}] : []),
|
||||
...(team.createdAt ? [{
|
||||
...(team.foundedDateLabel ? [{
|
||||
label: 'Founded',
|
||||
value: new Date(team.createdAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' }),
|
||||
value: team.foundedDateLabel,
|
||||
}] : []),
|
||||
...(team.leagues && team.leagues.length > 0 ? [{
|
||||
label: 'Activity',
|
||||
value: `${team.leagues.length} ${team.leagues.length === 1 ? 'league' : 'leagues'}`,
|
||||
value: leagueCountLabel || 'Unknown',
|
||||
}] : []),
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -10,6 +10,7 @@ interface Member {
|
||||
driverName: string;
|
||||
role: string;
|
||||
joinedAt: string;
|
||||
joinedAtLabel: string;
|
||||
}
|
||||
|
||||
interface TeamMembersTableProps {
|
||||
@@ -49,7 +50,7 @@ export function TeamMembersTable({ members, isAdmin, onRemoveMember }: TeamMembe
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text size="xs" color="text-gray-500" font="mono">
|
||||
{new Date(member.joinedAt).toLocaleDateString()}
|
||||
{member.joinedAtLabel}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell textAlign="right">
|
||||
|
||||
Reference in New Issue
Block a user