website refactor
This commit is contained in:
@@ -17,7 +17,7 @@ interface AccordionProps {
|
||||
|
||||
export function Accordion({ title, icon, children, isOpen, onToggle }: AccordionProps) {
|
||||
return (
|
||||
<Box border={true} borderColor="border-charcoal-outline" rounded="lg" overflow="hidden" bg="bg-iron-gray/30">
|
||||
<Box border borderColor="border-charcoal-outline" rounded="lg" overflow="hidden" bg="bg-iron-gray/30">
|
||||
<Box
|
||||
as="button"
|
||||
onClick={onToggle}
|
||||
@@ -27,7 +27,8 @@ export function Accordion({ title, icon, children, isOpen, onToggle }: Accordion
|
||||
px={3}
|
||||
py={2}
|
||||
fullWidth
|
||||
className="hover:bg-iron-gray/50 transition-colors"
|
||||
hoverBg="iron-gray/50"
|
||||
clickable
|
||||
>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
{icon}
|
||||
@@ -39,7 +40,7 @@ export function Accordion({ title, icon, children, isOpen, onToggle }: Accordion
|
||||
</Box>
|
||||
|
||||
{isOpen && (
|
||||
<Box p={3} borderTop={true} borderColor="border-charcoal-outline">
|
||||
<Box p={3} borderTop borderColor="border-charcoal-outline">
|
||||
{children}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
interface AppFooterProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* AppFooter is the bottom section of the application.
|
||||
*/
|
||||
export function AppFooter({ children, className = '' }: AppFooterProps) {
|
||||
return (
|
||||
<footer className={`bg-[#141619] border-t border-[#23272B] py-8 px-4 md:px-6 ${className}`}>
|
||||
{children}
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
interface AppHeaderProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* AppHeader is the top control bar of the application.
|
||||
* It follows the "Telemetry Workspace" structure.
|
||||
*/
|
||||
export function AppHeader({ children, className = '' }: AppHeaderProps) {
|
||||
return (
|
||||
<header
|
||||
className={`sticky top-0 z-50 h-16 md:h-20 bg-[#0C0D0F]/80 backdrop-blur-md border-b border-[#23272B] flex items-center px-4 md:px-6 ${className}`}
|
||||
>
|
||||
{children}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
interface AppShellProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* AppShell is the root container for the entire application layout.
|
||||
* It provides the base background and layout structure.
|
||||
*/
|
||||
export function AppShell({ children, className = '' }: AppShellProps) {
|
||||
return (
|
||||
<div className={`min-h-screen bg-[#0C0D0F] text-gray-100 flex flex-col ${className}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
interface AppSidebarProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* AppSidebar is the "dashboard rail" of the application.
|
||||
* It provides global navigation and context.
|
||||
*/
|
||||
export function AppSidebar({ children, className = '' }: AppSidebarProps) {
|
||||
return (
|
||||
<aside
|
||||
className={`hidden lg:flex flex-col w-64 bg-[#141619] border-r border-[#23272B] overflow-y-auto ${className}`}
|
||||
>
|
||||
{children}
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
|
||||
import { ErrorBanner } from './ErrorBanner';
|
||||
|
||||
interface AuthErrorProps {
|
||||
action: string;
|
||||
}
|
||||
|
||||
export function AuthError({ action }: AuthErrorProps) {
|
||||
return (
|
||||
<ErrorBanner
|
||||
message={`Failed to load ${action} page`}
|
||||
title="Error"
|
||||
variant="error"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { LoadingSpinner } from './LoadingSpinner';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface AuthLoadingProps {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export function AuthLoading({ message = 'Authenticating...' }: AuthLoadingProps) {
|
||||
return (
|
||||
<Box
|
||||
fullWidth
|
||||
minHeight="100vh"
|
||||
display="flex"
|
||||
center
|
||||
bg="bg-[#0f1115]"
|
||||
>
|
||||
<Stack align="center" gap={4}>
|
||||
<LoadingSpinner size={10} />
|
||||
<Text color="text-gray-400" weight="medium">
|
||||
{message}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -3,14 +3,14 @@ import { Box, BoxProps } from './primitives/Box';
|
||||
import { Icon } from './Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
interface BadgeProps extends Omit<BoxProps<'div'>, 'children'> {
|
||||
interface BadgeProps {
|
||||
children: ReactNode;
|
||||
variant?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
||||
size?: 'xs' | 'sm' | 'md';
|
||||
icon?: LucideIcon;
|
||||
}
|
||||
|
||||
export function Badge({ children, className = '', variant = 'default', size = 'sm', icon, rounded = 'none', ...props }: BadgeProps) {
|
||||
export function Badge({ children, variant = 'default', size = 'sm', icon }: BadgeProps) {
|
||||
const baseClasses = 'flex items-center gap-1.5 border font-bold uppercase tracking-widest';
|
||||
|
||||
const sizeClasses = {
|
||||
@@ -19,16 +19,6 @@ export function Badge({ children, className = '', variant = 'default', size = 's
|
||||
md: 'px-3 py-1 text-xs'
|
||||
};
|
||||
|
||||
const roundedClasses = {
|
||||
none: 'rounded-none',
|
||||
sm: 'rounded-sm',
|
||||
md: 'rounded-md',
|
||||
lg: 'rounded-lg',
|
||||
xl: 'rounded-xl',
|
||||
'2xl': 'rounded-2xl',
|
||||
full: 'rounded-full'
|
||||
};
|
||||
|
||||
const variantClasses = {
|
||||
default: 'bg-gray-500/10 border-gray-500/30 text-gray-400',
|
||||
primary: 'bg-primary-accent/10 border-primary-accent/30 text-primary-accent',
|
||||
@@ -41,13 +31,11 @@ export function Badge({ children, className = '', variant = 'default', size = 's
|
||||
const classes = [
|
||||
baseClasses,
|
||||
sizeClasses[size],
|
||||
typeof rounded === 'string' && roundedClasses[rounded as keyof typeof roundedClasses] ? roundedClasses[rounded as keyof typeof roundedClasses] : '',
|
||||
!props.bg && !props.color && !props.borderColor ? variantClasses[variant] : '',
|
||||
className
|
||||
variantClasses[variant],
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
return (
|
||||
<Box className={classes} {...props}>
|
||||
<Box className={classes}>
|
||||
{icon && <Icon icon={icon} size={3} />}
|
||||
{children}
|
||||
</Box>
|
||||
|
||||
@@ -10,7 +10,6 @@ export type BreadcrumbItem = {
|
||||
|
||||
interface BreadcrumbsProps {
|
||||
items: BreadcrumbItem[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Breadcrumbs({ items }: BreadcrumbsProps) {
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Stack } from './primitives/Stack';
|
||||
import { Text } from './Text';
|
||||
import { Heading } from './Heading';
|
||||
import { Avatar } from './Avatar';
|
||||
import { Badge } from './Badge';
|
||||
import { Trophy, Flag, Users, Star } from 'lucide-react';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
interface DashboardHeroProps {
|
||||
driverName: string;
|
||||
avatarUrl?: string | null;
|
||||
rating: number;
|
||||
rank: number;
|
||||
totalRaces: number;
|
||||
winRate: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function DashboardHero({
|
||||
driverName,
|
||||
avatarUrl,
|
||||
rating,
|
||||
rank,
|
||||
totalRaces,
|
||||
winRate,
|
||||
className = '',
|
||||
}: DashboardHeroProps) {
|
||||
return (
|
||||
<Box
|
||||
position="relative"
|
||||
bg="bg-[#0C0D0F]"
|
||||
borderBottom
|
||||
borderColor="border-[#23272B]"
|
||||
overflow="hidden"
|
||||
className={className}
|
||||
>
|
||||
{/* Background Glow */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top={-100}
|
||||
right={-100}
|
||||
w="500px"
|
||||
h="500px"
|
||||
bg="bg-primary-blue/10"
|
||||
rounded="full"
|
||||
blur="3xl"
|
||||
/>
|
||||
|
||||
<Box p={{ base: 6, md: 10 }} position="relative" zIndex={10}>
|
||||
<Stack direction={{ base: 'col', md: 'row' }} align="center" gap={8}>
|
||||
{/* Avatar Section */}
|
||||
<Box position="relative">
|
||||
<Box
|
||||
p={1}
|
||||
rounded="2xl"
|
||||
bg="bg-[#141619]"
|
||||
border
|
||||
borderColor="border-[#23272B]"
|
||||
>
|
||||
<Avatar
|
||||
src={avatarUrl}
|
||||
alt={driverName}
|
||||
size={120}
|
||||
className="rounded-xl"
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom={-2}
|
||||
right={-2}
|
||||
w="10"
|
||||
h="10"
|
||||
rounded="xl"
|
||||
bg="bg-[#4ED4E0]"
|
||||
borderWidth="2px"
|
||||
borderStyle="solid"
|
||||
borderColor="border-[#0C0D0F]"
|
||||
display="flex"
|
||||
center
|
||||
>
|
||||
<Icon icon={Star} size={5} color="#0C0D0F" />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Info Section */}
|
||||
<Stack flex={1} align={{ base: 'center', md: 'start' }} gap={4}>
|
||||
<Box>
|
||||
<Heading level={1} uppercase letterSpacing="tighter" mb={2}>
|
||||
{driverName}
|
||||
</Heading>
|
||||
<Stack direction="row" gap={4}>
|
||||
<Stack gap={0.5}>
|
||||
<Text size="xs" color="text-gray-500" uppercase>Rating</Text>
|
||||
<Text size="sm" weight="bold" color="text-[#4ED4E0]" font="mono">{rating}</Text>
|
||||
</Stack>
|
||||
<Stack gap={0.5}>
|
||||
<Text size="xs" color="text-gray-500" uppercase>Rank</Text>
|
||||
<Text size="sm" weight="bold" color="text-[#FFBE4D]" font="mono">#{rank}</Text>
|
||||
</Stack>
|
||||
<Stack gap={0.5}>
|
||||
<Text size="xs" color="text-gray-500" uppercase>Starts</Text>
|
||||
<Text size="sm" weight="bold" color="text-gray-300" font="mono">{totalRaces}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<Stack direction="row" gap={3} wrap>
|
||||
<Badge variant="primary" rounded="lg" icon={Trophy}>
|
||||
{winRate}% Win Rate
|
||||
</Badge>
|
||||
<Badge variant="info" rounded="lg" icon={Flag}>
|
||||
Pro License
|
||||
</Badge>
|
||||
<Badge variant="default" rounded="lg" icon={Users}>
|
||||
Team Redline
|
||||
</Badge>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{/* Quick Stats */}
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={4}
|
||||
p={6}
|
||||
bg="bg-white/5"
|
||||
rounded="2xl"
|
||||
border
|
||||
borderColor="border-white/10"
|
||||
className="backdrop-blur-md"
|
||||
>
|
||||
<Stack align="center" px={4}>
|
||||
<Text size="2xl" weight="bold" color="text-white">12</Text>
|
||||
<Text size="xs" color="text-gray-500" uppercase>Podiums</Text>
|
||||
</Stack>
|
||||
<Box w="1px" h="10" bg="bg-white/10" />
|
||||
<Stack align="center" px={4}>
|
||||
<Text size="2xl" weight="bold" color="text-white">4</Text>
|
||||
<Text size="xs" color="text-gray-500" uppercase>Wins</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
interface DashboardRailProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DashboardRail is the primary sidebar navigation for the "Telemetry Workspace".
|
||||
* It provides a high-density, instrument-grade navigation experience.
|
||||
* Aligned with "Precision Racing Minimal" theme.
|
||||
*/
|
||||
export function DashboardRail({ children, className = '' }: DashboardRailProps) {
|
||||
return (
|
||||
<aside
|
||||
className={`hidden lg:flex flex-col w-64 bg-[#141619] border-r border-[#23272B] overflow-y-auto ${className}`}
|
||||
>
|
||||
{children}
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Grid } from './primitives/Grid';
|
||||
import { Stack } from './primitives/Stack';
|
||||
@@ -14,7 +14,6 @@ import { Stack } from './primitives/Stack';
|
||||
|
||||
interface LayoutProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
padding?: string;
|
||||
gap?: string;
|
||||
grid?: boolean;
|
||||
@@ -27,7 +26,6 @@ interface LayoutProps {
|
||||
|
||||
export function Layout({
|
||||
children,
|
||||
className = '',
|
||||
padding = 'p-6',
|
||||
gap = 'gap-4',
|
||||
grid = false,
|
||||
@@ -41,7 +39,7 @@ export function Layout({
|
||||
return (
|
||||
<Grid
|
||||
cols={gridCols as any}
|
||||
className={`${padding} ${gap} ${className}`}
|
||||
className={`${padding} ${gap}`}
|
||||
>
|
||||
{children}
|
||||
</Grid>
|
||||
@@ -54,7 +52,7 @@ export function Layout({
|
||||
direction={flexCol ? 'col' : 'row'}
|
||||
align={items}
|
||||
justify={justify}
|
||||
className={`${padding} ${gap} ${className}`}
|
||||
className={`${padding} ${gap}`}
|
||||
>
|
||||
{children}
|
||||
</Stack>
|
||||
@@ -62,7 +60,7 @@ export function Layout({
|
||||
}
|
||||
|
||||
return (
|
||||
<Box className={`${padding} ${gap} ${className}`}>
|
||||
<Box className={`${padding} ${gap}`}>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
|
||||
interface OnboardingCTAProps {
|
||||
onBack?: () => void;
|
||||
onNext?: () => void;
|
||||
nextLabel?: string;
|
||||
backLabel?: string;
|
||||
isLastStep?: boolean;
|
||||
canNext?: boolean;
|
||||
isLoading?: boolean;
|
||||
type?: 'button' | 'submit';
|
||||
}
|
||||
|
||||
export function OnboardingCTA({
|
||||
onBack,
|
||||
onNext,
|
||||
nextLabel = 'Continue',
|
||||
backLabel = 'Back',
|
||||
isLastStep = false,
|
||||
canNext = true,
|
||||
isLoading = false,
|
||||
type = 'button',
|
||||
}: OnboardingCTAProps) {
|
||||
return (
|
||||
<Stack direction="row" justify="between" mt={8} gap={4}>
|
||||
{onBack ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onBack}
|
||||
disabled={isLoading}
|
||||
className="px-8"
|
||||
>
|
||||
{backLabel}
|
||||
</Button>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
|
||||
<Button
|
||||
type={type}
|
||||
variant="primary"
|
||||
onClick={onNext}
|
||||
disabled={isLoading || !canNext}
|
||||
className="px-8 min-w-[140px]"
|
||||
>
|
||||
{isLoading ? (
|
||||
<Stack direction="row" gap={2} align="center">
|
||||
<span className="animate-spin">⟳</span>
|
||||
<span>Processing...</span>
|
||||
</Stack>
|
||||
) : (
|
||||
isLastStep ? 'Complete Setup' : nextLabel
|
||||
)}
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export function OnboardingCardAccent() {
|
||||
return (
|
||||
<div className="absolute top-0 right-0 w-40 h-40 bg-gradient-to-bl from-primary-blue/10 to-transparent rounded-bl-full" />
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
interface OnboardingContainerProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function OnboardingContainer({ children }: OnboardingContainerProps) {
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto px-4 py-10">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
|
||||
interface OnboardingErrorProps {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export function OnboardingError({ message }: OnboardingErrorProps) {
|
||||
return (
|
||||
<Box
|
||||
mt={6}
|
||||
display="flex"
|
||||
alignItems="start"
|
||||
gap={3}
|
||||
p={4}
|
||||
rounded="xl"
|
||||
bg="bg-red-500/10"
|
||||
border
|
||||
borderColor="border-red-500/30"
|
||||
>
|
||||
<Text color="text-red-400" flexShrink={0} mt={0.5}>⚠</Text>
|
||||
<Text size="sm" color="text-red-400">{message}</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import React, { ReactNode, FormEvent } from 'react';
|
||||
import { Box } from './primitives/Box';
|
||||
|
||||
interface OnboardingFormProps {
|
||||
children: ReactNode;
|
||||
onSubmit: (e: FormEvent) => void;
|
||||
}
|
||||
|
||||
export function OnboardingForm({ children, onSubmit }: OnboardingFormProps) {
|
||||
return (
|
||||
<Box as="form" onSubmit={onSubmit} position="relative">
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
interface OnboardingHeaderProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
emoji: string;
|
||||
}
|
||||
|
||||
export function OnboardingHeader({ title, subtitle, emoji }: OnboardingHeaderProps) {
|
||||
return (
|
||||
<div className="text-center mb-8">
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-primary-blue/20 to-purple-600/10 border border-primary-blue/30 mx-auto mb-4">
|
||||
<span className="text-2xl">{emoji}</span>
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold mb-2">{title}</h1>
|
||||
<p className="text-gray-400">{subtitle}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function OnboardingHelpText() {
|
||||
return (
|
||||
<p className="text-center text-xs text-gray-500 mt-6">
|
||||
Your avatar will be AI-generated based on your photo and chosen suit color
|
||||
</p>
|
||||
);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import { Button } from '@/ui/Button';
|
||||
|
||||
interface OnboardingNavigationProps {
|
||||
onBack: () => void;
|
||||
onNext?: () => void;
|
||||
isLastStep: boolean;
|
||||
canSubmit: boolean;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export function OnboardingNavigation({ onBack, onNext, isLastStep, canSubmit, loading }: OnboardingNavigationProps) {
|
||||
return (
|
||||
<div className="mt-8 flex items-center justify-between">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onBack}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<span>←</span>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
{!isLastStep ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={onNext}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
Continue
|
||||
<span>→</span>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={loading || !canSubmit}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<span className="animate-spin">⟳</span>
|
||||
Creating Profile...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>✓</span>
|
||||
Complete Setup
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Stack } from '@/ui/primitives/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
|
||||
interface OnboardingStepHeaderProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export function OnboardingStepHeader({ title, description }: OnboardingStepHeaderProps) {
|
||||
return (
|
||||
<Stack gap={1} mb={6}>
|
||||
<Text size="2xl" color="text-white" weight="bold" className="tracking-tight" block>
|
||||
{title}
|
||||
</Text>
|
||||
{description && (
|
||||
<Text size="sm" color="text-gray-400" block>
|
||||
{description}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Surface } from './primitives/Surface';
|
||||
|
||||
interface OnboardingStepPanelProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function OnboardingStepPanel({ children, className = '' }: OnboardingStepPanelProps) {
|
||||
return (
|
||||
<Surface
|
||||
variant="muted"
|
||||
rounded="2xl"
|
||||
border
|
||||
p={6}
|
||||
borderColor="border-charcoal-outline"
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</Surface>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
interface ProfileBioProps {
|
||||
bio: string;
|
||||
}
|
||||
|
||||
export function ProfileBio({ bio }: ProfileBioProps) {
|
||||
return (
|
||||
<Card>
|
||||
<Box mb={3}>
|
||||
<Heading level={2} icon={<Icon icon={User} size={5} color="#3b82f6" />}>
|
||||
About
|
||||
</Heading>
|
||||
</Box>
|
||||
<Text color="text-gray-300">{bio}</Text>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Grid } from './primitives/Grid';
|
||||
|
||||
interface Stat {
|
||||
label: string;
|
||||
value: string | number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
interface ProfileStatGridProps {
|
||||
stats: Stat[];
|
||||
}
|
||||
|
||||
export function ProfileStatGrid({ stats }: ProfileStatGridProps) {
|
||||
return (
|
||||
<Grid cols={2} mdCols={4} gap={4}>
|
||||
{stats.map((stat, idx) => (
|
||||
<Box
|
||||
key={idx}
|
||||
p={4}
|
||||
bg="bg-[#0f1115]"
|
||||
rounded="xl"
|
||||
border
|
||||
borderColor="border-[#262626]"
|
||||
textAlign="center"
|
||||
>
|
||||
<Text size="3xl" weight="bold" color={stat.color} block mb={1}>{stat.value}</Text>
|
||||
<Text size="xs" color="text-gray-500" uppercase letterSpacing="0.05em">{stat.label}</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Box } from '@/ui/primitives/Box';
|
||||
import { Surface } from '@/ui/primitives/Surface';
|
||||
import { BarChart3, TrendingUp, User } from 'lucide-react';
|
||||
|
||||
export type ProfileTab = 'overview' | 'stats' | 'ratings';
|
||||
|
||||
interface ProfileTabsProps {
|
||||
activeTab: ProfileTab;
|
||||
onTabChange: (tab: ProfileTab) => void;
|
||||
}
|
||||
|
||||
export function ProfileTabs({ activeTab, onTabChange }: ProfileTabsProps) {
|
||||
return (
|
||||
<Surface variant="muted" rounded="xl" padding={1} style={{ backgroundColor: 'rgba(38, 38, 38, 0.5)', border: '1px solid #262626', width: 'fit-content' }}>
|
||||
<Box style={{ display: 'flex', alignItems: 'center', gap: '0.25rem' }}>
|
||||
<Button
|
||||
variant={activeTab === 'overview' ? 'primary' : 'ghost'}
|
||||
onClick={() => onTabChange('overview')}
|
||||
size="sm"
|
||||
icon={<Icon icon={User} size={4} />}
|
||||
>
|
||||
Overview
|
||||
</Button>
|
||||
<Button
|
||||
variant={activeTab === 'stats' ? 'primary' : 'ghost'}
|
||||
onClick={() => onTabChange('stats')}
|
||||
size="sm"
|
||||
icon={<Icon icon={BarChart3} size={4} />}
|
||||
>
|
||||
Detailed Stats
|
||||
</Button>
|
||||
<Button
|
||||
variant={activeTab === 'ratings' ? 'primary' : 'ghost'}
|
||||
onClick={() => onTabChange('ratings')}
|
||||
size="sm"
|
||||
icon={<Icon icon={TrendingUp} size={4} />}
|
||||
>
|
||||
Ratings
|
||||
</Button>
|
||||
</Box>
|
||||
</Surface>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user