feat: new sidebar added
							parent
							
								
									74a50f22e4
								
							
						
					
					
						commit
						8ee1aeb8d3
					
				|  | @ -18,7 +18,7 @@ class AuthenticatedSessionController extends Controller | ||||||
|      */ |      */ | ||||||
|     public function create(): Response |     public function create(): Response | ||||||
|     { |     { | ||||||
|         return Inertia::render('Auth/Login', [ |         return Inertia::render('auth/login', [ | ||||||
|             'canResetPassword' => Route::has('password.request'), |             'canResetPassword' => Route::has('password.request'), | ||||||
|             'status' => session('status'), |             'status' => session('status'), | ||||||
|         ]); |         ]); | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ class ConfirmablePasswordController extends Controller | ||||||
|      */ |      */ | ||||||
|     public function show(): Response |     public function show(): Response | ||||||
|     { |     { | ||||||
|         return Inertia::render('Auth/ConfirmPassword'); |         return Inertia::render('auth/confirm-password'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -17,6 +17,6 @@ class EmailVerificationPromptController extends Controller | ||||||
|     { |     { | ||||||
|         return $request->user()->hasVerifiedEmail() |         return $request->user()->hasVerifiedEmail() | ||||||
|                     ? redirect()->intended(route('dashboard', absolute: false)) |                     ? redirect()->intended(route('dashboard', absolute: false)) | ||||||
|                     : Inertia::render('Auth/VerifyEmail', ['status' => session('status')]); |                     : Inertia::render('auth/verify-email', ['status' => session('status')]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ class NewPasswordController extends Controller | ||||||
|      */ |      */ | ||||||
|     public function create(Request $request): Response |     public function create(Request $request): Response | ||||||
|     { |     { | ||||||
|         return Inertia::render('Auth/ResetPassword', [ |         return Inertia::render('auth/reset-password', [ | ||||||
|             'email' => $request->email, |             'email' => $request->email, | ||||||
|             'token' => $request->route('token'), |             'token' => $request->route('token'), | ||||||
|         ]); |         ]); | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ class PasswordResetLinkController extends Controller | ||||||
|      */ |      */ | ||||||
|     public function create(): Response |     public function create(): Response | ||||||
|     { |     { | ||||||
|         return Inertia::render('Auth/ForgotPassword', [ |         return Inertia::render('auth/forgot-password', [ | ||||||
|             'status' => session('status'), |             'status' => session('status'), | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ class RegisteredUserController extends Controller | ||||||
|      */ |      */ | ||||||
|     public function create(): Response |     public function create(): Response | ||||||
|     { |     { | ||||||
|         return Inertia::render('Auth/Register'); |         return Inertia::render('auth/register'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ class ProfileController extends Controller | ||||||
|      */ |      */ | ||||||
|     public function edit(Request $request): Response |     public function edit(Request $request): Response | ||||||
|     { |     { | ||||||
|         return Inertia::render('Profile/Edit', [ |         return Inertia::render('profile/edit', [ | ||||||
|             'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail, |             'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail, | ||||||
|             'status' => session('status'), |             'status' => session('status'), | ||||||
|         ]); |         ]); | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ | ||||||
|     "prefix": "" |     "prefix": "" | ||||||
|   }, |   }, | ||||||
|   "aliases": { |   "aliases": { | ||||||
|     "components": "@/Components", |     "components": "@/components", | ||||||
|     "utils": "@/lib/utils" |     "utils": "@/lib/utils" | ||||||
|   } |   } | ||||||
| } | } | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								
								
									
									
										
											12
										
									
									package.json
									
									
									
									
								
								
							
							
										
											12
										
									
									package.json
									
									
									
									
								|  | @ -25,14 +25,16 @@ | ||||||
|     }, |     }, | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "@radix-ui/react-alert-dialog": "^1.0.5", |         "@radix-ui/react-alert-dialog": "^1.0.5", | ||||||
|         "@radix-ui/react-avatar": "^1.0.4", |         "@radix-ui/react-avatar": "^1.1.1", | ||||||
|         "@radix-ui/react-collapsible": "^1.0.3", |         "@radix-ui/react-collapsible": "^1.1.1", | ||||||
|         "@radix-ui/react-dropdown-menu": "^2.0.6", |         "@radix-ui/react-dialog": "^1.1.2", | ||||||
|  |         "@radix-ui/react-dropdown-menu": "^2.1.2", | ||||||
|         "@radix-ui/react-label": "^2.0.2", |         "@radix-ui/react-label": "^2.0.2", | ||||||
|         "@radix-ui/react-popover": "^1.0.7", |         "@radix-ui/react-popover": "^1.0.7", | ||||||
|         "@radix-ui/react-scroll-area": "^1.0.5", |         "@radix-ui/react-scroll-area": "^1.0.5", | ||||||
|         "@radix-ui/react-slot": "^1.0.2", |         "@radix-ui/react-separator": "^1.1.0", | ||||||
|         "@radix-ui/react-tooltip": "^1.0.7", |         "@radix-ui/react-slot": "^1.1.0", | ||||||
|  |         "@radix-ui/react-tooltip": "^1.1.3", | ||||||
|         "class-variance-authority": "^0.7.0", |         "class-variance-authority": "^0.7.0", | ||||||
|         "clsx": "^2.1.1", |         "clsx": "^2.1.1", | ||||||
|         "date-fns": "^3.6.0", |         "date-fns": "^3.6.0", | ||||||
|  |  | ||||||
|  | @ -5,47 +5,63 @@ | ||||||
| @layer base { | @layer base { | ||||||
|     :root { |     :root { | ||||||
|         --background: 0 0% 100%; |         --background: 0 0% 100%; | ||||||
|         --foreground: 222.2 84% 4.9%; |         --foreground: 240 10% 3.9%; | ||||||
|         --card: 0 0% 100%; |         --card: 0 0% 100%; | ||||||
|         --card-foreground: 222.2 84% 4.9%; |         --card-foreground: 240 10% 3.9%; | ||||||
|         --popover: 0 0% 100%; |         --popover: 0 0% 100%; | ||||||
|         --popover-foreground: 222.2 84% 4.9%; |         --popover-foreground: 240 10% 3.9%; | ||||||
|         --primary: 221.2 83.2% 53.3%; |         --primary: 240 5.9% 10%; | ||||||
|         --primary-foreground: 210 40% 98%; |         --primary-foreground: 0 0% 98%; | ||||||
|         --secondary: 210 40% 96.1%; |         --secondary: 240 4.8% 95.9%; | ||||||
|         --secondary-foreground: 222.2 47.4% 11.2%; |         --secondary-foreground: 240 5.9% 10%; | ||||||
|         --muted: 210 40% 96.1%; |         --muted: 240 4.8% 95.9%; | ||||||
|         --muted-foreground: 215.4 16.3% 46.9%; |         --muted-foreground: 240 3.8% 46.1%; | ||||||
|         --accent: 210 40% 96.1%; |         --accent: 240 4.8% 95.9%; | ||||||
|         --accent-foreground: 222.2 47.4% 11.2%; |         --accent-foreground: 240 5.9% 10%; | ||||||
|         --destructive: 0 84.2% 60.2%; |         --destructive: 0 84.2% 60.2%; | ||||||
|         --destructive-foreground: 210 40% 98%; |         --destructive-foreground: 0 0% 98%; | ||||||
|         --border: 214.3 31.8% 91.4%; |         --border: 240 5.9% 90%; | ||||||
|         --input: 214.3 31.8% 91.4%; |         --input: 240 5.9% 90%; | ||||||
|         --ring: 221.2 83.2% 53.3%; |         --ring: 240 5.9% 10%; | ||||||
|         --radius: 0.5rem; |         --radius: 0.5rem; | ||||||
|  |         --sidebar-background: 0 0% 98%; | ||||||
|  |         --sidebar-foreground: 240 5.3% 26.1%; | ||||||
|  |         --sidebar-primary: 240 5.9% 10%; | ||||||
|  |         --sidebar-primary-foreground: 0 0% 98%; | ||||||
|  |         --sidebar-accent: 240 4.8% 95.9%; | ||||||
|  |         --sidebar-accent-foreground: 240 5.9% 10%; | ||||||
|  |         --sidebar-border: 220 13% 91%; | ||||||
|  |         --sidebar-ring: 217.2 91.2% 59.8%; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .dark { |     .dark { | ||||||
|         --background: 222.2 84% 4.9%; |         --background: 240 10% 3.9%; | ||||||
|         --foreground: 210 40% 98%; |         --foreground: 0 0% 98%; | ||||||
|         --card: 222.2 84% 4.9%; |         --card: 240 10% 3.9%; | ||||||
|         --card-foreground: 210 40% 98%; |         --card-foreground: 0 0% 98%; | ||||||
|         --popover: 222.2 84% 4.9%; |         --popover: 240 10% 3.9%; | ||||||
|         --popover-foreground: 210 40% 98%; |         --popover-foreground: 0 0% 98%; | ||||||
|         --primary: 217.2 91.2% 59.8%; |         --primary: 0 0% 98%; | ||||||
|         --primary-foreground: 222.2 47.4% 11.2%; |         --primary-foreground: 240 5.9% 10%; | ||||||
|         --secondary: 217.2 32.6% 17.5%; |         --secondary: 240 3.7% 15.9%; | ||||||
|         --secondary-foreground: 210 40% 98%; |         --secondary-foreground: 0 0% 98%; | ||||||
|         --muted: 217.2 32.6% 17.5%; |         --muted: 240 3.7% 15.9%; | ||||||
|         --muted-foreground: 215 20.2% 65.1%; |         --muted-foreground: 240 5% 64.9%; | ||||||
|         --accent: 217.2 32.6% 17.5%; |         --accent: 240 3.7% 15.9%; | ||||||
|         --accent-foreground: 210 40% 98%; |         --accent-foreground: 0 0% 98%; | ||||||
|         --destructive: 0 62.8% 30.6%; |         --destructive: 0 62.8% 30.6%; | ||||||
|         --destructive-foreground: 210 40% 98%; |         --destructive-foreground: 0 0% 98%; | ||||||
|         --border: 217.2 32.6% 17.5%; |         --border: 240 3.7% 15.9%; | ||||||
|         --input: 217.2 32.6% 17.5%; |         --input: 240 3.7% 15.9%; | ||||||
|         --ring: 224.3 76.3% 48%; |         --ring: 240 4.9% 83.9%; | ||||||
|  |         --sidebar-background: 240 5.9% 10%; | ||||||
|  |         --sidebar-foreground: 240 4.8% 95.9%; | ||||||
|  |         --sidebar-primary: 224.3 76.3% 48%; | ||||||
|  |         --sidebar-primary-foreground: 0 0% 100%; | ||||||
|  |         --sidebar-accent: 240 3.7% 15.9%; | ||||||
|  |         --sidebar-accent-foreground: 240 4.8% 95.9%; | ||||||
|  |         --sidebar-border: 240 3.7% 15.9%; | ||||||
|  |         --sidebar-ring: 217.2 91.2% 59.8%; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,60 +0,0 @@ | ||||||
| import { Sheet, SheetContent, SheetTrigger } from "@/Components/ui/sheet"; |  | ||||||
| import { Button } from "@/Components/ui/button"; |  | ||||||
| import { AlignJustifyIcon, Menu } from "lucide-react"; |  | ||||||
| import { Link } from "@inertiajs/react"; |  | ||||||
| import { MenuItemProp } from "@/types"; |  | ||||||
| import ApplicationLogo from "@/Components/ApplicationLogo"; |  | ||||||
| import { DropdownMenuSeparator } from "./ui/dropdown-menu"; |  | ||||||
| 
 |  | ||||||
| const MobileMenu = ({ links }: { links: MenuItemProp[] }) => { |  | ||||||
|     return ( |  | ||||||
|         <Sheet> |  | ||||||
|             <SheetTrigger asChild> |  | ||||||
|                 <Button |  | ||||||
|                     variant="outline" |  | ||||||
|                     size="icon" |  | ||||||
|                     className="shrink-0 md:hidden" |  | ||||||
|                 > |  | ||||||
|                     <Menu className="h-5 w-5" /> |  | ||||||
|                     <span className="sr-only">Toggle navigation menu</span> |  | ||||||
|                 </Button> |  | ||||||
|             </SheetTrigger> |  | ||||||
|             <SheetContent side="left" className="flex flex-col"> |  | ||||||
|                 <nav className="text-lg font-medium"> |  | ||||||
|                     <Link |  | ||||||
|                         href="#" |  | ||||||
|                         className="flex items-center gap-2 text-lg font-semibold mb-4" |  | ||||||
|                     > |  | ||||||
|                         <ApplicationLogo className="h-8 w-8 fill-current text-gray-500" /> |  | ||||||
| 
 |  | ||||||
|                         <span>Acme Inc</span> |  | ||||||
|                     </Link> |  | ||||||
| 
 |  | ||||||
|                     <DropdownMenuSeparator /> |  | ||||||
| 
 |  | ||||||
|                     <div className="space-y-2"> |  | ||||||
|                         {links.map((link, index) => ( |  | ||||||
|                             <MobileMenuItem key={index} link={link} /> |  | ||||||
|                         ))} |  | ||||||
|                     </div> |  | ||||||
|                 </nav> |  | ||||||
|             </SheetContent> |  | ||||||
|         </Sheet> |  | ||||||
|     ); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const MobileMenuItem = ({ link }: { link: MenuItemProp }) => { |  | ||||||
|     const Icon = (link.icon ?? AlignJustifyIcon) as React.ElementType; |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <Link |  | ||||||
|             href={link.href} |  | ||||||
|             className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground" |  | ||||||
|         > |  | ||||||
|             <Icon className="h-5 w-5" /> |  | ||||||
|             {link.title} |  | ||||||
|         </Link> |  | ||||||
|     ); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export default MobileMenu; |  | ||||||
|  | @ -1,116 +0,0 @@ | ||||||
| import { Link, usePage } from "@inertiajs/react"; |  | ||||||
| import { AlignJustifyIcon, Home, Icon } from "lucide-react"; |  | ||||||
| import { cn } from "@/lib/utils"; |  | ||||||
| import { buttonVariants } from "@/Components/ui/button"; |  | ||||||
| import { |  | ||||||
|     Tooltip, |  | ||||||
|     TooltipContent, |  | ||||||
|     TooltipProvider, |  | ||||||
|     TooltipTrigger, |  | ||||||
| } from "@/Components/ui/tooltip"; |  | ||||||
| import { MenuItemProp } from "@/types"; |  | ||||||
| import ApplicationLogo from "@/Components/ApplicationLogo"; |  | ||||||
| import { ReactNode } from "react"; |  | ||||||
| 
 |  | ||||||
| type Props = { |  | ||||||
|     links: MenuItemProp[]; |  | ||||||
|     isCollapsed?: boolean; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| type MenuItemProps = { |  | ||||||
|     link: MenuItemProp; |  | ||||||
|     isActive?: boolean; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const CollapsedMenuItem = ({ link, isActive }: MenuItemProps) => { |  | ||||||
|     const Icon = (link.icon ?? AlignJustifyIcon) as React.ElementType; |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <Tooltip delayDuration={0}> |  | ||||||
|             <TooltipTrigger asChild> |  | ||||||
|                 <Link |  | ||||||
|                     href={link.href} |  | ||||||
|                     className={cn( |  | ||||||
|                         buttonVariants({ |  | ||||||
|                             variant: isActive ? "default" : "ghost", |  | ||||||
|                             size: "icon", |  | ||||||
|                         }), |  | ||||||
|                         "h-9 w-9", |  | ||||||
|                         link.variant === "default" && |  | ||||||
|                             "dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white" |  | ||||||
|                     )} |  | ||||||
|                 > |  | ||||||
|                     {<Icon className="h-4 w-4" />} |  | ||||||
|                     <span className="sr-only">{link.title}</span> |  | ||||||
|                 </Link> |  | ||||||
|             </TooltipTrigger> |  | ||||||
|             <TooltipContent side="right" className="flex items-center gap-4"> |  | ||||||
|                 {link.title} |  | ||||||
|             </TooltipContent> |  | ||||||
|         </Tooltip> |  | ||||||
|     ); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const ExpandedMenuItem = ({ link, isActive }: MenuItemProps) => { |  | ||||||
|     const Icon = (link.icon ?? AlignJustifyIcon) as React.ElementType; |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <Link |  | ||||||
|             href={link.href} |  | ||||||
|             className={cn( |  | ||||||
|                 buttonVariants({ variant: isActive ? "default" : "ghost" }), |  | ||||||
|                 "justify-start" |  | ||||||
|             )} |  | ||||||
|         > |  | ||||||
|             {<Icon className="mr-2 h-4 w-4" />} |  | ||||||
|             {link.title} |  | ||||||
|         </Link> |  | ||||||
|     ); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const Sidebar = ({ links, isCollapsed }: Props) => { |  | ||||||
|     const { url } = usePage(); |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <TooltipProvider> |  | ||||||
|             <nav className="hidden bg-muted/40 md:block h-full"> |  | ||||||
|                 <div className="flex h-full max-h-screen flex-col gap-2"> |  | ||||||
|                     <div className="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6"> |  | ||||||
|                         <Link |  | ||||||
|                             href="/" |  | ||||||
|                             className={cn("flex items-center font-semibold", { |  | ||||||
|                                 "justify-center": isCollapsed, |  | ||||||
|                             })} |  | ||||||
|                         > |  | ||||||
|                             <ApplicationLogo className="h-6 w-6 fill-current text-gray-500" /> |  | ||||||
|                             {!isCollapsed && ( |  | ||||||
|                                 <span className="pl-2">Acme Inc</span> |  | ||||||
|                             )} |  | ||||||
|                         </Link> |  | ||||||
|                     </div> |  | ||||||
|                     <div className="flex-1"> |  | ||||||
|                         <nav className="grid items-start space-y-2 text-sm font-medium lg:px-4"> |  | ||||||
|                             {links.map((link, index) => |  | ||||||
|                                 isCollapsed ? ( |  | ||||||
|                                     <CollapsedMenuItem |  | ||||||
|                                         key={index} |  | ||||||
|                                         link={link} |  | ||||||
|                                         isActive={link.href.includes(url)} |  | ||||||
|                                     /> |  | ||||||
|                                 ) : ( |  | ||||||
|                                     <ExpandedMenuItem |  | ||||||
|                                         key={index} |  | ||||||
|                                         link={link} |  | ||||||
|                                         isActive={link.href.includes(url)} |  | ||||||
|                                     /> |  | ||||||
|                                 ) |  | ||||||
|                             )} |  | ||||||
|                         </nav> |  | ||||||
|                     </div> |  | ||||||
|                 </div> |  | ||||||
|             </nav> |  | ||||||
|         </TooltipProvider> |  | ||||||
|     ); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export default Sidebar; |  | ||||||
|  | @ -1,156 +0,0 @@ | ||||||
| import { PropsWithChildren, ReactNode, useState } from "react"; |  | ||||||
| import { Link } from "@inertiajs/react"; |  | ||||||
| import { MenuItemProp, User } from "@/types"; |  | ||||||
| import { CircleUser, Search, UserIcon } from "lucide-react"; |  | ||||||
| import { Button } from "@/Components/ui/button"; |  | ||||||
| import { |  | ||||||
|     DropdownMenu, |  | ||||||
|     DropdownMenuContent, |  | ||||||
|     DropdownMenuItem, |  | ||||||
|     DropdownMenuLabel, |  | ||||||
|     DropdownMenuSeparator, |  | ||||||
|     DropdownMenuTrigger, |  | ||||||
| } from "@/Components/ui/dropdown-menu"; |  | ||||||
| import { Input } from "@/Components/ui/input"; |  | ||||||
| import Sidebar from "@/Components/Sidebar"; |  | ||||||
| import MobileMenu from "@/Components/MobileMenu"; |  | ||||||
| import { |  | ||||||
|     ResizableHandle, |  | ||||||
|     ResizablePanel, |  | ||||||
|     ResizablePanelGroup, |  | ||||||
| } from "@/Components/ui/resizable"; |  | ||||||
| import { ScrollArea } from "@/Components/ui/scroll-area"; |  | ||||||
| import { cn } from "@/lib/utils"; |  | ||||||
| import AppearanceDropdown from "@/Components/AppearanceDropdown"; |  | ||||||
| 
 |  | ||||||
| const links: MenuItemProp[] = [ |  | ||||||
|     { |  | ||||||
|         title: "Dashboard", |  | ||||||
|         href: route("dashboard"), |  | ||||||
|         variant: "default", |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         title: "Profile", |  | ||||||
|         href: route("profile.edit"), |  | ||||||
|         variant: "ghost", |  | ||||||
|         icon: UserIcon, |  | ||||||
|     }, |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| export default function AuthenticatedLayout({ |  | ||||||
|     user, |  | ||||||
|     header, |  | ||||||
|     children, |  | ||||||
| }: PropsWithChildren<{ |  | ||||||
|     user: User; |  | ||||||
|     header?: ReactNode; |  | ||||||
| }>) { |  | ||||||
|     const [isCollapsed, setIsCollapsed] = useState(false); |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|         <ResizablePanelGroup |  | ||||||
|             direction="horizontal" |  | ||||||
|             className="h-screen w-full md:grid-cols-[220px_1fr] lg:grid-cols-[280px_1fr] fixed" |  | ||||||
|         > |  | ||||||
|             <ResizablePanel |  | ||||||
|                 defaultSize={14} |  | ||||||
|                 minSize={8} |  | ||||||
|                 maxSize={20} |  | ||||||
|                 collapsedSize={3.5} |  | ||||||
|                 collapsible={true} |  | ||||||
|                 onCollapse={() => { |  | ||||||
|                     setIsCollapsed(true); |  | ||||||
|                 }} |  | ||||||
|                 onExpand={() => { |  | ||||||
|                     setIsCollapsed(false); |  | ||||||
|                 }} |  | ||||||
|                 className={cn("min-w-[65px] hidden md:block", { |  | ||||||
|                     "transition-all duration-300 ease-in-out": isCollapsed, |  | ||||||
|                 })} |  | ||||||
|             > |  | ||||||
|                 <Sidebar links={links} isCollapsed={isCollapsed} /> |  | ||||||
|             </ResizablePanel> |  | ||||||
| 
 |  | ||||||
|             <ResizableHandle withHandle className={"hidden md:flex"} /> |  | ||||||
| 
 |  | ||||||
|             <ResizablePanel className="h-full w-full flex flex-col"> |  | ||||||
|                 <header className="flex h-14 items-center gap-4 border-b bg-muted/40 px-4 lg:h-[60px] lg:px-6"> |  | ||||||
|                     <MobileMenu links={links} /> |  | ||||||
| 
 |  | ||||||
|                     <div className="w-full flex-1 flex gap-4 justify-between items-center"> |  | ||||||
|                         <form className="flex-1"> |  | ||||||
|                             <div className="relative"> |  | ||||||
|                                 <Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" /> |  | ||||||
|                                 <Input |  | ||||||
|                                     type="search" |  | ||||||
|                                     placeholder="Search products..." |  | ||||||
|                                     className="w-full appearance-none bg-background pl-8 shadow-none md:w-2/3 lg:w-1/3" |  | ||||||
|                                 /> |  | ||||||
|                             </div> |  | ||||||
|                         </form> |  | ||||||
| 
 |  | ||||||
|                         <div className="space-x-4"> |  | ||||||
|                             <AppearanceDropdown /> |  | ||||||
| 
 |  | ||||||
|                             <DropdownMenu> |  | ||||||
|                                 <DropdownMenuTrigger asChild> |  | ||||||
|                                     <Button |  | ||||||
|                                         variant="secondary" |  | ||||||
|                                         size="icon" |  | ||||||
|                                         className="rounded-full" |  | ||||||
|                                     > |  | ||||||
|                                         <CircleUser className="h-5 w-5" /> |  | ||||||
|                                         <span className="sr-only"> |  | ||||||
|                                             Toggle user menu |  | ||||||
|                                         </span> |  | ||||||
|                                     </Button> |  | ||||||
|                                 </DropdownMenuTrigger> |  | ||||||
|                                 <DropdownMenuContent |  | ||||||
|                                     className="w-56" |  | ||||||
|                                     align="end" |  | ||||||
|                                 > |  | ||||||
|                                     <DropdownMenuLabel className="font-normal"> |  | ||||||
|                                         <div className="flex flex-col space-y-1"> |  | ||||||
|                                             <p className="text-sm font-medium leading-none"> |  | ||||||
|                                                 {user.name} |  | ||||||
|                                             </p> |  | ||||||
|                                             <p className="text-xs leading-none text-muted-foreground"> |  | ||||||
|                                                 {user.email} |  | ||||||
|                                             </p> |  | ||||||
|                                         </div> |  | ||||||
|                                     </DropdownMenuLabel> |  | ||||||
|                                     <DropdownMenuSeparator /> |  | ||||||
|                                     <DropdownMenuItem asChild> |  | ||||||
|                                         <Link |  | ||||||
|                                             className="cursor-pointer" |  | ||||||
|                                             href={route("profile.edit")} |  | ||||||
|                                         > |  | ||||||
|                                             Profile |  | ||||||
|                                         </Link> |  | ||||||
|                                     </DropdownMenuItem> |  | ||||||
|                                     <DropdownMenuSeparator /> |  | ||||||
|                                     <DropdownMenuItem asChild> |  | ||||||
|                                         <Link |  | ||||||
|                                             className="cursor-pointer w-full" |  | ||||||
|                                             href={route("logout")} |  | ||||||
|                                             method={"post"} |  | ||||||
|                                             as={"button"} |  | ||||||
|                                         > |  | ||||||
|                                             Log out |  | ||||||
|                                         </Link> |  | ||||||
|                                     </DropdownMenuItem> |  | ||||||
|                                 </DropdownMenuContent> |  | ||||||
|                             </DropdownMenu> |  | ||||||
|                         </div> |  | ||||||
|                     </div> |  | ||||||
|                 </header> |  | ||||||
| 
 |  | ||||||
|                 <ScrollArea className="px-6 pt-6 flex-1"> |  | ||||||
|                     <div className="pb-4">{header}</div> |  | ||||||
| 
 |  | ||||||
|                     <div className="mb-6">{children}</div> |  | ||||||
|                 </ScrollArea> |  | ||||||
|             </ResizablePanel> |  | ||||||
|         </ResizablePanelGroup> |  | ||||||
|     ); |  | ||||||
| } |  | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; |  | ||||||
| import { Head } from "@inertiajs/react"; |  | ||||||
| import { PageProps } from "@/types"; |  | ||||||
| 
 |  | ||||||
| export default function Dashboard({ auth }: PageProps) { |  | ||||||
|     return ( |  | ||||||
|         <AuthenticatedLayout |  | ||||||
|             user={auth.user} |  | ||||||
|             header={ |  | ||||||
|                 <h2 className="font-semibold text-xl leading-tight"> |  | ||||||
|                     Dashboard |  | ||||||
|                 </h2> |  | ||||||
|             } |  | ||||||
|         > |  | ||||||
|             <Head title="Dashboard" /> |  | ||||||
| 
 |  | ||||||
|             <div className="flex flex-col gap-4 md:gap-8"></div> |  | ||||||
|         </AuthenticatedLayout> |  | ||||||
|     ); |  | ||||||
| } |  | ||||||
|  | @ -4,7 +4,7 @@ import "../css/app.css"; | ||||||
| import { createRoot } from "react-dom/client"; | import { createRoot } from "react-dom/client"; | ||||||
| import { createInertiaApp } from "@inertiajs/react"; | import { createInertiaApp } from "@inertiajs/react"; | ||||||
| import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers"; | import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers"; | ||||||
| import { ThemeProvider } from "@/Components/ThemeProvider"; | import { ThemeProvider } from "@/components/theme-provider"; | ||||||
| 
 | 
 | ||||||
| const appName = import.meta.env.VITE_APP_NAME || "Laravel"; | const appName = import.meta.env.VITE_APP_NAME || "Laravel"; | ||||||
| 
 | 
 | ||||||
|  | @ -12,8 +12,8 @@ createInertiaApp({ | ||||||
|     title: (title) => `${title} - ${appName}`, |     title: (title) => `${title} - ${appName}`, | ||||||
|     resolve: (name) => |     resolve: (name) => | ||||||
|         resolvePageComponent( |         resolvePageComponent( | ||||||
|             `./Pages/${name}.tsx`, |             `./pages/${name}.tsx`, | ||||||
|             import.meta.glob("./Pages/**/*.tsx") |             import.meta.glob("./pages/**/*.tsx") | ||||||
|         ), |         ), | ||||||
|     setup({ el, App, props }) { |     setup({ el, App, props }) { | ||||||
|         const root = createRoot(el); |         const root = createRoot(el); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,102 @@ | ||||||
|  | "use client" | ||||||
|  | 
 | ||||||
|  | import * as React from "react" | ||||||
|  | import { | ||||||
|  |   Command, | ||||||
|  |   Frame, Home, | ||||||
|  |   LifeBuoy, | ||||||
|  |   Send, | ||||||
|  |   SquareTerminal, | ||||||
|  | } from "lucide-react" | ||||||
|  | 
 | ||||||
|  | import { NavMain } from "@/components/nav-main" | ||||||
|  | import { NavSecondary } from "@/components/nav-secondary" | ||||||
|  | import { NavUser } from "@/components/nav-user" | ||||||
|  | import { | ||||||
|  |   Sidebar, | ||||||
|  |   SidebarContent, | ||||||
|  |   SidebarFooter, | ||||||
|  |   SidebarHeader, | ||||||
|  |   SidebarMenu, | ||||||
|  |   SidebarMenuButton, | ||||||
|  |   SidebarMenuItem, | ||||||
|  | } from "@/components/ui/sidebar" | ||||||
|  | import {Link, usePage} from "@inertiajs/react"; | ||||||
|  | import {PageProps} from "@/types"; | ||||||
|  | 
 | ||||||
|  | const data = { | ||||||
|  |   user: { | ||||||
|  |     name: "shadcn", | ||||||
|  |     email: "m@example.com", | ||||||
|  |     avatar: "/avatars/shadcn.jpg", | ||||||
|  |   }, | ||||||
|  |   navMain: [ | ||||||
|  |     { | ||||||
|  |       title: "Dashboard", | ||||||
|  |       url: "/dashboard", | ||||||
|  |       icon: Home, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: "Projects", | ||||||
|  |       url: "#", | ||||||
|  |       icon: SquareTerminal, | ||||||
|  |       isActive: true, | ||||||
|  |       items: [ | ||||||
|  |         { | ||||||
|  |           title: "History", | ||||||
|  |           url: "#", | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |   { | ||||||
|  |       title: "Design Engineering", | ||||||
|  |       url: "#", | ||||||
|  |       icon: Frame, | ||||||
|  |   }, | ||||||
|  |   ], | ||||||
|  |   navSecondary: [ | ||||||
|  |     { | ||||||
|  |       title: "Support", | ||||||
|  |       url: "#", | ||||||
|  |       icon: LifeBuoy, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: "Feedback", | ||||||
|  |       url: "#", | ||||||
|  |       icon: Send, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) { | ||||||
|  |     const { auth } = usePage<PageProps>().props; | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |     <Sidebar variant="inset" {...props}> | ||||||
|  |       <SidebarHeader> | ||||||
|  |         <SidebarMenu> | ||||||
|  |           <SidebarMenuItem> | ||||||
|  |             <SidebarMenuButton size="lg" asChild> | ||||||
|  |               <Link href={route('dashboard')}> | ||||||
|  |                 <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"> | ||||||
|  |                   <Command className="size-4" /> | ||||||
|  |                 </div> | ||||||
|  |                 <div className="grid flex-1 text-left text-sm leading-tight"> | ||||||
|  |                   <span className="truncate font-semibold">Acme Inc</span> | ||||||
|  |                   <span className="truncate text-xs">Enterprise</span> | ||||||
|  |                 </div> | ||||||
|  |               </Link> | ||||||
|  |             </SidebarMenuButton> | ||||||
|  |           </SidebarMenuItem> | ||||||
|  |         </SidebarMenu> | ||||||
|  |       </SidebarHeader> | ||||||
|  |       <SidebarContent> | ||||||
|  |         <NavMain items={data.navMain} /> | ||||||
|  |         <NavSecondary items={data.navSecondary} className="mt-auto" /> | ||||||
|  |       </SidebarContent> | ||||||
|  |       <SidebarFooter> | ||||||
|  |         <NavUser user={auth.user} /> | ||||||
|  |       </SidebarFooter> | ||||||
|  |     </Sidebar> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | @ -1,22 +1,21 @@ | ||||||
| import { | import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,} from "@/components/ui/dropdown-menu"; | ||||||
|     DropdownMenu, | import {Button} from "@/components/ui/button"; | ||||||
|     DropdownMenuContent, |  | ||||||
|     DropdownMenuItem, |  | ||||||
|     DropdownMenuTrigger, |  | ||||||
| } from "@/Components/ui/dropdown-menu"; |  | ||||||
| import { Button } from "@/Components/ui/button"; |  | ||||||
| import {Moon, Sun} from "lucide-react"; | import {Moon, Sun} from "lucide-react"; | ||||||
| import {useTheme} from "next-themes"; | import {useTheme} from "next-themes"; | ||||||
|  | import {cn} from "@/lib/utils"; | ||||||
| 
 | 
 | ||||||
| const AppearanceDropdown = () => { | const AppearanceDropdown = () => { | ||||||
|     const { setTheme } = useTheme(); |     const {setTheme, theme} = useTheme(); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|         <DropdownMenu> |         <DropdownMenu> | ||||||
|             <DropdownMenuTrigger asChild> |             <DropdownMenuTrigger asChild> | ||||||
|                 <Button variant="outline" size="icon"> |                 <Button | ||||||
|                     <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /> |                     variant="ghost" | ||||||
|                     <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" /> |                     size="icon" | ||||||
|  |                     className={cn("h-7 w-7")} | ||||||
|  |                 > | ||||||
|  |                     {theme == 'light' ? <Sun/> : <Moon/>} | ||||||
|                     <span className="sr-only">Toggle theme</span> |                     <span className="sr-only">Toggle theme</span> | ||||||
|                 </Button> |                 </Button> | ||||||
|             </DropdownMenuTrigger> |             </DropdownMenuTrigger> | ||||||
|  | @ -0,0 +1,79 @@ | ||||||
|  | "use client" | ||||||
|  | 
 | ||||||
|  | import { ChevronRight, type LucideIcon } from "lucide-react" | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   Collapsible, | ||||||
|  |   CollapsibleContent, | ||||||
|  |   CollapsibleTrigger, | ||||||
|  | } from "@/components/ui/collapsible" | ||||||
|  | import { | ||||||
|  |   SidebarGroup, | ||||||
|  |   SidebarGroupLabel, | ||||||
|  |   SidebarMenu, | ||||||
|  |   SidebarMenuAction, | ||||||
|  |   SidebarMenuButton, | ||||||
|  |   SidebarMenuItem, | ||||||
|  |   SidebarMenuSub, | ||||||
|  |   SidebarMenuSubButton, | ||||||
|  |   SidebarMenuSubItem, | ||||||
|  | } from "@/components/ui/sidebar" | ||||||
|  | import {Link} from "@inertiajs/react"; | ||||||
|  | 
 | ||||||
|  | export function NavMain({ | ||||||
|  |   items, | ||||||
|  | }: { | ||||||
|  |   items: { | ||||||
|  |     title: string | ||||||
|  |     url: string | ||||||
|  |     icon: LucideIcon | ||||||
|  |     isActive?: boolean | ||||||
|  |     items?: { | ||||||
|  |       title: string | ||||||
|  |       url: string | ||||||
|  |     }[] | ||||||
|  |   }[] | ||||||
|  | }) { | ||||||
|  |   return ( | ||||||
|  |     <SidebarGroup> | ||||||
|  |       <SidebarGroupLabel>Navigation</SidebarGroupLabel> | ||||||
|  |       <SidebarMenu> | ||||||
|  |         {items.map((item) => ( | ||||||
|  |           <Collapsible key={item.title} asChild defaultOpen={item.isActive}> | ||||||
|  |             <SidebarMenuItem> | ||||||
|  |               <SidebarMenuButton asChild tooltip={item.title}> | ||||||
|  |                 <a href={item.url}> | ||||||
|  |                   <item.icon /> | ||||||
|  |                   <span>{item.title}</span> | ||||||
|  |                 </a> | ||||||
|  |               </SidebarMenuButton> | ||||||
|  |               {item.items?.length ? ( | ||||||
|  |                 <> | ||||||
|  |                   <CollapsibleTrigger asChild> | ||||||
|  |                     <SidebarMenuAction className="data-[state=open]:rotate-90"> | ||||||
|  |                       <ChevronRight /> | ||||||
|  |                       <span className="sr-only">Toggle</span> | ||||||
|  |                     </SidebarMenuAction> | ||||||
|  |                   </CollapsibleTrigger> | ||||||
|  |                   <CollapsibleContent> | ||||||
|  |                     <SidebarMenuSub> | ||||||
|  |                       {item.items?.map((subItem) => ( | ||||||
|  |                         <SidebarMenuSubItem key={subItem.title}> | ||||||
|  |                           <SidebarMenuSubButton asChild> | ||||||
|  |                             <Link href={subItem.url}> | ||||||
|  |                               <span>{subItem.title}</span> | ||||||
|  |                             </Link> | ||||||
|  |                           </SidebarMenuSubButton> | ||||||
|  |                         </SidebarMenuSubItem> | ||||||
|  |                       ))} | ||||||
|  |                     </SidebarMenuSub> | ||||||
|  |                   </CollapsibleContent> | ||||||
|  |                 </> | ||||||
|  |               ) : null} | ||||||
|  |             </SidebarMenuItem> | ||||||
|  |           </Collapsible> | ||||||
|  |         ))} | ||||||
|  |       </SidebarMenu> | ||||||
|  |     </SidebarGroup> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,40 @@ | ||||||
|  | import * as React from "react" | ||||||
|  | import { type LucideIcon } from "lucide-react" | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   SidebarGroup, | ||||||
|  |   SidebarGroupContent, | ||||||
|  |   SidebarMenu, | ||||||
|  |   SidebarMenuButton, | ||||||
|  |   SidebarMenuItem, | ||||||
|  | } from "@/components/ui/sidebar" | ||||||
|  | 
 | ||||||
|  | export function NavSecondary({ | ||||||
|  |   items, | ||||||
|  |   ...props | ||||||
|  | }: { | ||||||
|  |   items: { | ||||||
|  |     title: string | ||||||
|  |     url: string | ||||||
|  |     icon: LucideIcon | ||||||
|  |   }[] | ||||||
|  | } & React.ComponentPropsWithoutRef<typeof SidebarGroup>) { | ||||||
|  |   return ( | ||||||
|  |     <SidebarGroup {...props}> | ||||||
|  |       <SidebarGroupContent> | ||||||
|  |         <SidebarMenu> | ||||||
|  |           {items.map((item) => ( | ||||||
|  |             <SidebarMenuItem key={item.title}> | ||||||
|  |               <SidebarMenuButton asChild size="sm"> | ||||||
|  |                 <a href={item.url}> | ||||||
|  |                   <item.icon /> | ||||||
|  |                   <span>{item.title}</span> | ||||||
|  |                 </a> | ||||||
|  |               </SidebarMenuButton> | ||||||
|  |             </SidebarMenuItem> | ||||||
|  |           ))} | ||||||
|  |         </SidebarMenu> | ||||||
|  |       </SidebarGroupContent> | ||||||
|  |     </SidebarGroup> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,103 @@ | ||||||
|  | "use client" | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   BadgeCheck, | ||||||
|  |   ChevronsUpDown, | ||||||
|  |   LogOut, | ||||||
|  | } from "lucide-react" | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   Avatar, | ||||||
|  |   AvatarFallback, | ||||||
|  | } from "@/components/ui/avatar" | ||||||
|  | import { | ||||||
|  |   DropdownMenu, | ||||||
|  |   DropdownMenuContent, | ||||||
|  |   DropdownMenuGroup, | ||||||
|  |   DropdownMenuItem, | ||||||
|  |   DropdownMenuLabel, | ||||||
|  |   DropdownMenuSeparator, | ||||||
|  |   DropdownMenuTrigger, | ||||||
|  | } from "@/components/ui/dropdown-menu" | ||||||
|  | import { | ||||||
|  |   SidebarMenu, | ||||||
|  |   SidebarMenuButton, | ||||||
|  |   SidebarMenuItem, | ||||||
|  |   useSidebar, | ||||||
|  | } from "@/components/ui/sidebar" | ||||||
|  | import {User} from "@/types"; | ||||||
|  | import {Link} from "@inertiajs/react"; | ||||||
|  | 
 | ||||||
|  | export function NavUser({ | ||||||
|  |   user, | ||||||
|  | }: { | ||||||
|  |   user: User | ||||||
|  | }) { | ||||||
|  |   const { isMobile } = useSidebar() | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <SidebarMenu> | ||||||
|  |       <SidebarMenuItem> | ||||||
|  |         <DropdownMenu> | ||||||
|  |           <DropdownMenuTrigger asChild> | ||||||
|  |             <SidebarMenuButton | ||||||
|  |               size="lg" | ||||||
|  |               className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" | ||||||
|  |             > | ||||||
|  |               <Avatar className="h-8 w-8 rounded-lg"> | ||||||
|  |                 {/*<AvatarImage src={user.avatar} alt={user.name} />*/} | ||||||
|  |                 <AvatarFallback className="rounded-lg">CN</AvatarFallback> | ||||||
|  |               </Avatar> | ||||||
|  |               <div className="grid flex-1 text-left text-sm leading-tight"> | ||||||
|  |                 <span className="truncate font-semibold">{user.name}</span> | ||||||
|  |                 <span className="truncate text-xs">{user.email}</span> | ||||||
|  |               </div> | ||||||
|  |               <ChevronsUpDown className="ml-auto size-4" /> | ||||||
|  |             </SidebarMenuButton> | ||||||
|  |           </DropdownMenuTrigger> | ||||||
|  |           <DropdownMenuContent | ||||||
|  |             className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg" | ||||||
|  |             side={isMobile ? "bottom" : "right"} | ||||||
|  |             align="end" | ||||||
|  |             sideOffset={4} | ||||||
|  |           > | ||||||
|  |             <DropdownMenuLabel className="p-0 font-normal"> | ||||||
|  |               <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> | ||||||
|  |                 <Avatar className="h-8 w-8 rounded-lg"> | ||||||
|  |                   {/*<AvatarImage src={user.avatar} alt={user.name} />*/} | ||||||
|  |                   <AvatarFallback className="rounded-lg">AC</AvatarFallback> | ||||||
|  |                 </Avatar> | ||||||
|  |                 <div className="grid flex-1 text-left text-sm leading-tight"> | ||||||
|  |                   <span className="truncate font-semibold">{user.name}</span> | ||||||
|  |                   <span className="truncate text-xs">{user.email}</span> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |             </DropdownMenuLabel> | ||||||
|  |             <DropdownMenuSeparator /> | ||||||
|  |             <DropdownMenuGroup> | ||||||
|  |               <DropdownMenuItem asChild> | ||||||
|  |                 <Link href={route('profile.edit')}> | ||||||
|  |                   <BadgeCheck /> | ||||||
|  |                   Edit Profile | ||||||
|  |                 </Link> | ||||||
|  |               </DropdownMenuItem> | ||||||
|  |             </DropdownMenuGroup> | ||||||
|  |             <DropdownMenuSeparator /> | ||||||
|  | 
 | ||||||
|  |             <DropdownMenuItem asChild> | ||||||
|  |               <Link | ||||||
|  |                   className="w-full" | ||||||
|  |                   href={route("logout")} | ||||||
|  |                   method={"post"} | ||||||
|  |                   as={"button"} | ||||||
|  |               > | ||||||
|  |                 <LogOut /> | ||||||
|  |                 Log out | ||||||
|  |               </Link> | ||||||
|  |             </DropdownMenuItem> | ||||||
|  |           </DropdownMenuContent> | ||||||
|  |         </DropdownMenu> | ||||||
|  |       </SidebarMenuItem> | ||||||
|  |     </SidebarMenu> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | @ -4,7 +4,7 @@ import * as React from "react" | ||||||
| import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" | import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" | ||||||
| 
 | 
 | ||||||
| import { cn } from "@/lib/utils" | import { cn } from "@/lib/utils" | ||||||
| import { buttonVariants } from "@/Components/ui/button" | import { buttonVariants } from "@/components/ui/button" | ||||||
| import {VariantProps} from "class-variance-authority"; | import {VariantProps} from "class-variance-authority"; | ||||||
| 
 | 
 | ||||||
| const AlertDialog = AlertDialogPrimitive.Root | const AlertDialog = AlertDialogPrimitive.Root | ||||||
|  | @ -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 "@/lib/utils" | ||||||
|  | 
 | ||||||
|  | const Breadcrumb = React.forwardRef< | ||||||
|  |   HTMLElement, | ||||||
|  |   React.ComponentPropsWithoutRef<"nav"> & { | ||||||
|  |     separator?: React.ReactNode | ||||||
|  |   } | ||||||
|  | >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />) | ||||||
|  | Breadcrumb.displayName = "Breadcrumb" | ||||||
|  | 
 | ||||||
|  | const BreadcrumbList = React.forwardRef< | ||||||
|  |   HTMLOListElement, | ||||||
|  |   React.ComponentPropsWithoutRef<"ol"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <ol | ||||||
|  |     ref={ref} | ||||||
|  |     className={cn( | ||||||
|  |       "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", | ||||||
|  |       className | ||||||
|  |     )} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | BreadcrumbList.displayName = "BreadcrumbList" | ||||||
|  | 
 | ||||||
|  | const BreadcrumbItem = React.forwardRef< | ||||||
|  |   HTMLLIElement, | ||||||
|  |   React.ComponentPropsWithoutRef<"li"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <li | ||||||
|  |     ref={ref} | ||||||
|  |     className={cn("inline-flex items-center gap-1.5", className)} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | BreadcrumbItem.displayName = "BreadcrumbItem" | ||||||
|  | 
 | ||||||
|  | const BreadcrumbLink = React.forwardRef< | ||||||
|  |   HTMLAnchorElement, | ||||||
|  |   React.ComponentPropsWithoutRef<"a"> & { | ||||||
|  |     asChild?: boolean | ||||||
|  |   } | ||||||
|  | >(({ asChild, className, ...props }, ref) => { | ||||||
|  |   const Comp = asChild ? Slot : "a" | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Comp | ||||||
|  |       ref={ref} | ||||||
|  |       className={cn("transition-colors hover:text-foreground", className)} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | BreadcrumbLink.displayName = "BreadcrumbLink" | ||||||
|  | 
 | ||||||
|  | const BreadcrumbPage = React.forwardRef< | ||||||
|  |   HTMLSpanElement, | ||||||
|  |   React.ComponentPropsWithoutRef<"span"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <span | ||||||
|  |     ref={ref} | ||||||
|  |     role="link" | ||||||
|  |     aria-disabled="true" | ||||||
|  |     aria-current="page" | ||||||
|  |     className={cn("font-normal text-foreground", className)} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | BreadcrumbPage.displayName = "BreadcrumbPage" | ||||||
|  | 
 | ||||||
|  | const BreadcrumbSeparator = ({ | ||||||
|  |   children, | ||||||
|  |   className, | ||||||
|  |   ...props | ||||||
|  | }: React.ComponentProps<"li">) => ( | ||||||
|  |   <li | ||||||
|  |     role="presentation" | ||||||
|  |     aria-hidden="true" | ||||||
|  |     className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)} | ||||||
|  |     {...props} | ||||||
|  |   > | ||||||
|  |     {children ?? <ChevronRight />} | ||||||
|  |   </li> | ||||||
|  | ) | ||||||
|  | BreadcrumbSeparator.displayName = "BreadcrumbSeparator" | ||||||
|  | 
 | ||||||
|  | const BreadcrumbEllipsis = ({ | ||||||
|  |   className, | ||||||
|  |   ...props | ||||||
|  | }: React.ComponentProps<"span">) => ( | ||||||
|  |   <span | ||||||
|  |     role="presentation" | ||||||
|  |     aria-hidden="true" | ||||||
|  |     className={cn("flex h-9 w-9 items-center justify-center", className)} | ||||||
|  |     {...props} | ||||||
|  |   > | ||||||
|  |     <MoreHorizontal className="h-4 w-4" /> | ||||||
|  |     <span className="sr-only">More</span> | ||||||
|  |   </span> | ||||||
|  | ) | ||||||
|  | BreadcrumbEllipsis.displayName = "BreadcrumbElipssis" | ||||||
|  | 
 | ||||||
|  | export { | ||||||
|  |   Breadcrumb, | ||||||
|  |   BreadcrumbList, | ||||||
|  |   BreadcrumbItem, | ||||||
|  |   BreadcrumbLink, | ||||||
|  |   BreadcrumbPage, | ||||||
|  |   BreadcrumbSeparator, | ||||||
|  |   BreadcrumbEllipsis, | ||||||
|  | } | ||||||
|  | @ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority" | ||||||
| import { cn } from "@/lib/utils" | import { cn } from "@/lib/utils" | ||||||
| 
 | 
 | ||||||
| const buttonVariants = cva( | const buttonVariants = cva( | ||||||
|   "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", |   "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", | ||||||
|   { |   { | ||||||
|     variants: { |     variants: { | ||||||
|       variant: { |       variant: { | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | "use client" | ||||||
|  | 
 | ||||||
|  | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" | ||||||
|  | 
 | ||||||
|  | const Collapsible = CollapsiblePrimitive.Root | ||||||
|  | 
 | ||||||
|  | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger | ||||||
|  | 
 | ||||||
|  | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent | ||||||
|  | 
 | ||||||
|  | export { Collapsible, CollapsibleTrigger, CollapsibleContent } | ||||||
|  | @ -83,7 +83,7 @@ const DropdownMenuItem = React.forwardRef< | ||||||
|   <DropdownMenuPrimitive.Item |   <DropdownMenuPrimitive.Item | ||||||
|     ref={ref} |     ref={ref} | ||||||
|     className={cn( |     className={cn( | ||||||
|       "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", |       "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", | ||||||
|       inset && "pl-8", |       inset && "pl-8", | ||||||
|       className |       className | ||||||
|     )} |     )} | ||||||
|  | @ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>( | ||||||
|       <input |       <input | ||||||
|         type={type} |         type={type} | ||||||
|         className={cn( |         className={cn( | ||||||
|           "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", |           "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", | ||||||
|           className |           className | ||||||
|         )} |         )} | ||||||
|         ref={ref} |         ref={ref} | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | "use client" | ||||||
|  | 
 | ||||||
|  | import * as React from "react" | ||||||
|  | import * as SeparatorPrimitive from "@radix-ui/react-separator" | ||||||
|  | 
 | ||||||
|  | import { cn } from "@/lib/utils" | ||||||
|  | 
 | ||||||
|  | const Separator = React.forwardRef< | ||||||
|  |   React.ElementRef<typeof SeparatorPrimitive.Root>, | ||||||
|  |   React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> | ||||||
|  | >( | ||||||
|  |   ( | ||||||
|  |     { className, orientation = "horizontal", decorative = true, ...props }, | ||||||
|  |     ref | ||||||
|  |   ) => ( | ||||||
|  |     <SeparatorPrimitive.Root | ||||||
|  |       ref={ref} | ||||||
|  |       decorative={decorative} | ||||||
|  |       orientation={orientation} | ||||||
|  |       className={cn( | ||||||
|  |         "shrink-0 bg-border", | ||||||
|  |         orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | ) | ||||||
|  | Separator.displayName = SeparatorPrimitive.Root.displayName | ||||||
|  | 
 | ||||||
|  | export { Separator } | ||||||
|  | @ -0,0 +1,764 @@ | ||||||
|  | "use client" | ||||||
|  | 
 | ||||||
|  | import * as React from "react" | ||||||
|  | import { Slot } from "@radix-ui/react-slot" | ||||||
|  | import { VariantProps, cva } from "class-variance-authority" | ||||||
|  | import { PanelLeft } from "lucide-react" | ||||||
|  | 
 | ||||||
|  | import { useIsMobile } from "@/hooks/use-mobile" | ||||||
|  | import { cn } from "@/lib/utils" | ||||||
|  | import { Button } from "@/components/ui/button" | ||||||
|  | import { Input } from "@/components/ui/input" | ||||||
|  | import { Separator } from "@/components/ui/separator" | ||||||
|  | import { Sheet, SheetContent } from "@/components/ui/sheet" | ||||||
|  | import { Skeleton } from "@/components/ui/skeleton" | ||||||
|  | import { | ||||||
|  |   Tooltip, | ||||||
|  |   TooltipContent, | ||||||
|  |   TooltipProvider, | ||||||
|  |   TooltipTrigger, | ||||||
|  | } from "@/components/ui/tooltip" | ||||||
|  | 
 | ||||||
|  | const SIDEBAR_COOKIE_NAME = "sidebar:state" | ||||||
|  | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 | ||||||
|  | const SIDEBAR_WIDTH = "16rem" | ||||||
|  | const SIDEBAR_WIDTH_MOBILE = "18rem" | ||||||
|  | const SIDEBAR_WIDTH_ICON = "3rem" | ||||||
|  | const SIDEBAR_KEYBOARD_SHORTCUT = "b" | ||||||
|  | 
 | ||||||
|  | type SidebarContext = { | ||||||
|  |   state: "expanded" | "collapsed" | ||||||
|  |   open: boolean | ||||||
|  |   setOpen: (open: boolean) => void | ||||||
|  |   openMobile: boolean | ||||||
|  |   setOpenMobile: (open: boolean) => void | ||||||
|  |   isMobile: boolean | ||||||
|  |   toggleSidebar: () => void | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const SidebarContext = React.createContext<SidebarContext | null>(null) | ||||||
|  | 
 | ||||||
|  | function useSidebar() { | ||||||
|  |   const context = React.useContext(SidebarContext) | ||||||
|  |   if (!context) { | ||||||
|  |     throw new Error("useSidebar must be used within a Sidebar.") | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return context | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const SidebarProvider = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> & { | ||||||
|  |     defaultOpen?: boolean | ||||||
|  |     open?: boolean | ||||||
|  |     onOpenChange?: (open: boolean) => void | ||||||
|  |   } | ||||||
|  | >( | ||||||
|  |   ( | ||||||
|  |     { | ||||||
|  |       defaultOpen = true, | ||||||
|  |       open: openProp, | ||||||
|  |       onOpenChange: setOpenProp, | ||||||
|  |       className, | ||||||
|  |       style, | ||||||
|  |       children, | ||||||
|  |       ...props | ||||||
|  |     }, | ||||||
|  |     ref | ||||||
|  |   ) => { | ||||||
|  |     const isMobile = useIsMobile() | ||||||
|  |     const [openMobile, setOpenMobile] = React.useState(false) | ||||||
|  | 
 | ||||||
|  |     // This is the internal state of the sidebar.
 | ||||||
|  |     // We use openProp and setOpenProp for control from outside the component.
 | ||||||
|  |     const [_open, _setOpen] = React.useState(defaultOpen) | ||||||
|  |     const open = openProp ?? _open | ||||||
|  |     const setOpen = React.useCallback( | ||||||
|  |       (value: boolean | ((value: boolean) => boolean)) => { | ||||||
|  |         if (setOpenProp) { | ||||||
|  |           return setOpenProp?.( | ||||||
|  |             typeof value === "function" ? value(open) : value | ||||||
|  |           ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         _setOpen(value) | ||||||
|  | 
 | ||||||
|  |         // This sets the cookie to keep the sidebar state.
 | ||||||
|  |         document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` | ||||||
|  |       }, | ||||||
|  |       [setOpenProp, open] | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     // Helper to toggle the sidebar.
 | ||||||
|  |     const toggleSidebar = React.useCallback(() => { | ||||||
|  |       return isMobile | ||||||
|  |         ? setOpenMobile((open) => !open) | ||||||
|  |         : setOpen((open) => !open) | ||||||
|  |     }, [isMobile, setOpen, setOpenMobile]) | ||||||
|  | 
 | ||||||
|  |     // Adds a keyboard shortcut to toggle the sidebar.
 | ||||||
|  |     React.useEffect(() => { | ||||||
|  |       const handleKeyDown = (event: KeyboardEvent) => { | ||||||
|  |         if ( | ||||||
|  |           event.key === SIDEBAR_KEYBOARD_SHORTCUT && | ||||||
|  |           (event.metaKey || event.ctrlKey) | ||||||
|  |         ) { | ||||||
|  |           event.preventDefault() | ||||||
|  |           toggleSidebar() | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       window.addEventListener("keydown", handleKeyDown) | ||||||
|  |       return () => window.removeEventListener("keydown", handleKeyDown) | ||||||
|  |     }, [toggleSidebar]) | ||||||
|  | 
 | ||||||
|  |     // We add a state so that we can do data-state="expanded" or "collapsed".
 | ||||||
|  |     // This makes it easier to style the sidebar with Tailwind classes.
 | ||||||
|  |     const state = open ? "expanded" : "collapsed" | ||||||
|  | 
 | ||||||
|  |     const contextValue = React.useMemo<SidebarContext>( | ||||||
|  |       () => ({ | ||||||
|  |         state, | ||||||
|  |         open, | ||||||
|  |         setOpen, | ||||||
|  |         isMobile, | ||||||
|  |         openMobile, | ||||||
|  |         setOpenMobile, | ||||||
|  |         toggleSidebar, | ||||||
|  |       }), | ||||||
|  |       [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |       <SidebarContext.Provider value={contextValue}> | ||||||
|  |         <TooltipProvider delayDuration={0}> | ||||||
|  |           <div | ||||||
|  |             style={ | ||||||
|  |               { | ||||||
|  |                 "--sidebar-width": SIDEBAR_WIDTH, | ||||||
|  |                 "--sidebar-width-icon": SIDEBAR_WIDTH_ICON, | ||||||
|  |                 ...style, | ||||||
|  |               } as React.CSSProperties | ||||||
|  |             } | ||||||
|  |             className={cn( | ||||||
|  |               "group/sidebar-wrapper flex min-h-svh w-full text-sidebar-foreground has-[[data-variant=inset]]:bg-sidebar", | ||||||
|  |               className | ||||||
|  |             )} | ||||||
|  |             ref={ref} | ||||||
|  |             {...props} | ||||||
|  |           > | ||||||
|  |             {children} | ||||||
|  |           </div> | ||||||
|  |         </TooltipProvider> | ||||||
|  |       </SidebarContext.Provider> | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  | ) | ||||||
|  | SidebarProvider.displayName = "SidebarProvider" | ||||||
|  | 
 | ||||||
|  | const Sidebar = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> & { | ||||||
|  |     side?: "left" | "right" | ||||||
|  |     variant?: "sidebar" | "floating" | "inset" | ||||||
|  |     collapsible?: "offcanvas" | "icon" | "none" | ||||||
|  |   } | ||||||
|  | >( | ||||||
|  |   ( | ||||||
|  |     { | ||||||
|  |       side = "left", | ||||||
|  |       variant = "sidebar", | ||||||
|  |       collapsible = "offcanvas", | ||||||
|  |       className, | ||||||
|  |       children, | ||||||
|  |       ...props | ||||||
|  |     }, | ||||||
|  |     ref | ||||||
|  |   ) => { | ||||||
|  |     const { isMobile, state, openMobile, setOpenMobile } = useSidebar() | ||||||
|  | 
 | ||||||
|  |     if (collapsible === "none") { | ||||||
|  |       return ( | ||||||
|  |         <div | ||||||
|  |           className={cn( | ||||||
|  |             "flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground", | ||||||
|  |             className | ||||||
|  |           )} | ||||||
|  |           ref={ref} | ||||||
|  |           {...props} | ||||||
|  |         > | ||||||
|  |           {children} | ||||||
|  |         </div> | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (isMobile) { | ||||||
|  |       return ( | ||||||
|  |         <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}> | ||||||
|  |           <SheetContent | ||||||
|  |             data-sidebar="sidebar" | ||||||
|  |             data-mobile="true" | ||||||
|  |             className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" | ||||||
|  |             style={ | ||||||
|  |               { | ||||||
|  |                 "--sidebar-width": SIDEBAR_WIDTH_MOBILE, | ||||||
|  |               } as React.CSSProperties | ||||||
|  |             } | ||||||
|  |             side={side} | ||||||
|  |           > | ||||||
|  |             <div className="flex h-full w-full flex-col">{children}</div> | ||||||
|  |           </SheetContent> | ||||||
|  |         </Sheet> | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |       <div | ||||||
|  |         ref={ref} | ||||||
|  |         className="group peer hidden md:block" | ||||||
|  |         data-state={state} | ||||||
|  |         data-collapsible={state === "collapsed" ? collapsible : ""} | ||||||
|  |         data-variant={variant} | ||||||
|  |         data-side={side} | ||||||
|  |       > | ||||||
|  |         {/* This is what handles the sidebar gap on desktop */} | ||||||
|  |         <div | ||||||
|  |           className={cn( | ||||||
|  |             "duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear", | ||||||
|  |             "group-data-[collapsible=offcanvas]:w-0", | ||||||
|  |             "group-data-[side=right]:rotate-180", | ||||||
|  |             variant === "floating" || variant === "inset" | ||||||
|  |               ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]" | ||||||
|  |               : "group-data-[collapsible=icon]:w-[--sidebar-width-icon]" | ||||||
|  |           )} | ||||||
|  |         /> | ||||||
|  |         <div | ||||||
|  |           className={cn( | ||||||
|  |             "duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex", | ||||||
|  |             side === "left" | ||||||
|  |               ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" | ||||||
|  |               : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", | ||||||
|  |             // Adjust the padding for floating and inset variants.
 | ||||||
|  |             variant === "floating" || variant === "inset" | ||||||
|  |               ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]" | ||||||
|  |               : "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l", | ||||||
|  |             className | ||||||
|  |           )} | ||||||
|  |           {...props} | ||||||
|  |         > | ||||||
|  |           <div | ||||||
|  |             data-sidebar="sidebar" | ||||||
|  |             className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow" | ||||||
|  |           > | ||||||
|  |             {children} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  | ) | ||||||
|  | Sidebar.displayName = "Sidebar" | ||||||
|  | 
 | ||||||
|  | const SidebarTrigger = React.forwardRef< | ||||||
|  |   React.ElementRef<typeof Button>, | ||||||
|  |   React.ComponentProps<typeof Button> | ||||||
|  | >(({ className, onClick, ...props }, ref) => { | ||||||
|  |   const { toggleSidebar } = useSidebar() | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Button | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="trigger" | ||||||
|  |       variant="ghost" | ||||||
|  |       size="icon" | ||||||
|  |       className={cn("h-7 w-7", className)} | ||||||
|  |       onClick={(event) => { | ||||||
|  |         onClick?.(event) | ||||||
|  |         toggleSidebar() | ||||||
|  |       }} | ||||||
|  |       {...props} | ||||||
|  |     > | ||||||
|  |       <PanelLeft /> | ||||||
|  |       <span className="sr-only">Toggle Sidebar</span> | ||||||
|  |     </Button> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarTrigger.displayName = "SidebarTrigger" | ||||||
|  | 
 | ||||||
|  | const SidebarRail = React.forwardRef< | ||||||
|  |   HTMLButtonElement, | ||||||
|  |   React.ComponentProps<"button"> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   const { toggleSidebar } = useSidebar() | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <button | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="rail" | ||||||
|  |       aria-label="Toggle Sidebar" | ||||||
|  |       tabIndex={-1} | ||||||
|  |       onClick={toggleSidebar} | ||||||
|  |       title="Toggle Sidebar" | ||||||
|  |       className={cn( | ||||||
|  |         "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex", | ||||||
|  |         "[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize", | ||||||
|  |         "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", | ||||||
|  |         "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar", | ||||||
|  |         "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", | ||||||
|  |         "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarRail.displayName = "SidebarRail" | ||||||
|  | 
 | ||||||
|  | const SidebarInset = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"main"> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   return ( | ||||||
|  |     <main | ||||||
|  |       ref={ref} | ||||||
|  |       className={cn( | ||||||
|  |         "relative flex min-h-svh flex-1 flex-col bg-background", | ||||||
|  |         "peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarInset.displayName = "SidebarInset" | ||||||
|  | 
 | ||||||
|  | const SidebarInput = React.forwardRef< | ||||||
|  |   React.ElementRef<typeof Input>, | ||||||
|  |   React.ComponentProps<typeof Input> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   return ( | ||||||
|  |     <Input | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="input" | ||||||
|  |       className={cn( | ||||||
|  |         "h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarInput.displayName = "SidebarInput" | ||||||
|  | 
 | ||||||
|  | const SidebarHeader = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="header" | ||||||
|  |       className={cn("flex flex-col gap-2 p-2", className)} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarHeader.displayName = "SidebarHeader" | ||||||
|  | 
 | ||||||
|  | const SidebarFooter = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="footer" | ||||||
|  |       className={cn("flex flex-col gap-2 p-2", className)} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarFooter.displayName = "SidebarFooter" | ||||||
|  | 
 | ||||||
|  | const SidebarSeparator = React.forwardRef< | ||||||
|  |   React.ElementRef<typeof Separator>, | ||||||
|  |   React.ComponentProps<typeof Separator> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   return ( | ||||||
|  |     <Separator | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="separator" | ||||||
|  |       className={cn("mx-2 w-auto bg-sidebar-border", className)} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarSeparator.displayName = "SidebarSeparator" | ||||||
|  | 
 | ||||||
|  | const SidebarContent = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="content" | ||||||
|  |       className={cn( | ||||||
|  |         "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarContent.displayName = "SidebarContent" | ||||||
|  | 
 | ||||||
|  | const SidebarGroup = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> | ||||||
|  | >(({ className, ...props }, ref) => { | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="group" | ||||||
|  |       className={cn("relative flex w-full min-w-0 flex-col p-2", className)} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarGroup.displayName = "SidebarGroup" | ||||||
|  | 
 | ||||||
|  | const SidebarGroupLabel = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> & { asChild?: boolean } | ||||||
|  | >(({ className, asChild = false, ...props }, ref) => { | ||||||
|  |   const Comp = asChild ? Slot : "div" | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Comp | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="group-label" | ||||||
|  |       className={cn( | ||||||
|  |         "duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", | ||||||
|  |         "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarGroupLabel.displayName = "SidebarGroupLabel" | ||||||
|  | 
 | ||||||
|  | const SidebarGroupAction = React.forwardRef< | ||||||
|  |   HTMLButtonElement, | ||||||
|  |   React.ComponentProps<"button"> & { asChild?: boolean } | ||||||
|  | >(({ className, asChild = false, ...props }, ref) => { | ||||||
|  |   const Comp = asChild ? Slot : "button" | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Comp | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="group-action" | ||||||
|  |       className={cn( | ||||||
|  |         "absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", | ||||||
|  |         // Increases the hit area of the button on mobile.
 | ||||||
|  |         "after:absolute after:-inset-2 after:md:hidden", | ||||||
|  |         "group-data-[collapsible=icon]:hidden", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarGroupAction.displayName = "SidebarGroupAction" | ||||||
|  | 
 | ||||||
|  | const SidebarGroupContent = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <div | ||||||
|  |     ref={ref} | ||||||
|  |     data-sidebar="group-content" | ||||||
|  |     className={cn("w-full text-sm", className)} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | SidebarGroupContent.displayName = "SidebarGroupContent" | ||||||
|  | 
 | ||||||
|  | const SidebarMenu = React.forwardRef< | ||||||
|  |   HTMLUListElement, | ||||||
|  |   React.ComponentProps<"ul"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <ul | ||||||
|  |     ref={ref} | ||||||
|  |     data-sidebar="menu" | ||||||
|  |     className={cn("flex w-full min-w-0 flex-col gap-1", className)} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | SidebarMenu.displayName = "SidebarMenu" | ||||||
|  | 
 | ||||||
|  | const SidebarMenuItem = React.forwardRef< | ||||||
|  |   HTMLLIElement, | ||||||
|  |   React.ComponentProps<"li"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <li | ||||||
|  |     ref={ref} | ||||||
|  |     data-sidebar="menu-item" | ||||||
|  |     className={cn("group/menu-item relative", className)} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | SidebarMenuItem.displayName = "SidebarMenuItem" | ||||||
|  | 
 | ||||||
|  | const sidebarMenuButtonVariants = cva( | ||||||
|  |   "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", | ||||||
|  |   { | ||||||
|  |     variants: { | ||||||
|  |       variant: { | ||||||
|  |         default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", | ||||||
|  |         outline: | ||||||
|  |           "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", | ||||||
|  |       }, | ||||||
|  |       size: { | ||||||
|  |         default: "h-8 text-sm", | ||||||
|  |         sm: "h-7 text-xs", | ||||||
|  |         lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     defaultVariants: { | ||||||
|  |       variant: "default", | ||||||
|  |       size: "default", | ||||||
|  |     }, | ||||||
|  |   } | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const SidebarMenuButton = React.forwardRef< | ||||||
|  |   HTMLButtonElement, | ||||||
|  |   React.ComponentProps<"button"> & { | ||||||
|  |     asChild?: boolean | ||||||
|  |     isActive?: boolean | ||||||
|  |     tooltip?: string | React.ComponentProps<typeof TooltipContent> | ||||||
|  |   } & VariantProps<typeof sidebarMenuButtonVariants> | ||||||
|  | >( | ||||||
|  |   ( | ||||||
|  |     { | ||||||
|  |       asChild = false, | ||||||
|  |       isActive = false, | ||||||
|  |       variant = "default", | ||||||
|  |       size = "default", | ||||||
|  |       tooltip, | ||||||
|  |       className, | ||||||
|  |       ...props | ||||||
|  |     }, | ||||||
|  |     ref | ||||||
|  |   ) => { | ||||||
|  |     const Comp = asChild ? Slot : "button" | ||||||
|  |     const { isMobile, state } = useSidebar() | ||||||
|  | 
 | ||||||
|  |     const button = ( | ||||||
|  |       <Comp | ||||||
|  |         ref={ref} | ||||||
|  |         data-sidebar="menu-button" | ||||||
|  |         data-size={size} | ||||||
|  |         data-active={isActive} | ||||||
|  |         className={cn(sidebarMenuButtonVariants({ variant, size }), className)} | ||||||
|  |         {...props} | ||||||
|  |       /> | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     if (!tooltip) { | ||||||
|  |       return button | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (typeof tooltip === "string") { | ||||||
|  |       tooltip = { | ||||||
|  |         children: tooltip, | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |       <Tooltip> | ||||||
|  |         <TooltipTrigger asChild>{button}</TooltipTrigger> | ||||||
|  |         <TooltipContent | ||||||
|  |           side="right" | ||||||
|  |           align="center" | ||||||
|  |           hidden={state !== "collapsed" || isMobile} | ||||||
|  |           {...tooltip} | ||||||
|  |         /> | ||||||
|  |       </Tooltip> | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  | ) | ||||||
|  | SidebarMenuButton.displayName = "SidebarMenuButton" | ||||||
|  | 
 | ||||||
|  | const SidebarMenuAction = React.forwardRef< | ||||||
|  |   HTMLButtonElement, | ||||||
|  |   React.ComponentProps<"button"> & { | ||||||
|  |     asChild?: boolean | ||||||
|  |     showOnHover?: boolean | ||||||
|  |   } | ||||||
|  | >(({ className, asChild = false, showOnHover = false, ...props }, ref) => { | ||||||
|  |   const Comp = asChild ? Slot : "button" | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Comp | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="menu-action" | ||||||
|  |       className={cn( | ||||||
|  |         "absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0", | ||||||
|  |         // Increases the hit area of the button on mobile.
 | ||||||
|  |         "after:absolute after:-inset-2 after:md:hidden", | ||||||
|  |         "peer-data-[size=sm]/menu-button:top-1", | ||||||
|  |         "peer-data-[size=default]/menu-button:top-1.5", | ||||||
|  |         "peer-data-[size=lg]/menu-button:top-2.5", | ||||||
|  |         "group-data-[collapsible=icon]:hidden", | ||||||
|  |         showOnHover && | ||||||
|  |           "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarMenuAction.displayName = "SidebarMenuAction" | ||||||
|  | 
 | ||||||
|  | const SidebarMenuBadge = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <div | ||||||
|  |     ref={ref} | ||||||
|  |     data-sidebar="menu-badge" | ||||||
|  |     className={cn( | ||||||
|  |       "absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none", | ||||||
|  |       "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", | ||||||
|  |       "peer-data-[size=sm]/menu-button:top-1", | ||||||
|  |       "peer-data-[size=default]/menu-button:top-1.5", | ||||||
|  |       "peer-data-[size=lg]/menu-button:top-2.5", | ||||||
|  |       "group-data-[collapsible=icon]:hidden", | ||||||
|  |       className | ||||||
|  |     )} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | SidebarMenuBadge.displayName = "SidebarMenuBadge" | ||||||
|  | 
 | ||||||
|  | const SidebarMenuSkeleton = React.forwardRef< | ||||||
|  |   HTMLDivElement, | ||||||
|  |   React.ComponentProps<"div"> & { | ||||||
|  |     showIcon?: boolean | ||||||
|  |   } | ||||||
|  | >(({ className, showIcon = false, ...props }, ref) => { | ||||||
|  |   // Random width between 50 to 90%.
 | ||||||
|  |   const width = React.useMemo(() => { | ||||||
|  |     return `${Math.floor(Math.random() * 40) + 50}%` | ||||||
|  |   }, []) | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="menu-skeleton" | ||||||
|  |       className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)} | ||||||
|  |       {...props} | ||||||
|  |     > | ||||||
|  |       {showIcon && ( | ||||||
|  |         <Skeleton | ||||||
|  |           className="size-4 rounded-md" | ||||||
|  |           data-sidebar="menu-skeleton-icon" | ||||||
|  |         /> | ||||||
|  |       )} | ||||||
|  |       <Skeleton | ||||||
|  |         className="h-4 flex-1 max-w-[--skeleton-width]" | ||||||
|  |         data-sidebar="menu-skeleton-text" | ||||||
|  |         style={ | ||||||
|  |           { | ||||||
|  |             "--skeleton-width": width, | ||||||
|  |           } as React.CSSProperties | ||||||
|  |         } | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton" | ||||||
|  | 
 | ||||||
|  | const SidebarMenuSub = React.forwardRef< | ||||||
|  |   HTMLUListElement, | ||||||
|  |   React.ComponentProps<"ul"> | ||||||
|  | >(({ className, ...props }, ref) => ( | ||||||
|  |   <ul | ||||||
|  |     ref={ref} | ||||||
|  |     data-sidebar="menu-sub" | ||||||
|  |     className={cn( | ||||||
|  |       "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5", | ||||||
|  |       "group-data-[collapsible=icon]:hidden", | ||||||
|  |       className | ||||||
|  |     )} | ||||||
|  |     {...props} | ||||||
|  |   /> | ||||||
|  | )) | ||||||
|  | SidebarMenuSub.displayName = "SidebarMenuSub" | ||||||
|  | 
 | ||||||
|  | const SidebarMenuSubItem = React.forwardRef< | ||||||
|  |   HTMLLIElement, | ||||||
|  |   React.ComponentProps<"li"> | ||||||
|  | >(({ ...props }, ref) => <li ref={ref} {...props} />) | ||||||
|  | SidebarMenuSubItem.displayName = "SidebarMenuSubItem" | ||||||
|  | 
 | ||||||
|  | const SidebarMenuSubButton = React.forwardRef< | ||||||
|  |   HTMLAnchorElement, | ||||||
|  |   React.ComponentProps<"a"> & { | ||||||
|  |     asChild?: boolean | ||||||
|  |     size?: "sm" | "md" | ||||||
|  |     isActive?: boolean | ||||||
|  |   } | ||||||
|  | >(({ asChild = false, size = "md", isActive, className, ...props }, ref) => { | ||||||
|  |   const Comp = asChild ? Slot : "a" | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Comp | ||||||
|  |       ref={ref} | ||||||
|  |       data-sidebar="menu-sub-button" | ||||||
|  |       data-size={size} | ||||||
|  |       data-active={isActive} | ||||||
|  |       className={cn( | ||||||
|  |         "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", | ||||||
|  |         "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", | ||||||
|  |         size === "sm" && "text-xs", | ||||||
|  |         size === "md" && "text-sm", | ||||||
|  |         "group-data-[collapsible=icon]:hidden", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  | SidebarMenuSubButton.displayName = "SidebarMenuSubButton" | ||||||
|  | 
 | ||||||
|  | export { | ||||||
|  |   Sidebar, | ||||||
|  |   SidebarContent, | ||||||
|  |   SidebarFooter, | ||||||
|  |   SidebarGroup, | ||||||
|  |   SidebarGroupAction, | ||||||
|  |   SidebarGroupContent, | ||||||
|  |   SidebarGroupLabel, | ||||||
|  |   SidebarHeader, | ||||||
|  |   SidebarInput, | ||||||
|  |   SidebarInset, | ||||||
|  |   SidebarMenu, | ||||||
|  |   SidebarMenuAction, | ||||||
|  |   SidebarMenuBadge, | ||||||
|  |   SidebarMenuButton, | ||||||
|  |   SidebarMenuItem, | ||||||
|  |   SidebarMenuSkeleton, | ||||||
|  |   SidebarMenuSub, | ||||||
|  |   SidebarMenuSubButton, | ||||||
|  |   SidebarMenuSubItem, | ||||||
|  |   SidebarProvider, | ||||||
|  |   SidebarRail, | ||||||
|  |   SidebarSeparator, | ||||||
|  |   SidebarTrigger, | ||||||
|  |   useSidebar, | ||||||
|  | } | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | import { cn } from "@/lib/utils" | ||||||
|  | 
 | ||||||
|  | function Skeleton({ | ||||||
|  |   className, | ||||||
|  |   ...props | ||||||
|  | }: React.HTMLAttributes<HTMLDivElement>) { | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       className={cn("animate-pulse rounded-md bg-muted", className)} | ||||||
|  |       {...props} | ||||||
|  |     /> | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { Skeleton } | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | import * as React from "react" | ||||||
|  | 
 | ||||||
|  | const MOBILE_BREAKPOINT = 768 | ||||||
|  | 
 | ||||||
|  | export function useIsMobile() { | ||||||
|  |   const [isMobile, setIsMobile] = React.useState<boolean | undefined>(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 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | import { PropsWithChildren, ReactNode } from "react"; | ||||||
|  | import {AppSidebar} from "@/components/app-sidebar"; | ||||||
|  | import {SidebarInset, SidebarProvider, SidebarTrigger} from "@/components/ui/sidebar"; | ||||||
|  | import {Separator} from "@/components/ui/separator"; | ||||||
|  | import { | ||||||
|  |     Breadcrumb, | ||||||
|  |     BreadcrumbItem, | ||||||
|  |     BreadcrumbList, BreadcrumbPage, | ||||||
|  | } from "@/components/ui/breadcrumb"; | ||||||
|  | import AppearanceDropdown from "@/components/appearance-dropdown"; | ||||||
|  | 
 | ||||||
|  | export default function AuthenticatedLayout({ | ||||||
|  |     header, | ||||||
|  |     children | ||||||
|  | }: PropsWithChildren<{ | ||||||
|  |     header?: ReactNode; | ||||||
|  | }>) { | ||||||
|  |     return ( | ||||||
|  |         <SidebarProvider> | ||||||
|  |             <AppSidebar /> | ||||||
|  | 
 | ||||||
|  |             <SidebarInset> | ||||||
|  |                 <header className="sticky top-0 bg-background flex h-16 shrink-0 items-center gap-2 justify-between p-4 border-b md:border-none md:rounded-xl"> | ||||||
|  |                     <div className="flex items-center gap-2"> | ||||||
|  |                         <SidebarTrigger className="-ml-1" /> | ||||||
|  |                         <Separator orientation="vertical" className="mr-2 h-4" /> | ||||||
|  |                         <Breadcrumb> | ||||||
|  |                             <BreadcrumbList> | ||||||
|  |                                 <BreadcrumbItem> | ||||||
|  |                                     <BreadcrumbPage>{header}</BreadcrumbPage> | ||||||
|  |                                 </BreadcrumbItem> | ||||||
|  |                             </BreadcrumbList> | ||||||
|  |                         </Breadcrumb> | ||||||
|  |                     </div> | ||||||
|  |                     <div> | ||||||
|  |                         <AppearanceDropdown /> | ||||||
|  |                     </div> | ||||||
|  |                 </header> | ||||||
|  | 
 | ||||||
|  |                 <main className="p-4 md:pt-0 h-full"> | ||||||
|  |                     {children} | ||||||
|  |                 </main> | ||||||
|  |             </SidebarInset> | ||||||
|  |         </SidebarProvider> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import ApplicationLogo from "@/Components/ApplicationLogo"; | import ApplicationLogo from "@/components/application-logo"; | ||||||
| import { Link } from "@inertiajs/react"; | import { Link } from "@inertiajs/react"; | ||||||
| import { PropsWithChildren } from "react"; | import { PropsWithChildren } from "react"; | ||||||
| 
 | 
 | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import { FormEventHandler, useEffect } from "react"; | import { FormEventHandler, useEffect } from "react"; | ||||||
| import GuestLayout from "@/Layouts/GuestLayout"; | import GuestLayout from "@/layouts/guest-layout"; | ||||||
| import { Head, useForm } from "@inertiajs/react"; | import { Head, useForm } from "@inertiajs/react"; | ||||||
| import { | import { | ||||||
|     Card, |     Card, | ||||||
|  | @ -7,11 +7,11 @@ import { | ||||||
|     CardDescription, |     CardDescription, | ||||||
|     CardFooter, |     CardFooter, | ||||||
|     CardHeader, |     CardHeader, | ||||||
| } from "@/Components/ui/card"; | } from "@/components/ui/card"; | ||||||
| import { Label } from "@/Components/ui/label"; | import { Label } from "@/components/ui/label"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| 
 | 
 | ||||||
| export default function ConfirmPassword() { | export default function ConfirmPassword() { | ||||||
|     const { data, setData, post, processing, errors, reset } = useForm({ |     const { data, setData, post, processing, errors, reset } = useForm({ | ||||||
|  | @ -1,16 +1,16 @@ | ||||||
| import GuestLayout from "@/Layouts/GuestLayout"; | import GuestLayout from "@/layouts/guest-layout"; | ||||||
| import { Head, useForm } from "@inertiajs/react"; | import { Head, useForm } from "@inertiajs/react"; | ||||||
| import { FormEventHandler } from "react"; | import { FormEventHandler } from "react"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| import { | import { | ||||||
|     Card, |     Card, | ||||||
|     CardContent, |     CardContent, | ||||||
|     CardDescription, |     CardDescription, | ||||||
|     CardFooter, |     CardFooter, | ||||||
|     CardHeader, |     CardHeader, | ||||||
| } from "@/Components/ui/card"; | } from "@/components/ui/card"; | ||||||
| 
 | 
 | ||||||
| export default function ForgotPassword({ status }: { status?: string }) { | export default function ForgotPassword({ status }: { status?: string }) { | ||||||
|     const { data, setData, post, processing, errors } = useForm({ |     const { data, setData, post, processing, errors } = useForm({ | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import { FormEventHandler, useEffect } from "react"; | import { FormEventHandler, useEffect } from "react"; | ||||||
| import GuestLayout from "@/Layouts/GuestLayout"; | import GuestLayout from "@/layouts/guest-layout"; | ||||||
| import { Head, Link, useForm } from "@inertiajs/react"; | import { Head, Link, useForm } from "@inertiajs/react"; | ||||||
| import { | import { | ||||||
|     Card, |     Card, | ||||||
|  | @ -7,11 +7,11 @@ import { | ||||||
|     CardDescription, |     CardDescription, | ||||||
|     CardHeader, |     CardHeader, | ||||||
|     CardTitle, |     CardTitle, | ||||||
| } from "@/Components/ui/card"; | } from "@/components/ui/card"; | ||||||
| import { Label } from "@/Components/ui/label"; | import { Label } from "@/components/ui/label"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| 
 | 
 | ||||||
| export default function Login({ | export default function Login({ | ||||||
|     status, |     status, | ||||||
|  | @ -1,17 +1,17 @@ | ||||||
| import { FormEventHandler, useEffect } from "react"; | import { FormEventHandler, useEffect } from "react"; | ||||||
| import GuestLayout from "@/Layouts/GuestLayout"; | import GuestLayout from "@/layouts/guest-layout"; | ||||||
| import { Head, Link, useForm } from "@inertiajs/react"; | import { Head, Link, useForm } from "@inertiajs/react"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| import { | import { | ||||||
|     Card, |     Card, | ||||||
|     CardContent, |     CardContent, | ||||||
|     CardDescription, |     CardDescription, | ||||||
|     CardHeader, |     CardHeader, | ||||||
|     CardTitle, |     CardTitle, | ||||||
| } from "@/Components/ui/card"; | } from "@/components/ui/card"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { Label } from "@/Components/ui/label"; | import { Label } from "@/components/ui/label"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| 
 | 
 | ||||||
| export default function Register() { | export default function Register() { | ||||||
|     const { data, setData, post, processing, errors, reset } = useForm({ |     const { data, setData, post, processing, errors, reset } = useForm({ | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| import { FormEventHandler, useEffect } from "react"; | import { FormEventHandler, useEffect } from "react"; | ||||||
| import GuestLayout from "@/Layouts/GuestLayout"; | import GuestLayout from "@/layouts/guest-layout"; | ||||||
| import { Head, useForm } from "@inertiajs/react"; | import { Head, useForm } from "@inertiajs/react"; | ||||||
| import { Label } from "@/Components/ui/label"; | import { Label } from "@/components/ui/label"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| import { | import { | ||||||
|     Card, |     Card, | ||||||
|     CardContent, |     CardContent, | ||||||
|  | @ -12,7 +12,7 @@ import { | ||||||
|     CardFooter, |     CardFooter, | ||||||
|     CardHeader, |     CardHeader, | ||||||
|     CardTitle, |     CardTitle, | ||||||
| } from "@/Components/ui/card"; | } from "@/components/ui/card"; | ||||||
| 
 | 
 | ||||||
| export default function ResetPassword({ | export default function ResetPassword({ | ||||||
|     token, |     token, | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| import GuestLayout from "@/Layouts/GuestLayout"; | import GuestLayout from "@/layouts/guest-layout"; | ||||||
| import { Head, Link, useForm } from "@inertiajs/react"; | import { Head, Link, useForm } from "@inertiajs/react"; | ||||||
| import { FormEventHandler } from "react"; | import { FormEventHandler } from "react"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| import { | import { | ||||||
|     Card, |     Card, | ||||||
|     CardContent, |     CardContent, | ||||||
|     CardDescription, |     CardDescription, | ||||||
|     CardHeader, |     CardHeader, | ||||||
| } from "@/Components/ui/card"; | } from "@/components/ui/card"; | ||||||
| 
 | 
 | ||||||
| export default function VerifyEmail({ status }: { status?: string }) { | export default function VerifyEmail({ status }: { status?: string }) { | ||||||
|     const { post, processing } = useForm({}); |     const { post, processing } = useForm({}); | ||||||
|  | @ -0,0 +1,22 @@ | ||||||
|  | import AuthenticatedLayout from "@/layouts/authenticated-layout"; | ||||||
|  | import { Head } from "@inertiajs/react"; | ||||||
|  | import { PageProps } from "@/types"; | ||||||
|  | 
 | ||||||
|  | export default function Dashboard() { | ||||||
|  |     return ( | ||||||
|  |         <AuthenticatedLayout | ||||||
|  |             header="Dashboard" | ||||||
|  |         > | ||||||
|  |             <Head title="Dashboard" /> | ||||||
|  | 
 | ||||||
|  |             <div className="flex flex-1 flex-col gap-4 h-full"> | ||||||
|  |                 <div className="grid auto-rows-min gap-4 md:grid-cols-3"> | ||||||
|  |                     <div className="aspect-video rounded-xl bg-muted/50" /> | ||||||
|  |                     <div className="aspect-video rounded-xl bg-muted/50" /> | ||||||
|  |                     <div className="aspect-video rounded-xl bg-muted/50" /> | ||||||
|  |                 </div> | ||||||
|  |                 <div className="flex-1 rounded-xl bg-muted/50 h-full" /> | ||||||
|  |             </div> | ||||||
|  |         </AuthenticatedLayout> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | @ -1,28 +1,23 @@ | ||||||
| import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; | import AuthenticatedLayout from "@/layouts/authenticated-layout"; | ||||||
| import DeleteUserForm from "./Partials/DeleteUserForm"; | import DeleteUserForm from "@/pages/profile/partials/delete-user-form"; | ||||||
| import UpdatePasswordForm from "./Partials/UpdatePasswordForm"; | import UpdatePasswordForm from "@/pages/profile/partials/update-password-form"; | ||||||
| import UpdateProfileInformationForm from "./Partials/UpdateProfileInformationForm"; | import UpdateProfileInformationForm from "@/pages/profile/partials/update-profile-information-form"; | ||||||
| import { Head } from "@inertiajs/react"; | import { Head } from "@inertiajs/react"; | ||||||
| import { PageProps } from "@/types"; |  | ||||||
| import { | import { | ||||||
|     Card, |     Card, | ||||||
|     CardContent, |     CardContent, | ||||||
|     CardDescription, |     CardDescription, | ||||||
|     CardHeader, |     CardHeader, | ||||||
|     CardTitle, |     CardTitle, | ||||||
| } from "@/Components/ui/card"; | } from "@/components/ui/card"; | ||||||
| 
 | 
 | ||||||
| export default function Edit({ | export default function Edit({ | ||||||
|     auth, |  | ||||||
|     mustVerifyEmail, |     mustVerifyEmail, | ||||||
|     status, |     status, | ||||||
| }: PageProps<{ mustVerifyEmail: boolean; status?: string }>) { | }: { mustVerifyEmail: boolean; status?: string }) { | ||||||
|     return ( |     return ( | ||||||
|         <AuthenticatedLayout |         <AuthenticatedLayout | ||||||
|             user={auth.user} |             header={'Edit Profile'} | ||||||
|             header={ |  | ||||||
|                 <h2 className="font-semibold text-xl leading-tight">Profile</h2> |  | ||||||
|             } |  | ||||||
|         > |         > | ||||||
|             <Head title="Profile" /> |             <Head title="Profile" /> | ||||||
| 
 | 
 | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| import { FormEventHandler, useRef, useState } from "react"; | import { FormEventHandler, useRef, useState } from "react"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| import { useForm } from "@inertiajs/react"; | import { useForm } from "@inertiajs/react"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| import { Label } from "@/Components/ui/label"; | import { Label } from "@/components/ui/label"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { | import { | ||||||
|     AlertDialog, |     AlertDialog, | ||||||
|     AlertDialogAction, |     AlertDialogAction, | ||||||
|  | @ -14,7 +14,7 @@ import { | ||||||
|     AlertDialogHeader, |     AlertDialogHeader, | ||||||
|     AlertDialogTitle, |     AlertDialogTitle, | ||||||
|     AlertDialogTrigger, |     AlertDialogTrigger, | ||||||
| } from "@/Components/ui/alert-dialog"; | } from "@/components/ui/alert-dialog"; | ||||||
| 
 | 
 | ||||||
| export default function DeleteUserForm({ | export default function DeleteUserForm({ | ||||||
|     className = "", |     className = "", | ||||||
|  | @ -62,7 +62,7 @@ export default function DeleteUserForm({ | ||||||
|                     </Button> |                     </Button> | ||||||
|                 </AlertDialogTrigger> |                 </AlertDialogTrigger> | ||||||
| 
 | 
 | ||||||
|                 <AlertDialogContent asChild> |                 <AlertDialogContent> | ||||||
|                     <form onSubmit={deleteUser}> |                     <form onSubmit={deleteUser}> | ||||||
|                         <AlertDialogHeader> |                         <AlertDialogHeader> | ||||||
|                             <AlertDialogTitle> |                             <AlertDialogTitle> | ||||||
|  | @ -76,7 +76,7 @@ export default function DeleteUserForm({ | ||||||
|                             </AlertDialogDescription> |                             </AlertDialogDescription> | ||||||
|                         </AlertDialogHeader> |                         </AlertDialogHeader> | ||||||
| 
 | 
 | ||||||
|                         <div> |                         <div className="py-4"> | ||||||
|                             <Label htmlFor="password" className="sr-only"> |                             <Label htmlFor="password" className="sr-only"> | ||||||
|                                 Password |                                 Password | ||||||
|                             </Label> |                             </Label> | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| import { useRef, FormEventHandler } from "react"; | import { useRef, FormEventHandler } from "react"; | ||||||
| import { useForm } from "@inertiajs/react"; | import { useForm } from "@inertiajs/react"; | ||||||
| import { Transition } from "@headlessui/react"; | import { Transition } from "@headlessui/react"; | ||||||
| import { Label } from "@/Components/ui/label"; | import { Label } from "@/components/ui/label"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| 
 | 
 | ||||||
| export default function UpdatePasswordForm({ | export default function UpdatePasswordForm({ | ||||||
|     className = "", |     className = "", | ||||||
|  | @ -2,10 +2,10 @@ import { Link, useForm, usePage } from "@inertiajs/react"; | ||||||
| import { Transition } from "@headlessui/react"; | import { Transition } from "@headlessui/react"; | ||||||
| import { FormEventHandler } from "react"; | import { FormEventHandler } from "react"; | ||||||
| import { PageProps } from "@/types"; | import { PageProps } from "@/types"; | ||||||
| import { Label } from "@/Components/ui/label"; | import { Label } from "@/components/ui/label"; | ||||||
| import { Input } from "@/Components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { InputError } from "@/Components/ui/InputError"; | import { InputError } from "@/components/ui/input-error"; | ||||||
| import { Button } from "@/Components/ui/button"; | import { Button } from "@/components/ui/button"; | ||||||
| 
 | 
 | ||||||
| export default function UpdateProfileInformation({ | export default function UpdateProfileInformation({ | ||||||
|     mustVerifyEmail, |     mustVerifyEmail, | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
|         <!-- Scripts --> |         <!-- Scripts --> | ||||||
|         @routes |         @routes | ||||||
|         @viteReactRefresh |         @viteReactRefresh | ||||||
|         @vite(['resources/js/app.tsx', "resources/js/Pages/{$page['component']}.tsx"]) |         @vite(['resources/js/app.tsx', "resources/js/pages/{$page['component']}.tsx"]) | ||||||
|         @inertiaHead |         @inertiaHead | ||||||
|     </head> |     </head> | ||||||
|     <body class="font-sans antialiased"> |     <body class="font-sans antialiased"> | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ use Illuminate\Support\Facades\Route; | ||||||
| use Inertia\Inertia; | use Inertia\Inertia; | ||||||
| 
 | 
 | ||||||
| Route::get('/', function () { | Route::get('/', function () { | ||||||
|     return Inertia::render('Welcome', [ |     return Inertia::render('welcome', [ | ||||||
|         'canLogin' => Route::has('login'), |         'canLogin' => Route::has('login'), | ||||||
|         'canRegister' => Route::has('register'), |         'canRegister' => Route::has('register'), | ||||||
|         'laravelVersion' => Application::VERSION, |         'laravelVersion' => Application::VERSION, | ||||||
|  | @ -15,7 +15,7 @@ Route::get('/', function () { | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| Route::get('/dashboard', function () { | Route::get('/dashboard', function () { | ||||||
|     return Inertia::render('Dashboard'); |     return Inertia::render('dashboard'); | ||||||
| })->middleware(['auth', 'verified'])->name('dashboard'); | })->middleware(['auth', 'verified'])->name('dashboard'); | ||||||
| 
 | 
 | ||||||
| Route::middleware(['auth', 'verified'])->group(function () { | Route::middleware(['auth', 'verified'])->group(function () { | ||||||
|  |  | ||||||
|  | @ -8,68 +8,86 @@ module.exports = { | ||||||
|   prefix: "", |   prefix: "", | ||||||
|   theme: { |   theme: { | ||||||
|   	container: { |   	container: { | ||||||
|       center: true, |   		center: 'true', | ||||||
|       padding: "2rem", |   		padding: '2rem', | ||||||
|   		screens: { |   		screens: { | ||||||
|         "2xl": "1400px", |   			'2xl': '1400px' | ||||||
|       }, |   		} | ||||||
|   	}, |   	}, | ||||||
|   	extend: { |   	extend: { | ||||||
|   		colors: { |   		colors: { | ||||||
|         border: "hsl(var(--border))", |   			border: 'hsl(var(--border))', | ||||||
|         input: "hsl(var(--input))", |   			input: 'hsl(var(--input))', | ||||||
|         ring: "hsl(var(--ring))", |   			ring: 'hsl(var(--ring))', | ||||||
|         background: "hsl(var(--background))", |   			background: 'hsl(var(--background))', | ||||||
|         foreground: "hsl(var(--foreground))", |   			foreground: 'hsl(var(--foreground))', | ||||||
|   			primary: { |   			primary: { | ||||||
|           DEFAULT: "hsl(var(--primary))", |   				DEFAULT: 'hsl(var(--primary))', | ||||||
|           foreground: "hsl(var(--primary-foreground))", |   				foreground: 'hsl(var(--primary-foreground))' | ||||||
|   			}, |   			}, | ||||||
|   			secondary: { |   			secondary: { | ||||||
|           DEFAULT: "hsl(var(--secondary))", |   				DEFAULT: 'hsl(var(--secondary))', | ||||||
|           foreground: "hsl(var(--secondary-foreground))", |   				foreground: 'hsl(var(--secondary-foreground))' | ||||||
|   			}, |   			}, | ||||||
|   			destructive: { |   			destructive: { | ||||||
|           DEFAULT: "hsl(var(--destructive))", |   				DEFAULT: 'hsl(var(--destructive))', | ||||||
|           foreground: "hsl(var(--destructive-foreground))", |   				foreground: 'hsl(var(--destructive-foreground))' | ||||||
|   			}, |   			}, | ||||||
|   			muted: { |   			muted: { | ||||||
|           DEFAULT: "hsl(var(--muted))", |   				DEFAULT: 'hsl(var(--muted))', | ||||||
|           foreground: "hsl(var(--muted-foreground))", |   				foreground: 'hsl(var(--muted-foreground))' | ||||||
|   			}, |   			}, | ||||||
|   			accent: { |   			accent: { | ||||||
|           DEFAULT: "hsl(var(--accent))", |   				DEFAULT: 'hsl(var(--accent))', | ||||||
|           foreground: "hsl(var(--accent-foreground))", |   				foreground: 'hsl(var(--accent-foreground))' | ||||||
|   			}, |   			}, | ||||||
|   			popover: { |   			popover: { | ||||||
|           DEFAULT: "hsl(var(--popover))", |   				DEFAULT: 'hsl(var(--popover))', | ||||||
|           foreground: "hsl(var(--popover-foreground))", |   				foreground: 'hsl(var(--popover-foreground))' | ||||||
|   			}, |   			}, | ||||||
|   			card: { |   			card: { | ||||||
|           DEFAULT: "hsl(var(--card))", |   				DEFAULT: 'hsl(var(--card))', | ||||||
|           foreground: "hsl(var(--card-foreground))", |   				foreground: 'hsl(var(--card-foreground))' | ||||||
|   			}, |   			}, | ||||||
|  |   			sidebar: { | ||||||
|  |   				DEFAULT: 'hsl(var(--sidebar-background))', | ||||||
|  |   				foreground: 'hsl(var(--sidebar-foreground))', | ||||||
|  |   				primary: 'hsl(var(--sidebar-primary))', | ||||||
|  |   				'primary-foreground': 'hsl(var(--sidebar-primary-foreground))', | ||||||
|  |   				accent: 'hsl(var(--sidebar-accent))', | ||||||
|  |   				'accent-foreground': 'hsl(var(--sidebar-accent-foreground))', | ||||||
|  |   				border: 'hsl(var(--sidebar-border))', | ||||||
|  |   				ring: 'hsl(var(--sidebar-ring))' | ||||||
|  |   			} | ||||||
|   		}, |   		}, | ||||||
|   		borderRadius: { |   		borderRadius: { | ||||||
|         lg: "var(--radius)", |   			lg: 'var(--radius)', | ||||||
|         md: "calc(var(--radius) - 2px)", |   			md: 'calc(var(--radius) - 2px)', | ||||||
|         sm: "calc(var(--radius) - 4px)", |   			sm: 'calc(var(--radius) - 4px)' | ||||||
|   		}, |   		}, | ||||||
|   		keyframes: { |   		keyframes: { | ||||||
|         "accordion-down": { |   			'accordion-down': { | ||||||
|           from: { height: "0" }, |   				from: { | ||||||
|           to: { height: "var(--radix-accordion-content-height)" }, |   					height: '0' | ||||||
|   				}, |   				}, | ||||||
|         "accordion-up": { |   				to: { | ||||||
|           from: { height: "var(--radix-accordion-content-height)" }, |   					height: 'var(--radix-accordion-content-height)' | ||||||
|           to: { height: "0" }, |   				} | ||||||
|   			}, |   			}, | ||||||
|  |   			'accordion-up': { | ||||||
|  |   				from: { | ||||||
|  |   					height: 'var(--radix-accordion-content-height)' | ||||||
|  |   				}, | ||||||
|  |   				to: { | ||||||
|  |   					height: '0' | ||||||
|  |   				} | ||||||
|  |   			} | ||||||
|   		}, |   		}, | ||||||
|   		animation: { |   		animation: { | ||||||
|         "accordion-down": "accordion-down 0.2s ease-out", |   			'accordion-down': 'accordion-down 0.2s ease-out', | ||||||
|         "accordion-up": "accordion-up 0.2s ease-out", |   			'accordion-up': 'accordion-up 0.2s ease-out' | ||||||
|       }, |   		} | ||||||
|     }, |   	} | ||||||
|   }, |   }, | ||||||
|   plugins: [require("tailwindcss-animate")], |   plugins: [require("tailwindcss-animate")], | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue