diff --git a/apps/website/.eslintrc.json b/apps/website/.eslintrc.json index 1a79579c2..1a594be34 100644 --- a/apps/website/.eslintrc.json +++ b/apps/website/.eslintrc.json @@ -53,9 +53,9 @@ "lib/builders/view-data/*.tsx" ], "rules": { - "gridpilot-rules/view-data-builder-contract": "off", + "gridpilot-rules/filename-matches-export": "off", "gridpilot-rules/single-export-per-file": "off", - "gridpilot-rules/filename-matches-export": "off" + "gridpilot-rules/view-data-builder-contract": "off" } }, { @@ -72,11 +72,11 @@ "lib/mutations/**/*.ts" ], "rules": { - "gridpilot-rules/mutation-contract": "off", - "gridpilot-rules/mutation-must-use-builders": "off", - "gridpilot-rules/mutation-must-map-errors": "off", + "gridpilot-rules/clean-error-handling": "off", "gridpilot-rules/filename-service-match": "off", - "gridpilot-rules/clean-error-handling": "off" + "gridpilot-rules/mutation-contract": "off", + "gridpilot-rules/mutation-must-map-errors": "off", + "gridpilot-rules/mutation-must-use-builders": "off" } }, { @@ -84,16 +84,16 @@ "templates/**/*.tsx" ], "rules": { - "gridpilot-rules/template-no-direct-mutations": "off", - "gridpilot-rules/template-no-side-effects": "off", + "gridpilot-rules/component-no-data-manipulation": "off", + "gridpilot-rules/no-hardcoded-routes": "off", + "gridpilot-rules/no-raw-html-in-app": "off", "gridpilot-rules/template-no-async-render": "off", + "gridpilot-rules/template-no-direct-mutations": "off", "gridpilot-rules/template-no-external-state": "off", "gridpilot-rules/template-no-global-objects": "off", "gridpilot-rules/template-no-mutation-props": "off", - "gridpilot-rules/template-no-unsafe-html": "off", - "gridpilot-rules/component-no-data-manipulation": "off", - "gridpilot-rules/no-hardcoded-routes": "off", - "gridpilot-rules/no-raw-html-in-app": "off" + "gridpilot-rules/template-no-side-effects": "off", + "gridpilot-rules/template-no-unsafe-html": "off" } }, { @@ -111,33 +111,33 @@ "app/**/layout.tsx" ], "rules": { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + "gridpilot-rules/component-classification": "off", + "gridpilot-rules/no-console": "off", + "gridpilot-rules/no-direct-process-env": "off", + "gridpilot-rules/no-hardcoded-routes": "off", + "gridpilot-rules/no-hardcoded-search-params": "off", + "gridpilot-rules/no-index-files": "off", + "gridpilot-rules/no-next-cookies-in-pages": "off", + "gridpilot-rules/no-raw-html-in-app": "off", "gridpilot-rules/rsc-no-container-manager": "off", - "gridpilot-rules/rsc-no-page-data-fetcher": "off", - "gridpilot-rules/rsc-no-view-models": "off", - "gridpilot-rules/rsc-no-presenters": "off", - "gridpilot-rules/rsc-no-intl": "off", - "gridpilot-rules/rsc-no-sorting-filtering": "off", - "gridpilot-rules/rsc-no-display-objects": "off", - "gridpilot-rules/rsc-no-unsafe-services": "off", + "gridpilot-rules/rsc-no-container-manager-calls": "off", "gridpilot-rules/rsc-no-di": "off", + "gridpilot-rules/rsc-no-display-objects": "off", + "gridpilot-rules/rsc-no-intl": "off", "gridpilot-rules/rsc-no-local-helpers": "off", "gridpilot-rules/rsc-no-object-construction": "off", - "gridpilot-rules/rsc-no-container-manager-calls": "off", - "gridpilot-rules/no-hardcoded-search-params": "off", - "gridpilot-rules/no-next-cookies-in-pages": "off", - "gridpilot-rules/no-hardcoded-routes": "off", - "gridpilot-rules/component-classification": "off", - "gridpilot-rules/no-raw-html-in-app": "off", - "gridpilot-rules/no-console": "off", + "gridpilot-rules/rsc-no-page-data-fetcher": "off", + "gridpilot-rules/rsc-no-presenters": "off", + "gridpilot-rules/rsc-no-sorting-filtering": "off", + "gridpilot-rules/rsc-no-unsafe-services": "off", + "gridpilot-rules/rsc-no-view-models": "off", "import/no-default-export": "off", "no-restricted-syntax": "off", "react-hooks/exhaustive-deps": "off", "react-hooks/rules-of-hooks": "off", - "react/no-unescaped-entities": "off", - "gridpilot-rules/no-index-files": "off", - "gridpilot-rules/no-direct-process-env": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unused-vars": "off" + "react/no-unescaped-entities": "off" } }, { @@ -149,8 +149,8 @@ "lib/mutations/auth/types/*.ts" ], "rules": { - "gridpilot-rules/no-direct-process-env": "off", - "gridpilot-rules/clean-error-handling": "off" + "gridpilot-rules/clean-error-handling": "off", + "gridpilot-rules/no-direct-process-env": "off" } }, { @@ -159,10 +159,10 @@ "lib/display-objects/**/*.tsx" ], "rules": { - "gridpilot-rules/display-no-domain-models": "off", "gridpilot-rules/display-no-business-logic": "off", - "gridpilot-rules/model-no-domain-in-display": "off", - "gridpilot-rules/filename-display-match": "off" + "gridpilot-rules/display-no-domain-models": "off", + "gridpilot-rules/filename-display-match": "off", + "gridpilot-rules/model-no-domain-in-display": "off" } }, { @@ -170,17 +170,17 @@ "lib/page-queries/**/*.ts" ], "rules": { - "gridpilot-rules/page-query-no-null-returns": "off", - "gridpilot-rules/page-query-filename": "off", + "gridpilot-rules/clean-error-handling": "off", + "gridpilot-rules/filename-matches-export": "off", + "gridpilot-rules/no-hardcoded-routes": "off", + "gridpilot-rules/no-hardcoded-search-params": "off", "gridpilot-rules/page-query-contract": "off", "gridpilot-rules/page-query-execute": "off", - "gridpilot-rules/page-query-return-type": "off", + "gridpilot-rules/page-query-filename": "off", "gridpilot-rules/page-query-must-use-builders": "off", - "gridpilot-rules/single-export-per-file": "off", - "gridpilot-rules/filename-matches-export": "off", - "gridpilot-rules/clean-error-handling": "off", - "gridpilot-rules/no-hardcoded-routes": "off", - "gridpilot-rules/no-hardcoded-search-params": "off" + "gridpilot-rules/page-query-no-null-returns": "off", + "gridpilot-rules/page-query-return-type": "off", + "gridpilot-rules/single-export-per-file": "off" } }, { @@ -197,11 +197,11 @@ "lib/services/**/*.ts" ], "rules": { - "gridpilot-rules/services-no-external-api": "off", - "gridpilot-rules/services-must-be-pure": "off", "gridpilot-rules/filename-service-match": "off", + "gridpilot-rules/services-implement-contract": "off", + "gridpilot-rules/services-must-be-pure": "off", "gridpilot-rules/services-must-return-result": "off", - "gridpilot-rules/services-implement-contract": "off" + "gridpilot-rules/services-no-external-api": "off" } }, { @@ -210,12 +210,12 @@ "app/**/*.ts" ], "rules": { - "gridpilot-rules/client-only-no-server-code": "off", "gridpilot-rules/client-only-must-have-directive": "off", - "gridpilot-rules/server-actions-must-use-mutations": "off", - "gridpilot-rules/server-actions-return-result": "off", + "gridpilot-rules/client-only-no-server-code": "off", + "gridpilot-rules/no-use-mutation-in-client": "off", "gridpilot-rules/server-actions-interface": "off", - "gridpilot-rules/no-use-mutation-in-client": "off" + "gridpilot-rules/server-actions-must-use-mutations": "off", + "gridpilot-rules/server-actions-return-result": "off" } }, { @@ -223,8 +223,8 @@ "lib/write-boundaries/**/*.ts" ], "rules": { - "gridpilot-rules/write-boundary-no-direct-mutations": "error", - "gridpilot-rules/write-boundary-must-use-repository": "error" + "gridpilot-rules/write-boundary-must-use-repository": "error", + "gridpilot-rules/write-boundary-no-direct-mutations": "error" } }, { @@ -263,10 +263,10 @@ "app/**/*.ts" ], "rules": { - "gridpilot-rules/no-raw-html-in-app": "off", - "gridpilot-rules/no-nextjs-imports-in-ui": "off", + "gridpilot-rules/component-classification": "off", "gridpilot-rules/no-hardcoded-routes": "off", - "gridpilot-rules/component-classification": "off" + "gridpilot-rules/no-nextjs-imports-in-ui": "off", + "gridpilot-rules/no-raw-html-in-app": "off" } }, { @@ -276,8 +276,8 @@ ], "rules": { "gridpilot-rules/no-hardcoded-routes": "error", - "gridpilot-rules/server-actions-return-result": "error", - "gridpilot-rules/server-actions-interface": "error" + "gridpilot-rules/server-actions-interface": "error", + "gridpilot-rules/server-actions-return-result": "error" } }, { @@ -286,9 +286,9 @@ "ui/**/*.ts" ], "rules": { - "gridpilot-rules/ui-element-purity": "error", + "gridpilot-rules/component-classification": "error", "gridpilot-rules/no-nextjs-imports-in-ui": "error", - "gridpilot-rules/component-classification": "error" + "gridpilot-rules/ui-element-purity": "error" } }, { @@ -297,11 +297,10 @@ "components/**/*.ts" ], "rules": { - "gridpilot-rules/no-nextjs-imports-in-ui": "off", "gridpilot-rules/component-classification": "off", "gridpilot-rules/no-hardcoded-routes": "off", + "gridpilot-rules/no-nextjs-imports-in-ui": "off", "gridpilot-rules/no-raw-html-in-app": "error", - "gridpilot-rules/no-generic-ui-primitives-in-components": "error", "no-restricted-imports": "off" } }, @@ -310,8 +309,7 @@ "components/mockups/**/*.tsx" ], "rules": { - "gridpilot-rules/no-raw-html-in-app": "off", - "gridpilot-rules/no-generic-ui-primitives-in-components": "off" + "gridpilot-rules/no-raw-html-in-app": "off" } }, { @@ -319,11 +317,11 @@ "lib/services/**/*.ts" ], "rules": { + "gridpilot-rules/no-hardcoded-routes": "off", "gridpilot-rules/service-function-format": "off", - "gridpilot-rules/services-must-be-pure": "off", - "gridpilot-rules/services-no-external-api": "off", "gridpilot-rules/services-implement-contract": "off", - "gridpilot-rules/no-hardcoded-routes": "off" + "gridpilot-rules/services-must-be-pure": "off", + "gridpilot-rules/services-no-external-api": "off" } }, { @@ -350,8 +348,8 @@ "off", { "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "caughtErrorsIgnorePattern": "^_" + "caughtErrorsIgnorePattern": "^_", + "varsIgnorePattern": "^_" } ], "boundaries/element-types": [ @@ -370,6 +368,7 @@ ] } ], + "gridpilot-rules/no-index-files": "off", "import/no-default-export": "off", "import/no-named-as-default-member": "off", "no-restricted-syntax": "off", @@ -377,8 +376,7 @@ "react-hooks/rules-of-hooks": "off", "react/no-unescaped-entities": "off", "unused-imports/no-unused-imports": "off", - "unused-imports/no-unused-vars": "off", - "gridpilot-rules/no-index-files": "off" + "unused-imports/no-unused-vars": "off" }, "settings": { "boundaries/elements": [ @@ -393,4 +391,4 @@ "typescript": {} } } -} +} \ No newline at end of file diff --git a/apps/website/components/achievements/AchievementCard.tsx b/apps/website/components/achievements/AchievementCard.tsx index 02e30b087..4c8c7d55e 100644 --- a/apps/website/components/achievements/AchievementCard.tsx +++ b/apps/website/components/achievements/AchievementCard.tsx @@ -1,6 +1,7 @@ import { Card } from '@/ui/Card'; import { Text } from '@/ui/Text'; import { Group } from '@/ui/Group'; +import { Stack } from '@/ui/Stack'; import { DateDisplay } from '@/lib/display-objects/DateDisplay'; interface AchievementCardProps { @@ -31,13 +32,13 @@ export function AchievementCard({ > {icon} - + {title} {description} {DateDisplay.formatShort(unlockedAt)} - + ); diff --git a/apps/website/components/achievements/AchievementGrid.tsx b/apps/website/components/achievements/AchievementGrid.tsx index 325e6ab8f..f93808ea3 100644 --- a/apps/website/components/achievements/AchievementGrid.tsx +++ b/apps/website/components/achievements/AchievementGrid.tsx @@ -4,6 +4,9 @@ import { Grid } from '@/ui/Grid'; import { Heading } from '@/ui/Heading'; import { Icon } from '@/ui/Icon'; import { Group } from '@/ui/Group'; +import { Stack } from '@/ui/Stack'; +import { Box } from '@/ui/Box'; +import { Surface } from '@/ui/Surface'; import { Text } from '@/ui/Text'; import { Award, Crown, Medal, Star, Target, Trophy, Zap } from 'lucide-react'; import React from 'react'; @@ -36,9 +39,9 @@ function getAchievementIcon(icon: string) { export function AchievementGrid({ achievements }: AchievementGridProps) { return ( - - - + + + Achievements @@ -46,38 +49,39 @@ export function AchievementGrid({ achievements }: AchievementGridProps) { {achievements.length} earned - - - {achievements.map((achievement) => { - const AchievementIcon = getAchievementIcon(achievement.icon); - const rarity = AchievementDisplay.getRarityVariant(achievement.rarity); - return ( - - - - - - - {achievement.title} - {achievement.description} - - - {achievement.rarity} - - - - {AchievementDisplay.formatDate(achievement.earnedAt)} - - + + + {achievements.map((achievement) => { + const AchievementIcon = getAchievementIcon(achievement.icon); + const rarity = AchievementDisplay.getRarityVariant(achievement.rarity); + return ( + + + + + + + {achievement.title} + {achievement.description} + + + {achievement.rarity} + + + + {AchievementDisplay.formatDate(achievement.earnedAt)} + + + - - - ); - })} - + + ); + })} + + ); } diff --git a/apps/website/components/admin/AdminDataTable.tsx b/apps/website/components/admin/AdminDataTable.tsx index ed7f0166e..adc119ef3 100644 --- a/apps/website/components/admin/AdminDataTable.tsx +++ b/apps/website/components/admin/AdminDataTable.tsx @@ -1,6 +1,7 @@ 'use client'; import { Card } from '@/ui/Card'; +import { Box } from '@/ui/Box'; import React from 'react'; interface AdminDataTableProps { @@ -20,14 +21,12 @@ export function AdminDataTable({ }: AdminDataTableProps) { return ( -
{children} -
+
); } diff --git a/apps/website/components/admin/AdminUsersTable.tsx b/apps/website/components/admin/AdminUsersTable.tsx index 9a7c4ee1b..6593f7137 100644 --- a/apps/website/components/admin/AdminUsersTable.tsx +++ b/apps/website/components/admin/AdminUsersTable.tsx @@ -6,6 +6,8 @@ import { Button } from '@/ui/Button'; import { IconButton } from '@/ui/IconButton'; import { SimpleCheckbox } from '@/ui/SimpleCheckbox'; import { Badge } from '@/ui/Badge'; +import { Box } from '@/ui/Box'; +import { Group } from '@/ui/Group'; import { DriverIdentity } from '@/ui/DriverIdentity'; import { Table, @@ -87,13 +89,13 @@ export function AdminUsersTable({ /> -
+ {user.roles.map((role) => ( {role} ))} -
+
@@ -104,7 +106,7 @@ export function AdminUsersTable({ -
+ {user.status === 'active' ? (
+
))} diff --git a/apps/website/components/admin/UserFilters.tsx b/apps/website/components/admin/UserFilters.tsx index f4e60d6d6..521dedbe1 100644 --- a/apps/website/components/admin/UserFilters.tsx +++ b/apps/website/components/admin/UserFilters.tsx @@ -5,6 +5,8 @@ import { Icon } from '@/ui/Icon'; import { Input } from '@/ui/Input'; import { Select } from '@/ui/Select'; import { Text } from '@/ui/Text'; +import { Box } from '@/ui/Box'; +import { Group } from '@/ui/Group'; import { Filter, Search } from 'lucide-react'; import { AdminToolbar } from './AdminToolbar'; import React from 'react'; @@ -31,7 +33,7 @@ export function UserFilters({ return ( + Filters {(search || roleFilter || statusFilter) && ( @@ -43,16 +45,18 @@ export function UserFilters({ Clear all )} - + } > - onSearch(e.target.value)} - style={{ width: '300px' }} - /> + + onSearch(e.target.value)} + fullWidth + /> + } fullWidth /> - + } > -
+ {children} -
+ - + ); } diff --git a/apps/website/components/leaderboards/LeaderboardsHero.tsx b/apps/website/components/leaderboards/LeaderboardsHero.tsx index 224053c24..b53fe1cda 100644 --- a/apps/website/components/leaderboards/LeaderboardsHero.tsx +++ b/apps/website/components/leaderboards/LeaderboardsHero.tsx @@ -18,42 +18,42 @@ export function LeaderboardsHero({ onNavigateToDrivers, onNavigateToTeams }: Lea return ( - - + - - + + - Leaderboards - Precision Performance Tracking + Leaderboards + Precision Performance Tracking @@ -65,7 +65,6 @@ export function LeaderboardsHero({ onNavigateToDrivers, onNavigateToTeams }: Lea variant="primary" onClick={onNavigateToDrivers} icon={} - shadow="shadow-lg shadow-primary-blue/20" > Driver Rankings @@ -73,7 +72,6 @@ export function LeaderboardsHero({ onNavigateToDrivers, onNavigateToTeams }: Lea variant="secondary" onClick={onNavigateToTeams} icon={} - hoverBg="bg-white/5" > Team Rankings diff --git a/apps/website/components/leaderboards/RankBadge.tsx b/apps/website/components/leaderboards/RankBadge.tsx index 7ce5f534e..7d6b2b946 100644 --- a/apps/website/components/leaderboards/RankBadge.tsx +++ b/apps/website/components/leaderboards/RankBadge.tsx @@ -1,5 +1,6 @@ import { Badge } from '@/ui/Badge'; import { Text } from '@/ui/Text'; +import { Group } from '@/ui/Group'; import React from 'react'; interface RankBadgeProps { @@ -28,10 +29,10 @@ export function RankBadge({ rank, size = 'md' }: RankBadgeProps) { return ( -
- {medal && {medal}} + + {medal && {medal}} #{rank} -
+
); } diff --git a/apps/website/components/leaderboards/RankMedal.tsx b/apps/website/components/leaderboards/RankMedal.tsx index a04474a07..6f285ea00 100644 --- a/apps/website/components/leaderboards/RankMedal.tsx +++ b/apps/website/components/leaderboards/RankMedal.tsx @@ -1,6 +1,7 @@ import { MedalDisplay } from '@/lib/display-objects/MedalDisplay'; import { Icon } from '@/ui/Icon'; import { Text } from '@/ui/Text'; +import { Surface } from '@/ui/Surface'; import { Crown, Medal } from 'lucide-react'; import React from 'react'; @@ -33,23 +34,21 @@ export function RankMedal({ rank, size = 'md', showIcon = true }: RankMedalProps }; return ( -
{isTop3 && showIcon ? ( ) : ( {rank} )} -
+ ); } diff --git a/apps/website/components/leaderboards/TeamRankingRow.tsx b/apps/website/components/leaderboards/TeamRankingRow.tsx index b38ddd129..c67dccc18 100644 --- a/apps/website/components/leaderboards/TeamRankingRow.tsx +++ b/apps/website/components/leaderboards/TeamRankingRow.tsx @@ -2,6 +2,10 @@ import { getMediaUrl } from '@/lib/utilities/media'; import { Image } from '@/ui/Image'; import { TableCell, TableRow } from '@/ui/Table'; import { Text } from '@/ui/Text'; +import { Box } from '@/ui/Box'; +import { Group } from '@/ui/Group'; +import { Stack } from '@/ui/Stack'; +import { Surface } from '@/ui/Surface'; import { RankMedal } from './RankMedal'; import React from 'react'; @@ -34,22 +38,22 @@ export function TeamRankingRow({ onClick={onClick} > -
+ -
+
-
-
+ + {name} -
-
+ + {memberCount} Members -
-
+ +
diff --git a/apps/website/components/leagues/JoinRequestsPanel.tsx b/apps/website/components/leagues/JoinRequestsPanel.tsx index 59c717554..c70d3b6f4 100644 --- a/apps/website/components/leagues/JoinRequestsPanel.tsx +++ b/apps/website/components/leagues/JoinRequestsPanel.tsx @@ -4,6 +4,9 @@ import { IconButton } from '@/ui/IconButton'; import { Panel } from '@/ui/Panel'; import { Icon } from '@/ui/Icon'; import { Text } from '@/ui/Text'; +import { Box } from '@/ui/Box'; +import { Group } from '@/ui/Group'; +import { Stack } from '@/ui/Stack'; import { ListItem, ListItemInfo, ListItemActions } from '@/ui/ListItem'; import { EmptyState } from '@/ui/EmptyState'; import { Check, Clock, X, UserPlus } from 'lucide-react'; @@ -34,17 +37,17 @@ export function JoinRequestsPanel({ requests, onAccept, onDecline }: JoinRequest return ( -
+ {requests.map((request) => ( + {request.requestedAt} -
+ } /> @@ -66,7 +69,7 @@ export function JoinRequestsPanel({ requests, onAccept, onDecline }: JoinRequest ))} - +
); } diff --git a/apps/website/components/leagues/LeagueBasicsSection.tsx b/apps/website/components/leagues/LeagueBasicsSection.tsx index a2ecf651f..8112cd536 100644 --- a/apps/website/components/leagues/LeagueBasicsSection.tsx +++ b/apps/website/components/leagues/LeagueBasicsSection.tsx @@ -7,6 +7,8 @@ import { Icon } from '@/ui/Icon'; import { Input } from '@/ui/Input'; import { Grid } from '@/ui/Grid'; import { Stack } from '@/ui/Stack'; +import { Box } from '@/ui/Box'; +import { Group } from '@/ui/Group'; import { Surface } from '@/ui/Surface'; import { Text } from '@/ui/Text'; import { TextArea } from '@/ui/TextArea'; @@ -46,42 +48,38 @@ export function LeagueBasicsSection({ return ( {/* Emotional header for the step */} - - + + Every great championship starts with a name - - - + + + This is where legends begin. Give your league an identity that drivers will remember. - + {/* League name */} - - League name * - - } + label="League name *" + icon={} value={basics.name} onChange={(e: React.ChangeEvent) => updateBasics({ name: e.target.value })} placeholder="e.g., GridPilot Sprint Series" - variant={errors?.name ? 'error' : 'default'} - errorMessage={errors?.name} + error={errors?.name} disabled={disabled} autoFocus + fullWidth /> - + Make it memorable — this is what drivers will see first - - Try: + + Try: {[ 'Sunday Showdown Series', 'Midnight Endurance League', @@ -94,27 +92,19 @@ export function LeagueBasicsSection({ variant="secondary" size="sm" disabled={disabled} - rounded="full" - fontSize="0.75rem" - px={2} - py={0.5} - h="auto" + rounded + style={{ fontSize: '0.75rem', height: 'auto', padding: '0.125rem 0.5rem' }} > {name} ))} - + {/* Description - Now Required */}