website refactor

This commit is contained in:
2026-01-18 23:24:30 +01:00
parent aeaa43f4d3
commit 182056a57b
487 changed files with 1783 additions and 2170 deletions
@@ -6,7 +6,7 @@ import { AdminUsersTemplate } from '@/templates/AdminUsersTemplate';
import { AdminUsersViewData } from '@/lib/view-data/AdminUsersViewData'; import { AdminUsersViewData } from '@/lib/view-data/AdminUsersViewData';
import { updateUserStatus, deleteUser } from '@/app/actions/adminActions'; import { updateUserStatus, deleteUser } from '@/app/actions/adminActions';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { ConfirmDialog } from '@/components/shared/ux/ConfirmDialog'; import { ConfirmDialog } from '@/ui/ConfirmDialog';
interface AdminUsersWrapperProps { interface AdminUsersWrapperProps {
initialViewData: AdminUsersViewData; initialViewData: AdminUsersViewData;
@@ -3,7 +3,7 @@
import type { DriversViewData } from '@/lib/types/view-data/DriversViewData'; import type { DriversViewData } from '@/lib/types/view-data/DriversViewData';
import { DriversTemplate } from '@/templates/DriversTemplate'; import { DriversTemplate } from '@/templates/DriversTemplate';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
@@ -4,7 +4,7 @@ import type { ProfileTab } from '@/components/profile/ProfileTabs';
import type { DriverProfileViewData } from '@/lib/types/view-data/DriverProfileViewData'; import type { DriverProfileViewData } from '@/lib/types/view-data/DriverProfileViewData';
import { DriverProfileTemplate } from '@/templates/DriverProfileTemplate'; import { DriverProfileTemplate } from '@/templates/DriverProfileTemplate';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
+15 -15
View File
@@ -4,26 +4,26 @@ import { LeagueCard } from '@/components/leagues/LeagueCardWrapper';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import type { LeaguesViewData } from '@/lib/view-data/LeaguesViewData'; import type { LeaguesViewData } from '@/lib/view-data/LeaguesViewData';
import { LeagueSummaryViewModel } from '@/lib/view-models/LeagueSummaryViewModel'; import { LeagueSummaryViewModel } from '@/lib/view-models/LeagueSummaryViewModel';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Box } from '@/ui/primitives/Box'; import { Stack } from '@/ui/Stack';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { import {
Award, Award,
Clock, Clock,
Flag, Flag,
Flame, Flame,
Globe, Globe,
Plus, Plus,
Search, Search,
Sparkles, Sparkles,
Target, Target,
Timer, Timer,
Trophy, Trophy,
Users, Users,
type LucideIcon, type LucideIcon,
} from 'lucide-react'; } from 'lucide-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import React, { useState } from 'react'; import React, { useState } from 'react';
@@ -1,26 +1,26 @@
'use client'; 'use client';
import { import {
createRaceAction, createRaceAction,
deleteRaceAction, deleteRaceAction,
publishScheduleAction, publishScheduleAction,
unpublishScheduleAction, unpublishScheduleAction,
updateRaceAction updateRaceAction
} from '@/app/actions/leagueScheduleActions'; } from '@/app/actions/leagueScheduleActions';
import { PageWrapper } from '@/components/shared/state/PageWrapper'; import { PageWrapper } from '@/ui/PageWrapper';
import { ConfirmDialog } from '@/components/shared/ux/ConfirmDialog'; import { ConfirmDialog } from '@/ui/ConfirmDialog';
import { import {
useLeagueAdminSchedule, useLeagueAdminSchedule,
useLeagueAdminStatus, useLeagueAdminStatus,
useLeagueSeasons useLeagueSeasons
} from "@/hooks/league/useLeagueScheduleAdminPageData"; } from "@/hooks/league/useLeagueScheduleAdminPageData";
import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId"; import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId";
import { RaceScheduleCommandModel } from '@/lib/command-models/leagues/RaceScheduleCommandModel'; import { RaceScheduleCommandModel } from '@/lib/command-models/leagues/RaceScheduleCommandModel';
import { LeagueAdminScheduleTemplate } from '@/templates/LeagueAdminScheduleTemplate'; import { LeagueAdminScheduleTemplate } from '@/templates/LeagueAdminScheduleTemplate';
import { Box } from '@/ui/Box';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Box } from '@/ui/primitives/Box'; import { Stack } from '@/ui/Stack';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { useParams, useRouter } from 'next/navigation'; import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -11,10 +11,10 @@ import type { StewardingViewData } from '@/lib/view-data/leagues/StewardingViewD
import { DriverViewModel } from '@/lib/view-models/DriverViewModel'; import { DriverViewModel } from '@/lib/view-models/DriverViewModel';
import { ProtestViewModel } from '@/lib/view-models/ProtestViewModel'; import { ProtestViewModel } from '@/lib/view-models/ProtestViewModel';
import { RaceViewModel } from '@/lib/view-models/RaceViewModel'; import { RaceViewModel } from '@/lib/view-models/RaceViewModel';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Box } from '@/ui/primitives/Box'; import { Stack } from '@/ui/Stack';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
@@ -7,44 +7,44 @@ import { PROTEST_SERVICE_TOKEN } from '@/lib/di/tokens';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { import {
AlertCircle, AlertCircle,
AlertTriangle, AlertTriangle,
ArrowLeft, ArrowLeft,
Calendar, Calendar,
CheckCircle, CheckCircle,
ChevronDown, ChevronDown,
Clock, Clock,
ExternalLink, ExternalLink,
Flag, Flag,
Gavel, Gavel,
Grid3x3, Grid3x3,
MapPin, MapPin,
MessageCircle, MessageCircle,
Send, Send,
Shield, Shield,
ShieldAlert, ShieldAlert,
TrendingDown, TrendingDown,
User, User,
Video, Video,
XCircle, XCircle,
type LucideIcon type LucideIcon
} from 'lucide-react'; } from 'lucide-react';
import { useParams, useRouter } from 'next/navigation'; import { useParams, useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
// Shared state components // Shared state components
import { LoadingWrapper } from '@/components/shared/state/LoadingWrapper'; import { LoadingWrapper } from '@/ui/LoadingWrapper';
import { StateContainer } from '@/components/shared/state/StateContainer'; import { StateContainer } from '@/ui/StateContainer';
import { useLeagueAdminStatus } from "@/hooks/league/useLeagueAdminStatus"; import { useLeagueAdminStatus } from "@/hooks/league/useLeagueAdminStatus";
import { useProtestDetail } from "@/hooks/league/useProtestDetail"; import { useProtestDetail } from "@/hooks/league/useProtestDetail";
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { GridItem } from '@/ui/primitives/GridItem'; import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon as UIIcon } from '@/ui/Icon'; import { Icon as UIIcon } from '@/ui/Icon';
import { Link as UILink } from '@/ui/Link'; import { Link as UILink } from '@/ui/Link';
import { Box } from '@/ui/primitives/Box'; import { Grid } from '@/ui/Grid';
import { Grid } from '@/ui/primitives/Grid'; import { GridItem } from '@/ui/GridItem';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
type PenaltyUiConfig = { type PenaltyUiConfig = {
@@ -2,15 +2,15 @@
import { WalletSummaryPanel } from '@/components/leagues/WalletSummaryPanel'; import { WalletSummaryPanel } from '@/components/leagues/WalletSummaryPanel';
import type { LeagueWalletViewData } from '@/lib/view-data/leagues/LeagueWalletViewData'; import type { LeagueWalletViewData } from '@/lib/view-data/leagues/LeagueWalletViewData';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon as UIIcon } from '@/ui/Icon'; import { Icon as UIIcon } from '@/ui/Icon';
import { Box } from '@/ui/primitives/Box'; import { Stack } from '@/ui/Stack';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { import {
Download Download
} from 'lucide-react'; } from 'lucide-react';
interface WalletTemplateProps { interface WalletTemplateProps {
@@ -2,28 +2,28 @@
import { useAuth } from '@/components/auth/AuthContext'; import { useAuth } from '@/components/auth/AuthContext';
import { LeagueReviewSummary } from '@/components/leagues/LeagueReviewSummary'; import { LeagueReviewSummary } from '@/components/leagues/LeagueReviewSummary';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Box } from '@/ui/primitives/Box'; import { Stack } from '@/ui/Stack';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { import {
AlertCircle, AlertCircle,
Award, Award,
Calendar, Calendar,
Check, Check,
CheckCircle2, CheckCircle2,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
FileText, FileText,
Loader2, Loader2,
Scale, Scale,
Sparkles, Sparkles,
Trophy, Trophy,
Users, Users,
} from 'lucide-react'; } from 'lucide-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { FormEvent, useCallback, useEffect, useState } from 'react'; import { FormEvent, useCallback, useEffect, useState } from 'react';
@@ -33,8 +33,8 @@ import { LeagueWizardCommandModel } from '@/lib/command-models/leagues/LeagueWiz
import { LeagueBasicsSection } from '@/components/leagues/LeagueBasicsSection'; import { LeagueBasicsSection } from '@/components/leagues/LeagueBasicsSection';
import { LeagueDropSection } from '@/components/leagues/LeagueDropSection'; import { LeagueDropSection } from '@/components/leagues/LeagueDropSection';
import { import {
ChampionshipsSection, ChampionshipsSection,
ScoringPatternSection ScoringPatternSection
} from '@/components/leagues/LeagueScoringSection'; } from '@/components/leagues/LeagueScoringSection';
import { LeagueStewardingSection } from '@/components/leagues/LeagueStewardingSection'; import { LeagueStewardingSection } from '@/components/leagues/LeagueStewardingSection';
import { LeagueStructureSection } from '@/components/leagues/LeagueStructureSection'; import { LeagueStructureSection } from '@/components/leagues/LeagueStructureSection';
+1 -1
View File
@@ -1,4 +1,4 @@
import { PageWrapper } from '@/components/shared/state/PageWrapper'; import { PageWrapper } from '@/ui/PageWrapper';
import { HomeTemplate, type HomeViewData } from '@/templates/HomeTemplate'; import { HomeTemplate, type HomeViewData } from '@/templates/HomeTemplate';
import { PageDataFetcher } from '@/lib/page/PageDataFetcher'; import { PageDataFetcher } from '@/lib/page/PageDataFetcher';
import { HomePageQuery } from '@/lib/page-queries/HomePageQuery'; import { HomePageQuery } from '@/lib/page-queries/HomePageQuery';
@@ -2,6 +2,7 @@
import { UploadDropzone } from '@/components/media/UploadDropzone'; import { UploadDropzone } from '@/components/media/UploadDropzone';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
@@ -9,7 +10,6 @@ import { Heading } from '@/ui/Heading';
import { MediaMetaPanel, mapMediaMetadata } from '@/ui/MediaMetaPanel'; import { MediaMetaPanel, mapMediaMetadata } from '@/ui/MediaMetaPanel';
import { MediaPreviewCard } from '@/ui/MediaPreviewCard'; import { MediaPreviewCard } from '@/ui/MediaPreviewCard';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Box } from '@/ui/primitives/Box';
import Link from 'next/link'; import Link from 'next/link';
import { useState } from 'react'; import { useState } from 'react';
@@ -1,11 +1,11 @@
'use client'; 'use client';
import { InlineNotice } from '@/components/shared/ux/InlineNotice'; import { InlineNotice } from '@/ui/InlineNotice';
import { ProgressLine } from '@/components/shared/ux/ProgressLine'; import { ProgressLine } from '@/ui/ProgressLine';
import type { Result } from '@/lib/contracts/Result'; import type { Result } from '@/lib/contracts/Result';
import type { ProfileViewData } from '@/lib/view-data/ProfileViewData'; import type { ProfileViewData } from '@/lib/view-data/ProfileViewData';
import { ProfileSettingsTemplate } from '@/templates/ProfileSettingsTemplate'; import { ProfileSettingsTemplate } from '@/templates/ProfileSettingsTemplate';
import { Box } from '@/ui/primitives/Box'; import { Box } from '@/ui/Box';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
@@ -1,11 +1,11 @@
'use client'; 'use client';
import { InlineNotice } from '@/components/shared/ux/InlineNotice'; import { InlineNotice } from '@/ui/InlineNotice';
import { ProgressLine } from '@/components/shared/ux/ProgressLine'; import { ProgressLine } from '@/ui/ProgressLine';
import type { Result } from '@/lib/contracts/Result'; import type { Result } from '@/lib/contracts/Result';
import type { SponsorshipRequestsViewData } from '@/lib/view-data/SponsorshipRequestsViewData'; import type { SponsorshipRequestsViewData } from '@/lib/view-data/SponsorshipRequestsViewData';
import { SponsorshipRequestsTemplate } from '@/templates/SponsorshipRequestsTemplate'; import { SponsorshipRequestsTemplate } from '@/templates/SponsorshipRequestsTemplate';
import { Box } from '@/ui/primitives/Box'; import { Box } from '@/ui/Box';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
+1 -1
View File
@@ -1,5 +1,5 @@
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { PageWrapper } from '@/components/shared/state/PageWrapper'; import { PageWrapper } from '@/ui/PageWrapper';
import { RaceDetailPageQuery } from '@/lib/page-queries/races/RaceDetailPageQuery'; import { RaceDetailPageQuery } from '@/lib/page-queries/races/RaceDetailPageQuery';
import { RaceDetailPageClient } from './RaceDetailPageClient'; import { RaceDetailPageClient } from './RaceDetailPageClient';
+1 -1
View File
@@ -1,5 +1,5 @@
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { PageWrapper } from '@/components/shared/state/PageWrapper'; import { PageWrapper } from '@/ui/PageWrapper';
import { RaceResultsPageQuery } from '@/lib/page-queries/races/RaceResultsPageQuery'; import { RaceResultsPageQuery } from '@/lib/page-queries/races/RaceResultsPageQuery';
import { RaceResultsPageClient } from './RaceResultsPageClient'; import { RaceResultsPageClient } from './RaceResultsPageClient';
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { PageWrapper } from '@/components/shared/state/PageWrapper'; import { PageWrapper } from '@/ui/PageWrapper';
import { RaceStewardingTemplate, type StewardingTab } from '@/templates/RaceStewardingTemplate'; import { RaceStewardingTemplate, type StewardingTab } from '@/templates/RaceStewardingTemplate';
import { RaceStewardingPageQuery } from '@/lib/page-queries/races/RaceStewardingPageQuery'; import { RaceStewardingPageQuery } from '@/lib/page-queries/races/RaceStewardingPageQuery';
import { type RaceStewardingViewData } from '@/lib/view-data/races/RaceStewardingViewData'; import { type RaceStewardingViewData } from '@/lib/view-data/races/RaceStewardingViewData';
@@ -2,7 +2,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { StatefulPageWrapper } from '@/components/shared/state/StatefulPageWrapper'; import { StatefulPageWrapper } from '@/ui/StatefulPageWrapper';
import { RacesAllTemplate } from '@/templates/RacesAllTemplate'; import { RacesAllTemplate } from '@/templates/RacesAllTemplate';
import { useAllRacesPageData } from '@/hooks/race/useAllRacesPageData'; import { useAllRacesPageData } from '@/hooks/race/useAllRacesPageData';
import { type RacesViewData, type RaceViewData } from '@/lib/view-data/RacesViewData'; import { type RacesViewData, type RaceViewData } from '@/lib/view-data/RacesViewData';
+3 -8
View File
@@ -2,10 +2,10 @@
import { useSponsorBilling } from "@/hooks/sponsor/useSponsorBilling"; import { useSponsorBilling } from "@/hooks/sponsor/useSponsorBilling";
import { SponsorBillingTemplate } from "@/templates/SponsorBillingTemplate"; import { SponsorBillingTemplate } from "@/templates/SponsorBillingTemplate";
import { Box } from "@/ui/primitives/Box"; import { Box } from "@/ui/Box";
import { Text } from "@/ui/Text";
import { Button } from "@/ui/Button"; import { Button } from "@/ui/Button";
import { DollarSign, AlertTriangle, Calendar, TrendingUp } from "lucide-react"; import { Text } from "@/ui/Text";
import { AlertTriangle, Calendar, DollarSign, TrendingUp } from "lucide-react";
export default function SponsorBillingPage() { export default function SponsorBillingPage() {
const { data: billingData, isLoading, error, retry } = useSponsorBilling('demo-sponsor-1'); const { data: billingData, isLoading, error, retry } = useSponsorBilling('demo-sponsor-1');
@@ -40,11 +40,6 @@ export default function SponsorBillingPage() {
console.log('Setting default payment method:', methodId); console.log('Setting default payment method:', methodId);
}; };
const handleRemoveMethod = (methodId: string) => {
if (window.confirm('Remove this payment method?')) {
console.log('Removing payment method:', methodId);
}
};
const handleDownloadInvoice = (invoiceId: string) => { const handleDownloadInvoice = (invoiceId: string) => {
console.log('Downloading invoice:', invoiceId); console.log('Downloading invoice:', invoiceId);
+3 -3
View File
@@ -1,11 +1,11 @@
'use client'; 'use client';
import { useState } from 'react';
import { useSponsorSponsorships } from "@/hooks/sponsor/useSponsorSponsorships"; import { useSponsorSponsorships } from "@/hooks/sponsor/useSponsorSponsorships";
import { SponsorCampaignsTemplate, SponsorshipType } from "@/templates/SponsorCampaignsTemplate"; import { SponsorCampaignsTemplate, SponsorshipType } from "@/templates/SponsorCampaignsTemplate";
import { Box } from "@/ui/primitives/Box"; import { Box } from "@/ui/Box";
import { Text } from "@/ui/Text";
import { Button } from "@/ui/Button"; import { Button } from "@/ui/Button";
import { Text } from "@/ui/Text";
import { useState } from 'react';
export default function SponsorCampaignsPage() { export default function SponsorCampaignsPage() {
const [typeFilter, setTypeFilter] = useState<SponsorshipType>('all'); const [typeFilter, setTypeFilter] = useState<SponsorshipType>('all');
+1 -1
View File
@@ -1,5 +1,5 @@
import { createRouteGuard } from '@/lib/auth/createRouteGuard'; import { createRouteGuard } from '@/lib/auth/createRouteGuard';
import { Box } from '@/ui/primitives/Box'; import { Box } from '@/ui/Box';
import { headers } from 'next/headers'; import { headers } from 'next/headers';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
@@ -1,5 +1,5 @@
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { PageWrapper } from '@/components/shared/state/PageWrapper'; import { PageWrapper } from '@/ui/PageWrapper';
import { SponsorLeagueDetailPageClient } from './SponsorLeagueDetailPageClient'; import { SponsorLeagueDetailPageClient } from './SponsorLeagueDetailPageClient';
import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient'; import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter'; import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
+1 -1
View File
@@ -1,4 +1,4 @@
import { PageWrapper } from '@/components/shared/state/PageWrapper'; import { PageWrapper } from '@/ui/PageWrapper';
import { SponsorLeaguesPageClient } from './SponsorLeaguesPageClient'; import { SponsorLeaguesPageClient } from './SponsorLeaguesPageClient';
import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient'; import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter'; import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
+1 -1
View File
@@ -3,7 +3,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { SponsorSettingsTemplate } from '@/templates/SponsorSettingsTemplate'; import { SponsorSettingsTemplate } from '@/templates/SponsorSettingsTemplate';
import { logoutAction } from '@/app/actions/logoutAction'; import { logoutAction } from '@/app/actions/logoutAction';
import { ConfirmDialog } from '@/components/shared/ux/ConfirmDialog'; import { ConfirmDialog } from '@/ui/ConfirmDialog';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { logger } from '@/lib/infrastructure/logging/logger'; import { logger } from '@/lib/infrastructure/logging/logger';
+18 -18
View File
@@ -5,31 +5,31 @@ import { SponsorHero } from '@/components/sponsors/SponsorHero';
import { SponsorWorkflowMockup } from '@/components/sponsors/SponsorWorkflowMockup'; import { SponsorWorkflowMockup } from '@/components/sponsors/SponsorWorkflowMockup';
import { SponsorSignupCommandModel } from '@/lib/command-models/sponsors/SponsorSignupCommandModel'; import { SponsorSignupCommandModel } from '@/lib/command-models/sponsors/SponsorSignupCommandModel';
import { siteConfig } from '@/lib/siteConfig'; import { siteConfig } from '@/lib/siteConfig';
import { Box } from '@/ui/Box';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Box } from '@/ui/primitives/Box'; import { Stack } from '@/ui/Stack';
import { Stack } from '@/ui/primitives/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { motion, useReducedMotion } from 'framer-motion'; import { motion, useReducedMotion } from 'framer-motion';
import { import {
ArrowRight, ArrowRight,
BarChart3, BarChart3,
Building2, Building2,
Car, Car,
CheckCircle2, CheckCircle2,
Eye, Eye,
Flag, Flag,
Globe, Globe,
Mail, Mail,
Megaphone, Megaphone,
Shield, Shield,
Target, Target,
TrendingUp, TrendingUp,
Trophy, Trophy,
Upload, Upload,
Users Users
} from 'lucide-react'; } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
+1 -1
View File
@@ -4,7 +4,7 @@ import { CreateTeamForm } from '@/components/teams/CreateTeamForm';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Section } from '@/ui/Section'; import { Section } from '@/ui/Section';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@@ -2,7 +2,7 @@ import { Badge } from '@/ui/Badge';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Hash, LucideIcon, Percent, Search, Star, Trophy } from 'lucide-react'; import { Hash, LucideIcon, Percent, Search, Star, Trophy } from 'lucide-react';
@@ -4,7 +4,7 @@ import { Input } from '@/ui/Input';
import { ControlBar } from '@/ui/ControlBar'; import { ControlBar } from '@/ui/ControlBar';
import { Select } from '@/ui/Select'; import { Select } from '@/ui/Select';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Group } from '@/ui/Group'; import { ButtonGroup } from '@/ui/ButtonGroup';
import { useState } from 'react'; import { useState } from 'react';
export function ActionFiltersBar() { export function ActionFiltersBar() {
@@ -12,39 +12,41 @@ export function ActionFiltersBar() {
return ( return (
<ControlBar <ControlBar
actions={ leftContent={
<Input <Input
placeholder="SEARCH_ID..." placeholder="SEARCH_ID..."
/> />
} }
> >
<Group direction="row" align="center" gap={2}> <ButtonGroup gap={4}>
<Text size="xs" variant="low" weight="bold" uppercase>Filter:</Text> <ButtonGroup gap={2}>
<Select <Text size="xs" variant="low" weight="bold" uppercase>Filter:</Text>
options={[ <Select
{ label: 'All Types', value: 'all' }, options={[
{ label: 'User Update', value: 'user' }, { label: 'All Types', value: 'all' },
{ label: 'Onboarding', value: 'onboarding' } { label: 'User Update', value: 'user' },
]} { label: 'Onboarding', value: 'onboarding' }
value={filter} ]}
onChange={(e) => setFilter(e.target.value)} value={filter}
fullWidth={false} onChange={(e) => setFilter(e.target.value)}
/> fullWidth={false}
</Group> />
<Group direction="row" align="center" gap={2}> </ButtonGroup>
<Text size="xs" variant="low" weight="bold" uppercase>Status:</Text> <ButtonGroup gap={2}>
<Select <Text size="xs" variant="low" weight="bold" uppercase>Status:</Text>
options={[ <Select
{ label: 'All Status', value: 'all' }, options={[
{ label: 'Completed', value: 'completed' }, { label: 'All Status', value: 'all' },
{ label: 'Pending', value: 'pending' }, { label: 'Completed', value: 'completed' },
{ label: 'Failed', value: 'failed' } { label: 'Pending', value: 'pending' },
]} { label: 'Failed', value: 'failed' }
value="all" ]}
onChange={() => {}} value="all"
fullWidth={false} onChange={() => {}}
/> fullWidth={false}
</Group> />
</ButtonGroup>
</ButtonGroup>
</ControlBar> </ControlBar>
); );
} }
@@ -3,6 +3,7 @@
import { Header } from '@/ui/Header'; import { Header } from '@/ui/Header';
import { StatusIndicator } from '@/ui/StatusIndicator'; import { StatusIndicator } from '@/ui/StatusIndicator';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { ButtonGroup } from '@/ui/ButtonGroup';
import { Activity } from 'lucide-react'; import { Activity } from 'lucide-react';
import React from 'react'; import React from 'react';
@@ -15,12 +16,11 @@ export function ActionsHeader({ title }: ActionsHeaderProps) {
<Header <Header
actions={<StatusIndicator icon={Activity} variant="info" label="SYSTEM_READY" />} actions={<StatusIndicator icon={Activity} variant="info" label="SYSTEM_READY" />}
> >
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}> <ButtonGroup gap={4}>
<div style={{ width: '0.5rem', height: '1.5rem', backgroundColor: 'var(--ui-color-intent-primary)', borderRadius: 'var(--ui-radius-sm)' }} />
<Text as="h1" size="xl" weight="medium" variant="high" uppercase> <Text as="h1" size="xl" weight="medium" variant="high" uppercase>
{title} {title}
</Text> </Text>
</div> </ButtonGroup>
</Header> </Header>
); );
} }
@@ -1,6 +1,6 @@
'use client'; 'use client';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import React from 'react'; import React from 'react';
interface AuthFooterLinksProps { interface AuthFooterLinksProps {
+1 -1
View File
@@ -1,6 +1,6 @@
'use client'; 'use client';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import React from 'react'; import React from 'react';
interface AuthFormProps { interface AuthFormProps {
+6 -12
View File
@@ -1,7 +1,7 @@
import { LoadingSpinner } from '../../ui/LoadingSpinner'; import { LoadingSpinner } from '../../ui/LoadingSpinner';
import { Box } from '../../ui/primitives/Box'; import { Stack } from '../../ui/Stack';
import { Stack } from '../../ui/primitives/Stack';
import { Text } from '../../ui/Text'; import { Text } from '../../ui/Text';
import { Section } from '../../ui/Section';
interface AuthLoadingProps { interface AuthLoadingProps {
message?: string; message?: string;
@@ -9,19 +9,13 @@ interface AuthLoadingProps {
export function AuthLoading({ message = 'Authenticating...' }: AuthLoadingProps) { export function AuthLoading({ message = 'Authenticating...' }: AuthLoadingProps) {
return ( return (
<Box <Section variant="dark" padding="none">
fullWidth <Stack align="center" justify="center" gap={4} fullWidth>
minHeight="100vh"
display="flex"
center
bg="bg-[#0f1115]"
>
<Stack align="center" gap={4}>
<LoadingSpinner size={10} /> <LoadingSpinner size={10} />
<Text color="text-gray-400" weight="medium"> <Text variant="low" weight="medium">
{message} {message}
</Text> </Text>
</Stack> </Stack>
</Box> </Section>
); );
} }
@@ -1,11 +1,12 @@
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Card } from '@/ui/Card';
interface KpiItem { interface KpiItem {
label: string; label: string;
value: string | number; value: string | number;
color?: string; intent?: 'primary' | 'success' | 'warning' | 'critical' | 'high' | 'med' | 'low';
} }
interface DashboardKpiRowProps { interface DashboardKpiRowProps {
@@ -16,32 +17,31 @@ interface DashboardKpiRowProps {
* DashboardKpiRow * DashboardKpiRow
* *
* A horizontal row of key performance indicators with telemetry styling. * A horizontal row of key performance indicators with telemetry styling.
* Uses UI primitives to comply with architectural constraints.
*/ */
export function DashboardKpiRow({ items }: DashboardKpiRowProps) { export function DashboardKpiRow({ items }: DashboardKpiRowProps) {
return ( return (
<Grid responsiveGridCols={{ base: 2, md: 3, lg: 6 }} gap={4}> <Grid cols={{ base: 2, md: 3, lg: 6 }} gap={4}>
{items.map((item, index) => ( {items.map((item, index) => (
<Stack key={index} borderLeft pl={4} borderColor="var(--color-outline)"> <Card key={index} variant="dark">
<Text <Stack gap={1}>
size="xs" <Text
weight="bold" size="xs"
uppercase weight="bold"
letterSpacing="tighter" uppercase
color="var(--color-text-low)" variant="low"
block >
> {item.label}
{item.label} </Text>
</Text> <Text
<Text size="xl"
size="xl" font="mono"
font="mono" weight="bold"
weight="bold" variant={item.intent || 'high'}
color={item.color || 'var(--color-text-high)'} >
> {item.value}
{item.value} </Text>
</Text> </Stack>
</Stack> </Card>
))} ))}
</Grid> </Grid>
); );
@@ -1,4 +1,4 @@
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import React from 'react'; import React from 'react';
interface DashboardRailProps { interface DashboardRailProps {
@@ -13,7 +13,7 @@ interface DashboardRailProps {
*/ */
export function DashboardRail({ children }: DashboardRailProps) { export function DashboardRail({ children }: DashboardRailProps) {
return ( return (
<Stack as="nav" h="full" align="center" py={4} gap={4}> <Stack align="center" gap={4} fullWidth>
{children} {children}
</Stack> </Stack>
); );
@@ -1,7 +1,7 @@
import { Sidebar } from '@/ui/Sidebar'; import { Box } from '@/ui/Box';
import { Header } from '@/ui/Header'; import { Header } from '@/ui/Header';
import { MainContent } from '@/ui/MainContent'; import { MainContent } from '@/ui/MainContent';
import { Box } from '@/ui/primitives/Box'; import { Sidebar } from '@/ui/Sidebar';
import React from 'react'; import React from 'react';
interface DashboardShellProps { interface DashboardShellProps {
+29 -27
View File
@@ -8,6 +8,8 @@ import { Text } from '@/ui/Text';
import { FloatingAction } from '@/ui/FloatingAction'; import { FloatingAction } from '@/ui/FloatingAction';
import { DebugPanel } from '@/ui/DebugPanel'; import { DebugPanel } from '@/ui/DebugPanel';
import { StatGrid } from '@/ui/StatGrid'; import { StatGrid } from '@/ui/StatGrid';
import { Stack } from '@/ui/Stack';
import { ButtonGroup } from '@/ui/ButtonGroup';
import { Bug, Shield } from 'lucide-react'; import { Bug, Shield } from 'lucide-react';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
@@ -207,13 +209,13 @@ export function DebugModeToggle({ show }: DebugModeToggleProps) {
onClose={() => setIsOpen(false)} onClose={() => setIsOpen(false)}
icon={<Icon icon={Bug} size={4} intent="success" />} icon={<Icon icon={Bug} size={4} intent="success" />}
> >
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}> <Stack gap={4}>
{/* Debug Toggle */} {/* Debug Toggle */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0.5rem', backgroundColor: 'var(--ui-color-bg-surface-muted)', borderRadius: '0.5rem' }}> <Stack align="center" justify="between">
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> <ButtonGroup gap={2}>
<Icon icon={Shield} size={4} intent={debugEnabled ? 'success' : 'low'} /> <Icon icon={Shield} size={4} intent={debugEnabled ? 'success' : 'low'} />
<Text size="sm" weight="medium">Debug Mode</Text> <Text size="sm" weight="medium">Debug Mode</Text>
</div> </ButtonGroup>
<Button <Button
onClick={toggleDebug} onClick={toggleDebug}
size="sm" size="sm"
@@ -221,7 +223,7 @@ export function DebugModeToggle({ show }: DebugModeToggleProps) {
> >
{debugEnabled ? 'ON' : 'OFF'} {debugEnabled ? 'ON' : 'OFF'}
</Button> </Button>
</div> </Stack>
{/* Metrics */} {/* Metrics */}
{debugEnabled && ( {debugEnabled && (
@@ -238,41 +240,41 @@ export function DebugModeToggle({ show }: DebugModeToggleProps) {
{/* Actions */} {/* Actions */}
{debugEnabled && ( {debugEnabled && (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}> <Stack gap={2}>
<Text size="xs" weight="semibold" variant="low" uppercase>Test Actions</Text> <Text size="xs" weight="semibold" variant="low" uppercase>Test Actions</Text>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.5rem' }}> <ButtonGroup gap={2}>
<Button onClick={triggerTestError} variant="danger" size="sm">Test Error</Button> <Button onClick={triggerTestError} variant="danger" size="sm" fullWidth>Test Error</Button>
<Button onClick={triggerTestApiCall} size="sm">Test API</Button> <Button onClick={triggerTestApiCall} size="sm" fullWidth>Test API</Button>
</div> </ButtonGroup>
<Text size="xs" weight="semibold" variant="low" uppercase style={{ marginTop: '0.5rem' }}>Utilities</Text> <Text size="xs" weight="semibold" variant="low" uppercase>Utilities</Text>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.5rem' }}> <ButtonGroup gap={2}>
<Button onClick={copyDebugInfo} variant="secondary" size="sm">Copy Info</Button> <Button onClick={copyDebugInfo} variant="secondary" size="sm" fullWidth>Copy Info</Button>
<Button onClick={clearAllLogs} variant="secondary" size="sm">Clear Logs</Button> <Button onClick={clearAllLogs} variant="secondary" size="sm" fullWidth>Clear Logs</Button>
</div> </ButtonGroup>
</div> </Stack>
)} )}
{/* Quick Links */} {/* Quick Links */}
{debugEnabled && ( {debugEnabled && (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem' }}> <Stack gap={1}>
<Text size="xs" weight="semibold" variant="low" uppercase>Quick Access</Text> <Text size="xs" weight="semibold" variant="low" uppercase>Quick Access</Text>
<div style={{ fontSize: '10px', color: 'var(--ui-color-text-low)', fontFamily: 'var(--ui-font-mono)' }}> <Stack gap={0.5}>
<div>• window.__GRIDPILOT_GLOBAL_HANDLER__</div> <Text size="xs" variant="low" font="mono">• window.__GRIDPILOT_GLOBAL_HANDLER__</Text>
<div>• window.__GRIDPILOT_API_LOGGER__</div> <Text size="xs" variant="low" font="mono">• window.__GRIDPILOT_API_LOGGER__</Text>
<div>• window.__GRIDPILOT_REACT_ERRORS__</div> <Text size="xs" variant="low" font="mono">• window.__GRIDPILOT_REACT_ERRORS__</Text>
</div> </Stack>
</div> </Stack>
)} )}
{/* Status */} {/* Status */}
<div style={{ paddingTop: '0.5rem', borderTop: '1px solid var(--ui-color-border-muted)', textAlign: 'center' }}> <Stack align="center">
<Text size="xs" variant="low" style={{ fontSize: '10px' }}> <Text size="xs" variant="low">
{debugEnabled ? 'Debug features active' : 'Debug mode disabled'} {debugEnabled ? 'Debug features active' : 'Debug mode disabled'}
{isDev && ' • Development Environment'} {isDev && ' • Development Environment'}
</Text> </Text>
</div> </Stack>
</div> </Stack>
</DebugPanel> </DebugPanel>
)} )}
</React.Fragment> </React.Fragment>
+19 -34
View File
@@ -4,7 +4,7 @@ import { useNotifications } from '@/components/notifications/NotificationProvide
import type { NotificationVariant } from '@/components/notifications/notificationTypes'; import type { NotificationVariant } from '@/components/notifications/notificationTypes';
import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId"; import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId";
import { ApiConnectionMonitor } from '@/lib/api/base/ApiConnectionMonitor'; import { ApiConnectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler'; import { CircuitBreakerRegistry } from '@/lib/api/base/CircuitBreakerRegistry';
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler'; import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
import { Activity, AlertTriangle, ChevronDown, ChevronUp, MessageSquare, Wrench, X } from 'lucide-react'; import { Activity, AlertTriangle, ChevronDown, ChevronUp, MessageSquare, Wrench, X } from 'lucide-react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@@ -16,7 +16,7 @@ import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { IconButton } from '@/ui/IconButton'; import { IconButton } from '@/ui/IconButton';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { APIStatusSection } from './sections/APIStatusSection'; import { APIStatusSection } from './sections/APIStatusSection';
import { NotificationSendSection } from './sections/NotificationSendSection'; import { NotificationSendSection } from './sections/NotificationSendSection';
import { NotificationTypeSection } from './sections/NotificationTypeSection'; import { NotificationTypeSection } from './sections/NotificationTypeSection';
@@ -229,7 +229,7 @@ export function DevToolbar() {
if (isMinimized) { if (isMinimized) {
return ( return (
<Stack position="fixed" bottom="4" right="4" zIndex={50}> <Stack align="end" justify="end">
<IconButton <IconButton
icon={Wrench} icon={Wrench}
onClick={() => setIsMinimized(false)} onClick={() => setIsMinimized(false)}
@@ -242,25 +242,13 @@ export function DevToolbar() {
} }
return ( return (
<Stack <Stack gap={4}>
position="fixed"
bottom="4"
right="4"
zIndex={50}
w="80"
bg="bg-deep-graphite"
border
borderColor="border-charcoal-outline"
rounded="xl"
shadow="2xl"
overflow="hidden"
>
{/* Header */} {/* Header */}
<Stack direction="row" align="center" justify="between" px={4} py={3} bg="bg-iron-gray/50" borderBottom borderColor="border-charcoal-outline"> <Stack direction="row" align="center" justify="between" gap={4}>
<Stack direction="row" align="center" gap={2}> <Stack direction="row" align="center" gap={2}>
<Icon icon={Wrench} size={4} color="rgb(59, 130, 246)" /> <Icon icon={Wrench} size={4} intent="primary" />
<Text size="sm" weight="semibold" color="text-white">Dev Toolbar</Text> <Text size="sm" weight="semibold" variant="high">Dev Toolbar</Text>
<Badge variant="primary" size="xs"> <Badge variant="primary" size="sm">
DEMO DEMO
</Badge> </Badge>
</Stack> </Stack>
@@ -282,12 +270,11 @@ export function DevToolbar() {
{/* Content */} {/* Content */}
{isExpanded && ( {isExpanded && (
<Stack p={4}> <Stack gap={4}>
<Stack gap={3}> <Stack gap={3}>
{/* Notification Section - Accordion */} {/* Notification Section - Accordion */}
<Accordion <Accordion
title="Notifications" title="Notifications"
icon={<Icon icon={MessageSquare} size={4} color="rgb(156, 163, 175)" />}
isOpen={openAccordion === 'notifications'} isOpen={openAccordion === 'notifications'}
onToggle={() => setOpenAccordion(openAccordion === 'notifications' ? null : 'notifications')} onToggle={() => setOpenAccordion(openAccordion === 'notifications' ? null : 'notifications')}
> >
@@ -313,7 +300,6 @@ export function DevToolbar() {
{/* API Status Section - Accordion */} {/* API Status Section - Accordion */}
<Accordion <Accordion
title="API Status" title="API Status"
icon={<Icon icon={Activity} size={4} color="rgb(156, 163, 175)" />}
isOpen={openAccordion === 'apiStatus'} isOpen={openAccordion === 'apiStatus'}
onToggle={() => setOpenAccordion(openAccordion === 'apiStatus' ? null : 'apiStatus')} onToggle={() => setOpenAccordion(openAccordion === 'apiStatus' ? null : 'apiStatus')}
> >
@@ -331,27 +317,26 @@ export function DevToolbar() {
{/* Error Stats Section - Accordion */} {/* Error Stats Section - Accordion */}
<Accordion <Accordion
title="Error Stats" title="Error Stats"
icon={<Icon icon={AlertTriangle} size={4} color="rgb(156, 163, 175)" />}
isOpen={openAccordion === 'errors'} isOpen={openAccordion === 'errors'}
onToggle={() => setOpenAccordion(openAccordion === 'errors' ? null : 'errors')} onToggle={() => setOpenAccordion(openAccordion === 'errors' ? null : 'errors')}
> >
<Stack gap={2}> <Stack gap={2}>
<Stack direction="row" justify="between" align="center" p={2} bg="bg-iron-gray/30" rounded="md"> <Stack direction="row" justify="between" align="center" gap={2}>
<Text size="xs" color="text-gray-400">Total Errors</Text> <Text size="xs" variant="low">Total Errors</Text>
<Text size="xs" font="mono" weight="bold" color="text-red-400">{errorStats.total}</Text> <Text size="xs" font="mono" weight="bold" variant="critical">{errorStats.total}</Text>
</Stack> </Stack>
{Object.keys(errorStats.byType).length > 0 ? ( {Object.keys(errorStats.byType).length > 0 ? (
<Stack gap={1}> <Stack gap={1}>
{Object.entries(errorStats.byType).map(([type, count]) => ( {Object.entries(errorStats.byType).map(([type, count]) => (
<Stack key={type} direction="row" justify="between" align="center" p={1.5} bg="bg-deep-graphite" rounded="md"> <Stack key={type} direction="row" justify="between" align="center" gap={2}>
<Text size="xs" color="text-gray-300">{type}</Text> <Text size="xs" variant="low">{type}</Text>
<Text size="xs" font="mono" color="text-warning-amber">{count}</Text> <Text size="xs" font="mono" variant="warning">{count}</Text>
</Stack> </Stack>
))} ))}
</Stack> </Stack>
) : ( ) : (
<Stack align="center" py={2}> <Stack align="center">
<Text size="xs" color="text-gray-500">No errors yet</Text> <Text size="xs" variant="low">No errors yet</Text>
</Stack> </Stack>
)} )}
<Button <Button
@@ -374,8 +359,8 @@ export function DevToolbar() {
{/* Collapsed state hint */} {/* Collapsed state hint */}
{!isExpanded && ( {!isExpanded && (
<Stack px={4} py={2}> <Stack>
<Text size="xs" color="text-gray-500">Click ↑ to expand dev tools</Text> <Text size="xs" variant="low">Click ↑ to expand dev tools</Text>
</Stack> </Stack>
)} )}
</Stack> </Stack>
@@ -4,8 +4,8 @@ import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Badge, StatRow, StatusIndicator } from '@/ui/StatusIndicator'; import { Badge, StatRow, StatusIndicator } from '@/ui/StatusIndicator';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Activity, RefreshCw, Terminal, Wifi } from 'lucide-react'; import { Activity, RefreshCw, Terminal, Wifi } from 'lucide-react';
interface APIStatusSectionProps { interface APIStatusSectionProps {
@@ -2,7 +2,7 @@
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Bell, Loader2 } from 'lucide-react'; import { Bell, Loader2 } from 'lucide-react';
import type { DemoNotificationType, DemoUrgency } from '../types'; import type { DemoNotificationType, DemoUrgency } from '../types';
@@ -2,8 +2,8 @@
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { AlertTriangle, Award, LucideIcon, MessageSquare, Shield, TrendingUp, Vote } from 'lucide-react'; import { AlertTriangle, Award, LucideIcon, MessageSquare, Shield, TrendingUp, Vote } from 'lucide-react';
import type { DemoNotificationType } from '../types'; import type { DemoNotificationType } from '../types';
@@ -4,7 +4,7 @@ import { getGlobalReplaySystem } from '@/lib/infrastructure/ErrorReplay';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { IconButton } from '@/ui/IconButton'; import { IconButton } from '@/ui/IconButton';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Box, Clock, Copy, Download, Play, Trash2 } from 'lucide-react'; import { Box, Clock, Copy, Download, Play, Trash2 } from 'lucide-react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@@ -2,8 +2,8 @@
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { AlertCircle, Bell, BellRing, LucideIcon } from 'lucide-react'; import { AlertCircle, Bell, BellRing, LucideIcon } from 'lucide-react';
import type { DemoUrgency } from '../types'; import type { DemoUrgency } from '../types';
@@ -3,8 +3,8 @@ import { Card } from '@/ui/Card';
import { GoalCard } from '@/ui/GoalCard'; import { GoalCard } from '@/ui/GoalCard';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { MilestoneItem } from '@/components/achievements/MilestoneItem'; import { MilestoneItem } from '@/components/achievements/MilestoneItem';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
interface Achievement { interface Achievement {
id: string; id: string;
@@ -1,10 +1,10 @@
import { Box } from '@/ui/Box';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Box } from '@/ui/primitives/Box'; import { Grid } from '@/ui/Grid';
import { Grid } from '@/ui/primitives/Grid';
import { StatGridItem } from '@/ui/StatGridItem'; import { StatGridItem } from '@/ui/StatGridItem';
import { TrendingUp } from 'lucide-react'; import { TrendingUp } from 'lucide-react';
@@ -3,7 +3,7 @@
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { InfoBox } from '@/ui/InfoBox'; import { InfoBox } from '@/ui/InfoBox';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { TextArea } from '@/ui/TextArea'; import { TextArea } from '@/ui/TextArea';
import { AlertCircle } from 'lucide-react'; import { AlertCircle } from 'lucide-react';
@@ -1,10 +1,10 @@
import { DriverIdentity } from '@/components/drivers/DriverIdentity'; import { DriverIdentity } from '@/ui/DriverIdentity';
import { DriverStats } from '@/components/drivers/DriverStats'; import { DriverStats } from '@/components/drivers/DriverStats';
import { RankBadge } from '@/components/leaderboards/RankBadge'; import { RankBadge } from '@/components/leaderboards/RankBadge';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { DriverViewModel } from '@/lib/view-models/DriverViewModel'; import { DriverViewModel } from '@/lib/view-models/DriverViewModel';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
export interface DriverCardProps { export interface DriverCardProps {
id: string; id: string;
@@ -4,7 +4,7 @@ import { CountryFlagDisplay } from '@/lib/display-objects/CountryFlagDisplay';
import { Badge } from '@/ui/Badge'; import { Badge } from '@/ui/Badge';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Zap } from 'lucide-react'; import { Zap } from 'lucide-react';
@@ -1,6 +1,6 @@
import { RatingBadge } from '@/components/drivers/RatingBadge'; import { RatingBadge } from '@/components/drivers/RatingBadge';
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import React from 'react'; import React from 'react';
@@ -1,8 +1,8 @@
'use client'; 'use client';
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Box } from '@/ui/primitives/Box';
interface DriverPerformanceOverviewProps { interface DriverPerformanceOverviewProps {
stats: { stats: {
@@ -8,7 +8,7 @@ import { useDriverProfile } from "@/hooks/driver/useDriverProfile";
import type { DriverViewModel } from '@/lib/view-models/DriverViewModel'; import type { DriverViewModel } from '@/lib/view-models/DriverViewModel';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { StatCard } from '@/ui/StatCard'; import { StatCard } from '@/ui/StatCard';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { ProfileStats } from './ProfileStats'; import { ProfileStats } from './ProfileStats';
@@ -4,7 +4,7 @@ import { RatingBadge } from '@/components/drivers/RatingBadge';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Check, Globe, Trophy, UserPlus } from 'lucide-react'; import { Check, Globe, Trophy, UserPlus } from 'lucide-react';
import { SafetyRatingBadge } from './SafetyRatingBadge'; import { SafetyRatingBadge } from './SafetyRatingBadge';
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Box } from '@/ui/primitives/Box';
import { BarChart3, LayoutDashboard, ShieldCheck } from 'lucide-react'; import { BarChart3, LayoutDashboard, ShieldCheck } from 'lucide-react';
export type ProfileTab = 'overview' | 'stats' | 'ratings'; export type ProfileTab = 'overview' | 'stats' | 'ratings';
@@ -2,7 +2,7 @@
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Car, Clock, MailCheck, MapPin, Users2 } from 'lucide-react'; import { Car, Clock, MailCheck, MapPin, Users2 } from 'lucide-react';
interface DriverRacingProfileProps { interface DriverRacingProfileProps {
@@ -1,7 +1,7 @@
import { Box } from '@/ui/Box';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Box } from '@/ui/primitives/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Star, Trophy } from 'lucide-react'; import { Star, Trophy } from 'lucide-react';
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { Box } from '@/ui/Box';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Box } from '@/ui/primitives/Box';
import { Search } from 'lucide-react'; import { Search } from 'lucide-react';
interface DriverSearchBarProps { interface DriverSearchBarProps {
@@ -1,4 +1,4 @@
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
interface DriverStatsProps { interface DriverStatsProps {
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Box } from '@/ui/primitives/Box';
interface StatItem { interface StatItem {
label: string; label: string;
@@ -3,7 +3,7 @@
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { Link } from '@/ui/Link'; import { Link } from '@/ui/Link';
import { PlaceholderImage } from '@/ui/PlaceholderImage'; import { PlaceholderImage } from '@/ui/PlaceholderImage';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import React from 'react'; import React from 'react';
+22 -21
View File
@@ -2,8 +2,11 @@
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Table, TableHead, TableBody, TableRow, TableHeaderCell } from '@/ui/Table';
import { TrendingUp } from 'lucide-react'; import { TrendingUp } from 'lucide-react';
import { Card } from '@/ui/Card';
import { Icon } from '@/ui/Icon';
import React from 'react'; import React from 'react';
interface DriverTableProps { interface DriverTableProps {
@@ -14,31 +17,29 @@ export function DriverTable({ children }: DriverTableProps) {
return ( return (
<Stack gap={4}> <Stack gap={4}>
<Stack direction="row" align="center" gap={3}> <Stack direction="row" align="center" gap={3}>
<Stack display="flex" h="10" w="10" alignItems="center" justifyContent="center" rounded="xl" bg="bg-primary-blue/10" border borderColor="border-primary-blue/20"> <Card variant="dark">
<TrendingUp size={20} color="#198CFF" /> <Icon icon={TrendingUp} size={5} intent="primary" />
</Stack> </Card>
<Stack> <Stack>
<Heading level={2}>Driver Rankings</Heading> <Heading level={2}>Driver Rankings</Heading>
<Text size="xs" color="text-gray-500">Top performers by skill rating</Text> <Text size="xs" variant="low">Top performers by skill rating</Text>
</Stack> </Stack>
</Stack> </Stack>
<Stack overflow="hidden" rounded="xl" border borderColor="border-charcoal-outline" bg="bg-deep-charcoal/50"> <Table>
<Stack as="table" w="full" textAlign="left"> <TableHead>
<Stack as="thead"> <TableRow>
<Stack as="tr" borderBottom borderColor="border-charcoal-outline" bg="bg-deep-charcoal/80"> <TableHeaderCell textAlign="center" w="60px">#</TableHeaderCell>
<Stack as="th" px={6} py={4} fontSize="xs" color="text-gray-500" textAlign="center" width="60px">#</Stack> <TableHeaderCell>Driver</TableHeaderCell>
<Stack as="th" px={6} py={4} fontSize="xs" color="text-gray-500">Driver</Stack> <TableHeaderCell w="150px">Nationality</TableHeaderCell>
<Stack as="th" px={6} py={4} fontSize="xs" color="text-gray-500" width="150px">Nationality</Stack> <TableHeaderCell textAlign="right" w="100px">Rating</TableHeaderCell>
<Stack as="th" px={6} py={4} fontSize="xs" color="text-gray-500" textAlign="right" width="100px">Rating</Stack> <TableHeaderCell textAlign="right" w="80px">Wins</TableHeaderCell>
<Stack as="th" px={6} py={4} fontSize="xs" color="text-gray-500" textAlign="right" width="80px">Wins</Stack> </TableRow>
</Stack> </TableHead>
</Stack> <TableBody>
<Stack as="tbody"> {children}
{children} </TableBody>
</Stack> </Table>
</Stack>
</Stack>
</Stack> </Stack>
); );
} }
@@ -3,7 +3,9 @@
import { RatingBadge } from '@/components/drivers/RatingBadge'; import { RatingBadge } from '@/components/drivers/RatingBadge';
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { TableRow, TableCell } from '@/ui/Table';
import { Avatar } from '@/ui/Avatar';
interface DriverTableRowProps { interface DriverTableRowProps {
rank: number; rank: number;
@@ -24,61 +26,45 @@ export function DriverTableRow({
wins, wins,
onClick, onClick,
}: DriverTableRowProps) { }: DriverTableRowProps) {
const defaultAvatar = 'https://cdn.gridpilot.com/avatars/default.png';
return ( return (
<Stack <TableRow onClick={onClick} clickable>
as="tr" <TableCell textAlign="center">
onClick={onClick}
cursor="pointer"
transition
hoverBg="bg-primary-blue/5"
group
borderBottom
borderColor="border-charcoal-outline/50"
>
<Stack as="td" px={6} py={4} textAlign="center">
<Text <Text
size="sm" size="sm"
weight="bold" weight="bold"
font="mono" font="mono"
color={rank <= 3 ? 'text-warning-amber' : 'text-gray-500'} variant={rank <= 3 ? 'warning' : 'low'}
> >
{rank} {rank}
</Text> </Text>
</Stack> </TableCell>
<Stack as="td" px={6} py={4}> <TableCell>
<Stack direction="row" align="center" gap={3}> <Stack direction="row" align="center" gap={3}>
<Stack position="relative" h="8" w="8" overflow="hidden" rounded="full" border borderColor="border-charcoal-outline" bg="bg-deep-charcoal"> <Avatar
<Image src={avatarUrl || undefined}
src={avatarUrl || defaultAvatar} alt={name}
alt={name} size="sm"
fill />
objectFit="cover"
/>
</Stack>
<Text <Text
size="sm" size="sm"
weight="semibold" weight="semibold"
color="text-white" variant="high"
groupHoverTextColor="text-primary-blue"
transition
> >
{name} {name}
</Text> </Text>
</Stack> </Stack>
</Stack> </TableCell>
<Stack as="td" px={6} py={4}> <TableCell>
<Text size="xs" color="text-gray-400">{nationality}</Text> <Text size="xs" variant="low">{nationality}</Text>
</Stack> </TableCell>
<Stack as="td" px={6} py={4} textAlign="right"> <TableCell textAlign="right">
<RatingBadge rating={rating} size="sm" /> <RatingBadge rating={rating} size="sm" />
</Stack> </TableCell>
<Stack as="td" px={6} py={4} textAlign="right"> <TableCell textAlign="right">
<Text size="sm" weight="semibold" font="mono" color="text-performance-green"> <Text size="sm" weight="semibold" font="mono" variant="success">
{wins} {wins}
</Text> </Text>
</Stack> </TableCell>
</Stack> </TableRow>
); );
} }
@@ -2,15 +2,18 @@
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Section } from '@/ui/Section';
import { Container } from '@/ui/Container';
import { Card } from '@/ui/Card';
import { Icon } from '@/ui/Icon';
import { Trophy, Users } from 'lucide-react'; import { Trophy, Users } from 'lucide-react';
interface DriverStat { interface DriverStat {
label: string; label: string;
value: string | number; value: string | number;
color?: string; intent?: 'primary' | 'success' | 'warning' | 'telemetry';
animate?: boolean;
} }
interface DriversDirectoryHeaderProps { interface DriversDirectoryHeaderProps {
@@ -29,71 +32,53 @@ export function DriversDirectoryHeader({
onViewLeaderboard, onViewLeaderboard,
}: DriversDirectoryHeaderProps) { }: DriversDirectoryHeaderProps) {
const stats: DriverStat[] = [ const stats: DriverStat[] = [
{ label: 'drivers', value: totalDrivers, color: 'text-primary-blue' }, { label: 'drivers', value: totalDrivers, intent: 'primary' },
{ label: 'active', value: activeDrivers, color: 'text-performance-green', animate: true }, { label: 'active', value: activeDrivers, intent: 'success' },
{ label: 'total wins', value: totalWins.toLocaleString(), color: 'text-warning-amber' }, { label: 'total wins', value: totalWins.toLocaleString(), intent: 'warning' },
{ label: 'races', value: totalRaces.toLocaleString(), color: 'text-neon-aqua' }, { label: 'races', value: totalRaces.toLocaleString(), intent: 'telemetry' },
]; ];
return ( return (
<Stack <Section variant="dark" padding="md">
as="header" <Container>
position="relative" <Stack direction="row" align="center" justify="between" gap={8}>
overflow="hidden" <Stack gap={6}>
rounded="2xl" <Stack direction="row" align="center" gap={3}>
border <Card variant="dark">
borderColor="border-charcoal-outline/50" <Icon icon={Users} size={6} intent="primary" />
bg="bg-gradient-to-br from-iron-gray/80 via-deep-graphite to-iron-gray/60" </Card>
p={{ base: 8, lg: 10 }} <Heading level={1}>Drivers</Heading>
>
{/* Background Accents */}
<Stack position="absolute" right="-24" top="-24" w="96" h="96" rounded="full" bg="bg-primary-blue/5" blur="3xl" />
<Stack position="absolute" bottom="-16" left="-16" w="64" h="64" rounded="full" bg="bg-neon-aqua/5" blur="3xl" />
<Stack position="relative" display="flex" flexDirection={{ base: 'col', lg: 'row' }} alignItems={{ lg: 'center' }} justifyContent="between" gap={8}>
<Stack maxWidth="2xl">
<Stack direction="row" align="center" gap={3} mb={4}>
<Stack display="flex" h="12" w="12" alignItems="center" justifyContent="center" rounded="xl" border borderColor="border-charcoal-outline" bg="bg-deep-charcoal" shadow="lg">
<Users size={24} color="#198CFF" />
</Stack> </Stack>
<Heading level={1}>Drivers</Heading>
</Stack> <Text size="lg" variant="low">
Meet the racers who make every lap count. From rookies to champions, track their journey and see who&apos;s dominating the grid.
<Text size="lg" color="text-gray-400" block leading="relaxed"> </Text>
Meet the racers who make every lap count. From rookies to champions, track their journey and see who&apos;s dominating the grid.
</Text>
<Stack display="flex" flexWrap="wrap" gap={6} mt={6}> <Stack direction="row" gap={6} wrap>
{stats.map((stat, index) => ( {stats.map((stat, index) => (
<Stack key={index} direction="row" align="center" gap={2}> <Stack key={index} direction="row" align="center" gap={2}>
<Stack <Text size="sm" variant="low">
w="2" <Text as="span" weight="semibold" variant="high">{stat.value}</Text> {stat.label}
h="2" </Text>
rounded="full" </Stack>
bg={stat.color?.replace('text-', 'bg-') || 'bg-primary-blue'} ))}
animate={stat.animate ? 'pulse' : 'none'} </Stack>
/> </Stack>
<Text size="sm" color="text-gray-400">
<Text as="span" weight="semibold" color="text-white">{stat.value}</Text> {stat.label} <Stack gap={2} align="center">
</Text> <Button
</Stack> variant="primary"
))} onClick={onViewLeaderboard}
icon={<Icon icon={Trophy} size={5} />}
>
View Leaderboard
</Button>
<Text size="xs" variant="low" align="center">
See full driver rankings
</Text>
</Stack> </Stack>
</Stack> </Stack>
</Container>
<Stack gap={2}> </Section>
<Button
variant="primary"
onClick={onViewLeaderboard}
icon={<Trophy size={20} />}
>
View Leaderboard
</Button>
<Text size="xs" color="text-gray-500" align="center" block>
See full driver rankings
</Text>
</Stack>
</Stack>
</Stack>
); );
} }
@@ -1,8 +1,8 @@
import { Box } from '@/ui/Box';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Box } from '@/ui/primitives/Box';
import { Search } from 'lucide-react'; import { Search } from 'lucide-react';
interface DriversSearchProps { interface DriversSearchProps {
@@ -3,11 +3,11 @@
import { MedalBadge } from '@/components/leaderboards/MedalBadge'; import { MedalBadge } from '@/components/leaderboards/MedalBadge';
import { mediaConfig } from '@/lib/config/mediaConfig'; import { mediaConfig } from '@/lib/config/mediaConfig';
import { Badge } from '@/ui/Badge'; import { Badge } from '@/ui/Badge';
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { MiniStat } from '@/ui/MiniStat'; import { MiniStat } from '@/ui/MiniStat';
import { Box } from '@/ui/primitives/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Flag, Shield, Star, TrendingUp } from 'lucide-react'; import { Flag, Shield, Star, TrendingUp } from 'lucide-react';
@@ -3,7 +3,7 @@ import { Button } from '@/ui/Button';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Car, Download, Edit, Trash2 } from 'lucide-react'; import { Car, Download, Edit, Trash2 } from 'lucide-react';
@@ -1,7 +1,7 @@
import { Box } from '@/ui/Box';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Box } from '@/ui/primitives/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
interface PerformanceMetricsProps { interface PerformanceMetricsProps {
@@ -5,9 +5,9 @@ import { CircularProgress } from '@/ui/CircularProgress';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { HorizontalBarChart } from '@/ui/HorizontalBarChart'; import { HorizontalBarChart } from '@/ui/HorizontalBarChart';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { GridItem } from '@/ui/primitives/GridItem'; import { GridItem } from '@/ui/GridItem';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Activity, BarChart3, Target, TrendingUp } from 'lucide-react'; import { Activity, BarChart3, Target, TrendingUp } from 'lucide-react';
@@ -8,7 +8,7 @@ import { CountryFlag } from '@/ui/CountryFlag';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { PlaceholderImage } from '@/ui/PlaceholderImage'; import { PlaceholderImage } from '@/ui/PlaceholderImage';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
interface ProfileHeaderProps { interface ProfileHeaderProps {
@@ -6,8 +6,8 @@ import { Button } from '@/ui/Button';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Image } from '@/ui/Image'; import { Image } from '@/ui/Image';
import { Link } from '@/ui/Link'; import { Link } from '@/ui/Link';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Surface } from '@/ui/primitives/Surface'; import { Surface } from '@/ui/Surface';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Calendar, Clock, ExternalLink, Globe, Star, Trophy, UserPlus } from 'lucide-react'; import { Calendar, Clock, ExternalLink, Globe, Star, Trophy, UserPlus } from 'lucide-react';
@@ -6,7 +6,7 @@ import { Card } from '@/ui/Card';
import { Checkbox } from '@/ui/Checkbox'; import { Checkbox } from '@/ui/Checkbox';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Select } from '@/ui/Select'; import { Select } from '@/ui/Select';
import { TextArea } from '@/ui/TextArea'; import { TextArea } from '@/ui/TextArea';
import { Toggle } from '@/ui/Toggle'; import { Toggle } from '@/ui/Toggle';
@@ -4,7 +4,7 @@ import { RankBadge } from '@/components/leaderboards/RankBadge';
import { useDriverProfile } from "@/hooks/driver/useDriverProfile"; import { useDriverProfile } from "@/hooks/driver/useDriverProfile";
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { StatCard } from '@/ui/StatCard'; import { StatCard } from '@/ui/StatCard';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { useMemo } from 'react'; import { useMemo } from 'react';
@@ -3,7 +3,7 @@
import { Card, Card as Surface } from '@/ui/Card'; import { Card, Card as Surface } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Flag, UserPlus, Users } from 'lucide-react'; import { Flag, UserPlus, Users } from 'lucide-react';
+10 -21
View File
@@ -3,33 +3,22 @@ import { Badge } from '@/ui/Badge';
interface RatingBadgeProps { interface RatingBadgeProps {
rating: number; rating: number;
size?: 'sm' | 'md' | 'lg'; size?: 'sm' | 'md';
className?: string;
} }
export function RatingBadge({ rating, size = 'md', className = '' }: RatingBadgeProps) { export function RatingBadge({ rating, size = 'md' }: RatingBadgeProps) {
const getColor = (val: number) => { const getVariant = (val: number): 'warning' | 'primary' | 'success' | 'default' => {
if (val >= 2500) return { variant: 'warning' as const }; if (val >= 2500) return 'warning';
if (val >= 2000) return { color: 'text-purple-400', bg: 'bg-purple-400/10', borderColor: 'border-purple-400/20' }; if (val >= 2000) return 'primary'; // Simplified
if (val >= 1500) return { variant: 'primary' as const }; if (val >= 1500) return 'primary';
if (val >= 1000) return { variant: 'success' as const }; if (val >= 1000) return 'success';
return { variant: 'default' as const }; return 'default';
}; };
const sizeMap: Record<string, 'xs' | 'sm' | 'md'> = {
sm: 'xs',
md: 'sm',
lg: 'md',
};
const config = getColor(rating);
return ( return (
<Badge <Badge
{...config} variant={getVariant(rating)}
size={sizeMap[size]} size={size}
className={`font-mono ${className}`}
rounded="sm"
> >
{rating.toLocaleString()} {rating.toLocaleString()}
</Badge> </Badge>
@@ -4,7 +4,7 @@ import { RatingComponent } from '@/components/drivers/RatingComponent';
import { RatingHistoryItem } from '@/components/drivers/RatingHistoryItem'; import { RatingHistoryItem } from '@/components/drivers/RatingHistoryItem';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
interface RatingBreakdownProps { interface RatingBreakdownProps {
@@ -1,6 +1,6 @@
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { ProgressBar } from '@/ui/ProgressBar'; import { ProgressBar } from '@/ui/ProgressBar';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
@@ -1,6 +1,6 @@
import { Box } from '@/ui/primitives/Box'; import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
interface RatingHistoryItemProps { interface RatingHistoryItemProps {
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Box } from '@/ui/primitives/Box';
import { Shield } from 'lucide-react'; import { Shield } from 'lucide-react';
interface SafetyRatingBadgeProps { interface SafetyRatingBadgeProps {
@@ -1,8 +1,8 @@
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Box } from '@/ui/primitives/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { BarChart3 } from 'lucide-react'; import { BarChart3 } from 'lucide-react';
@@ -1,6 +1,6 @@
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { LucideIcon } from 'lucide-react'; import { LucideIcon } from 'lucide-react';
@@ -1,7 +1,7 @@
import { Badge } from '@/ui/Badge'; import { Badge } from '@/ui/Badge';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { ChevronRight, LucideIcon, UserPlus } from 'lucide-react'; import { ChevronRight, LucideIcon, UserPlus } from 'lucide-react';
@@ -4,7 +4,7 @@ import React, { Component, ReactNode, ErrorInfo, useState, version } from 'react
import { ApiError } from '@/lib/api/base/ApiError'; import { ApiError } from '@/lib/api/base/ApiError';
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler'; import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
import { DevErrorPanel } from './DevErrorPanel'; import { DevErrorPanel } from './DevErrorPanel';
import { ErrorDisplay } from '@/components/shared/state/ErrorDisplay'; import { ErrorDisplay } from '@/ui/ErrorDisplay';
interface Props { interface Props {
children: ReactNode; children: ReactNode;
@@ -5,7 +5,7 @@ import { getErrorSeverity, isConnectivityError, isRetryable, parseApiError } fro
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { IconButton } from '@/ui/IconButton'; import { IconButton } from '@/ui/IconButton';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { import {
@@ -8,7 +8,7 @@ import { Button } from '@/ui/Button';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { IconButton } from '@/ui/IconButton'; import { IconButton } from '@/ui/IconButton';
import { Input } from '@/ui/Input'; import { Input } from '@/ui/Input';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { import {
Activity, Activity,
@@ -2,7 +2,7 @@
import React from 'react'; import React from 'react';
import { ApiError } from '@/lib/api/base/ApiError'; import { ApiError } from '@/lib/api/base/ApiError';
import { ErrorDisplay as UiErrorDisplay } from '@/components/shared/state/ErrorDisplay'; import { ErrorDisplay as UiErrorDisplay } from '@/ui/ErrorDisplay';
interface ErrorDisplayProps { interface ErrorDisplayProps {
error: ApiError; error: ApiError;
@@ -3,6 +3,7 @@
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Glow } from '@/ui/Glow'; import { Glow } from '@/ui/Glow';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/Stack';
import { ErrorPageContainer } from '@/ui/ErrorPageContainer'; import { ErrorPageContainer } from '@/ui/ErrorPageContainer';
import { AppErrorBoundaryView } from './AppErrorBoundaryView'; import { AppErrorBoundaryView } from './AppErrorBoundaryView';
import { ErrorDetailsBlock } from './ErrorDetailsBlock'; import { ErrorDetailsBlock } from './ErrorDetailsBlock';
@@ -32,13 +33,13 @@ export function ErrorScreen({ error, reset, onHome }: ErrorScreenProps) {
description="The application encountered an unexpected state. Our telemetry has logged the incident." description="The application encountered an unexpected state. Our telemetry has logged the incident."
> >
{/* Error Message Summary */} {/* Error Message Summary */}
<div style={{ width: '100%', marginBottom: '1.5rem' }}> <Stack fullWidth marginBottom={6}>
<Card variant="outline"> <Card variant="outline">
<Text font="mono" size="sm" variant="warning" block> <Text font="mono" size="sm" variant="warning" block>
{error.message || 'Unknown execution error'} {error.message || 'Unknown execution error'}
</Text> </Text>
</Card> </Card>
</div> </Stack>
<ErrorRecoveryActions onRetry={reset} onHome={onHome} /> <ErrorRecoveryActions onRetry={reset} onHome={onHome} />
@@ -9,6 +9,8 @@ import { Icon } from '@/ui/Icon';
import { EmptyState } from '@/ui/EmptyState'; import { EmptyState } from '@/ui/EmptyState';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Stack } from '@/ui/Stack';
interface FeedItem { interface FeedItem {
id: string; id: string;
headline: string; headline: string;
@@ -32,7 +34,7 @@ export function ActivityFeed({ items, hasItems }: ActivityFeedProps) {
actions={<Icon icon={Activity} size={5} intent="primary" />} actions={<Icon icon={Activity} size={5} intent="primary" />}
/> />
{hasItems ? ( {hasItems ? (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}> <Stack gap={4}>
{items.slice(0, 5).map((item) => ( {items.slice(0, 5).map((item) => (
<ActivityItem <ActivityItem
key={item.id} key={item.id}
@@ -47,7 +49,7 @@ export function ActivityFeed({ items, hasItems }: ActivityFeedProps) {
)} )}
</ActivityItem> </ActivityItem>
))} ))}
</div> </Stack>
) : ( ) : (
<EmptyState <EmptyState
icon={Activity} icon={Activity}
+2 -2
View File
@@ -4,8 +4,8 @@ import { UpcomingRacesSidebar } from '@/components/races/UpcomingRacesSidebar';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Section } from '@/ui/Section'; import { Section } from '@/ui/Section';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
interface HomeFeatureDescriptionProps { interface HomeFeatureDescriptionProps {
lead: string; lead: string;
@@ -3,8 +3,8 @@
import React from 'react'; import React from 'react';
import { Panel } from '@/ui/Panel'; import { Panel } from '@/ui/Panel';
import { Glow } from '@/ui/Glow'; import { Glow } from '@/ui/Glow';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Section } from '@/ui/Section'; import { Section } from '@/ui/Section';
@@ -19,7 +19,6 @@ interface HomeFeatureSectionProps {
/** /**
* HomeFeatureSection - A semantic section highlighting a feature. * HomeFeatureSection - A semantic section highlighting a feature.
* Refactored to use semantic HTML and Tailwind.
*/ */
export function HomeFeatureSection({ export function HomeFeatureSection({
heading, heading,
@@ -28,18 +27,6 @@ export function HomeFeatureSection({
layout, layout,
accentColor = 'primary', accentColor = 'primary',
}: HomeFeatureSectionProps) { }: HomeFeatureSectionProps) {
const accentBorderColor = {
primary: 'border-primary-accent/40',
aqua: 'border-telemetry-aqua/40',
amber: 'border-warning-amber/40',
}[accentColor];
const accentBgColor = {
primary: 'primary-accent',
aqua: 'telemetry-aqua',
amber: 'warning-amber',
}[accentColor];
const glowColor = ({ const glowColor = ({
primary: 'primary', primary: 'primary',
aqua: 'aqua', aqua: 'aqua',
@@ -47,7 +34,7 @@ export function HomeFeatureSection({
}[accentColor] || 'primary') as 'primary' | 'aqua' | 'amber' | 'purple'; }[accentColor] || 'primary') as 'primary' | 'aqua' | 'amber' | 'purple';
return ( return (
<Section variant="dark" py={24} borderBottom borderColor="border-gray" overflow="hidden" position="relative"> <Section variant="dark" padding="lg">
<Glow <Glow
color={glowColor} color={glowColor}
size="lg" size="lg"
@@ -56,31 +43,25 @@ export function HomeFeatureSection({
/> />
<Container> <Container>
<Grid cols={1} lgCols={2} gap={12} alignItems="center"> <Grid cols={{ base: 1, lg: 2 }} gap={12}>
{/* Text Content */} {/* Text Content */}
<Stack gap={8} order={{ lg: layout === 'text-right' ? 2 : 1 }}> <Stack gap={8}>
<Stack gap={4}> <Stack gap={4}>
<Stack w="12" h="1" bg={accentBgColor}>{null}</Stack> <Heading level={2} weight="bold">
<Heading level={2} fontSize={{ base: '3xl', md: '5xl' }} weight="bold" letterSpacing="tighter" lineHeight="none" color="text-white">
{heading} {heading}
</Heading> </Heading>
</Stack> </Stack>
<Stack color="text-gray-500" borderLeft borderStyle="solid" borderColor="border-gray/20" pl={6}> <Stack>
{description} {description}
</Stack> </Stack>
</Stack> </Stack>
{/* Mockup Panel */} {/* Mockup Panel */}
<Stack order={{ lg: layout === 'text-right' ? 1 : 2 }}> <Panel variant="dark">
<Panel padding={1} variant="dark" border={true} position="relative" group overflow="hidden"> <Stack align="center" justify="center">
<Stack bg="graphite-black" minHeight="300px" align="center" justify="center"> {mockup}
{mockup} </Stack>
</Stack> </Panel>
{/* Decorative corner accents */}
<Stack position="absolute" top="0" left="0" w="4" h="4" borderTop borderLeft borderColor={accentBorderColor} opacity={0.4}>{null}</Stack>
<Stack position="absolute" bottom="0" right="0" w="4" h="4" borderBottom borderRight borderColor={accentBorderColor} opacity={0.4}>{null}</Stack>
</Panel>
</Stack>
</Grid> </Grid>
</Container> </Container>
</Section> </Section>
+48 -65
View File
@@ -8,8 +8,8 @@ import { DiscordIcon } from '@/ui/icons/DiscordIcon';
import { Code, Lightbulb, LucideIcon, MessageSquare, Users } from 'lucide-react'; import { Code, Lightbulb, LucideIcon, MessageSquare, Users } from 'lucide-react';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Card } from '@/ui/Card'; import { Card } from '@/ui/Card';
import { Section } from '@/ui/Section'; import { Section } from '@/ui/Section';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
@@ -18,71 +18,59 @@ export function HomeFooterCTA() {
const discordUrl = process.env.NEXT_PUBLIC_DISCORD_URL || '#'; const discordUrl = process.env.NEXT_PUBLIC_DISCORD_URL || '#';
return ( return (
<Section variant="dark" py={32} borderBottom borderColor="border-gray/50" overflow="hidden" position="relative"> <Section variant="dark" padding="lg">
<Glow color="primary" size="xl" position="center" opacity={0.05} /> <Glow color="primary" size="xl" position="center" opacity={0.05} />
<Container> <Container>
<Card position="relative" overflow="hidden" variant="outline" p={{ base: 8, md: 12 }} className="bg-panel-gray/40"> <Card variant="outline">
{/* Discord brand accent */} <Stack align="center" gap={12}>
<Stack position="absolute" top={0} left={0} right={0} h="1" bg="primary-accent">{null}</Stack>
<Stack align="center" gap={12} center>
{/* Header */} {/* Header */}
<Stack align="center" gap={6}> <Stack align="center" gap={6}>
<Stack position="relative" align="center" justify="center" w={{ base: '16', md: '20' }} h={{ base: '16', md: '20' }} bg="primary-accent/10" border borderColor="primary-accent/30"> <DiscordIcon size={40} />
<DiscordIcon color="text-primary-accent" size={40} />
<Stack position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent">{null}</Stack>
<Stack position="absolute" bottom="-1px" right="-1px" w="2" h="2" borderBottom borderRight borderColor="primary-accent">{null}</Stack>
</Stack>
<Stack gap={4} align="center"> <Stack gap={4} align="center">
<Heading level={2} weight="bold" color="text-white" fontSize={{ base: '2xl', md: '4xl' }} letterSpacing="tight"> <Heading level={2} weight="bold" align="center">
Join the Grid on Discord Join the Grid on Discord
</Heading> </Heading>
<Stack w="16" h="1" bg="primary-accent">{null}</Stack>
</Stack> </Stack>
</Stack> </Stack>
{/* Personal message */} {/* Personal message */}
<Stack maxWidth="2xl" mx="auto" align="center"> <Stack align="center" gap={6}>
<Stack gap={6}> <Text size="lg" variant="high" weight="medium" align="center">
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed" textAlign="center"> GridPilot is a <Text as="span" variant="high" weight="bold">solo developer project</Text> built for the community.
GridPilot is a <Text as="span" color="text-white" weight="bold">solo developer project</Text> built for the community. </Text>
</Text> <Text size="base" variant="low" align="center">
<Text size="base" color="text-gray-400" weight="normal" leading="relaxed" textAlign="center"> We are in early alpha. Join us to help shape the future of motorsport infrastructure. Your feedback directly influences the roadmap.
We are in early alpha. Join us to help shape the future of motorsport infrastructure. Your feedback directly influences the roadmap. </Text>
</Text>
</Stack>
</Stack> </Stack>
{/* Benefits grid */} {/* Benefits grid */}
<Stack maxWidth="4xl" mx="auto" fullWidth> <Grid cols={{ base: 1, md: 2 }} gap={6}>
<Grid cols={1} mdCols={2} gap={6}> <BenefitItem
<BenefitItem icon={MessageSquare}
icon={MessageSquare} title="Share your pain points"
title="Share your pain points" description="Tell us what frustrates you about league racing today."
description="Tell us what frustrates you about league racing today." />
/> <BenefitItem
<BenefitItem icon={Lightbulb}
icon={Lightbulb} title="Shape the product"
title="Shape the product" description="Your ideas directly influence our roadmap."
description="Your ideas directly influence our roadmap." />
/> <BenefitItem
<BenefitItem icon={Users}
icon={Users} title="Connect with racers"
title="Connect with racers" description="Join a community of like-minded competitive drivers."
description="Join a community of like-minded competitive drivers." />
/> <BenefitItem
<BenefitItem icon={Code}
icon={Code} title="Early Access"
title="Early Access" description="Test new features before they go public."
description="Test new features before they go public." />
/> </Grid>
</Grid>
</Stack>
{/* CTA Button */} {/* CTA Button */}
<Stack gap={6} pt={4} align="center"> <Stack gap={6} align="center">
<Button <Button
as="a" as="a"
href={discordUrl} href={discordUrl}
@@ -90,19 +78,14 @@ export function HomeFooterCTA() {
rel="noopener noreferrer" rel="noopener noreferrer"
variant="primary" variant="primary"
size="lg" size="lg"
px={16}
py={4}
h="auto"
icon={<DiscordIcon size={24} />} icon={<DiscordIcon size={24} />}
> >
Join Discord Join Discord
</Button> </Button>
<Stack border borderStyle="dashed" borderColor="primary-accent/50" px={4} py={1}> <Text size="xs" variant="primary" weight="bold" font="mono" uppercase>
<Text size="xs" color="text-primary-accent" weight="bold" font="mono" uppercase letterSpacing="widest"> Early Alpha Access Available
Early Alpha Access Available </Text>
</Text>
</Stack>
</Stack> </Stack>
</Stack> </Stack>
</Card> </Card>
@@ -113,14 +96,14 @@ export function HomeFooterCTA() {
function BenefitItem({ icon, title, description }: { icon: LucideIcon, title: string, description: string }) { function BenefitItem({ icon, title, description }: { icon: LucideIcon, title: string, description: string }) {
return ( return (
<Stack direction="row" align="start" gap={5} p={6} bg="panel-gray/20" border borderColor="border-gray" className="transition-all hover:border-primary-accent/30 group"> <Card variant="dark">
<Stack align="center" justify="center" flexShrink={0} w="10" h="10" bg="primary-accent/5" border borderColor="border-gray/50" className="transition-all group-hover:border-primary-accent/30"> <Stack align="start" gap={5}>
<Icon icon={icon} size={5} color="text-primary-accent" /> <Icon icon={icon} size={5} intent="primary" />
<Stack gap={2}>
<Text size="base" weight="bold" variant="high">{title}</Text>
<Text size="sm" variant="low">{description}</Text>
</Stack>
</Stack> </Stack>
<Stack gap={2}> </Card>
<Text size="base" weight="bold" color="text-white" letterSpacing="wide">{title}</Text>
<Text size="sm" color="text-gray-400" leading="relaxed">{description}</Text>
</Stack>
</Stack>
); );
} }
+17 -35
View File
@@ -5,7 +5,9 @@ import { Container } from '@/ui/Container';
import { Glow } from '@/ui/Glow'; import { Glow } from '@/ui/Glow';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Section } from '@/ui/Section';
import { ButtonGroup } from '@/ui/ButtonGroup';
interface HomeHeaderProps { interface HomeHeaderProps {
title: string; title: string;
@@ -23,7 +25,6 @@ interface HomeHeaderProps {
/** /**
* HomeHeader - Semantic hero section for the landing page. * HomeHeader - Semantic hero section for the landing page.
* Follows "Precision Racing Minimal" theme.
*/ */
export function HomeHeader({ export function HomeHeader({
title, title,
@@ -33,43 +34,29 @@ export function HomeHeader({
secondaryAction, secondaryAction,
}: HomeHeaderProps) { }: HomeHeaderProps) {
return ( return (
<Stack as="header" position="relative" overflow="hidden" bg="graphite-black" py={{ base: 24, lg: 32 }} borderBottom borderColor="border-gray"> <Section variant="dark" padding="lg">
<Glow color="primary" size="xl" position="top-right" opacity={0.1} /> <Glow color="primary" size="xl" position="top-right" opacity={0.1} />
<Container> <Container>
<Stack maxWidth="4xl" fullWidth> <Stack gap={8}>
<Stack direction="row" align="center" gap={3} borderLeft borderStyle="solid" borderWidth="2px" borderColor="primary-accent" bg="primary-accent/5" px={4} py={1} mb={8}> <Text size="xs" weight="bold" uppercase variant="primary">
<Text size="xs" weight="bold" uppercase letterSpacing="0.3em" color="text-primary-accent"> {subtitle}
{subtitle} </Text>
</Text>
</Stack>
<Heading <Heading level={1} weight="bold">
level={1}
fontSize={{ base: '5xl', md: '7xl', lg: '8xl' }}
weight="bold"
color="text-white"
letterSpacing="tighter"
lineHeight="0.9"
mb={8}
>
{title} {title}
</Heading> </Heading>
<Stack borderLeft borderStyle="solid" borderColor="border-gray" pl={8} mb={12} maxWidth="2xl"> <Text size="lg" variant="low">
<Text size="lg" color="text-gray-400" leading="relaxed" opacity={0.8}> {description}
{description} </Text>
</Text>
</Stack>
<Stack direction={{ base: 'col', md: 'row' }} gap={4}> <ButtonGroup gap={4}>
<Button <Button
as="a" as="a"
href={primaryAction.href} href={primaryAction.href}
variant="primary" variant="primary"
h="14" size="lg"
px={12}
fontSize="xs"
> >
{primaryAction.label} {primaryAction.label}
</Button> </Button>
@@ -77,18 +64,13 @@ export function HomeHeader({
as="a" as="a"
href={secondaryAction.href} href={secondaryAction.href}
variant="secondary" variant="secondary"
h="14" size="lg"
px={12}
fontSize="xs"
bg="transparent"
borderColor="border-gray"
hoverBorderColor="primary-accent/50"
> >
{secondaryAction.label} {secondaryAction.label}
</Button> </Button>
</Stack> </ButtonGroup>
</Stack> </Stack>
</Container> </Container>
</Stack> </Section>
); );
} }
+28 -46
View File
@@ -4,58 +4,40 @@ import React from 'react';
import { MetricCard } from '@/ui/MetricCard'; import { MetricCard } from '@/ui/MetricCard';
import { Activity, Users, Trophy, Calendar } from 'lucide-react'; import { Activity, Users, Trophy, Calendar } from 'lucide-react';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Stack } from '@/ui/primitives/Stack';
/** /**
* HomeStatsStrip - A thin strip showing some status or quick info. * HomeStatsStrip - A thin strip showing some status or quick info.
* Part of the "Telemetry-workspace" feel. * Part of the "Telemetry-workspace" feel.
* Refactored to use semantic HTML and Tailwind.
*/ */
export function HomeStatsStrip() { export function HomeStatsStrip() {
return ( return (
<Stack bg="graphite-black" borderBottom borderTop borderColor="border-gray/30" py={0}> <Container>
<Container> <Grid cols={{ base: 2, md: 4 }} gap={4}>
<Grid cols={2} mdCols={4} gap={0} borderLeft borderRight borderColor="border-gray/30"> <MetricCard
<MetricCard label="Active Drivers"
label="Active Drivers" value="1,284"
value="1,284" icon={Users}
icon={Users} trend={{ value: 12, isPositive: true }}
trend={{ value: 12, isPositive: true }} />
border={false} <MetricCard
bg="transparent" label="Live Sessions"
/> value="42"
<Stack borderLeft borderColor="border-gray/30"> icon={Activity}
<MetricCard intent="telemetry"
label="Live Sessions" />
value="42" <MetricCard
icon={Activity} label="Total Races"
color="text-telemetry-aqua" value="15,402"
border={false} icon={Trophy}
bg="transparent" intent="warning"
/> />
</Stack> <MetricCard
<Stack borderLeft borderColor="border-gray/30"> label="Next Event"
<MetricCard value="14:00"
label="Total Races" icon={Calendar}
value="15,402" />
icon={Trophy} </Grid>
color="text-warning-amber" </Container>
border={false}
bg="transparent"
/>
</Stack>
<Stack borderLeft borderColor="border-gray/30">
<MetricCard
label="Next Event"
value="14:00"
icon={Calendar}
border={false}
bg="transparent"
/>
</Stack>
</Grid>
</Container>
</Stack>
); );
} }
@@ -2,9 +2,9 @@
import { LeagueCard } from '@/components/leagues/LeagueCard'; import { LeagueCard } from '@/components/leagues/LeagueCard';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Link } from '@/ui/Link'; import { Link } from '@/ui/Link';
import { Box } from '@/ui/primitives/Box';
interface League { interface League {
id: string; id: string;
@@ -4,13 +4,11 @@ import { routes } from '@/lib/routing/RouteConfig';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Text } from '@/ui/Text'; import { ButtonGroup } from '@/ui/ButtonGroup';
import { Stack } from '@/ui/primitives/Stack';
import { Plus, Search, Shield, Users } from 'lucide-react'; import { Plus, Search, Shield, Users } from 'lucide-react';
/** /**
* QuickLinksPanel - Semantic quick actions bar. * QuickLinksPanel - Semantic quick actions bar.
* Replaces HomeQuickActions with a more semantic implementation.
*/ */
export function QuickLinksPanel() { export function QuickLinksPanel() {
const links = [ const links = [
@@ -21,34 +19,20 @@ export function QuickLinksPanel() {
]; ];
return ( return (
<Stack as="nav" bg="panel-gray/50" py={8} borderBottom borderColor="border-gray/30"> <Container>
<Container> <ButtonGroup alignment="center" gap={4} marginTop={8}>
<Stack direction="row" wrap justify="center" gap={4}> {links.map((link) => (
{links.map((link) => ( <Button
<Button key={link.label}
key={link.label} as="a"
as="a" href={link.href}
href={link.href} variant="secondary"
variant="secondary" icon={<Icon icon={link.icon} size={4} intent="low" />}
px={6} >
bg="graphite-black" {link.label}
borderColor="border-gray/50" </Button>
className="flex items-center gap-3 transition-all hover:border-primary-accent/50 group" ))}
> </ButtonGroup>
<Icon </Container>
icon={link.icon}
size={4}
color="text-gray-500"
groupHoverTextColor="primary-accent"
transition
/>
<Text size="xs" weight="bold" uppercase letterSpacing="widest">
{link.label}
</Text>
</Button>
))}
</Stack>
</Container>
</Stack>
); );
} }
@@ -5,7 +5,7 @@ import { routes } from '@/lib/routing/RouteConfig';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Link } from '@/ui/Link'; import { Link } from '@/ui/Link';
import { Panel } from '@/ui/Panel'; import { Panel } from '@/ui/Panel';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
interface Race { interface Race {
@@ -2,9 +2,9 @@
import { TeamCard } from '@/components/teams/TeamCard'; import { TeamCard } from '@/components/teams/TeamCard';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Link } from '@/ui/Link'; import { Link } from '@/ui/Link';
import { Box } from '@/ui/primitives/Box';
interface Team { interface Team {
id: string; id: string;
@@ -1,17 +1,16 @@
import { useParallax } from "@/hooks/useScrollProgress";
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Grid } from '@/ui/Grid';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { useRef } from 'react'; import { Section } from '@/ui/Section';
import { Panel } from '@/ui/Panel';
interface AlternatingSectionProps { interface AlternatingSectionProps {
heading: string; heading: string;
description: string | React.ReactNode; description: string | React.ReactNode;
mockup: React.ReactNode; mockup: React.ReactNode;
layout: 'text-left' | 'text-right'; layout: 'text-left' | 'text-right';
backgroundImage?: string;
backgroundVideo?: string;
} }
export function AlternatingSection({ export function AlternatingSection({
@@ -19,112 +18,49 @@ export function AlternatingSection({
description, description,
mockup, mockup,
layout, layout,
backgroundImage,
backgroundVideo
}: AlternatingSectionProps) { }: AlternatingSectionProps) {
const sectionRef = useRef<HTMLElement>(null); const textContent = (
const bgParallax = useParallax(sectionRef, 0.1); <Stack gap={8}>
<Stack gap={4}>
return ( <Heading level={2} weight="bold">
<Stack {heading}
as="section" </Heading>
ref={sectionRef} </Stack>
position="relative" <Stack>
overflow="hidden" {typeof description === 'string' ? (
bg="graphite-black" <Text size="lg" variant="low">{description}</Text>
py={{ base: 20, md: 32 }} ) : (
className="border-b border-border-gray" description
> )}
{backgroundVideo && ( </Stack>
<Stack
position="absolute"
inset="0"
fullWidth
fullHeight
overflow="hidden"
>
<Stack
as="video"
autoPlay
loop
muted
playsInline
fullWidth
fullHeight
objectFit="cover"
opacity={0.1}
>
<Stack as="source" src={backgroundVideo} type="video/mp4" />
</Stack>
{/* Dark overlay to ensure readability */}
<Stack position="absolute" inset="0" bg="linear-gradient(to bottom, #0C0D0F, transparent, #0C0D0F)" />
</Stack>
)}
{backgroundImage && !backgroundVideo && (
<Stack
position="absolute"
inset="0"
fullWidth
fullHeight
overflow="hidden"
>
<Stack
position="absolute"
inset="0"
bg={`url(${backgroundImage})`}
backgroundSize="cover"
backgroundPosition="center"
opacity={0.1}
style={{ transform: `translateY(${bgParallax * 0.3}px)` }}
/>
{/* Dark overlay to ensure readability */}
<Stack position="absolute" inset="0" bg="linear-gradient(to bottom, #0C0D0F, transparent, #0C0D0F)" />
</Stack>
)}
<Container size="lg" position="relative" zIndex={10}>
<Stack display="grid" gridCols={{ base: 1, lg: 2 }} gap={{ base: 12, lg: 24 }} alignItems="center">
{/* Text Content */}
<Stack
display="flex"
flexDirection="column"
gap={8}
order={{ lg: layout === 'text-right' ? 2 : 1 }}
>
<Stack gap={4}>
<Stack w="8" h="1" bg="primary-accent" />
<Heading level={2} fontSize={{ base: '3xl', md: '5xl' }} weight="bold" className="tracking-tighter uppercase leading-none">
{heading}
</Heading>
</Stack>
<Stack className="text-gray-500 border-l border-border-gray/20 pl-6">
{typeof description === 'string' ? (
<Text size="lg" leading="relaxed" weight="normal">{description}</Text>
) : (
description
)}
</Stack>
</Stack>
{/* Mockup */}
<Stack
position="relative"
order={{ lg: layout === 'text-right' ? 1 : 2 }}
className="bg-panel-gray/20 border border-border-gray/30 rounded-none p-1 shadow-2xl group"
>
<Stack
fullWidth
minHeight={{ base: '240px', md: '380px' }}
className="overflow-hidden rounded-none border border-border-gray/20 bg-graphite-black"
>
{mockup}
</Stack>
{/* Decorative corner accents */}
<Stack position="absolute" top="-1px" left="-1px" w="3" h="3" borderTop borderLeft borderColor="primary-accent/40" />
<Stack position="absolute" bottom="-1px" right="-1px" w="3" h="3" borderBottom borderRight borderColor="primary-accent/40" />
</Stack>
</Stack>
</Container>
</Stack> </Stack>
); );
const mockupContent = (
<Panel variant="dark">
<Stack align="center" justify="center">
{mockup}
</Stack>
</Panel>
);
return (
<Section variant="dark" padding="lg">
<Container size="lg">
<Grid cols={{ base: 1, lg: 2 }} gap={12}>
{layout === 'text-left' ? (
<>
{textContent}
{mockupContent}
</>
) : (
<>
{mockupContent}
{textContent}
</>
)}
</Grid>
</Container>
</Section>
);
} }
@@ -1,9 +1,9 @@
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Box } from '@/ui/primitives/Box'; import { Surface } from '@/ui/Surface';
import { Surface } from '@/ui/primitives/Surface';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { motion, useReducedMotion } from 'framer-motion'; import { motion, useReducedMotion } from 'framer-motion';
import { LucideIcon } from 'lucide-react'; import { LucideIcon } from 'lucide-react';
@@ -1,10 +1,10 @@
'use client'; 'use client';
import { routes } from '@/lib/routing/RouteConfig'; import { routes } from '@/lib/routing/RouteConfig';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Button } from '@/ui/Button'; import { Button } from '@/ui/Button';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Grid } from '@/ui/primitives/Grid'; import { Grid } from '@/ui/Grid';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Link } from '@/ui/Link'; import { Link } from '@/ui/Link';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
+1 -1
View File
@@ -2,7 +2,7 @@
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { ChevronDown } from 'lucide-react'; import { ChevronDown } from 'lucide-react';
@@ -9,7 +9,7 @@ import { StandingsTableMockup } from '@/components/mockups/StandingsTableMockup'
import { TeamCompetitionMockup } from '@/components/mockups/TeamCompetitionMockup'; import { TeamCompetitionMockup } from '@/components/mockups/TeamCompetitionMockup';
import { Container } from '@/ui/Container'; import { Container } from '@/ui/Container';
import { Heading } from '@/ui/Heading'; import { Heading } from '@/ui/Heading';
import { Stack } from '@/ui/primitives/Stack'; import { Stack } from '@/ui/Stack';
import { Section } from '@/ui/Section'; import { Section } from '@/ui/Section';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
@@ -1,5 +1,5 @@
import { Box } from '@/ui/Box';
import { Icon } from '@/ui/Icon'; import { Icon } from '@/ui/Icon';
import { Box } from '@/ui/primitives/Box';
import { Text } from '@/ui/Text'; import { Text } from '@/ui/Text';
import { LucideIcon } from 'lucide-react'; import { LucideIcon } from 'lucide-react';

Some files were not shown because too many files have changed in this diff Show More