{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "app-shell-3",
  "description": "App shell with a collapsible icon-rail sidebar, search top bar, avatar, and dashboard placeholder cards",
  "dependencies": [
    "@remixicon/react",
    "recharts",
    "@base-ui/react"
  ],
  "registryDependencies": [
    "avatar",
    "badge",
    "button",
    "card",
    "chart",
    "command",
    "dropdown-menu",
    "kbd",
    "sheet",
    "sidebar",
    "sonner"
  ],
  "files": [
    {
      "path": "registry/blocks/app-shell/3/app-shell-block.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  RiBarChartBoxLine,\n  RiCheckboxCircleLine,\n  RiErrorWarningLine,\n  RiFolder3Line,\n  RiGitMergeLine,\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\"\nimport { Area, AreaChart, CartesianGrid, XAxis } from \"recharts\"\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport {\n  type ChartConfig,\n  ChartContainer,\n  ChartTooltip,\n  ChartTooltipContent,\n} from \"@/components/ui/chart\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Card,\n  CardAction,\n  CardContent,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\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\"\nimport { Toaster } from \"@/components/ui/sonner\"\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 stats = [\n  { label: \"Total revenue\", value: \"$48.2k\", delta: \"+12.4%\" },\n  { label: \"Active users\", value: \"2,840\", delta: \"+5.1%\" },\n  { label: \"Conversion\", value: \"3.6%\", delta: \"-0.8%\" },\n]\n\nconst performanceData = [\n  { month: \"Jan\", revenue: 31_200 },\n  { month: \"Feb\", revenue: 34_800 },\n  { month: \"Mar\", revenue: 33_100 },\n  { month: \"Apr\", revenue: 38_600 },\n  { month: \"May\", revenue: 42_900 },\n  { month: \"Jun\", revenue: 41_300 },\n  { month: \"Jul\", revenue: 45_700 },\n  { month: \"Aug\", revenue: 48_200 },\n]\n\nconst chartConfig = {\n  revenue: {\n    label: \"Revenue\",\n    color: \"var(--primary)\",\n  },\n} satisfies ChartConfig\n\nconst activity = [\n  {\n    name: \"Priya Nair\",\n    avatar: \"https://i.pravatar.cc/150?img=45\",\n    icon: RiRocketLine,\n    action: \"deployed\",\n    target: \"acme-web v2.4\",\n    time: \"2 Min Ago\",\n  },\n  {\n    name: \"Marco Diaz\",\n    avatar: \"https://i.pravatar.cc/150?img=33\",\n    icon: RiGitMergeLine,\n    action: \"merged\",\n    target: \"PR #482: checkout refactor\",\n    time: \"1 Hour Ago\",\n  },\n  {\n    name: \"Lena Fischer\",\n    avatar: \"https://i.pravatar.cc/150?img=47\",\n    icon: RiCheckboxCircleLine,\n    action: \"closed\",\n    target: \"8 tasks in Sprint 7\",\n    time: \"3 Hours Ago\",\n  },\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: [\"Ctrl\", \",\"] },\n]\n\nexport default function AppShellBlock({\n  children,\n}: {\n  children?: React.ReactNode\n}) {\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      <Toaster />\n\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>Ctrl</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\">\n          {children ?? (\n            <div className=\"mx-auto w-full max-w-5xl space-y-6\">\n              <div className=\"flex flex-wrap items-end justify-between gap-3\">\n                <div>\n                  <h1 className=\"text-2xl font-semibold tracking-tight\">\n                    {activeNav}\n                  </h1>\n                  <p className=\"text-sm text-muted-foreground\">\n                    Here is what is happening across Acme today.\n                  </p>\n                </div>\n                <Badge variant=\"secondary\">\n                  <span className=\"size-2 rounded-full bg-primary\" />\n                  Live\n                </Badge>\n              </div>\n\n              <div className=\"grid gap-4 sm:grid-cols-2 md:grid-cols-3\">\n                {stats.map((stat) => (\n                  <Card key={stat.label}>\n                    <CardHeader>\n                      <CardTitle className=\"text-sm font-medium text-muted-foreground\">\n                        {stat.label}\n                      </CardTitle>\n                      <CardAction>\n                        <Badge\n                          variant={\n                            stat.delta.startsWith(\"-\")\n                              ? \"destructive\"\n                              : \"secondary\"\n                          }\n                        >\n                          {stat.delta}\n                        </Badge>\n                      </CardAction>\n                    </CardHeader>\n                    <CardContent>\n                      <span className=\"text-3xl font-semibold tracking-tight\">\n                        {stat.value}\n                      </span>\n                    </CardContent>\n                  </Card>\n                ))}\n              </div>\n\n              <div className=\"grid gap-4 md:grid-cols-3\">\n                <Card className=\"md:col-span-2\">\n                  <CardHeader>\n                    <CardTitle>Performance</CardTitle>\n                    <CardAction>\n                      <Badge variant=\"secondary\" className=\"tabular-nums\">\n                        +18.2% MoM\n                      </Badge>\n                    </CardAction>\n                  </CardHeader>\n                  <CardContent>\n                    <ChartContainer\n                      config={chartConfig}\n                      className=\"h-56 w-full [&_.recharts-surface:focus-visible]:outline-2 [&_.recharts-surface:focus-visible]:outline-offset-2 [&_.recharts-surface:focus-visible]:outline-ring [&_.recharts-surface:focus-visible]:outline-solid\"\n                    >\n                      <AreaChart\n                        aria-label=\"Revenue by month\"\n                        data={performanceData}\n                        margin={{ left: 0, right: 0 }}\n                      >\n                        <defs>\n                          <linearGradient\n                            id=\"fillRevenue\"\n                            x1=\"0\"\n                            y1=\"0\"\n                            x2=\"0\"\n                            y2=\"1\"\n                          >\n                            <stop\n                              offset=\"5%\"\n                              stopColor=\"var(--color-revenue)\"\n                              stopOpacity={0.3}\n                            />\n                            <stop\n                              offset=\"95%\"\n                              stopColor=\"var(--color-revenue)\"\n                              stopOpacity={0}\n                            />\n                          </linearGradient>\n                        </defs>\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={\n                            <ChartTooltipContent\n                              formatter={(value) =>\n                                \"$\" + Number(value).toLocaleString(\"en-US\")\n                              }\n                            />\n                          }\n                        />\n                        <Area\n                          type=\"monotone\"\n                          dataKey=\"revenue\"\n                          stroke=\"var(--color-revenue)\"\n                          strokeWidth={2}\n                          fill=\"url(#fillRevenue)\"\n                          dot={false}\n                          activeDot={{ r: 4, strokeWidth: 0 }}\n                        />\n                      </AreaChart>\n                    </ChartContainer>\n                  </CardContent>\n                </Card>\n                <Card>\n                  <CardHeader>\n                    <CardTitle>Recent Activity</CardTitle>\n                  </CardHeader>\n                  <CardContent className=\"space-y-4\">\n                    {activity.map((item) => (\n                      <div key={item.target} className=\"flex items-start gap-3\">\n                        <div className=\"relative shrink-0\">\n                          <Avatar size=\"sm\">\n                            <AvatarImage\n                              src={item.avatar}\n                              alt={item.name}\n                              className=\"grayscale\"\n                            />\n                            <AvatarFallback className=\"text-[10px]\">\n                              {item.name\n                                .split(\" \")\n                                .map((n) => n[0])\n                                .join(\"\")}\n                            </AvatarFallback>\n                          </Avatar>\n                          <span className=\"absolute -right-1 -bottom-1 flex size-4 items-center justify-center border border-border bg-card\">\n                            <item.icon className=\"size-2.5 text-muted-foreground\" />\n                          </span>\n                        </div>\n                        <div className=\"min-w-0 flex-1\">\n                          <p className=\"text-sm leading-snug\">\n                            <span className=\"font-medium text-foreground\">\n                              {item.name}\n                            </span>{\" \"}\n                            <span className=\"text-muted-foreground\">\n                              {item.action}\n                            </span>{\" \"}\n                            <span className=\"font-medium text-foreground\">\n                              {item.target}\n                            </span>\n                          </p>\n                          <p className=\"mt-0.5 text-xs text-muted-foreground\">\n                            {item.time}\n                          </p>\n                        </div>\n                      </div>\n                    ))}\n                  </CardContent>\n                </Card>\n              </div>\n            </div>\n          )}\n        </main>\n      </SidebarInset>\n\n      <CommandDialog open={commandOpen} onOpenChange={setCommandOpen}>\n        <Command>\n          <CommandInput\n            aria-label=\"Type a command or search\"\n            placeholder=\"Type a command or search...\"\n          />\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"
    }
  ],
  "meta": {
    "height": "90vh",
    "tier": "free"
  },
  "categories": [
    "app-shell"
  ],
  "type": "registry:block"
}