# Dashboard 4

> Commerce dashboard with revenue trends, refund rates, category ranking pie, and quick action items.

A shadcn/ui block from the [@efferd](https://efferd.com/) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/efferd/dashboard-4).

- Page, with a live preview: https://shadcn.exchange/r/efferd/dashboard-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/efferd/dashboard-4-light-bf43b3590cb11cc0.png
- Registry item: https://efferd.com/r/dashboard-4.json
- Categories: dashboard
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://efferd.com/r/dashboard-4.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `badge`, `button`, `card`, `chart`, `item`, `select`, `@efferd/app-shell-4`, `@efferd/formater`

## Files

### `registry/blocks/dashboard/4/category-rank-chart.tsx`

```tsx
"use client";

import React from "react";
import { LabelList, Pie, PieChart } from "recharts";
import {
	Card,
	CardContent,
	CardDescription,
	CardHeader,
	CardTitle,
} from "@/components/ui/card";
import {
	type ChartConfig,
	ChartContainer,
	ChartLegend,
	ChartLegendContent,
} from "@/components/ui/chart";

export type CategoryMixDatum = {
	category: string;
	/** Percent of total (0–100). Usually sums to 100. */
	share: number;
};

const SLICE_PALETTE = [
	"var(--chart-1)",
	"var(--chart-2)",
	"var(--chart-3)",
	"var(--chart-4)",
	"var(--chart-5)",
] as const;

/** At most four named slices; remaining share rolls into a fifth slice, “Others”. */
const MAX_NAMED_SLICES = 4;

/** Rolling window for the mix (days). */
const periodDays = 7;

/** Demo mix (sums to 100). Replace or pass `data` on the component. */
const data = [
	{ category: "Apparel", share: 22 },
	{ category: "Accessories", share: 22 },
	{ category: "Footwear", share: 18 },
	{ category: "Home & living", share: 14 },
	{ category: "Beauty", share: 11 },
	{ category: "Outlet", share: 8 },
	{ category: "Sports", share: 5 },
] as const satisfies readonly CategoryMixDatum[];

/** Sort by share descending, keep top four, merge the rest into “Others” (max five slices). */
function consolidateTopFourAndOthers(
	data: readonly CategoryMixDatum[]
): CategoryMixDatum[] {
	if (data.length <= MAX_NAMED_SLICES) {
		return [...data];
	}

	const sorted = [...data].sort((a, b) => b.share - a.share);
	const head = sorted.slice(0, MAX_NAMED_SLICES);
	const tail = sorted.slice(MAX_NAMED_SLICES);
	const othersShare = tail.reduce((sum, row) => sum + row.share, 0);

	return [...head, { category: "Others", share: othersShare }];
}

type SliceRow = {
	key: string;
	category: string;
	share: number;
	fill: string;
};

function buildSlices(data: readonly CategoryMixDatum[]): {
	chartConfig: ChartConfig;
	pieData: SliceRow[];
} {
	const chartConfig: ChartConfig = {
		share: {
			label: "Share",
		},
	};

	const pieData: SliceRow[] = data.map((row, i) => {
		const key = `s${i}`;
		const color = SLICE_PALETTE[i % SLICE_PALETTE.length];
		chartConfig[key] = {
			label: row.category,
			color,
		};
		return {
			key,
			category: row.category,
			share: row.share,
			fill: `var(--color-${key})`,
		};
	});

	return { chartConfig, pieData };
}

export function CategoryRankChart() {
	const { chartConfig, pieData } = React.useMemo(
		() => buildSlices(consolidateTopFourAndOthers(data)),
		[]
	);

	return (
		<Card>
			<CardHeader>
				<CardTitle >Revenue Share by Category</CardTitle>
				<CardDescription>Last {periodDays} days.</CardDescription>
			</CardHeader>
			<CardContent className="my-auto p-0">
				<ChartContainer
					className="aspect-auto h-72 w-full"
					config={chartConfig}
				>
					<PieChart accessibilityLayer>
						<Pie
							cornerRadius={4}
							data={pieData}
							dataKey="share"
							innerRadius={50}
							nameKey="key"
							outerRadius="88%"
							stroke="var(--card)"
							strokeWidth={4}
						>
							<LabelList
								className="fill-background font-medium"
								dataKey="share"
								fill="currentColor"
								fontWeight={500}
								formatter={(label) => {
									const n = Number(label);
									return Number.isFinite(n) ? `${n}%` : String(label ?? "");
								}}
								position="inside"
								stroke="none"
							/>
						</Pie>
						<ChartLegend
							content={
								<ChartLegendContent
									className="flex flex-wrap gap-3 pt-2"
									nameKey="key"
								/>
							}
						/>
					</PieChart>
				</ChartContainer>
			</CardContent>
		</Card>
	);
}
```

### `registry/blocks/dashboard/4/dashboard.tsx`

```tsx
import { CategoryRankChart } from "@/components/category-rank-chart";
import { QuickActions } from "@/components/quick-actions";
import { RefundReturnRateChart } from "@/components/refund-return-rate-chart";
import { RevenueChart } from "@/components/revenue-chart";
import { DashboardStats } from "@/components/stats";

export function Dashboard() {
	return (
		<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
			<DashboardStats />
			<RevenueChart />
			<RefundReturnRateChart />
			<CategoryRankChart />
			<QuickActions />
		</div>
	);
}
```

### `registry/blocks/dashboard/4/quick-actions.tsx`

```tsx
import {
	Card,
	CardContent,
	CardDescription,
	CardHeader,
	CardTitle,
} from "@/components/ui/card";
import {
	Item,
	ItemActions,
	ItemContent,
	ItemDescription,
	ItemGroup,
	ItemMedia,
	ItemTitle,
} from "@/components/ui/item";

const actions = [
	{
		title: "Add product",
		description: "Create a new SKU.",
		href: "#",
		icon: (
			<IconPlaceholder
				aria-hidden="true"
				hugeicons="Add01Icon"
				lucide="PackagePlusIcon"
				phosphor="PackageIcon"
				remixicon="RiAddBoxLine"
				tabler="IconPackageImport"
			/>
		),
	},
	{
		title: "Review unfulfilled",
		description: "Orders waiting to ship.",
		href: "#",
		icon: (
			<IconPlaceholder
				aria-hidden="true"
				hugeicons="DeliveryTruck01Icon"
				lucide="TruckIcon"
				phosphor="TruckIcon"
				remixicon="RiTruckLine"
				tabler="IconTruckDelivery"
			/>
		),
	},
	{
		title: "Store settings",
		description: "Payments, checkouts etc.",
		href: "#",
		icon: (
			<IconPlaceholder
				aria-hidden="true"
				hugeicons="Settings02Icon"
				lucide="SettingsIcon"
				phosphor="GearSixIcon"
				remixicon="RiSettings3Line"
				tabler="IconSettings"
			/>
		),
	},
	{
		title: "Export sales",
		description: "CSV for accountings.",
		href: "#",
		icon: (
			<IconPlaceholder
				aria-hidden="true"
				hugeicons="Download01Icon"
				lucide="DownloadIcon"
				phosphor="DownloadSimpleIcon"
				remixicon="RiDownloadLine"
				tabler="IconDownload"
			/>
		),
	},
] as const;

export function QuickActions() {
	return (
		<Card>
			<CardHeader>
				<CardTitle >Quick actions</CardTitle>
				<CardDescription>Shortcuts to same destinations.</CardDescription>
			</CardHeader>
			<CardContent>
				<ItemGroup className="gap-0">
					{actions.map((a) => (
						<Item asChild key={a.title} size="sm">
							<a href={a.href}>
								<ItemMedia variant="icon">{a.icon}</ItemMedia>
								<ItemContent>
									<ItemTitle>{a.title}</ItemTitle>
									<ItemDescription className="line-clamp-1">
										{a.description}
									</ItemDescription>
								</ItemContent>
								<ItemActions>
									<IconPlaceholder
										aria-hidden="true"
										className="size-4 shrink-0 text-muted-foreground"
										hugeicons="ArrowRight01Icon"
										lucide="ChevronRightIcon"
										phosphor="CaretRightIcon"
										remixicon="RiArrowRightSLine"
										tabler="IconChevronRight"
									/>
								</ItemActions>
							</a>
						</Item>
					))}
				</ItemGroup>
			</CardContent>
		</Card>
	);
}
```

### `registry/blocks/dashboard/4/refund-return-rate-chart.tsx`

```tsx
"use client";

import { CartesianGrid, Line, LineChart, XAxis } from "recharts";
import { Button } from "@/components/ui/button";
import {
	Card,
	CardContent,
	CardDescription,
	CardFooter,
	CardHeader,
	CardTitle,
} from "@/components/ui/card";
import {
	type ChartConfig,
	ChartContainer,
	ChartTooltip,
	ChartTooltipContent,
} from "@/components/ui/chart";
import { Delta, DeltaIcon, DeltaValue } from "@/components/delta";

/** Daily return rate (% of fulfilled orders returned), last 7 days (demo). */
const returnDaily7 = [
	{ day: "Mon", returnRate: 2.2 },
	{ day: "Tue", returnRate: 1.5 },
	{ day: "Wed", returnRate: 3.1 },
	{ day: "Thu", returnRate: 4.8 },
	{ day: "Fri", returnRate: 2.4 },
	{ day: "Sat", returnRate: 3.2 },
	{ day: "Sun", returnRate: 3.9 },
] as const;

/** Share of orders that were refunded over the same window (demo). */
const REFUNDED_SHARE_OF_ORDERS_PCT = 2.6;

const chartConfig = {
	returnRate: {
		label: "Return %",
		color: "var(--chart-1)",
	},
} satisfies ChartConfig;

export function RefundReturnRateChart() {
	const first = returnDaily7[0];
	const lastW = returnDaily7.at(-1) ?? first;
	const returnTrendPct =
		first.returnRate > 0
			? ((lastW.returnRate - first.returnRate) / first.returnRate) * 100
			: 0;

	return (
		<Card className="md:col-span-2">
			<CardHeader className="flex flex-col sm:flex-row sm:items-start sm:justify-between">
				<div className="space-y-1">
					<CardTitle >Return rate</CardTitle>
					<CardDescription>Last 7 days</CardDescription>
				</div>
				<div className="space-y-1">
					<CardTitle className="text-right">
						{REFUNDED_SHARE_OF_ORDERS_PCT}%
					</CardTitle>
					<CardDescription>of orders refunded</CardDescription>
				</div>
			</CardHeader>
			<CardContent className="mt-auto">
				<ChartContainer
					className="aspect-auto h-56 w-full"
					config={chartConfig}
				>
					<LineChart
						accessibilityLayer
						data={returnDaily7}
						margin={{ left: 12, right: 12, top: 12, bottom: 0 }}
					>
						<CartesianGrid horizontal={false} strokeDasharray="3 3" />
						<XAxis
							axisLine={false}
							dataKey="day"
							interval={1}
							minTickGap={8}
							tickLine={false}
							tickMargin={8}
						/>
						<ChartTooltip content={<ChartTooltipContent indicator="line" />} />
						<Line
							dataKey="returnRate"
							dot={false}
							stroke="var(--color-returnRate)"
							strokeWidth={2.5}
							type="monotone"
						/>
					</LineChart>
				</ChartContainer>
			</CardContent>
			<CardFooter>
				<div className="flex min-w-0 flex-1 flex-wrap items-center gap-1 text-muted-foreground text-xs">
					<Delta value={returnTrendPct}>
						<DeltaIcon />
						<DeltaValue />
					</Delta>
					<span className="inline-flex min-w-0 text-pretty">
						vs first day (last 7 days)
					</span>
				</div>
				<Button
					asChild
					className="text-muted-foreground"
					size="xs"
					variant="ghost"
				>
					<a href="#/orders/returns">
						Returns desk
						<IconPlaceholder
							aria-hidden="true"
							data-icon="inline-end"
							hugeicons="ArrowRight02Icon"
							lucide="ArrowRightIcon"
							phosphor="ArrowRightIcon"
							remixicon="RiArrowRightLine"
							tabler="IconArrowRight"
						/>
					</a>
				</Button>
			</CardFooter>
		</Card>
	);
}
```

### `registry/blocks/dashboard/4/revenue-chart-data.ts`

```ts
export const revenueChartDemo = [
	{ date: "2026-01-21", revenue: 10_135 },
	{ date: "2026-01-22", revenue: 10_415 },
	{ date: "2026-01-23", revenue: 10_349 },
	{ date: "2026-01-24", revenue: 10_011 },
	{ date: "2026-01-25", revenue: 9657 },
	{ date: "2026-01-26", revenue: 9548 },
	{ date: "2026-01-27", revenue: 9770 },
	{ date: "2026-01-28", revenue: 10_177 },
	{ date: "2026-01-29", revenue: 10_500 },
	{ date: "2026-01-30", revenue: 10_539 },
	{ date: "2026-01-31", revenue: 10_302 },
	{ date: "2026-02-01", revenue: 9993 },
	{ date: "2026-02-02", revenue: 9858 },
	{ date: "2026-02-03", revenue: 10_014 },
	{ date: "2026-02-04", revenue: 10_367 },
	{ date: "2026-02-05", revenue: 10_700 },
	{ date: "2026-02-06", revenue: 10_828 },
	{ date: "2026-02-07", revenue: 10_739 },
	{ date: "2026-02-08", revenue: 10_204 },
	{ date: "2026-02-09", revenue: 10_137 },
	{ date: "2026-02-10", revenue: 10_288 },
	{ date: "2026-02-11", revenue: 10_583 },
	{ date: "2026-02-12", revenue: 11_391 },
	{ date: "2026-02-13", revenue: 11_548 },
	{ date: "2026-02-14", revenue: 11_569 },
	{ date: "2026-02-15", revenue: 11_590 },
	{ date: "2026-02-16", revenue: 11_748 },
	{ date: "2026-02-17", revenue: 12_056 },
	{ date: "2026-02-18", revenue: 12_380 },
	{ date: "2026-02-19", revenue: 12_539 },
	{ date: "2026-02-20", revenue: 12_448 },
	{ date: "2026-02-21", revenue: 12_200 },
	{ date: "2026-02-22", revenue: 12_016 },
	{ date: "2026-02-23", revenue: 12_085 },
	{ date: "2026-02-24", revenue: 12_845 },
	{ date: "2026-02-25", revenue: 13_283 },
	{ date: "2026-02-26", revenue: 13_160 },
	{ date: "2026-02-27", revenue: 13_188 },
	{ date: "2026-02-28", revenue: 13_031 },
	{ date: "2026-03-01", revenue: 12_930 },
	{ date: "2026-03-02", revenue: 13_113 },
	{ date: "2026-03-03", revenue: 13_618 },
	{ date: "2026-03-04", revenue: 14_259 },
	{ date: "2026-03-05", revenue: 14_745 },
	{ date: "2026-03-06", revenue: 14_880 },
	{ date: "2026-03-07", revenue: 14_692 },
	{ date: "2026-03-08", revenue: 14_414 },
	{ date: "2026-03-09", revenue: 14_316 },
	{ date: "2026-03-10", revenue: 14_519 },
	{ date: "2026-03-11", revenue: 14_924 },
	{ date: "2026-03-12", revenue: 15_299 },
	{ date: "2026-03-13", revenue: 15_451 },
	{ date: "2026-03-14", revenue: 14_847 },
	{ date: "2026-03-15", revenue: 15_211 },
	{ date: "2026-03-16", revenue: 15_192 },
	{ date: "2026-03-17", revenue: 15_411 },
	{ date: "2026-03-18", revenue: 15_789 },
	{ date: "2026-03-19", revenue: 16_140 },
	{ date: "2026-03-20", revenue: 16_317 },
	{ date: "2026-03-21", revenue: 16_321 },
	{ date: "2026-03-22", revenue: 16_020 },
	{ date: "2026-03-23", revenue: 16_380 },
	{ date: "2026-03-24", revenue: 16_060 },
	{ date: "2026-03-25", revenue: 16_710 },
	{ date: "2026-03-26", revenue: 16_340 },
	{ date: "2026-03-27", revenue: 16_890 },
	{ date: "2026-03-28", revenue: 16_530 },
	{ date: "2026-03-29", revenue: 16_995 },
	{ date: "2026-03-30", revenue: 16_720 },
	{ date: "2026-03-31", revenue: 17_115 },
	{ date: "2026-04-01", revenue: 16_865 },
	{ date: "2026-04-02", revenue: 17_395 },
	{ date: "2026-04-03", revenue: 17_055 },
	{ date: "2026-04-04", revenue: 16_695 },
	{ date: "2026-04-05", revenue: 16_425 },
	{ date: "2026-04-06", revenue: 16_980 },
	{ date: "2026-04-07", revenue: 16_520 },
	{ date: "2026-04-08", revenue: 17_410 },
	{ date: "2026-04-09", revenue: 16_745 },
	{ date: "2026-04-10", revenue: 17_595 },
	{ date: "2026-04-11", revenue: 16_955 },
	{ date: "2026-04-12", revenue: 17_720 },
	{ date: "2026-04-13", revenue: 17_185 },
	{ date: "2026-04-14", revenue: 18_020 },
	{ date: "2026-04-15", revenue: 17_395 },
	{ date: "2026-04-16", revenue: 17_880 },
	{ date: "2026-04-17", revenue: 17_210 },
	{ date: "2026-04-18", revenue: 18_240 },
	{ date: "2026-04-19", revenue: 17_420 },
	{ date: "2026-04-20", revenue: 17_785 },
];
```

### `registry/blocks/dashboard/4/revenue-chart.tsx`

```tsx
"use client";

import { useId, useMemo, useState } from "react";
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
import { formatChartAxisTick, formatChartTooltipDate } from "@/lib/formater";
import { Button } from "@/components/ui/button";
import {
	Card,
	CardContent,
	CardFooter,
	CardHeader,
	CardTitle,
} from "@/components/ui/card";
import {
	type ChartConfig,
	ChartContainer,
	ChartTooltip,
	ChartTooltipContent,
} from "@/components/ui/chart";
import {
	Select,
	SelectContent,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from "@/components/ui/select";
import { Delta, DeltaIcon, DeltaValue } from "@/components/delta";
import { revenueChartDemo } from "@/components/revenue-chart-data";

/** Matches `<Select>`; chart uses the last N days of `revenueChartDemo`. */
type PeriodDays = 7 | 14 | 30 | 60 | 90;

// Recharts XAxis: `interval` is tick skip index (0 = all, 1 = every other, …).
const xAxisIntervalByPeriod: Record<PeriodDays, number> = {
	7: 0,
	14: 1,
	30: 3,
	60: 4,
	90: 6,
};

type RevenueRow = {
	date: string;
	revenue: number;
};

const chartConfig = {
	revenue: {
		label: "Revenue",
		color: "var(--chart-1)",
	},
} satisfies ChartConfig;

export function RevenueChart() {
	const chartUid = useId().replace(/:/g, "");
	const idAreaGradient = `revenue-area-grad-${chartUid}`;
	const [periodDays, setPeriodDays] = useState<PeriodDays>(60);

	const chartRows = useMemo(
		() => revenueChartDemo.slice(-periodDays),
		[periodDays]
	);

	// Footer delta: first → last point in the active series (not calendar MoM).
	const growthPct = useMemo(() => {
		const first = chartRows[0]?.revenue ?? 0;
		const last = chartRows.at(-1)?.revenue ?? first;
		if (!first) {
			return 0;
		}
		return ((last - first) / first) * 100;
	}, [chartRows]);

	// Extra spacing so long ranges don’t collide; pairs with `interval` above.
	let xAxisMinTickGap: number | undefined;
	if (periodDays <= 7) {
		xAxisMinTickGap = undefined;
	} else {
		xAxisMinTickGap = Math.max(8, Math.min(52, Math.floor(periodDays / 2)));
	}

	return (
		<Card className="md:col-span-2 lg:col-span-4">
			<CardHeader className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
				<CardTitle className="text-balance">Revenue</CardTitle>
				<Select
					onValueChange={(v) => {
						setPeriodDays(Number(v) as PeriodDays);
					}}
					value={String(periodDays)}
				>
					<SelectTrigger
						aria-label="Revenue time range"
						className="w-full min-w-36 sm:w-fit"
						size="sm"
					>
						<SelectValue placeholder="Range" />
					</SelectTrigger>
					<SelectContent align="end">
						<SelectItem value="7">Last 7 days</SelectItem>
						<SelectItem value="14">Last 14 days</SelectItem>
						<SelectItem value="30">Last 30 days</SelectItem>
						<SelectItem value="60">Last 60 days</SelectItem>
						<SelectItem value="90">Last 90 days</SelectItem>
					</SelectContent>
				</Select>
			</CardHeader>
			<CardContent>
				<ChartContainer
					className="aspect-auto h-60 w-full p-0"
					config={chartConfig}
				>
					<AreaChart
						accessibilityLayer
						// Copy so Recharts always sees a mutable array reference.
						data={[...chartRows]}
						margin={{ left: 24, right: 8, top: 8, bottom: 0 }}
					>
						<defs>
							<linearGradient id={idAreaGradient} x1="0" x2="0" y1="0" y2="1">
								<stop
									offset="0%"
									stopColor="var(--color-revenue)"
									stopOpacity={0.2}
								/>
								<stop
									offset="100%"
									stopColor="var(--color-revenue)"
									stopOpacity={0}
								/>
							</linearGradient>
						</defs>
						<CartesianGrid horizontal={false} strokeDasharray="2 2" />
						<XAxis
							axisLine={false}
							dataKey="date"
							interval={xAxisIntervalByPeriod[periodDays]}
							minTickGap={xAxisMinTickGap}
							tickFormatter={(value) =>
								formatChartAxisTick(String(value), periodDays)
							}
							tickLine={false}
							tickMargin={8}
						/>
						<ChartTooltip
							content={
								<ChartTooltipContent
									className="min-w-36"
									indicator="line"
									labelFormatter={(_, payload) => {
										const row = payload?.[0]?.payload as RevenueRow | undefined;
										if (!row?.date) {
											return "";
										}
										return formatChartTooltipDate(row.date, "short");
									}}
								/>
							}
						/>
						<Area
							dataKey="revenue"
							dot={false}
							fill={`url(#${idAreaGradient})`}
							stroke="var(--color-revenue)"
							strokeWidth={2}
							type="monotone"
						/>
					</AreaChart>
				</ChartContainer>
			</CardContent>
			<CardFooter className="flex items-center justify-between">
				<div className="flex items-center gap-1 text-muted-foreground text-xs">
					<Delta value={growthPct}>
						<DeltaIcon />
						<DeltaValue />
					</Delta>
					<p className="inline-flex text-pretty">
						vs first day in last {periodDays} days.
					</p>
				</div>
				<Button
					asChild
					className="text-muted-foreground"
					size="xs"
					variant="ghost"
				>
					<a href="#/reports">
						View report
						<IconPlaceholder
							aria-hidden="true"
							data-icon="inline-end"
							hugeicons="ArrowRight02Icon"
							lucide="ArrowRightIcon"
							phosphor="ArrowRightIcon"
							remixicon="RiArrowRightLine"
							tabler="IconArrowRight"
						/>
					</a>
				</Button>
			</CardFooter>
		</Card>
	);
}
```

### `registry/blocks/dashboard/4/stats.tsx`

```tsx
import {
	Card,
	CardContent,
	CardFooter,
	CardHeader,
	CardTitle,
} from "@/components/ui/card";
import { Delta, DeltaIcon, DeltaValue } from "@/components/delta";

