Add statcard variants

This commit is contained in:
2025-01-01 14:22:26 -05:00
parent 42f5033173
commit 9b5c59c61d
2 changed files with 106 additions and 84 deletions

View File

@@ -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>

View File

@@ -979,72 +979,96 @@ const StatCard = ({
onDetailsClick,
isLoading = false,
progress,
}) => (
<Card
className={`${className} bg-white dark:bg-gray-900/60 backdrop-blur-sm ${
onClick || onDetailsClick
? "cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors"
: ""
}`}
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">
{title}
</CardTitle>
{info && (
<Info
className="w-4 h-4 text-muted-foreground cursor-help"
title={info}
/>
)}
</div>
{Icon && <Icon className={`h-4 w-4 ${iconColor}`} />}
</CardHeader>
<CardContent className="p-4 pt-0">
{isLoading ? (
<>
<Skeleton className="h-8 w-32 mb-2 bg-muted rounded-sm" />
<Skeleton className="h-4 w-24 bg-muted rounded-sm" />
</>
) : (
<div className="space-y-2">
<div>
<div className={`text-2xl font-bold ${colorClass}`}>
{valuePrefix}
{value}
{valueSuffix}
</div>
{description && (
<div className="text-sm text-muted-foreground mt-2 items-center gap-2 flex">
{description}
{trend && (
<span
className={`flex items-center gap-1 ${
trend === "up"
? "text-emerald-600 dark:text-emerald-400"
: "text-rose-600 dark:text-rose-400"
}`}
>
{trend === "up" ? (
<ArrowUp className="w-4 h-4" />
) : (
<ArrowDown className="w-4 h-4" />
)}
{trendPrefix}
{trendValue}
{trendSuffix}
</span>
)}
</div>
)}
</div>
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} ${variants[variant]} ${
onClick || onDetailsClick
? "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={titleVariants[variant]}>
{title}
</CardTitle>
{info && (
<Info
className={`w-4 h-4 ${variant === 'mini' ? 'text-gray-300' : 'text-muted-foreground'} cursor-help`}
title={info}
/>
)}
</div>
)}
</CardContent>
</Card>
);
{Icon && <Icon className={`h-4 w-4 ${variant === 'mini' ? 'text-gray-100' : iconColor}`} />}
</CardHeader>
<CardContent className="p-4 pt-0">
{isLoading ? (
<>
<Skeleton className="h-8 w-32 mb-2 bg-muted rounded-sm" />
<Skeleton className="h-4 w-24 bg-muted rounded-sm" />
</>
) : (
<div className="space-y-2">
<div>
<div className={`${valueVariants[variant]} ${colorClass}`}>
{valuePrefix}
{value}
{valueSuffix}
</div>
{description && (
<div className={`mt-2 items-center gap-2 flex ${descriptionVariants[variant]}`}>
{description}
{trend && (
<span
className={`flex items-center gap-0 ${
trend === "up"
? "text-emerald-600 dark:text-emerald-400"
: "text-rose-600 dark:text-rose-400"
}`}
>
{trend === "up" ? (
<ArrowUp className="w-4 h-4" />
) : (
<ArrowDown className="w-4 h-4" />
)}
{trendPrefix}
{trendValue}
{trendSuffix}
</span>
)}
</div>
)}
</div>
</div>
)}
</CardContent>
</Card>
);
};
// Add this before the StatCards component
const useDataCache = () => {