{"$schema":"https://ui.shadcn.com/schema/registry-item.json","type":"registry:block","registryDependencies":["badge","button","card","chart","item","select","@efferd/app-shell-4","@efferd/formater"],"name":"dashboard-4","description":"Commerce dashboard with revenue trends, refund rates, category ranking pie, and quick action items.","categories":["dashboard"],"files":[{"path":"registry/blocks/dashboard/4/category-rank-chart.tsx","type":"registry:component","content":"\"use client\";\n\nimport React from \"react\";\nimport { LabelList, Pie, PieChart } from \"recharts\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport {\n\ttype ChartConfig,\n\tChartContainer,\n\tChartLegend,\n\tChartLegendContent,\n} from \"@/components/ui/chart\";\n\nexport type CategoryMixDatum = {\n\tcategory: string;\n\t/** Percent of total (0–100). Usually sums to 100. */\n\tshare: number;\n};\n\nconst SLICE_PALETTE = [\n\t\"var(--chart-1)\",\n\t\"var(--chart-2)\",\n\t\"var(--chart-3)\",\n\t\"var(--chart-4)\",\n\t\"var(--chart-5)\",\n] as const;\n\n/** At most four named slices; remaining share rolls into a fifth slice, “Others”. */\nconst MAX_NAMED_SLICES = 4;\n\n/** Rolling window for the mix (days). */\nconst periodDays = 7;\n\n/** Demo mix (sums to 100). Replace or pass `data` on the component. */\nconst data = [\n\t{ category: \"Apparel\", share: 22 },\n\t{ category: \"Accessories\", share: 22 },\n\t{ category: \"Footwear\", share: 18 },\n\t{ category: \"Home & living\", share: 14 },\n\t{ category: \"Beauty\", share: 11 },\n\t{ category: \"Outlet\", share: 8 },\n\t{ category: \"Sports\", share: 5 },\n] as const satisfies readonly CategoryMixDatum[];\n\n/** Sort by share descending, keep top four, merge the rest into “Others” (max five slices). */\nfunction consolidateTopFourAndOthers(\n\tdata: readonly CategoryMixDatum[]\n): CategoryMixDatum[] {\n\tif (data.length <= MAX_NAMED_SLICES) {\n\t\treturn [...data];\n\t}\n\n\tconst sorted = [...data].sort((a, b) => b.share - a.share);\n\tconst head = sorted.slice(0, MAX_NAMED_SLICES);\n\tconst tail = sorted.slice(MAX_NAMED_SLICES);\n\tconst othersShare = tail.reduce((sum, row) => sum + row.share, 0);\n\n\treturn [...head, { category: \"Others\", share: othersShare }];\n}\n\ntype SliceRow = {\n\tkey: string;\n\tcategory: string;\n\tshare: number;\n\tfill: string;\n};\n\nfunction buildSlices(data: readonly CategoryMixDatum[]): {\n\tchartConfig: ChartConfig;\n\tpieData: SliceRow[];\n} {\n\tconst chartConfig: ChartConfig = {\n\t\tshare: {\n\t\t\tlabel: \"Share\",\n\t\t},\n\t};\n\n\tconst pieData: SliceRow[] = data.map((row, i) => {\n\t\tconst key = `s${i}`;\n\t\tconst color = SLICE_PALETTE[i % SLICE_PALETTE.length];\n\t\tchartConfig[key] = {\n\t\t\tlabel: row.category,\n\t\t\tcolor,\n\t\t};\n\t\treturn {\n\t\t\tkey,\n\t\t\tcategory: row.category,\n\t\t\tshare: row.share,\n\t\t\tfill: `var(--color-${key})`,\n\t\t};\n\t});\n\n\treturn { chartConfig, pieData };\n}\n\nexport function CategoryRankChart() {\n\tconst { chartConfig, pieData } = React.useMemo(\n\t\t() => buildSlices(consolidateTopFourAndOthers(data)),\n\t\t[]\n\t);\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Revenue Share by Category</CardTitle>\n\t\t\t\t<CardDescription>Last {periodDays} days.</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent className=\"my-auto p-0\">\n\t\t\t\t<ChartContainer\n\t\t\t\t\tclassName=\"aspect-auto h-72 w-full\"\n\t\t\t\t\tconfig={chartConfig}\n\t\t\t\t>\n\t\t\t\t\t<PieChart accessibilityLayer>\n\t\t\t\t\t\t<Pie\n\t\t\t\t\t\t\tcornerRadius={4}\n\t\t\t\t\t\t\tdata={pieData}\n\t\t\t\t\t\t\tdataKey=\"share\"\n\t\t\t\t\t\t\tinnerRadius={50}\n\t\t\t\t\t\t\tnameKey=\"key\"\n\t\t\t\t\t\t\touterRadius=\"88%\"\n\t\t\t\t\t\t\tstroke=\"var(--card)\"\n\t\t\t\t\t\t\tstrokeWidth={4}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<LabelList\n\t\t\t\t\t\t\t\tclassName=\"fill-background font-medium\"\n\t\t\t\t\t\t\t\tdataKey=\"share\"\n\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\tfontWeight={500}\n\t\t\t\t\t\t\t\tformatter={(label) => {\n\t\t\t\t\t\t\t\t\tconst n = Number(label);\n\t\t\t\t\t\t\t\t\treturn Number.isFinite(n) ? `${n}%` : String(label ?? \"\");\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tposition=\"inside\"\n\t\t\t\t\t\t\t\tstroke=\"none\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Pie>\n\t\t\t\t\t\t<ChartLegend\n\t\t\t\t\t\t\tcontent={\n\t\t\t\t\t\t\t\t<ChartLegendContent\n\t\t\t\t\t\t\t\t\tclassName=\"flex flex-wrap gap-3 pt-2\"\n\t\t\t\t\t\t\t\t\tnameKey=\"key\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PieChart>\n\t\t\t\t</ChartContainer>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/4/dashboard.tsx","type":"registry:component","content":"import { CategoryRankChart } from \"@/components/category-rank-chart\";\nimport { QuickActions } from \"@/components/quick-actions\";\nimport { RefundReturnRateChart } from \"@/components/refund-return-rate-chart\";\nimport { RevenueChart } from \"@/components/revenue-chart\";\nimport { DashboardStats } from \"@/components/stats\";\n\nexport function Dashboard() {\n\treturn (\n\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4\">\n\t\t\t<DashboardStats />\n\t\t\t<RevenueChart />\n\t\t\t<RefundReturnRateChart />\n\t\t\t<CategoryRankChart />\n\t\t\t<QuickActions />\n\t\t</div>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/4/quick-actions.tsx","type":"registry:component","content":"import {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport {\n\tItem,\n\tItemActions,\n\tItemContent,\n\tItemDescription,\n\tItemGroup,\n\tItemMedia,\n\tItemTitle,\n} from \"@/components/ui/item\";\n\nconst actions = [\n\t{\n\t\ttitle: \"Add product\",\n\t\tdescription: \"Create a new SKU.\",\n\t\thref: \"#\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\thugeicons=\"Add01Icon\"\n\t\t\t\tlucide=\"PackagePlusIcon\"\n\t\t\t\tphosphor=\"PackageIcon\"\n\t\t\t\tremixicon=\"RiAddBoxLine\"\n\t\t\t\ttabler=\"IconPackageImport\"\n\t\t\t/>\n\t\t),\n\t},\n\t{\n\t\ttitle: \"Review unfulfilled\",\n\t\tdescription: \"Orders waiting to ship.\",\n\t\thref: \"#\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\thugeicons=\"DeliveryTruck01Icon\"\n\t\t\t\tlucide=\"TruckIcon\"\n\t\t\t\tphosphor=\"TruckIcon\"\n\t\t\t\tremixicon=\"RiTruckLine\"\n\t\t\t\ttabler=\"IconTruckDelivery\"\n\t\t\t/>\n\t\t),\n\t},\n\t{\n\t\ttitle: \"Store settings\",\n\t\tdescription: \"Payments, checkouts etc.\",\n\t\thref: \"#\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\thugeicons=\"Settings02Icon\"\n\t\t\t\tlucide=\"SettingsIcon\"\n\t\t\t\tphosphor=\"GearSixIcon\"\n\t\t\t\tremixicon=\"RiSettings3Line\"\n\t\t\t\ttabler=\"IconSettings\"\n\t\t\t/>\n\t\t),\n\t},\n\t{\n\t\ttitle: \"Export sales\",\n\t\tdescription: \"CSV for accountings.\",\n\t\thref: \"#\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\thugeicons=\"Download01Icon\"\n\t\t\t\tlucide=\"DownloadIcon\"\n\t\t\t\tphosphor=\"DownloadSimpleIcon\"\n\t\t\t\tremixicon=\"RiDownloadLine\"\n\t\t\t\ttabler=\"IconDownload\"\n\t\t\t/>\n\t\t),\n\t},\n] as const;\n\nexport function QuickActions() {\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Quick actions</CardTitle>\n\t\t\t\t<CardDescription>Shortcuts to same destinations.</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<ItemGroup className=\"gap-0\">\n\t\t\t\t\t{actions.map((a) => (\n\t\t\t\t\t\t<Item asChild key={a.title} size=\"sm\">\n\t\t\t\t\t\t\t<a href={a.href}>\n\t\t\t\t\t\t\t\t<ItemMedia variant=\"icon\">{a.icon}</ItemMedia>\n\t\t\t\t\t\t\t\t<ItemContent>\n\t\t\t\t\t\t\t\t\t<ItemTitle>{a.title}</ItemTitle>\n\t\t\t\t\t\t\t\t\t<ItemDescription className=\"line-clamp-1\">\n\t\t\t\t\t\t\t\t\t\t{a.description}\n\t\t\t\t\t\t\t\t\t</ItemDescription>\n\t\t\t\t\t\t\t\t</ItemContent>\n\t\t\t\t\t\t\t\t<ItemActions>\n\t\t\t\t\t\t\t\t\t<IconPlaceholder\n\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"size-4 shrink-0 text-muted-foreground\"\n\t\t\t\t\t\t\t\t\t\thugeicons=\"ArrowRight01Icon\"\n\t\t\t\t\t\t\t\t\t\tlucide=\"ChevronRightIcon\"\n\t\t\t\t\t\t\t\t\t\tphosphor=\"CaretRightIcon\"\n\t\t\t\t\t\t\t\t\t\tremixicon=\"RiArrowRightSLine\"\n\t\t\t\t\t\t\t\t\t\ttabler=\"IconChevronRight\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</ItemActions>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</Item>\n\t\t\t\t\t))}\n\t\t\t\t</ItemGroup>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/4/refund-return-rate-chart.tsx","type":"registry:component","content":"\"use client\";\n\nimport { CartesianGrid, Line, LineChart, XAxis } from \"recharts\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardFooter,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport {\n\ttype ChartConfig,\n\tChartContainer,\n\tChartTooltip,\n\tChartTooltipContent,\n} from \"@/components/ui/chart\";\nimport { Delta, DeltaIcon, DeltaValue } from \"@/components/delta\";\n\n/** Daily return rate (% of fulfilled orders returned), last 7 days (demo). */\nconst returnDaily7 = [\n\t{ day: \"Mon\", returnRate: 2.2 },\n\t{ day: \"Tue\", returnRate: 1.5 },\n\t{ day: \"Wed\", returnRate: 3.1 },\n\t{ day: \"Thu\", returnRate: 4.8 },\n\t{ day: \"Fri\", returnRate: 2.4 },\n\t{ day: \"Sat\", returnRate: 3.2 },\n\t{ day: \"Sun\", returnRate: 3.9 },\n] as const;\n\n/** Share of orders that were refunded over the same window (demo). */\nconst REFUNDED_SHARE_OF_ORDERS_PCT = 2.6;\n\nconst chartConfig = {\n\treturnRate: {\n\t\tlabel: \"Return %\",\n\t\tcolor: \"var(--chart-1)\",\n\t},\n} satisfies ChartConfig;\n\nexport function RefundReturnRateChart() {\n\tconst first = returnDaily7[0];\n\tconst lastW = returnDaily7.at(-1) ?? first;\n\tconst returnTrendPct =\n\t\tfirst.returnRate > 0\n\t\t\t? ((lastW.returnRate - first.returnRate) / first.returnRate) * 100\n\t\t\t: 0;\n\n\treturn (\n\t\t<Card className=\"md:col-span-2\">\n\t\t\t<CardHeader className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between\">\n\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t<CardTitle>Return rate</CardTitle>\n\t\t\t\t\t<CardDescription>Last 7 days</CardDescription>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t<CardTitle className=\"text-right\">\n\t\t\t\t\t\t{REFUNDED_SHARE_OF_ORDERS_PCT}%\n\t\t\t\t\t</CardTitle>\n\t\t\t\t\t<CardDescription>of orders refunded</CardDescription>\n\t\t\t\t</div>\n\t\t\t</CardHeader>\n\t\t\t<CardContent className=\"mt-auto\">\n\t\t\t\t<ChartContainer\n\t\t\t\t\tclassName=\"aspect-auto h-56 w-full\"\n\t\t\t\t\tconfig={chartConfig}\n\t\t\t\t>\n\t\t\t\t\t<LineChart\n\t\t\t\t\t\taccessibilityLayer\n\t\t\t\t\t\tdata={returnDaily7}\n\t\t\t\t\t\tmargin={{ left: 12, right: 12, top: 12, bottom: 0 }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<CartesianGrid horizontal={false} strokeDasharray=\"3 3\" />\n\t\t\t\t\t\t<XAxis\n\t\t\t\t\t\t\taxisLine={false}\n\t\t\t\t\t\t\tdataKey=\"day\"\n\t\t\t\t\t\t\tinterval={1}\n\t\t\t\t\t\t\tminTickGap={8}\n\t\t\t\t\t\t\ttickLine={false}\n\t\t\t\t\t\t\ttickMargin={8}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<ChartTooltip content={<ChartTooltipContent indicator=\"line\" />} />\n\t\t\t\t\t\t<Line\n\t\t\t\t\t\t\tdataKey=\"returnRate\"\n\t\t\t\t\t\t\tdot={false}\n\t\t\t\t\t\t\tstroke=\"var(--color-returnRate)\"\n\t\t\t\t\t\t\tstrokeWidth={2.5}\n\t\t\t\t\t\t\ttype=\"monotone\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</LineChart>\n\t\t\t\t</ChartContainer>\n\t\t\t</CardContent>\n\t\t\t<CardFooter>\n\t\t\t\t<div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1 text-muted-foreground text-xs\">\n\t\t\t\t\t<Delta value={returnTrendPct}>\n\t\t\t\t\t\t<DeltaIcon />\n\t\t\t\t\t\t<DeltaValue />\n\t\t\t\t\t</Delta>\n\t\t\t\t\t<span className=\"inline-flex min-w-0 text-pretty\">\n\t\t\t\t\t\tvs first day (last 7 days)\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<Button\n\t\t\t\t\tasChild\n\t\t\t\t\tclassName=\"text-muted-foreground\"\n\t\t\t\t\tsize=\"xs\"\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t>\n\t\t\t\t\t<a href=\"#/orders/returns\">\n\t\t\t\t\t\tReturns desk\n\t\t\t\t\t\t<IconPlaceholder\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\tdata-icon=\"inline-end\"\n\t\t\t\t\t\t\thugeicons=\"ArrowRight02Icon\"\n\t\t\t\t\t\t\tlucide=\"ArrowRightIcon\"\n\t\t\t\t\t\t\tphosphor=\"ArrowRightIcon\"\n\t\t\t\t\t\t\tremixicon=\"RiArrowRightLine\"\n\t\t\t\t\t\t\ttabler=\"IconArrowRight\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</a>\n\t\t\t\t</Button>\n\t\t\t</CardFooter>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/4/revenue-chart-data.ts","type":"registry:component","content":"export const revenueChartDemo = [\n\t{ date: \"2026-01-21\", revenue: 10_135 },\n\t{ date: \"2026-01-22\", revenue: 10_415 },\n\t{ date: \"2026-01-23\", revenue: 10_349 },\n\t{ date: \"2026-01-24\", revenue: 10_011 },\n\t{ date: \"2026-01-25\", revenue: 9657 },\n\t{ date: \"2026-01-26\", revenue: 9548 },\n\t{ date: \"2026-01-27\", revenue: 9770 },\n\t{ date: \"2026-01-28\", revenue: 10_177 },\n\t{ date: \"2026-01-29\", revenue: 10_500 },\n\t{ date: \"2026-01-30\", revenue: 10_539 },\n\t{ date: \"2026-01-31\", revenue: 10_302 },\n\t{ date: \"2026-02-01\", revenue: 9993 },\n\t{ date: \"2026-02-02\", revenue: 9858 },\n\t{ date: \"2026-02-03\", revenue: 10_014 },\n\t{ date: \"2026-02-04\", revenue: 10_367 },\n\t{ date: \"2026-02-05\", revenue: 10_700 },\n\t{ date: \"2026-02-06\", revenue: 10_828 },\n\t{ date: \"2026-02-07\", revenue: 10_739 },\n\t{ date: \"2026-02-08\", revenue: 10_204 },\n\t{ date: \"2026-02-09\", revenue: 10_137 },\n\t{ date: \"2026-02-10\", revenue: 10_288 },\n\t{ date: \"2026-02-11\", revenue: 10_583 },\n\t{ date: \"2026-02-12\", revenue: 11_391 },\n\t{ date: \"2026-02-13\", revenue: 11_548 },\n\t{ date: \"2026-02-14\", revenue: 11_569 },\n\t{ date: \"2026-02-15\", revenue: 11_590 },\n\t{ date: \"2026-02-16\", revenue: 11_748 },\n\t{ date: \"2026-02-17\", revenue: 12_056 },\n\t{ date: \"2026-02-18\", revenue: 12_380 },\n\t{ date: \"2026-02-19\", revenue: 12_539 },\n\t{ date: \"2026-02-20\", revenue: 12_448 },\n\t{ date: \"2026-02-21\", revenue: 12_200 },\n\t{ date: \"2026-02-22\", revenue: 12_016 },\n\t{ date: \"2026-02-23\", revenue: 12_085 },\n\t{ date: \"2026-02-24\", revenue: 12_845 },\n\t{ date: \"2026-02-25\", revenue: 13_283 },\n\t{ date: \"2026-02-26\", revenue: 13_160 },\n\t{ date: \"2026-02-27\", revenue: 13_188 },\n\t{ date: \"2026-02-28\", revenue: 13_031 },\n\t{ date: \"2026-03-01\", revenue: 12_930 },\n\t{ date: \"2026-03-02\", revenue: 13_113 },\n\t{ date: \"2026-03-03\", revenue: 13_618 },\n\t{ date: \"2026-03-04\", revenue: 14_259 },\n\t{ date: \"2026-03-05\", revenue: 14_745 },\n\t{ date: \"2026-03-06\", revenue: 14_880 },\n\t{ date: \"2026-03-07\", revenue: 14_692 },\n\t{ date: \"2026-03-08\", revenue: 14_414 },\n\t{ date: \"2026-03-09\", revenue: 14_316 },\n\t{ date: \"2026-03-10\", revenue: 14_519 },\n\t{ date: \"2026-03-11\", revenue: 14_924 },\n\t{ date: \"2026-03-12\", revenue: 15_299 },\n\t{ date: \"2026-03-13\", revenue: 15_451 },\n\t{ date: \"2026-03-14\", revenue: 14_847 },\n\t{ date: \"2026-03-15\", revenue: 15_211 },\n\t{ date: \"2026-03-16\", revenue: 15_192 },\n\t{ date: \"2026-03-17\", revenue: 15_411 },\n\t{ date: \"2026-03-18\", revenue: 15_789 },\n\t{ date: \"2026-03-19\", revenue: 16_140 },\n\t{ date: \"2026-03-20\", revenue: 16_317 },\n\t{ date: \"2026-03-21\", revenue: 16_321 },\n\t{ date: \"2026-03-22\", revenue: 16_020 },\n\t{ date: \"2026-03-23\", revenue: 16_380 },\n\t{ date: \"2026-03-24\", revenue: 16_060 },\n\t{ date: \"2026-03-25\", revenue: 16_710 },\n\t{ date: \"2026-03-26\", revenue: 16_340 },\n\t{ date: \"2026-03-27\", revenue: 16_890 },\n\t{ date: \"2026-03-28\", revenue: 16_530 },\n\t{ date: \"2026-03-29\", revenue: 16_995 },\n\t{ date: \"2026-03-30\", revenue: 16_720 },\n\t{ date: \"2026-03-31\", revenue: 17_115 },\n\t{ date: \"2026-04-01\", revenue: 16_865 },\n\t{ date: \"2026-04-02\", revenue: 17_395 },\n\t{ date: \"2026-04-03\", revenue: 17_055 },\n\t{ date: \"2026-04-04\", revenue: 16_695 },\n\t{ date: \"2026-04-05\", revenue: 16_425 },\n\t{ date: \"2026-04-06\", revenue: 16_980 },\n\t{ date: \"2026-04-07\", revenue: 16_520 },\n\t{ date: \"2026-04-08\", revenue: 17_410 },\n\t{ date: \"2026-04-09\", revenue: 16_745 },\n\t{ date: \"2026-04-10\", revenue: 17_595 },\n\t{ date: \"2026-04-11\", revenue: 16_955 },\n\t{ date: \"2026-04-12\", revenue: 17_720 },\n\t{ date: \"2026-04-13\", revenue: 17_185 },\n\t{ date: \"2026-04-14\", revenue: 18_020 },\n\t{ date: \"2026-04-15\", revenue: 17_395 },\n\t{ date: \"2026-04-16\", revenue: 17_880 },\n\t{ date: \"2026-04-17\", revenue: 17_210 },\n\t{ date: \"2026-04-18\", revenue: 18_240 },\n\t{ date: \"2026-04-19\", revenue: 17_420 },\n\t{ date: \"2026-04-20\", revenue: 17_785 },\n];\n"},{"path":"registry/blocks/dashboard/4/revenue-chart.tsx","type":"registry:component","content":"\"use client\";\n\nimport { useId, useMemo, useState } from \"react\";\nimport { Area, AreaChart, CartesianGrid, XAxis } from \"recharts\";\nimport { formatChartAxisTick, formatChartTooltipDate } from \"@/lib/formater\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardFooter,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport {\n\ttype ChartConfig,\n\tChartContainer,\n\tChartTooltip,\n\tChartTooltipContent,\n} from \"@/components/ui/chart\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@/components/ui/select\";\nimport { Delta, DeltaIcon, DeltaValue } from \"@/components/delta\";\nimport { revenueChartDemo } from \"@/components/revenue-chart-data\";\n\n/** Matches `<Select>`; chart uses the last N days of `revenueChartDemo`. */\ntype PeriodDays = 7 | 14 | 30 | 60 | 90;\n\n// Recharts XAxis: `interval` is tick skip index (0 = all, 1 = every other, …).\nconst xAxisIntervalByPeriod: Record<PeriodDays, number> = {\n\t7: 0,\n\t14: 1,\n\t30: 3,\n\t60: 4,\n\t90: 6,\n};\n\ntype RevenueRow = {\n\tdate: string;\n\trevenue: number;\n};\n\nconst chartConfig = {\n\trevenue: {\n\t\tlabel: \"Revenue\",\n\t\tcolor: \"var(--chart-1)\",\n\t},\n} satisfies ChartConfig;\n\nexport function RevenueChart() {\n\tconst chartUid = useId().replace(/:/g, \"\");\n\tconst idAreaGradient = `revenue-area-grad-${chartUid}`;\n\tconst [periodDays, setPeriodDays] = useState<PeriodDays>(60);\n\n\tconst chartRows = useMemo(\n\t\t() => revenueChartDemo.slice(-periodDays),\n\t\t[periodDays]\n\t);\n\n\t// Footer delta: first → last point in the active series (not calendar MoM).\n\tconst growthPct = useMemo(() => {\n\t\tconst first = chartRows[0]?.revenue ?? 0;\n\t\tconst last = chartRows.at(-1)?.revenue ?? first;\n\t\tif (!first) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn ((last - first) / first) * 100;\n\t}, [chartRows]);\n\n\t// Extra spacing so long ranges don’t collide; pairs with `interval` above.\n\tlet xAxisMinTickGap: number | undefined;\n\tif (periodDays <= 7) {\n\t\txAxisMinTickGap = undefined;\n\t} else {\n\t\txAxisMinTickGap = Math.max(8, Math.min(52, Math.floor(periodDays / 2)));\n\t}\n\n\treturn (\n\t\t<Card className=\"md:col-span-2 lg:col-span-4\">\n\t\t\t<CardHeader className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n\t\t\t\t<CardTitle className=\"text-balance\">Revenue</CardTitle>\n\t\t\t\t<Select\n\t\t\t\t\tonValueChange={(v) => {\n\t\t\t\t\t\tsetPeriodDays(Number(v) as PeriodDays);\n\t\t\t\t\t}}\n\t\t\t\t\tvalue={String(periodDays)}\n\t\t\t\t>\n\t\t\t\t\t<SelectTrigger\n\t\t\t\t\t\taria-label=\"Revenue time range\"\n\t\t\t\t\t\tclassName=\"w-full min-w-36 sm:w-fit\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<SelectValue placeholder=\"Range\" />\n\t\t\t\t\t</SelectTrigger>\n\t\t\t\t\t<SelectContent align=\"end\">\n\t\t\t\t\t\t<SelectItem value=\"7\">Last 7 days</SelectItem>\n\t\t\t\t\t\t<SelectItem value=\"14\">Last 14 days</SelectItem>\n\t\t\t\t\t\t<SelectItem value=\"30\">Last 30 days</SelectItem>\n\t\t\t\t\t\t<SelectItem value=\"60\">Last 60 days</SelectItem>\n\t\t\t\t\t\t<SelectItem value=\"90\">Last 90 days</SelectItem>\n\t\t\t\t\t</SelectContent>\n\t\t\t\t</Select>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<ChartContainer\n\t\t\t\t\tclassName=\"aspect-auto h-60 w-full p-0\"\n\t\t\t\t\tconfig={chartConfig}\n\t\t\t\t>\n\t\t\t\t\t<AreaChart\n\t\t\t\t\t\taccessibilityLayer\n\t\t\t\t\t\t// Copy so Recharts always sees a mutable array reference.\n\t\t\t\t\t\tdata={[...chartRows]}\n\t\t\t\t\t\tmargin={{ left: 24, right: 8, top: 8, bottom: 0 }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<defs>\n\t\t\t\t\t\t\t<linearGradient id={idAreaGradient} x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">\n\t\t\t\t\t\t\t\t<stop\n\t\t\t\t\t\t\t\t\toffset=\"0%\"\n\t\t\t\t\t\t\t\t\tstopColor=\"var(--color-revenue)\"\n\t\t\t\t\t\t\t\t\tstopOpacity={0.2}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<stop\n\t\t\t\t\t\t\t\t\toffset=\"100%\"\n\t\t\t\t\t\t\t\t\tstopColor=\"var(--color-revenue)\"\n\t\t\t\t\t\t\t\t\tstopOpacity={0}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</linearGradient>\n\t\t\t\t\t\t</defs>\n\t\t\t\t\t\t<CartesianGrid horizontal={false} strokeDasharray=\"2 2\" />\n\t\t\t\t\t\t<XAxis\n\t\t\t\t\t\t\taxisLine={false}\n\t\t\t\t\t\t\tdataKey=\"date\"\n\t\t\t\t\t\t\tinterval={xAxisIntervalByPeriod[periodDays]}\n\t\t\t\t\t\t\tminTickGap={xAxisMinTickGap}\n\t\t\t\t\t\t\ttickFormatter={(value) =>\n\t\t\t\t\t\t\t\tformatChartAxisTick(String(value), periodDays)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttickLine={false}\n\t\t\t\t\t\t\ttickMargin={8}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<ChartTooltip\n\t\t\t\t\t\t\tcontent={\n\t\t\t\t\t\t\t\t<ChartTooltipContent\n\t\t\t\t\t\t\t\t\tclassName=\"min-w-36\"\n\t\t\t\t\t\t\t\t\tindicator=\"line\"\n\t\t\t\t\t\t\t\t\tlabelFormatter={(_, payload) => {\n\t\t\t\t\t\t\t\t\t\tconst row = payload?.[0]?.payload as RevenueRow | undefined;\n\t\t\t\t\t\t\t\t\t\tif (!row?.date) {\n\t\t\t\t\t\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\treturn formatChartTooltipDate(row.date, \"short\");\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Area\n\t\t\t\t\t\t\tdataKey=\"revenue\"\n\t\t\t\t\t\t\tdot={false}\n\t\t\t\t\t\t\tfill={`url(#${idAreaGradient})`}\n\t\t\t\t\t\t\tstroke=\"var(--color-revenue)\"\n\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\ttype=\"monotone\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</AreaChart>\n\t\t\t\t</ChartContainer>\n\t\t\t</CardContent>\n\t\t\t<CardFooter className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-1 text-muted-foreground text-xs\">\n\t\t\t\t\t<Delta value={growthPct}>\n\t\t\t\t\t\t<DeltaIcon />\n\t\t\t\t\t\t<DeltaValue />\n\t\t\t\t\t</Delta>\n\t\t\t\t\t<p className=\"inline-flex text-pretty\">\n\t\t\t\t\t\tvs first day in last {periodDays} days.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<Button\n\t\t\t\t\tasChild\n\t\t\t\t\tclassName=\"text-muted-foreground\"\n\t\t\t\t\tsize=\"xs\"\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t>\n\t\t\t\t\t<a href=\"#/reports\">\n\t\t\t\t\t\tView report\n\t\t\t\t\t\t<IconPlaceholder\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\tdata-icon=\"inline-end\"\n\t\t\t\t\t\t\thugeicons=\"ArrowRight02Icon\"\n\t\t\t\t\t\t\tlucide=\"ArrowRightIcon\"\n\t\t\t\t\t\t\tphosphor=\"ArrowRightIcon\"\n\t\t\t\t\t\t\tremixicon=\"RiArrowRightLine\"\n\t\t\t\t\t\t\ttabler=\"IconArrowRight\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</a>\n\t\t\t\t</Button>\n\t\t\t</CardFooter>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/4/stats.tsx","type":"registry:component","content":"import {\n\tCard,\n\tCardContent,\n\tCardFooter,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport { Delta, DeltaIcon, DeltaValue } from \"@/components/delta\";\n\ntype Stat = {\n\tlabel: string;\n\tvalue: string;\n\tdelta: number;\n\thint: string;\n};\n\nconst stats: readonly Stat[] = [\n\t{\n\t\tlabel: \"Total revenue\",\n\t\tvalue: \"$284,920\",\n\t\tdelta: 8.2,\n\t\thint: \"vs prior 30 days\",\n\t},\n\t{\n\t\tlabel: \"Orders\",\n\t\tvalue: \"1,842\",\n\t\tdelta: 4.1,\n\t\thint: \"vs prior 30 days\",\n\t},\n\t{\n\t\tlabel: \"Average order value\",\n\t\tvalue: \"$154.60\",\n\t\tdelta: -1.3,\n\t\thint: \"vs prior 30 days\",\n\t},\n\t{\n\t\tlabel: \"Store conversion\",\n\t\tvalue: \"3.06%\",\n\t\tdelta: 0.6,\n\t\thint: \"vs prior 30 days\",\n\t},\n] as const;\n\nexport function DashboardStats() {\n\treturn (\n\t\t<>\n\t\t\t{stats.map((s) => (\n\t\t\t\t<StatCard key={s.label} stat={s} />\n\t\t\t))}\n\t\t</>\n\t);\n}\n\nfunction StatCard({ stat }: { stat: Stat }) {\n\tconst { label, value, delta, hint } = stat;\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle className=\"font-normal text-muted-foreground text-xs\">\n\t\t\t\t\t{label}\n\t\t\t\t</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<p className=\"text-balance font-semibold text-2xl tabular-nums tracking-tight\">\n\t\t\t\t\t{value}\n\t\t\t\t</p>\n\t\t\t</CardContent>\n\t\t\t<CardFooter className=\"gap-1.5 text-xs\">\n\t\t\t\t<Delta value={delta} variant=\"default\">\n\t\t\t\t\t<DeltaIcon />\n\t\t\t\t\t<DeltaValue />\n\t\t\t\t</Delta>\n\t\t\t\t<span className=\"text-pretty text-muted-foreground\">{hint}</span>\n\t\t\t</CardFooter>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/delta.tsx","type":"registry:component","content":"\"use client\";\nimport { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\n\ntype DeltaIconVariant = \"default\" | \"trend\" | \"arrow\";\ntype DeltaVariant = \"default\" | \"badge\";\n\ntype DeltaContextValue = {\n\tvalue: number;\n};\n\nconst DeltaContext = React.createContext<DeltaContextValue | null>(null);\n\nfunction useDeltaValue() {\n\tconst context = React.useContext(DeltaContext);\n\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"DeltaIcon and DeltaValue must be used inside a `Delta` component.\"\n\t\t);\n\t}\n\n\treturn context.value;\n}\n\nfunction Delta({\n\tclassName,\n\tvalue,\n\tvariant = \"default\",\n\t...props\n}: React.ComponentProps<\"div\"> & {\n\tvalue: number;\n\tvariant?: DeltaVariant;\n}) {\n\treturn (\n\t\t<DeltaContext.Provider value={{ value }}>\n\t\t\t{variant === \"badge\" ? (\n\t\t\t\t<Badge\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"gap-1 border-none tabular-nums [&_svg]:size-4 [&_svg]:shrink-0\",\n\t\t\t\t\t\tvalue > 0\n\t\t\t\t\t\t\t? \"bg-emerald-500/10 text-emerald-500\"\n\t\t\t\t\t\t\t: \"bg-red-500/10 text-red-500\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdata-slot=\"delta\"\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t{...(props as React.ComponentProps<typeof Badge>)}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"inline-flex items-center gap-1 text-muted-foreground tabular-nums\",\n\t\t\t\t\t\t\"[&_svg]:size-3 [&_svg]:shrink-0\",\n\t\t\t\t\t\tvalue > 0 ? \"text-emerald-600 dark:text-emerald-400\" : \"\",\n\t\t\t\t\t\tvalue < 0 ? \"text-rose-600 dark:text-rose-400\" : \"\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t\tdata-slot=\"delta\"\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</DeltaContext.Provider>\n\t);\n}\n\nfunction FilledShell({\n\tvalue,\n\tchildren,\n}: {\n\tvalue: number;\n\tchildren: React.ReactNode;\n}) {\n\treturn (\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"inline-flex size-3 shrink-0 items-center justify-center rounded-full\",\n\t\t\t\t\"[&_svg]:size-2! [&_svg]:shrink-0 [&_svg]:stroke-3! [&_svg]:text-background\",\n\t\t\t\tvalue > 0 && \"bg-emerald-500\",\n\t\t\t\tvalue < 0 && \"bg-red-500\",\n\t\t\t\t(!value || value === 0) && \"bg-muted-foreground\"\n\t\t\t)}\n\t\t\tdata-slot=\"delta-icon\"\n\t\t>\n\t\t\t{children}\n\t\t</span>\n\t);\n}\n\nfunction DeltaIcon({\n\tvariant = \"default\",\n\tfilled = false,\n\tclassName,\n\t...props\n}: Omit<React.ComponentProps<\"svg\">, \"fill\"> & {\n\tvariant?: DeltaIconVariant;\n\tfilled?: boolean;\n}) {\n\tconst resolvedValue = useDeltaValue();\n\n\tconst mergedClassName = cn(className);\n\n\tconst shell = (node: React.ReactElement) =>\n\t\tfilled ? <FilledShell value={resolvedValue}>{node}</FilledShell> : node;\n\n\tconst slotProps = filled ? {} : { \"data-slot\": \"delta-icon\" as const };\n\n\tif (!resolvedValue || resolvedValue === 0) {\n\t\treturn shell(\n\t\t\t<IconPlaceholder\n\t\t\t\t{...slotProps}\n\t\t\t\tclassName={mergedClassName}\n\t\t\t\thugeicons=\"MinusSignIcon\"\n\t\t\t\tlucide=\"MinusIcon\"\n\t\t\t\tphosphor=\"MinusIcon\"\n\t\t\t\tremixicon=\"RiSubtractLine\"\n\t\t\t\ttabler=\"IconMinus\"\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t}\n\n\tif (resolvedValue > 0) {\n\t\tif (variant === \"trend\") {\n\t\t\treturn shell(\n\t\t\t\t<IconPlaceholder\n\t\t\t\t\t{...slotProps}\n\t\t\t\t\tclassName={mergedClassName}\n\t\t\t\t\thugeicons=\"TradeUpIcon\"\n\t\t\t\t\tlucide=\"TrendingUpIcon\"\n\t\t\t\t\tphosphor=\"TrendUpIcon\"\n\t\t\t\t\tremixicon=\"RiArrowRightUpLine\"\n\t\t\t\t\ttabler=\"IconTrendingUp\"\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\n\t\tif (variant === \"arrow\") {\n\t\t\treturn shell(\n\t\t\t\t<IconPlaceholder\n\t\t\t\t\t{...slotProps}\n\t\t\t\t\tclassName={mergedClassName}\n\t\t\t\t\thugeicons=\"ArrowUp02Icon\"\n\t\t\t\t\tlucide=\"ArrowUpIcon\"\n\t\t\t\t\tphosphor=\"ArrowUpIcon\"\n\t\t\t\t\tremixicon=\"RiArrowUpLine\"\n\t\t\t\t\ttabler=\"IconArrowUp\"\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\n\t\treturn shell(\n\t\t\t<IconPlaceholder\n\t\t\t\t{...slotProps}\n\t\t\t\tclassName={mergedClassName}\n\t\t\t\thugeicons=\"ArrowUp01Icon\"\n\t\t\t\tlucide=\"ChevronUpIcon\"\n\t\t\t\tphosphor=\"CaretUpIcon\"\n\t\t\t\tremixicon=\"RiArrowUpSLine\"\n\t\t\t\ttabler=\"IconChevronUp\"\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t}\n\n\tif (variant === \"trend\") {\n\t\treturn shell(\n\t\t\t<IconPlaceholder\n\t\t\t\t{...slotProps}\n\t\t\t\tclassName={mergedClassName}\n\t\t\t\thugeicons=\"TradeDownIcon\"\n\t\t\t\tlucide=\"TrendingDownIcon\"\n\t\t\t\tphosphor=\"TrendDownIcon\"\n\t\t\t\tremixicon=\"RiArrowRightDownLine\"\n\t\t\t\ttabler=\"IconTrendingDown\"\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t}\n\n\tif (variant === \"arrow\") {\n\t\treturn shell(\n\t\t\t<IconPlaceholder\n\t\t\t\t{...slotProps}\n\t\t\t\tclassName={mergedClassName}\n\t\t\t\thugeicons=\"ArrowDown02Icon\"\n\t\t\t\tlucide=\"ArrowDownIcon\"\n\t\t\t\tphosphor=\"ArrowDownIcon\"\n\t\t\t\tremixicon=\"RiArrowDownLine\"\n\t\t\t\ttabler=\"IconArrowDown\"\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn shell(\n\t\t<IconPlaceholder\n\t\t\t{...slotProps}\n\t\t\tclassName={mergedClassName}\n\t\t\thugeicons=\"ArrowDown01Icon\"\n\t\t\tlucide=\"ChevronDownIcon\"\n\t\t\tphosphor=\"CaretDownIcon\"\n\t\t\tremixicon=\"RiArrowDownSLine\"\n\t\t\ttabler=\"IconChevronDown\"\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nfunction DeltaValue({\n\tclassName,\n\tprecision = 1,\n\tsuffix = \"%\",\n\tabsolute = true,\n\t...props\n}: React.ComponentProps<\"span\"> & {\n\tprecision?: number;\n\tsuffix?: string;\n\tabsolute?: boolean;\n}) {\n\tconst resolvedValue = useDeltaValue();\n\n\tconst formattedValue = (\n\t\tabsolute ? Math.abs(resolvedValue) : resolvedValue\n\t).toFixed(precision);\n\n\treturn (\n\t\t<span\n\t\t\tclassName={cn(\"tabular-nums\", className)}\n\t\t\tdata-slot=\"delta-value\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{formattedValue}\n\t\t\t{suffix}\n\t\t</span>\n\t);\n}\nexport { Delta, DeltaIcon, DeltaValue };\n"}]}