kaze 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -1
- data/README.md +1 -1
- data/lib/kaze/commands/app/hotwire_stack.rb +2 -0
- data/lib/kaze/commands/app/inertia_react_stack.rb +5 -1
- data/lib/kaze/commands/app/inertia_vue_stack.rb +5 -1
- data/lib/kaze/commands/app.rb +9 -2
- data/lib/kaze/version.rb +1 -1
- data/stubs/default/config/routes.rb +11 -4
- data/stubs/default/test/integration/appearance_test.rb +11 -0
- data/stubs/default/test/integration/password_update_test.rb +9 -1
- data/stubs/hotwire/app/assets/tailwind/application.css +119 -1
- data/stubs/hotwire/app/components/breadcrumbs_component.rb +32 -0
- data/stubs/hotwire/app/components/danger_button_component.rb +1 -1
- data/stubs/hotwire/app/components/dropdown_component.html.erb +2 -2
- data/stubs/hotwire/app/components/dropdown_component.rb +5 -1
- data/stubs/hotwire/app/components/dropdown_link_component.rb +1 -1
- data/stubs/hotwire/app/components/heading_component.rb +19 -0
- data/stubs/hotwire/app/components/icon_component.rb +30 -0
- data/stubs/hotwire/app/components/input_error_component.rb +1 -1
- data/stubs/hotwire/app/components/input_label_component.rb +3 -3
- data/stubs/hotwire/app/components/modal_component.html.erb +2 -2
- data/stubs/hotwire/app/components/primary_button_component.rb +1 -1
- data/stubs/hotwire/app/components/secondary_button_component.rb +1 -1
- data/stubs/hotwire/app/components/sidebar_menu_button_component.rb +25 -0
- data/stubs/hotwire/app/components/text_input_component.rb +1 -1
- data/stubs/hotwire/app/components/user_avatar_component.rb +27 -0
- data/stubs/hotwire/app/controllers/application_controller.rb +1 -0
- data/stubs/hotwire/app/controllers/concerns/sidebar_state.rb +14 -0
- data/stubs/hotwire/app/controllers/settings/appearance_controller.rb +7 -0
- data/stubs/hotwire/app/controllers/settings/password_controller.rb +19 -0
- data/stubs/hotwire/app/controllers/{profile_controller.rb → settings/profile_controller.rb} +3 -4
- data/stubs/hotwire/app/views/auth/login.html.erb +1 -1
- data/stubs/hotwire/app/views/dashboard/index.html.erb +6 -11
- data/stubs/hotwire/app/views/layouts/_settings.html.erb +26 -0
- data/stubs/hotwire/app/views/layouts/_sidebar.html.erb +56 -0
- data/stubs/hotwire/app/views/layouts/_sidebar_header.html.erb +11 -0
- data/stubs/hotwire/app/views/layouts/_sidebar_user.html.erb +34 -0
- data/stubs/hotwire/app/views/layouts/application.html.erb +24 -15
- data/stubs/hotwire/app/views/settings/appearance/_update_appearance_form.html.erb +28 -0
- data/stubs/hotwire/app/views/settings/appearance/edit.html.erb +10 -0
- data/stubs/hotwire/app/views/{profile/partials → settings/password}/_update_password_form.html.erb +2 -10
- data/stubs/hotwire/app/views/settings/password/edit.html.erb +10 -0
- data/stubs/hotwire/app/views/{profile/partials → settings/profile}/_confirm_user_deletion_modal.html.erb +2 -2
- data/stubs/hotwire/app/views/settings/profile/_delete_user_form.html.erb +6 -0
- data/stubs/hotwire/app/views/{profile/partials → settings/profile}/_update_profile_information_form.html.erb +4 -12
- data/stubs/hotwire/app/views/{profile → settings/profile}/destroy.turbo_stream.erb +1 -1
- data/stubs/hotwire/app/views/settings/profile/edit.html.erb +14 -0
- data/stubs/inertia-common/app/controllers/concerns/handle_inertia_requests.rb +4 -1
- data/stubs/inertia-common/app/controllers/settings/appearance_controller.rb +7 -0
- data/stubs/inertia-common/app/controllers/settings/password_controller.rb +17 -0
- data/stubs/inertia-common/app/controllers/{profile_controller.rb → settings/profile_controller.rb} +2 -2
- data/stubs/inertia-common/test/integration/appearance_test.rb +11 -0
- data/stubs/inertia-common/test/integration/password_update_test.rb +10 -2
- data/stubs/inertia-react-ts/app/assets/tailwind/application.css +120 -1
- data/stubs/inertia-react-ts/app/javascript/Components/AppLogo.tsx +17 -0
- data/stubs/inertia-react-ts/app/javascript/Components/AppShell.tsx +9 -0
- data/stubs/inertia-react-ts/app/javascript/Components/AppSidebar.tsx +65 -0
- data/stubs/inertia-react-ts/app/javascript/Components/AppSidebarHeader.tsx +14 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Breadcrumbs.tsx +43 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Heading.tsx +19 -0
- data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +1 -1
- data/stubs/inertia-react-ts/app/javascript/Components/NavFooter.tsx +32 -0
- data/stubs/inertia-react-ts/app/javascript/Components/NavMain.tsx +32 -0
- data/stubs/inertia-react-ts/app/javascript/Components/NavUser.tsx +39 -0
- data/stubs/inertia-react-ts/app/javascript/Components/UserInfo.tsx +23 -0
- data/stubs/inertia-react-ts/app/javascript/Components/UserMenuContent.tsx +57 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/avatar.tsx +51 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/breadcrumb.tsx +109 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/button.tsx +49 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/checkbox.tsx +27 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/dialog.tsx +109 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/dropdown-menu.tsx +217 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/input.tsx +21 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/label.tsx +19 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/separator.tsx +26 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/sheet.tsx +137 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/sidebar.tsx +719 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/skeleton.tsx +13 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ui/tooltip.tsx +55 -0
- data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +19 -127
- data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +1 -1
- data/stubs/inertia-react-ts/app/javascript/Layouts/SettingsLayout.tsx +57 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +6 -6
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +17 -12
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +14 -14
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +10 -10
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/VerifyEmail.tsx +2 -2
- data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +13 -8
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Appearance.tsx +33 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.tsx +107 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/UpdateAppearanceForm.tsx +41 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdatePasswordForm.tsx +12 -20
- data/stubs/inertia-react-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdateProfileInformationForm.tsx +13 -21
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Password.tsx +33 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Profile.tsx +35 -0
- data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +6 -1
- data/stubs/inertia-react-ts/app/javascript/hooks/use-initials.tsx +23 -0
- data/stubs/inertia-react-ts/app/javascript/hooks/use-mobile-navigation.tsx +11 -0
- data/stubs/inertia-react-ts/app/javascript/hooks/use-mobile.tsx +36 -0
- data/stubs/inertia-react-ts/app/javascript/lib/appearance.ts +1 -2
- data/stubs/inertia-react-ts/app/javascript/lib/utils.ts +6 -0
- data/stubs/inertia-react-ts/app/javascript/types/global.d.ts +2 -2
- data/stubs/inertia-react-ts/app/javascript/types/index.d.ts +14 -0
- data/stubs/inertia-react-ts/components.json +21 -0
- data/stubs/inertia-react-ts/package.json +13 -0
- data/stubs/inertia-vue-ts/app/assets/tailwind/application.css +120 -1
- data/stubs/inertia-vue-ts/app/javascript/Components/AppLogo.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/AppShell.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/AppSidebar.vue +65 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/AppSidebarHeader.vue +20 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Breadcrumbs.vue +32 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Heading.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +1 -1
- data/stubs/inertia-vue-ts/app/javascript/Components/NavFooter.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/NavMain.vue +32 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/NavUser.vue +40 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/UserInfo.vue +25 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/UserMenuContent.vue +47 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/Avatar.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/AvatarFallback.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/AvatarImage.vue +16 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/index.ts +3 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/Breadcrumb.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbLink.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbList.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/index.ts +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/button/Button.vue +31 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/button/index.ts +34 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/checkbox/Checkbox.vue +35 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/checkbox/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/Dialog.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogClose.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogContent.vue +52 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogDescription.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogFooter.vue +25 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogHeader.vue +14 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogOverlay.vue +26 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogTitle.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogTrigger.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/index.ts +9 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenu.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuContent.vue +37 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuTrigger.vue +14 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/index.ts +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/input/Input.vue +50 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/input/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/label/Label.vue +26 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/label/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/separator/Separator.vue +29 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/separator/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/Sheet.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetClose.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetContent.vue +62 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetDescription.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetFooter.vue +16 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetHeader.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetOverlay.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetTitle.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetTrigger.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/index.ts +8 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/Sidebar.vue +96 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarContent.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarFooter.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroup.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupAction.vue +27 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupContent.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupLabel.vue +25 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarHeader.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarInput.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarInset.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenu.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuAction.vue +35 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuBadge.vue +26 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuButton.vue +48 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuItem.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSub.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarProvider.vue +82 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarRail.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarSeparator.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarTrigger.vue +28 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/index.ts +60 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/utils.ts +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/skeleton/Skeleton.vue +17 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/skeleton/index.ts +1 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/Tooltip.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipContent.vue +34 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipProvider.vue +14 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipTrigger.vue +15 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/index.ts +4 -0
- data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +17 -147
- data/stubs/inertia-vue-ts/app/javascript/Layouts/SettingsLayout.vue +57 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +7 -7
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +10 -12
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +12 -14
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +8 -10
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/VerifyEmail.vue +3 -3
- data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +15 -10
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Appearance.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.vue +96 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/UpdateAppearanceForm.vue +42 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdatePasswordForm.vue +12 -20
- data/stubs/inertia-vue-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdateProfileInformationForm.vue +12 -20
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Password.vue +33 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Profile.vue +40 -0
- data/stubs/inertia-vue-ts/app/javascript/composables/useInitials.ts +29 -0
- data/stubs/inertia-vue-ts/app/javascript/lib/appearance.ts +1 -2
- data/stubs/inertia-vue-ts/app/javascript/lib/utils.ts +6 -0
- data/stubs/inertia-vue-ts/app/javascript/types/index.d.ts +14 -0
- data/stubs/inertia-vue-ts/app/javascript/types/vue-shims.d.ts +10 -0
- data/stubs/inertia-vue-ts/components.json +20 -0
- data/stubs/inertia-vue-ts/package.json +8 -1
- data/stubs/inertia-vue-ts/vite.config.ts +25 -1
- metadata +171 -46
- data/stubs/hotwire/app/components/nav_link_component.rb +0 -15
- data/stubs/hotwire/app/components/responsive_nav_link_component.rb +0 -15
- data/stubs/hotwire/app/controllers/password_controller.rb +0 -13
- data/stubs/hotwire/app/views/layouts/_navigation.html.erb +0 -82
- data/stubs/hotwire/app/views/profile/edit.html.erb +0 -29
- data/stubs/hotwire/app/views/profile/partials/_delete_user_form.html.erb +0 -14
- data/stubs/hotwire/app/views/profile/partials/_update_appearance_form.html.erb +0 -39
- data/stubs/inertia-common/app/controllers/password_controller.rb +0 -13
- data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +0 -11
- data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +0 -20
- data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +0 -100
- data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +0 -14
- data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +0 -66
- data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +0 -21
- data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +0 -20
- data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +0 -21
- data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +0 -22
- data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +0 -32
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +0 -37
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +0 -102
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateAppearanceForm.tsx +0 -47
- data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +0 -29
- data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +0 -7
- data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +0 -75
- data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +0 -16
- data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +0 -12
- data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +0 -109
- data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +0 -21
- data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +0 -7
- data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +0 -21
- data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +0 -19
- data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +0 -23
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +0 -43
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +0 -99
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateAppearanceForm.vue +0 -47
|
@@ -1,9 +1,128 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
2
3
|
|
|
3
4
|
/* Drive `dark:` from a `dark` class rather than prefers-color-scheme, so the
|
|
4
5
|
appearance preference can override the operating system. The layouts apply
|
|
5
6
|
the class before first paint. */
|
|
6
|
-
@custom-variant dark (&:
|
|
7
|
+
@custom-variant dark (&:is(.dark *));
|
|
8
|
+
|
|
9
|
+
@theme inline {
|
|
10
|
+
--color-background: var(--background);
|
|
11
|
+
--color-foreground: var(--foreground);
|
|
12
|
+
|
|
13
|
+
--color-card: var(--card);
|
|
14
|
+
--color-card-foreground: var(--card-foreground);
|
|
15
|
+
|
|
16
|
+
--color-popover: var(--popover);
|
|
17
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
18
|
+
|
|
19
|
+
--color-primary: var(--primary);
|
|
20
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
21
|
+
|
|
22
|
+
--color-secondary: var(--secondary);
|
|
23
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
24
|
+
|
|
25
|
+
--color-muted: var(--muted);
|
|
26
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
27
|
+
|
|
28
|
+
--color-accent: var(--accent);
|
|
29
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
30
|
+
|
|
31
|
+
--color-destructive: var(--destructive);
|
|
32
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
33
|
+
|
|
34
|
+
--color-border: var(--border);
|
|
35
|
+
--color-input: var(--input);
|
|
36
|
+
--color-ring: var(--ring);
|
|
37
|
+
|
|
38
|
+
--color-sidebar: var(--sidebar);
|
|
39
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
40
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
41
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
42
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
43
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
44
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
45
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
46
|
+
|
|
47
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
48
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
49
|
+
--radius-lg: var(--radius);
|
|
50
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
:root {
|
|
54
|
+
--radius: 0.625rem;
|
|
55
|
+
|
|
56
|
+
--background: oklch(1 0 0);
|
|
57
|
+
--foreground: oklch(0.145 0 0);
|
|
58
|
+
--card: oklch(1 0 0);
|
|
59
|
+
--card-foreground: oklch(0.145 0 0);
|
|
60
|
+
--popover: oklch(1 0 0);
|
|
61
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
62
|
+
--primary: oklch(0.205 0 0);
|
|
63
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
64
|
+
--secondary: oklch(0.97 0 0);
|
|
65
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
66
|
+
--muted: oklch(0.97 0 0);
|
|
67
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
68
|
+
--accent: oklch(0.97 0 0);
|
|
69
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
70
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
71
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
72
|
+
--border: oklch(0.922 0 0);
|
|
73
|
+
--input: oklch(0.922 0 0);
|
|
74
|
+
--ring: oklch(0.87 0 0);
|
|
75
|
+
|
|
76
|
+
--sidebar: oklch(0.985 0 0);
|
|
77
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
78
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
79
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
80
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
81
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
82
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
83
|
+
--sidebar-ring: oklch(0.87 0 0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.dark {
|
|
87
|
+
--background: oklch(0.145 0 0);
|
|
88
|
+
--foreground: oklch(0.985 0 0);
|
|
89
|
+
--card: oklch(0.145 0 0);
|
|
90
|
+
--card-foreground: oklch(0.985 0 0);
|
|
91
|
+
--popover: oklch(0.145 0 0);
|
|
92
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
93
|
+
--primary: oklch(0.985 0 0);
|
|
94
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
95
|
+
--secondary: oklch(0.269 0 0);
|
|
96
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
97
|
+
--muted: oklch(0.269 0 0);
|
|
98
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
99
|
+
--accent: oklch(0.269 0 0);
|
|
100
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
101
|
+
--destructive: oklch(0.396 0.141 25.723);
|
|
102
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
103
|
+
--border: oklch(0.269 0 0);
|
|
104
|
+
--input: oklch(0.269 0 0);
|
|
105
|
+
--ring: oklch(0.439 0 0);
|
|
106
|
+
|
|
107
|
+
--sidebar: oklch(0.205 0 0);
|
|
108
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
109
|
+
--sidebar-primary: oklch(0.985 0 0);
|
|
110
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
111
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
112
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
113
|
+
--sidebar-border: oklch(0.269 0 0);
|
|
114
|
+
--sidebar-ring: oklch(0.439 0 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@layer base {
|
|
118
|
+
* {
|
|
119
|
+
@apply border-border outline-ring/50;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
body {
|
|
123
|
+
@apply bg-background text-foreground;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
7
126
|
|
|
8
127
|
@plugin "@tailwindcss/forms";
|
|
9
128
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import ApplicationLogo from '@/Components/ApplicationLogo'
|
|
2
|
+
|
|
3
|
+
export default function AppLogo() {
|
|
4
|
+
const appName = import.meta.env.VITE_APP_NAME || 'Rails'
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<>
|
|
8
|
+
<div className="flex aspect-square size-8 items-center justify-center rounded-md bg-sidebar-primary text-sidebar-primary-foreground">
|
|
9
|
+
<ApplicationLogo className="size-5 fill-current text-white dark:text-black" />
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div className="ml-1 grid flex-1 text-left text-sm">
|
|
13
|
+
<span className="mb-0.5 truncate leading-tight font-semibold">{appName}</span>
|
|
14
|
+
</div>
|
|
15
|
+
</>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react'
|
|
2
|
+
import { usePage } from '@inertiajs/react'
|
|
3
|
+
import { SidebarProvider } from '@/Components/ui/sidebar'
|
|
4
|
+
|
|
5
|
+
export default function AppShell({ children }: PropsWithChildren) {
|
|
6
|
+
const sidebarOpen = usePage().props.sidebar_open
|
|
7
|
+
|
|
8
|
+
return <SidebarProvider defaultOpen={sidebarOpen}>{children}</SidebarProvider>
|
|
9
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Link } from '@inertiajs/react'
|
|
2
|
+
import { BookOpen, FolderGit2, LayoutGrid } from 'lucide-react'
|
|
3
|
+
import AppLogo from '@/Components/AppLogo'
|
|
4
|
+
import NavFooter from '@/Components/NavFooter'
|
|
5
|
+
import NavMain from '@/Components/NavMain'
|
|
6
|
+
import NavUser from '@/Components/NavUser'
|
|
7
|
+
import {
|
|
8
|
+
Sidebar,
|
|
9
|
+
SidebarContent,
|
|
10
|
+
SidebarFooter,
|
|
11
|
+
SidebarHeader,
|
|
12
|
+
SidebarMenu,
|
|
13
|
+
SidebarMenuButton,
|
|
14
|
+
SidebarMenuItem,
|
|
15
|
+
} from '@/Components/ui/sidebar'
|
|
16
|
+
import { dashboard_path } from '@/routes'
|
|
17
|
+
import { NavItem } from '@/types'
|
|
18
|
+
|
|
19
|
+
const mainNavItems: NavItem[] = [
|
|
20
|
+
{
|
|
21
|
+
title: 'Dashboard',
|
|
22
|
+
href: dashboard_path(),
|
|
23
|
+
icon: LayoutGrid,
|
|
24
|
+
},
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
const footerNavItems: NavItem[] = [
|
|
28
|
+
{
|
|
29
|
+
title: 'Repository',
|
|
30
|
+
href: 'https://github.com/cuonggt/kaze',
|
|
31
|
+
icon: FolderGit2,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
title: 'Documentation',
|
|
35
|
+
href: 'https://github.com/cuonggt/kaze#readme',
|
|
36
|
+
icon: BookOpen,
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
export default function AppSidebar() {
|
|
41
|
+
return (
|
|
42
|
+
<Sidebar collapsible="icon" variant="inset">
|
|
43
|
+
<SidebarHeader>
|
|
44
|
+
<SidebarMenu>
|
|
45
|
+
<SidebarMenuItem>
|
|
46
|
+
<SidebarMenuButton size="lg" asChild>
|
|
47
|
+
<Link href={dashboard_path()} prefetch>
|
|
48
|
+
<AppLogo />
|
|
49
|
+
</Link>
|
|
50
|
+
</SidebarMenuButton>
|
|
51
|
+
</SidebarMenuItem>
|
|
52
|
+
</SidebarMenu>
|
|
53
|
+
</SidebarHeader>
|
|
54
|
+
|
|
55
|
+
<SidebarContent>
|
|
56
|
+
<NavMain items={mainNavItems} />
|
|
57
|
+
</SidebarContent>
|
|
58
|
+
|
|
59
|
+
<SidebarFooter>
|
|
60
|
+
<NavFooter items={footerNavItems} className="mt-auto" />
|
|
61
|
+
<NavUser />
|
|
62
|
+
</SidebarFooter>
|
|
63
|
+
</Sidebar>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Breadcrumbs from '@/Components/Breadcrumbs'
|
|
2
|
+
import { SidebarTrigger } from '@/Components/ui/sidebar'
|
|
3
|
+
import { BreadcrumbItem } from '@/types'
|
|
4
|
+
|
|
5
|
+
export default function AppSidebarHeader({ breadcrumbs = [] }: { breadcrumbs?: BreadcrumbItem[] }) {
|
|
6
|
+
return (
|
|
7
|
+
<header className="flex h-16 shrink-0 items-center gap-2 border-b border-sidebar-border/50 px-6 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 md:px-4">
|
|
8
|
+
<div className="flex items-center gap-2">
|
|
9
|
+
<SidebarTrigger className="-ml-1" />
|
|
10
|
+
<Breadcrumbs breadcrumbs={breadcrumbs} />
|
|
11
|
+
</div>
|
|
12
|
+
</header>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Fragment } from 'react'
|
|
2
|
+
import { Link } from '@inertiajs/react'
|
|
3
|
+
import {
|
|
4
|
+
Breadcrumb,
|
|
5
|
+
BreadcrumbItem,
|
|
6
|
+
BreadcrumbLink,
|
|
7
|
+
BreadcrumbList,
|
|
8
|
+
BreadcrumbPage,
|
|
9
|
+
BreadcrumbSeparator,
|
|
10
|
+
} from '@/Components/ui/breadcrumb'
|
|
11
|
+
import { BreadcrumbItem as BreadcrumbItemType } from '@/types'
|
|
12
|
+
|
|
13
|
+
export default function Breadcrumbs({ breadcrumbs }: { breadcrumbs: BreadcrumbItemType[] }) {
|
|
14
|
+
if (breadcrumbs.length === 0) {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Breadcrumb>
|
|
20
|
+
<BreadcrumbList>
|
|
21
|
+
{breadcrumbs.map((item, index) => {
|
|
22
|
+
const isLast = index === breadcrumbs.length - 1
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Fragment key={item.href}>
|
|
26
|
+
<BreadcrumbItem>
|
|
27
|
+
{isLast ? (
|
|
28
|
+
<BreadcrumbPage>{item.title}</BreadcrumbPage>
|
|
29
|
+
) : (
|
|
30
|
+
<BreadcrumbLink asChild>
|
|
31
|
+
<Link href={item.href}>{item.title}</Link>
|
|
32
|
+
</BreadcrumbLink>
|
|
33
|
+
)}
|
|
34
|
+
</BreadcrumbItem>
|
|
35
|
+
|
|
36
|
+
{!isLast && <BreadcrumbSeparator />}
|
|
37
|
+
</Fragment>
|
|
38
|
+
)
|
|
39
|
+
})}
|
|
40
|
+
</BreadcrumbList>
|
|
41
|
+
</Breadcrumb>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default function Heading({
|
|
2
|
+
title,
|
|
3
|
+
description,
|
|
4
|
+
variant = 'default',
|
|
5
|
+
}: {
|
|
6
|
+
title: string
|
|
7
|
+
description?: string
|
|
8
|
+
variant?: 'default' | 'small'
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<header className={variant === 'small' ? '' : 'mb-8 space-y-0.5'}>
|
|
12
|
+
<h2 className={variant === 'small' ? 'mb-0.5 text-base font-medium' : 'text-xl font-semibold tracking-tight'}>
|
|
13
|
+
{title}
|
|
14
|
+
</h2>
|
|
15
|
+
|
|
16
|
+
{description && <p className="text-sm text-muted-foreground">{description}</p>}
|
|
17
|
+
</header>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -6,7 +6,7 @@ export default function InputError({
|
|
|
6
6
|
...props
|
|
7
7
|
}: HTMLAttributes<HTMLParagraphElement> & { message?: string }) {
|
|
8
8
|
return message ? (
|
|
9
|
-
<p {...props} className={`text-sm text-
|
|
9
|
+
<p {...props} className={`text-sm text-destructive ${className}`}>
|
|
10
10
|
{message}
|
|
11
11
|
</p>
|
|
12
12
|
) : null
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef } from 'react'
|
|
2
|
+
import { SidebarGroup, SidebarGroupContent, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from '@/Components/ui/sidebar'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
import { NavItem } from '@/types'
|
|
5
|
+
|
|
6
|
+
export default function NavFooter({
|
|
7
|
+
items,
|
|
8
|
+
className,
|
|
9
|
+
...props
|
|
10
|
+
}: ComponentPropsWithoutRef<typeof SidebarGroup> & { items: NavItem[] }) {
|
|
11
|
+
return (
|
|
12
|
+
<SidebarGroup {...props} className={cn('group-data-[collapsible=icon]:p-0', className)}>
|
|
13
|
+
<SidebarGroupContent>
|
|
14
|
+
<SidebarMenu>
|
|
15
|
+
{items.map((item) => (
|
|
16
|
+
<SidebarMenuItem key={item.title}>
|
|
17
|
+
<SidebarMenuButton
|
|
18
|
+
asChild
|
|
19
|
+
className="text-neutral-600 hover:text-neutral-800 dark:text-neutral-300 dark:hover:text-neutral-100"
|
|
20
|
+
>
|
|
21
|
+
<a href={item.href} target="_blank" rel="noopener noreferrer">
|
|
22
|
+
{item.icon && <item.icon className="h-5 w-5" />}
|
|
23
|
+
<span>{item.title}</span>
|
|
24
|
+
</a>
|
|
25
|
+
</SidebarMenuButton>
|
|
26
|
+
</SidebarMenuItem>
|
|
27
|
+
))}
|
|
28
|
+
</SidebarMenu>
|
|
29
|
+
</SidebarGroupContent>
|
|
30
|
+
</SidebarGroup>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Link, usePage } from '@inertiajs/react'
|
|
2
|
+
import {
|
|
3
|
+
SidebarGroup,
|
|
4
|
+
SidebarGroupLabel,
|
|
5
|
+
SidebarMenu,
|
|
6
|
+
SidebarMenuButton,
|
|
7
|
+
SidebarMenuItem,
|
|
8
|
+
} from '@/Components/ui/sidebar'
|
|
9
|
+
import { NavItem } from '@/types'
|
|
10
|
+
|
|
11
|
+
export default function NavMain({ items }: { items: NavItem[] }) {
|
|
12
|
+
const { url } = usePage()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<SidebarGroup className="px-2 py-0">
|
|
16
|
+
<SidebarGroupLabel>Platform</SidebarGroupLabel>
|
|
17
|
+
|
|
18
|
+
<SidebarMenu>
|
|
19
|
+
{items.map((item) => (
|
|
20
|
+
<SidebarMenuItem key={item.title}>
|
|
21
|
+
<SidebarMenuButton asChild isActive={url.startsWith(item.href)} tooltip={{ children: item.title }}>
|
|
22
|
+
<Link href={item.href} prefetch>
|
|
23
|
+
{item.icon && <item.icon />}
|
|
24
|
+
<span>{item.title}</span>
|
|
25
|
+
</Link>
|
|
26
|
+
</SidebarMenuButton>
|
|
27
|
+
</SidebarMenuItem>
|
|
28
|
+
))}
|
|
29
|
+
</SidebarMenu>
|
|
30
|
+
</SidebarGroup>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { usePage } from '@inertiajs/react'
|
|
2
|
+
import { ChevronsUpDown } from 'lucide-react'
|
|
3
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@/Components/ui/dropdown-menu'
|
|
4
|
+
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar } from '@/Components/ui/sidebar'
|
|
5
|
+
import UserInfo from '@/Components/UserInfo'
|
|
6
|
+
import UserMenuContent from '@/Components/UserMenuContent'
|
|
7
|
+
import { useIsMobile } from '@/hooks/use-mobile'
|
|
8
|
+
|
|
9
|
+
export default function NavUser() {
|
|
10
|
+
const user = usePage().props.auth.user
|
|
11
|
+
const { state } = useSidebar()
|
|
12
|
+
const isMobile = useIsMobile()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<SidebarMenu>
|
|
16
|
+
<SidebarMenuItem>
|
|
17
|
+
<DropdownMenu>
|
|
18
|
+
<DropdownMenuTrigger asChild>
|
|
19
|
+
<SidebarMenuButton
|
|
20
|
+
size="lg"
|
|
21
|
+
className="group text-sidebar-accent-foreground data-[state=open]:bg-sidebar-accent"
|
|
22
|
+
>
|
|
23
|
+
<UserInfo user={user} />
|
|
24
|
+
<ChevronsUpDown className="ml-auto size-4" />
|
|
25
|
+
</SidebarMenuButton>
|
|
26
|
+
</DropdownMenuTrigger>
|
|
27
|
+
|
|
28
|
+
<DropdownMenuContent
|
|
29
|
+
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
|
30
|
+
align="end"
|
|
31
|
+
side={isMobile ? 'bottom' : state === 'collapsed' ? 'left' : 'bottom'}
|
|
32
|
+
>
|
|
33
|
+
<UserMenuContent user={user} />
|
|
34
|
+
</DropdownMenuContent>
|
|
35
|
+
</DropdownMenu>
|
|
36
|
+
</SidebarMenuItem>
|
|
37
|
+
</SidebarMenu>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Avatar, AvatarFallback } from '@/Components/ui/avatar'
|
|
2
|
+
import { useInitials } from '@/hooks/use-initials'
|
|
3
|
+
import { User } from '@/types'
|
|
4
|
+
|
|
5
|
+
export default function UserInfo({ user, showEmail = false }: { user: User; showEmail?: boolean }) {
|
|
6
|
+
const getInitials = useInitials()
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<>
|
|
10
|
+
<Avatar className="h-8 w-8 overflow-hidden rounded-full">
|
|
11
|
+
<AvatarFallback className="rounded-lg bg-neutral-200 text-black dark:bg-neutral-700 dark:text-white">
|
|
12
|
+
{getInitials(user.name)}
|
|
13
|
+
</AvatarFallback>
|
|
14
|
+
</Avatar>
|
|
15
|
+
|
|
16
|
+
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
17
|
+
<span className="truncate font-medium">{user.name}</span>
|
|
18
|
+
|
|
19
|
+
{showEmail && <span className="truncate text-xs text-muted-foreground">{user.email}</span>}
|
|
20
|
+
</div>
|
|
21
|
+
</>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Link, router } from '@inertiajs/react'
|
|
2
|
+
import { LogOut, Settings } from 'lucide-react'
|
|
3
|
+
import {
|
|
4
|
+
DropdownMenuGroup,
|
|
5
|
+
DropdownMenuItem,
|
|
6
|
+
DropdownMenuLabel,
|
|
7
|
+
DropdownMenuSeparator,
|
|
8
|
+
} from '@/Components/ui/dropdown-menu'
|
|
9
|
+
import UserInfo from '@/Components/UserInfo'
|
|
10
|
+
import { useMobileNavigation } from '@/hooks/use-mobile-navigation'
|
|
11
|
+
import { logout_path, profile_edit_path } from '@/routes'
|
|
12
|
+
import { User } from '@/types'
|
|
13
|
+
|
|
14
|
+
export default function UserMenuContent({ user }: { user: User }) {
|
|
15
|
+
const cleanup = useMobileNavigation()
|
|
16
|
+
|
|
17
|
+
const handleLogout = () => {
|
|
18
|
+
cleanup()
|
|
19
|
+
router.flushAll()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<DropdownMenuLabel className="p-0 font-normal">
|
|
25
|
+
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
|
26
|
+
<UserInfo user={user} showEmail />
|
|
27
|
+
</div>
|
|
28
|
+
</DropdownMenuLabel>
|
|
29
|
+
|
|
30
|
+
<DropdownMenuSeparator />
|
|
31
|
+
|
|
32
|
+
<DropdownMenuGroup>
|
|
33
|
+
<DropdownMenuItem asChild>
|
|
34
|
+
<Link className="block w-full cursor-pointer" href={profile_edit_path()} prefetch onClick={cleanup}>
|
|
35
|
+
<Settings className="mr-2" />
|
|
36
|
+
Settings
|
|
37
|
+
</Link>
|
|
38
|
+
</DropdownMenuItem>
|
|
39
|
+
</DropdownMenuGroup>
|
|
40
|
+
|
|
41
|
+
<DropdownMenuSeparator />
|
|
42
|
+
|
|
43
|
+
<DropdownMenuItem asChild>
|
|
44
|
+
<Link
|
|
45
|
+
className="block w-full cursor-pointer"
|
|
46
|
+
href={logout_path()}
|
|
47
|
+
method="post"
|
|
48
|
+
as="button"
|
|
49
|
+
onClick={handleLogout}
|
|
50
|
+
>
|
|
51
|
+
<LogOut className="mr-2" />
|
|
52
|
+
Log out
|
|
53
|
+
</Link>
|
|
54
|
+
</DropdownMenuItem>
|
|
55
|
+
</>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
function Avatar({
|
|
7
|
+
className,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
|
10
|
+
return (
|
|
11
|
+
<AvatarPrimitive.Root
|
|
12
|
+
data-slot="avatar"
|
|
13
|
+
className={cn(
|
|
14
|
+
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function AvatarImage({
|
|
23
|
+
className,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
26
|
+
return (
|
|
27
|
+
<AvatarPrimitive.Image
|
|
28
|
+
data-slot="avatar-image"
|
|
29
|
+
className={cn("aspect-square size-full", className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function AvatarFallback({
|
|
36
|
+
className,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
39
|
+
return (
|
|
40
|
+
<AvatarPrimitive.Fallback
|
|
41
|
+
data-slot="avatar-fallback"
|
|
42
|
+
className={cn(
|
|
43
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
44
|
+
className
|
|
45
|
+
)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { Avatar, AvatarImage, AvatarFallback }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
2
|
+
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
|
8
|
+
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
12
|
+
return (
|
|
13
|
+
<ol
|
|
14
|
+
data-slot="breadcrumb-list"
|
|
15
|
+
className={cn(
|
|
16
|
+
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
25
|
+
return (
|
|
26
|
+
<li
|
|
27
|
+
data-slot="breadcrumb-item"
|
|
28
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function BreadcrumbLink({
|
|
35
|
+
asChild,
|
|
36
|
+
className,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<"a"> & {
|
|
39
|
+
asChild?: boolean
|
|
40
|
+
}) {
|
|
41
|
+
const Comp = asChild ? Slot : "a"
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Comp
|
|
45
|
+
data-slot="breadcrumb-link"
|
|
46
|
+
className={cn("hover:text-foreground transition-colors", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
53
|
+
return (
|
|
54
|
+
<span
|
|
55
|
+
data-slot="breadcrumb-page"
|
|
56
|
+
role="link"
|
|
57
|
+
aria-disabled="true"
|
|
58
|
+
aria-current="page"
|
|
59
|
+
className={cn("text-foreground font-normal", className)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function BreadcrumbSeparator({
|
|
66
|
+
children,
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: React.ComponentProps<"li">) {
|
|
70
|
+
return (
|
|
71
|
+
<li
|
|
72
|
+
data-slot="breadcrumb-separator"
|
|
73
|
+
role="presentation"
|
|
74
|
+
aria-hidden="true"
|
|
75
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
{children ?? <ChevronRight />}
|
|
79
|
+
</li>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function BreadcrumbEllipsis({
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<"span">) {
|
|
87
|
+
return (
|
|
88
|
+
<span
|
|
89
|
+
data-slot="breadcrumb-ellipsis"
|
|
90
|
+
role="presentation"
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<MoreHorizontal className="size-4" />
|
|
96
|
+
<span className="sr-only">More</span>
|
|
97
|
+
</span>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
Breadcrumb,
|
|
103
|
+
BreadcrumbList,
|
|
104
|
+
BreadcrumbItem,
|
|
105
|
+
BreadcrumbLink,
|
|
106
|
+
BreadcrumbPage,
|
|
107
|
+
BreadcrumbSeparator,
|
|
108
|
+
BreadcrumbEllipsis,
|
|
109
|
+
}
|