website refactor
This commit is contained in:
@@ -5,7 +5,6 @@ import { Button } from '@/ui/Button';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { TextArea } from '@/ui/TextArea';
|
||||
import { useCreateTeam } from "@/hooks/team/useCreateTeam";
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { InfoBanner } from '@/ui/InfoBanner';
|
||||
@@ -80,7 +79,7 @@ export function CreateTeamForm({ onCancel, onSuccess, onNavigate }: CreateTeamFo
|
||||
};
|
||||
|
||||
return (
|
||||
<Box as="form" onSubmit={handleSubmit}>
|
||||
<Stack as="form" onSubmit={handleSubmit}>
|
||||
<Stack gap={6}>
|
||||
<Input
|
||||
label="Team Name *"
|
||||
@@ -125,7 +124,7 @@ export function CreateTeamForm({ onCancel, onSuccess, onNavigate }: CreateTeamFo
|
||||
</Stack>
|
||||
</InfoBanner>
|
||||
|
||||
<Box display="flex" gap={3}>
|
||||
<Stack display="flex" gap={3}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
@@ -144,8 +143,8 @@ export function CreateTeamForm({ onCancel, onSuccess, onNavigate }: CreateTeamFo
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import React from 'react';
|
||||
import { UserPlus } from 'lucide-react';
|
||||
import { getMediaUrl } from '@/lib/utilities/media';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
@@ -32,9 +31,9 @@ export function FeaturedRecruiting({ teams, onTeamClick }: FeaturedRecruitingPro
|
||||
if (recruitingTeams.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Box mb={10}>
|
||||
<Stack mb={10}>
|
||||
<Stack direction="row" align="center" gap={3} mb={4}>
|
||||
<Box
|
||||
<Stack
|
||||
display="flex"
|
||||
center
|
||||
width="10"
|
||||
@@ -45,11 +44,11 @@ export function FeaturedRecruiting({ teams, onTeamClick }: FeaturedRecruitingPro
|
||||
borderColor="border-performance-green/20"
|
||||
>
|
||||
<Icon icon={UserPlus} size={5} color="text-performance-green" />
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Heading level={2}>Looking for Drivers</Heading>
|
||||
<Text size="xs" color="text-gray-500">Teams actively recruiting new members</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<RecruitingTeamGrid>
|
||||
@@ -66,6 +65,6 @@ export function FeaturedRecruiting({ teams, onTeamClick }: FeaturedRecruitingPro
|
||||
/>
|
||||
))}
|
||||
</RecruitingTeamGrid>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Users, Trophy } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Image } from '@/ui/Image';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
@@ -27,7 +26,7 @@ export function RecruitingTeamCard({
|
||||
onClick,
|
||||
}: RecruitingTeamCardProps) {
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
as="button"
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
@@ -38,8 +37,8 @@ export function RecruitingTeamCard({
|
||||
borderColor="border-charcoal-outline"
|
||||
className="hover:border-performance-green/40 transition-all duration-200 text-left group"
|
||||
>
|
||||
<Box display="flex" alignItems="start" justifyContent="between" mb={3}>
|
||||
<Box width="8" height="8" rounded="lg" bg="bg-charcoal-outline" border={true} borderColor="border-charcoal-outline" overflow="hidden">
|
||||
<Stack display="flex" alignItems="start" justifyContent="between" mb={3}>
|
||||
<Stack width="8" height="8" rounded="lg" bg="bg-charcoal-outline" border={true} borderColor="border-charcoal-outline" overflow="hidden">
|
||||
<Image
|
||||
src={logoUrl}
|
||||
alt={name}
|
||||
@@ -47,12 +46,12 @@ export function RecruitingTeamCard({
|
||||
height={32}
|
||||
objectFit="cover"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Badge variant="success">
|
||||
<Box w="1.5" h="1.5" rounded="full" bg="bg-performance-green" animate="pulse" mr={1} />
|
||||
<Stack w="1.5" h="1.5" rounded="full" bg="bg-performance-green" animate="pulse" mr={1} />
|
||||
Recruiting
|
||||
</Badge>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Text color="text-white" weight="semibold" block mb={1} className="group-hover:text-performance-green transition-colors line-clamp-1">
|
||||
{name}
|
||||
@@ -62,7 +61,7 @@ export function RecruitingTeamCard({
|
||||
<Stack direction="row" align="center" gap={2} wrap>
|
||||
{category && (
|
||||
<Stack direction="row" align="center" gap={1}>
|
||||
<Box w="1.5" h="1.5" rounded="full" bg="bg-purple-400" />
|
||||
<Stack w="1.5" h="1.5" rounded="full" bg="bg-purple-400" />
|
||||
<Text size="xs" color="text-purple-400">{category}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -75,6 +74,6 @@ export function RecruitingTeamCard({
|
||||
<Text size="xs" color="text-gray-400">{totalWins} wins</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Card } from '@/ui/Card';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { TextArea } from '@/ui/TextArea';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
@@ -100,18 +99,18 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
|
||||
return (
|
||||
<Stack gap={6}>
|
||||
<Card>
|
||||
<Box display="flex" alignItems="center" justifyContent="between" mb={6}>
|
||||
<Stack display="flex" alignItems="center" justifyContent="between" mb={6}>
|
||||
<Heading level={3}>Team Settings</Heading>
|
||||
{!editMode && (
|
||||
<Button variant="secondary" onClick={() => setEditMode(true)}>
|
||||
Edit Details
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{editMode ? (
|
||||
<Stack gap={4}>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
|
||||
Team Name
|
||||
</Text>
|
||||
@@ -120,9 +119,9 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
|
||||
value={editedTeam.name}
|
||||
onChange={(e) => setEditedTeam({ ...editedTeam, name: e.target.value })}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Box>
|
||||
<Stack>
|
||||
<Text as="label" size="sm" weight="medium" color="text-gray-400" block mb={2}>
|
||||
Team Tag
|
||||
</Text>
|
||||
@@ -133,7 +132,7 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
|
||||
maxLength={4}
|
||||
/>
|
||||
<Text size="xs" color="text-gray-500" block mt={1}>Max 4 characters</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<TextArea
|
||||
label="Description"
|
||||
@@ -163,18 +162,18 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack gap={4}>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Text size="sm" color="text-gray-400" block>Team Name</Text>
|
||||
<Text color="text-white" weight="medium" block>{team.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text size="sm" color="text-gray-400" block>Team Tag</Text>
|
||||
<Text color="text-white" weight="medium" block>{team.tag}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text size="sm" color="text-gray-400" block>Description</Text>
|
||||
<Text color="text-white" block>{team.description}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
</Card>
|
||||
@@ -183,9 +182,9 @@ export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
|
||||
<Heading level={3} mb={6}>Join Requests</Heading>
|
||||
|
||||
{loading ? (
|
||||
<Box textAlign="center" py={8}>
|
||||
<Stack textAlign="center" py={8}>
|
||||
<Text color="text-gray-400">Loading requests...</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
) : joinRequests.length > 0 ? (
|
||||
<JoinRequestList>
|
||||
{joinRequests.map((request: TeamJoinRequestViewModel) => (
|
||||
|
||||
@@ -7,13 +7,12 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Image } from '@/ui/Image';
|
||||
import { PlaceholderImage } from '@/ui/PlaceholderImage';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface TeamCardProps {
|
||||
@@ -46,13 +45,13 @@ export function TeamCard({
|
||||
onClick,
|
||||
}: TeamCardProps) {
|
||||
return (
|
||||
<Box onClick={onClick} h="full" cursor={onClick ? 'pointer' : 'default'} className="group">
|
||||
<Stack onClick={onClick} h="full" cursor={onClick ? 'pointer' : 'default'} className="group">
|
||||
<Card h="full" p={0} display="flex" flexDirection="col" overflow="hidden" className="bg-panel-gray/40 border-border-gray/50 hover:border-primary-accent/30 hover:bg-panel-gray/60 transition-all duration-300">
|
||||
{/* Header with Logo */}
|
||||
<Box p={5} pb={0}>
|
||||
<Stack p={5} pb={0}>
|
||||
<Stack direction="row" align="start" gap={4}>
|
||||
{/* Logo */}
|
||||
<Box
|
||||
<Stack
|
||||
w="16"
|
||||
h="16"
|
||||
rounded="none"
|
||||
@@ -77,11 +76,11 @@ export function TeamCard({
|
||||
) : (
|
||||
<PlaceholderImage size={64} />
|
||||
)}
|
||||
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent/30" />
|
||||
</Box>
|
||||
<Stack position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent/30" />
|
||||
</Stack>
|
||||
|
||||
{/* Title & Badges */}
|
||||
<Box flexGrow={1} minWidth="0">
|
||||
<Stack flexGrow={1} minWidth="0">
|
||||
<Stack direction="row" align="start" justify="between" gap={2}>
|
||||
<Heading level={4} weight="bold" fontSize="lg" className="tracking-tight group-hover:text-primary-accent transition-colors">
|
||||
{name}
|
||||
@@ -99,12 +98,12 @@ export function TeamCard({
|
||||
{specializationContent}
|
||||
{categoryBadge}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Content */}
|
||||
<Box p={5} display="flex" flexDirection="col" flexGrow={1}>
|
||||
<Stack p={5} display="flex" flexDirection="col" flexGrow={1}>
|
||||
{/* Description */}
|
||||
<Text
|
||||
size="xs"
|
||||
@@ -122,7 +121,7 @@ export function TeamCard({
|
||||
{(region || languagesContent) && (
|
||||
<Stack direction="row" align="center" gap={2} wrap mb={4}>
|
||||
{region && (
|
||||
<Box
|
||||
<Stack
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={2}
|
||||
@@ -135,17 +134,17 @@ export function TeamCard({
|
||||
>
|
||||
<Icon icon={Globe} size={3} color="text-primary-accent" />
|
||||
<Text size="xs" color="text-gray-400" weight="bold" className="uppercase tracking-widest">{region}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
{languagesContent}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{/* Spacer */}
|
||||
<Box flexGrow={1} />
|
||||
<Stack flexGrow={1} />
|
||||
|
||||
{/* Footer */}
|
||||
<Box
|
||||
<Stack
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="between"
|
||||
@@ -165,9 +164,9 @@ export function TeamCard({
|
||||
<Text size="xs" color="text-gray-500" weight="bold" className="uppercase tracking-widest">VIEW</Text>
|
||||
<Icon icon={ChevronRight} size={3} color="text-gray-500" className="transition-transform group-hover:translate-x-0.5" />
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
TrendingUp,
|
||||
Shield
|
||||
} from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
@@ -102,12 +101,12 @@ export function TeamCard({
|
||||
)}
|
||||
categoryBadge={category && (
|
||||
<Badge variant="primary">
|
||||
<Box w="2" h="2" rounded="full" bg="bg-purple-500" mr={1.5} />
|
||||
<Stack w="2" h="2" rounded="full" bg="bg-purple-500" mr={1.5} />
|
||||
{category}
|
||||
</Badge>
|
||||
)}
|
||||
languagesContent={languages && languages.length > 0 && (
|
||||
<Box
|
||||
<Stack
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={1.5}
|
||||
@@ -123,7 +122,7 @@ export function TeamCard({
|
||||
{languages.slice(0, 2).join(', ')}
|
||||
{languages.length > 2 && ` +${languages.length - 2}`}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
statsContent={
|
||||
<>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
@@ -31,7 +30,7 @@ export function TeamDetailsHeader({
|
||||
onAdminClick,
|
||||
}: TeamDetailsHeaderProps) {
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
bg="surface-charcoal"
|
||||
border
|
||||
borderColor="outline-steel"
|
||||
@@ -40,7 +39,7 @@ export function TeamDetailsHeader({
|
||||
overflow="hidden"
|
||||
>
|
||||
{/* Background accent */}
|
||||
<Box
|
||||
<Stack
|
||||
position="absolute"
|
||||
top="0"
|
||||
right="0"
|
||||
@@ -53,7 +52,7 @@ export function TeamDetailsHeader({
|
||||
/>
|
||||
|
||||
<Stack direction="row" align="start" gap={8} position="relative">
|
||||
<Box
|
||||
<Stack
|
||||
w="32"
|
||||
h="32"
|
||||
bg="base-black"
|
||||
@@ -68,15 +67,15 @@ export function TeamDetailsHeader({
|
||||
) : (
|
||||
<Text size="2xl" weight="bold" color="text-gray-700">{name.substring(0, 2).toUpperCase()}</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Box flex="1">
|
||||
<Stack flex="1">
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Heading level={1} weight="bold">{name}</Heading>
|
||||
{tag && (
|
||||
<Box px={2} py={1} bg="base-black" border borderColor="outline-steel">
|
||||
<Stack px={2} py={1} bg="base-black" border borderColor="outline-steel">
|
||||
<Text size="xs" font="mono" color="primary-accent" weight="bold">[{tag}]</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -85,18 +84,18 @@ export function TeamDetailsHeader({
|
||||
</Text>
|
||||
|
||||
<Stack direction="row" gap={6} mt={6}>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Text size="xs" color="text-gray-500" uppercase font="mono" letterSpacing="widest">Personnel</Text>
|
||||
<Text block weight="bold" color="text-white">{memberCount} Units</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text size="xs" color="text-gray-500" uppercase font="mono" letterSpacing="widest">Established</Text>
|
||||
<Text block weight="bold" color="text-white">
|
||||
{foundedDate ? new Date(foundedDate).toLocaleDateString() : 'Unknown'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Stack gap={3}>
|
||||
{isAdmin && (
|
||||
@@ -109,6 +108,6 @@ export function TeamDetailsHeader({
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Search, Star, Trophy, Percent, Hash, LucideIcon } from 'lucide-react';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
@@ -50,7 +49,7 @@ export function TeamFilter({
|
||||
<Stack mb={6} gap={4}>
|
||||
{/* Search and Level Filter Row */}
|
||||
<Stack direction="row" align="center" gap={4} wrap>
|
||||
<Box maxWidth="448px" fullWidth>
|
||||
<Stack maxWidth="448px" fullWidth>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search teams..."
|
||||
@@ -58,7 +57,7 @@ export function TeamFilter({
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
icon={<Icon icon={Search} size={5} color="text-gray-500" />}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Level Filter */}
|
||||
<Stack direction="row" align="center" gap={2} wrap>
|
||||
@@ -92,7 +91,7 @@ export function TeamFilter({
|
||||
{/* Sort Options */}
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Text size="sm" color="text-gray-400">Sort by:</Text>
|
||||
<Box p={1} rounded="lg" border={true} borderColor="border-charcoal-outline" bg="bg-iron-gray" bgOpacity={0.5}>
|
||||
<Stack p={1} rounded="lg" border={true} borderColor="border-charcoal-outline" bg="bg-iron-gray" bgOpacity={0.5}>
|
||||
<Stack direction="row" align="center" gap={1}>
|
||||
{SORT_OPTIONS.map((option) => {
|
||||
const isActive = sortBy === option.id;
|
||||
@@ -109,7 +108,7 @@ export function TeamFilter({
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
@@ -30,7 +29,7 @@ export function TeamHeaderPanel({
|
||||
actions,
|
||||
}: TeamHeaderPanelProps) {
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
bg="surface-charcoal"
|
||||
border
|
||||
borderColor="border-steel-grey"
|
||||
@@ -38,12 +37,12 @@ export function TeamHeaderPanel({
|
||||
className="relative overflow-hidden"
|
||||
>
|
||||
{/* Instrument-grade accent corner */}
|
||||
<Box position="absolute" top="-1px" left="-1px" w="4" h="4" borderTop borderLeft borderColor="primary-blue/40" />
|
||||
<Stack position="absolute" top="-1px" left="-1px" w="4" h="4" borderTop borderLeft borderColor="primary-blue/40" />
|
||||
|
||||
<Stack direction="row" align="start" justify="between" wrap gap={6}>
|
||||
<Stack direction="row" align="start" gap={6} wrap flexGrow={1}>
|
||||
{/* Logo Container */}
|
||||
<Box
|
||||
<Stack
|
||||
w="24"
|
||||
h="24"
|
||||
bg="base-graphite"
|
||||
@@ -56,10 +55,10 @@ export function TeamHeaderPanel({
|
||||
>
|
||||
<TeamLogo teamId={teamId} alt={name} />
|
||||
{/* Corner detail */}
|
||||
<Box position="absolute" bottom="0" right="0" w="2" h="2" bg="primary-blue/20" />
|
||||
</Box>
|
||||
<Stack position="absolute" bottom="0" right="0" w="2" h="2" bg="primary-blue/20" />
|
||||
</Stack>
|
||||
|
||||
<Box flexGrow={1} minWidth="0">
|
||||
<Stack flexGrow={1} minWidth="0">
|
||||
<Stack direction="row" align="center" gap={3} mb={2}>
|
||||
<Heading level={1} weight="bold" className="tracking-tight">{name}</Heading>
|
||||
{tag && <TeamTag tag={tag} />}
|
||||
@@ -72,29 +71,29 @@ export function TeamHeaderPanel({
|
||||
)}
|
||||
|
||||
<Stack direction="row" align="center" gap={4} wrap>
|
||||
<Box display="flex" alignItems="center" gap={1.5}>
|
||||
<Box w="1.5" h="1.5" bg="primary-blue" />
|
||||
<Stack display="flex" alignItems="center" gap={1.5}>
|
||||
<Stack w="1.5" h="1.5" bg="primary-blue" />
|
||||
<Text size="xs" color="text-gray-300" font="mono" className="uppercase tracking-wider">
|
||||
{memberCount} {memberCount === 1 ? 'Member' : 'Members'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{category && (
|
||||
<Box display="flex" alignItems="center" gap={1.5}>
|
||||
<Box w="1.5" h="1.5" bg="telemetry-aqua" />
|
||||
<Stack display="flex" alignItems="center" gap={1.5}>
|
||||
<Stack w="1.5" h="1.5" bg="telemetry-aqua" />
|
||||
<Text size="xs" color="text-gray-300" font="mono" className="uppercase tracking-wider">
|
||||
{category}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{activeLeaguesCount !== undefined && (
|
||||
<Box display="flex" alignItems="center" gap={1.5}>
|
||||
<Box w="1.5" h="1.5" bg="warning-amber" />
|
||||
<Stack display="flex" alignItems="center" gap={1.5}>
|
||||
<Stack w="1.5" h="1.5" bg="warning-amber" />
|
||||
<Text size="xs" color="text-gray-300" font="mono" className="uppercase tracking-wider">
|
||||
{activeLeaguesCount} {activeLeaguesCount === 1 ? 'League' : 'Leagues'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{foundedDate && (
|
||||
@@ -103,15 +102,15 @@ export function TeamHeaderPanel({
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{actions && (
|
||||
<Box>
|
||||
<Stack>
|
||||
{actions}
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
|
||||
|
||||
import { JoinTeamButton } from '@/components/teams/JoinTeamButton';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { TeamLogo } from '@/components/teams/TeamLogo';
|
||||
import { TeamTag } from '@/components/teams/TeamTag';
|
||||
import { Text } from '@/ui/Text';
|
||||
@@ -28,7 +27,7 @@ export function TeamHero({ team, memberCount, onUpdate }: TeamHeroProps) {
|
||||
<Card>
|
||||
<Stack direction="row" align="start" justify="between" wrap gap={6}>
|
||||
<Stack direction="row" align="start" gap={6} wrap flexGrow={1}>
|
||||
<Box
|
||||
<Stack
|
||||
w="24"
|
||||
h="24"
|
||||
rounded="lg"
|
||||
@@ -40,9 +39,9 @@ export function TeamHero({ team, memberCount, onUpdate }: TeamHeroProps) {
|
||||
justifyContent="center"
|
||||
>
|
||||
<TeamLogo teamId={team.id} alt={team.name} />
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Box flexGrow={1} minWidth="0">
|
||||
<Stack flexGrow={1} minWidth="0">
|
||||
<Stack direction="row" align="center" gap={3} mb={2}>
|
||||
<Heading level={1}>{team.name}</Heading>
|
||||
{team.tag && <TeamTag tag={team.tag} />}
|
||||
@@ -54,7 +53,7 @@ export function TeamHero({ team, memberCount, onUpdate }: TeamHeroProps) {
|
||||
<Text size="sm" color="text-gray-400">{memberCount} {memberCount === 1 ? 'member' : 'members'}</Text>
|
||||
{team.category && (
|
||||
<Stack direction="row" align="center" gap={1.5}>
|
||||
<Box w="2" h="2" rounded="full" bg="bg-purple-500" />
|
||||
<Stack w="2" h="2" rounded="full" bg="bg-purple-500" />
|
||||
<Text size="sm" color="text-purple-400">{team.category}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -69,7 +68,7 @@ export function TeamHero({ team, memberCount, onUpdate }: TeamHeroProps) {
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<JoinTeamButton teamId={team.id} onUpdate={onUpdate} />
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
import { Users } from 'lucide-react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface TeamHeroSectionProps {
|
||||
@@ -24,9 +23,9 @@ export function TeamHeroSection({
|
||||
sideContent,
|
||||
}: TeamHeroSectionProps) {
|
||||
return (
|
||||
<Box position="relative" mb={10} overflow="hidden">
|
||||
<Stack position="relative" mb={10} overflow="hidden">
|
||||
{/* Main Hero Card */}
|
||||
<Box
|
||||
<Stack
|
||||
position="relative"
|
||||
py={12}
|
||||
px={8}
|
||||
@@ -36,19 +35,19 @@ export function TeamHeroSection({
|
||||
border
|
||||
>
|
||||
{/* Background decorations */}
|
||||
<Box position="absolute" top="0" right="0" w="80" h="80" bg="bg-purple-500" bgOpacity={0.1} rounded="full" blur="3xl" />
|
||||
<Box position="absolute" bottom="0" left="1/4" w="64" h="64" bg="bg-neon-aqua" bgOpacity={0.05} rounded="full" blur="3xl" />
|
||||
<Box position="absolute" top="1/2" right="1/4" w="48" h="48" bg="bg-yellow-400" bgOpacity={0.05} rounded="full" blur="2xl" />
|
||||
<Stack position="absolute" top="0" right="0" w="80" h="80" bg="bg-purple-500" bgOpacity={0.1} rounded="full" blur="3xl" />
|
||||
<Stack position="absolute" bottom="0" left="1/4" w="64" h="64" bg="bg-neon-aqua" bgOpacity={0.05} rounded="full" blur="3xl" />
|
||||
<Stack position="absolute" top="1/2" right="1/4" w="48" h="48" bg="bg-yellow-400" bgOpacity={0.05} rounded="full" blur="2xl" />
|
||||
|
||||
<Box position="relative" zIndex={10}>
|
||||
<Stack position="relative" zIndex={10}>
|
||||
<Stack direction={{ base: 'col', lg: 'row' }} align="start" justify="between" gap={8}>
|
||||
<Box maxWidth="xl">
|
||||
<Stack maxWidth="xl">
|
||||
{/* Badge */}
|
||||
<Box mb={4}>
|
||||
<Stack mb={4}>
|
||||
<Badge variant="primary" icon={Users}>
|
||||
Team Racing
|
||||
</Badge>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Heading level={1}>
|
||||
{title}
|
||||
@@ -67,15 +66,15 @@ export function TeamHeroSection({
|
||||
<Stack direction="row" gap={3} wrap>
|
||||
{actionsContent}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Side Content */}
|
||||
<Box w={{ base: 'full', lg: '72' }}>
|
||||
<Stack w={{ base: 'full', lg: '72' }}>
|
||||
{sideContent}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Users, UserPlus } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
|
||||
interface TeamHeroStatsProps {
|
||||
teamCount: number;
|
||||
@@ -13,7 +12,7 @@ interface TeamHeroStatsProps {
|
||||
export function TeamHeroStats({ teamCount, recruitingCount }: TeamHeroStatsProps) {
|
||||
return (
|
||||
<Stack direction="row" align="center" gap={4}>
|
||||
<Box
|
||||
<Stack
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={2}
|
||||
@@ -27,8 +26,8 @@ export function TeamHeroStats({ teamCount, recruitingCount }: TeamHeroStatsProps
|
||||
<Icon icon={Users} size={4} color="text-purple-400" />
|
||||
<Text weight="semibold" color="text-white">{teamCount}</Text>
|
||||
<Text size="sm" color="text-gray-500">Teams</Text>
|
||||
</Box>
|
||||
<Box
|
||||
</Stack>
|
||||
<Stack
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={2}
|
||||
@@ -42,7 +41,7 @@ export function TeamHeroStats({ teamCount, recruitingCount }: TeamHeroStatsProps
|
||||
<Icon icon={UserPlus} size={4} color="text-performance-green" />
|
||||
<Text weight="semibold" color="text-white">{recruitingCount}</Text>
|
||||
<Text size="sm" color="text-gray-500">Recruiting</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Image } from '@/ui/Image';
|
||||
@@ -14,7 +13,7 @@ interface TeamIdentityProps {
|
||||
export function TeamIdentity({ name, logoUrl, performanceLevel, category }: TeamIdentityProps) {
|
||||
return (
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box width="10" height="10" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
|
||||
<Stack width="10" height="10" rounded="lg" overflow="hidden" border borderColor="border-charcoal-outline">
|
||||
<Image
|
||||
src={logoUrl}
|
||||
alt={name}
|
||||
@@ -24,8 +23,8 @@ export function TeamIdentity({ name, logoUrl, performanceLevel, category }: Team
|
||||
fullHeight
|
||||
objectFit="cover"
|
||||
/>
|
||||
</Box>
|
||||
<Box flex={1}>
|
||||
</Stack>
|
||||
<Stack flex={1}>
|
||||
<Text weight="semibold" color="text-white" block truncate>{name}</Text>
|
||||
{(performanceLevel || category) && (
|
||||
<Stack direction="row" align="center" gap={2} mt={1} wrap>
|
||||
@@ -34,13 +33,13 @@ export function TeamIdentity({ name, logoUrl, performanceLevel, category }: Team
|
||||
)}
|
||||
{category && (
|
||||
<Stack direction="row" align="center" gap={1}>
|
||||
<Box width="1.5" height="1.5" rounded="full" bg="bg-primary-blue" opacity={0.5} />
|
||||
<Stack width="1.5" height="1.5" rounded="full" bg="bg-primary-blue" opacity={0.5} />
|
||||
<Text size="xs" color="text-primary-blue">{category}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
|
||||
|
||||
import { Crown, Trophy, Users } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Image } from '@/ui/Image';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface TeamLeaderboardItemProps {
|
||||
@@ -37,7 +36,7 @@ export function TeamLeaderboardItem({
|
||||
medalBorder,
|
||||
}: TeamLeaderboardItemProps) {
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
as="button"
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
@@ -56,7 +55,7 @@ export function TeamLeaderboardItem({
|
||||
className="last:border-0"
|
||||
>
|
||||
{/* Position */}
|
||||
<Box
|
||||
<Stack
|
||||
w="8"
|
||||
h="8"
|
||||
display="flex"
|
||||
@@ -75,10 +74,10 @@ export function TeamLeaderboardItem({
|
||||
) : (
|
||||
position
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Team Info */}
|
||||
<Box w="9" h="9" rounded="md" overflow="hidden" bg="bg-deep-graphite" border style={{ borderColor: 'rgba(38, 38, 38, 0.8)' }} flexShrink={0}>
|
||||
<Stack w="9" h="9" rounded="md" overflow="hidden" bg="bg-deep-graphite" border style={{ borderColor: 'rgba(38, 38, 38, 0.8)' }} flexShrink={0}>
|
||||
<Image
|
||||
src={logoUrl}
|
||||
alt={name}
|
||||
@@ -86,15 +85,15 @@ export function TeamLeaderboardItem({
|
||||
height={36}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
/>
|
||||
</Box>
|
||||
<Box style={{ flex: 1, minWidth: 0 }}>
|
||||
</Stack>
|
||||
<Stack style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text weight="medium" color="text-white" className="truncate" block>
|
||||
{name}
|
||||
</Text>
|
||||
<Stack direction="row" align="center" gap={2} wrap mt={0.5}>
|
||||
{category && (
|
||||
<Stack direction="row" align="center" gap={1}>
|
||||
<Box w="1.5" h="1.5" rounded="full" bg="bg-purple-500" />
|
||||
<Stack w="1.5" h="1.5" rounded="full" bg="bg-purple-500" />
|
||||
<Text size="xs" color="text-purple-400">{category}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -108,20 +107,20 @@ export function TeamLeaderboardItem({
|
||||
</Stack>
|
||||
{isRecruiting && (
|
||||
<Stack direction="row" align="center" gap={1}>
|
||||
<Box w="1.5" h="1.5" rounded="full" bg="bg-performance-green" />
|
||||
<Stack w="1.5" h="1.5" rounded="full" bg="bg-performance-green" />
|
||||
<Text size="xs" color="text-performance-green">Recruiting</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Rating */}
|
||||
<Box textAlign="right">
|
||||
<Stack textAlign="right">
|
||||
<Text font="mono" weight="semibold" color="text-purple-400" block>
|
||||
{typeof rating === 'number' ? Math.round(rating).toLocaleString() : '—'}
|
||||
</Text>
|
||||
<Text size="xs" color="text-gray-500">Rating</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Table, TableHead, TableBody, TableRow, TableHeader, TableCell } from '@/ui/Table';
|
||||
@@ -21,7 +20,7 @@ interface TeamLeaderboardPanelProps {
|
||||
|
||||
export function TeamLeaderboardPanel({ teams, onTeamClick }: TeamLeaderboardPanelProps) {
|
||||
return (
|
||||
<Box border borderColor="border-steel-grey" bg="surface-charcoal/50" overflow="hidden">
|
||||
<Stack border borderColor="border-steel-grey" bg="surface-charcoal/50" overflow="hidden">
|
||||
<Table>
|
||||
<TableHead className="bg-base-graphite/50">
|
||||
<TableRow>
|
||||
@@ -46,9 +45,9 @@ export function TeamLeaderboardPanel({ teams, onTeamClick }: TeamLeaderboardPane
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box w="8" h="8" bg="base-graphite" border borderColor="border-steel-grey" display="flex" center overflow="hidden">
|
||||
<Stack w="8" h="8" bg="base-graphite" border borderColor="border-steel-grey" display="flex" center overflow="hidden">
|
||||
<TeamLogo teamId={team.id} alt={team.name} />
|
||||
</Box>
|
||||
</Stack>
|
||||
<Text weight="bold" size="sm" color="text-white" className="group-hover:text-primary-blue transition-colors">
|
||||
{team.name}
|
||||
</Text>
|
||||
@@ -70,6 +69,6 @@ export function TeamLeaderboardPanel({ teams, onTeamClick }: TeamLeaderboardPane
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Button } from '@/ui/Button';
|
||||
@@ -22,7 +21,7 @@ interface TeamMembersTableProps {
|
||||
|
||||
export function TeamMembersTable({ members, isAdmin, onRemoveMember }: TeamMembersTableProps) {
|
||||
return (
|
||||
<Box border borderColor="outline-steel" bg="surface-charcoal/30">
|
||||
<Stack border borderColor="outline-steel" bg="surface-charcoal/30">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
@@ -38,16 +37,16 @@ export function TeamMembersTable({ members, isAdmin, onRemoveMember }: TeamMembe
|
||||
<TableRow key={member.driverId}>
|
||||
<TableCell>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box w="8" h="8" bg="base-black" border borderColor="outline-steel" display="flex" center>
|
||||
<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>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Text weight="bold" size="sm" color="text-white">{member.driverName}</Text>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box px={2} py={0.5} bg="base-black" border borderColor="outline-steel" display="inline-block">
|
||||
<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>
|
||||
</Box>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text size="xs" color="text-gray-500" font="mono">
|
||||
@@ -74,6 +73,6 @@ export function TeamMembersTable({ members, isAdmin, onRemoveMember }: TeamMembe
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
import { ChevronRight, Users } from 'lucide-react';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
@@ -44,7 +43,7 @@ export function TeamMembershipCard({
|
||||
>
|
||||
<Icon icon={Users} size={6} color="var(--neon-purple)" />
|
||||
</Surface>
|
||||
<Box style={{ flex: 1, minWidth: 0 }}>
|
||||
<Stack style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text weight="semibold" color="text-white" className="truncate group-hover:text-purple-400 transition-colors" block>
|
||||
{teamName}
|
||||
</Text>
|
||||
@@ -56,7 +55,7 @@ export function TeamMembershipCard({
|
||||
Since {new Date(joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Icon icon={ChevronRight} size={4} color="var(--text-gray-500)" className="group-hover:text-purple-400 transition-colors" />
|
||||
</Surface>
|
||||
</Link>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
|
||||
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Card , Card as Surface } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { ChevronRight, Shield, Users } from 'lucide-react';
|
||||
|
||||
@@ -25,15 +23,15 @@ interface TeamMembershipGridProps {
|
||||
export function TeamMembershipGrid({ memberships }: TeamMembershipGridProps) {
|
||||
return (
|
||||
<Card>
|
||||
<Box mb={4}>
|
||||
<Stack mb={4}>
|
||||
<Heading level={2} icon={<Shield style={{ width: '1.25rem', height: '1.25rem', color: '#a855f7' }} />}>
|
||||
Team Memberships
|
||||
<Text size="sm" color="text-gray-500" weight="normal" style={{ marginLeft: '0.5rem' }}>({memberships.length})</Text>
|
||||
</Heading>
|
||||
</Box>
|
||||
<Box style={{ display: 'grid', gridTemplateColumns: 'repeat(1, minmax(0, 1fr))', gap: '1rem' }}>
|
||||
</Stack>
|
||||
<Stack style={{ display: 'grid', gridTemplateColumns: 'repeat(1, minmax(0, 1fr))', gap: '1rem' }}>
|
||||
{memberships.map((membership) => (
|
||||
<Box key={membership.team.id}>
|
||||
<Stack key={membership.team.id}>
|
||||
<Link
|
||||
href={`/teams/${membership.team.id}`}
|
||||
variant="ghost"
|
||||
@@ -42,7 +40,7 @@ export function TeamMembershipGrid({ memberships }: TeamMembershipGridProps) {
|
||||
<Surface variant="muted" rounded="lg" padding={3} style={{ backgroundColor: 'rgba(147, 51, 234, 0.2)', border: '1px solid rgba(147, 51, 234, 0.3)' }}>
|
||||
<Users style={{ width: '1.5rem', height: '1.5rem', color: '#a855f7' }} />
|
||||
</Surface>
|
||||
<Box style={{ flex: 1, minWidth: 0 }}>
|
||||
<Stack style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text weight="semibold" color="text-white" block truncate>{membership.team.name}</Text>
|
||||
<Stack direction="row" align="center" gap={2} mt={1}>
|
||||
<Surface variant="muted" rounded="full" padding={1} style={{ paddingLeft: '0.5rem', paddingRight: '0.5rem', backgroundColor: 'rgba(147, 51, 234, 0.2)', color: '#a855f7' }}>
|
||||
@@ -50,13 +48,13 @@ export function TeamMembershipGrid({ memberships }: TeamMembershipGridProps) {
|
||||
</Surface>
|
||||
<Text size="xs" color="text-gray-500">Since {membership.joinedAt.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
<ChevronRight style={{ width: '1rem', height: '1rem', color: '#737373' }} />
|
||||
</Surface>
|
||||
</Link>
|
||||
</Box>
|
||||
</Stack>
|
||||
))}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { Trophy, Crown, Users } from 'lucide-react';
|
||||
import type { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel';
|
||||
import { getMediaUrl } from '@/lib/utilities/media';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
@@ -75,7 +74,7 @@ export function TeamPodium({ teams, onClick }: TeamPodiumProps) {
|
||||
p={0}
|
||||
transition
|
||||
>
|
||||
<Box
|
||||
<Stack
|
||||
bg={getBgColor(position)}
|
||||
rounded="xl"
|
||||
border={true}
|
||||
@@ -85,18 +84,18 @@ export function TeamPodium({ teams, onClick }: TeamPodiumProps) {
|
||||
>
|
||||
{/* Crown for 1st place */}
|
||||
{position === 1 && (
|
||||
<Box position="absolute" top="-4" left="1/2" translateX="-1/2">
|
||||
<Box position="relative">
|
||||
<Box animate="pulse">
|
||||
<Stack position="absolute" top="-4" left="1/2" translateX="-1/2">
|
||||
<Stack position="relative">
|
||||
<Stack animate="pulse">
|
||||
<Icon icon={Crown} size={8} color="text-warning-amber" />
|
||||
</Box>
|
||||
<Box position="absolute" inset="0" bg="bg-yellow-400" bgOpacity={0.3} blur="md" rounded="full" />
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack position="absolute" inset="0" bg="bg-yellow-400" bgOpacity={0.3} blur="md" rounded="full" />
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{/* Team logo */}
|
||||
<Box h="20" w="20" display="flex" center rounded="xl" bg="bg-deep-graphite" border={true} borderColor="border-charcoal-outline" overflow="hidden" mb={3}>
|
||||
<Stack h="20" w="20" display="flex" center rounded="xl" bg="bg-deep-graphite" border={true} borderColor="border-charcoal-outline" overflow="hidden" mb={3}>
|
||||
<Image
|
||||
src={team.logoUrl || getMediaUrl('team-logo', team.id)}
|
||||
alt={team.name}
|
||||
@@ -104,7 +103,7 @@ export function TeamPodium({ teams, onClick }: TeamPodiumProps) {
|
||||
height={80}
|
||||
objectFit="cover"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Team name */}
|
||||
<Text weight="bold" size="sm" color="text-white" align="center" block truncate maxWidth="28">
|
||||
@@ -134,7 +133,7 @@ export function TeamPodium({ teams, onClick }: TeamPodiumProps) {
|
||||
<Text size="xs" color="text-gray-400">{team.memberCount}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Users } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
@@ -43,9 +42,9 @@ export function TeamRankingsTable({ teams, sortBy, onTeamClick }: TeamRankingsTa
|
||||
<Text size="xs" weight="medium" color="text-gray-500" block>Team</Text>
|
||||
</TableHeader>
|
||||
<TableHeader>
|
||||
<Box display={{ base: 'none', lg: 'block' }}>
|
||||
<Stack display={{ base: 'none', lg: 'block' }}>
|
||||
<Text size="xs" weight="medium" color="text-gray-500" align="center" block>Members</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</TableHeader>
|
||||
<TableHeader>
|
||||
<Text size="xs" weight="medium" color="text-gray-500" align="center" block>Rating</Text>
|
||||
@@ -74,26 +73,26 @@ export function TeamRankingsTable({ teams, sortBy, onTeamClick }: TeamRankingsTa
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display={{ base: 'none', lg: 'flex' }} alignItems="center" justifyContent="center">
|
||||
<Stack display={{ base: 'none', lg: 'flex' }} alignItems="center" justifyContent="center">
|
||||
<Stack direction="row" align="center" gap={1.5}>
|
||||
<Icon icon={Users} size={3.5} color="text-gray-500" />
|
||||
<Text size="sm" color="text-gray-400">{team.memberCount}</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex" center>
|
||||
<Stack display="flex" center>
|
||||
<Text font="mono" weight="semibold" color={sortBy === 'rating' ? 'text-primary-blue' : 'text-white'}>
|
||||
0
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex" center>
|
||||
<Stack display="flex" center>
|
||||
<Text font="mono" weight="semibold" color={sortBy === 'wins' ? 'text-primary-blue' : 'text-white'}>
|
||||
{team.totalWins}
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
|
||||
@@ -4,7 +4,6 @@ import React, { useMemo, useState } from 'react';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { useTeamRoster } from "@/hooks/team/useTeamRoster";
|
||||
import type { DriverViewModel } from '@/lib/view-models/DriverViewModel';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
@@ -76,9 +75,9 @@ export function TeamRoster({
|
||||
if (loading) {
|
||||
return (
|
||||
<Card>
|
||||
<Box display="flex" justifyContent="center" py={8}>
|
||||
<Stack display="flex" justifyContent="center" py={8}>
|
||||
<Text color="text-gray-400">Loading roster...</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -86,17 +85,17 @@ export function TeamRoster({
|
||||
return (
|
||||
<Card>
|
||||
<Stack direction="row" align="center" justify="between" mb={6} wrap gap={4}>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Heading level={3}>Team Roster</Heading>
|
||||
<Text size="sm" color="text-gray-400" block mt={1}>
|
||||
{memberships.length} {memberships.length === 1 ? 'member' : 'members'} • Avg Rating:{' '}
|
||||
<Text color="text-primary-blue" weight="medium">{teamAverageRating.toFixed(0)}</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Text size="sm" color="text-gray-400">Sort by:</Text>
|
||||
<Box width="32">
|
||||
<Stack width="32">
|
||||
<Select
|
||||
value={sortBy}
|
||||
onChange={(e) => setSortBy(e.target.value as typeof sortBy)}
|
||||
@@ -106,7 +105,7 @@ export function TeamRoster({
|
||||
{ value: 'name', label: 'Name' },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -130,7 +129,7 @@ export function TeamRoster({
|
||||
overallRank={overallRank}
|
||||
actions={canManageMembership ? (
|
||||
<>
|
||||
<Box width="32">
|
||||
<Stack width="32">
|
||||
<Select
|
||||
value={displayRole}
|
||||
onChange={(e) =>
|
||||
@@ -141,7 +140,7 @@ export function TeamRoster({
|
||||
{ value: 'admin', label: 'Admin' },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Button
|
||||
variant="danger"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { DriverIdentity } from '@/components/drivers/DriverIdentity';
|
||||
@@ -25,7 +24,7 @@ export function TeamRosterItem({
|
||||
actions,
|
||||
}: TeamRosterItemProps) {
|
||||
return (
|
||||
<Box
|
||||
<Stack
|
||||
bg="bg-iron-gray/50"
|
||||
rounded="lg"
|
||||
border={true}
|
||||
@@ -47,17 +46,17 @@ export function TeamRosterItem({
|
||||
|
||||
{rating !== null && (
|
||||
<Stack direction="row" align="center" gap={6}>
|
||||
<Box display="flex" flexDirection="col" alignItems="center">
|
||||
<Stack display="flex" flexDirection="col" alignItems="center">
|
||||
<Text size="lg" weight="bold" color="text-primary-blue" block>
|
||||
{rating}
|
||||
</Text>
|
||||
<Text size="xs" color="text-gray-400">Rating</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
{overallRank !== null && (
|
||||
<Box display="flex" flexDirection="col" alignItems="center">
|
||||
<Stack display="flex" flexDirection="col" alignItems="center">
|
||||
<Text size="sm" color="text-gray-300" block>#{overallRank}</Text>
|
||||
<Text size="xs" color="text-gray-500">Rank</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
@@ -68,6 +67,6 @@ export function TeamRosterItem({
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Input } from '@/ui/Input';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Search } from 'lucide-react';
|
||||
|
||||
interface TeamSearchBarProps {
|
||||
@@ -13,9 +12,9 @@ interface TeamSearchBarProps {
|
||||
|
||||
export function TeamSearchBar({ searchQuery, onSearchChange }: TeamSearchBarProps) {
|
||||
return (
|
||||
<Box id="teams-list" mb={6}>
|
||||
<Stack id="teams-list" mb={6}>
|
||||
<Stack direction="row" gap={4} wrap>
|
||||
<Box flexGrow={1}>
|
||||
<Stack flexGrow={1}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search teams by name, description, region, or language..."
|
||||
@@ -23,8 +22,8 @@ export function TeamSearchBar({ searchQuery, onSearchChange }: TeamSearchBarProp
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
icon={<Icon icon={Search} size={5} color="var(--text-gray-500)" />}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
@@ -25,7 +24,7 @@ export function TeamsDirectoryHeader({ onCreateTeam }: TeamsDirectoryHeaderProps
|
||||
borderColor="outline-steel"
|
||||
pb={6}
|
||||
>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Heading level={1} weight="bold">Teams</Heading>
|
||||
<Text
|
||||
color="text-gray-500"
|
||||
@@ -37,8 +36,8 @@ export function TeamsDirectoryHeader({ onCreateTeam }: TeamsDirectoryHeaderProps
|
||||
>
|
||||
Operational Units & Racing Collectives
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={onCreateTeam}
|
||||
@@ -46,7 +45,7 @@ export function TeamsDirectoryHeader({ onCreateTeam }: TeamsDirectoryHeaderProps
|
||||
>
|
||||
Initialize Team
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { Trophy, Crown, Users } from 'lucide-react';
|
||||
import type { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel';
|
||||
import { getMediaUrl } from '@/lib/utilities/media';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
@@ -75,7 +74,7 @@ export function TopThreePodium({ teams, onClick }: TopThreePodiumProps) {
|
||||
p={0}
|
||||
transition
|
||||
>
|
||||
<Box
|
||||
<Stack
|
||||
bg={getBgColor(position)}
|
||||
rounded="xl"
|
||||
border
|
||||
@@ -85,18 +84,18 @@ export function TopThreePodium({ teams, onClick }: TopThreePodiumProps) {
|
||||
>
|
||||
{/* Crown for 1st place */}
|
||||
{position === 1 && (
|
||||
<Box position="absolute" top="-4" left="1/2" translateX="-1/2">
|
||||
<Box position="relative">
|
||||
<Box animate="pulse">
|
||||
<Stack position="absolute" top="-4" left="1/2" translateX="-1/2">
|
||||
<Stack position="relative">
|
||||
<Stack animate="pulse">
|
||||
<Icon icon={Crown} size={8} color="var(--warning-amber)" />
|
||||
</Box>
|
||||
<Box position="absolute" inset="0" bg="bg-yellow-400" bgOpacity={0.3} blur="md" rounded="full" />
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack position="absolute" inset="0" bg="bg-yellow-400" bgOpacity={0.3} blur="md" rounded="full" />
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{/* Team logo */}
|
||||
<Box h="20" w="20" display="flex" center rounded="xl" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" overflow="hidden" mb={3}>
|
||||
<Stack h="20" w="20" display="flex" center rounded="xl" bg="bg-deep-graphite" border borderColor="border-charcoal-outline" overflow="hidden" mb={3}>
|
||||
<Image
|
||||
src={team.logoUrl || getMediaUrl('team-logo', team.id)}
|
||||
alt={team.name}
|
||||
@@ -104,7 +103,7 @@ export function TopThreePodium({ teams, onClick }: TopThreePodiumProps) {
|
||||
height={80}
|
||||
objectFit="cover"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
{/* Team name */}
|
||||
<Text weight="bold" size="sm" color="text-white" textAlign="center" block truncate maxWidth="28">
|
||||
@@ -134,7 +133,7 @@ export function TopThreePodium({ teams, onClick }: TopThreePodiumProps) {
|
||||
<Text size="xs" color="text-gray-400">{team.memberCount}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user