69 lines
1.7 KiB
TypeScript
69 lines
1.7 KiB
TypeScript
import React, { ReactNode } from 'react';
|
|
import { Card } from './Card';
|
|
import { Box } from './primitives/Box';
|
|
import { Text } from './Text';
|
|
import { Icon } from './Icon';
|
|
import { LucideIcon } from 'lucide-react';
|
|
|
|
export interface StatCardProps {
|
|
label: string;
|
|
value: string | number;
|
|
icon?: LucideIcon;
|
|
intent?: 'primary' | 'success' | 'warning' | 'critical' | 'telemetry';
|
|
trend?: {
|
|
value: number;
|
|
isPositive: boolean;
|
|
};
|
|
footer?: ReactNode;
|
|
}
|
|
|
|
export const StatCard = ({
|
|
label,
|
|
value,
|
|
icon,
|
|
intent = 'primary',
|
|
trend,
|
|
footer
|
|
}: StatCardProps) => {
|
|
return (
|
|
<Card variant="default">
|
|
<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="high" block marginTop={1}>
|
|
{value}
|
|
</Text>
|
|
</Box>
|
|
{icon && (
|
|
<Box padding={2} rounded="lg" bg="var(--ui-color-bg-surface-muted)">
|
|
<Icon icon={icon} size={5} intent={intent} />
|
|
</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>
|
|
);
|
|
};
|