From 369660bad12f3077c09d495e48ba671cbcadf6df Mon Sep 17 00:00:00 2001 From: Jack Merrill Date: Tue, 1 Apr 2025 17:00:22 -0400 Subject: [PATCH] :tada: init --- .env.example | 4 - app/dashboard/page.tsx | 61 +++ app/globals.css | 170 ++++-- app/layout.tsx | 35 +- app/login/page.tsx | 19 + app/page.tsx | 22 +- app/protected/page.tsx | 38 -- app/protected/reset-password/page.tsx | 37 -- bun.lockb | Bin 93862 -> 110609 bytes components/app-sidebar.tsx | 276 ++++++++++ components/login-form.tsx | 67 +++ components/nav-actions.tsx | 153 ++++++ components/nav-favorites.tsx | 94 ++++ components/nav-main.tsx | 35 ++ components/nav-secondary.tsx | 43 ++ components/nav-workspaces.tsx | 85 +++ components/team-switcher.tsx | 83 +++ components/ui/breadcrumb.tsx | 109 ++++ components/ui/card.tsx | 92 ++++ components/ui/collapsible.tsx | 33 ++ components/ui/popover.tsx | 48 ++ components/ui/separator.tsx | 28 + components/ui/sheet.tsx | 139 +++++ components/ui/sidebar.tsx | 726 ++++++++++++++++++++++++++ components/ui/skeleton.tsx | 13 + components/ui/tooltip.tsx | 61 +++ hooks/use-mobile.ts | 19 + package.json | 23 +- postcss.config.js | 6 - postcss.config.mjs | 6 + 30 files changed, 2332 insertions(+), 193 deletions(-) delete mode 100644 .env.example create mode 100644 app/dashboard/page.tsx create mode 100644 app/login/page.tsx delete mode 100644 app/protected/page.tsx delete mode 100644 app/protected/reset-password/page.tsx create mode 100644 components/app-sidebar.tsx create mode 100644 components/login-form.tsx create mode 100644 components/nav-actions.tsx create mode 100644 components/nav-favorites.tsx create mode 100644 components/nav-main.tsx create mode 100644 components/nav-secondary.tsx create mode 100644 components/nav-workspaces.tsx create mode 100644 components/team-switcher.tsx create mode 100644 components/ui/breadcrumb.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/collapsible.tsx create mode 100644 components/ui/popover.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 components/ui/sidebar.tsx create mode 100644 components/ui/skeleton.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 hooks/use-mobile.ts delete mode 100644 postcss.config.js create mode 100644 postcss.config.mjs diff --git a/.env.example b/.env.example deleted file mode 100644 index 6937031..0000000 --- a/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# Update these with your Supabase details from your project settings > API -# https://app.supabase.com/project/_/settings/api -NEXT_PUBLIC_SUPABASE_URL=your-project-url -NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..2d2390b --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,61 @@ +import { AppSidebar } from "@/components/app-sidebar"; +import { NavActions } from "@/components/nav-actions"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbList, + BreadcrumbPage, +} from "@/components/ui/breadcrumb"; +import { Separator } from "@/components/ui/separator"; +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar"; +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; + +export default async function Page() { + const supabase = await createClient(); + + const { + data: { user }, + } = await supabase.auth.getUser(); + + if (!user) { + return redirect("/login"); + } + + return ( + + + +
+
+ + + + + + + Project Management & Task Tracking + + + + +
+
+ +
+
+
+
+
+
+ + + ); +} diff --git a/app/globals.css b/app/globals.css index f450d1e..a356309 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,67 +1,121 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; +@import "tw-animate-css"; -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; - --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 3.9%; - --radius: 0.5rem; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - } +@custom-variant dark (&:is(.dark *)); - .dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 0 0% 9%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - } +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); } @layer base { * { - @apply border-border; + @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; diff --git a/app/layout.tsx b/app/layout.tsx index 8f6dcb7..d81c446 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -37,39 +37,8 @@ export default function RootLayout({ enableSystem disableTransitionOnChange > -
-
- -
- {children} -
- - -
-
+ {children} + diff --git a/app/login/page.tsx b/app/login/page.tsx new file mode 100644 index 0000000..94b501b --- /dev/null +++ b/app/login/page.tsx @@ -0,0 +1,19 @@ +import { Brain, BrainCircuit, GalleryVerticalEnd } from "lucide-react"; + +import { LoginForm } from "@/components/login-form"; + +export default function LoginPage() { + return ( + + ); +} diff --git a/app/page.tsx b/app/page.tsx index 9144694..9ed9260 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,15 +2,19 @@ import Hero from "@/components/hero"; import ConnectSupabaseSteps from "@/components/tutorial/connect-supabase-steps"; import SignUpUserSteps from "@/components/tutorial/sign-up-user-steps"; import { hasEnvVars } from "@/utils/supabase/check-env-vars"; +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; export default async function Home() { - return ( - <> - -
-

Next steps

- {hasEnvVars ? : } -
- - ); + const supabase = await createClient(); + + const { + data: { user }, + } = await supabase.auth.getUser(); + + if (!user) { + return redirect("/login"); + } else { + return redirect("/documents"); + } } diff --git a/app/protected/page.tsx b/app/protected/page.tsx deleted file mode 100644 index 5508aba..0000000 --- a/app/protected/page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import FetchDataSteps from "@/components/tutorial/fetch-data-steps"; -import { createClient } from "@/utils/supabase/server"; -import { InfoIcon } from "lucide-react"; -import { redirect } from "next/navigation"; - -export default async function ProtectedPage() { - const supabase = await createClient(); - - const { - data: { user }, - } = await supabase.auth.getUser(); - - if (!user) { - return redirect("/sign-in"); - } - - return ( -
-
-
- - This is a protected page that you can only see as an authenticated - user -
-
-
-

Your user details

-
-          {JSON.stringify(user, null, 2)}
-        
-
-
-

Next steps

- -
-
- ); -} diff --git a/app/protected/reset-password/page.tsx b/app/protected/reset-password/page.tsx deleted file mode 100644 index 9cd7084..0000000 --- a/app/protected/reset-password/page.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { resetPasswordAction } from "@/app/actions"; -import { FormMessage, Message } from "@/components/form-message"; -import { SubmitButton } from "@/components/submit-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; - -export default async function ResetPassword(props: { - searchParams: Promise; -}) { - const searchParams = await props.searchParams; - return ( -
-

Reset password

-

- Please enter your new password below. -

- - - - - - Reset password - - - - ); -} diff --git a/bun.lockb b/bun.lockb index 709b069394d20167584dd647278ec9f1e9111ffa..36eaec4bb13ab0d4a322636813e7cecac9ea0f55 100755 GIT binary patch delta 27987 zcmeHw2UJu^^Y7dNgh2;EK?gxaOsE9OAUXsQl>rqKVn#q2ML>dzX)r5>6+zgt~h-PP6A)qQ*JFwN@gCb>5& z%yMn)+&V7$)~XJXF7KagV@t1Q*0bn6_g-`5uPbL-nX49b>^*ifr=ZJ%%k5Un(6yq- z30cBzBQh)$itLr3D$ulq*tiTxnkhv-H7y|{L!Xc~AM%QjCnpRiv0|dJLQwL5`g z(3;@ynJEpw&RTN;DYMV1o>2B#3H2$q-|SJ~tKlUqP*q zU(-^ds0^Bso}}*wLoXnoJdmC+cxXbJLJ^melmeoNL18uW*MgdZMuJuXb*&{O>kLpD<1U~S1KyxiaV{F5A$b6X46TPsly3@3Z%5)|WME%i_yiiyxd z@*qx(p+-)kCmuEw>2dHhM3#+2k7T5I`H)^~2?oP1xi9{U~OZuXT#Dd zN1^x(g)|fwBzg#xJm86jX-MKyQU>W0&|C1-fqgEbXGgk>GQeant<6FnRY-X8In z-3AHrfEg;%7+!H!C=hqqqmfT8ZwgAD+2JlKumw*I7=e;OPf(ty-cu|WB~cA1bu0js z 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 delta 18150 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%0A}CBUW~qryPNGdtluW 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 vd}Rsy8*_?H$;!$}ZJj$VBXx9OR^GV7r) { + return ( + + + + + + + + + + + + + ) +} diff --git a/components/login-form.tsx b/components/login-form.tsx new file mode 100644 index 0000000..44add0e --- /dev/null +++ b/components/login-form.tsx @@ -0,0 +1,67 @@ +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +export function LoginForm({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ + + Welcome back + Login with your Google account + + +
+
+
+ +
+
+ + Or continue with + +
+
+
+ + +
+ +
+
+
+
+
+
+ ); +} diff --git a/components/nav-actions.tsx b/components/nav-actions.tsx new file mode 100644 index 0000000..867c85a --- /dev/null +++ b/components/nav-actions.tsx @@ -0,0 +1,153 @@ +"use client" + +import * as React from "react" +import { + ArrowDown, + ArrowUp, + Bell, + Copy, + CornerUpLeft, + CornerUpRight, + FileText, + GalleryVerticalEnd, + LineChart, + Link, + MoreHorizontal, + Settings2, + Star, + Trash, + Trash2, +} from "lucide-react" + +import { Button } from "@/components/ui/button" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +const data = [ + [ + { + label: "Customize Page", + icon: Settings2, + }, + { + label: "Turn into wiki", + icon: FileText, + }, + ], + [ + { + label: "Copy Link", + icon: Link, + }, + { + label: "Duplicate", + icon: Copy, + }, + { + label: "Move to", + icon: CornerUpRight, + }, + { + label: "Move to Trash", + icon: Trash2, + }, + ], + [ + { + label: "Undo", + icon: CornerUpLeft, + }, + { + label: "View analytics", + icon: LineChart, + }, + { + label: "Version History", + icon: GalleryVerticalEnd, + }, + { + label: "Show delete pages", + icon: Trash, + }, + { + label: "Notifications", + icon: Bell, + }, + ], + [ + { + label: "Import", + icon: ArrowUp, + }, + { + label: "Export", + icon: ArrowDown, + }, + ], +] + +export function NavActions() { + const [isOpen, setIsOpen] = React.useState(false) + + React.useEffect(() => { + setIsOpen(true) + }, []) + + return ( +
+
+ Edit Oct 08 +
+ + + + + + + + + {data.map((group, index) => ( + + + + {group.map((item, index) => ( + + + {item.label} + + + ))} + + + + ))} + + + + +
+ ) +} diff --git a/components/nav-favorites.tsx b/components/nav-favorites.tsx new file mode 100644 index 0000000..34e2aea --- /dev/null +++ b/components/nav-favorites.tsx @@ -0,0 +1,94 @@ +"use client" + +import { + ArrowUpRight, + Link, + MoreHorizontal, + StarOff, + Trash2, +} from "lucide-react" + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/components/ui/sidebar" + +export function NavFavorites({ + favorites, +}: { + favorites: { + name: string + url: string + emoji: string + }[] +}) { + const { isMobile } = useSidebar() + + return ( + + Favorites + + {favorites.map((item) => ( + + +
+ {item.emoji} + {item.name} + + + + + + + More + + + + + + Remove from Favorites + + + + + Copy Link + + + + Open in New Tab + + + + + Delete + + + + + ))} + + + + More + + + + + ) +} diff --git a/components/nav-main.tsx b/components/nav-main.tsx new file mode 100644 index 0000000..31edc97 --- /dev/null +++ b/components/nav-main.tsx @@ -0,0 +1,35 @@ +"use client" + +import { type LucideIcon } from "lucide-react" + +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +export function NavMain({ + items, +}: { + items: { + title: string + url: string + icon: LucideIcon + isActive?: boolean + }[] +}) { + return ( + + {items.map((item) => ( + + + + + {item.title} + + + + ))} + + ) +} diff --git a/components/nav-secondary.tsx b/components/nav-secondary.tsx new file mode 100644 index 0000000..71d2ebf --- /dev/null +++ b/components/nav-secondary.tsx @@ -0,0 +1,43 @@ +import React from "react" +import { type LucideIcon } from "lucide-react" + +import { + SidebarGroup, + SidebarGroupContent, + SidebarMenu, + SidebarMenuBadge, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +export function NavSecondary({ + items, + ...props +}: { + items: { + title: string + url: string + icon: LucideIcon + badge?: React.ReactNode + }[] +} & React.ComponentPropsWithoutRef) { + return ( + + + + {items.map((item) => ( + + + + + {item.title} + + + {item.badge && {item.badge}} + + ))} + + + + ) +} diff --git a/components/nav-workspaces.tsx b/components/nav-workspaces.tsx new file mode 100644 index 0000000..70ade7f --- /dev/null +++ b/components/nav-workspaces.tsx @@ -0,0 +1,85 @@ +import { ChevronRight, MoreHorizontal, Plus } from "lucide-react" + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible" +import { + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, +} from "@/components/ui/sidebar" + +export function NavWorkspaces({ + workspaces, +}: { + workspaces: { + name: string + emoji: React.ReactNode + pages: { + name: string + emoji: React.ReactNode + }[] + }[] +}) { + return ( + + Workspaces + + + {workspaces.map((workspace) => ( + + + + + {workspace.emoji} + {workspace.name} + + + + + + + + + + + + + {workspace.pages.map((page) => ( + + + + {page.emoji} + {page.name} + + + + ))} + + + + + ))} + + + + More + + + + + + ) +} diff --git a/components/team-switcher.tsx b/components/team-switcher.tsx new file mode 100644 index 0000000..76144b2 --- /dev/null +++ b/components/team-switcher.tsx @@ -0,0 +1,83 @@ +"use client" + +import * as React from "react" +import { ChevronDown, Plus } from "lucide-react" + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +export function TeamSwitcher({ + teams, +}: { + teams: { + name: string + logo: React.ElementType + plan: string + }[] +}) { + const [activeTeam, setActiveTeam] = React.useState(teams[0]) + + if (!activeTeam) { + return null + } + + return ( + + + + + +
+ +
+ {activeTeam.name} + +
+
+ + + Teams + + {teams.map((team, index) => ( + setActiveTeam(team)} + className="gap-2 p-2" + > +
+ +
+ {team.name} + ⌘{index + 1} +
+ ))} + + +
+ +
+
Add team
+
+
+
+
+
+ ) +} diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return