From f035cfe7ced702d009994c1ad579f7dfecb0d6b5 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Thu, 15 Jan 2026 17:12:24 +0100 Subject: [PATCH] website refactor --- apps/website/app/admin/layout.tsx | 2 +- apps/website/app/leagues/LeaguesClient.tsx | 6 +- .../rulebook/LeagueRulebookPageClient.tsx | 2 +- .../[id]/stewarding/StewardingTemplate.tsx | 8 +- .../stewarding/protests/[protestId]/page.tsx | 6 +- .../leagues/[id]/wallet/WalletTemplate.tsx | 6 +- .../app/leagues/create/CreateLeagueWizard.tsx | 10 +- apps/website/app/leagues/create/page.tsx | 4 +- apps/website/app/profile/layout.tsx | 2 +- apps/website/app/profile/liveries/page.tsx | 2 +- .../app/profile/liveries/upload/page.tsx | 8 +- apps/website/app/profile/settings/page.tsx | 8 +- apps/website/app/sponsor/billing/page.tsx | 14 +- apps/website/app/sponsor/campaigns/page.tsx | 6 +- apps/website/app/sponsor/settings/page.tsx | 14 +- apps/website/app/sponsor/signup/page.tsx | 24 +- .../TeamLeaderboardPageWrapper.tsx | 2 +- apps/website/components/AppWrapper.tsx | 4 +- .../components/auth/AuthWorkflowMockup.tsx | 139 +- .../components/auth/UserRolesPreview.tsx | 8 +- .../components/dashboard/ActivityFeed.tsx | 62 - .../dashboard/ChampionshipStandings.tsx | 56 - .../components/dashboard/DashboardHero.tsx | 101 -- .../components/dashboard/FriendsSidebar.tsx | 83 -- .../components/dashboard/NextRaceCard.tsx | 74 - .../components/dashboard/UpcomingRaces.tsx | 68 - apps/website/components/dev/Accordion.tsx | 41 - apps/website/components/dev/DevToolbar.tsx | 272 ++-- .../dev/sections/APIStatusSection.tsx | 205 +-- .../dev/sections/NotificationSendSection.tsx | 70 +- .../dev/sections/NotificationTypeSection.tsx | 76 +- .../components/dev/sections/ReplaySection.tsx | 129 +- .../dev/sections/UrgencySection.tsx | 108 +- apps/website/components/dev/types.ts | 6 +- .../components/drivers/CareerHighlights.tsx | 127 -- .../drivers/CategoryDistribution.tsx | 69 - .../components/drivers/CreateDriverForm.tsx | 191 ++- .../website/components/drivers/DriverCard.tsx | 79 -- .../components/drivers/DriverIdentity.tsx | 73 - .../components/drivers/DriverProfile.tsx | 123 +- .../components/drivers/DriverRankings.tsx | 81 -- .../components/drivers/FeaturedDriverCard.tsx | 120 -- .../components/drivers/LeaderboardPreview.tsx | 140 -- .../components/drivers/PerformanceMetrics.tsx | 82 -- .../components/drivers/ProfileRaceHistory.tsx | 93 +- .../components/drivers/ProfileSettings.tsx | 232 ++- .../components/drivers/ProfileStats.tsx | 184 ++- .../components/drivers/RankingsPodium.tsx | 94 -- .../components/drivers/RankingsTable.tsx | 122 -- .../components/drivers/RatingBreakdown.tsx | 203 --- .../components/drivers/RecentActivity.tsx | 79 -- .../components/drivers/SkillDistribution.tsx | 69 - .../components/errors/ApiErrorBoundary.tsx | 8 +- .../components/errors/DevErrorPanel.tsx | 539 +++---- .../errors/EnhancedErrorBoundary.tsx | 179 +-- .../components/errors/EnhancedFormError.tsx | 383 +++-- .../errors/ErrorAnalyticsDashboard.tsx | 877 ++++++------ .../components/errors/ErrorDisplay.tsx | 136 +- .../errors/NotificationIntegration.tsx | 2 +- apps/website/components/feed/FeedItemCard.tsx | 85 +- .../components/landing/AlternatingSection.tsx | 108 -- apps/website/components/landing/FAQ.tsx | 108 +- .../components/landing/FeatureGrid.tsx | 106 -- .../components/landing/FeatureItem.tsx | 59 +- apps/website/components/landing/Footer.tsx | 92 -- apps/website/components/landing/Hero.tsx | 139 -- .../components/landing/LandingItems.tsx | 54 - .../leaderboards/DriverLeaderboardPreview.tsx | 145 +- .../leaderboards/LeaderboardsHero.tsx | 31 +- .../leaderboards/TeamLeaderboardPreview.tsx | 163 ++- .../website/components/leagues/EmptyState.tsx | 50 +- .../components/leagues/EndRaceModal.tsx | 142 +- .../components/leagues/JoinLeagueButton.tsx | 84 +- .../components/leagues/LeagueActivityFeed.tsx | 125 +- .../leagues/LeagueBasicsSection.tsx | 132 +- .../website/components/leagues/LeagueCard.tsx | 289 ---- .../leagues/LeagueChampionshipStats.tsx | 64 +- .../components/leagues/LeagueCover.tsx | 27 - .../leagues/LeagueDecalPlacementEditor.tsx | 320 +++-- .../components/leagues/LeagueDropSection.tsx | 549 +++++--- .../components/leagues/LeagueHeader.tsx | 84 +- .../website/components/leagues/LeagueLogo.tsx | 30 - .../components/leagues/LeagueMembers.tsx | 281 ++-- .../leagues/LeagueMembershipFeesSection.tsx | 162 +-- .../leagues/LeagueOwnershipTransfer.tsx | 2 +- .../leagues/LeagueReviewSummary.tsx | 469 ++++--- .../leagues/LeagueSchedule.test.tsx | 79 -- .../components/leagues/LeagueSchedule.tsx | 202 ++- .../leagues/LeagueScoringSection.tsx | 581 +++++--- .../leagues/LeagueSponsorshipsSection.tsx | 206 +-- .../leagues/LeagueStewardingSection.tsx | 502 ++++--- .../leagues/LeagueStructureSection.tsx | 973 +++++++------ .../components/leagues/LeagueSummaryCard.tsx | 70 - .../website/components/leagues/LeagueTabs.tsx | 12 +- .../leagues/LeagueTimingsSection.tsx | 1242 +++++++++++------ .../leagues/LeagueVisibilitySection.tsx | 597 +++++--- .../components/leagues/MembershipStatus.tsx | 69 +- .../website/components/leagues/PenaltyFAB.tsx | 23 - .../components/leagues/PenaltyHistoryList.tsx | 145 +- .../leagues/PendingProtestsList.tsx | 115 -- .../components/leagues/PointsTable.tsx | 55 - .../components/leagues/QuickPenaltyModal.tsx | 203 +-- .../components/leagues/ReadonlyLeagueInfo.tsx | 39 +- .../components/leagues/ReviewProtestModal.tsx | 425 +++--- .../components/leagues/RulebookTabs.tsx | 40 - .../components/leagues/ScheduleRaceCard.tsx | 5 +- .../leagues/SponsorshipRequestCard.tsx | 2 + .../leagues/SponsorshipSlotCard.tsx | 2 + .../components/leagues/StandingsTable.tsx | 703 ++++++---- .../components/leagues/StewardingStats.tsx | 49 +- .../components/leagues/TransactionRow.tsx | 15 +- .../mockups/CareerProgressionMockup.tsx | 289 ++-- .../mockups/CompanionAutomationMockup.tsx | 444 +++--- .../mockups/DriverProfileMockup.tsx | 314 +++-- .../mockups/LeagueDiscoveryMockup.tsx | 422 +++--- .../components/mockups/LeagueHomeMockup.tsx | 297 ++-- .../mockups/ProtestWorkflowMockup.tsx | 209 ++- .../components/mockups/RaceHistoryMockup.tsx | 447 ++++-- .../components/mockups/SimPlatformMockup.tsx | 261 ++-- .../mockups/StandingsTableMockup.tsx | 301 ++-- .../mockups/TeamCompetitionMockup.tsx | 376 +++-- .../notifications/ModalNotification.tsx | 274 ++-- .../notifications/NotificationCenter.tsx | 229 +-- .../notifications/NotificationProvider.tsx | 35 +- .../notifications/ToastNotification.tsx | 134 +- .../components/onboarding/AvatarStep.tsx | 94 +- .../onboarding/OnboardingWizard.tsx | 14 +- .../onboarding/PersonalInfoStep.tsx | 8 +- .../components/profile/CareerStats.tsx | 46 - .../components/profile/DriverRatingPill.tsx | 28 - .../components/profile/DriverSummaryPill.tsx | 83 -- .../components/profile/ProfileHeader.tsx | 84 -- apps/website/components/profile/UserPill.tsx | 580 ++++---- .../components/races/FileProtestModal.tsx | 256 ++-- .../components/races/ImportResultsForm.tsx | 75 +- .../components/races/LatestResultsSidebar.tsx | 47 - .../components/races/LiveRaceBanner.tsx | 51 - .../components/races/LiveRacesBanner.tsx | 68 - apps/website/components/races/ProtestCard.tsx | 124 -- .../website/components/races/QuickActions.tsx | 32 - apps/website/components/races/RaceCard.tsx | 105 -- .../components/races/RaceDetailCard.tsx | 44 - .../components/races/RaceEntryList.tsx | 88 -- .../components/races/RaceFilterBar.tsx | 76 - .../components/races/RaceFilterModal.tsx | 151 -- apps/website/components/races/RaceHero.tsx | 70 - apps/website/components/races/RaceList.tsx | 179 --- .../website/components/races/RaceListItem.tsx | 145 -- .../components/races/RacePageHeader.tsx | 59 - .../components/races/RacePenaltyRow.tsx | 58 - .../components/races/RaceResultCard.tsx | 85 -- .../components/races/RaceResultsHeader.tsx | 67 - apps/website/components/races/RaceSidebar.tsx | 144 -- apps/website/components/races/RaceStats.tsx | 46 - .../components/races/RaceStewardingStats.tsx | 36 - .../components/races/RaceUserResult.tsx | 92 -- .../website/components/races/ResultsTable.tsx | 273 ---- .../components/races/SidebarRaceItem.tsx | 33 - .../components/races/StewardingTabs.tsx | 44 - .../components/shared/CapabilityGate.tsx | 2 +- .../website/components/shared/HeroSection.tsx | 117 -- .../website/components/shared/StatusBadge.tsx | 58 - .../components/shared/state/ErrorDisplay.tsx | 252 ---- .../components/sponsors/ActivityItem.tsx | 25 +- .../sponsors/AvailableLeagueCard.tsx | 138 +- .../components/sponsors/MetricCard.tsx | 56 +- .../sponsors/PendingSponsorshipRequests.tsx | 229 +-- .../components/sponsors/RenewalAlert.tsx | 28 +- .../sponsors/SponsorBenefitCard.tsx | 86 +- .../components/sponsors/SponsorHero.tsx | 144 -- .../sponsors/SponsorInsightsCard.tsx | 385 +++-- .../sponsors/SponsorInsightsCardHelpers.ts | 2 +- .../components/sponsors/SponsorTierCard.tsx | 80 +- .../sponsors/SponsorWorkflowMockup.tsx | 157 +-- .../sponsors/SponsorshipCategoryCard.tsx | 43 +- .../components/teams/CreateTeamForm.tsx | 121 +- .../components/teams/FeaturedRecruiting.tsx | 156 +-- .../components/teams/JoinTeamButton.tsx | 14 +- .../components/teams/SkillLevelSection.tsx | 74 +- apps/website/components/teams/StatItem.tsx | 14 - apps/website/components/teams/TeamAdmin.tsx | 196 ++- apps/website/components/teams/TeamCard.tsx | 215 --- apps/website/components/teams/TeamHero.tsx | 83 -- .../components/teams/TeamHeroSection.tsx | 177 --- .../components/teams/TeamLadderRow.tsx | 77 - .../teams/TeamLeaderboardPreview.tsx | 183 --- .../components/teams/TeamMembershipCard.tsx | 36 +- .../components/teams/TeamRankingsTable.tsx | 118 -- apps/website/components/teams/TeamRoster.tsx | 169 +-- .../components/teams/TeamStandings.tsx | 71 +- .../services/error/ErrorAnalyticsService.ts | 84 ++ .../services/league/LeagueActivityService.ts | 41 + apps/website/lib/utilities/roster-utils.ts | 48 + apps/website/templates/DashboardTemplate.tsx | 12 +- .../templates/DriverProfileTemplate.tsx | 34 +- .../templates/DriverRankingsTemplate.tsx | 4 +- apps/website/templates/DriversTemplate.tsx | 24 +- apps/website/templates/HomeTemplate.tsx | 26 +- .../templates/LeaderboardsTemplate.tsx | 6 +- .../templates/LeagueDetailTemplate.tsx | 2 +- .../templates/LeagueRulebookTemplate.tsx | 4 +- .../templates/LeagueScheduleTemplate.tsx | 2 +- .../templates/LeagueSponsorshipsTemplate.tsx | 2 +- .../templates/ProfileLeaguesTemplate.tsx | 2 +- apps/website/templates/ProfileTemplate.tsx | 18 +- apps/website/templates/RaceDetailTemplate.tsx | 14 +- .../website/templates/RaceResultsTemplate.tsx | 6 +- .../templates/RaceStewardingTemplate.tsx | 10 +- apps/website/templates/RacesAllTemplate.tsx | 10 +- apps/website/templates/RacesTemplate.tsx | 12 +- apps/website/templates/TeamDetailTemplate.tsx | 41 +- .../templates/TeamLeaderboardTemplate.tsx | 29 +- apps/website/templates/TeamsTemplate.tsx | 6 +- apps/website/templates/auth/LoginTemplate.tsx | 4 +- apps/website/ui/Accordion.tsx | 48 + apps/website/ui/AchievementCard.tsx | 51 + .../profile => ui}/AchievementGrid.tsx | 21 +- apps/website/ui/ActiveDriverCard.tsx | 67 + apps/website/ui/ActivityFeed.tsx | 52 + apps/website/ui/ActivityFeedItem.tsx | 49 + apps/website/ui/ActivityFeedList.tsx | 14 + apps/website/ui/ActivityItem.tsx | 65 + apps/website/ui/AlternatingSection.tsx | 129 ++ apps/website/ui/AuthContainer.tsx | 2 +- apps/website/ui/AuthError.tsx | 3 +- apps/website/ui/AuthLoading.tsx | 5 +- apps/website/ui/AvailableLeagueCard.tsx | 160 +++ apps/website/ui/Badge.tsx | 35 +- apps/website/ui/BenefitCard.tsx | 109 ++ apps/website/ui/BorderTabs.tsx | 64 + apps/website/ui/Box.tsx | 243 +++- .../{components/layout => ui}/Breadcrumbs.tsx | 0 apps/website/ui/Button.tsx | 25 +- apps/website/ui/Card.tsx | 2 +- apps/website/ui/CareerHighlights.tsx | 95 ++ apps/website/ui/CareerStats.tsx | 36 + apps/website/ui/CategoryDistribution.tsx | 77 + apps/website/ui/CategoryDistributionCard.tsx | 35 + apps/website/ui/ChampionshipStandings.tsx | 56 + apps/website/ui/ChampionshipStandingsList.tsx | 14 + apps/website/ui/Checkbox.tsx | 34 + .../charts => ui}/CircularProgress.tsx | 3 +- apps/website/ui/Container.tsx | 7 +- apps/website/ui/CountryFlag.tsx | 3 +- apps/website/ui/CountrySelect.tsx | 6 +- apps/website/ui/DangerZone.tsx | 26 + apps/website/ui/DashboardHero.tsx | 139 ++ apps/website/ui/DashboardHeroWrapper.tsx | 59 + apps/website/ui/DateHeader.tsx | 30 + .../dev => ui}/DebugModeToggle.tsx | 313 +++-- .../{components/landing => ui}/DiscordCTA.tsx | 12 +- apps/website/ui/DriverCard.tsx | 71 + apps/website/ui/DriverEntryRow.tsx | 118 ++ apps/website/ui/DriverIdentity.tsx | 82 ++ apps/website/ui/DriverRankings.tsx | 52 + apps/website/ui/DriverRatingPill.tsx | 31 + apps/website/ui/DriverStats.tsx | 34 + apps/website/ui/DriverSummaryPill.tsx | 137 ++ apps/website/ui/DriverSummaryPillWrapper.tsx | 28 + .../drivers => ui}/DriversSearch.tsx | 13 +- apps/website/ui/DurationField.tsx | 2 +- .../shared/state => ui}/EmptyState.tsx | 11 +- apps/website/ui/ErrorBanner.tsx | 5 +- apps/website/ui/ErrorDisplay.tsx | 248 ++++ apps/website/ui/FeatureGrid.tsx | 102 ++ apps/website/ui/FeaturedDriverCard.tsx | 161 +++ .../feed => ui}/FeedEmptyState.tsx | 3 +- apps/website/ui/FeedItem.tsx | 77 + .../{components/feed => ui}/FeedLayout.tsx | 10 +- .../{components/feed => ui}/FeedList.tsx | 14 +- apps/website/ui/FilePicker.tsx | 43 + apps/website/ui/FilterGroup.tsx | 46 + .../races => ui}/FinishDistributionChart.tsx | 34 +- apps/website/ui/Footer.tsx | 106 ++ apps/website/ui/FormField.tsx | 4 +- apps/website/ui/FriendItem.tsx | 49 + apps/website/ui/FriendsList.tsx | 14 + .../profile => ui}/FriendsPreview.tsx | 21 +- apps/website/ui/FriendsSidebar.tsx | 71 + apps/website/ui/GoalCard.tsx | 41 + apps/website/ui/Grid.tsx | 15 +- apps/website/ui/Header.tsx | 2 +- apps/website/ui/Heading.tsx | 69 +- apps/website/ui/Hero.tsx | 31 +- .../charts => ui}/HorizontalBarChart.tsx | 3 +- apps/website/ui/HorizontalStatCard.tsx | 52 + apps/website/ui/HorizontalStatItem.tsx | 18 + apps/website/ui/Icon.tsx | 13 +- apps/website/ui/IconButton.tsx | 44 + apps/website/ui/Image.tsx | 22 +- apps/website/ui/InfoBanner.tsx | 10 +- apps/website/ui/InfoItem.tsx | 36 + .../races => ui}/InlinePenaltyButton.tsx | 18 +- apps/website/ui/Input.tsx | 51 +- apps/website/ui/JoinRequestItem.tsx | 76 + apps/website/ui/JoinRequestList.tsx | 14 + apps/website/ui/LandingHero.tsx | 171 +++ apps/website/ui/LandingItems.tsx | 54 + apps/website/ui/LatestResultsSidebar.tsx | 47 + apps/website/ui/LeaderboardItem.tsx | 116 ++ apps/website/ui/LeaderboardList.tsx | 16 + apps/website/ui/LeaderboardPreview.tsx | 105 ++ apps/website/ui/LeagueCard.tsx | 197 +++ apps/website/ui/LeagueCardWrapper.tsx | 178 +++ apps/website/ui/LeagueCover.tsx | 20 + apps/website/ui/LeagueCoverWrapper.tsx | 16 + apps/website/ui/LeagueHeader.tsx | 61 + .../profile => ui}/LeagueListItem.tsx | 9 +- apps/website/ui/LeagueLogo.tsx | 22 + apps/website/ui/LeagueLogoWrapper.tsx | 16 + apps/website/ui/LeagueMemberRow.tsx | 101 ++ apps/website/ui/LeagueMemberTable.tsx | 28 + apps/website/ui/LeagueSummaryCard.tsx | 121 ++ apps/website/ui/LeagueSummaryCardWrapper.tsx | 28 + apps/website/ui/Link.tsx | 42 +- apps/website/ui/LiveRaceBanner.tsx | 62 + apps/website/ui/LiveRaceItem.tsx | 43 + apps/website/ui/LiveRacesBanner.tsx | 59 + .../{components/profile => ui}/LiveryCard.tsx | 0 .../shared/state => ui}/LoadingWrapper.tsx | 115 +- apps/website/ui/MedalBadge.tsx | 40 + apps/website/ui/MilestoneItem.tsx | 21 + apps/website/ui/MiniStat.tsx | 18 + apps/website/ui/MockupStack.tsx | 2 +- apps/website/ui/Modal.tsx | 115 +- apps/website/ui/ModalIcon.tsx | 32 + apps/website/ui/NextRaceCard.tsx | 123 ++ apps/website/ui/NextRaceCardWrapper.tsx | 30 + .../OnboardingCardAccent.tsx | 0 .../onboarding => ui}/OnboardingContainer.tsx | 0 .../onboarding => ui}/OnboardingError.tsx | 0 .../onboarding => ui}/OnboardingForm.tsx | 0 .../onboarding => ui}/OnboardingHeader.tsx | 0 .../onboarding => ui}/OnboardingHelpText.tsx | 0 .../OnboardingNavigation.tsx | 2 +- apps/website/ui/PageHeader.tsx | 10 +- apps/website/ui/PageHero.tsx | 129 ++ .../shared/state => ui}/PageWrapper.tsx | 41 +- .../RacePagination.tsx => ui/Pagination.tsx} | 64 +- apps/website/ui/PenaltyFAB.tsx | 31 + apps/website/ui/PenaltyRow.tsx | 62 + apps/website/ui/PendingProtestsList.tsx | 68 + apps/website/ui/PerformanceMetrics.tsx | 86 ++ .../profile => ui}/PerformanceOverview.tsx | 17 +- apps/website/ui/PlaceholderImage.tsx | 3 +- apps/website/ui/Podium.tsx | 70 + apps/website/ui/PointsTable.tsx | 65 + apps/website/ui/PresetCard.tsx | 2 +- .../{components/profile => ui}/ProfileBio.tsx | 9 +- apps/website/ui/ProfileHeader.tsx | 98 ++ .../profile => ui}/ProfileHero.tsx | 14 +- .../profile => ui}/ProfileLayoutShell.tsx | 2 +- .../profile => ui}/ProfileStatGrid.tsx | 5 +- .../profile => ui}/ProfileTabs.tsx | 5 +- apps/website/ui/ProgressBar.tsx | 33 + apps/website/ui/ProtestCardWrapper.tsx | 58 + apps/website/ui/ProtestListItem.tsx | 118 ++ apps/website/ui/QuickActionItem.tsx | 51 + apps/website/ui/QuickActions.tsx | 29 + apps/website/ui/RaceCard.tsx | 167 +++ apps/website/ui/RaceCardWrapper.tsx | 45 + apps/website/ui/RaceDetailCard.tsx | 33 + apps/website/ui/RaceEntryList.tsx | 61 + apps/website/ui/RaceFilterBar.tsx | 64 + apps/website/ui/RaceFilterModal.tsx | 130 ++ apps/website/ui/RaceHero.tsx | 77 + apps/website/ui/RaceHeroWrapper.tsx | 34 + .../races => ui}/RaceJoinButton.tsx | 42 +- apps/website/ui/RaceList.tsx | 102 ++ apps/website/ui/RaceListItem.tsx | 143 ++ apps/website/ui/RaceListItemWrapper.tsx | 74 + apps/website/ui/RacePageHeader.tsx | 48 + apps/website/ui/RacePenaltyRowWrapper.tsx | 51 + apps/website/ui/RaceResultCard.tsx | 106 ++ apps/website/ui/RaceResultCardWrapper.tsx | 39 + apps/website/ui/RaceResultHero.tsx | 158 +++ apps/website/ui/RaceResultList.tsx | 14 + .../races => ui}/RaceResultRow.tsx | 68 +- apps/website/ui/RaceResultsHeader.tsx | 64 + apps/website/ui/RaceResultsTable.tsx | 264 ++++ apps/website/ui/RaceSidebar.tsx | 106 ++ apps/website/ui/RaceStats.tsx | 44 + apps/website/ui/RaceStatusBadge.tsx | 38 + apps/website/ui/RaceStewardingStats.tsx | 35 + apps/website/ui/RaceSummaryItem.tsx | 29 + apps/website/ui/RaceUserResultWrapper.tsx | 18 + .../profile => ui}/RacingProfile.tsx | 13 +- apps/website/ui/RangeField.tsx | 4 +- .../{components/drivers => ui}/RankBadge.tsx | 27 +- apps/website/ui/RankingList.tsx | 14 + apps/website/ui/RankingListItem.tsx | 70 + apps/website/ui/RankingsPodium.tsx | 141 ++ apps/website/ui/RankingsTable.tsx | 116 ++ apps/website/ui/RatingBreakdown.tsx | 118 ++ apps/website/ui/RatingComponent.tsx | 52 + apps/website/ui/RatingHistoryItem.tsx | 42 + apps/website/ui/RecentActivity.tsx | 84 ++ apps/website/ui/RecruitingTeamCard.tsx | 80 ++ apps/website/ui/RecruitingTeamGrid.tsx | 14 + apps/website/ui/RenewalItem.tsx | 57 + apps/website/ui/RulebookTabs.tsx | 27 + apps/website/ui/Section.tsx | 4 +- apps/website/ui/SectionHeader.tsx | 10 +- apps/website/ui/SegmentedControl.tsx | 3 +- apps/website/ui/Select.tsx | 56 +- apps/website/ui/SidebarActionLink.tsx | 38 + apps/website/ui/SidebarRaceItem.tsx | 44 + apps/website/ui/SkillDistribution.tsx | 88 ++ apps/website/ui/SkillLevelButton.tsx | 41 + apps/website/ui/SkillLevelHeader.tsx | 85 ++ apps/website/ui/SponsorActivityItem.tsx | 36 + apps/website/ui/SponsorHero.tsx | 158 +++ .../sponsors => ui}/SponsorLogo.tsx | 5 +- apps/website/ui/SponsorMetricCard.tsx | 48 + apps/website/ui/SponsorSlotCard.tsx | 46 + apps/website/ui/SponsorTierCard.tsx | 99 ++ apps/website/ui/SponsorshipCategoryCard.tsx | 55 + apps/website/ui/SponsorshipRequestItem.tsx | 165 +++ apps/website/ui/SponsorshipTierBadge.tsx | 26 + apps/website/ui/Stack.tsx | 55 +- apps/website/ui/StandingsItem.tsx | 48 + apps/website/ui/StandingsList.tsx | 33 + .../{components/dashboard => ui}/StatBox.tsx | 18 +- apps/website/ui/StatCard.tsx | 104 +- apps/website/ui/StatGridItem.tsx | 37 + apps/website/ui/StatItem.tsx | 19 + .../shared/state => ui}/StateContainer.tsx | 78 +- .../state => ui}/StatefulPageWrapper.tsx | 2 - apps/website/ui/StatusIndicator.tsx | 109 ++ apps/website/ui/StewardingTabs.tsx | 27 + apps/website/ui/SummaryItem.tsx | 51 + apps/website/ui/TabNavigation.tsx | 57 +- apps/website/ui/Table.tsx | 48 +- apps/website/ui/TeamCard.tsx | 174 +++ apps/website/ui/TeamCardWrapper.tsx | 137 ++ apps/website/ui/TeamFilter.tsx | 116 ++ apps/website/ui/TeamGrid.tsx | 14 + apps/website/ui/TeamHero.tsx | 79 ++ apps/website/ui/TeamHeroSection.tsx | 83 ++ apps/website/ui/TeamHeroSectionWrapper.tsx | 147 ++ apps/website/ui/TeamHeroStats.tsx | 48 + apps/website/ui/TeamIdentity.tsx | 43 + apps/website/ui/TeamLadderRow.tsx | 73 + apps/website/ui/TeamLadderTable.tsx | 26 + apps/website/ui/TeamLeaderboardItem.tsx | 127 ++ apps/website/ui/TeamLeaderboardPreview.tsx | 64 + .../ui/TeamLeaderboardPreviewWrapper.tsx | 81 ++ .../{components/teams => ui}/TeamLogo.tsx | 5 +- apps/website/ui/TeamMembershipCard.tsx | 64 + .../profile => ui}/TeamMembershipGrid.tsx | 11 +- apps/website/ui/TeamPodium.tsx | 145 ++ apps/website/ui/TeamRankingsTable.tsx | 104 ++ apps/website/ui/TeamRosterItem.tsx | 73 + apps/website/ui/TeamRosterList.tsx | 14 + .../teams => ui}/TeamSearchBar.tsx | 14 +- apps/website/ui/TeamStatItem.tsx | 26 + apps/website/ui/TeamTag.tsx | 21 + apps/website/ui/Text.tsx | 80 +- apps/website/ui/TextArea.tsx | 56 + .../teams => ui}/TopThreePodium.tsx | 107 +- .../{components/races => ui}/TrackImage.tsx | 5 +- apps/website/ui/UpcomingRaceItem.tsx | 56 + apps/website/ui/UpcomingRaces.tsx | 62 + apps/website/ui/UpcomingRacesList.tsx | 14 + .../races => ui}/UpcomingRacesSidebar.tsx | 26 +- .../teams => ui}/WhyJoinTeamSection.tsx | 23 +- apps/website/ui/WorkflowMockup.tsx | 172 +++ .../state/types.ts => ui/state-types.ts} | 6 +- 468 files changed, 24378 insertions(+), 17324 deletions(-) delete mode 100644 apps/website/components/dashboard/ActivityFeed.tsx delete mode 100644 apps/website/components/dashboard/ChampionshipStandings.tsx delete mode 100644 apps/website/components/dashboard/DashboardHero.tsx delete mode 100644 apps/website/components/dashboard/FriendsSidebar.tsx delete mode 100644 apps/website/components/dashboard/NextRaceCard.tsx delete mode 100644 apps/website/components/dashboard/UpcomingRaces.tsx delete mode 100644 apps/website/components/dev/Accordion.tsx delete mode 100644 apps/website/components/drivers/CareerHighlights.tsx delete mode 100644 apps/website/components/drivers/CategoryDistribution.tsx delete mode 100644 apps/website/components/drivers/DriverCard.tsx delete mode 100644 apps/website/components/drivers/DriverIdentity.tsx delete mode 100644 apps/website/components/drivers/DriverRankings.tsx delete mode 100644 apps/website/components/drivers/FeaturedDriverCard.tsx delete mode 100644 apps/website/components/drivers/LeaderboardPreview.tsx delete mode 100644 apps/website/components/drivers/PerformanceMetrics.tsx delete mode 100644 apps/website/components/drivers/RankingsPodium.tsx delete mode 100644 apps/website/components/drivers/RankingsTable.tsx delete mode 100644 apps/website/components/drivers/RatingBreakdown.tsx delete mode 100644 apps/website/components/drivers/RecentActivity.tsx delete mode 100644 apps/website/components/drivers/SkillDistribution.tsx delete mode 100644 apps/website/components/landing/AlternatingSection.tsx delete mode 100644 apps/website/components/landing/FeatureGrid.tsx delete mode 100644 apps/website/components/landing/Footer.tsx delete mode 100644 apps/website/components/landing/Hero.tsx delete mode 100644 apps/website/components/landing/LandingItems.tsx delete mode 100644 apps/website/components/leagues/LeagueCard.tsx delete mode 100644 apps/website/components/leagues/LeagueCover.tsx delete mode 100644 apps/website/components/leagues/LeagueLogo.tsx delete mode 100644 apps/website/components/leagues/LeagueSchedule.test.tsx delete mode 100644 apps/website/components/leagues/LeagueSummaryCard.tsx delete mode 100644 apps/website/components/leagues/PenaltyFAB.tsx delete mode 100644 apps/website/components/leagues/PendingProtestsList.tsx delete mode 100644 apps/website/components/leagues/PointsTable.tsx delete mode 100644 apps/website/components/leagues/RulebookTabs.tsx delete mode 100644 apps/website/components/profile/CareerStats.tsx delete mode 100644 apps/website/components/profile/DriverRatingPill.tsx delete mode 100644 apps/website/components/profile/DriverSummaryPill.tsx delete mode 100644 apps/website/components/profile/ProfileHeader.tsx delete mode 100644 apps/website/components/races/LatestResultsSidebar.tsx delete mode 100644 apps/website/components/races/LiveRaceBanner.tsx delete mode 100644 apps/website/components/races/LiveRacesBanner.tsx delete mode 100644 apps/website/components/races/ProtestCard.tsx delete mode 100644 apps/website/components/races/QuickActions.tsx delete mode 100644 apps/website/components/races/RaceCard.tsx delete mode 100644 apps/website/components/races/RaceDetailCard.tsx delete mode 100644 apps/website/components/races/RaceEntryList.tsx delete mode 100644 apps/website/components/races/RaceFilterBar.tsx delete mode 100644 apps/website/components/races/RaceFilterModal.tsx delete mode 100644 apps/website/components/races/RaceHero.tsx delete mode 100644 apps/website/components/races/RaceList.tsx delete mode 100644 apps/website/components/races/RaceListItem.tsx delete mode 100644 apps/website/components/races/RacePageHeader.tsx delete mode 100644 apps/website/components/races/RacePenaltyRow.tsx delete mode 100644 apps/website/components/races/RaceResultCard.tsx delete mode 100644 apps/website/components/races/RaceResultsHeader.tsx delete mode 100644 apps/website/components/races/RaceSidebar.tsx delete mode 100644 apps/website/components/races/RaceStats.tsx delete mode 100644 apps/website/components/races/RaceStewardingStats.tsx delete mode 100644 apps/website/components/races/RaceUserResult.tsx delete mode 100644 apps/website/components/races/ResultsTable.tsx delete mode 100644 apps/website/components/races/SidebarRaceItem.tsx delete mode 100644 apps/website/components/races/StewardingTabs.tsx delete mode 100644 apps/website/components/shared/HeroSection.tsx delete mode 100644 apps/website/components/shared/StatusBadge.tsx delete mode 100644 apps/website/components/shared/state/ErrorDisplay.tsx delete mode 100644 apps/website/components/sponsors/SponsorHero.tsx delete mode 100644 apps/website/components/teams/StatItem.tsx delete mode 100644 apps/website/components/teams/TeamCard.tsx delete mode 100644 apps/website/components/teams/TeamHero.tsx delete mode 100644 apps/website/components/teams/TeamHeroSection.tsx delete mode 100644 apps/website/components/teams/TeamLadderRow.tsx delete mode 100644 apps/website/components/teams/TeamLeaderboardPreview.tsx delete mode 100644 apps/website/components/teams/TeamRankingsTable.tsx create mode 100644 apps/website/lib/services/error/ErrorAnalyticsService.ts create mode 100644 apps/website/lib/services/league/LeagueActivityService.ts create mode 100644 apps/website/lib/utilities/roster-utils.ts create mode 100644 apps/website/ui/Accordion.tsx create mode 100644 apps/website/ui/AchievementCard.tsx rename apps/website/{components/profile => ui}/AchievementGrid.tsx (96%) create mode 100644 apps/website/ui/ActiveDriverCard.tsx create mode 100644 apps/website/ui/ActivityFeed.tsx create mode 100644 apps/website/ui/ActivityFeedItem.tsx create mode 100644 apps/website/ui/ActivityFeedList.tsx create mode 100644 apps/website/ui/ActivityItem.tsx create mode 100644 apps/website/ui/AlternatingSection.tsx create mode 100644 apps/website/ui/AvailableLeagueCard.tsx create mode 100644 apps/website/ui/BenefitCard.tsx create mode 100644 apps/website/ui/BorderTabs.tsx rename apps/website/{components/layout => ui}/Breadcrumbs.tsx (100%) create mode 100644 apps/website/ui/CareerHighlights.tsx create mode 100644 apps/website/ui/CareerStats.tsx create mode 100644 apps/website/ui/CategoryDistribution.tsx create mode 100644 apps/website/ui/CategoryDistributionCard.tsx create mode 100644 apps/website/ui/ChampionshipStandings.tsx create mode 100644 apps/website/ui/ChampionshipStandingsList.tsx create mode 100644 apps/website/ui/Checkbox.tsx rename apps/website/{components/charts => ui}/CircularProgress.tsx (97%) create mode 100644 apps/website/ui/DangerZone.tsx create mode 100644 apps/website/ui/DashboardHero.tsx create mode 100644 apps/website/ui/DashboardHeroWrapper.tsx create mode 100644 apps/website/ui/DateHeader.tsx rename apps/website/{components/dev => ui}/DebugModeToggle.tsx (52%) rename apps/website/{components/landing => ui}/DiscordCTA.tsx (98%) create mode 100644 apps/website/ui/DriverCard.tsx create mode 100644 apps/website/ui/DriverEntryRow.tsx create mode 100644 apps/website/ui/DriverIdentity.tsx create mode 100644 apps/website/ui/DriverRankings.tsx create mode 100644 apps/website/ui/DriverRatingPill.tsx create mode 100644 apps/website/ui/DriverStats.tsx create mode 100644 apps/website/ui/DriverSummaryPill.tsx create mode 100644 apps/website/ui/DriverSummaryPillWrapper.tsx rename apps/website/{components/drivers => ui}/DriversSearch.tsx (58%) rename apps/website/{components/shared/state => ui}/EmptyState.tsx (98%) create mode 100644 apps/website/ui/ErrorDisplay.tsx create mode 100644 apps/website/ui/FeatureGrid.tsx create mode 100644 apps/website/ui/FeaturedDriverCard.tsx rename apps/website/{components/feed => ui}/FeedEmptyState.tsx (86%) create mode 100644 apps/website/ui/FeedItem.tsx rename apps/website/{components/feed => ui}/FeedLayout.tsx (84%) rename apps/website/{components/feed => ui}/FeedList.tsx (59%) create mode 100644 apps/website/ui/FilePicker.tsx create mode 100644 apps/website/ui/FilterGroup.tsx rename apps/website/{components/races => ui}/FinishDistributionChart.tsx (56%) create mode 100644 apps/website/ui/Footer.tsx create mode 100644 apps/website/ui/FriendItem.tsx create mode 100644 apps/website/ui/FriendsList.tsx rename apps/website/{components/profile => ui}/FriendsPreview.tsx (98%) create mode 100644 apps/website/ui/FriendsSidebar.tsx create mode 100644 apps/website/ui/GoalCard.tsx rename apps/website/{components/charts => ui}/HorizontalBarChart.tsx (95%) create mode 100644 apps/website/ui/HorizontalStatCard.tsx create mode 100644 apps/website/ui/HorizontalStatItem.tsx create mode 100644 apps/website/ui/IconButton.tsx create mode 100644 apps/website/ui/InfoItem.tsx rename apps/website/{components/races => ui}/InlinePenaltyButton.tsx (64%) create mode 100644 apps/website/ui/JoinRequestItem.tsx create mode 100644 apps/website/ui/JoinRequestList.tsx create mode 100644 apps/website/ui/LandingHero.tsx create mode 100644 apps/website/ui/LandingItems.tsx create mode 100644 apps/website/ui/LatestResultsSidebar.tsx create mode 100644 apps/website/ui/LeaderboardItem.tsx create mode 100644 apps/website/ui/LeaderboardList.tsx create mode 100644 apps/website/ui/LeaderboardPreview.tsx create mode 100644 apps/website/ui/LeagueCard.tsx create mode 100644 apps/website/ui/LeagueCardWrapper.tsx create mode 100644 apps/website/ui/LeagueCover.tsx create mode 100644 apps/website/ui/LeagueCoverWrapper.tsx create mode 100644 apps/website/ui/LeagueHeader.tsx rename apps/website/{components/profile => ui}/LeagueListItem.tsx (97%) create mode 100644 apps/website/ui/LeagueLogo.tsx create mode 100644 apps/website/ui/LeagueLogoWrapper.tsx create mode 100644 apps/website/ui/LeagueMemberRow.tsx create mode 100644 apps/website/ui/LeagueMemberTable.tsx create mode 100644 apps/website/ui/LeagueSummaryCard.tsx create mode 100644 apps/website/ui/LeagueSummaryCardWrapper.tsx create mode 100644 apps/website/ui/LiveRaceBanner.tsx create mode 100644 apps/website/ui/LiveRaceItem.tsx create mode 100644 apps/website/ui/LiveRacesBanner.tsx rename apps/website/{components/profile => ui}/LiveryCard.tsx (100%) rename apps/website/{components/shared/state => ui}/LoadingWrapper.tsx (63%) create mode 100644 apps/website/ui/MedalBadge.tsx create mode 100644 apps/website/ui/MilestoneItem.tsx create mode 100644 apps/website/ui/MiniStat.tsx create mode 100644 apps/website/ui/ModalIcon.tsx create mode 100644 apps/website/ui/NextRaceCard.tsx create mode 100644 apps/website/ui/NextRaceCardWrapper.tsx rename apps/website/{components/onboarding => ui}/OnboardingCardAccent.tsx (100%) rename apps/website/{components/onboarding => ui}/OnboardingContainer.tsx (100%) rename apps/website/{components/onboarding => ui}/OnboardingError.tsx (100%) rename apps/website/{components/onboarding => ui}/OnboardingForm.tsx (100%) rename apps/website/{components/onboarding => ui}/OnboardingHeader.tsx (100%) rename apps/website/{components/onboarding => ui}/OnboardingHelpText.tsx (100%) rename apps/website/{components/onboarding => ui}/OnboardingNavigation.tsx (97%) create mode 100644 apps/website/ui/PageHero.tsx rename apps/website/{components/shared/state => ui}/PageWrapper.tsx (87%) rename apps/website/{components/races/RacePagination.tsx => ui/Pagination.tsx} (52%) create mode 100644 apps/website/ui/PenaltyFAB.tsx create mode 100644 apps/website/ui/PenaltyRow.tsx create mode 100644 apps/website/ui/PendingProtestsList.tsx create mode 100644 apps/website/ui/PerformanceMetrics.tsx rename apps/website/{components/profile => ui}/PerformanceOverview.tsx (93%) create mode 100644 apps/website/ui/Podium.tsx create mode 100644 apps/website/ui/PointsTable.tsx rename apps/website/{components/profile => ui}/ProfileBio.tsx (93%) create mode 100644 apps/website/ui/ProfileHeader.tsx rename apps/website/{components/profile => ui}/ProfileHero.tsx (96%) rename apps/website/{components/profile => ui}/ProfileLayoutShell.tsx (68%) rename apps/website/{components/profile => ui}/ProfileStatGrid.tsx (95%) rename apps/website/{components/profile => ui}/ProfileTabs.tsx (93%) create mode 100644 apps/website/ui/ProgressBar.tsx create mode 100644 apps/website/ui/ProtestCardWrapper.tsx create mode 100644 apps/website/ui/ProtestListItem.tsx create mode 100644 apps/website/ui/QuickActionItem.tsx create mode 100644 apps/website/ui/QuickActions.tsx create mode 100644 apps/website/ui/RaceCard.tsx create mode 100644 apps/website/ui/RaceCardWrapper.tsx create mode 100644 apps/website/ui/RaceDetailCard.tsx create mode 100644 apps/website/ui/RaceEntryList.tsx create mode 100644 apps/website/ui/RaceFilterBar.tsx create mode 100644 apps/website/ui/RaceFilterModal.tsx create mode 100644 apps/website/ui/RaceHero.tsx create mode 100644 apps/website/ui/RaceHeroWrapper.tsx rename apps/website/{components/races => ui}/RaceJoinButton.tsx (71%) create mode 100644 apps/website/ui/RaceList.tsx create mode 100644 apps/website/ui/RaceListItem.tsx create mode 100644 apps/website/ui/RaceListItemWrapper.tsx create mode 100644 apps/website/ui/RacePageHeader.tsx create mode 100644 apps/website/ui/RacePenaltyRowWrapper.tsx create mode 100644 apps/website/ui/RaceResultCard.tsx create mode 100644 apps/website/ui/RaceResultCardWrapper.tsx create mode 100644 apps/website/ui/RaceResultHero.tsx create mode 100644 apps/website/ui/RaceResultList.tsx rename apps/website/{components/races => ui}/RaceResultRow.tsx (52%) create mode 100644 apps/website/ui/RaceResultsHeader.tsx create mode 100644 apps/website/ui/RaceResultsTable.tsx create mode 100644 apps/website/ui/RaceSidebar.tsx create mode 100644 apps/website/ui/RaceStats.tsx create mode 100644 apps/website/ui/RaceStatusBadge.tsx create mode 100644 apps/website/ui/RaceStewardingStats.tsx create mode 100644 apps/website/ui/RaceSummaryItem.tsx create mode 100644 apps/website/ui/RaceUserResultWrapper.tsx rename apps/website/{components/profile => ui}/RacingProfile.tsx (96%) rename apps/website/{components/drivers => ui}/RankBadge.tsx (62%) create mode 100644 apps/website/ui/RankingList.tsx create mode 100644 apps/website/ui/RankingListItem.tsx create mode 100644 apps/website/ui/RankingsPodium.tsx create mode 100644 apps/website/ui/RankingsTable.tsx create mode 100644 apps/website/ui/RatingBreakdown.tsx create mode 100644 apps/website/ui/RatingComponent.tsx create mode 100644 apps/website/ui/RatingHistoryItem.tsx create mode 100644 apps/website/ui/RecentActivity.tsx create mode 100644 apps/website/ui/RecruitingTeamCard.tsx create mode 100644 apps/website/ui/RecruitingTeamGrid.tsx create mode 100644 apps/website/ui/RenewalItem.tsx create mode 100644 apps/website/ui/RulebookTabs.tsx create mode 100644 apps/website/ui/SidebarActionLink.tsx create mode 100644 apps/website/ui/SidebarRaceItem.tsx create mode 100644 apps/website/ui/SkillDistribution.tsx create mode 100644 apps/website/ui/SkillLevelButton.tsx create mode 100644 apps/website/ui/SkillLevelHeader.tsx create mode 100644 apps/website/ui/SponsorActivityItem.tsx create mode 100644 apps/website/ui/SponsorHero.tsx rename apps/website/{components/sponsors => ui}/SponsorLogo.tsx (91%) create mode 100644 apps/website/ui/SponsorMetricCard.tsx create mode 100644 apps/website/ui/SponsorSlotCard.tsx create mode 100644 apps/website/ui/SponsorTierCard.tsx create mode 100644 apps/website/ui/SponsorshipCategoryCard.tsx create mode 100644 apps/website/ui/SponsorshipRequestItem.tsx create mode 100644 apps/website/ui/SponsorshipTierBadge.tsx create mode 100644 apps/website/ui/StandingsItem.tsx create mode 100644 apps/website/ui/StandingsList.tsx rename apps/website/{components/dashboard => ui}/StatBox.tsx (64%) create mode 100644 apps/website/ui/StatGridItem.tsx create mode 100644 apps/website/ui/StatItem.tsx rename apps/website/{components/shared/state => ui}/StateContainer.tsx (85%) rename apps/website/{components/shared/state => ui}/StatefulPageWrapper.tsx (96%) create mode 100644 apps/website/ui/StatusIndicator.tsx create mode 100644 apps/website/ui/StewardingTabs.tsx create mode 100644 apps/website/ui/SummaryItem.tsx create mode 100644 apps/website/ui/TeamCard.tsx create mode 100644 apps/website/ui/TeamCardWrapper.tsx create mode 100644 apps/website/ui/TeamFilter.tsx create mode 100644 apps/website/ui/TeamGrid.tsx create mode 100644 apps/website/ui/TeamHero.tsx create mode 100644 apps/website/ui/TeamHeroSection.tsx create mode 100644 apps/website/ui/TeamHeroSectionWrapper.tsx create mode 100644 apps/website/ui/TeamHeroStats.tsx create mode 100644 apps/website/ui/TeamIdentity.tsx create mode 100644 apps/website/ui/TeamLadderRow.tsx create mode 100644 apps/website/ui/TeamLadderTable.tsx create mode 100644 apps/website/ui/TeamLeaderboardItem.tsx create mode 100644 apps/website/ui/TeamLeaderboardPreview.tsx create mode 100644 apps/website/ui/TeamLeaderboardPreviewWrapper.tsx rename apps/website/{components/teams => ui}/TeamLogo.tsx (90%) create mode 100644 apps/website/ui/TeamMembershipCard.tsx rename apps/website/{components/profile => ui}/TeamMembershipGrid.tsx (96%) create mode 100644 apps/website/ui/TeamPodium.tsx create mode 100644 apps/website/ui/TeamRankingsTable.tsx create mode 100644 apps/website/ui/TeamRosterItem.tsx create mode 100644 apps/website/ui/TeamRosterList.tsx rename apps/website/{components/teams => ui}/TeamSearchBar.tsx (80%) create mode 100644 apps/website/ui/TeamStatItem.tsx create mode 100644 apps/website/ui/TeamTag.tsx create mode 100644 apps/website/ui/TextArea.tsx rename apps/website/{components/teams => ui}/TopThreePodium.tsx (51%) rename apps/website/{components/races => ui}/TrackImage.tsx (91%) create mode 100644 apps/website/ui/UpcomingRaceItem.tsx create mode 100644 apps/website/ui/UpcomingRaces.tsx create mode 100644 apps/website/ui/UpcomingRacesList.tsx rename apps/website/{components/races => ui}/UpcomingRacesSidebar.tsx (56%) rename apps/website/{components/teams => ui}/WhyJoinTeamSection.tsx (69%) create mode 100644 apps/website/ui/WorkflowMockup.tsx rename apps/website/{components/shared/state/types.ts => ui/state-types.ts} (97%) diff --git a/apps/website/app/admin/layout.tsx b/apps/website/app/admin/layout.tsx index 3e6c395b0..74f7696ed 100644 --- a/apps/website/app/admin/layout.tsx +++ b/apps/website/app/admin/layout.tsx @@ -1,7 +1,7 @@ import { headers } from 'next/headers'; import { redirect } from 'next/navigation'; import { createRouteGuard } from '@/lib/auth/createRouteGuard'; -import Section from '@/ui/Section'; +import { Section } from '@/ui/Section'; interface AdminLayoutProps { children: React.ReactNode; diff --git a/apps/website/app/leagues/LeaguesClient.tsx b/apps/website/app/leagues/LeaguesClient.tsx index 6f1c40b77..44ccdce84 100644 --- a/apps/website/app/leagues/LeaguesClient.tsx +++ b/apps/website/app/leagues/LeaguesClient.tsx @@ -18,7 +18,7 @@ import { Target, Timer, } from 'lucide-react'; -import LeagueCard from '@/components/leagues/LeagueCard'; +import { LeagueCard } from '@/ui/LeagueCardWrapper'; import { Button } from '@/ui/Button'; import { Card } from '@/ui/Card'; import { Input } from '@/ui/Input'; @@ -29,7 +29,7 @@ import { Stack } from '@/ui/Stack'; import { Text } from '@/ui/Text'; import { Grid } from '@/ui/Grid'; import { GridItem } from '@/ui/GridItem'; -import { HeroSection } from '@/components/shared/HeroSection'; +import { PageHero } from '@/ui/PageHero'; import type { LeaguesViewData } from '@/lib/view-data/LeaguesViewData'; import type { LeagueSummaryViewModel } from '@/lib/view-models/LeagueSummaryViewModel'; @@ -468,7 +468,7 @@ export function LeaguesClient({ return ( {/* Hero Section */} - setFormData({ ...formData, contactEmail: e.target.value })} placeholder="sponsor@company.com" - error={!!errors.contactEmail} + variant={errors.contactEmail ? 'error' : 'default'} errorMessage={errors.contactEmail} /> @@ -482,7 +482,7 @@ export default function SponsorSignupPage() { value={formData.password} onChange={(e) => setFormData({ ...formData, password: e.target.value })} placeholder="••••••••" - error={!!errors.password} + variant={errors.password ? 'error' : 'default'} errorMessage={errors.password} /> @@ -567,7 +567,7 @@ export default function SponsorSignupPage() { value={formData.companyName} onChange={(e) => setFormData({ ...formData, companyName: e.target.value })} placeholder="Your company name" - error={!!errors.companyName} + variant={errors.companyName ? 'error' : 'default'} errorMessage={errors.companyName} /> @@ -584,7 +584,7 @@ export default function SponsorSignupPage() { value={formData.contactEmail} onChange={(e) => setFormData({ ...formData, contactEmail: e.target.value })} placeholder="sponsor@company.com" - error={!!errors.contactEmail} + variant={errors.contactEmail ? 'error' : 'default'} errorMessage={errors.contactEmail} /> @@ -688,7 +688,7 @@ export default function SponsorSignupPage() { value={formData.password} onChange={(e) => setFormData({ ...formData, password: e.target.value })} placeholder="Min. 8 characters" - error={!!errors.password} + variant={errors.password ? 'error' : 'default'} errorMessage={errors.password} /> @@ -702,7 +702,7 @@ export default function SponsorSignupPage() { value={formData.confirmPassword} onChange={(e) => setFormData({ ...formData, confirmPassword: e.target.value })} placeholder="Confirm password" - error={!!errors.confirmPassword} + variant={errors.confirmPassword ? 'error' : 'default'} errorMessage={errors.confirmPassword} /> diff --git a/apps/website/app/teams/leaderboard/TeamLeaderboardPageWrapper.tsx b/apps/website/app/teams/leaderboard/TeamLeaderboardPageWrapper.tsx index f503c9a38..b4c9ec820 100644 --- a/apps/website/app/teams/leaderboard/TeamLeaderboardPageWrapper.tsx +++ b/apps/website/app/teams/leaderboard/TeamLeaderboardPageWrapper.tsx @@ -1,7 +1,7 @@ 'use client'; import { useRouter } from 'next/navigation'; -import TeamLeaderboardTemplate from '@/templates/TeamLeaderboardTemplate'; +import { TeamLeaderboardTemplate } from '@/templates/TeamLeaderboardTemplate'; import { useState } from 'react'; import type { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel'; diff --git a/apps/website/components/AppWrapper.tsx b/apps/website/components/AppWrapper.tsx index afa9a8582..9f12867cf 100644 --- a/apps/website/components/AppWrapper.tsx +++ b/apps/website/components/AppWrapper.tsx @@ -4,10 +4,10 @@ import { ContainerProvider } from '@/lib/di/providers/ContainerProvider'; import { QueryClientProvider } from '@/lib/providers/QueryClientProvider'; import { AuthProvider } from '@/lib/auth/AuthContext'; import { FeatureFlagProvider } from '@/lib/feature/FeatureFlagProvider'; -import NotificationProvider from '@/components/notifications/NotificationProvider'; +import { NotificationProvider } from '@/components/notifications/NotificationProvider'; import { NotificationIntegration } from '@/components/errors/NotificationIntegration'; import { EnhancedErrorBoundary } from '@/components/errors/EnhancedErrorBoundary'; -import DevToolbar from '@/components/dev/DevToolbar'; +import { DevToolbar } from '@/components/dev/DevToolbar'; import React from 'react'; interface AppWrapperProps { diff --git a/apps/website/components/auth/AuthWorkflowMockup.tsx b/apps/website/components/auth/AuthWorkflowMockup.tsx index 6cfa9bf31..6f5654b4c 100644 --- a/apps/website/components/auth/AuthWorkflowMockup.tsx +++ b/apps/website/components/auth/AuthWorkflowMockup.tsx @@ -1,29 +1,14 @@ 'use client'; -import { motion, useReducedMotion, AnimatePresence } from 'framer-motion'; -import { useEffect, useState } from 'react'; +import React from 'react'; import { UserPlus, Link as LinkIcon, Settings, Trophy, Car, - CheckCircle2, - LucideIcon } from 'lucide-react'; -import { Box } from '@/ui/Box'; -import { Stack } from '@/ui/Stack'; -import { Text } from '@/ui/Text'; -import { Icon } from '@/ui/Icon'; -import { Surface } from '@/ui/Surface'; - -interface WorkflowStep { - id: number; - icon: LucideIcon; - title: string; - description: string; - color: string; -} +import { WorkflowMockup, WorkflowStep } from '@/ui/WorkflowMockup'; const WORKFLOW_STEPS: WorkflowStep[] = [ { @@ -64,123 +49,5 @@ const WORKFLOW_STEPS: WorkflowStep[] = [ ]; export function AuthWorkflowMockup() { - const shouldReduceMotion = useReducedMotion(); - const [isMounted, setIsMounted] = useState(false); - const [activeStep, setActiveStep] = useState(0); - - useEffect(() => { - setIsMounted(true); - }, []); - - useEffect(() => { - if (!isMounted) return; - - const interval = setInterval(() => { - setActiveStep((prev) => (prev + 1) % WORKFLOW_STEPS.length); - }, 3000); - - return () => clearInterval(interval); - }, [isMounted]); - - if (!isMounted) { - return ( - - - - {WORKFLOW_STEPS.map((step) => ( - - - - - {step.title} - - ))} - - - - ); - } - - return ( - - - {/* Connection Lines */} - - - - - - - {/* Steps */} - - {WORKFLOW_STEPS.map((step, index) => { - const isActive = index === activeStep; - const isCompleted = index < activeStep; - const StepIcon = step.icon; - - return ( - setActiveStep(index)} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - - {isCompleted ? ( - - ) : ( - - )} - - - - ); - })} - - - {/* Active Step Preview - Mobile */} - - - - - Step {activeStep + 1}: {WORKFLOW_STEPS[activeStep]?.title || ''} - - - {WORKFLOW_STEPS[activeStep]?.description || ''} - - - - - - - ); + return ; } diff --git a/apps/website/components/auth/UserRolesPreview.tsx b/apps/website/components/auth/UserRolesPreview.tsx index cc311b1b1..a276d1bc2 100644 --- a/apps/website/components/auth/UserRolesPreview.tsx +++ b/apps/website/components/auth/UserRolesPreview.tsx @@ -52,7 +52,9 @@ export function UserRolesPreview({ variant = 'full' }: UserRolesPreviewProps) { justifyContent="center" mb={1} > - + + + {role.title} @@ -89,7 +91,9 @@ export function UserRolesPreview({ variant = 'full' }: UserRolesPreviewProps) { alignItems="center" justifyContent="center" > - + + + {role.title} diff --git a/apps/website/components/dashboard/ActivityFeed.tsx b/apps/website/components/dashboard/ActivityFeed.tsx deleted file mode 100644 index 39cbad1f1..000000000 --- a/apps/website/components/dashboard/ActivityFeed.tsx +++ /dev/null @@ -1,62 +0,0 @@ -'use client'; - -import React from 'react'; -import { Activity } from 'lucide-react'; -import { Card } from '@/ui/Card'; -import { Stack } from '@/ui/Stack'; -import { Heading } from '@/ui/Heading'; -import { Box } from '@/ui/Box'; -import { Text } from '@/ui/Text'; -import { Link } from '@/ui/Link'; - -interface FeedItem { - id: string; - headline: string; - body?: string; - formattedTime: string; - ctaHref?: string; - ctaLabel?: string; -} - -interface ActivityFeedProps { - items: FeedItem[]; - hasItems: boolean; -} - -export function ActivityFeed({ items, hasItems }: ActivityFeedProps) { - return ( - - - }> - Recent Activity - - {hasItems ? ( - - {items.slice(0, 5).map((item) => ( - - - {item.headline} - {item.body && {item.body}} - {item.formattedTime} - - {item.ctaHref && item.ctaLabel && ( - - - {item.ctaLabel} - - - )} - - ))} - - ) : ( - - - No activity yet - Join leagues and add friends to see activity here - - )} - - - ); -} diff --git a/apps/website/components/dashboard/ChampionshipStandings.tsx b/apps/website/components/dashboard/ChampionshipStandings.tsx deleted file mode 100644 index 3b1da05f7..000000000 --- a/apps/website/components/dashboard/ChampionshipStandings.tsx +++ /dev/null @@ -1,56 +0,0 @@ -'use client'; - -import React from 'react'; -import { Award, ChevronRight } from 'lucide-react'; -import { Card } from '@/ui/Card'; -import { Stack } from '@/ui/Stack'; -import { Heading } from '@/ui/Heading'; -import { Box } from '@/ui/Box'; -import { Text } from '@/ui/Text'; -import { Link } from '@/ui/Link'; -import { routes } from '@/lib/routing/RouteConfig'; - -interface Standing { - leagueId: string; - leagueName: string; - position: string; - points: string; - totalDrivers: string; -} - -interface ChampionshipStandingsProps { - standings: Standing[]; -} - -export function ChampionshipStandings({ standings }: ChampionshipStandingsProps) { - return ( - - - - }> - Your Championship Standings - - - - - View all - - - - - - - {standings.map((summary) => ( - - - {summary.leagueName} - Position {summary.position} • {summary.points} points - - {summary.totalDrivers} drivers - - ))} - - - - ); -} diff --git a/apps/website/components/dashboard/DashboardHero.tsx b/apps/website/components/dashboard/DashboardHero.tsx deleted file mode 100644 index 672399b43..000000000 --- a/apps/website/components/dashboard/DashboardHero.tsx +++ /dev/null @@ -1,101 +0,0 @@ -'use client'; - -import React from 'react'; -import { Trophy, Medal, Target, Users, Flag, User } from 'lucide-react'; -import { Box } from '@/ui/Box'; -import { Stack } from '@/ui/Stack'; -import { Text } from '@/ui/Text'; -import { Heading } from '@/ui/Heading'; -import { Image } from '@/ui/Image'; -import { Button } from '@/ui/Button'; -import { Link } from '@/ui/Link'; -import { Surface } from '@/ui/Surface'; -import { StatBox } from './StatBox'; -import { routes } from '@/lib/routing/RouteConfig'; - -interface DashboardHeroProps { - currentDriver: { - name: string; - avatarUrl: string; - country: string; - rating: string | number; - rank: string | number; - totalRaces: string | number; - wins: string | number; - podiums: string | number; - consistency: string; - }; - activeLeaguesCount: string | number; -} - -export function DashboardHero({ currentDriver, activeLeaguesCount }: DashboardHeroProps) { - return ( - - {/* Background Pattern */} - - - - - - {/* Welcome Message */} - - - - - {currentDriver.name} - - - - - - Good morning, - - {currentDriver.name} - {currentDriver.country} - - - - {currentDriver.rating} - - - #{currentDriver.rank} - - {currentDriver.totalRaces} races completed - - - - - {/* Quick Actions */} - - - - - - - - - - - {/* Quick Stats Row */} - - {/* At md this should be 4 columns */} - - - - - - - - - ); -} diff --git a/apps/website/components/dashboard/FriendsSidebar.tsx b/apps/website/components/dashboard/FriendsSidebar.tsx deleted file mode 100644 index 129028c89..000000000 --- a/apps/website/components/dashboard/FriendsSidebar.tsx +++ /dev/null @@ -1,83 +0,0 @@ -'use client'; - -import React from 'react'; -import { Users, UserPlus } from 'lucide-react'; -import { Card } from '@/ui/Card'; -import { Stack } from '@/ui/Stack'; -import { Heading } from '@/ui/Heading'; -import { Box } from '@/ui/Box'; -import { Text } from '@/ui/Text'; -import { Link } from '@/ui/Link'; -import { Image } from '@/ui/Image'; -import { Button } from '@/ui/Button'; -import { routes } from '@/lib/routing/RouteConfig'; - -interface Friend { - id: string; - name: string; - avatarUrl: string; - country: string; -} - -interface FriendsSidebarProps { - friends: Friend[]; - hasFriends: boolean; -} - -export function FriendsSidebar({ friends, hasFriends }: FriendsSidebarProps) { - return ( - - - - }> - Friends - - {friends.length} friends - - {hasFriends ? ( - - {friends.slice(0, 6).map((friend) => ( - - - {friend.name} - - - {friend.name} - {friend.country} - - - ))} - {friends.length > 6 && ( - - - +{friends.length - 6} more - - - )} - - ) : ( - - - No friends yet - - - - - - - )} - - - ); -} diff --git a/apps/website/components/dashboard/NextRaceCard.tsx b/apps/website/components/dashboard/NextRaceCard.tsx deleted file mode 100644 index 21b53d4bd..000000000 --- a/apps/website/components/dashboard/NextRaceCard.tsx +++ /dev/null @@ -1,74 +0,0 @@ -'use client'; - -import React from 'react'; -import { Calendar, Clock, ChevronRight } from 'lucide-react'; -import { Box } from '@/ui/Box'; -import { Stack } from '@/ui/Stack'; -import { Text } from '@/ui/Text'; -import { Heading } from '@/ui/Heading'; -import { Button } from '@/ui/Button'; -import { Link } from '@/ui/Link'; -import { Surface } from '@/ui/Surface'; - -interface NextRaceCardProps { - nextRace: { - id: string; - track: string; - car: string; - formattedDate: string; - formattedTime: string; - timeUntil: string; - isMyLeague: boolean; - }; -} - -export function NextRaceCard({ nextRace }: NextRaceCardProps) { - return ( - - - - - - Next Race - - {nextRace.isMyLeague && ( - - Your League - - )} - - - - - {nextRace.track} - {nextRace.car} - - - - {nextRace.formattedDate} - - - - {nextRace.formattedTime} - - - - - - - Starts in - {nextRace.timeUntil} - - - - - - - - - - - ); -} diff --git a/apps/website/components/dashboard/UpcomingRaces.tsx b/apps/website/components/dashboard/UpcomingRaces.tsx deleted file mode 100644 index a3531e7cf..000000000 --- a/apps/website/components/dashboard/UpcomingRaces.tsx +++ /dev/null @@ -1,68 +0,0 @@ -'use client'; - -import React from 'react'; -import { Calendar } from 'lucide-react'; -import { Card } from '@/ui/Card'; -import { Stack } from '@/ui/Stack'; -import { Heading } from '@/ui/Heading'; -import { Box } from '@/ui/Box'; -import { Text } from '@/ui/Text'; -import { Link } from '@/ui/Link'; -import { routes } from '@/lib/routing/RouteConfig'; - -interface UpcomingRace { - id: string; - track: string; - car: string; - formattedDate: string; - formattedTime: string; - isMyLeague: boolean; -} - -interface UpcomingRacesProps { - races: UpcomingRace[]; - hasRaces: boolean; -} - -export function UpcomingRaces({ races, hasRaces }: UpcomingRacesProps) { - return ( - - - - }> - Upcoming Races - - - - View all - - - - {hasRaces ? ( - - {races.slice(0, 5).map((race) => ( - - {race.track} - {race.car} - - {race.formattedDate} - - {race.formattedTime} - - {race.isMyLeague && ( - - Your League - - )} - - ))} - - ) : ( - - No upcoming races - - )} - - - ); -} diff --git a/apps/website/components/dev/Accordion.tsx b/apps/website/components/dev/Accordion.tsx deleted file mode 100644 index a091ce214..000000000 --- a/apps/website/components/dev/Accordion.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client'; - -import { ReactNode } from 'react'; -import { ChevronDown, ChevronUp } from 'lucide-react'; - -interface AccordionProps { - title: string; - icon: ReactNode; - children: ReactNode; - isOpen: boolean; - onToggle: () => void; -} - -export function Accordion({ title, icon, children, isOpen, onToggle }: AccordionProps) { - return ( -
- - - {isOpen && ( -
- {children} -
- )} -
- ); -} \ No newline at end of file diff --git a/apps/website/components/dev/DevToolbar.tsx b/apps/website/components/dev/DevToolbar.tsx index 194400ad4..0a2735d04 100644 --- a/apps/website/components/dev/DevToolbar.tsx +++ b/apps/website/components/dev/DevToolbar.tsx @@ -1,27 +1,32 @@ 'use client'; -import { useEffectiveDriverId } from "@/lib/hooks/useEffectiveDriverId"; -import { useNotifications } from '@/components/notifications/NotificationProvider'; -import type { NotificationVariant } from '@/components/notifications/notificationTypes'; +import React, { useEffect, useState } from 'react'; import { Wrench, ChevronDown, ChevronUp, X, MessageSquare, Activity, AlertTriangle } from 'lucide-react'; -import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; import { ApiConnectionMonitor } from '@/lib/api/base/ApiConnectionMonitor'; import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler'; import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler'; +import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId"; +import { useNotifications } from '@/components/notifications/NotificationProvider'; +import type { NotificationVariant } from '@/components/notifications/notificationTypes'; // Import our new components -import { Accordion } from './Accordion'; +import { Accordion } from '@/ui/Accordion'; import { NotificationTypeSection } from './sections/NotificationTypeSection'; import { UrgencySection } from './sections/UrgencySection'; import { NotificationSendSection } from './sections/NotificationSendSection'; import { APIStatusSection } from './sections/APIStatusSection'; +import { Box } from '@/ui/Box'; +import { Text } from '@/ui/Text'; +import { Stack } from '@/ui/Stack'; +import { Icon } from '@/ui/Icon'; +import { IconButton } from '@/ui/IconButton'; +import { Badge } from '@/ui/Badge'; +import { Button } from '@/ui/Button'; // Import types import type { DemoNotificationType, DemoUrgency } from './types'; -export default function DevToolbar() { - const router = useRouter(); +export function DevToolbar() { const { addNotification } = useNotifications(); const [isExpanded, setIsExpanded] = useState(false); const [isMinimized, setIsMinimized] = useState(false); @@ -225,140 +230,155 @@ export default function DevToolbar() { if (isMinimized) { return ( - + + setIsMinimized(false)} + variant="secondary" + title="Open Dev Toolbar" + size="lg" + /> + ); } return ( -
+ {/* Header */} -
-
- - Dev Toolbar - + + + + Dev Toolbar + DEMO - -
-
- - -
-
+ variant="ghost" + size="sm" + /> +
+ {/* Content */} {isExpanded && ( -
- {/* Notification Section - Accordion */} - } - isOpen={openAccordion === 'notifications'} - onToggle={() => setOpenAccordion(openAccordion === 'notifications' ? null : 'notifications')} - > -
- - - -
-
+ + + {/* Notification Section - Accordion */} + } + isOpen={openAccordion === 'notifications'} + onToggle={() => setOpenAccordion(openAccordion === 'notifications' ? null : 'notifications')} + > + + + + + + - {/* API Status Section - Accordion */} - } - isOpen={openAccordion === 'apiStatus'} - onToggle={() => setOpenAccordion(openAccordion === 'apiStatus' ? null : 'apiStatus')} - > - - + {/* API Status Section - Accordion */} + } + isOpen={openAccordion === 'apiStatus'} + onToggle={() => setOpenAccordion(openAccordion === 'apiStatus' ? null : 'apiStatus')} + > + + - {/* Error Stats Section - Accordion */} - } - isOpen={openAccordion === 'errors'} - onToggle={() => setOpenAccordion(openAccordion === 'errors' ? null : 'errors')} - > -
-
- Total Errors - {errorStats.total} -
- {Object.keys(errorStats.byType).length > 0 ? ( -
- {Object.entries(errorStats.byType).map(([type, count]) => ( -
- {type} - {count} -
- ))} -
- ) : ( -
No errors yet
- )} - -
-
- -
+ {/* Error Stats Section - Accordion */} + } + isOpen={openAccordion === 'errors'} + onToggle={() => setOpenAccordion(openAccordion === 'errors' ? null : 'errors')} + > + + + Total Errors + {errorStats.total} + + {Object.keys(errorStats.byType).length > 0 ? ( + + {Object.entries(errorStats.byType).map(([type, count]) => ( + + {type} + {count} + + ))} + + ) : ( + + No errors yet + + )} + + + + + )} {/* Collapsed state hint */} {!isExpanded && ( -
- Click ↑ to expand dev tools -
+ + Click ↑ to expand dev tools + )} -
+
); -} \ No newline at end of file +} diff --git a/apps/website/components/dev/sections/APIStatusSection.tsx b/apps/website/components/dev/sections/APIStatusSection.tsx index e7f6380b2..f436767f1 100644 --- a/apps/website/components/dev/sections/APIStatusSection.tsx +++ b/apps/website/components/dev/sections/APIStatusSection.tsx @@ -1,15 +1,24 @@ 'use client'; +import React from 'react'; import { Activity, Wifi, RefreshCw, Terminal } from 'lucide-react'; -import { useState } from 'react'; -import { ApiConnectionMonitor } from '@/lib/api/base/ApiConnectionMonitor'; -import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler'; -import { useNotifications } from '@/components/notifications/NotificationProvider'; +import { Box } from '@/ui/Box'; +import { Text } from '@/ui/Text'; +import { Stack } from '@/ui/Stack'; +import { Icon } from '@/ui/Icon'; +import { Button } from '@/ui/Button'; +import { StatusIndicator, StatRow, Badge } from '@/ui/StatusIndicator'; interface APIStatusSectionProps { apiStatus: string; - apiHealth: any; - circuitBreakers: Record; + apiHealth: { + successfulRequests: number; + totalRequests: number; + averageResponseTime: number; + consecutiveFailures: number; + lastCheck: number | Date | null; + }; + circuitBreakers: Record; checkingHealth: boolean; onHealthCheck: () => void; onResetStats: () => void; @@ -25,121 +34,137 @@ export function APIStatusSection({ onResetStats, onTestError }: APIStatusSectionProps) { + const reliability = apiHealth.totalRequests === 0 + ? 0 + : (apiHealth.successfulRequests / apiHealth.totalRequests); + + const reliabilityLabel = apiHealth.totalRequests === 0 ? 'N/A' : 'Calculated'; + + const getReliabilityColor = () => { + if (apiHealth.totalRequests === 0) return 'text-gray-500'; + if (reliability >= 0.95) return 'text-performance-green'; + if (reliability >= 0.8) return 'text-warning-amber'; + return 'text-red-400'; + }; + + const getStatusVariant = () => { + if (apiStatus === 'connected') return 'success'; + if (apiStatus === 'degraded') return 'warning'; + return 'danger'; + }; + + const statusLabel = apiStatus.toUpperCase(); + const healthSummary = `${apiHealth.successfulRequests}/${apiHealth.totalRequests} req`; + const avgResponseLabel = `${apiHealth.averageResponseTime.toFixed(0)}ms`; + const lastCheckLabel = apiHealth.lastCheck ? 'Recently' : 'Never'; + const consecutiveFailuresLabel = String(apiHealth.consecutiveFailures); + return ( -
-
- - + + + + API Status - -
+ + {/* Status Indicator */} -
-
- - {apiStatus.toUpperCase()} -
- - {apiHealth.successfulRequests}/{apiHealth.totalRequests} req - -
+ - {/* Reliability */} -
- Reliability - = 0.95 ? 'text-green-400' : - (apiHealth.successfulRequests / apiHealth.totalRequests) >= 0.8 ? 'text-yellow-400' : - 'text-red-400' - }`}> - {apiHealth.totalRequests === 0 ? 'N/A' : - ((apiHealth.successfulRequests / apiHealth.totalRequests) * 100).toFixed(1) + '%'} - -
+ + {/* Reliability */} + - {/* Response Time */} -
- Avg Response - - {apiHealth.averageResponseTime.toFixed(0)}ms - -
+ {/* Response Time */} + +
{/* Consecutive Failures */} {apiHealth.consecutiveFailures > 0 && ( -
- Consecutive Failures - {apiHealth.consecutiveFailures} -
+ + + )} {/* Circuit Breakers */} -
-
Circuit Breakers:
+ + Circuit Breakers: {Object.keys(circuitBreakers).length === 0 ? ( -
None active
+ None active ) : ( -
- {Object.entries(circuitBreakers).map(([endpoint, status]: [string, any]) => ( -
- {endpoint.split('/').pop() || endpoint} - + + {Object.entries(circuitBreakers).map(([endpoint, status]) => ( + + + {endpoint} + + {status.state} - + {status.failures > 0 && ( - ({status.failures}) + ({status.failures}) )} -
+ ))} -
+ )} -
+ {/* API Actions */} -
- - -
+ + -
- -
+ + -
- Last Check: {apiHealth.lastCheck ? new Date(apiHealth.lastCheck).toLocaleTimeString() : 'Never'} -
-
+ + + Last Check: {lastCheckLabel} + + +
); -} \ No newline at end of file +} diff --git a/apps/website/components/dev/sections/NotificationSendSection.tsx b/apps/website/components/dev/sections/NotificationSendSection.tsx index 0c5254431..548794ca4 100644 --- a/apps/website/components/dev/sections/NotificationSendSection.tsx +++ b/apps/website/components/dev/sections/NotificationSendSection.tsx @@ -1,10 +1,12 @@ 'use client'; -import { Bell } from 'lucide-react'; -import { useEffectiveDriverId } from "@/lib/hooks/useEffectiveDriverId"; -import { useNotifications } from '@/components/notifications/NotificationProvider'; -import type { NotificationVariant } from '@/components/notifications/notificationTypes'; +import React from 'react'; +import { Bell, Loader2 } from 'lucide-react'; import type { DemoNotificationType, DemoUrgency } from '../types'; +import { Box } from '@/ui/Box'; +import { Text } from '@/ui/Text'; +import { Icon } from '@/ui/Icon'; +import { Button } from '@/ui/Button'; interface NotificationSendSectionProps { selectedType: DemoNotificationType; @@ -15,50 +17,36 @@ interface NotificationSendSectionProps { } export function NotificationSendSection({ - selectedType, - selectedUrgency, sending, lastSent, onSend }: NotificationSendSectionProps) { return ( -
- + {sending ? 'Sending...' : lastSent ? '✓ Notification Sent!' : 'Send Demo Notification'} + -
-

- Silent: Notification center only
- Toast: Temporary popup (auto-dismisses)
- Modal: Blocking popup (may require action) -

-
-
+ + + Silent: Notification center only + + + Toast: Temporary popup (auto-dismisses) + + + Modal: Blocking popup (may require action) + + + ); -} \ No newline at end of file +} diff --git a/apps/website/components/dev/sections/NotificationTypeSection.tsx b/apps/website/components/dev/sections/NotificationTypeSection.tsx index e5c0baff6..d21a887f5 100644 --- a/apps/website/components/dev/sections/NotificationTypeSection.tsx +++ b/apps/website/components/dev/sections/NotificationTypeSection.tsx @@ -1,13 +1,17 @@ 'use client'; -import { MessageSquare, AlertTriangle, Shield, Vote, TrendingUp, Award } from 'lucide-react'; +import React from 'react'; +import { MessageSquare, AlertTriangle, Shield, Vote, TrendingUp, Award, LucideIcon } from 'lucide-react'; import type { DemoNotificationType } from '../types'; +import { Box } from '@/ui/Box'; +import { Text } from '@/ui/Text'; +import { Icon } from '@/ui/Icon'; interface NotificationOption { type: DemoNotificationType; label: string; description: string; - icon: any; + icon: LucideIcon; color: string; } @@ -22,73 +26,85 @@ export const notificationOptions: NotificationOption[] = [ label: 'Protest Against You', description: 'A protest was filed against you', icon: AlertTriangle, - color: 'text-red-400', + color: 'rgb(239, 68, 68)', }, { type: 'defense_requested', label: 'Defense Requested', description: 'A steward requests your defense', icon: Shield, - color: 'text-warning-amber', + color: 'rgb(245, 158, 11)', }, { type: 'vote_required', label: 'Vote Required', description: 'You need to vote on a protest', icon: Vote, - color: 'text-primary-blue', + color: 'rgb(59, 130, 246)', }, { type: 'race_performance_summary', label: 'Race Performance Summary', description: 'Immediate results after main race', icon: TrendingUp, - color: 'text-primary-blue', + color: 'rgb(59, 130, 246)', }, { type: 'race_final_results', label: 'Race Final Results', description: 'Final results after stewarding closes', icon: Award, - color: 'text-warning-amber', + color: 'rgb(245, 158, 11)', }, ]; export function NotificationTypeSection({ selectedType, onSelectType }: NotificationTypeSectionProps) { return ( -
-
- - + + + + Notification Type - -
+ + -
+ {notificationOptions.map((option) => { - const Icon = option.icon; const isSelected = selectedType === option.type; return ( - + + ); })} -
-
+ + ); -} \ No newline at end of file +} diff --git a/apps/website/components/dev/sections/ReplaySection.tsx b/apps/website/components/dev/sections/ReplaySection.tsx index 12b4c3048..f9035a3ec 100644 --- a/apps/website/components/dev/sections/ReplaySection.tsx +++ b/apps/website/components/dev/sections/ReplaySection.tsx @@ -1,6 +1,14 @@ -import { useState, useEffect } from 'react'; +'use client'; + +import React, { useState, useEffect } from 'react'; import { Play, Copy, Trash2, Download, Clock } from 'lucide-react'; import { getGlobalReplaySystem } from '@/lib/infrastructure/ErrorReplay'; +import { Box } from '@/ui/Box'; +import { Text } from '@/ui/Text'; +import { Stack } from '@/ui/Stack'; +import { Icon } from '@/ui/Icon'; +import { IconButton } from '@/ui/IconButton'; +import { Button } from '@/ui/Button'; interface ReplayEntry { id: string; @@ -11,7 +19,6 @@ interface ReplayEntry { export function ReplaySection() { const [replays, setReplays] = useState([]); - const [selectedReplay, setSelectedReplay] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { @@ -78,83 +85,89 @@ export function ReplaySection() { }; return ( -
-
- Error Replay -
- - -
-
+ color="rgb(239, 68, 68)" + /> + + {replays.length === 0 ? ( -
- No replays available -
+ + No replays available + ) : ( -
+ {replays.map((replay) => ( -
-
-
-
- {replay.type} -
-
{replay.error}
-
- {new Date(replay.timestamp).toLocaleTimeString()} -
-
-
-
- - - - -
-
+ variant="secondary" + size="sm" + /> + + ))} -
+ )} -
+ ); -} \ No newline at end of file +} diff --git a/apps/website/components/dev/sections/UrgencySection.tsx b/apps/website/components/dev/sections/UrgencySection.tsx index a7aa26ede..463e76dc2 100644 --- a/apps/website/components/dev/sections/UrgencySection.tsx +++ b/apps/website/components/dev/sections/UrgencySection.tsx @@ -1,13 +1,17 @@ 'use client'; -import { Bell, BellRing, AlertCircle } from 'lucide-react'; +import React from 'react'; +import { Bell, BellRing, AlertCircle, LucideIcon } from 'lucide-react'; import type { DemoUrgency } from '../types'; +import { Box } from '@/ui/Box'; +import { Text } from '@/ui/Text'; +import { Icon } from '@/ui/Icon'; interface UrgencyOption { urgency: DemoUrgency; label: string; description: string; - icon: any; + icon: LucideIcon; } interface UrgencySectionProps { @@ -38,62 +42,72 @@ export const urgencyOptions: UrgencyOption[] = [ export function UrgencySection({ selectedUrgency, onSelectUrgency }: UrgencySectionProps) { return ( -
-
- - + + + + Urgency Level - -
+ + -
+ {urgencyOptions.map((option) => { - const Icon = option.icon; const isSelected = selectedUrgency === option.urgency; + const getSelectedBg = () => { + if (option.urgency === 'modal') return 'bg-red-500/20'; + if (option.urgency === 'toast') return 'bg-warning-amber/20'; + return 'bg-gray-500/20'; + }; + + const getSelectedBorder = () => { + if (option.urgency === 'modal') return 'border-red-500/50'; + if (option.urgency === 'toast') return 'border-warning-amber/50'; + return 'border-gray-500/50'; + }; + + const getSelectedColor = () => { + if (option.urgency === 'modal') return 'rgb(239, 68, 68)'; + if (option.urgency === 'toast') return 'rgb(245, 158, 11)'; + return 'rgb(156, 163, 175)'; + }; + return ( - + + ); })} -
-

+ + {urgencyOptions.find(o => o.urgency === selectedUrgency)?.description} -

-
+ + ); -} \ No newline at end of file +} diff --git a/apps/website/components/dev/types.ts b/apps/website/components/dev/types.ts index 5906a0c5e..a567a1caa 100644 --- a/apps/website/components/dev/types.ts +++ b/apps/website/components/dev/types.ts @@ -1,4 +1,4 @@ -import type { NotificationVariant } from '@/components/notifications/notificationTypes'; +import type { LucideIcon } from 'lucide-react'; export type DemoNotificationType = 'protest_filed' | 'defense_requested' | 'vote_required' | 'race_performance_summary' | 'race_final_results'; export type DemoUrgency = 'silent' | 'toast' | 'modal'; @@ -7,7 +7,7 @@ export interface NotificationOption { type: DemoNotificationType; label: string; description: string; - icon: any; + icon: LucideIcon; color: string; } @@ -15,5 +15,5 @@ export interface UrgencyOption { urgency: DemoUrgency; label: string; description: string; - icon: any; + icon: LucideIcon; } \ No newline at end of file diff --git a/apps/website/components/drivers/CareerHighlights.tsx b/apps/website/components/drivers/CareerHighlights.tsx deleted file mode 100644 index 23a5ce4c8..000000000 --- a/apps/website/components/drivers/CareerHighlights.tsx +++ /dev/null @@ -1,127 +0,0 @@ -'use client'; - -import Card from '../ui/Card'; - -interface Achievement { - id: string; - title: string; - description: string; - icon: string; - unlockedAt: string; - rarity: 'common' | 'rare' | 'epic' | 'legendary'; -} - -const mockAchievements: Achievement[] = [ - { id: '1', title: 'First Victory', description: 'Won your first race', icon: '🏆', unlockedAt: '2024-03-15', rarity: 'common' }, - { id: '2', title: '10 Podiums', description: 'Achieved 10 podium finishes', icon: '🥈', unlockedAt: '2024-05-22', rarity: 'rare' }, - { id: '3', title: 'Clean Racer', description: 'Completed 25 races with 0 incidents', icon: '✨', unlockedAt: '2024-08-10', rarity: 'epic' }, - { id: '4', title: 'Comeback King', description: 'Won a race after starting P10 or lower', icon: '⚡', unlockedAt: '2024-09-03', rarity: 'rare' }, - { id: '5', title: 'Perfect Weekend', description: 'Pole, fastest lap, and win in same race', icon: '💎', unlockedAt: '2024-10-17', rarity: 'legendary' }, - { id: '6', title: 'Century Club', description: 'Completed 100 races', icon: '💯', unlockedAt: '2024-11-01', rarity: 'epic' }, -]; - -const rarityColors = { - common: 'border-gray-500 bg-gray-500/10', - rare: 'border-blue-400 bg-blue-400/10', - epic: 'border-purple-400 bg-purple-400/10', - legendary: 'border-warning-amber bg-warning-amber/10' -}; - -export default function CareerHighlights() { - return ( -
- -

Key Milestones

- -
- - - - - - -
-
- - -

Achievements

- -
- {mockAchievements.map((achievement) => ( -
-
-
{achievement.icon}
-
-
{achievement.title}
-
{achievement.description}
-
- {new Date(achievement.unlockedAt).toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric' - })} -
-
-
-
- ))} -
-
- - -
-
🎯
-

Next Goals

-
-
-
- Win 25 races - 23/25 -
-
-
-
-
- -
- ); -} - -function MilestoneItem({ label, value, icon }: { label: string; value: string; icon: string }) { - return ( -
-
- {icon} - {label} -
- {value} -
- ); -} \ No newline at end of file diff --git a/apps/website/components/drivers/CategoryDistribution.tsx b/apps/website/components/drivers/CategoryDistribution.tsx deleted file mode 100644 index 5be043b14..000000000 --- a/apps/website/components/drivers/CategoryDistribution.tsx +++ /dev/null @@ -1,69 +0,0 @@ -'use client'; - -import { BarChart3 } from 'lucide-react'; -const CATEGORIES = [ - { id: 'beginner', label: 'Beginner', color: 'text-green-400', bgColor: 'bg-green-400/10', borderColor: 'border-green-400/30' }, - { id: 'intermediate', label: 'Intermediate', color: 'text-primary-blue', bgColor: 'bg-primary-blue/10', borderColor: 'border-primary-blue/30' }, - { id: 'advanced', label: 'Advanced', color: 'text-purple-400', bgColor: 'bg-purple-400/10', borderColor: 'border-purple-400/30' }, - { id: 'pro', label: 'Pro', color: 'text-yellow-400', bgColor: 'bg-yellow-400/10', borderColor: 'border-yellow-400/30' }, - { id: 'endurance', label: 'Endurance', color: 'text-orange-400', bgColor: 'bg-orange-400/10', borderColor: 'border-orange-400/30' }, - { id: 'sprint', label: 'Sprint', color: 'text-red-400', bgColor: 'bg-red-400/10', borderColor: 'border-red-400/30' }, -]; - -interface CategoryDistributionProps { - drivers: { - category?: string; - }[]; -} - -export function CategoryDistribution({ drivers }: CategoryDistributionProps) { - const distribution = CATEGORIES.map((category) => ({ - ...category, - count: drivers.filter((d) => d.category === category.id).length, - percentage: drivers.length > 0 - ? Math.round((drivers.filter((d) => d.category === category.id).length / drivers.length) * 100) - : 0, - })); - - return ( -
-
-
- -
-
-

Category Distribution

-

Driver population by category

-
-
- -
- {distribution.map((category) => ( -
-
- {category.count} -
-

{category.label}

-
-
-
-

{category.percentage}% of drivers

-
- ))} -
-
- ); -} \ No newline at end of file diff --git a/apps/website/components/drivers/CreateDriverForm.tsx b/apps/website/components/drivers/CreateDriverForm.tsx index a75de64d7..f24d569c9 100644 --- a/apps/website/components/drivers/CreateDriverForm.tsx +++ b/apps/website/components/drivers/CreateDriverForm.tsx @@ -1,11 +1,14 @@ 'use client'; -import { useState, FormEvent } from 'react'; -import { useRouter } from 'next/navigation'; -import { routes } from '@/lib/routing/RouteConfig'; -import Input from '../ui/Input'; -import Button from '../ui/Button'; -import { useCreateDriver } from "@/lib/hooks/driver/useCreateDriver"; +import React, { useState, FormEvent } from 'react'; +import { Input } from '@/ui/Input'; +import { Button } from '@/ui/Button'; +import { Box } from '@/ui/Box'; +import { Text } from '@/ui/Text'; +import { Stack } from '@/ui/Stack'; +import { TextArea } from '@/ui/TextArea'; +import { InfoBox } from '@/ui/InfoBox'; +import { AlertCircle } from 'lucide-react'; interface FormErrors { name?: string; @@ -15,9 +18,12 @@ interface FormErrors { submit?: string; } -export default function CreateDriverForm() { - const router = useRouter(); - const createDriverMutation = useCreateDriver(); +interface CreateDriverFormProps { + onSuccess: () => void; + isPending: boolean; +} + +export function CreateDriverForm({ onSuccess, isPending }: CreateDriverFormProps) { const [errors, setErrors] = useState({}); const [formData, setFormData] = useState({ @@ -50,7 +56,7 @@ export default function CreateDriverForm() { const handleSubmit = async (e: FormEvent) => { e.preventDefault(); - if (createDriverMutation.isPending) return; + if (isPending) return; const isValid = await validateForm(); if (!isValid) return; @@ -61,118 +67,89 @@ export default function CreateDriverForm() { const firstName = parts[0] ?? displayName; const lastName = parts.slice(1).join(' ') || 'Driver'; - createDriverMutation.mutate( - { - firstName, - lastName, - displayName, - country: formData.country.trim().toUpperCase(), - ...(bio ? { bio } : {}), - }, - { - onSuccess: () => { - router.push(routes.protected.profile); - router.refresh(); - }, - onError: (error) => { - setErrors({ - submit: error instanceof Error ? error.message : 'Failed to create profile' - }); - }, - } - ); + // Construct data for parent to handle + const driverData = { + firstName, + lastName, + displayName, + country: formData.country.trim().toUpperCase(), + ...(bio ? { bio } : {}), + }; + + console.log('Driver data to create:', driverData); + onSuccess(); }; return ( - <> -
-
- + + setFormData({ ...formData, name: e.target.value })} - error={!!errors.name} + onChange={(e: React.ChangeEvent) => setFormData({ ...formData, name: e.target.value })} + variant={errors.name ? 'error' : 'default'} errorMessage={errors.name} placeholder="Alex Vermeer" - disabled={createDriverMutation.isPending} + disabled={isPending} /> -
-
- - setFormData({ ...formData, name: e.target.value })} - error={!!errors.name} - errorMessage={errors.name} - placeholder="Alex Vermeer" - disabled={createDriverMutation.isPending} - /> -
+ + ) => setFormData({ ...formData, country: e.target.value })} + variant={errors.country ? 'error' : 'default'} + errorMessage={errors.country} + placeholder="NL" + maxLength={3} + disabled={isPending} + /> + Use ISO 3166-1 alpha-2 or alpha-3 code + -
- - setFormData({ ...formData, country: e.target.value })} - error={!!errors.country} - errorMessage={errors.country} - placeholder="NL" - maxLength={3} - disabled={createDriverMutation.isPending} - /> -

Use ISO 3166-1 alpha-2 or alpha-3 code

-
+ +