Files
gridpilot.gg/apps/website/ui/StatCard.tsx
2026-01-19 12:35:16 +01:00

89 lines
2.4 KiB
TypeScript

import { LucideIcon } from 'lucide-react';
import { ReactNode } from 'react';
import { Box } from './Box';
import { Card } from './Card';
import { Icon } from './Icon';
import { Text } from './Text';
export interface StatCardProps {
label: string;
value: string | number;
icon?: LucideIcon;
intent?: 'primary' | 'success' | 'warning' | 'critical' | 'telemetry' | 'low' | 'high' | 'med';
variant?: 'default' | 'dark' | 'muted' | 'glass' | 'outline' | 'blue' | 'green' | 'orange';
font?: 'sans' | 'mono';
trend?: {
value: number;
isPositive: boolean;
};
footer?: ReactNode;
suffix?: string;
prefix?: string;
delay?: number;
}
export const StatCard = ({
label,
value,
icon,
intent: intentProp,
variant = 'default',
font = 'sans',
trend,
footer,
suffix,
prefix,
delay
}: StatCardProps) => {
const variantMap: Record<string, { variant: any, intent: any }> = {
blue: { variant: 'default', intent: 'primary' },
green: { variant: 'default', intent: 'success' },
orange: { variant: 'default', intent: 'warning' },
};
const mapped = variantMap[variant as string] || { variant, intent: intentProp || 'primary' };
const finalVariant = mapped.variant;
const finalIntent = mapped.intent;
return (
<Card variant={finalVariant}>
<Box display="flex" alignItems="start" justifyContent="between" marginBottom={4}>
<Box>
<Text size="xs" weight="bold" variant="low" uppercase>
{label}
</Text>
<Text size="2xl" weight="bold" variant={finalIntent as any || 'high'} font={font} block marginTop={1}>
{prefix}{value}{suffix}
</Text>
</Box>
{icon && (
<Box padding={2} rounded="lg" bg="var(--ui-color-bg-surface-muted)">
<Icon icon={icon} size={5} intent={finalIntent} />
</Box>
)}
</Box>
{trend && (
<Box display="flex" alignItems="center" gap={1} marginBottom={footer ? 4 : 0}>
<Text
size="xs"
weight="bold"
variant={trend.isPositive ? 'success' : 'critical'}
>
{trend.isPositive ? '+' : '-'}{Math.abs(trend.value)}%
</Text>
<Text size="xs" variant="low">
vs last period
</Text>
</Box>
)}
{footer && (
<Box borderTop paddingTop={4}>
{footer}
</Box>
)}
</Card>
);
};