feat(dashboard): add date filter and stat groups components
- Implemented DashboardDateFilter component for selecting date ranges. - Created DashboardStatGroups component to display various dashboard statistics. - Added dashboard chart data processing logic to support new chart specifications. - Introduced reusable Chart components for rendering different types of charts.
This commit is contained in:
@@ -58,10 +58,8 @@ export interface StatCardProps {
|
||||
valueClassName?: string
|
||||
/** Extra classes for the icon tile, e.g. a warning tint instead of the primary one. */
|
||||
iconClassName?: string
|
||||
/** Makes the card a toggle button that filters the list below it (click again to clear). */
|
||||
onClick?: () => void
|
||||
/** The card's filter is the one currently applied — highlighted and announced as pressed. */
|
||||
active?: boolean
|
||||
/** Tighter padding and a smaller icon / text, for dense rows such as the dashboard. */
|
||||
compact?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,8 +78,7 @@ export function StatCard({
|
||||
className,
|
||||
valueClassName,
|
||||
iconClassName,
|
||||
onClick,
|
||||
active = false,
|
||||
compact = false,
|
||||
}: StatCardProps) {
|
||||
const hasDelta = typeof delta === "number" && delta !== 0
|
||||
const isPositive = hasDelta && delta > 0
|
||||
@@ -98,31 +95,36 @@ export function StatCard({
|
||||
? { type: "button" as const, onClick, "aria-pressed": active, title: active ? "Click to clear this filter" : "Click to filter the list" }
|
||||
: {})}
|
||||
className={cn(
|
||||
"@container min-w-0 rounded-2xl bg-card px-4 py-5 text-left shadow-sm ring-1 ring-foreground/10 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:ring-primary/30",
|
||||
onClick && "w-full cursor-pointer focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
|
||||
className,
|
||||
// After className so the "selected" look wins over a card's own tint.
|
||||
active && "bg-primary/5 ring-2 ring-primary"
|
||||
"@container min-w-0 rounded-2xl bg-card shadow-sm ring-1 ring-foreground/10 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:ring-primary/30",
|
||||
compact ? "px-3 py-2.5" : "px-4 py-5",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* Wide enough: icon beside the text. Narrow (e.g. two per row on a phone): icon above, so the
|
||||
value keeps the full width instead of being cut off. */}
|
||||
<div className="flex flex-col gap-2.5 @[12.5rem]:flex-row @[12.5rem]:items-center @[12.5rem]:gap-3.5">
|
||||
<div
|
||||
className={cn(
|
||||
"flex",
|
||||
// Compact cards keep the icon beside the text at every width; the regular card stacks it above when narrow.
|
||||
compact ? "flex-row items-center gap-2.5" : "flex-col gap-2.5 @[12.5rem]:flex-row @[12.5rem]:items-center @[12.5rem]:gap-3.5"
|
||||
)}
|
||||
>
|
||||
{Icon && (
|
||||
<div
|
||||
className={cn(
|
||||
"flex size-9 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary @[12.5rem]:size-11",
|
||||
"flex shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary",
|
||||
compact ? "size-9" : "size-9 @[12.5rem]:size-11",
|
||||
iconClassName
|
||||
)}
|
||||
>
|
||||
<Icon className="size-4.5 @[12.5rem]:size-5" aria-hidden />
|
||||
<Icon className={compact ? "size-4" : "size-4.5 @[12.5rem]:size-5"} aria-hidden />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<p className="truncate text-sm text-muted-foreground">{label}</p>
|
||||
<p className={cn("text-muted-foreground", compact ? "text-xs leading-tight" : "truncate text-sm")}>{label}</p>
|
||||
<div className="flex items-end justify-between gap-2">
|
||||
<p className={cn("text-base leading-tight font-semibold tracking-tight wrap-break-word text-foreground @[12.5rem]:text-lg", valueClassName)}>
|
||||
<p className={cn("leading-tight font-semibold tracking-tight wrap-break-word text-foreground", compact ? "text-base" : "text-base @[12.5rem]:text-lg", valueClassName)}>
|
||||
{formatValue(value)}
|
||||
</p>
|
||||
{trend && trend.length > 1 && <Sparkline points={trend} />}
|
||||
|
||||
Reference in New Issue
Block a user