{"$schema":"https://ui.shadcn.com/schema/registry-item.json","type":"registry:block","registryDependencies":["avatar","badge","button","card","chart","dropdown-menu","select","table","@efferd/indicator","@efferd/app-shell-3","@efferd/formater"],"name":"dashboard-3","description":"Customer support dashboard with conversation charts, CSAT scores, on-duty team, and recent tickets.","categories":["dashboard"],"files":[{"path":"registry/blocks/dashboard/3/channel-breakdown-chart.tsx","type":"registry:component","content":"\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { ComponentProps } 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\";\nimport { Delta, DeltaIcon, DeltaValue } from \"@/components/delta\";\n\ntype ChannelKey = \"direct\" | \"email\" | \"social\";\n\ntype ChannelDatum = {\n\tchannel: ChannelKey;\n\tshare: number;\n\tfill: string;\n};\n\nconst chartData: ChannelDatum[] = [\n\t{ channel: \"direct\", share: 44, fill: \"var(--color-direct)\" },\n\t{ channel: \"email\", share: 36, fill: \"var(--color-email)\" },\n\t{ channel: \"social\", share: 20, fill: \"var(--color-social)\" },\n];\n\nconst chartConfig = {\n\tshare: {\n\t\tlabel: \"Share\",\n\t},\n\tdirect: {\n\t\tlabel: \"Direct\",\n\t\tcolor: \"var(--chart-1)\",\n\t},\n\temail: {\n\t\tlabel: \"Email\",\n\t\tcolor: \"var(--chart-3)\",\n\t},\n\tsocial: {\n\t\tlabel: \"Social\",\n\t\tcolor: \"var(--chart-5)\",\n\t},\n} satisfies ChartConfig;\n\nexport function ChannelBreakdownChart({\n\tclassName,\n\t...props\n}: ComponentProps<typeof Card>) {\n\treturn (\n\t\t<Card\n\t\t\tclassName={cn(\"flex flex-col shadow-none dark:ring-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<CardHeader className=\"items-center space-y-1 pb-0 sm:items-start\">\n\t\t\t\t<div className=\"flex flex-wrap items-center justify-center gap-2 sm:justify-start\">\n\t\t\t\t\t<CardTitle>Traffic by channel</CardTitle>\n\t\t\t\t\t<Delta value={2.4} variant=\"badge\">\n\t\t\t\t\t\t<DeltaIcon variant=\"trend\" />\n\t\t\t\t\t\t<DeltaValue suffix=\"pp\" />\n\t\t\t\t\t</Delta>\n\t\t\t\t</div>\n\t\t\t\t<CardDescription>\n\t\t\t\t\tShare of new conversations in last 7 days\n\t\t\t\t</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent className=\"my-auto\">\n\t\t\t\t<ChartContainer\n\t\t\t\t\tclassName=\"mx-auto aspect-square max-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={8}\n\t\t\t\t\t\t\tdata={chartData}\n\t\t\t\t\t\t\tdataKey=\"share\"\n\t\t\t\t\t\t\tinnerRadius={36}\n\t\t\t\t\t\t\tnameKey=\"channel\"\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 content={<ChartLegendContent nameKey=\"channel\" />} />\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/3/conversation-volume-chart.tsx","type":"registry:component","content":"\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { type ComponentProps, useId, useMemo, useState } from \"react\";\nimport { Area, AreaChart, CartesianGrid, XAxis, YAxis } from \"recharts\";\nimport {\n\tformatChartAxisTick,\n\tformatChartTooltipDate,\n\tparseIsoCalendarDate,\n} from \"@/lib/formater\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\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\";\n\ntype PeriodDays = 7 | 30 | 60;\n\ntype VolumeRow = {\n\tdate: string;\n\tconversations: number;\n};\n\n/**\n * Demo data.\n */\nconst chartData: VolumeRow[] = [\n\t{ date: \"2026-02-18\", conversations: 162 },\n\t{ date: \"2026-02-19\", conversations: 162 },\n\t{ date: \"2026-02-20\", conversations: 162 },\n\t{ date: \"2026-02-21\", conversations: 162 },\n\t{ date: \"2026-02-22\", conversations: 162 },\n\t{ date: \"2026-02-23\", conversations: 162 },\n\t{ date: \"2026-02-24\", conversations: 161 },\n\t{ date: \"2026-02-25\", conversations: 161 },\n\t{ date: \"2026-02-26\", conversations: 160 },\n\t{ date: \"2026-02-27\", conversations: 160 },\n\t{ date: \"2026-02-28\", conversations: 159 },\n\t{ date: \"2026-03-01\", conversations: 158 },\n\t{ date: \"2026-03-02\", conversations: 158 },\n\t{ date: \"2026-03-03\", conversations: 157 },\n\t{ date: \"2026-03-04\", conversations: 157 },\n\t{ date: \"2026-03-05\", conversations: 157 },\n\t{ date: \"2026-03-06\", conversations: 157 },\n\t{ date: \"2026-03-07\", conversations: 157 },\n\t{ date: \"2026-03-08\", conversations: 157 },\n\t{ date: \"2026-03-09\", conversations: 157 },\n\t{ date: \"2026-03-10\", conversations: 157 },\n\t{ date: \"2026-03-11\", conversations: 157 },\n\t{ date: \"2026-03-12\", conversations: 157 },\n\t{ date: \"2026-03-13\", conversations: 157 },\n\t{ date: \"2026-03-14\", conversations: 156 },\n\t{ date: \"2026-03-15\", conversations: 156 },\n\t{ date: \"2026-03-16\", conversations: 155 },\n\t{ date: \"2026-03-17\", conversations: 155 },\n\t{ date: \"2026-03-18\", conversations: 154 },\n\t{ date: \"2026-03-19\", conversations: 153 },\n\t{ date: \"2026-03-20\", conversations: 152 },\n\t{ date: \"2026-03-21\", conversations: 151 },\n\t{ date: \"2026-03-22\", conversations: 150 },\n\t{ date: \"2026-03-23\", conversations: 149 },\n\t{ date: \"2026-03-24\", conversations: 149 },\n\t{ date: \"2026-03-25\", conversations: 150 },\n\t{ date: \"2026-03-26\", conversations: 147 },\n\t{ date: \"2026-03-27\", conversations: 151 },\n\t{ date: \"2026-03-28\", conversations: 147 },\n\t{ date: \"2026-03-29\", conversations: 145 },\n\t{ date: \"2026-03-30\", conversations: 143 },\n\t{ date: \"2026-03-31\", conversations: 141 },\n\t{ date: \"2026-04-01\", conversations: 139 },\n\t{ date: \"2026-04-02\", conversations: 137 },\n\t{ date: \"2026-04-03\", conversations: 137 },\n\t{ date: \"2026-04-04\", conversations: 137 },\n\t{ date: \"2026-04-05\", conversations: 137 },\n\t{ date: \"2026-04-06\", conversations: 137 },\n\t{ date: \"2026-04-07\", conversations: 137 },\n\t{ date: \"2026-04-08\", conversations: 136 },\n\t{ date: \"2026-04-09\", conversations: 134 },\n\t{ date: \"2026-04-10\", conversations: 132 },\n\t{ date: \"2026-04-11\", conversations: 129 },\n\t{ date: \"2026-04-12\", conversations: 127 },\n\t{ date: \"2026-04-13\", conversations: 125 },\n\t{ date: \"2026-04-14\", conversations: 124 },\n\t{ date: \"2026-04-15\", conversations: 124 },\n\t{ date: \"2026-04-16\", conversations: 124 },\n\t{ date: \"2026-04-17\", conversations: 124 },\n\t{ date: \"2026-04-18\", conversations: 124 },\n\t{ date: \"2026-04-19\", conversations: 124 },\n\t{ date: \"2026-04-20\", conversations: 122 },\n\t{ date: \"2026-04-21\", conversations: 120 },\n\t{ date: \"2026-04-22\", conversations: 118 },\n\t{ date: \"2026-04-23\", conversations: 115 },\n\t{ date: \"2026-04-24\", conversations: 113 },\n\t{ date: \"2026-04-25\", conversations: 112 },\n\t{ date: \"2026-04-26\", conversations: 111 },\n\t{ date: \"2026-04-27\", conversations: 114 },\n\t{ date: \"2026-04-28\", conversations: 110 },\n\t{ date: \"2026-04-29\", conversations: 113 },\n\t{ date: \"2026-04-30\", conversations: 111 },\n\t{ date: \"2026-05-01\", conversations: 111 },\n\t{ date: \"2026-05-02\", conversations: 107 },\n\t{ date: \"2026-05-03\", conversations: 109 },\n\t{ date: \"2026-05-04\", conversations: 105 },\n\t{ date: \"2026-05-05\", conversations: 103 },\n\t{ date: \"2026-05-06\", conversations: 101 },\n\t{ date: \"2026-05-07\", conversations: 98 },\n\t{ date: \"2026-05-08\", conversations: 100 },\n\t{ date: \"2026-05-09\", conversations: 97 },\n\t{ date: \"2026-05-10\", conversations: 101 },\n\t{ date: \"2026-05-11\", conversations: 99 },\n\t{ date: \"2026-05-12\", conversations: 100 },\n\t{ date: \"2026-05-13\", conversations: 94 },\n\t{ date: \"2026-05-14\", conversations: 102 },\n\t{ date: \"2026-05-15\", conversations: 99 },\n\t{ date: \"2026-05-16\", conversations: 107 },\n\t{ date: \"2026-05-17\", conversations: 104 },\n\t{ date: \"2026-05-18\", conversations: 118 },\n\t{ date: \"2026-05-19\", conversations: 127 },\n];\n\nconst lastChartRow = chartData.at(-1);\nif (lastChartRow === undefined) {\n\tthrow new Error(\n\t\t\"ConversationVolumeChart: chartData must include at least one row\"\n\t);\n}\nconst volumeChartReferenceDate = parseIsoCalendarDate(lastChartRow.date);\n\nconst chartConfig = {\n\tconversations: {\n\t\tlabel: \"New threads\",\n\t\tcolor: \"var(--chart-2)\",\n\t},\n} satisfies ChartConfig;\n\nexport function ConversationVolumeChart({\n\tclassName,\n\t...props\n}: ComponentProps<typeof Card>) {\n\tconst chartUid = useId().replace(/:/g, \"\");\n\tconst idAreaGradient = `conversation-volume-area-grad-${chartUid}`;\n\n\tconst [periodDays, setPeriodDays] = useState<PeriodDays>(30);\n\n\tconst chartRows = useMemo(() => {\n\t\tconst startDate = new Date(volumeChartReferenceDate);\n\t\tstartDate.setDate(startDate.getDate() - periodDays);\n\t\treturn chartData.filter(\n\t\t\t(item) => parseIsoCalendarDate(item.date) >= startDate\n\t\t);\n\t}, [periodDays]);\n\n\tconst growthPctNum = useMemo(() => {\n\t\tconst first = chartRows[0];\n\t\tif (!first) {\n\t\t\treturn 0;\n\t\t}\n\t\tconst last = chartRows.at(-1);\n\t\tif (!last) {\n\t\t\treturn 0;\n\t\t}\n\t\tconst a = first.conversations;\n\t\tconst b = last.conversations;\n\t\tif (!a) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn ((b - a) / a) * 100;\n\t}, [chartRows]);\n\n\tlet xAxisMinTickGap: number | undefined;\n\tif (periodDays <= 7) {\n\t\txAxisMinTickGap = undefined;\n\t} else if (periodDays >= 60) {\n\t\txAxisMinTickGap = 20;\n\t} else {\n\t\txAxisMinTickGap = 28;\n\t}\n\n\treturn (\n\t\t<Card\n\t\t\tclassName={cn(\n\t\t\t\t\"shadow-none md:col-span-2 lg:col-span-3 dark:ring-0\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\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<div className=\"min-w-0 space-y-2\">\n\t\t\t\t\t<div className=\"flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t<CardTitle>Conversation volume</CardTitle>\n\t\t\t\t\t\t<Delta value={growthPctNum} variant=\"badge\">\n\t\t\t\t\t\t\t<DeltaIcon variant=\"trend\" />\n\t\t\t\t\t\t\t<DeltaValue />\n\t\t\t\t\t\t</Delta>\n\t\t\t\t\t</div>\n\t\t\t\t\t<CardDescription>\n\t\t\t\t\t\tNew threads per day for the selected window.\n\t\t\t\t\t</CardDescription>\n\t\t\t\t</div>\n\t\t\t\t<Select\n\t\t\t\t\tonValueChange={(v) => {\n\t\t\t\t\t\tconst n = Number(v);\n\t\t\t\t\t\tsetPeriodDays(n 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=\"Conversation volume 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=\"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</SelectContent>\n\t\t\t\t</Select>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<ChartContainer className=\"aspect-22/8 w-full\" config={chartConfig}>\n\t\t\t\t\t<AreaChart\n\t\t\t\t\t\taccessibilityLayer\n\t\t\t\t\t\tdata={chartRows}\n\t\t\t\t\t\tmargin={{ left: 4, 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-conversations)\"\n\t\t\t\t\t\t\t\t\tstopOpacity={0.45}\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=\"55%\"\n\t\t\t\t\t\t\t\t\tstopColor=\"var(--color-conversations)\"\n\t\t\t\t\t\t\t\t\tstopOpacity={0.12}\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-conversations)\"\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 className=\"stroke-border\" vertical={false} />\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={periodDays <= 7 ? 0 : \"preserveStartEnd\"}\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<YAxis\n\t\t\t\t\t\t\taxisLine={false}\n\t\t\t\t\t\t\ttick={{ className: \"tabular-nums\" }}\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\twidth={36}\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-34\"\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 VolumeRow | 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, \"long\");\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\tcursor={false}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Area\n\t\t\t\t\t\t\tdataKey=\"conversations\"\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-conversations)\"\n\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\ttype=\"natural\"\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</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/3/csat-responses-chart.tsx","type":"registry:component","content":"\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { ComponentProps } from \"react\";\nimport { Bar, BarChart, Rectangle, XAxis } 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\tChartTooltip,\n\tChartTooltipContent,\n} from \"@/components/ui/chart\";\n\nconst chartData = [\n\t{ day: \"Apr 8\", direct: 11, email: 9, social: 4 },\n\t{ day: \"Apr 9\", direct: 15, email: 11, social: 5 },\n\t{ day: \"Apr 10\", direct: 13, email: 10, social: 5 },\n\t{ day: \"Apr 11\", direct: 16, email: 12, social: 5 },\n\t{ day: \"Apr 12\", direct: 12, email: 10, social: 5 },\n\t{ day: \"Apr 13\", direct: 14, email: 10, social: 6 },\n\t{ day: \"Apr 14\", direct: 11, email: 9, social: 5 },\n\t{ day: \"Apr 15\", direct: 16, email: 7, social: 4 },\n\t{ day: \"Apr 16\", direct: 13, email: 11, social: 5 },\n\t{ day: \"Apr 17\", direct: 15, email: 11, social: 6 },\n] as const;\n\nconst chartConfig = {\n\tdirect: {\n\t\tlabel: \"Direct\",\n\t\tcolor: \"var(--chart-1)\",\n\t},\n\temail: {\n\t\tlabel: \"Email\",\n\t\tcolor: \"var(--chart-3)\",\n\t},\n\tsocial: {\n\t\tlabel: \"Social\",\n\t\tcolor: \"var(--chart-5)\",\n\t},\n} satisfies ChartConfig;\n\n/** Half of bar width (8) so ends read as fully rounded “caps”. */\nconst BAR_RADIUS = 5;\n\n/**\n *  column hover background.\n */\nfunction ColumnHoverCursor(props: React.ComponentProps<typeof Rectangle>) {\n\treturn (\n\t\t<Rectangle\n\t\t\tfill=\"var(--muted)\"\n\t\t\tfillOpacity={0.5}\n\t\t\tradius={BAR_RADIUS * 2}\n\t\t\tstroke=\"none\"\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport function CsatResponsesChart({\n\tclassName,\n\t...props\n}: ComponentProps<typeof Card>) {\n\treturn (\n\t\t<Card\n\t\t\tclassName={cn(\"shadow-none md:col-span-2 dark:ring-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>CSAT responses</CardTitle>\n\t\t\t\t<CardDescription>\n\t\t\t\t\tPost-resolution surveys submitted per day by channel, last 10 days.\n\t\t\t\t</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<ChartContainer className=\"aspect-video w-full\" config={chartConfig}>\n\t\t\t\t\t<BarChart accessibilityLayer data={[...chartData]}>\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={0}\n\t\t\t\t\t\t\tminTickGap={8}\n\t\t\t\t\t\t\ttickFormatter={(value) => String(value)}\n\t\t\t\t\t\t\ttickLine={false}\n\t\t\t\t\t\t\ttickMargin={10}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<ChartTooltip\n\t\t\t\t\t\t\tcontent={<ChartTooltipContent hideLabel />}\n\t\t\t\t\t\t\tcursor={<ColumnHoverCursor />}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Bar\n\t\t\t\t\t\t\tbackground={{\n\t\t\t\t\t\t\t\tfill: \"var(--muted)\",\n\t\t\t\t\t\t\t\tradius: BAR_RADIUS,\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tbarSize={8}\n\t\t\t\t\t\t\tdataKey=\"social\"\n\t\t\t\t\t\t\tfill=\"var(--color-social)\"\n\t\t\t\t\t\t\toverflow=\"visible\"\n\t\t\t\t\t\t\tradius={[0, 0, BAR_RADIUS, BAR_RADIUS]}\n\t\t\t\t\t\t\tstackId=\"csat\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Bar\n\t\t\t\t\t\t\tbarSize={8}\n\t\t\t\t\t\t\tdataKey=\"email\"\n\t\t\t\t\t\t\tfill=\"var(--color-email)\"\n\t\t\t\t\t\t\toverflow=\"visible\"\n\t\t\t\t\t\t\tradius={0}\n\t\t\t\t\t\t\tstackId=\"csat\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Bar\n\t\t\t\t\t\t\tbarSize={8}\n\t\t\t\t\t\t\tdataKey=\"direct\"\n\t\t\t\t\t\t\tfill=\"var(--color-direct)\"\n\t\t\t\t\t\t\toverflow=\"visible\"\n\t\t\t\t\t\t\tradius={[BAR_RADIUS, BAR_RADIUS, 0, 0]}\n\t\t\t\t\t\t\tstackId=\"csat\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</BarChart>\n\t\t\t\t</ChartContainer>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/3/dashboard.tsx","type":"registry:component","content":"import { ChannelBreakdownChart } from \"@/components/channel-breakdown-chart\";\nimport { ConversationVolumeChart } from \"@/components/conversation-volume-chart\";\nimport { CsatResponsesChart } from \"@/components/csat-responses-chart\";\nimport { FirstReplyTimeChart } from \"@/components/first-reply-time-chart\";\nimport { RecentConversations } from \"@/components/recent-conversations\";\nimport { DashboardStats } from \"@/components/stats\";\nimport { SupportActivity } from \"@/components/support-activity\";\nimport { TeamOnDuty } from \"@/components/team-on-duty\";\n\nexport function Dashboard() {\n\treturn (\n\t\t<div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n\t\t\t<DashboardStats />\n\t\t\t<ConversationVolumeChart />\n\t\t\t<ChannelBreakdownChart />\n\t\t\t<CsatResponsesChart />\n\t\t\t<FirstReplyTimeChart />\n\t\t\t<TeamOnDuty />\n\t\t\t<RecentConversations />\n\t\t\t<SupportActivity />\n\t\t</div>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/3/first-reply-time-chart.tsx","type":"registry:component","content":"\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { ComponentProps } from \"react\";\nimport { CartesianGrid, LabelList, Line, LineChart, XAxis } 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\tChartTooltip,\n\tChartTooltipContent,\n} from \"@/components/ui/chart\";\nimport { Delta, DeltaIcon, DeltaValue } from \"@/components/delta\";\n\ntype ReplyRow = {\n\tday: string;\n\tminutes: number;\n};\n\nconst chartRows: ReplyRow[] = [\n\t{ day: \"Mon\", minutes: 5.4 },\n\t{ day: \"Tue\", minutes: 4.9 },\n\t{ day: \"Wed\", minutes: 5.2 },\n\t{ day: \"Thu\", minutes: 4.7 },\n\t{ day: \"Fri\", minutes: 4.5 },\n\t{ day: \"Sat\", minutes: 5.0 },\n\t{ day: \"Sun\", minutes: 4.1 },\n];\n\nconst firstMinutes = chartRows[0]?.minutes ?? 0;\nconst lastMinutes = chartRows.at(-1)?.minutes ?? firstMinutes;\n\n/** Positive when median first reply faster Mon → Sun. */\nconst replyImprovementPct =\n\tfirstMinutes > 0 ? ((firstMinutes - lastMinutes) / firstMinutes) * 100 : 0;\n\nconst chartConfig = {\n\tminutes: {\n\t\tlabel: \"Minutes\",\n\t\tcolor: \"var(--chart-2)\",\n\t},\n} satisfies ChartConfig;\n\nexport function FirstReplyTimeChart({\n\tclassName,\n\t...props\n}: ComponentProps<typeof Card>) {\n\treturn (\n\t\t<Card\n\t\t\tclassName={cn(\"shadow-none md:col-span-2 dark:ring-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<CardHeader className=\"space-y-1\">\n\t\t\t\t<div className=\"flex flex-wrap items-center gap-2\">\n\t\t\t\t\t<CardTitle>Median first reply</CardTitle>\n\t\t\t\t\t<Delta value={replyImprovementPct} variant=\"badge\">\n\t\t\t\t\t\t<DeltaIcon variant=\"trend\" />\n\t\t\t\t\t\t<DeltaValue />\n\t\t\t\t\t</Delta>\n\t\t\t\t</div>\n\t\t\t\t<CardDescription>\n\t\t\t\t\tMinutes to first agent message, last 7 days.\n\t\t\t\t</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<ChartContainer className=\"aspect-video w-full\" config={chartConfig}>\n\t\t\t\t\t<LineChart\n\t\t\t\t\t\taccessibilityLayer\n\t\t\t\t\t\tdata={chartRows}\n\t\t\t\t\t\tmargin={{ top: 24, left: 20, right: 12, bottom: 8 }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<CartesianGrid className=\"stroke-border\" vertical={false} />\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={0}\n\t\t\t\t\t\t\ttickFormatter={(value) => String(value).slice(0, 3)}\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={<ChartTooltipContent indicator=\"line\" />}\n\t\t\t\t\t\t\tcursor={false}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Line\n\t\t\t\t\t\t\tactiveDot={{ r: 6 }}\n\t\t\t\t\t\t\tdataKey=\"minutes\"\n\t\t\t\t\t\t\tdot={{ fill: \"var(--color-minutes)\" }}\n\t\t\t\t\t\t\tstroke=\"var(--color-minutes)\"\n\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\ttype=\"natural\"\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-foreground\"\n\t\t\t\t\t\t\t\tdataKey=\"minutes\"\n\t\t\t\t\t\t\t\tfontSize={12}\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\t\t\t\t\t\t\t\t\t\t? `${n.toFixed(1)}m`\n\t\t\t\t\t\t\t\t\t\t: String(label ?? \"\");\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\toffset={12}\n\t\t\t\t\t\t\t\tposition=\"top\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Line>\n\t\t\t\t\t</LineChart>\n\t\t\t\t</ChartContainer>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/3/recent-conversations.tsx","type":"registry:component","content":"import { cn } from \"@/lib/utils\";\nimport type { ComponentProps } from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport {\n\tTable,\n\tTableBody,\n\tTableCell,\n\tTableHead,\n\tTableHeader,\n\tTableRow,\n} from \"@/components/ui/table\";\n\ntype Conversation = {\n\tcustomer: string;\n\tsubject: string;\n\tchannel: \"email\" | \"chat\";\n\twaitMinutes: number;\n\tstate: \"waiting\" | \"open\" | \"snoozed\";\n};\n\nconst rows: Conversation[] = [\n\t{\n\t\tcustomer: \"Northwind Labs\",\n\t\tsubject: \"Billing portal access\",\n\t\tchannel: \"email\",\n\t\twaitMinutes: 6,\n\t\tstate: \"waiting\",\n\t},\n\t{\n\t\tcustomer: \"Blue River Co.\",\n\t\tsubject: \"Shipment ETA\",\n\t\tchannel: \"chat\",\n\t\twaitMinutes: 1,\n\t\tstate: \"open\",\n\t},\n\t{\n\t\tcustomer: \"Oak Street Studio\",\n\t\tsubject: \"API rate limits\",\n\t\tchannel: \"email\",\n\t\twaitMinutes: 58,\n\t\tstate: \"snoozed\",\n\t},\n\t{\n\t\tcustomer: \"Harbor Freight LLC\",\n\t\tsubject: \"Workspace SSO\",\n\t\tchannel: \"chat\",\n\t\twaitMinutes: 3,\n\t\tstate: \"open\",\n\t},\n];\n\nfunction formatWaitTime(minutes: number): string {\n\tif (minutes <= 0) {\n\t\treturn \"Just now\";\n\t}\n\tif (minutes === 1) {\n\t\treturn \"1 minute\";\n\t}\n\tif (minutes < 55) {\n\t\treturn `${minutes} minutes`;\n\t}\n\tif (minutes < 60) {\n\t\treturn \"Almost an hour\";\n\t}\n\tif (minutes < 75) {\n\t\treturn \"About an hour\";\n\t}\n\tif (minutes < 120) {\n\t\treturn \"Over an hour\";\n\t}\n\tconst hours = Math.round(minutes / 60);\n\treturn hours === 1 ? \"About an hour\" : `About ${hours} hours`;\n}\n\nfunction statusVariant(\n\tstate: Conversation[\"state\"]\n): ComponentProps<typeof Badge>[\"variant\"] {\n\tif (state === \"waiting\") {\n\t\treturn \"destructive\";\n\t}\n\tif (state === \"snoozed\") {\n\t\treturn \"outline\";\n\t}\n\treturn \"secondary\";\n}\n\nfunction statusLabel(state: Conversation[\"state\"]): string {\n\tif (state === \"waiting\") {\n\t\treturn \"In queue\";\n\t}\n\tif (state === \"snoozed\") {\n\t\treturn \"Snoozed\";\n\t}\n\treturn \"Active\";\n}\n\nfunction channelIcon(channel: Conversation[\"channel\"]) {\n\tif (channel === \"email\") {\n\t\treturn (\n\t\t\t<IconPlaceholder\n\t\t\t\tclassName=\"size-3.5 shrink-0\"\n\t\t\t\thugeicons=\"MailSend01Icon\"\n\t\t\t\tlucide=\"MailIcon\"\n\t\t\t\tphosphor=\"EnvelopeIcon\"\n\t\t\t\tremixicon=\"RiMailLine\"\n\t\t\t\ttabler=\"IconMail\"\n\t\t\t/>\n\t\t);\n\t}\n\treturn (\n\t\t<IconPlaceholder\n\t\t\tclassName=\"size-3.5 shrink-0\"\n\t\t\thugeicons=\"Chat01Icon\"\n\t\t\tlucide=\"MessageCircleIcon\"\n\t\t\tphosphor=\"ChatIcon\"\n\t\t\tremixicon=\"RiChat1Line\"\n\t\t\ttabler=\"IconMessageCircle\"\n\t\t/>\n\t);\n}\n\nexport function RecentConversations({\n\tclassName,\n\t...props\n}: ComponentProps<typeof Card>) {\n\treturn (\n\t\t<Card\n\t\t\tclassName={cn(\"gap-0 shadow-none md:col-span-2 dark:ring-0\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<CardHeader className=\"border-b\">\n\t\t\t\t<CardTitle>Recent conversations</CardTitle>\n\t\t\t\t<CardDescription>Latest 4 threads from your inbox</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent className=\"p-0\">\n\t\t\t\t<Table>\n\t\t\t\t\t<TableHeader>\n\t\t\t\t\t\t<TableRow className=\"hover:bg-transparent\">\n\t\t\t\t\t\t\t<TableHead className=\"pl-6\">Customer</TableHead>\n\t\t\t\t\t\t\t<TableHead className=\"hidden sm:table-cell\">Topic</TableHead>\n\t\t\t\t\t\t\t<TableHead>Channel</TableHead>\n\t\t\t\t\t\t\t<TableHead className=\"text-right\">Wait</TableHead>\n\t\t\t\t\t\t\t<TableHead className=\"pr-6 text-right\">Status</TableHead>\n\t\t\t\t\t\t</TableRow>\n\t\t\t\t\t</TableHeader>\n\t\t\t\t\t<TableBody>\n\t\t\t\t\t\t{rows.map((r) => {\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<TableRow\n\t\t\t\t\t\t\t\t\tclassName=\"h-14 hover:bg-transparent\"\n\t\t\t\t\t\t\t\t\tkey={`${r.customer}-${r.subject}`}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<TableCell className=\"max-w-36 truncate pl-6 font-medium\">\n\t\t\t\t\t\t\t\t\t\t{r.customer}\n\t\t\t\t\t\t\t\t\t</TableCell>\n\t\t\t\t\t\t\t\t\t<TableCell className=\"hidden max-w-32 sm:table-cell\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"line-clamp-1 text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t\t\t\t\t{r.subject}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</TableCell>\n\t\t\t\t\t\t\t\t\t<TableCell>\n\t\t\t\t\t\t\t\t\t\t<span className=\"inline-flex items-center gap-2 font-medium text-sm capitalize\">\n\t\t\t\t\t\t\t\t\t\t\t{channelIcon(r.channel)}\n\t\t\t\t\t\t\t\t\t\t\t{r.channel}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</TableCell>\n\t\t\t\t\t\t\t\t\t<TableCell className=\"text-right text-muted-foreground text-sm\">\n\t\t\t\t\t\t\t\t\t\t{formatWaitTime(r.waitMinutes)}\n\t\t\t\t\t\t\t\t\t</TableCell>\n\t\t\t\t\t\t\t\t\t<TableCell className=\"pr-6 text-right\">\n\t\t\t\t\t\t\t\t\t\t<Badge variant={statusVariant(r.state)}>\n\t\t\t\t\t\t\t\t\t\t\t{statusLabel(r.state)}\n\t\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t\t</TableCell>\n\t\t\t\t\t\t\t\t</TableRow>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</TableBody>\n\t\t\t\t</Table>\n\t\t\t\t<div className=\"flex justify-center border-t py-3\">\n\t\t\t\t\t<Button asChild size=\"sm\" variant=\"ghost\">\n\t\t\t\t\t\t<a href=\"#/inbox\">\n\t\t\t\t\t\t\tView all conversations\n\t\t\t\t\t\t\t<IconPlaceholder\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tdata-icon=\"inline-end\"\n\t\t\t\t\t\t\t\thugeicons=\"ArrowRight02Icon\"\n\t\t\t\t\t\t\t\tlucide=\"ArrowRightIcon\"\n\t\t\t\t\t\t\t\tphosphor=\"ArrowRightIcon\"\n\t\t\t\t\t\t\t\tremixicon=\"RiArrowRightLine\"\n\t\t\t\t\t\t\t\ttabler=\"IconArrowRight\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/3/stats.tsx","type":"registry:component","content":"import { cn } from \"@/lib/utils\";\nimport {\n\tCard,\n\tCardContent,\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\tfootnote: string;\n\t/** When true, a negative delta is treated as favorable (e.g. queue depth, reply time). */\n\tlowerIsBetter: boolean;\n};\n\nconst stats: readonly Stat[] = [\n\t{\n\t\tlabel: \"Open queue\",\n\t\tvalue: \"38\",\n\t\tdelta: -12.4,\n\t\tfootnote: \"vs yesterday\",\n\t\tlowerIsBetter: true,\n\t},\n\t{\n\t\tlabel: \"Active conversations\",\n\t\tvalue: \"126\",\n\t\tdelta: 5.2,\n\t\tfootnote: \"vs last week\",\n\t\tlowerIsBetter: false,\n\t},\n\t{\n\t\tlabel: \"Median first reply\",\n\t\tvalue: \"4.1m\",\n\t\tdelta: -8.0,\n\t\tfootnote: \"vs last week\",\n\t\tlowerIsBetter: true,\n\t},\n\t{\n\t\tlabel: \"CSAT (30d)\",\n\t\tvalue: \"94%\",\n\t\tdelta: 1.1,\n\t\tfootnote: \"vs prior 30d\",\n\t\tlowerIsBetter: false,\n\t},\n];\n\nexport function DashboardStats() {\n\treturn (\n\t\t<>\n\t\t\t{stats.map((s) => (\n\t\t\t\t<Card className={cn(\"shadow-none dark:ring-0\")} key={s.label}>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle className=\"font-normal text-muted-foreground text-xs\">\n\t\t\t\t\t\t\t{s.label}\n\t\t\t\t\t\t</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t<p className=\"font-semibold text-2xl tabular-nums\">{s.value}</p>\n\t\t\t\t\t\t<div className=\"flex items-center gap-1 text-xs\">\n\t\t\t\t\t\t\t<Delta value={s.delta}>\n\t\t\t\t\t\t\t\t<DeltaIcon />\n\t\t\t\t\t\t\t\t<DeltaValue />\n\t\t\t\t\t\t\t</Delta>\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{s.footnote}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t))}\n\t\t</>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/3/support-activity.tsx","type":"registry:component","content":"import { cn } from \"@/lib/utils\";\nimport type { ComponentProps } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\n\nconst items = [\n\t{\n\t\ttitle: \"SLA warning cleared for Oak Street Studio\",\n\t\ttime: \"12 min ago\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\thugeicons=\"ShieldCheck\"\n\t\t\t\tlucide=\"ShieldCheckIcon\"\n\t\t\t\tphosphor=\"ShieldCheckIcon\"\n\t\t\t\tremixicon=\"RiShieldCheckLine\"\n\t\t\t\ttabler=\"IconShieldCheck\"\n\t\t\t/>\n\t\t),\n\t},\n\t{\n\t\ttitle: \"Conversation escalated to Tier 2\",\n\t\ttime: \"28 min ago\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\thugeicons=\"Layers01Icon\"\n\t\t\t\tlucide=\"LayersIcon\"\n\t\t\t\tphosphor=\"StackIcon\"\n\t\t\t\tremixicon=\"RiStackLine\"\n\t\t\t\ttabler=\"IconStack\"\n\t\t\t/>\n\t\t),\n\t},\n\t{\n\t\ttitle: \"Macro “Refund approved” updated\",\n\t\ttime: \"1 hr ago\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\thugeicons=\"MagicWand01Icon\"\n\t\t\t\tlucide=\"WandIcon\"\n\t\t\t\tphosphor=\"MagicWandIcon\"\n\t\t\t\tremixicon=\"RiMagicLine\"\n\t\t\t\ttabler=\"IconWand\"\n\t\t\t/>\n\t\t),\n\t},\n\t{\n\t\ttitle: \"New customer segment synced from CRM\",\n\t\ttime: \"3 hr ago\",\n\t\ticon: (\n\t\t\t<IconPlaceholder\n\t\t\t\thugeicons=\"UserGroupIcon\"\n\t\t\t\tlucide=\"UsersRoundIcon\"\n\t\t\t\tphosphor=\"UsersThreeIcon\"\n\t\t\t\tremixicon=\"RiGroupLine\"\n\t\t\t\ttabler=\"IconUsersGroup\"\n\t\t\t/>\n\t\t),\n\t},\n] as const;\n\nexport function SupportActivity({\n\tclassName,\n\t...props\n}: ComponentProps<typeof Card>) {\n\treturn (\n\t\t<Card className={cn(\"gap-0 shadow-none dark:ring-0\", className)} {...props}>\n\t\t\t<CardHeader className=\"border-b\">\n\t\t\t\t<CardTitle>Workspace activity</CardTitle>\n\t\t\t\t<CardDescription>Operational signals.</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent className=\"px-0\">\n\t\t\t\t<ul className=\"flex flex-col divide-y divide-border\">\n\t\t\t\t\t{items.map((item) => (\n\t\t\t\t\t\t<li className=\"flex h-18 items-center gap-3 px-3\" key={item.title}>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tclassName=\"flex size-10 shrink-0 items-center justify-center [&_svg]:size-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{item.icon}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<div className=\"min-w-0 flex-1 space-y-1\">\n\t\t\t\t\t\t\t\t<p className=\"line-clamp-2 text-pretty text-foreground text-xs leading-snug\">\n\t\t\t\t\t\t\t\t\t{item.title}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-xs tabular-nums\">\n\t\t\t\t\t\t\t\t\t{item.time}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t</CardContent>\n\t\t\t<div className=\"flex items-center justify-center\">\n\t\t\t\t<Button asChild size=\"sm\" variant=\"ghost\">\n\t\t\t\t\t<a href=\"/#\">\n\t\t\t\t\t\tView All\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</div>\n\t\t</Card>\n\t);\n}\n"},{"path":"registry/blocks/dashboard/3/team-on-duty.tsx","type":"registry:component","content":"\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { type ComponentProps, useState } from \"react\";\nimport {\n\tAvatar,\n\tAvatarFallback,\n\tAvatarImage,\n} from \"@/components/ui/avatar\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"@/components/ui/card\";\nimport {\n\tDropdownMenu,\n\tDropdownMenuContent,\n\tDropdownMenuItem,\n\tDropdownMenuLabel,\n\tDropdownMenuSeparator,\n\tDropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { StatusIndicator } from \"@/components/indicator\";\n\ntype Teammate = {\n\tid: string;\n\tname: string;\n\tstatus: \"Online\" | \"Away\";\n\t/** Conversations currently assigned to this teammate */\n\topen: number;\n\timage?: string;\n};\n\nconst INITIAL_TEAMMATES: readonly Teammate[] = [\n\t{\n\t\tid: \"amelia\",\n\t\tname: \"Amelia Park\",\n\t\tstatus: \"Online\",\n\t\topen: 9,\n\t\timage: \"https://avatar.vercel.sh/ameliapark\",\n\t},\n\t{\n\t\tid: \"noah\",\n\t\tname: \"Noah Ibarra\",\n\t\tstatus: \"Online\",\n\t\topen: 7,\n\t\timage: \"https://avatar.vercel.sh/noahi\",\n\t},\n\t{\n\t\tid: \"priya\",\n\t\tname: \"Priya Desai\",\n\t\tstatus: \"Away\",\n\t\topen: 4,\n\t\timage: \"https://avatar.vercel.sh/priyadesai\",\n\t},\n\t{\n\t\tid: \"marcus\",\n\t\tname: \"Marcus Chen\",\n\t\tstatus: \"Online\",\n\t\topen: 11,\n\t\timage: \"https://avatar.vercel.sh/marcuschen\",\n\t},\n\t{\n\t\tid: \"emily\",\n\t\tname: \"Emily Johnson\",\n\t\tstatus: \"Away\",\n\t\topen: 2,\n\t\timage: \"https://avatar.vercel.sh/emilyjohnson\",\n\t},\n];\n\nfunction getInitials(name: string) {\n\treturn name\n\t\t.split(\" \")\n\t\t.map((word) => word.charAt(0).toUpperCase())\n\t\t.join(\"\");\n}\n\nexport function TeamOnDuty({\n\tclassName,\n\t...props\n}: ComponentProps<typeof Card>) {\n\tconst [teammates, setTeammates] = useState<Teammate[]>(() => [\n\t\t...INITIAL_TEAMMATES,\n\t]);\n\n\tfunction pullNextConversation(id: string) {\n\t\tsetTeammates((prev) =>\n\t\t\tprev.map((t) =>\n\t\t\t\tt.id === id ? { ...t, open: Math.max(0, t.open - 1) } : t\n\t\t\t)\n\t\t);\n\t}\n\n\treturn (\n\t\t<Card className={cn(\"shadow-none dark:ring-0\", className)} {...props}>\n\t\t\t<CardHeader className=\"border-b\">\n\t\t\t\t<CardTitle>Team on duty</CardTitle>\n\t\t\t\t<CardDescription>Who is carrying the queue right now</CardDescription>\n\t\t\t</CardHeader>\n\t\t\t<CardContent className=\"p-0\">\n\t\t\t\t<ul className=\"flex flex-col divide-y divide-border\">\n\t\t\t\t\t{teammates.map((t) => (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 p-3 first:pt-0 last:pb-0 sm:gap-3\"\n\t\t\t\t\t\t\tkey={t.id}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Avatar className=\"size-8\">\n\t\t\t\t\t\t\t\t<AvatarImage alt={t.name} src={t.image} />\n\t\t\t\t\t\t\t\t<AvatarFallback>{getInitials(t.name)}</AvatarFallback>\n\t\t\t\t\t\t\t</Avatar>\n\t\t\t\t\t\t\t<div className=\"min-w-0 flex-1 pr-1\">\n\t\t\t\t\t\t\t\t<p className=\"truncate font-medium text-foreground text-sm leading-snug\">\n\t\t\t\t\t\t\t\t\t{t.name}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<p className=\"flex items-center gap-2 text-[10px] leading-snug\">\n\t\t\t\t\t\t\t\t\t<span className=\"flex shrink-0 items-center gap-1\">\n\t\t\t\t\t\t\t\t\t\t<StatusIndicator\n\t\t\t\t\t\t\t\t\t\t\tcolor={t.status === \"Online\" ? \"emerald\" : \"amber\"}\n\t\t\t\t\t\t\t\t\t\t\tpulse={t.status === \"Online\"}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t{t.status}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<span className=\"inline-flex size-1 rounded-full bg-foreground/80\" />\n\t\t\t\t\t\t\t\t\t<span className=\"tabular-nums\">{t.open} assigned</span>\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<DropdownMenu>\n\t\t\t\t\t\t\t\t<DropdownMenuTrigger asChild>\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\taria-label={`Actions for ${t.name}`}\n\t\t\t\t\t\t\t\t\t\tsize=\"icon-xs\"\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<IconPlaceholder\n\t\t\t\t\t\t\t\t\t\t\thugeicons=\"MoreHorizontalCircle01Icon\"\n\t\t\t\t\t\t\t\t\t\t\tlucide=\"EllipsisIcon\"\n\t\t\t\t\t\t\t\t\t\t\tphosphor=\"DotsThreeIcon\"\n\t\t\t\t\t\t\t\t\t\t\tremixicon=\"RiMoreLine\"\n\t\t\t\t\t\t\t\t\t\t\ttabler=\"IconDots\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</DropdownMenuTrigger>\n\t\t\t\t\t\t\t\t<DropdownMenuContent align=\"end\" className=\"min-w-52\">\n\t\t\t\t\t\t\t\t\t<DropdownMenuLabel className=\"font-normal text-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t\t\t{t.name}\n\t\t\t\t\t\t\t\t\t</DropdownMenuLabel>\n\t\t\t\t\t\t\t\t\t<DropdownMenuSeparator />\n\t\t\t\t\t\t\t\t\t<DropdownMenuItem className=\"gap-2\">\n\t\t\t\t\t\t\t\t\t\t<IconPlaceholder\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"size-4 opacity-70\"\n\t\t\t\t\t\t\t\t\t\t\thugeicons=\"MailSend01Icon\"\n\t\t\t\t\t\t\t\t\t\t\tlucide=\"SendIcon\"\n\t\t\t\t\t\t\t\t\t\t\tphosphor=\"PaperPlaneTiltIcon\"\n\t\t\t\t\t\t\t\t\t\t\tremixicon=\"RiSendPlaneLine\"\n\t\t\t\t\t\t\t\t\t\t\ttabler=\"IconSend\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\tMessage\n\t\t\t\t\t\t\t\t\t</DropdownMenuItem>\n\t\t\t\t\t\t\t\t\t<DropdownMenuItem\n\t\t\t\t\t\t\t\t\t\tclassName=\"gap-2\"\n\t\t\t\t\t\t\t\t\t\tdisabled={t.open === 0}\n\t\t\t\t\t\t\t\t\t\tonSelect={() => {\n\t\t\t\t\t\t\t\t\t\t\tpullNextConversation(t.id);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<IconPlaceholder\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"size-4 opacity-70\"\n\t\t\t\t\t\t\t\t\t\t\thugeicons=\"CheckListIcon\"\n\t\t\t\t\t\t\t\t\t\t\tlucide=\"ListChecksIcon\"\n\t\t\t\t\t\t\t\t\t\t\tphosphor=\"ListChecksIcon\"\n\t\t\t\t\t\t\t\t\t\t\tremixicon=\"RiListCheck2\"\n\t\t\t\t\t\t\t\t\t\t\ttabler=\"IconChecklist\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\tPull next conversation\n\t\t\t\t\t\t\t\t\t</DropdownMenuItem>\n\t\t\t\t\t\t\t\t</DropdownMenuContent>\n\t\t\t\t\t\t\t</DropdownMenu>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t</CardContent>\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"}]}