{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dashboard",
  "description": "A logged-in app starter: a collapsible sidebar and a command-palette top bar wrapping a dashboard of stat cards, an interactive area chart, and a sortable, paginated data table.",
  "dependencies": [
    "@remixicon/react",
    "@tanstack/react-table",
    "recharts",
    "@base-ui/react"
  ],
  "registryDependencies": [
    "avatar",
    "badge",
    "button",
    "card",
    "chart",
    "checkbox",
    "command",
    "dropdown-menu",
    "input",
    "kbd",
    "select",
    "sheet",
    "sidebar",
    "sonner",
    "table",
    "utils"
  ],
  "files": [
    {
      "path": "registry/templates/dashboard/install-page.tsx",
      "content": "import { Toaster } from \"@/components/ui/sonner\"\nimport AppShell from \"@/components/dashboard/app-shell\"\nimport Charts from \"@/components/dashboard/charts\"\nimport Dashboard from \"@/components/dashboard/dashboard\"\nimport DataTable from \"@/components/dashboard/table\"\n\nexport default function DashboardTemplate() {\n  return (\n    <AppShell>\n      <Toaster />\n      <div className=\"mx-auto w-full max-w-6xl space-y-6\">\n        <Dashboard />\n        <Charts />\n        <DataTable />\n      </div>\n    </AppShell>\n  )\n}\n",
      "type": "registry:page",
      "target": "app/dashboard/page.tsx"
    },
    {
      "path": "registry/templates/dashboard/components/app-shell.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  RiBarChartBoxLine,\n  RiErrorWarningLine,\n  RiFolder3Line,\n  RiLayoutGridLine,\n  RiLogoutBoxRLine,\n  RiNotification3Line,\n  RiRocketLine,\n  RiSearchLine,\n  RiSettings3Line,\n  RiTeamLine,\n  RiUser3Line,\n  RiUserAddLine,\n} from \"@remixicon/react\"\nimport { toast } from \"sonner\"\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Command,\n  CommandDialog,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n  CommandShortcut,\n} from \"@/components/ui/command\"\nimport { Kbd, KbdGroup } from \"@/components/ui/kbd\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport {\n  Sheet,\n  SheetContent,\n  SheetDescription,\n  SheetHeader,\n  SheetTitle,\n  SheetTrigger,\n} from \"@/components/ui/sheet\"\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarHeader,\n  SidebarInset,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  SidebarProvider,\n  SidebarTrigger,\n} from \"@/components/ui/sidebar\"\n\nconst navItems = [\n  { label: \"Overview\", icon: RiLayoutGridLine },\n  { label: \"Analytics\", icon: RiBarChartBoxLine },\n  { label: \"Projects\", icon: RiFolder3Line },\n  { label: \"Team\", icon: RiTeamLine },\n  { label: \"Settings\", icon: RiSettings3Line },\n]\n\nconst notifications = [\n  {\n    id: 1,\n    icon: RiRocketLine,\n    title: \"Deployment succeeded\",\n    detail: \"acme-web v2.4 is live in production.\",\n    time: \"2 Min Ago\",\n    unread: true,\n    tone: \"primary\" as const,\n  },\n  {\n    id: 2,\n    icon: RiUserAddLine,\n    title: \"New team member\",\n    detail: \"Priya Nair accepted your invite.\",\n    time: \"1 Hour Ago\",\n    unread: true,\n    tone: \"primary\" as const,\n  },\n  {\n    id: 3,\n    icon: RiErrorWarningLine,\n    title: \"Usage at 80%\",\n    detail: \"You're approaching your monthly request limit.\",\n    time: \"3 Hours Ago\",\n    unread: false,\n    tone: \"destructive\" as const,\n  },\n]\n\nconst recentItems = [\n  { label: \"Acme Web Redesign\", icon: RiFolder3Line },\n  { label: \"Q3 Analytics report\", icon: RiBarChartBoxLine },\n  { label: \"Priya Nair\", icon: RiUser3Line },\n]\n\nconst commands = [\n  { label: \"Go to Overview\", icon: RiLayoutGridLine, shortcut: [\"G\", \"O\"] },\n  { label: \"Go to Analytics\", icon: RiBarChartBoxLine, shortcut: [\"G\", \"A\"] },\n  { label: \"Go to Projects\", icon: RiFolder3Line, shortcut: [\"G\", \"P\"] },\n  { label: \"Go to Team\", icon: RiTeamLine, shortcut: [\"G\", \"T\"] },\n  { label: \"Invite a teammate\", icon: RiUserAddLine, shortcut: [\"I\"] },\n  { label: \"Open settings\", icon: RiSettings3Line, shortcut: [\"⌘\", \",\"] },\n]\n\nexport default function AppShell({ children }: { children: React.ReactNode }) {\n  const [activeNav, setActiveNav] = React.useState(\"Overview\")\n  const [commandOpen, setCommandOpen] = React.useState(false)\n  const [notes, setNotes] = React.useState(notifications)\n  const unreadCount = notes.filter((n) => n.unread).length\n  const markAllRead = () =>\n    setNotes((prev) => prev.map((n) => ({ ...n, unread: false })))\n\n  React.useEffect(() => {\n    function onKeyDown(event: KeyboardEvent) {\n      if (event.key === \"k\" && (event.metaKey || event.ctrlKey)) {\n        event.preventDefault()\n        setCommandOpen((open) => !open)\n      }\n    }\n    document.addEventListener(\"keydown\", onKeyDown)\n    return () => document.removeEventListener(\"keydown\", onKeyDown)\n  }, [])\n\n  return (\n    <SidebarProvider className=\"min-h-svh bg-muted/30 text-foreground\">\n      <Sidebar collapsible=\"icon\">\n        <SidebarHeader>\n          <div className=\"flex h-10 items-center gap-2 px-1 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0\">\n            <svg\n              viewBox=\"0 0 24 24\"\n              fill=\"currentColor\"\n              aria-hidden=\"true\"\n              className=\"size-5 shrink-0 text-foreground\"\n            >\n              <rect\n                x=\"3\"\n                y=\"3\"\n                width=\"8\"\n                height=\"8\"\n                transform=\"rotate(-6 7 7)\"\n              />\n              <rect\n                x=\"3\"\n                y=\"13\"\n                width=\"8\"\n                height=\"8\"\n                transform=\"rotate(5 7 17)\"\n              />\n              <rect\n                x=\"13\"\n                y=\"13\"\n                width=\"8\"\n                height=\"8\"\n                transform=\"rotate(-4 17 17)\"\n              />\n              <rect\n                x=\"13\"\n                y=\"3\"\n                width=\"8\"\n                height=\"8\"\n                transform=\"rotate(15 17 7)\"\n              />\n            </svg>\n            <span className=\"truncate text-base font-semibold group-data-[collapsible=icon]:hidden\">\n              Acme\n            </span>\n          </div>\n        </SidebarHeader>\n\n        <SidebarContent>\n          <SidebarGroup>\n            <SidebarMenu>\n              {navItems.map((item) => (\n                <SidebarMenuItem key={item.label}>\n                  <SidebarMenuButton\n                    isActive={activeNav === item.label}\n                    tooltip={item.label}\n                    onClick={() => setActiveNav(item.label)}\n                  >\n                    <item.icon />\n                    <span>{item.label}</span>\n                  </SidebarMenuButton>\n                </SidebarMenuItem>\n              ))}\n            </SidebarMenu>\n          </SidebarGroup>\n        </SidebarContent>\n\n        <SidebarFooter>\n          <SidebarMenu>\n            <SidebarMenuItem>\n              <AccountMenu\n                trigger={\n                  <SidebarMenuButton\n                    size=\"lg\"\n                    tooltip=\"Account\"\n                    className=\"gap-2 group-data-[collapsible=icon]:justify-center\"\n                  >\n                    <Avatar size=\"sm\">\n                      <AvatarImage\n                        src=\"https://i.pravatar.cc/150?img=15\"\n                        alt=\"Avery Cole\"\n                        className=\"grayscale\"\n                      />\n                      <AvatarFallback>AC</AvatarFallback>\n                    </Avatar>\n                    <span className=\"flex min-w-0 flex-col group-data-[collapsible=icon]:hidden\">\n                      <span className=\"truncate text-xs font-medium\">\n                        Avery Cole\n                      </span>\n                      <span className=\"truncate text-xs text-muted-foreground\">\n                        avery@acme.com\n                      </span>\n                    </span>\n                  </SidebarMenuButton>\n                }\n              />\n            </SidebarMenuItem>\n          </SidebarMenu>\n        </SidebarFooter>\n      </Sidebar>\n\n      <SidebarInset>\n        <header className=\"flex h-16 shrink-0 items-center gap-3 border-b border-border bg-background px-4 sm:px-6\">\n          <SidebarTrigger className=\"-ml-1\" />\n\n          <button\n            type=\"button\"\n            onClick={() => setCommandOpen(true)}\n            className=\"relative flex h-9 w-full max-w-sm items-center gap-2 border border-border bg-background px-3 text-sm text-muted-foreground transition-colors hover:bg-muted/50\"\n          >\n            <RiSearchLine className=\"size-4 shrink-0\" aria-hidden=\"true\" />\n            <span className=\"flex-1 text-left\">Search projects, people...</span>\n            <KbdGroup className=\"hidden sm:inline-flex\">\n              <Kbd>⌘</Kbd>\n              <Kbd>K</Kbd>\n            </KbdGroup>\n          </button>\n\n          <div className=\"ml-auto flex items-center gap-2\">\n            <Sheet>\n              <SheetTrigger\n                render={\n                  <Button\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    aria-label=\"Notifications\"\n                    className=\"relative\"\n                  >\n                    <RiNotification3Line className=\"size-5\" />\n                    {unreadCount > 0 && (\n                      <span className=\"absolute -top-1 -right-1 flex size-4 items-center justify-center bg-primary text-[9px] leading-none font-bold text-primary-foreground ring-2 ring-background\">\n                        {unreadCount}\n                      </span>\n                    )}\n                  </Button>\n                }\n              />\n              <SheetContent className=\"w-full sm:max-w-sm\">\n                <SheetHeader className=\"gap-0.5 border-b border-border\">\n                  <SheetTitle>Notifications</SheetTitle>\n                  <SheetDescription>\n                    {unreadCount > 0\n                      ? `${unreadCount} unread updates`\n                      : \"You're all caught up\"}\n                  </SheetDescription>\n                </SheetHeader>\n                <div className=\"flex flex-1 flex-col overflow-y-auto\">\n                  {notes.map((note) => (\n                    <button\n                      key={note.id}\n                      type=\"button\"\n                      onClick={() =>\n                        setNotes((prev) =>\n                          prev.map((n) =>\n                            n.id === note.id ? { ...n, unread: false } : n\n                          )\n                        )\n                      }\n                      className={[\n                        \"flex items-start gap-3 border-b border-border px-4 py-3 text-left transition-colors hover:bg-muted/50 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring\",\n                        note.unread ? \"bg-muted/30\" : \"\",\n                      ]\n                        .filter(Boolean)\n                        .join(\" \")}\n                    >\n                      <span\n                        className={[\n                          \"mt-0.5 flex size-8 shrink-0 items-center justify-center\",\n                          note.tone === \"destructive\"\n                            ? \"bg-destructive/10 text-destructive\"\n                            : \"bg-primary/10 text-primary\",\n                        ].join(\" \")}\n                      >\n                        <note.icon className=\"size-4\" aria-hidden=\"true\" />\n                      </span>\n                      <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n                        <div className=\"flex items-center justify-between gap-2\">\n                          <p className=\"text-sm font-medium text-foreground\">\n                            {note.title}\n                          </p>\n                          <span className=\"shrink-0 text-[10px] text-muted-foreground tabular-nums\">\n                            {note.time}\n                          </span>\n                        </div>\n                        <p className=\"text-xs text-muted-foreground\">\n                          {note.detail}\n                        </p>\n                      </div>\n                      {note.unread && (\n                        <span\n                          className=\"mt-1.5 size-2 shrink-0 bg-primary\"\n                          aria-label=\"Unread\"\n                        />\n                      )}\n                    </button>\n                  ))}\n                </div>\n                <div className=\"flex items-center justify-between border-t border-border p-3\">\n                  <Button\n                    variant=\"ghost\"\n                    size=\"sm\"\n                    className=\"text-xs text-muted-foreground hover:text-foreground\"\n                    onClick={markAllRead}\n                    disabled={unreadCount === 0}\n                  >\n                    Mark all read\n                  </Button>\n                  <Button\n                    variant=\"ghost\"\n                    size=\"sm\"\n                    className=\"text-xs text-muted-foreground hover:text-foreground\"\n                    onClick={() =>\n                      toast(\"Notifications\", {\n                        description: \"Opening all notifications.\",\n                      })\n                    }\n                  >\n                    View all\n                  </Button>\n                </div>\n              </SheetContent>\n            </Sheet>\n\n            <AccountMenu\n              trigger={\n                <Button variant=\"ghost\" size=\"icon\" aria-label=\"Account menu\">\n                  <Avatar>\n                    <AvatarImage\n                      src=\"https://i.pravatar.cc/150?img=15\"\n                      alt=\"Avery Cole\"\n                      className=\"grayscale\"\n                    />\n                    <AvatarFallback>AC</AvatarFallback>\n                  </Avatar>\n                </Button>\n              }\n            />\n          </div>\n        </header>\n\n        <main className=\"flex-1 overflow-auto p-4 sm:p-6\">{children}</main>\n      </SidebarInset>\n\n      <CommandDialog open={commandOpen} onOpenChange={setCommandOpen}>\n        <Command>\n          <CommandInput placeholder=\"Type a command or search...\" />\n          <CommandList>\n            <CommandEmpty>No results found.</CommandEmpty>\n            <CommandGroup heading=\"Recent\">\n              {recentItems.map((item) => (\n                <CommandItem\n                  key={item.label}\n                  value={item.label}\n                  onSelect={() => {\n                    setCommandOpen(false)\n                    toast(item.label, { description: \"Opening recent item.\" })\n                  }}\n                >\n                  <item.icon />\n                  <span>{item.label}</span>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n            <CommandSeparator />\n            <CommandGroup heading=\"Commands\">\n              {commands.map((command) => (\n                <CommandItem\n                  key={command.label}\n                  value={command.label}\n                  onSelect={() => {\n                    setCommandOpen(false)\n                    toast(command.label, { description: \"Command executed.\" })\n                  }}\n                >\n                  <command.icon />\n                  <span>{command.label}</span>\n                  <CommandShortcut>\n                    <KbdGroup>\n                      {command.shortcut.map((key) => (\n                        <Kbd key={key}>{key}</Kbd>\n                      ))}\n                    </KbdGroup>\n                  </CommandShortcut>\n                </CommandItem>\n              ))}\n            </CommandGroup>\n          </CommandList>\n        </Command>\n      </CommandDialog>\n    </SidebarProvider>\n  )\n}\n\nfunction AccountMenu({ trigger }: { trigger: React.ReactElement }) {\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger render={trigger} />\n      <DropdownMenuContent align=\"end\" className=\"w-48\">\n        <div className=\"flex flex-col px-2 py-1.5\">\n          <span className=\"text-xs font-medium text-foreground\">\n            Avery Cole\n          </span>\n          <span className=\"text-xs text-muted-foreground\">avery@acme.com</span>\n        </div>\n        <DropdownMenuSeparator />\n        <DropdownMenuItem\n          onClick={() =>\n            toast(\"Account\", { description: \"Opening your account.\" })\n          }\n        >\n          <RiUser3Line />\n          Account\n        </DropdownMenuItem>\n        <DropdownMenuItem\n          onClick={() =>\n            toast(\"Settings\", { description: \"Opening your settings.\" })\n          }\n        >\n          <RiSettings3Line />\n          Settings\n        </DropdownMenuItem>\n        <DropdownMenuSeparator />\n        <DropdownMenuItem\n          variant=\"destructive\"\n          onClick={() =>\n            toast.success(\"Logged out\", { description: \"See you soon.\" })\n          }\n        >\n          <RiLogoutBoxRLine />\n          Log Out\n        </DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/app-shell.tsx"
    },
    {
      "path": "registry/templates/dashboard/components/charts.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Area, AreaChart, CartesianGrid, XAxis, YAxis } from \"recharts\"\nimport { RiArrowDownLine, RiArrowUpLine } from \"@remixicon/react\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport {\n  type ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"@/components/ui/chart\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\n\ntype RangeKey = \"7d\" | \"30d\" | \"90d\"\n\ntype DataPoint = {\n  label: string\n  visitors: number\n  signups: number\n}\n\nconst datasets: Record<RangeKey, DataPoint[]> = {\n  \"7d\": [\n    { label: \"Mon\", visitors: 4_120, signups: 312 },\n    { label: \"Tue\", visitors: 4_780, signups: 348 },\n    { label: \"Wed\", visitors: 5_240, signups: 401 },\n    { label: \"Thu\", visitors: 4_960, signups: 377 },\n    { label: \"Fri\", visitors: 6_310, signups: 489 },\n    { label: \"Sat\", visitors: 5_870, signups: 452 },\n    { label: \"Sun\", visitors: 5_120, signups: 398 },\n  ],\n  \"30d\": [\n    { label: \"Week 1\", visitors: 28_400, signups: 2_140 },\n    { label: \"Week 2\", visitors: 31_900, signups: 2_480 },\n    { label: \"Week 3\", visitors: 34_600, signups: 2_710 },\n    { label: \"Week 4\", visitors: 39_200, signups: 3_060 },\n  ],\n  \"90d\": [\n    { label: \"Jan\", visitors: 96_400, signups: 7_320 },\n    { label: \"Feb\", visitors: 104_800, signups: 8_010 },\n    { label: \"Mar\", visitors: 121_300, signups: 9_540 },\n  ],\n}\n\nconst ranges: { key: RangeKey; label: string }[] = [\n  { key: \"7d\", label: \"7 Days\" },\n  { key: \"30d\", label: \"30 Days\" },\n  { key: \"90d\", label: \"90 Days\" },\n]\n\nconst chartConfig = {\n  visitors: {\n    label: \"Visitors\",\n    color: \"var(--primary)\",\n  },\n  signups: {\n    label: \"Signups\",\n    color: \"var(--muted-foreground)\",\n  },\n} satisfies ChartConfig\nconst series = [\n  { key: \"visitors\", label: \"Visitors\" },\n  { key: \"signups\", label: \"Signups\" },\n] as const\n\nfunction formatTick(value: number | string): string {\n  const n = Number(value)\n  if (n >= 10_000) return `${Math.round(n / 1000)}k`\n  if (n >= 1000) return `${(n / 1000).toFixed(1)}k`\n  return String(n)\n}\n\nfunction summarize(data: DataPoint[], key: keyof Omit<DataPoint, \"label\">) {\n  const total = data.reduce((sum, point) => sum + point[key], 0)\n  const first = data[0]?.[key] ?? 0\n  const last = data[data.length - 1]?.[key] ?? 0\n  const deltaPct = first === 0 ? 0 : ((last - first) / first) * 100\n  return { total, deltaPct, up: deltaPct >= 0 }\n}\n\nexport default function Charts() {\n  const [range, setRange] = React.useState<RangeKey>(\"30d\")\n  const data = datasets[range]\n\n  return (\n    <section className=\"w-full\">\n      <Card className=\"w-full\">\n        <CardHeader className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n          <div className=\"space-y-1.5\">\n            <CardTitle>Traffic Overview</CardTitle>\n            <CardDescription>\n              Visitors and signups for Acme, last {range.replace(\"d\", \" days\")}\n            </CardDescription>\n          </div>\n          <Select\n            value={range}\n            onValueChange={(value) => setRange(value as RangeKey)}\n            items={ranges.map((r) => ({ value: r.key, label: r.label }))}\n          >\n            <SelectTrigger\n              className=\"w-28 shrink-0\"\n              aria-label=\"Select date range\"\n            >\n              <SelectValue />\n            </SelectTrigger>\n            <SelectContent>\n              {ranges.map((r) => (\n                <SelectItem key={r.key} value={r.key}>\n                  {r.label}\n                </SelectItem>\n              ))}\n            </SelectContent>\n          </Select>\n        </CardHeader>\n        <CardContent className=\"grid gap-8 md:grid-cols-2 md:gap-10\">\n          {series.map(({ key, label }) => {\n            const { total, deltaPct, up } = summarize(data, key)\n            return (\n              <div key={key} className=\"flex flex-col gap-4\">\n                <div className=\"flex items-baseline gap-2\">\n                  <span className=\"text-xs font-semibold tracking-widest text-muted-foreground uppercase\">\n                    {label}\n                  </span>\n                  <span className=\"ml-auto text-2xl font-semibold tabular-nums\">\n                    {total.toLocaleString(\"en-US\")}\n                  </span>\n                  <Badge\n                    variant={up ? \"secondary\" : \"destructive\"}\n                    className=\"gap-1\"\n                  >\n                    {up ? (\n                      <RiArrowUpLine\n                        data-icon=\"inline-start\"\n                        className=\"size-3\"\n                      />\n                    ) : (\n                      <RiArrowDownLine\n                        data-icon=\"inline-start\"\n                        className=\"size-3\"\n                      />\n                    )}\n                    {up ? \"+\" : \"\"}\n                    {deltaPct.toFixed(1)}%\n                  </Badge>\n                </div>\n\n                <ChartContainer config={chartConfig} className=\"h-56 w-full\">\n                  <AreaChart data={data} margin={{ left: 0, right: 8, top: 8 }}>\n                    <defs>\n                      <linearGradient\n                        id={`fill-${key}`}\n                        x1=\"0\"\n                        y1=\"0\"\n                        x2=\"0\"\n                        y2=\"1\"\n                      >\n                        <stop\n                          offset=\"5%\"\n                          stopColor={`var(--color-${key})`}\n                          stopOpacity={0.35}\n                        />\n                        <stop\n                          offset=\"95%\"\n                          stopColor={`var(--color-${key})`}\n                          stopOpacity={0.02}\n                        />\n                      </linearGradient>\n                    </defs>\n                    <CartesianGrid vertical={false} />\n                    <XAxis\n                      dataKey=\"label\"\n                      tickLine={false}\n                      axisLine={false}\n                      tickMargin={8}\n                    />\n                    <YAxis\n                      tickLine={false}\n                      axisLine={false}\n                      tickMargin={8}\n                      width={44}\n                      tickFormatter={formatTick}\n                    />\n                    <ChartTooltip\n                      cursor={false}\n                      content={\n                        <ChartTooltipContent\n                          formatter={(value, name) => (\n                            <span className=\"flex w-full justify-between gap-4\">\n                              <span className=\"text-muted-foreground capitalize\">\n                                {String(name)}\n                              </span>\n                              <span className=\"font-medium tabular-nums\">\n                                {Number(value).toLocaleString(\"en-US\")}\n                              </span>\n                            </span>\n                          )}\n                        />\n                      }\n                    />\n                    <Area\n                      dataKey={key}\n                      type=\"monotone\"\n                      stroke={`var(--color-${key})`}\n                      strokeWidth={2}\n                      fill={`url(#fill-${key})`}\n                    />\n                  </AreaChart>\n                </ChartContainer>\n              </div>\n            )\n          })}\n        </CardContent>\n      </Card>\n    </section>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/charts.tsx"
    },
    {
      "path": "registry/templates/dashboard/components/dashboard.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  RiArrowRightLine,\n  RiArrowRightUpLine,\n  RiArrowRightDownLine,\n} from \"@remixicon/react\"\nimport { Bar, BarChart, CartesianGrid, LabelList, XAxis } from \"recharts\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport {\n  Card,\n  CardAction,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport {\n  ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"@/components/ui/chart\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\n\ntype Range = \"7d\" | \"30d\" | \"90d\" | \"12m\"\n\nconst RANGES: { value: Range; label: string }[] = [\n  { value: \"7d\", label: \"Last 7 Days\" },\n  { value: \"30d\", label: \"Last 30 Days\" },\n  { value: \"90d\", label: \"Last 90 Days\" },\n  { value: \"12m\", label: \"Last 12 Months\" },\n]\n\nconst STATS_BY_RANGE: Record<\n  Range,\n  { label: string; value: string; delta: string; up: boolean }[]\n> = {\n  \"7d\": [\n    { label: \"Revenue\", value: \"$11.4k\", delta: \"+3.1%\", up: true },\n    { label: \"Active Users\", value: \"2,140\", delta: \"+1.2%\", up: true },\n    { label: \"Conversion\", value: \"3.31%\", delta: \"+0.2%\", up: true },\n    { label: \"Avg. Session\", value: \"4m 02s\", delta: \"-0.4%\", up: false },\n  ],\n  \"30d\": [\n    { label: \"Revenue\", value: \"$48.2k\", delta: \"+12.5%\", up: true },\n    { label: \"Active Users\", value: \"8,941\", delta: \"+4.3%\", up: true },\n    { label: \"Conversion\", value: \"3.24%\", delta: \"-0.8%\", up: false },\n    { label: \"Avg. Session\", value: \"4m 12s\", delta: \"+1.1%\", up: true },\n  ],\n  \"90d\": [\n    { label: \"Revenue\", value: \"$142.7k\", delta: \"+9.8%\", up: true },\n    { label: \"Active Users\", value: \"26,304\", delta: \"+7.6%\", up: true },\n    { label: \"Conversion\", value: \"3.18%\", delta: \"-1.4%\", up: false },\n    { label: \"Avg. Session\", value: \"4m 26s\", delta: \"+2.3%\", up: true },\n  ],\n  \"12m\": [\n    { label: \"Revenue\", value: \"$571.0k\", delta: \"+22.1%\", up: true },\n    { label: \"Active Users\", value: \"98,612\", delta: \"+18.4%\", up: true },\n    { label: \"Conversion\", value: \"3.02%\", delta: \"-2.9%\", up: false },\n    { label: \"Avg. Session\", value: \"4m 41s\", delta: \"+5.0%\", up: true },\n  ],\n}\n\nconst chartData = [\n  { month: \"Jan\", sessions: 1840 },\n  { month: \"Feb\", sessions: 2120 },\n  { month: \"Mar\", sessions: 1760 },\n  { month: \"Apr\", sessions: 2580 },\n  { month: \"May\", sessions: 2340 },\n  { month: \"Jun\", sessions: 3120 },\n  { month: \"Jul\", sessions: 2210 },\n  { month: \"Aug\", sessions: 2860 },\n  { month: \"Sep\", sessions: 2480 },\n  { month: \"Oct\", sessions: 3340 },\n  { month: \"Nov\", sessions: 2720 },\n  { month: \"Dec\", sessions: 3010 },\n]\n\nconst chartConfig = {\n  sessions: { label: \"Sessions\", color: \"var(--primary)\" },\n} satisfies ChartConfig\n\nexport default function Dashboard() {\n  const [range, setRange] = React.useState<Range>(\"30d\")\n  const stats = STATS_BY_RANGE[range]\n\n  return (\n    <section className=\"w-full\">\n      <div className=\"w-full\">\n        <div className=\"flex items-center justify-between gap-4\">\n          <h1 className=\"text-2xl font-bold tracking-tight\">Overview</h1>\n          <Select\n            items={RANGES}\n            value={range}\n            onValueChange={(value) => setRange(value as Range)}\n          >\n            <SelectTrigger aria-label=\"Date range\">\n              <SelectValue />\n            </SelectTrigger>\n            <SelectContent>\n              {RANGES.map((r) => (\n                <SelectItem key={r.value} value={r.value}>\n                  {r.label}\n                </SelectItem>\n              ))}\n            </SelectContent>\n          </Select>\n        </div>\n\n        <div className=\"mt-6 grid grid-cols-2 gap-4 md:grid-cols-4\">\n          {stats.map((stat) => (\n            <Card key={stat.label}>\n              <CardHeader>\n                <CardDescription>{stat.label}</CardDescription>\n                <CardTitle className=\"text-3xl font-bold tracking-tight\">\n                  {stat.value}\n                </CardTitle>\n              </CardHeader>\n              <CardContent className=\"flex items-center gap-2\">\n                <Badge\n                  variant={stat.up ? \"secondary\" : \"outline\"}\n                  className={\n                    stat.up\n                      ? undefined\n                      : \"border-destructive/40 text-destructive\"\n                  }\n                >\n                  {stat.up ? (\n                    <RiArrowRightUpLine\n                      data-icon=\"inline-start\"\n                      aria-hidden=\"true\"\n                    />\n                  ) : (\n                    <RiArrowRightDownLine\n                      data-icon=\"inline-start\"\n                      aria-hidden=\"true\"\n                    />\n                  )}\n                  {stat.delta}\n                </Badge>\n                <span className=\"hidden text-xs text-muted-foreground sm:inline\">\n                  Vs Last Period\n                </span>\n              </CardContent>\n            </Card>\n          ))}\n        </div>\n\n        <Card className=\"mt-4\">\n          <CardHeader>\n            <CardTitle>Monthly traffic</CardTitle>\n            <CardDescription>Sessions over the last 12 months</CardDescription>\n            <CardAction>\n              <a\n                href=\"#\"\n                className=\"flex items-center gap-1 text-sm font-medium text-muted-foreground\"\n              >\n                View Report\n                <RiArrowRightLine className=\"size-4\" aria-hidden=\"true\" />\n              </a>\n            </CardAction>\n          </CardHeader>\n          <CardContent>\n            <ChartContainer config={chartConfig} className=\"h-48 w-full\">\n              <BarChart\n                accessibilityLayer\n                data={chartData}\n                margin={{ top: 24 }}\n              >\n                <CartesianGrid vertical={false} />\n                <XAxis\n                  dataKey=\"month\"\n                  tickLine={false}\n                  axisLine={false}\n                  tickMargin={8}\n                />\n                <ChartTooltip\n                  cursor={false}\n                  content={<ChartTooltipContent />}\n                />\n                <Bar\n                  dataKey=\"sessions\"\n                  fill=\"var(--color-sessions)\"\n                  maxBarSize={48}\n                >\n                  <LabelList\n                    dataKey=\"sessions\"\n                    position=\"top\"\n                    offset={8}\n                    className=\"fill-muted-foreground\"\n                    fontSize={11}\n                    formatter={(value) =>\n                      (Number(value) / 1000).toFixed(1) + \"k\"\n                    }\n                  />\n                </Bar>\n              </BarChart>\n            </ChartContainer>\n          </CardContent>\n        </Card>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/dashboard.tsx"
    },
    {
      "path": "registry/templates/dashboard/components/table.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  type ColumnDef,\n  type ColumnFiltersState,\n  type SortingState,\n  type VisibilityState,\n  flexRender,\n  getCoreRowModel,\n  getFilteredRowModel,\n  getPaginationRowModel,\n  getSortedRowModel,\n  useReactTable,\n} from \"@tanstack/react-table\"\nimport {\n  RiAddLine,\n  RiArrowDownLine,\n  RiArrowLeftSLine,\n  RiArrowRightSLine,\n  RiArrowUpLine,\n  RiDeleteBinLine,\n  RiDownloadLine,\n  RiExpandUpDownLine,\n  RiLayoutColumnLine,\n  RiMoreLine,\n  RiPencilLine,\n  RiSearchLine,\n  RiShieldUserLine,\n  RiUserLine,\n  RiUserSettingsLine,\n} from \"@remixicon/react\"\nimport { toast } from \"sonner\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\"\n\ntype Status = \"Active\" | \"Invited\" | \"Inactive\"\ntype Role = \"Admin\" | \"Editor\" | \"Viewer\"\n\ntype Member = {\n  id: string\n  name: string\n  initials: string\n  avatar: string\n  email: string\n  status: Status\n  role: Role\n  joined: string\n}\n\nconst statusVariant: Record<Status, \"default\" | \"secondary\" | \"outline\"> = {\n  Active: \"default\",\n  Invited: \"secondary\",\n  Inactive: \"outline\",\n}\n\nconst roleClass: Record<Role, string> = {\n  Admin: \"text-foreground font-medium\",\n  Editor: \"text-muted-foreground\",\n  Viewer: \"text-muted-foreground\",\n}\n\nconst COLUMN_LABELS: Record<string, string> = {\n  name: \"Member\",\n  status: \"Status\",\n  role: \"Role\",\n  joined: \"Joined\",\n}\n\nconst members: Member[] = [\n  {\n    id: \"m-01\",\n    name: \"Ada Lovelace\",\n    initials: \"AL\",\n    avatar: \"https://i.pravatar.cc/80?img=47\",\n    email: \"ada@acme.io\",\n    status: \"Active\",\n    role: \"Admin\",\n    joined: \"2026-06-12\",\n  },\n  {\n    id: \"m-02\",\n    name: \"Alan Turing\",\n    initials: \"AT\",\n    avatar: \"https://i.pravatar.cc/80?img=11\",\n    email: \"alan@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-06-10\",\n  },\n  {\n    id: \"m-03\",\n    name: \"Grace Hopper\",\n    initials: \"GH\",\n    avatar: \"https://i.pravatar.cc/80?img=45\",\n    email: \"grace@acme.io\",\n    status: \"Invited\",\n    role: \"Editor\",\n    joined: \"2026-06-08\",\n  },\n  {\n    id: \"m-04\",\n    name: \"Linus Pauling\",\n    initials: \"LP\",\n    avatar: \"https://i.pravatar.cc/80?img=12\",\n    email: \"linus@acme.io\",\n    status: \"Inactive\",\n    role: \"Viewer\",\n    joined: \"2026-05-29\",\n  },\n  {\n    id: \"m-05\",\n    name: \"Katherine Johnson\",\n    initials: \"KJ\",\n    avatar: \"https://i.pravatar.cc/80?img=49\",\n    email: \"katherine@acme.io\",\n    status: \"Active\",\n    role: \"Viewer\",\n    joined: \"2026-05-21\",\n  },\n  {\n    id: \"m-06\",\n    name: \"Edsger Dijkstra\",\n    initials: \"ED\",\n    avatar: \"https://i.pravatar.cc/80?img=13\",\n    email: \"edsger@acme.io\",\n    status: \"Active\",\n    role: \"Admin\",\n    joined: \"2026-05-18\",\n  },\n  {\n    id: \"m-07\",\n    name: \"Barbara Liskov\",\n    initials: \"BL\",\n    avatar: \"https://i.pravatar.cc/80?img=44\",\n    email: \"barbara@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-05-14\",\n  },\n  {\n    id: \"m-08\",\n    name: \"Tim Berners-Lee\",\n    initials: \"TB\",\n    avatar: \"https://i.pravatar.cc/80?img=14\",\n    email: \"tim@acme.io\",\n    status: \"Invited\",\n    role: \"Viewer\",\n    joined: \"2026-05-09\",\n  },\n  {\n    id: \"m-09\",\n    name: \"Margaret Hamilton\",\n    initials: \"MH\",\n    avatar: \"https://i.pravatar.cc/80?img=48\",\n    email: \"margaret@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-05-04\",\n  },\n  {\n    id: \"m-10\",\n    name: \"Donald Knuth\",\n    initials: \"DK\",\n    avatar: \"https://i.pravatar.cc/80?img=15\",\n    email: \"donald@acme.io\",\n    status: \"Inactive\",\n    role: \"Viewer\",\n    joined: \"2026-04-28\",\n  },\n  {\n    id: \"m-11\",\n    name: \"Radia Perlman\",\n    initials: \"RP\",\n    avatar: \"https://i.pravatar.cc/80?img=43\",\n    email: \"radia@acme.io\",\n    status: \"Active\",\n    role: \"Admin\",\n    joined: \"2026-04-22\",\n  },\n  {\n    id: \"m-12\",\n    name: \"Ken Thompson\",\n    initials: \"KT\",\n    avatar: \"https://i.pravatar.cc/80?img=16\",\n    email: \"ken@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-04-19\",\n  },\n  {\n    id: \"m-13\",\n    name: \"Hedy Lamarr\",\n    initials: \"HL\",\n    avatar: \"https://i.pravatar.cc/80?img=41\",\n    email: \"hedy@acme.io\",\n    status: \"Invited\",\n    role: \"Viewer\",\n    joined: \"2026-04-15\",\n  },\n  {\n    id: \"m-14\",\n    name: \"Dennis Ritchie\",\n    initials: \"DR\",\n    avatar: \"https://i.pravatar.cc/80?img=17\",\n    email: \"dennis@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-04-11\",\n  },\n  {\n    id: \"m-15\",\n    name: \"Shafi Goldwasser\",\n    initials: \"SG\",\n    avatar: \"https://i.pravatar.cc/80?img=40\",\n    email: \"shafi@acme.io\",\n    status: \"Active\",\n    role: \"Viewer\",\n    joined: \"2026-04-07\",\n  },\n  {\n    id: \"m-16\",\n    name: \"John McCarthy\",\n    initials: \"JM\",\n    avatar: \"https://i.pravatar.cc/80?img=18\",\n    email: \"john@acme.io\",\n    status: \"Inactive\",\n    role: \"Viewer\",\n    joined: \"2026-04-02\",\n  },\n  {\n    id: \"m-17\",\n    name: \"Frances Allen\",\n    initials: \"FA\",\n    avatar: \"https://i.pravatar.cc/80?img=39\",\n    email: \"frances@acme.io\",\n    status: \"Active\",\n    role: \"Admin\",\n    joined: \"2026-03-29\",\n  },\n  {\n    id: \"m-18\",\n    name: \"Vint Cerf\",\n    initials: \"VC\",\n    avatar: \"https://i.pravatar.cc/80?img=19\",\n    email: \"vint@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-03-24\",\n  },\n  {\n    id: \"m-19\",\n    name: \"Adele Goldberg\",\n    initials: \"AG\",\n    avatar: \"https://i.pravatar.cc/80?img=38\",\n    email: \"adele@acme.io\",\n    status: \"Invited\",\n    role: \"Viewer\",\n    joined: \"2026-03-20\",\n  },\n  {\n    id: \"m-20\",\n    name: \"Bjarne Stroustrup\",\n    initials: \"BS\",\n    avatar: \"https://i.pravatar.cc/80?img=20\",\n    email: \"bjarne@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-03-16\",\n  },\n  {\n    id: \"m-21\",\n    name: \"Karen Spärck Jones\",\n    initials: \"KS\",\n    avatar: \"https://i.pravatar.cc/80?img=36\",\n    email: \"karen@acme.io\",\n    status: \"Active\",\n    role: \"Viewer\",\n    joined: \"2026-03-11\",\n  },\n  {\n    id: \"m-22\",\n    name: \"Brian Kernighan\",\n    initials: \"BK\",\n    avatar: \"https://i.pravatar.cc/80?img=51\",\n    email: \"brian@acme.io\",\n    status: \"Inactive\",\n    role: \"Viewer\",\n    joined: \"2026-03-06\",\n  },\n  {\n    id: \"m-23\",\n    name: \"Sophie Wilson\",\n    initials: \"SW\",\n    avatar: \"https://i.pravatar.cc/80?img=35\",\n    email: \"sophie@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-03-01\",\n  },\n  {\n    id: \"m-24\",\n    name: \"Guido van Rossum\",\n    initials: \"GR\",\n    avatar: \"https://i.pravatar.cc/80?img=52\",\n    email: \"guido@acme.io\",\n    status: \"Active\",\n    role: \"Admin\",\n    joined: \"2026-02-24\",\n  },\n  {\n    id: \"m-25\",\n    name: \"Lynn Conway\",\n    initials: \"LC\",\n    avatar: \"https://i.pravatar.cc/80?img=34\",\n    email: \"lynn@acme.io\",\n    status: \"Invited\",\n    role: \"Viewer\",\n    joined: \"2026-02-19\",\n  },\n  {\n    id: \"m-26\",\n    name: \"Ralph Merkle\",\n    initials: \"RM\",\n    avatar: \"https://i.pravatar.cc/80?img=53\",\n    email: \"ralph@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-02-13\",\n  },\n  {\n    id: \"m-27\",\n    name: \"Carol Shaw\",\n    initials: \"CS\",\n    avatar: \"https://i.pravatar.cc/80?img=32\",\n    email: \"carol@acme.io\",\n    status: \"Active\",\n    role: \"Viewer\",\n    joined: \"2026-02-08\",\n  },\n  {\n    id: \"m-28\",\n    name: \"Niklaus Wirth\",\n    initials: \"NW\",\n    avatar: \"https://i.pravatar.cc/80?img=54\",\n    email: \"niklaus@acme.io\",\n    status: \"Inactive\",\n    role: \"Viewer\",\n    joined: \"2026-02-02\",\n  },\n  {\n    id: \"m-29\",\n    name: \"Mary Allen Wilkes\",\n    initials: \"MW\",\n    avatar: \"https://i.pravatar.cc/80?img=31\",\n    email: \"mary@acme.io\",\n    status: \"Active\",\n    role: \"Admin\",\n    joined: \"2026-01-27\",\n  },\n  {\n    id: \"m-30\",\n    name: \"Leslie Lamport\",\n    initials: \"LL\",\n    avatar: \"https://i.pravatar.cc/80?img=55\",\n    email: \"leslie@acme.io\",\n    status: \"Active\",\n    role: \"Editor\",\n    joined: \"2026-01-21\",\n  },\n]\n\nconst dateFmt = new Intl.DateTimeFormat(\"en-US\", {\n  month: \"short\",\n  day: \"2-digit\",\n  year: \"numeric\",\n})\n\nfunction formatDate(value: string) {\n  const parsed = new Date(value)\n  return Number.isNaN(parsed.getTime()) ? value : dateFmt.format(parsed)\n}\n\nfunction SortIcon({ sorted }: { sorted: false | \"asc\" | \"desc\" }) {\n  if (sorted === \"asc\")\n    return <RiArrowUpLine className=\"size-3.5\" aria-hidden=\"true\" />\n  if (sorted === \"desc\")\n    return <RiArrowDownLine className=\"size-3.5\" aria-hidden=\"true\" />\n  return (\n    <RiExpandUpDownLine\n      className=\"size-3.5 text-muted-foreground/60\"\n      aria-hidden=\"true\"\n    />\n  )\n}\n\nconst columns: ColumnDef<Member>[] = [\n  {\n    id: \"select\",\n    enableSorting: false,\n    enableHiding: false,\n    header: ({ table }) => (\n      <Checkbox\n        checked={table.getIsAllPageRowsSelected()}\n        indeterminate={\n          table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected()\n        }\n        onCheckedChange={(checked) =>\n          table.toggleAllPageRowsSelected(checked === true)\n        }\n        aria-label=\"Select all members on this page\"\n      />\n    ),\n    cell: ({ row }) => (\n      <Checkbox\n        checked={row.getIsSelected()}\n        onCheckedChange={(checked) => row.toggleSelected(checked === true)}\n        aria-label={`Select ${row.original.name}`}\n      />\n    ),\n  },\n  {\n    accessorKey: \"name\",\n    header: ({ column }) => (\n      <button\n        type=\"button\"\n        onClick={() => column.toggleSorting(column.getIsSorted() === \"asc\")}\n        className=\"-mx-1 inline-flex items-center gap-1 rounded-none px-1 text-xs font-semibold tracking-widest text-muted-foreground uppercase transition-colors hover:text-foreground\"\n      >\n        Member\n        <SortIcon sorted={column.getIsSorted()} />\n      </button>\n    ),\n    filterFn: (row, _id, value: string) => {\n      const q = value.toLowerCase()\n      return (\n        row.original.name.toLowerCase().includes(q) ||\n        row.original.email.toLowerCase().includes(q)\n      )\n    },\n    cell: ({ row }) => {\n      const member = row.original\n      return (\n        <div className=\"flex min-w-0 items-center gap-3\">\n          <Avatar size=\"sm\" className=\"shrink-0 border border-border\">\n            <AvatarImage\n              src={member.avatar}\n              alt={member.name}\n              className=\"grayscale\"\n            />\n            <AvatarFallback className=\"text-xs\">\n              {member.initials}\n            </AvatarFallback>\n          </Avatar>\n          <div className=\"min-w-0\">\n            <p className=\"truncate text-sm leading-tight font-medium\">\n              {member.name}\n            </p>\n            <p className=\"truncate text-xs text-muted-foreground\">\n              {member.email}\n            </p>\n          </div>\n        </div>\n      )\n    },\n  },\n  {\n    accessorKey: \"status\",\n    enableSorting: false,\n    header: () => (\n      <span className=\"text-xs font-semibold tracking-widest text-muted-foreground uppercase\">\n        Status\n      </span>\n    ),\n    cell: ({ row }) => (\n      <Badge variant={statusVariant[row.original.status]} className=\"text-xs\">\n        {row.original.status}\n      </Badge>\n    ),\n  },\n  {\n    accessorKey: \"role\",\n    header: ({ column }) => (\n      <button\n        type=\"button\"\n        onClick={() => column.toggleSorting(column.getIsSorted() === \"asc\")}\n        className=\"-mx-1 inline-flex items-center gap-1 rounded-none px-1 text-xs font-semibold tracking-widest text-muted-foreground uppercase transition-colors hover:text-foreground\"\n      >\n        Role\n        <SortIcon sorted={column.getIsSorted()} />\n      </button>\n    ),\n    cell: ({ row }) => (\n      <span className={cn(\"text-sm\", roleClass[row.original.role])}>\n        {row.original.role}\n      </span>\n    ),\n  },\n  {\n    accessorKey: \"joined\",\n    sortingFn: \"datetime\",\n    header: ({ column }) => (\n      <button\n        type=\"button\"\n        onClick={() => column.toggleSorting(column.getIsSorted() === \"asc\")}\n        className=\"-mx-1 ml-auto inline-flex items-center gap-1 rounded-none px-1 text-xs font-semibold tracking-widest text-muted-foreground uppercase transition-colors hover:text-foreground\"\n      >\n        Joined\n        <SortIcon sorted={column.getIsSorted()} />\n      </button>\n    ),\n    cell: ({ row }) => (\n      <span className=\"block text-right text-xs text-muted-foreground tabular-nums\">\n        {formatDate(row.original.joined)}\n      </span>\n    ),\n  },\n  {\n    id: \"actions\",\n    enableSorting: false,\n    enableHiding: false,\n    header: () => <span className=\"sr-only\">Actions</span>,\n    cell: ({ row }) => (\n      <div className=\"flex justify-end\">\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            render={\n              <Button\n                variant=\"ghost\"\n                size=\"icon-sm\"\n                aria-label={`Actions for ${row.original.name}`}\n              >\n                <RiMoreLine className=\"size-4\" aria-hidden=\"true\" />\n              </Button>\n            }\n          />\n          <DropdownMenuContent align=\"end\" className=\"w-40\">\n            <DropdownMenuItem>\n              <RiUserLine aria-hidden=\"true\" />\n              View Profile\n            </DropdownMenuItem>\n            <DropdownMenuItem>\n              <RiPencilLine aria-hidden=\"true\" />\n              Edit\n            </DropdownMenuItem>\n            <DropdownMenuSeparator />\n            <DropdownMenuItem variant=\"destructive\">\n              <RiDeleteBinLine aria-hidden=\"true\" />\n              Remove\n            </DropdownMenuItem>\n          </DropdownMenuContent>\n        </DropdownMenu>\n      </div>\n    ),\n  },\n]\n\nexport default function DataTable() {\n  const [sorting, setSorting] = React.useState<SortingState>([\n    { id: \"joined\", desc: true },\n  ])\n  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(\n    []\n  )\n  const [columnVisibility, setColumnVisibility] =\n    React.useState<VisibilityState>({})\n  const [rowSelection, setRowSelection] = React.useState({})\n  const [data, setData] = React.useState(members)\n\n  const table = useReactTable({\n    data,\n    columns,\n    getRowId: (row) => row.id,\n    state: { sorting, columnFilters, columnVisibility, rowSelection },\n    onSortingChange: setSorting,\n    onColumnFiltersChange: setColumnFilters,\n    onColumnVisibilityChange: setColumnVisibility,\n    onRowSelectionChange: setRowSelection,\n    getCoreRowModel: getCoreRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    getFilteredRowModel: getFilteredRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    initialState: { pagination: { pageSize: 6 } },\n  })\n\n  const nameFilter = (table.getColumn(\"name\")?.getFilterValue() as string) ?? \"\"\n  const selectedCount = table.getFilteredSelectedRowModel().rows.length\n  const totalCount = table.getFilteredRowModel().rows.length\n  const pageCount = table.getPageCount()\n\n  function handleRemove() {\n    const selectedIds = new Set(\n      table.getFilteredSelectedRowModel().rows.map((row) => row.id)\n    )\n    setData((prev) => prev.filter((row) => !selectedIds.has(row.id)))\n    table.resetRowSelection()\n    toast(\"Members removed\", {\n      description: `${selectedIds.size} ${\n        selectedIds.size === 1 ? \"member\" : \"members\"\n      } removed from the workspace.`,\n    })\n  }\n\n  return (\n    <section className=\"w-full\">\n      <div className=\"w-full border border-border bg-card\">\n        <div className=\"flex flex-col gap-4 border-b border-border px-6 py-5 sm:flex-row sm:items-center sm:justify-between\">\n          <div className=\"flex items-center gap-3\">\n            <div className=\"flex size-9 items-center justify-center border border-border bg-card text-muted-foreground\">\n              <RiShieldUserLine className=\"size-4\" aria-hidden=\"true\" />\n            </div>\n            <div>\n              <h2 className=\"text-lg leading-tight font-semibold tracking-tight\">\n                Team Members\n              </h2>\n              <p className=\"text-sm text-muted-foreground\">\n                {data.length} members across 3 workspaces\n              </p>\n            </div>\n          </div>\n          <div className=\"flex w-full flex-wrap items-center gap-2 sm:w-auto\">\n            <div className=\"relative flex-1 sm:flex-none\">\n              <RiSearchLine\n                className=\"pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-foreground\"\n                aria-hidden=\"true\"\n              />\n              <Input\n                type=\"search\"\n                value={nameFilter}\n                onChange={(event) =>\n                  table.getColumn(\"name\")?.setFilterValue(event.target.value)\n                }\n                placeholder=\"Search members...\"\n                className=\"h-7 w-full pl-8 text-sm sm:w-48\"\n                aria-label=\"Search members by name or email\"\n              />\n            </div>\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <Button\n                    variant=\"outline\"\n                    size=\"sm\"\n                    aria-label=\"Toggle columns\"\n                  >\n                    <RiLayoutColumnLine\n                      className=\"size-3.5\"\n                      aria-hidden=\"true\"\n                    />\n                    View\n                  </Button>\n                }\n              />\n              <DropdownMenuContent align=\"end\" className=\"w-40\">\n                <DropdownMenuGroup>\n                  <DropdownMenuLabel>Toggle Columns</DropdownMenuLabel>\n                  <DropdownMenuSeparator />\n                  {table\n                    .getAllColumns()\n                    .filter((column) => column.getCanHide())\n                    .map((column) => (\n                      <DropdownMenuCheckboxItem\n                        key={column.id}\n                        checked={column.getIsVisible()}\n                        onCheckedChange={(checked) =>\n                          column.toggleVisibility(checked === true)\n                        }\n                        closeOnClick={false}\n                      >\n                        {COLUMN_LABELS[column.id] ?? column.id}\n                      </DropdownMenuCheckboxItem>\n                    ))}\n                </DropdownMenuGroup>\n              </DropdownMenuContent>\n            </DropdownMenu>\n            <Button\n              size=\"sm\"\n              onClick={() =>\n                toast(\"Invitation sent\", {\n                  description: \"They will appear here once they accept.\",\n                })\n              }\n            >\n              <RiAddLine className=\"mr-1 size-3.5\" aria-hidden=\"true\" />\n              Invite\n            </Button>\n          </div>\n        </div>\n\n        {selectedCount > 0 && (\n          <div className=\"flex flex-wrap items-center justify-between gap-3 border-b border-border bg-muted/40 px-6 py-2.5\">\n            <div className=\"flex items-center gap-2\">\n              <span className=\"text-sm font-medium text-foreground tabular-nums\">\n                {selectedCount} Selected\n              </span>\n              <Button\n                variant=\"ghost\"\n                size=\"xs\"\n                className=\"text-muted-foreground hover:text-foreground\"\n                onClick={() => table.resetRowSelection()}\n              >\n                Clear\n              </Button>\n            </div>\n            <div className=\"flex flex-wrap items-center gap-2\">\n              <Button\n                variant=\"outline\"\n                size=\"sm\"\n                onClick={() =>\n                  toast(\"Export started\", {\n                    description: `Exporting ${selectedCount} members to CSV.`,\n                  })\n                }\n              >\n                <RiDownloadLine className=\"size-3.5\" aria-hidden=\"true\" />\n                Export\n              </Button>\n              <Button\n                variant=\"outline\"\n                size=\"sm\"\n                onClick={() =>\n                  toast(\"Role updated\", {\n                    description: `Changed the role for ${selectedCount} members.`,\n                  })\n                }\n              >\n                <RiUserSettingsLine className=\"size-3.5\" aria-hidden=\"true\" />\n                Change role\n              </Button>\n              <Button\n                variant=\"outline\"\n                size=\"sm\"\n                className=\"text-destructive hover:text-destructive\"\n                onClick={handleRemove}\n              >\n                <RiDeleteBinLine className=\"size-3.5\" aria-hidden=\"true\" />\n                Remove\n              </Button>\n            </div>\n          </div>\n        )}\n\n        <div>\n          <Table>\n            <TableHeader>\n              {table.getHeaderGroups().map((headerGroup) => (\n                <TableRow\n                  key={headerGroup.id}\n                  className=\"border-b border-border bg-muted/40 hover:bg-muted/40\"\n                >\n                  {headerGroup.headers.map((header) => (\n                    <TableHead\n                      key={header.id}\n                      className={cn(\n                        \"h-9\",\n                        header.column.id === \"select\" && \"w-10 pl-6\",\n                        header.column.id === \"name\" && \"pl-3\",\n                        header.column.id === \"joined\" && \"text-right\",\n                        header.column.id === \"actions\" && \"w-10 pr-6\"\n                      )}\n                    >\n                      {header.isPlaceholder\n                        ? null\n                        : flexRender(\n                            header.column.columnDef.header,\n                            header.getContext()\n                          )}\n                    </TableHead>\n                  ))}\n                </TableRow>\n              ))}\n            </TableHeader>\n            <TableBody>\n              {table.getRowModel().rows.length ? (\n                table.getRowModel().rows.map((row) => (\n                  <TableRow\n                    key={row.id}\n                    data-state={row.getIsSelected() ? \"selected\" : undefined}\n                    className=\"border-b border-border transition-colors duration-100 last:border-b-0 hover:bg-muted/30\"\n                  >\n                    {row.getVisibleCells().map((cell) => (\n                      <TableCell\n                        key={cell.id}\n                        className={cn(\n                          \"py-3\",\n                          cell.column.id === \"select\" && \"pl-6\",\n                          cell.column.id === \"name\" && \"pl-3\",\n                          cell.column.id === \"actions\" && \"pr-6\"\n                        )}\n                      >\n                        {flexRender(\n                          cell.column.columnDef.cell,\n                          cell.getContext()\n                        )}\n                      </TableCell>\n                    ))}\n                  </TableRow>\n                ))\n              ) : (\n                <TableRow className=\"hover:bg-transparent\">\n                  <TableCell\n                    colSpan={columns.length}\n                    className=\"h-24 text-center text-sm text-muted-foreground\"\n                  >\n                    No members match your search.\n                  </TableCell>\n                </TableRow>\n              )}\n            </TableBody>\n          </Table>\n\n          <div className=\"flex items-center justify-between gap-4 border-t border-border bg-muted/20 px-6 py-3\">\n            <p className=\"text-xs text-muted-foreground\">\n              <span className=\"font-medium text-foreground\">{totalCount}</span>{\" \"}\n              {totalCount === 1 ? \"Result\" : \"Results\"}\n            </p>\n            <div className=\"flex items-center gap-1.5\">\n              <Button\n                variant=\"outline\"\n                size=\"icon\"\n                className=\"size-7\"\n                onClick={() => table.previousPage()}\n                disabled={!table.getCanPreviousPage()}\n                aria-label=\"Previous page\"\n              >\n                <RiArrowLeftSLine className=\"size-3.5\" aria-hidden=\"true\" />\n              </Button>\n              <span className=\"px-1 text-xs text-muted-foreground tabular-nums\">\n                Page {table.getState().pagination.pageIndex + 1} of{\" \"}\n                {Math.max(pageCount, 1)}\n              </span>\n              <Button\n                variant=\"outline\"\n                size=\"icon\"\n                className=\"size-7\"\n                onClick={() => table.nextPage()}\n                disabled={!table.getCanNextPage()}\n                aria-label=\"Next page\"\n              >\n                <RiArrowRightSLine className=\"size-3.5\" aria-hidden=\"true\" />\n              </Button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/table.tsx"
    }
  ],
  "meta": {
    "kind": "template",
    "tier": "free"
  },
  "type": "registry:block"
}