type Stat = {
	label: string;
	value: string;
	delta: number;
	hint: string;
};

const stats: readonly Stat[] = [
	{
		label: "Total revenue",
		value: "$284,920",
		delta: 8.2,
		hint: "vs prior 30 days",
	},
	{
		label: "Orders",
		value: "1,842",
		delta: 4.1,
		hint: "vs prior 30 days",
	},
	{
		label: "Average order value",
		value: "$154.60",
		delta: -1.3,
		hint: "vs prior 30 days",
	},
	{
		label: "Store conversion",
		value: "3.06%",
		delta: 0.6,
		hint: "vs prior 30 days",
	},
] as const;

export function DashboardStats() {
	return (
		<>
			{stats.map((s) => (
				<StatCard key={s.label} stat={s} />
			))}
		</>
	);
}

function StatCard({ stat }: { stat: Stat }) {
	const { label, value, delta, hint } = stat;
	return (
		<Card>
			<CardHeader>
				<CardTitle className="font-normal text-muted-foreground text-xs">
					{label}
				</CardTitle>
			</CardHeader>
			<CardContent>
				<p className="text-balance font-semibold text-2xl tabular-nums tracking-tight">
					{value}
				</p>
			</CardContent>
			<CardFooter className="gap-1.5 text-xs">
				<Delta value={delta} variant="default">
					<DeltaIcon />
					<DeltaValue />
				</Delta>
				<span className="text-pretty text-muted-foreground">{hint}</span>
			</CardFooter>
		</Card>
	);
}
```

### `registry/blocks/dashboard/delta.tsx`

```tsx
"use client";
import { cn } from "@/lib/utils";
import * as React from "react";
import { Badge } from "@/components/ui/badge";

