diff --git a/apps/client/@/shadcn/components/app-sidebar.tsx b/apps/client/@/shadcn/components/app-sidebar.tsx new file mode 100644 index 000000000..ee412821f --- /dev/null +++ b/apps/client/@/shadcn/components/app-sidebar.tsx @@ -0,0 +1,100 @@ +import * as React from "react"; +import { + AudioWaveform, + BookOpen, + Bot, + Command, + Frame, + GalleryVerticalEnd, + Map, + PieChart, + Settings2, + SquareTerminal, +} from "lucide-react"; + +import { NavMain } from "@/shadcn/components/nav-main"; +import { NavProjects } from "@/shadcn/components/nav-projects"; +import { NavUser } from "@/shadcn/components/nav-user"; +import { TeamSwitcher } from "@/shadcn/components/team-switcher"; +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarRail, +} from "@/shadcn/ui/sidebar"; +import ThemeSettings from "../../../components/ThemeSettings"; + +const data = { + user: { + name: "shadcn", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg", + }, + teams: [ + { + name: "Peppermint", + logo: GalleryVerticalEnd, + plan: `version: ${process.env.NEXT_PUBLIC_CLIENT_VERSION}`, + }, + ], + navMain: [ + { + title: "Dashboard", + url: "/", + icon: SquareTerminal, + isActive: true, + }, + { + title: "Issues", + url: "#", + icon: Bot, + items: [ + { + title: "Open", + url: "#", + }, + { + title: "Closed", + url: "#", + }, + ], + }, + { + title: "Admin", + url: "/", + icon: SquareTerminal, + isActive: true, + }, + ], +}; + +export function AppSidebar({ ...props }: React.ComponentProps) { + return ( + + + {/* */} +
+
+ +
+
+ Peppermint + + version: {process.env.NEXT_PUBLIC_CLIENT_VERSION} + +
+
+
+ + + + +
+ +
+
+ +
+ ); +} diff --git a/apps/client/@/shadcn/components/nav-main.tsx b/apps/client/@/shadcn/components/nav-main.tsx new file mode 100644 index 000000000..3d4886463 --- /dev/null +++ b/apps/client/@/shadcn/components/nav-main.tsx @@ -0,0 +1,81 @@ +import { ChevronRight, type LucideIcon } from "lucide-react"; + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/shadcn/ui/collapsible"; +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, +} from "@/shadcn/ui/sidebar"; + +export function NavMain({ + items, +}: { + items: { + title: string; + url: string; + icon?: LucideIcon; + isActive?: boolean; + items?: { + title: string; + url: string; + }[]; + }[]; +}) { + return ( + + + {items.map((item) => + item.items ? ( + + + + + {item.icon && } + {item.title} + + + + + + {item.items?.map((subItem) => ( + + + + {subItem.title} + + + + ))} + + + + + ) : ( + + + + {item.icon && } + {item.title} + + + + ) + )} + + + ); +} diff --git a/apps/client/@/shadcn/components/nav-projects.tsx b/apps/client/@/shadcn/components/nav-projects.tsx new file mode 100644 index 000000000..eda7920c5 --- /dev/null +++ b/apps/client/@/shadcn/components/nav-projects.tsx @@ -0,0 +1,87 @@ +import { + Folder, + Forward, + MoreHorizontal, + Trash2, + type LucideIcon, +} from "lucide-react" + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/shadcn/ui/dropdown-menu"; +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/shadcn/ui/sidebar"; + +export function NavProjects({ + projects, +}: { + projects: { + name: string + url: string + icon: LucideIcon + }[] +}) { + const { isMobile } = useSidebar() + + return ( + + Projects + + {projects.map((item) => ( + + + + + {item.name} + + + + + + + More + + + + + + View Project + + + + Share Project + + + + + Delete Project + + + + + ))} + + + + More + + + + + ) +} diff --git a/apps/client/@/shadcn/components/nav-user.tsx b/apps/client/@/shadcn/components/nav-user.tsx new file mode 100644 index 000000000..5c3d1f648 --- /dev/null +++ b/apps/client/@/shadcn/components/nav-user.tsx @@ -0,0 +1,112 @@ +import { + BadgeCheck, + Bell, + ChevronsUpDown, + CreditCard, + LogOut, + Sparkles, +} from "lucide-react" + +import { + Avatar, + AvatarFallback, + AvatarImage, +} from "@/shadcn/ui/avatar"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/shadcn/ui/dropdown-menu"; +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/shadcn/ui/sidebar"; + +export function NavUser({ + user, +}: { + user: { + name: string + email: string + avatar: string + } +}) { + const { isMobile } = useSidebar() + + return ( + + + + + + + + CN + +
+ {user.name} + {user.email} +
+ +
+
+ + +
+ + + CN + +
+ {user.name} + {user.email} +
+
+
+ + + + + Upgrade to Pro + + + + + + + Account + + + + Billing + + + + Notifications + + + + + + Log out + +
+
+
+
+ ) +} diff --git a/apps/client/@/shadcn/components/team-switcher.tsx b/apps/client/@/shadcn/components/team-switcher.tsx new file mode 100644 index 000000000..33ece28ea --- /dev/null +++ b/apps/client/@/shadcn/components/team-switcher.tsx @@ -0,0 +1,87 @@ +import * as React from "react" +import { ChevronsUpDown, Plus } from "lucide-react" + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/shadcn/ui/dropdown-menu"; +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/shadcn/ui/sidebar"; + +export function TeamSwitcher({ + teams, +}: { + teams: { + name: string + logo: React.ElementType + plan: string + }[] +}) { + const { isMobile } = useSidebar() + const [activeTeam, setActiveTeam] = React.useState(teams[0]) + + return ( + + + + + +
+ +
+
+ + {activeTeam.name} + + {activeTeam.plan} +
+ +
+
+ + + Teams + + {teams.map((team, index) => ( + setActiveTeam(team)} + className="gap-2 p-2" + > +
+ +
+ {team.name} + ⌘{index + 1} +
+ ))} + + +
+ +
+
Add team
+
+
+
+
+
+ ) +} diff --git a/apps/client/@/shadcn/hooks/use-mobile.tsx b/apps/client/@/shadcn/hooks/use-mobile.tsx new file mode 100644 index 000000000..2b0fe1dfe --- /dev/null +++ b/apps/client/@/shadcn/hooks/use-mobile.tsx @@ -0,0 +1,19 @@ +import * as React from "react" + +const MOBILE_BREAKPOINT = 768 + +export function useIsMobile() { + const [isMobile, setIsMobile] = React.useState(undefined) + + React.useEffect(() => { + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) + const onChange = () => { + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + } + mql.addEventListener("change", onChange) + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + return () => mql.removeEventListener("change", onChange) + }, []) + + return !!isMobile +} diff --git a/apps/client/@/shadcn/ui/avatar.tsx b/apps/client/@/shadcn/ui/avatar.tsx new file mode 100644 index 000000000..49e8d357a --- /dev/null +++ b/apps/client/@/shadcn/ui/avatar.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/shadcn/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/apps/client/@/shadcn/ui/breadcrumb.tsx b/apps/client/@/shadcn/ui/breadcrumb.tsx new file mode 100644 index 000000000..dc6880522 --- /dev/null +++ b/apps/client/@/shadcn/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/shadcn/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>