website refactor
This commit is contained in:
@@ -5,6 +5,11 @@ import { useState, useRef, useEffect } from 'react';
|
||||
import type * as React from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
|
||||
// Minimum drivers for ranked leagues
|
||||
const MIN_RANKED_DRIVERS = 10;
|
||||
@@ -82,28 +87,55 @@ function InfoFlyout({ isOpen, onClose, title, children, anchorRef }: InfoFlyoutP
|
||||
if (!isOpen || !mounted) return null;
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
<Box
|
||||
ref={flyoutRef}
|
||||
className="fixed z-50 w-[340px] max-h-[80vh] overflow-y-auto bg-iron-gray border border-charcoal-outline rounded-xl shadow-2xl animate-fade-in"
|
||||
style={{ top: position.top, left: position.left }}
|
||||
position="fixed"
|
||||
zIndex={50}
|
||||
w="340px"
|
||||
bg="bg-iron-gray"
|
||||
border
|
||||
borderColor="border-charcoal-outline"
|
||||
rounded="xl"
|
||||
shadow="2xl"
|
||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||
style={{ top: position.top, left: position.left, maxHeight: '80vh', overflowY: 'auto' }}
|
||||
>
|
||||
<div className="flex items-center justify-between p-4 border-b border-charcoal-outline/50 sticky top-0 bg-iron-gray z-10">
|
||||
<div className="flex items-center gap-2">
|
||||
<HelpCircle className="w-4 h-4 text-primary-blue" />
|
||||
<span className="text-sm font-semibold text-white">{title}</span>
|
||||
</div>
|
||||
<button
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="between"
|
||||
p={4}
|
||||
borderBottom
|
||||
borderColor="border-charcoal-outline/50"
|
||||
position="sticky"
|
||||
top="0"
|
||||
bg="bg-iron-gray"
|
||||
zIndex={10}
|
||||
>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={HelpCircle} size={4} color="text-primary-blue" />
|
||||
<Text size="sm" weight="semibold" color="text-white">{title}</Text>
|
||||
</Stack>
|
||||
<Box
|
||||
as="button"
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="flex h-6 w-6 items-center justify-center rounded-md hover:bg-charcoal-outline transition-colors"
|
||||
display="flex"
|
||||
h="6"
|
||||
w="6"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
rounded="md"
|
||||
transition
|
||||
hoverBg="bg-charcoal-outline"
|
||||
>
|
||||
<X className="w-4 h-4 text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<Icon icon={X} size={4} color="text-gray-400" />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box p={4}>
|
||||
{children}
|
||||
</div>
|
||||
</div>,
|
||||
</Box>
|
||||
</Box>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
@@ -155,96 +187,139 @@ export function LeagueVisibilitySection({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<Stack gap={8}>
|
||||
{/* Emotional header for the step */}
|
||||
<div className="text-center pb-2">
|
||||
<h3 className="text-lg font-semibold text-white mb-2">
|
||||
Choose your league's destiny
|
||||
</h3>
|
||||
<p className="text-sm text-gray-400 max-w-lg mx-auto">
|
||||
<Box textAlign="center" pb={2}>
|
||||
<Heading level={3} mb={2}>
|
||||
Choose your league's destiny
|
||||
</Heading>
|
||||
<Text size="sm" color="text-gray-400" maxWidth="lg" mx="auto" block>
|
||||
Will you compete for glory on the global leaderboards, or race with friends in a private series?
|
||||
</p>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{/* League Type Selection */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<Box display="grid" responsiveGridCols={{ base: 1, md: 2 }} gap={6}>
|
||||
{/* Ranked (Public) Option */}
|
||||
<div className="relative">
|
||||
<button
|
||||
<Box position="relative">
|
||||
<Box
|
||||
as="button"
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
onClick={() => handleVisibilityChange('public')}
|
||||
className={`w-full group relative flex flex-col gap-4 p-6 text-left rounded-xl border-2 transition-all duration-200 ${
|
||||
isRanked
|
||||
? 'border-primary-blue bg-gradient-to-br from-primary-blue/15 to-primary-blue/5 shadow-[0_0_30px_rgba(25,140,255,0.25)]'
|
||||
: 'border-charcoal-outline bg-iron-gray/30 hover:border-gray-500 hover:bg-iron-gray/50'
|
||||
} ${disabled ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer'}`}
|
||||
display="flex"
|
||||
flexDirection="col"
|
||||
gap={4}
|
||||
p={6}
|
||||
textAlign="left"
|
||||
rounded="xl"
|
||||
border
|
||||
borderColor={isRanked ? 'border-primary-blue' : 'border-charcoal-outline'}
|
||||
bg={isRanked ? 'bg-primary-blue/15' : 'bg-iron-gray/30'}
|
||||
w="full"
|
||||
position="relative"
|
||||
transition
|
||||
shadow={isRanked ? '0_0_30px_rgba(25,140,255,0.25)' : undefined}
|
||||
hoverBorderColor={!isRanked && !disabled ? 'border-gray-500' : undefined}
|
||||
hoverBg={!isRanked && !disabled ? 'bg-iron-gray/50' : undefined}
|
||||
opacity={disabled ? 0.6 : 1}
|
||||
cursor={disabled ? 'not-allowed' : 'pointer'}
|
||||
group
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`flex h-14 w-14 items-center justify-center rounded-xl ${
|
||||
isRanked ? 'bg-primary-blue/30' : 'bg-charcoal-outline/50'
|
||||
}`}>
|
||||
<Trophy className={`w-7 h-7 ${isRanked ? 'text-primary-blue' : 'text-gray-400'}`} />
|
||||
</div>
|
||||
<div>
|
||||
<div className={`text-xl font-bold ${isRanked ? 'text-white' : 'text-gray-300'}`}>
|
||||
<Box display="flex" alignItems="start" justifyContent="between">
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box
|
||||
display="flex"
|
||||
h="14"
|
||||
w="14"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
rounded="xl"
|
||||
bg={isRanked ? 'bg-primary-blue/30' : 'bg-charcoal-outline/50'}
|
||||
>
|
||||
<Icon icon={Trophy} size={7} color={isRanked ? 'text-primary-blue' : 'text-gray-400'} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text weight="bold" size="xl" color={isRanked ? 'text-white' : 'text-gray-300'} block>
|
||||
Ranked
|
||||
</div>
|
||||
<div className={`text-sm ${isRanked ? 'text-primary-blue' : 'text-gray-500'}`}>
|
||||
</Text>
|
||||
<Text size="sm" color={isRanked ? 'text-primary-blue' : 'text-gray-500'} block>
|
||||
Compete for glory
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
{/* Radio indicator */}
|
||||
<div className={`flex h-7 w-7 items-center justify-center rounded-full border-2 shrink-0 transition-colors ${
|
||||
isRanked ? 'border-primary-blue bg-primary-blue' : 'border-gray-500'
|
||||
}`}>
|
||||
{isRanked && <Check className="w-4 h-4 text-white" />}
|
||||
</div>
|
||||
</div>
|
||||
<Box
|
||||
display="flex"
|
||||
h="7"
|
||||
w="7"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
rounded="full"
|
||||
border
|
||||
borderColor={isRanked ? 'border-primary-blue' : 'border-gray-500'}
|
||||
bg={isRanked ? 'bg-primary-blue' : ''}
|
||||
flexShrink={0}
|
||||
transition
|
||||
>
|
||||
{isRanked && <Icon icon={Check} size={4} color="text-white" />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Emotional tagline */}
|
||||
<p className={`text-sm ${isRanked ? 'text-gray-300' : 'text-gray-500'}`}>
|
||||
<Text size="sm" color={isRanked ? 'text-gray-300' : 'text-gray-500'} block>
|
||||
Your results matter. Build your reputation in the global standings and climb the ranks.
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
{/* Features */}
|
||||
<div className="space-y-2.5 py-2">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||
<Check className="w-4 h-4 text-performance-green" />
|
||||
<span>Discoverable by all drivers</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||
<Check className="w-4 h-4 text-performance-green" />
|
||||
<span>Affects driver ratings & rankings</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||
<Check className="w-4 h-4 text-performance-green" />
|
||||
<span>Featured on leaderboards</span>
|
||||
</div>
|
||||
</div>
|
||||
<Stack gap={2.5} py={2}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Check} size={4} color="text-performance-green" />
|
||||
<Text size="sm" color="text-gray-400">Discoverable by all drivers</Text>
|
||||
</Stack>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Check} size={4} color="text-performance-green" />
|
||||
<Text size="sm" color="text-gray-400">Affects driver ratings & rankings</Text>
|
||||
</Stack>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Check} size={4} color="text-performance-green" />
|
||||
<Text size="sm" color="text-gray-400">Featured on leaderboards</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{/* Requirement badge */}
|
||||
<div className="flex items-center gap-2 mt-auto px-3 py-2 rounded-lg bg-warning-amber/10 border border-warning-amber/20 w-fit">
|
||||
<Users className="w-4 h-4 text-warning-amber" />
|
||||
<span className="text-xs text-warning-amber font-medium">
|
||||
<Box display="flex" alignItems="center" gap={2} mt="auto" px={3} py={2} rounded="lg" bg="bg-warning-amber/10" border borderColor="border-warning-amber/20" w="fit">
|
||||
<Icon icon={Users} size={4} color="text-warning-amber" />
|
||||
<Text size="xs" color="text-warning-amber" weight="medium">
|
||||
Requires {MIN_RANKED_DRIVERS}+ drivers for competitive integrity
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Info button */}
|
||||
<button
|
||||
<Box
|
||||
as="button"
|
||||
ref={rankedInfoRef}
|
||||
type="button"
|
||||
onClick={() => setShowRankedFlyout(true)}
|
||||
className="absolute top-3 right-3 flex h-7 w-7 items-center justify-center rounded-full text-gray-500 hover:text-primary-blue hover:bg-primary-blue/10 transition-colors"
|
||||
position="absolute"
|
||||
top="3"
|
||||
right="3"
|
||||
display="flex"
|
||||
h="7"
|
||||
w="7"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
rounded="full"
|
||||
transition
|
||||
color="text-gray-500"
|
||||
hoverTextColor="text-primary-blue"
|
||||
hoverBg="bg-primary-blue/10"
|
||||
>
|
||||
<HelpCircle className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<Icon icon={HelpCircle} size={4} />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Ranked Info Flyout */}
|
||||
<InfoFlyout
|
||||
@@ -253,119 +328,176 @@ export function LeagueVisibilitySection({
|
||||
title="Ranked Leagues"
|
||||
anchorRef={rankedInfoRef}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-xs text-gray-400">
|
||||
<Stack gap={4}>
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
Ranked leagues are competitive series where results matter. Your performance
|
||||
affects your driver rating and contributes to global leaderboards.
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] text-gray-500 uppercase tracking-wide">Requirements</div>
|
||||
<ul className="space-y-1.5">
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Users className="w-3.5 h-3.5 text-warning-amber shrink-0 mt-0.5" />
|
||||
<span><strong className="text-white">Minimum {MIN_RANKED_DRIVERS} drivers</strong> for competitive integrity</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Check className="w-3.5 h-3.5 text-performance-green shrink-0 mt-0.5" />
|
||||
<span>Anyone can discover and join your league</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Stack gap={2}>
|
||||
<Text size="xs" weight="bold" color="text-gray-500" transform="uppercase"
|
||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||
className="tracking-wide"
|
||||
block
|
||||
>
|
||||
Requirements
|
||||
</Text>
|
||||
<Stack gap={1.5}>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Users} size={3.5} color="text-warning-amber" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">
|
||||
<Text weight="bold" color="text-white">Minimum {MIN_RANKED_DRIVERS} drivers</Text> for competitive integrity
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Check} size={3.5} color="text-performance-green" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Anyone can discover and join your league</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] text-gray-500 uppercase tracking-wide">Benefits</div>
|
||||
<ul className="space-y-1.5">
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Trophy className="w-3.5 h-3.5 text-primary-blue shrink-0 mt-0.5" />
|
||||
<span>Results affect driver ratings and rankings</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Check className="w-3.5 h-3.5 text-performance-green shrink-0 mt-0.5" />
|
||||
<span>Featured in league discovery</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Stack gap={2}>
|
||||
<Text size="xs" weight="bold" color="text-gray-500" transform="uppercase"
|
||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||
className="tracking-wide"
|
||||
block
|
||||
>
|
||||
Benefits
|
||||
</Text>
|
||||
<Stack gap={1.5}>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Trophy} size={3.5} color="text-primary-blue" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Results affect driver ratings and rankings</Text>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Check} size={3.5} color="text-performance-green" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Featured in league discovery</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</InfoFlyout>
|
||||
|
||||
{/* Unranked (Private) Option */}
|
||||
<div className="relative">
|
||||
<button
|
||||
<Box position="relative">
|
||||
<Box
|
||||
as="button"
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
onClick={() => handleVisibilityChange('private')}
|
||||
className={`w-full group relative flex flex-col gap-4 p-6 text-left rounded-xl border-2 transition-all duration-200 ${
|
||||
!isRanked
|
||||
? 'border-neon-aqua bg-gradient-to-br from-neon-aqua/15 to-neon-aqua/5 shadow-[0_0_30px_rgba(67,201,230,0.2)]'
|
||||
: 'border-charcoal-outline bg-iron-gray/30 hover:border-gray-500 hover:bg-iron-gray/50'
|
||||
} ${disabled ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer'}`}
|
||||
display="flex"
|
||||
flexDirection="col"
|
||||
gap={4}
|
||||
p={6}
|
||||
textAlign="left"
|
||||
rounded="xl"
|
||||
border
|
||||
borderColor={!isRanked ? 'border-neon-aqua' : 'border-charcoal-outline'}
|
||||
bg={!isRanked ? 'bg-neon-aqua/15' : 'bg-iron-gray/30'}
|
||||
w="full"
|
||||
position="relative"
|
||||
transition
|
||||
shadow={!isRanked ? '0_0_30px_rgba(67,201,230,0.2)' : undefined}
|
||||
hoverBorderColor={isRanked && !disabled ? 'border-gray-500' : undefined}
|
||||
hoverBg={isRanked && !disabled ? 'bg-iron-gray/50' : undefined}
|
||||
opacity={disabled ? 0.6 : 1}
|
||||
cursor={disabled ? 'not-allowed' : 'pointer'}
|
||||
group
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`flex h-14 w-14 items-center justify-center rounded-xl ${
|
||||
!isRanked ? 'bg-neon-aqua/30' : 'bg-charcoal-outline/50'
|
||||
}`}>
|
||||
<Users className={`w-7 h-7 ${!isRanked ? 'text-neon-aqua' : 'text-gray-400'}`} />
|
||||
</div>
|
||||
<div>
|
||||
<div className={`text-xl font-bold ${!isRanked ? 'text-white' : 'text-gray-300'}`}>
|
||||
<Box display="flex" alignItems="start" justifyContent="between">
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box
|
||||
display="flex"
|
||||
h="14"
|
||||
w="14"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
rounded="xl"
|
||||
bg={!isRanked ? 'bg-neon-aqua/30' : 'bg-charcoal-outline/50'}
|
||||
>
|
||||
<Icon icon={Users} size={7} color={!isRanked ? 'text-neon-aqua' : 'text-gray-400'} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text weight="bold" size="xl" color={!isRanked ? 'text-white' : 'text-gray-300'} block>
|
||||
Unranked
|
||||
</div>
|
||||
<div className={`text-sm ${!isRanked ? 'text-neon-aqua' : 'text-gray-500'}`}>
|
||||
</Text>
|
||||
<Text size="sm" color={!isRanked ? 'text-neon-aqua' : 'text-gray-500'} block>
|
||||
Race with friends
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
{/* Radio indicator */}
|
||||
<div className={`flex h-7 w-7 items-center justify-center rounded-full border-2 shrink-0 transition-colors ${
|
||||
!isRanked ? 'border-neon-aqua bg-neon-aqua' : 'border-gray-500'
|
||||
}`}>
|
||||
{!isRanked && <Check className="w-4 h-4 text-deep-graphite" />}
|
||||
</div>
|
||||
</div>
|
||||
<Box
|
||||
display="flex"
|
||||
h="7"
|
||||
w="7"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
rounded="full"
|
||||
border
|
||||
borderColor={!isRanked ? 'border-neon-aqua' : 'border-gray-500'}
|
||||
bg={!isRanked ? 'bg-neon-aqua' : ''}
|
||||
flexShrink={0}
|
||||
transition
|
||||
>
|
||||
{!isRanked && <Icon icon={Check} size={4} color="text-deep-graphite" />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Emotional tagline */}
|
||||
<p className={`text-sm ${!isRanked ? 'text-gray-300' : 'text-gray-500'}`}>
|
||||
<Text size="sm" color={!isRanked ? 'text-gray-300' : 'text-gray-500'} block>
|
||||
Pure racing fun. No pressure, no rankings — just you and your crew hitting the track.
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
{/* Features */}
|
||||
<div className="space-y-2.5 py-2">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||
<Check className={`w-4 h-4 ${!isRanked ? 'text-neon-aqua' : 'text-gray-500'}`} />
|
||||
<span>Private, invite-only access</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||
<Check className={`w-4 h-4 ${!isRanked ? 'text-neon-aqua' : 'text-gray-500'}`} />
|
||||
<span>Zero impact on your rating</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm text-gray-400">
|
||||
<Check className={`w-4 h-4 ${!isRanked ? 'text-neon-aqua' : 'text-gray-500'}`} />
|
||||
<span>Perfect for practice & fun</span>
|
||||
</div>
|
||||
</div>
|
||||
<Stack gap={2.5} py={2}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Check} size={4} color={!isRanked ? 'text-neon-aqua' : 'text-gray-400'} />
|
||||
<Text size="sm" color="text-gray-400">Private, invite-only access</Text>
|
||||
</Stack>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Check} size={4} color={!isRanked ? 'text-neon-aqua' : 'text-gray-400'} />
|
||||
<Text size="sm" color="text-gray-400">Zero impact on your rating</Text>
|
||||
</Stack>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Check} size={4} color={!isRanked ? 'text-neon-aqua' : 'text-gray-400'} />
|
||||
<Text size="sm" color="text-gray-400">Perfect for practice & fun</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{/* Flexibility badge */}
|
||||
<div className="flex items-center gap-2 mt-auto px-3 py-2 rounded-lg bg-neon-aqua/10 border border-neon-aqua/20 w-fit">
|
||||
<Users className={`w-4 h-4 ${!isRanked ? 'text-neon-aqua' : 'text-gray-400'}`} />
|
||||
<span className={`text-xs font-medium ${!isRanked ? 'text-neon-aqua' : 'text-gray-400'}`}>
|
||||
<Box display="flex" alignItems="center" gap={2} mt="auto" px={3} py={2} rounded="lg" bg="bg-neon-aqua/10" border borderColor="border-neon-aqua/20" w="fit">
|
||||
<Icon icon={Users} size={4} color={!isRanked ? 'text-neon-aqua' : 'text-gray-400'} />
|
||||
<Text size="xs" color={!isRanked ? 'text-neon-aqua' : 'text-gray-400'} weight="medium">
|
||||
Any size — even 2 friends
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Info button */}
|
||||
<button
|
||||
<Box
|
||||
as="button"
|
||||
ref={unrankedInfoRef}
|
||||
type="button"
|
||||
onClick={() => setShowUnrankedFlyout(true)}
|
||||
className="absolute top-3 right-3 flex h-7 w-7 items-center justify-center rounded-full text-gray-500 hover:text-neon-aqua hover:bg-neon-aqua/10 transition-colors"
|
||||
position="absolute"
|
||||
top="3"
|
||||
right="3"
|
||||
display="flex"
|
||||
h="7"
|
||||
w="7"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
rounded="full"
|
||||
transition
|
||||
color="text-gray-500"
|
||||
hoverTextColor="text-neon-aqua"
|
||||
hoverBg="bg-neon-aqua/10"
|
||||
>
|
||||
<HelpCircle className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<Icon icon={HelpCircle} size={4} />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Unranked Info Flyout */}
|
||||
<InfoFlyout
|
||||
@@ -374,88 +506,103 @@ export function LeagueVisibilitySection({
|
||||
title="Unranked Leagues"
|
||||
anchorRef={unrankedInfoRef}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-xs text-gray-400">
|
||||
<Stack gap={4}>
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
Unranked leagues are casual, private series for racing with friends.
|
||||
Results don't affect driver ratings, so you can practice and have fun
|
||||
Results don't affect driver ratings, so you can practice and have fun
|
||||
without pressure.
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] text-gray-500 uppercase tracking-wide">Perfect For</div>
|
||||
<ul className="space-y-1.5">
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Check className="w-3.5 h-3.5 text-neon-aqua shrink-0 mt-0.5" />
|
||||
<span>Private racing with friends</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Check className="w-3.5 h-3.5 text-neon-aqua shrink-0 mt-0.5" />
|
||||
<span>Practice and training sessions</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Check className="w-3.5 h-3.5 text-neon-aqua shrink-0 mt-0.5" />
|
||||
<span>Small groups (2+ drivers)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Stack gap={2}>
|
||||
<Text size="xs" weight="bold" color="text-gray-500" transform="uppercase"
|
||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||
className="tracking-wide"
|
||||
block
|
||||
>
|
||||
Perfect For
|
||||
</Text>
|
||||
<Stack gap={1.5}>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Check} size={3.5} color="text-neon-aqua" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Private racing with friends</Text>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Check} size={3.5} color="text-neon-aqua" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Practice and training sessions</Text>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Check} size={3.5} color="text-neon-aqua" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Small groups (2+ drivers)</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] text-gray-500 uppercase tracking-wide">Features</div>
|
||||
<ul className="space-y-1.5">
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Users className="w-3.5 h-3.5 text-neon-aqua shrink-0 mt-0.5" />
|
||||
<span>Invite-only membership</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-xs text-gray-400">
|
||||
<Check className="w-3.5 h-3.5 text-neon-aqua shrink-0 mt-0.5" />
|
||||
<span>Full stats and standings (internal only)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Stack gap={2}>
|
||||
<Text size="xs" weight="bold" color="text-gray-500" transform="uppercase"
|
||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||
className="tracking-wide"
|
||||
block
|
||||
>
|
||||
Features
|
||||
</Text>
|
||||
<Stack gap={1.5}>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Users} size={3.5} color="text-neon-aqua" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Invite-only membership</Text>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="start" gap={2}>
|
||||
<Icon icon={Check} size={3.5} color="text-neon-aqua" flexShrink={0} mt={0.5} />
|
||||
<Text size="xs" color="text-gray-400">Full stats and standings (internal only)</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</InfoFlyout>
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
{errors?.visibility && (
|
||||
<div className="flex items-center gap-2 p-3 rounded-lg bg-warning-amber/10 border border-warning-amber/20">
|
||||
<HelpCircle className="w-4 h-4 text-warning-amber shrink-0" />
|
||||
<p className="text-xs text-warning-amber">{errors.visibility}</p>
|
||||
</div>
|
||||
<Box display="flex" alignItems="center" gap={2} p={3} rounded="lg" bg="bg-warning-amber/10" border borderColor="border-warning-amber/20">
|
||||
<Icon icon={HelpCircle} size={4} color="text-warning-amber" flexShrink={0} />
|
||||
<Text size="xs" color="text-warning-amber">{errors.visibility}</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Contextual info based on selection */}
|
||||
<div className={`rounded-xl p-5 border transition-all duration-300 ${
|
||||
isRanked
|
||||
? 'bg-primary-blue/5 border-primary-blue/20'
|
||||
: 'bg-neon-aqua/5 border-neon-aqua/20'
|
||||
}`}>
|
||||
<div className="flex items-start gap-3">
|
||||
<Box
|
||||
rounded="xl"
|
||||
p={5}
|
||||
border
|
||||
transition
|
||||
bg={isRanked ? 'bg-primary-blue/5' : 'bg-neon-aqua/5'}
|
||||
borderColor={isRanked ? 'border-primary-blue/20' : 'border-neon-aqua/20'}
|
||||
>
|
||||
<Box display="flex" alignItems="start" gap={3}>
|
||||
{isRanked ? (
|
||||
<>
|
||||
<Trophy className="w-5 h-5 text-primary-blue shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white mb-1">Ready to compete</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
<Icon icon={Trophy} size={5} color="text-primary-blue" flexShrink={0} mt={0.5} />
|
||||
<Box>
|
||||
<Text size="sm" weight="medium" color="text-white" block mb={1}>Ready to compete</Text>
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
Your league will be visible to all GridPilot drivers. Results will affect driver ratings
|
||||
and contribute to the global leaderboards. Make sure you have at least {MIN_RANKED_DRIVERS} drivers
|
||||
to ensure competitive integrity.
|
||||
</p>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Users className="w-5 h-5 text-neon-aqua shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white mb-1">Private racing awaits</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
<Icon icon={Users} size={5} color="text-neon-aqua" flexShrink={0} mt={0.5} />
|
||||
<Box>
|
||||
<Text size="sm" weight="medium" color="text-white" block mb={1}>Private racing awaits</Text>
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
Your league will be invite-only. Perfect for racing with friends, practice sessions,
|
||||
or any time you want to have fun without affecting your official ratings.
|
||||
</p>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user