73 lines
1.9 KiB
TypeScript
73 lines
1.9 KiB
TypeScript
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { LucideIcon } from 'lucide-react';
|
|
import { ReactNode } from 'react';
|
|
|
|
interface KPICardProps {
|
|
title: string;
|
|
value: string | number;
|
|
icon: LucideIcon;
|
|
iconBgColor: string;
|
|
iconColor: string;
|
|
subtitle?: string;
|
|
children?: ReactNode;
|
|
testId?: string;
|
|
onClick?: () => void;
|
|
}
|
|
|
|
export function KPICard({
|
|
title,
|
|
value,
|
|
icon: Icon,
|
|
iconBgColor,
|
|
iconColor,
|
|
subtitle,
|
|
children,
|
|
testId = 'kpi-card',
|
|
onClick
|
|
}: KPICardProps) {
|
|
return (
|
|
<Card
|
|
className="hover:shadow-lg transition-all duration-200 shadow-md cursor-pointer h-full flex flex-col"
|
|
onClick={onClick}
|
|
data-testid={testId}
|
|
>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle
|
|
className="text-sm font-medium text-muted-foreground"
|
|
data-testid={`${testId}-title`}
|
|
>
|
|
{title}
|
|
</CardTitle>
|
|
<div className={`p-1.5 sm:p-2 rounded-lg ${iconBgColor}`} data-testid={`${testId}-icon-wrapper`}>
|
|
<Icon
|
|
className={`h-4 w-4 sm:h-4 sm:w-4 ${iconColor}`}
|
|
data-testid={`${testId}-icon`}
|
|
/>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="flex flex-col flex-1 py-3">
|
|
<div
|
|
className="text-xl sm:text-2xl font-bold text-gray-900 mb-2"
|
|
data-testid={`${testId}-value`}
|
|
>
|
|
{value}
|
|
</div>
|
|
{subtitle && (
|
|
<div
|
|
className="text-xs text-muted-foreground mb-2"
|
|
data-testid={`${testId}-subtitle`}
|
|
>
|
|
{subtitle}
|
|
</div>
|
|
)}
|
|
{children && (
|
|
<div className="flex-1 flex flex-col" data-testid={`${testId}-children`}>
|
|
{children}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|