type DeltaIconVariant = "default" | "trend" | "arrow";
type DeltaVariant = "default" | "badge";

type DeltaContextValue = {
	value: number;
};

const DeltaContext = React.createContext<DeltaContextValue | null>(null);

function useDeltaValue() {
	const context = React.useContext(DeltaContext);

	if (!context) {
		throw new Error(
			"DeltaIcon and DeltaValue must be used inside a `Delta` component."
		);
	}

	return context.value;
}

function Delta({
	className,
	value,
	variant = "default",
	...props
}: React.ComponentProps<"div"> & {
	value: number;
	variant?: DeltaVariant;
}) {
	return (
		<DeltaContext.Provider value={{ value }}>
			{variant === "badge" ? (
				<Badge
					className={cn(
						"gap-1 border-none tabular-nums [&_svg]:size-4 [&_svg]:shrink-0",
						value > 0
							? "bg-emerald-500/10 text-emerald-500"
							: "bg-red-500/10 text-red-500",
						className
					)}
					data-slot="delta"
					variant="secondary"
					{...(props as React.ComponentProps<typeof Badge>)}
				/>
			) : (
				<div
					className={cn(
						"inline-flex items-center gap-1 text-muted-foreground tabular-nums",
						"[&_svg]:size-3 [&_svg]:shrink-0",
						value > 0 ? "text-emerald-600 dark:text-emerald-400" : "",
						value < 0 ? "text-rose-600 dark:text-rose-400" : "",
						className
					)}
					data-slot="delta"
					{...props}
				/>
			)}
		</DeltaContext.Provider>
	);
}

