website refactor

This commit is contained in:
2026-01-20 15:12:28 +01:00
parent a972bb4195
commit 94aaaff704
25 changed files with 793 additions and 574 deletions

View File

@@ -15,9 +15,7 @@ import { DangerZone } from '@/ui/DangerZone';
import { Input } from '@/ui/Input';
import { Text } from '@/ui/Text';
import { TextArea } from '@/ui/TextArea';
import { SectionHeader } from '@/ui/SectionHeader';
import { Box } from '@/ui/Box';
import { Group } from '@/ui/Group';
import { Stack } from '@/ui/Stack';
import React, { useState } from 'react';
@@ -40,14 +38,12 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
description: team.description || '',
});
// Use hooks for data fetching
const { data: joinRequests = [], isLoading: loading } = useTeamJoinRequests(
team.id,
team.ownerId,
true
);
// Use hooks for mutations
const updateTeamMutation = useUpdateTeam({
onSuccess: () => {
setEditMode(false);
@@ -96,17 +92,22 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
};
return (
<Stack gap={6}>
<Stack gap="lg">
<Panel
title="Team Settings"
actions={!editMode && (
<Button variant="secondary" size="sm" onClick={() => setEditMode(true)}>
Edit Details
</Button>
)}
variant="default"
padding="md"
>
<Box display="flex" justifyContent="between" alignItems="center" marginBottom={6}>
<Heading level={3} uppercase>Team Settings</Heading>
{!editMode && (
<Button variant="secondary" size="sm" onClick={() => setEditMode(true)}>
Edit Details
</Button>
)}
</Box>
{editMode ? (
<Stack gap={4}>
<Stack gap="md">
<Input
label="Team Name"
value={editedTeam.name}
@@ -128,7 +129,7 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
onChange={(e) => setEditedTeam({ ...editedTeam, description: e.target.value })}
/>
<Group gap={2}>
<Box display="flex" gap="sm">
<Button variant="primary" onClick={handleSaveChanges} disabled={updateTeamMutation.isPending}>
{updateTeamMutation.isPending ? 'Saving...' : 'Save Changes'}
</Button>
@@ -145,27 +146,30 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
>
Cancel
</Button>
</Group>
</Box>
</Stack>
) : (
<Stack gap={4}>
<Stack gap={1}>
<Text size="sm" variant="low" block>Team Name</Text>
<Text variant="high" weight="medium" block>{team.name}</Text>
<Stack gap="md">
<Stack gap="xs">
<Text size="xs" variant="low" uppercase>Team Name</Text>
<Text weight="bold">{team.name}</Text>
</Stack>
<Stack gap={1}>
<Text size="sm" variant="low" block>Team Tag</Text>
<Text variant="high" weight="medium" block>{team.tag}</Text>
<Stack gap="xs">
<Text size="xs" variant="low" uppercase>Team Tag</Text>
<Text weight="bold">{team.tag}</Text>
</Stack>
<Stack gap={1}>
<Text size="sm" variant="low" block>Description</Text>
<Text variant="high" block>{team.description}</Text>
<Stack gap="xs">
<Text size="xs" variant="low" uppercase>Description</Text>
<Text variant="med">{team.description}</Text>
</Stack>
</Stack>
)}
</Panel>
<Panel title="Join Requests">
<Panel variant="default" padding="md">
<Box marginBottom={6}>
<Heading level={3} uppercase>Join Requests</Heading>
</Box>
{loading ? (
<LoadingWrapper variant="spinner" message="Loading requests..." />
) : joinRequests.length > 0 ? (
@@ -202,3 +206,5 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
</Stack>
);
}
import { Heading } from '@/ui/Heading';

View File

@@ -1,56 +1,71 @@
'use client';
import { TeamLogo } from '@/components/teams/TeamLogo';
import { TeamCard as UITeamCard } from '@/ui/TeamCard';
import React, { ReactNode } from 'react';
import React from 'react';
import { TeamCard as UiTeamCard } from '@/ui/TeamCard';
import { TeamSummaryData } from '@/lib/view-data/TeamsViewData';
import { Image } from '@/ui/Image';
interface TeamCardProps {
name: string;
description?: string;
team?: TeamSummaryData;
// Compatibility props
name?: string;
leagueName?: string;
logo?: string;
memberCount: number;
isRecruiting?: boolean;
performanceBadge?: ReactNode;
specializationContent?: ReactNode;
categoryBadge?: ReactNode;
memberCount?: number;
ratingLabel?: string;
winsLabel?: string;
racesLabel?: string;
region?: string;
languagesContent?: ReactNode;
statsContent?: ReactNode;
onClick?: () => void;
isRecruiting?: boolean;
performanceLevel?: string;
description?: string;
onClick?: (id: string) => void;
}
export function TeamCard({
export function TeamCard({
team,
name,
description,
leagueName,
logo,
memberCount,
isRecruiting,
performanceBadge,
specializationContent,
categoryBadge,
ratingLabel,
winsLabel,
racesLabel,
region,
languagesContent,
onClick,
isRecruiting,
performanceLevel,
description,
onClick
}: TeamCardProps) {
const data = team || {
teamId: '',
teamName: name || '',
leagueName: leagueName || '',
memberCount: memberCount || 0,
logoUrl: logo,
ratingLabel: ratingLabel || '-',
winsLabel: winsLabel || '-',
racesLabel: racesLabel || '-',
region: region,
isRecruiting: isRecruiting || false,
performanceLevel: performanceLevel,
description: description,
};
return (
<UITeamCard
name={name}
description={description}
memberCount={memberCount}
isRecruiting={isRecruiting}
region={region}
onClick={onClick}
logo={
<TeamLogo src={logo} alt={name} size={64} />
}
badges={
<>
{performanceBadge}
{specializationContent}
{categoryBadge}
{languagesContent}
</>
}
<UiTeamCard
name={data.teamName}
leagueName={data.leagueName}
logo={data.logoUrl ? <Image src={data.logoUrl} alt={data.teamName} fullWidth fullHeight objectFit="cover" /> : undefined}
memberCount={data.memberCount}
rating={data.ratingLabel}
wins={data.winsLabel}
races={data.racesLabel}
region={data.region}
isRecruiting={data.isRecruiting}
performanceLevel={data.performanceLevel}
description={data.description}
onClick={() => onClick?.(data.teamId)}
/>
);
}

View File

@@ -4,11 +4,9 @@ import { Badge } from '@/ui/Badge';
import { Icon } from '@/ui/Icon';
import { Group } from '@/ui/Group';
import { Text } from '@/ui/Text';
import { BadgeGroup } from '@/ui/BadgeGroup';
import {
Clock,
Crown,
Languages,
Shield,
Star,
TrendingUp,
@@ -34,35 +32,8 @@ interface TeamCardProps {
onClick?: () => void;
}
function getPerformanceBadge(level?: string) {
switch (level) {
case 'pro':
return { icon: Crown, label: 'Pro', variant: 'warning' as const };
case 'advanced':
return { icon: Star, label: 'Advanced', variant: 'primary' as const };
case 'intermediate':
return { icon: TrendingUp, label: 'Intermediate', variant: 'default' as const };
case 'beginner':
return { icon: Shield, label: 'Beginner', variant: 'success' as const };
default:
return null;
}
}
function getSpecializationBadge(specialization?: string) {
switch (specialization) {
case 'endurance':
return { icon: Clock, label: 'Endurance', intent: 'warning' as const };
case 'sprint':
return { icon: Zap, label: 'Sprint', intent: 'telemetry' as const };
default:
return null;
}
}
export function TeamCard({
name,
description,
logo,
memberCount,
ratingLabel,
@@ -70,53 +41,21 @@ export function TeamCard({
racesLabel,
performanceLevel,
isRecruiting,
specialization,
region,
languages,
category,
onClick,
}: TeamCardProps) {
const performanceBadge = getPerformanceBadge(performanceLevel);
const specializationBadge = getSpecializationBadge(specialization);
return (
<UiTeamCard
name={name}
description={description}
logo={logo}
memberCount={memberCount}
ratingLabel={ratingLabel}
winsLabel={winsLabel}
racesLabel={racesLabel}
isRecruiting={isRecruiting}
onClick={onClick}
region={region}
performanceBadge={performanceBadge && (
<Badge variant={performanceBadge.variant} icon={performanceBadge.icon}>
{performanceBadge.label}
</Badge>
)}
specializationContent={specializationBadge && (
<Group gap={1}>
<Icon icon={specializationBadge.icon} size={3} intent={specializationBadge.intent} />
<Text size="xs" variant="low">{specializationBadge.label}</Text>
</Group>
)}
categoryBadge={category && (
<Badge variant="primary">
{category}
</Badge>
)}
languagesContent={languages && languages.length > 0 && (
<Badge variant="default" icon={Languages}>
{languages.slice(0, 2).join(', ')}
{languages.length > 2 && ` +${languages.length - 2}`}
</Badge>
)}
statsContent={
<Group gap={4} justify="center">
<TeamStatItem label="Rating" value={ratingLabel} intent="primary" align="center" />
<TeamStatItem label="Wins" value={winsLabel} intent="success" align="center" />
<TeamStatItem label="Races" value={racesLabel} intent="high" align="center" />
</Group>
}
performanceLevel={performanceLevel}
/>
);
}

View File

@@ -1,15 +1,15 @@
'use client';
import { Button } from '@/ui/Button';
import { Image } from '@/ui/Image';
import { TeamHero } from '@/ui/TeamHero';
import { Text } from '@/ui/Text';
import { Badge } from '@/ui/Badge';
import { StatGrid } from '@/ui/StatGrid';
import { Group } from '@/ui/Group';
import { Surface } from '@/ui/Surface';
import React from 'react';
import { Users, Calendar, Shield, Settings, Globe } from 'lucide-react';
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { Button } from '@/ui/Button';
import { Surface } from '@/ui/Surface';
import { Icon } from '@/ui/Icon';
interface TeamDetailsHeaderProps {
teamId: string;
name: string;
@@ -17,81 +17,126 @@ interface TeamDetailsHeaderProps {
description?: string;
logoUrl?: string;
memberCount: number;
memberCountLabel?: string;
foundedDate?: string;
foundedDateLabel?: string;
isAdmin?: boolean;
onAdminClick?: () => void;
}
export function TeamDetailsHeader({
name,
tag,
description,
logoUrl,
memberCount,
memberCountLabel,
foundedDate,
foundedDateLabel,
isAdmin,
onAdminClick,
}: TeamDetailsHeaderProps) {
return (
<TeamHero
title={
<Group gap={3}>
{name}
{tag && <Badge variant="outline">[{tag}]</Badge>}
</Group>
}
description={description || 'No mission statement provided.'}
sideContent={
<Surface
variant="muted"
rounded="lg"
width="8rem"
height="8rem"
display="flex"
alignItems="center"
justifyContent="center"
overflow="hidden"
border
>
{logoUrl ? (
<Image src={logoUrl} alt={name} width={128} height={128} />
) : (
<Text size="2xl" weight="bold" variant="low">{name.substring(0, 2).toUpperCase()}</Text>
)}
</Surface>
}
stats={
<StatGrid
columns={2}
variant="box"
stats={[
{
label: 'Personnel',
value: `${memberCount} Units`,
},
{
label: 'Established',
value: foundedDateLabel || 'Unknown',
}
]}
/>
}
actions={
<Group gap={3}>
{isAdmin && (
<Button variant="secondary" onClick={onAdminClick}>
Configure
</Button>
)}
<Button variant="primary">
Join Request
</Button>
</Group>
}
/>
<Box marginBottom={12}>
<Surface variant="precision" padding="none">
<Box padding={10}>
<Box display="flex" alignItems="start" gap={10} flexWrap="wrap">
{/* Logo Container */}
<Box
width={48}
height={48}
rounded="xl"
bg="var(--ui-color-bg-base)"
display="flex"
alignItems="center"
justifyContent="center"
overflow="hidden"
border="1px solid var(--ui-color-border-muted)"
flexShrink={0}
shadow="xl"
>
{logoUrl ? (
<Box as="img" src={logoUrl} alt={name} width="100%" height="100%" objectFit="cover" />
) : (
<Box display="flex" flexDirection="col" alignItems="center" gap="xs">
<Icon icon={Users} size={12} intent="low" />
<Text size="sm" variant="low" weight="bold" mono>{name.substring(0, 3).toUpperCase()}</Text>
</Box>
)}
</Box>
{/* Info Section */}
<Box flex={1} minWidth="300px">
<Stack gap="lg">
<Box>
<Box display="flex" alignItems="center" gap="md" flexWrap="wrap" marginBottom={4}>
<Heading level={1} weight="bold" style={{ fontSize: '3rem' }}>{name}</Heading>
{tag && (
<Box paddingX={3} paddingY={1} bg="rgba(25, 140, 255, 0.1)" border="1px solid var(--ui-color-intent-primary)" rounded="md">
<Text variant="primary" size="sm" weight="bold" mono>{tag}</Text>
</Box>
)}
</Box>
<Box maxWidth="800px">
<Text variant="med" size="xl" leading="relaxed">
{description || 'No mission statement provided.'}
</Text>
</Box>
</Box>
{/* Metadata Grid */}
<Box display="flex" gap={12} flexWrap="wrap" paddingTop={6} borderTop="1px solid var(--ui-color-border-muted)">
<Stack direction="row" align="center" gap="md">
<Box width={12} height={12} rounded="lg" bg="rgba(25, 140, 255, 0.05)" display="flex" alignItems="center" justifyContent="center" border="1px solid rgba(25, 140, 255, 0.1)">
<Icon icon={Users} size={5} intent="primary" />
</Box>
<Stack gap="none">
<Box>
<Text size="xs" variant="low" uppercase>Personnel</Text>
</Box>
<Text weight="bold" mono size="lg">{memberCount} UNITS</Text>
</Stack>
</Stack>
<Stack direction="row" align="center" gap="md">
<Box width={12} height={12} rounded="lg" bg="rgba(78, 212, 224, 0.05)" display="flex" alignItems="center" justifyContent="center" border="1px solid rgba(78, 212, 224, 0.1)">
<Icon icon={Calendar} size={5} intent="telemetry" />
</Box>
<Stack gap="none">
<Box>
<Text size="xs" variant="low" uppercase>Established</Text>
</Box>
<Text weight="bold" mono size="lg">{foundedDateLabel || 'UNKNOWN'}</Text>
</Stack>
</Stack>
<Stack direction="row" align="center" gap="md">
<Box width={12} height={12} rounded="lg" bg="rgba(255, 255, 255, 0.02)" display="flex" alignItems="center" justifyContent="center" border="1px solid var(--ui-color-border-muted)">
<Icon icon={Globe} size={5} intent="low" />
</Box>
<Stack gap="none">
<Box>
<Text size="xs" variant="low" uppercase>Region</Text>
</Box>
<Text weight="bold" mono size="lg">GLOBAL</Text>
</Stack>
</Stack>
</Box>
</Stack>
</Box>
{/* Action Buttons */}
<Box>
<Stack direction="row" gap="md">
{isAdmin && (
<Button variant="secondary" onClick={onAdminClick} icon={<Settings size={18} />}>
Configure
</Button>
)}
<Button variant="primary" icon={<Shield size={18} />}>
Join Request
</Button>
</Stack>
</Box>
</Box>
</Box>
</Surface>
</Box>
);
}

View File

@@ -1,5 +1,7 @@
'use client';
import React, { ReactNode } from 'react';
import { Grid } from '@/ui/Grid';
import { ReactNode } from 'react';
interface TeamGridProps {
children: ReactNode;
@@ -7,7 +9,10 @@ interface TeamGridProps {
export function TeamGrid({ children }: TeamGridProps) {
return (
<Grid cols={{ base: 1, md: 2, lg: 3 }} gap={4}>
<Grid
cols={3}
gap="lg"
>
{children}
</Grid>
);

View File

@@ -1,9 +1,12 @@
'use client';
import React from 'react';
import { Button } from '@/ui/Button';
import { Stack } from '@/ui/Stack';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/ui/Table';
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from '@/ui/Table';
import { Text } from '@/ui/Text';
import { Box } from '@/ui/Box';
import { Surface } from '@/ui/Surface';
interface Member {
driverId: string;
@@ -21,40 +24,54 @@ interface TeamMembersTableProps {
export function TeamMembersTable({ members, isAdmin, onRemoveMember }: TeamMembersTableProps) {
return (
<Stack border borderColor="outline-steel" bg="surface-charcoal/30">
<Surface variant="precision" padding="none">
<Table>
<TableHead>
<TableRow>
<TableHeader>Personnel</TableHeader>
<TableHeader>Role</TableHeader>
<TableHeader>Joined</TableHeader>
<TableHeader textAlign="right">Rating</TableHeader>
{isAdmin && <TableHeader textAlign="right">Actions</TableHeader>}
</TableRow>
<TableHeaderCell>Personnel</TableHeaderCell>
<TableHeaderCell>Role</TableHeaderCell>
<TableHeaderCell>Joined</TableHeaderCell>
<TableHeaderCell textAlign="right">Rating</TableHeaderCell>
{isAdmin && <TableHeaderCell textAlign="right">Actions</TableHeaderCell>}
</TableHead>
<TableBody>
{members.map((member) => (
<TableRow key={member.driverId}>
<TableCell>
<Stack direction="row" align="center" gap={3}>
<Stack w="8" h="8" bg="base-black" border borderColor="outline-steel" display="flex" center>
<Text size="xs" weight="bold" color="primary-accent">{member.driverName.substring(0, 2).toUpperCase()}</Text>
</Stack>
<Text weight="bold" size="sm" color="text-white">{member.driverName}</Text>
<Stack direction="row" align="center" gap="sm">
<Box
width={10}
height={10}
bg="var(--ui-color-bg-base)"
border="1px solid var(--ui-color-border-muted)"
display="flex"
alignItems="center"
justifyContent="center"
rounded="md"
>
<Text size="xs" weight="bold" variant="primary" mono>{member.driverName.substring(0, 2).toUpperCase()}</Text>
</Box>
<Text weight="bold" size="sm">{member.driverName}</Text>
</Stack>
</TableCell>
<TableCell>
<Stack px={2} py={0.5} bg="base-black" border borderColor="outline-steel" display="inline-block">
<Text size="xs" color="text-gray-400" font="mono" uppercase>{member.role}</Text>
</Stack>
<Box
paddingX={2}
paddingY={0.5}
bg="rgba(255,255,255,0.02)"
border="1px solid var(--ui-color-border-muted)"
display="inline-block"
rounded="sm"
>
<Text size="xs" variant="low" mono uppercase>{member.role}</Text>
</Box>
</TableCell>
<TableCell>
<Text size="xs" color="text-gray-500" font="mono">
<Text size="xs" variant="low" mono>
{member.joinedAtLabel}
</Text>
</TableCell>
<TableCell textAlign="right">
<Text font="mono" weight="bold" color="primary-accent">1450</Text>
<Text mono weight="bold" variant="primary">1450</Text>
</TableCell>
{isAdmin && (
<TableCell textAlign="right">
@@ -73,6 +90,6 @@ export function TeamMembersTable({ members, isAdmin, onRemoveMember }: TeamMembe
))}
</TableBody>
</Table>
</Stack>
</Surface>
);
}

View File

@@ -1,10 +1,9 @@
'use client';
import { Icon } from '@/ui/Icon';
import { Input } from '@/ui/Input';
import { Search } from 'lucide-react';
import { Box } from '@/ui/Box';
import React from 'react';
interface TeamSearchBarProps {
@@ -14,13 +13,16 @@ interface TeamSearchBarProps {
export function TeamSearchBar({ searchQuery, onSearchChange }: TeamSearchBarProps) {
return (
<Input
type="text"
placeholder="Search teams by name, description, region, or language..."
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
icon={<Icon icon={Search} size={5} intent="low" />}
fullWidth
/>
<Box marginBottom={8}>
<Input
type="text"
placeholder="FILTER BY TEAM NAME, REGION OR TAGS..."
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
icon={<Icon icon={Search} size={4} intent="low" />}
variant="search"
fullWidth
/>
</Box>
);
}

View File

@@ -1,8 +1,10 @@
'use client';
import React from 'react';
import { Box } from '@/ui/Box';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/ui/Table';
import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from '@/ui/Table';
import { Text } from '@/ui/Text';
import { Surface } from '@/ui/Surface';
interface Standing {
leagueId: string;
@@ -18,9 +20,9 @@ interface TeamStandingsPanelProps {
export function TeamStandingsPanel({ standings }: TeamStandingsPanelProps) {
return (
<Box border borderColor="outline-steel" bg="surface-charcoal/30">
<Box p={4} borderBottom borderColor="outline-steel">
<Text size="xs" weight="bold" color="text-gray-400" uppercase letterSpacing="widest">
<Surface variant="precision" padding="none">
<Box padding={6} borderBottom="1px solid var(--ui-color-border-muted)">
<Text size="xs" weight="bold" variant="low" uppercase letterSpacing="0.1em">
Active Campaign Standings
</Text>
</Box>
@@ -28,51 +30,49 @@ export function TeamStandingsPanel({ standings }: TeamStandingsPanelProps) {
{standings.length > 0 ? (
<Table>
<TableHead>
<TableRow>
<TableHeader>League</TableHeader>
<TableHeader textAlign="center">Pos</TableHeader>
<TableHeader textAlign="center">Races</TableHeader>
<TableHeader textAlign="right">Points</TableHeader>
</TableRow>
<TableHeaderCell>League</TableHeaderCell>
<TableHeaderCell textAlign="center">Pos</TableHeaderCell>
<TableHeaderCell textAlign="center">Races</TableHeaderCell>
<TableHeaderCell textAlign="right">Points</TableHeaderCell>
</TableHead>
<TableBody>
{standings.map((s) => (
<TableRow key={s.leagueId}>
<TableCell>
<Text weight="bold" size="sm" color="text-white">{s.leagueName}</Text>
<Text weight="bold" size="sm">{s.leagueName}</Text>
</TableCell>
<TableCell textAlign="center">
<Box
display="inline-flex"
center
w="6"
h="6"
bg={s.position <= 3 ? 'warning-amber/20' : 'base-black'}
border
borderColor={s.position <= 3 ? 'warning-amber/40' : 'outline-steel'}
width={8}
height={8}
bg={s.position <= 3 ? 'rgba(255, 190, 77, 0.1)' : 'var(--ui-color-bg-base)'}
border={s.position <= 3 ? '1px solid var(--ui-color-intent-warning)' : '1px solid var(--ui-color-border-muted)'}
rounded="md"
>
<Text size="xs" weight="bold" color={s.position <= 3 ? 'warning-amber' : 'text-gray-400'}>
<Text size="xs" weight="bold" variant={s.position <= 3 ? 'warning' : 'low'} mono>
{s.position}
</Text>
</Box>
</TableCell>
<TableCell textAlign="center">
<Text size="xs" color="text-gray-500" font="mono">{s.races}</Text>
<Text size="xs" variant="low" mono>{s.races}</Text>
</TableCell>
<TableCell textAlign="right">
<Text font="mono" weight="bold" color="telemetry-aqua">{s.points}</Text>
<Text mono weight="bold" variant="telemetry">{s.points}</Text>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
) : (
<Box py={12} textAlign="center">
<Text color="text-gray-600" font="mono" size="xs" uppercase letterSpacing="widest">
<Box paddingY={16} textAlign="center">
<Text variant="low" mono size="xs" uppercase letterSpacing="0.1em">
No active campaign telemetry
</Text>
</Box>
)}
</Box>
</Surface>
);
}

View File

@@ -1,46 +1,37 @@
'use client';
import { ReactNode } from 'react';
import React, { ReactNode } from 'react';
import { Box } from '@/ui/Box';
import { Container } from '@/ui/Container';
import { Group } from '@/ui/Group';
import { Text } from '@/ui/Text';
import { StatusDot } from '@/ui/StatusDot';
import { Heading } from '@/ui/Heading';
interface TeamsDirectoryProps {
children: ReactNode;
title?: string;
subtitle?: string;
}
export function TeamsDirectory({ children, title, subtitle }: TeamsDirectoryProps) {
export function TeamsDirectory({ children }: TeamsDirectoryProps) {
return (
<Container size="lg" py={12}>
<Group direction="column" gap={10} fullWidth>
{title && (
<Group direction="row" align="center" gap={2}>
<StatusDot intent="primary" size="md" />
<Text size="xs" weight="bold" variant="low" uppercase>{title}</Text>
</Group>
)}
<Box paddingY={12} bg="var(--ui-color-bg-base)" minHeight="100vh">
<Container size="xl">
{children}
</Group>
</Container>
</Container>
</Box>
);
}
export function TeamsDirectorySection({ children, title, accentColor = "primary-accent" }: { children: ReactNode, title: string, accentColor?: string }) {
const intentMap: Record<string, 'primary' | 'success' | 'warning' | 'critical' | 'telemetry'> = {
'primary-accent': 'primary',
'telemetry-aqua': 'telemetry',
};
interface TeamsDirectorySectionProps {
title: string;
children: ReactNode;
accentColor?: string;
}
export function TeamsDirectorySection({ title, children }: TeamsDirectorySectionProps) {
return (
<Group direction="column" gap={6} fullWidth>
<Group direction="row" align="center" gap={2}>
<StatusDot intent={intentMap[accentColor] || 'primary'} size="md" />
<Text size="xs" weight="bold" variant="low" uppercase>{title}</Text>
</Group>
<Box marginBottom={16}>
<Box marginBottom={8} borderBottom="1px solid var(--ui-color-border-muted)" paddingBottom={4}>
<Heading level={2} weight="bold" uppercase>{title}</Heading>
</Box>
{children}
</Group>
</Box>
);
}

View File

@@ -1,27 +1,26 @@
'use client';
import React from 'react';
import { Plus } from 'lucide-react';
import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon';
import { PageHeader } from '@/ui/PageHeader';
import { Plus, Users } from 'lucide-react';
import { TeamsHeader } from '@/ui/TeamsHeader';
interface TeamsDirectoryHeaderProps {
onCreateTeam: () => void;
}
export function TeamsDirectoryHeader({ onCreateTeam }: TeamsDirectoryHeaderProps) {
return (
<PageHeader
icon={Users}
title="Teams"
description="Operational Units & Racing Collectives"
<TeamsHeader
title="Directory"
subtitle="Professional Racing Rosters"
action={
<Button
variant="primary"
onClick={onCreateTeam}
icon={<Icon icon={Plus} size={4} />}
icon={<Plus size={16} />}
>
Initialize Team
Register Team
</Button>
}
/>