Add statcard variants
This commit is contained in:
@@ -351,8 +351,8 @@ const MiniStatCards = ({
|
||||
if (loading && !stats) {
|
||||
return (
|
||||
<Card className="w-full bg-white dark:bg-gray-900/60 backdrop-blur-sm">
|
||||
<CardContent className="p-4 pt-0">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<CardContent className="pb-2">
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<SkeletonCard key={i} />
|
||||
))}
|
||||
@@ -387,7 +387,7 @@ const MiniStatCards = ({
|
||||
description={
|
||||
stats?.periodProgress < 100 ? (
|
||||
<div className="flex items-center gap-1 text-sm">
|
||||
<span>Proj: </span>
|
||||
<span>Projected: </span>
|
||||
{projectionLoading ? (
|
||||
<Skeleton className="h-4 w-15" />
|
||||
) : (
|
||||
@@ -398,44 +398,38 @@ const MiniStatCards = ({
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
progress={
|
||||
stats?.periodProgress < 100 ? stats.periodProgress : undefined
|
||||
}
|
||||
trend={
|
||||
projectionLoading && stats?.periodProgress < 100
|
||||
? undefined
|
||||
: revenueTrend?.trend
|
||||
}
|
||||
trendValue={
|
||||
revenueTrend?.value ? formatPercentage(revenueTrend.value) : null
|
||||
}
|
||||
progress={stats?.periodProgress < 100 ? stats.periodProgress : undefined}
|
||||
trend={projectionLoading && stats?.periodProgress < 100 ? undefined : revenueTrend?.trend}
|
||||
trendValue={revenueTrend?.value ? formatPercentage(revenueTrend.value) : null}
|
||||
colorClass="text-green-600 dark:text-green-400"
|
||||
icon={DollarSign}
|
||||
iconColor="text-green-500"
|
||||
onDetailsClick={() => setSelectedMetric("revenue")}
|
||||
isLoading={loading || !stats}
|
||||
variant="mini"
|
||||
background="bg-gradient-to-br from-emerald-700/90 to-green-700/90 backdrop-blur-md hover:from-emerald-900 hover:to-green-800"
|
||||
/>
|
||||
|
||||
<StatCard
|
||||
title="Orders"
|
||||
value={stats?.orderCount}
|
||||
description={`${stats?.itemCount} items`}
|
||||
description={`${stats?.itemCount} total items`}
|
||||
trend={orderTrend?.trend}
|
||||
trendValue={
|
||||
orderTrend?.value ? formatPercentage(orderTrend.value) : null
|
||||
}
|
||||
trendValue={orderTrend?.value ? formatPercentage(orderTrend.value) : null}
|
||||
colorClass="text-blue-600 dark:text-blue-400"
|
||||
icon={ShoppingCart}
|
||||
iconColor="text-blue-500"
|
||||
onDetailsClick={() => setSelectedMetric("orders")}
|
||||
isLoading={loading || !stats}
|
||||
variant="mini"
|
||||
background="bg-gradient-to-br from-blue-700/90 to-indigo-700/90 backdrop-blur-md hover:from-blue-900 hover:to-indigo-800"
|
||||
/>
|
||||
|
||||
<StatCard
|
||||
title="AOV"
|
||||
value={stats?.averageOrderValue?.toFixed(2)}
|
||||
valuePrefix="$"
|
||||
description={`${stats?.averageItemsPerOrder?.toFixed(1)}/order`}
|
||||
description={`${stats?.averageItemsPerOrder?.toFixed(1)} items per order`}
|
||||
trend={aovTrend?.trend}
|
||||
trendValue={aovTrend?.value ? formatPercentage(aovTrend.value) : null}
|
||||
colorClass="text-purple-600 dark:text-purple-400"
|
||||
@@ -443,6 +437,8 @@ const MiniStatCards = ({
|
||||
iconColor="text-purple-500"
|
||||
onDetailsClick={() => setSelectedMetric("average_order")}
|
||||
isLoading={loading || !stats}
|
||||
variant="mini"
|
||||
background="bg-gradient-to-br from-purple-700/90 to-violet-700/90 backdrop-blur-md hover:from-purple-900 hover:to-violet-800"
|
||||
/>
|
||||
|
||||
<StatCard
|
||||
@@ -454,6 +450,8 @@ const MiniStatCards = ({
|
||||
iconColor="text-teal-500"
|
||||
onDetailsClick={() => setSelectedMetric("shipping")}
|
||||
isLoading={loading || !stats}
|
||||
variant="mini"
|
||||
background="bg-gradient-to-br from-teal-700/90 to-cyan-700/90 backdrop-blur-md hover:from-teal-900 hover:to-cyan-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -979,28 +979,51 @@ const StatCard = ({
|
||||
onDetailsClick,
|
||||
isLoading = false,
|
||||
progress,
|
||||
}) => (
|
||||
variant = "default",
|
||||
background
|
||||
}) => {
|
||||
const variants = {
|
||||
default: "bg-white dark:bg-gray-900/60",
|
||||
mini: background || "bg-gradient-to-br from-gray-900/90 to-gray-800/90 backdrop-blur-md hover:from-gray-900 hover:to-gray-800"
|
||||
};
|
||||
|
||||
const titleVariants = {
|
||||
default: "text-sm font-medium text-gray-600 dark:text-gray-300",
|
||||
mini: "text-sm font-bold text-gray-100"
|
||||
};
|
||||
|
||||
const valueVariants = {
|
||||
default: "text-2xl font-bold",
|
||||
mini: "text-2xl font-extrabold text-white"
|
||||
};
|
||||
|
||||
const descriptionVariants = {
|
||||
default: "text-sm text-muted-foreground",
|
||||
mini: "text-sm font-semibold text-gray-200"
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={`${className} bg-white dark:bg-gray-900/60 backdrop-blur-sm ${
|
||||
className={`${className} ${variants[variant]} ${
|
||||
onClick || onDetailsClick
|
||||
? "cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"
|
||||
? "cursor-pointer transition-all hover:brightness-110"
|
||||
: ""
|
||||
}`}
|
||||
onClick={onDetailsClick || onClick}
|
||||
>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4 pb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<CardTitle className="text-sm font-medium text-gray-600 dark:text-gray-300">
|
||||
<CardTitle className={titleVariants[variant]}>
|
||||
{title}
|
||||
</CardTitle>
|
||||
{info && (
|
||||
<Info
|
||||
className="w-4 h-4 text-muted-foreground cursor-help"
|
||||
className={`w-4 h-4 ${variant === 'mini' ? 'text-gray-300' : 'text-muted-foreground'} cursor-help`}
|
||||
title={info}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{Icon && <Icon className={`h-4 w-4 ${iconColor}`} />}
|
||||
{Icon && <Icon className={`h-4 w-4 ${variant === 'mini' ? 'text-gray-100' : iconColor}`} />}
|
||||
</CardHeader>
|
||||
<CardContent className="p-4 pt-0">
|
||||
{isLoading ? (
|
||||
@@ -1011,17 +1034,17 @@ const StatCard = ({
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<div className={`text-2xl font-bold ${colorClass}`}>
|
||||
<div className={`${valueVariants[variant]} ${colorClass}`}>
|
||||
{valuePrefix}
|
||||
{value}
|
||||
{valueSuffix}
|
||||
</div>
|
||||
{description && (
|
||||
<div className="text-sm text-muted-foreground mt-2 items-center gap-2 flex">
|
||||
<div className={`mt-2 items-center gap-2 flex ${descriptionVariants[variant]}`}>
|
||||
{description}
|
||||
{trend && (
|
||||
<span
|
||||
className={`flex items-center gap-1 ${
|
||||
className={`flex items-center gap-0 ${
|
||||
trend === "up"
|
||||
? "text-emerald-600 dark:text-emerald-400"
|
||||
: "text-rose-600 dark:text-rose-400"
|
||||
@@ -1044,7 +1067,8 @@ const StatCard = ({
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
// Add this before the StatCards component
|
||||
const useDataCache = () => {
|
||||
|
||||
Reference in New Issue
Block a user