function FilledShell({
	value,
	children,
}: {
	value: number;
	children: React.ReactNode;
}) {
	return (
		<span
			className={cn(
				"inline-flex size-3 shrink-0 items-center justify-center rounded-full",
				"[&_svg]:size-2! [&_svg]:shrink-0 [&_svg]:stroke-3! [&_svg]:text-background",
				value > 0 && "bg-emerald-500",
				value < 0 && "bg-red-500",
				(!value || value === 0) && "bg-muted-foreground"
			)}
			data-slot="delta-icon"
		>
			{children}
		</span>
	);
}

function DeltaIcon({
	variant = "default",
	filled = false,
	className,
	...props
}: Omit<React.ComponentProps<"svg">, "fill"> & {
	variant?: DeltaIconVariant;
	filled?: boolean;
}) {
	const resolvedValue = useDeltaValue();

	const mergedClassName = cn(className);

	const shell = (node: React.ReactElement) =>
		filled ? <FilledShell value={resolvedValue}>{node}</FilledShell> : node;

	const slotProps = filled ? {} : { "data-slot": "delta-icon" as const };

	if (!resolvedValue || resolvedValue === 0) {
		return shell(
			<IconPlaceholder
				{...slotProps}
				className={mergedClassName}
				hugeicons="MinusSignIcon"
				lucide="MinusIcon"
				phosphor="MinusIcon"
				remixicon="RiSubtractLine"
				tabler="IconMinus"
				{...props}
			/>
		);
	}

	if (resolvedValue > 0) {
		if (variant === "trend") {
			return shell(
				<IconPlaceholder
					{...slotProps}
					className={mergedClassName}
					hugeicons="TradeUpIcon"
					lucide="TrendingUpIcon"
					phosphor="TrendUpIcon"
					remixicon="RiArrowRightUpLine"
					tabler="IconTrendingUp"
					{...props}
				/>
			);
		}

		if (variant === "arrow") {
			return shell(
				<IconPlaceholder
					{...slotProps}
					className={mergedClassName}
					hugeicons="ArrowUp02Icon"
					lucide="ArrowUpIcon"
					phosphor="ArrowUpIcon"
					remixicon="RiArrowUpLine"
					tabler="IconArrowUp"
					{...props}
				/>
			);
		}

		return shell(
			<IconPlaceholder
				{...slotProps}
				className={mergedClassName}
				hugeicons="ArrowUp01Icon"
				lucide="ChevronUpIcon"
				phosphor="CaretUpIcon"
				remixicon="RiArrowUpSLine"
				tabler="IconChevronUp"
				{...props}
			/>
		);
	}

	if (variant === "trend") {
		return shell(
			<IconPlaceholder
				{...slotProps}
				className={mergedClassName}
				hugeicons="TradeDownIcon"
				lucide="TrendingDownIcon"
				phosphor="TrendDownIcon"
				remixicon="RiArrowRightDownLine"
				tabler="IconTrendingDown"
				{...props}
			/>
		);
	}

	if (variant === "arrow") {
		return shell(
			<IconPlaceholder
				{...slotProps}
				className={mergedClassName}
				hugeicons="ArrowDown02Icon"
				lucide="ArrowDownIcon"
				phosphor="ArrowDownIcon"
				remixicon="RiArrowDownLine"
				tabler="IconArrowDown"
				{...props}
			/>
		);
	}

	return shell(
		<IconPlaceholder
			{...slotProps}
			className={mergedClassName}
			hugeicons="ArrowDown01Icon"
			lucide="ChevronDownIcon"
			phosphor="CaretDownIcon"
			remixicon="RiArrowDownSLine"
			tabler="IconChevronDown"
			{...props}
		/>
	);
}

function DeltaValue({
	className,
	precision = 1,
	suffix = "%",
	absolute = true,
	...props
}: React.ComponentProps<"span"> & {
	precision?: number;
	suffix?: string;
	absolute?: boolean;
}) {
	const resolvedValue = useDeltaValue();

	const formattedValue = (
		absolute ? Math.abs(resolvedValue) : resolvedValue
	).toFixed(precision);

	return (
		<span
			className={cn("tabular-nums", className)}
			data-slot="delta-value"
			{...props}
		>
			{formattedValue}
			{suffix}
		</span>
	);
}
export { Delta, DeltaIcon, DeltaValue };
```
