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
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# The initials circle beside a user's name. The Inertia stacks build this from
|
|
2
|
+
# shadcn's avatar and a useInitials hook; here it is one component.
|
|
3
|
+
class UserAvatarComponent < ViewComponent::Base
|
|
4
|
+
erb_template <<~ERB
|
|
5
|
+
<span class="<%= @classes %>"><%= @initials %></span>
|
|
6
|
+
ERB
|
|
7
|
+
|
|
8
|
+
def initialize(name, classes: nil)
|
|
9
|
+
@initials = initials(name)
|
|
10
|
+
@classes = [
|
|
11
|
+
'flex size-8 shrink-0 items-center justify-center rounded-full bg-neutral-200 text-xs',
|
|
12
|
+
'font-medium text-black dark:bg-neutral-700 dark:text-white',
|
|
13
|
+
classes
|
|
14
|
+
].compact.join(' ')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def initials(name)
|
|
19
|
+
parts = name.to_s.strip.split(/\s+/).reject(&:empty?)
|
|
20
|
+
|
|
21
|
+
case parts.length
|
|
22
|
+
when 0 then ''
|
|
23
|
+
when 1 then parts.first[0].upcase
|
|
24
|
+
else "#{parts.first[0]}#{parts.last[0]}".upcase
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module SidebarState
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
helper_method :sidebar_open?
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
# The sidebar writes this cookie as it is toggled. Reading it here keeps the
|
|
10
|
+
# first paint in step with the last choice instead of flashing open.
|
|
11
|
+
def sidebar_open?
|
|
12
|
+
cookies[:sidebar_state].nil? || cookies[:sidebar_state] == 'true'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class Settings::PasswordController < ApplicationController
|
|
2
|
+
skip_ensure_email_is_verified
|
|
3
|
+
|
|
4
|
+
def edit
|
|
5
|
+
@update_password_form = UpdatePasswordForm.new
|
|
6
|
+
|
|
7
|
+
render 'settings/password/edit'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def update
|
|
11
|
+
@update_password_form = UpdatePasswordForm.new(params.permit(:current_password, :password, :password_confirmation))
|
|
12
|
+
|
|
13
|
+
return render partial: 'settings/password/update_password_form', status: :unprocessable_entity if @update_password_form.invalid?
|
|
14
|
+
|
|
15
|
+
Current.auth.user.update(password: @update_password_form.password)
|
|
16
|
+
|
|
17
|
+
redirect_back_or_to password_edit_path, flash: { status: 'password-updated' }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
class ProfileController < ApplicationController
|
|
1
|
+
class Settings::ProfileController < ApplicationController
|
|
2
2
|
skip_ensure_email_is_verified
|
|
3
3
|
|
|
4
4
|
def edit
|
|
5
5
|
@update_profile_information_form = UpdateProfileInformationForm.new(name: Current.auth.user.name, email: Current.auth.user.email)
|
|
6
|
-
@update_password_form = UpdatePasswordForm.new
|
|
7
6
|
@delete_user_form = DeleteUserForm.new
|
|
8
7
|
|
|
9
|
-
render 'profile/edit'
|
|
8
|
+
render 'settings/profile/edit'
|
|
10
9
|
end
|
|
11
10
|
|
|
12
11
|
def update
|
|
13
12
|
@update_profile_information_form = UpdateProfileInformationForm.new(params.permit(:name, :email))
|
|
14
13
|
|
|
15
|
-
return render partial: 'profile/
|
|
14
|
+
return render partial: 'settings/profile/update_profile_information_form', status: :unprocessable_entity if @update_profile_information_form.invalid?
|
|
16
15
|
|
|
17
16
|
@update_profile_information_form.update
|
|
18
17
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<!-- Remember Me -->
|
|
17
17
|
<div class="block mt-4">
|
|
18
18
|
<label for="remember_me" class="inline-flex items-center">
|
|
19
|
-
<input id="remember_me" type="checkbox" class="
|
|
19
|
+
<input id="remember_me" type="checkbox" class="size-4 shrink-0 rounded-[4px] border border-input shadow-xs transition-shadow outline-none accent-primary focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50" name="remember">
|
|
20
20
|
<span class="ms-2 text-sm text-gray-600 dark:text-gray-400">Remember me</span>
|
|
21
21
|
</label>
|
|
22
22
|
</div>
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
<% content_for(:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
</h2>
|
|
1
|
+
<% content_for(:title, 'Dashboard') %>
|
|
2
|
+
<% content_for(:breadcrumbs) do %>
|
|
3
|
+
<%= render(BreadcrumbsComponent.new([ { title: 'Dashboard', href: dashboard_path } ])) %>
|
|
5
4
|
<% end %>
|
|
6
|
-
<div class="
|
|
7
|
-
<div class="
|
|
8
|
-
|
|
9
|
-
<div class="p-6 text-gray-900 dark:text-gray-100">
|
|
10
|
-
You're logged in!
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
5
|
+
<div class="flex h-full flex-1 flex-col gap-4 rounded-xl p-4">
|
|
6
|
+
<div class="relative min-h-[60vh] flex-1 overflow-hidden rounded-xl border border-sidebar-border/70 p-6 dark:border-sidebar-border">
|
|
7
|
+
You're logged in!
|
|
13
8
|
</div>
|
|
14
9
|
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<%# The settings shell: a heading, the sub-nav down the side, and the page's own
|
|
2
|
+
sections in the content column. %>
|
|
3
|
+
<div class="px-4 py-6">
|
|
4
|
+
<%= render(HeadingComponent.new(title: 'Settings', description: 'Manage your profile and account settings')) %>
|
|
5
|
+
<div class="flex flex-col lg:flex-row lg:space-x-12">
|
|
6
|
+
<aside class="w-full max-w-xl lg:w-48">
|
|
7
|
+
<nav class="flex flex-col space-y-1 space-x-0" aria-label="Settings">
|
|
8
|
+
<% [
|
|
9
|
+
{ title: 'Profile', href: profile_edit_path },
|
|
10
|
+
{ title: 'Password', href: password_edit_path },
|
|
11
|
+
{ title: 'Appearance', href: appearance_edit_path }
|
|
12
|
+
].each do |item| %>
|
|
13
|
+
<a href="<%= item[:href] %>" class="inline-flex h-8 w-full items-center justify-start gap-2 rounded-md px-3 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground <%= 'bg-muted' if current_page?(item[:href]) %>">
|
|
14
|
+
<%= item[:title] %>
|
|
15
|
+
</a>
|
|
16
|
+
<% end %>
|
|
17
|
+
</nav>
|
|
18
|
+
</aside>
|
|
19
|
+
<div class="my-6 h-px w-full bg-border lg:hidden"></div>
|
|
20
|
+
<div class="flex-1 md:max-w-2xl">
|
|
21
|
+
<section class="max-w-xl space-y-12">
|
|
22
|
+
<%= yield %>
|
|
23
|
+
</section>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<%# Drawer backdrop. Only ever visible below md, where the sidebar overlays the
|
|
2
|
+
page rather than sitting beside it. %>
|
|
3
|
+
<div x-show="sidebarOpenMobile"
|
|
4
|
+
x-transition:enter="transition-opacity ease-linear duration-200"
|
|
5
|
+
x-transition:enter-start="opacity-0"
|
|
6
|
+
x-transition:leave="transition-opacity ease-linear duration-200"
|
|
7
|
+
x-transition:leave-end="opacity-0"
|
|
8
|
+
x-on:click="sidebarOpenMobile = false"
|
|
9
|
+
class="fixed inset-0 z-40 bg-black/50 md:hidden"
|
|
10
|
+
style="display: none;"></div>
|
|
11
|
+
<%# Holds the rail's width in the flow so the page content is never covered.
|
|
12
|
+
The sidebar itself is fixed, so without this it would overlap. %>
|
|
13
|
+
<div class="hidden shrink-0 transition-[width] duration-200 ease-linear md:block"
|
|
14
|
+
:class="sidebarOpen ? 'w-64' : 'w-16'"></div>
|
|
15
|
+
<%# One sidebar for both breakpoints: a drawer that slides in below md, a rail
|
|
16
|
+
that collapses to icons at md and up. %>
|
|
17
|
+
<div class="fixed inset-y-0 left-0 z-50 flex w-72 flex-col gap-2 bg-sidebar p-2 text-sidebar-foreground transition-all duration-200 ease-linear md:z-10 md:translate-x-0"
|
|
18
|
+
:class="[sidebarOpenMobile ? 'translate-x-0' : '-translate-x-full', sidebarOpen ? 'md:w-64' : 'md:w-16']">
|
|
19
|
+
<div class="flex flex-col gap-2">
|
|
20
|
+
<a href="<%= dashboard_path %>" class="flex h-12 items-center gap-2 overflow-hidden rounded-md p-2 text-left hover:bg-sidebar-accent hover:text-sidebar-accent-foreground">
|
|
21
|
+
<div class="flex aspect-square size-8 shrink-0 items-center justify-center rounded-md bg-sidebar-primary text-sidebar-primary-foreground">
|
|
22
|
+
<%= render(ApplicationLogoComponent.new({ class: "size-5 fill-current text-white dark:text-black" })) %>
|
|
23
|
+
</div>
|
|
24
|
+
<div x-show="sidebarOpen || sidebarOpenMobile" class="ml-1 grid flex-1 text-left text-sm">
|
|
25
|
+
<span class="mb-0.5 truncate leading-tight font-semibold">Rails</span>
|
|
26
|
+
</div>
|
|
27
|
+
</a>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="flex min-h-0 flex-1 flex-col gap-2 overflow-auto">
|
|
30
|
+
<div class="relative flex w-full min-w-0 flex-col p-2">
|
|
31
|
+
<div x-show="sidebarOpen || sidebarOpenMobile" class="px-2 py-1 text-xs font-medium text-sidebar-foreground/70">Platform</div>
|
|
32
|
+
<ul class="flex w-full min-w-0 flex-col gap-1">
|
|
33
|
+
<li>
|
|
34
|
+
<%= render(SidebarMenuButtonComponent.new(href: dashboard_path, icon: "layout-grid", active: current_page?(dashboard_path))) do %>
|
|
35
|
+
Dashboard
|
|
36
|
+
<% end %>
|
|
37
|
+
</li>
|
|
38
|
+
</ul>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="flex flex-col gap-2">
|
|
42
|
+
<ul class="flex w-full min-w-0 flex-col gap-1">
|
|
43
|
+
<li>
|
|
44
|
+
<%= render(SidebarMenuButtonComponent.new(href: "https://github.com/cuonggt/kaze", icon: "folder-git-2", external: true, class: "text-neutral-600 hover:text-neutral-800 dark:text-neutral-300 dark:hover:text-neutral-100")) do %>
|
|
45
|
+
Repository
|
|
46
|
+
<% end %>
|
|
47
|
+
</li>
|
|
48
|
+
<li>
|
|
49
|
+
<%= render(SidebarMenuButtonComponent.new(href: "https://github.com/cuonggt/kaze#readme", icon: "book-open", external: true, class: "text-neutral-600 hover:text-neutral-800 dark:text-neutral-300 dark:hover:text-neutral-100")) do %>
|
|
50
|
+
Documentation
|
|
51
|
+
<% end %>
|
|
52
|
+
</li>
|
|
53
|
+
</ul>
|
|
54
|
+
<%= render "layouts/sidebar_user" %>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%# The bar above the page content: the rail trigger, then the page's own
|
|
2
|
+
breadcrumbs. %>
|
|
3
|
+
<header class="flex h-16 shrink-0 items-center gap-2 border-b border-sidebar-border/50 px-6 transition-[width,height] ease-linear md:px-4">
|
|
4
|
+
<div class="flex items-center gap-2">
|
|
5
|
+
<button x-on:click="toggle()" class="-ml-1 inline-flex size-7 items-center justify-center rounded-md hover:bg-accent hover:text-accent-foreground" aria-label="Toggle sidebar">
|
|
6
|
+
<span x-show="sidebarOpen"><%= render(IconComponent.new('panel-left-close')) %></span>
|
|
7
|
+
<span x-show="! sidebarOpen" style="display: none;"><%= render(IconComponent.new('panel-left-open')) %></span>
|
|
8
|
+
</button>
|
|
9
|
+
<%= yield :breadcrumbs %>
|
|
10
|
+
</div>
|
|
11
|
+
</header>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<%# The user row at the foot of the sidebar. %>
|
|
2
|
+
<%= render DropdownComponent.new(align: 'left', side: 'top', width: 'w-[15rem]') do |component| %>
|
|
3
|
+
<% component.with_trigger do %>
|
|
4
|
+
<button class="flex h-12 w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm hover:bg-sidebar-accent hover:text-sidebar-accent-foreground">
|
|
5
|
+
<%= render(UserAvatarComponent.new(Current.auth.user.name)) %>
|
|
6
|
+
<span x-show="sidebarOpen || sidebarOpenMobile" class="grid flex-1 text-left text-sm leading-tight">
|
|
7
|
+
<span class="truncate font-medium"><%= Current.auth.user.name %></span>
|
|
8
|
+
</span>
|
|
9
|
+
<%= render(IconComponent.new('chevrons-up-down', classes: 'ml-auto size-4 shrink-0')) %>
|
|
10
|
+
</button>
|
|
11
|
+
<% end %>
|
|
12
|
+
<div class="px-1 py-1.5">
|
|
13
|
+
<div class="flex items-center gap-2 text-left text-sm">
|
|
14
|
+
<%= render(UserAvatarComponent.new(Current.auth.user.name)) %>
|
|
15
|
+
<div class="grid flex-1 leading-tight">
|
|
16
|
+
<span class="truncate font-medium"><%= Current.auth.user.name %></span>
|
|
17
|
+
<span class="truncate text-xs text-muted-foreground"><%= Current.auth.user.email %></span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="-mx-1 my-1 h-px bg-border"></div>
|
|
22
|
+
<%= render(DropdownLinkComponent.new({ href: profile_edit_path })) do %>
|
|
23
|
+
<%= render(IconComponent.new('settings')) %>
|
|
24
|
+
Settings
|
|
25
|
+
<% end %>
|
|
26
|
+
<div class="-mx-1 my-1 h-px bg-border"></div>
|
|
27
|
+
<form method="POST" action="<%= logout_path %>">
|
|
28
|
+
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
|
|
29
|
+
<%= render(DropdownLinkComponent.new({ href: logout_path, onclick: "event.preventDefault();this.closest('form').submit()" })) do %>
|
|
30
|
+
<%= render(IconComponent.new('log-out')) %>
|
|
31
|
+
Log Out
|
|
32
|
+
<% end %>
|
|
33
|
+
</form>
|
|
34
|
+
<% end %>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
-
<title><%= content_for(:title)
|
|
6
|
+
<title><%= content_for?(:title) ? "#{content_for(:title)} - Rails" : "Rails" %></title>
|
|
7
7
|
<%= csrf_meta_tags %>
|
|
8
8
|
<%= csp_meta_tag %>
|
|
9
9
|
<%= stylesheet_link_tag "tailwind", "data-turbo-track": Rails.env.production? ? "reload" : "" %>
|
|
@@ -14,20 +14,29 @@
|
|
|
14
14
|
<%= yield :head %>
|
|
15
15
|
</head>
|
|
16
16
|
<body class="font-sans antialiased">
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
<%# `sidebarOpen` is the rail's state at md and up, `sidebarOpenMobile` the drawer's below
|
|
18
|
+
it. One trigger drives whichever applies, and the rail's state is kept in
|
|
19
|
+
a cookie so the next page renders the way this one was left. %>
|
|
20
|
+
<div
|
|
21
|
+
x-data="{
|
|
22
|
+
sidebarOpen: <%= sidebar_open? %>,
|
|
23
|
+
sidebarOpenMobile: false,
|
|
24
|
+
toggle() {
|
|
25
|
+
if (window.matchMedia('(max-width: 767px)').matches) {
|
|
26
|
+
this.sidebarOpenMobile = ! this.sidebarOpenMobile
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
this.sidebarOpen = ! this.sidebarOpen
|
|
30
|
+
document.cookie = 'sidebar_state=' + this.sidebarOpen + '; path=/; max-age=604800; samesite=lax'
|
|
31
|
+
}
|
|
32
|
+
}"
|
|
33
|
+
class="flex min-h-svh w-full bg-sidebar"
|
|
34
|
+
>
|
|
35
|
+
<%= render "layouts/sidebar" %>
|
|
36
|
+
<div class="relative flex min-h-svh flex-1 flex-col overflow-hidden bg-background md:m-2 md:ml-0 md:rounded-xl md:shadow-sm">
|
|
37
|
+
<%= render "layouts/sidebar_header" %>
|
|
38
|
+
<main class="min-w-0 flex-1"><%= yield %></main>
|
|
39
|
+
</div>
|
|
31
40
|
</div>
|
|
32
41
|
</body>
|
|
33
42
|
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<%# Alpine holds the preference; the buttons themselves are rendered here so the
|
|
2
|
+
icons come through the same component the rest of the layout uses. %>
|
|
3
|
+
<div
|
|
4
|
+
x-data="{
|
|
5
|
+
preference: localStorage.getItem('appearance') || 'system',
|
|
6
|
+
select(value) {
|
|
7
|
+
this.preference = value
|
|
8
|
+
localStorage.setItem('appearance', value)
|
|
9
|
+
window.applyAppearance()
|
|
10
|
+
}
|
|
11
|
+
}"
|
|
12
|
+
class="inline-flex gap-1 rounded-lg bg-neutral-100 p-1 dark:bg-neutral-800"
|
|
13
|
+
>
|
|
14
|
+
<% [ [ 'light', 'Light', 'sun' ], [ 'dark', 'Dark', 'moon' ], [ 'system', 'System', 'monitor' ] ].each do |value, label, icon| %>
|
|
15
|
+
<button
|
|
16
|
+
type="button"
|
|
17
|
+
@click="select('<%= value %>')"
|
|
18
|
+
:aria-pressed="preference === '<%= value %>'"
|
|
19
|
+
class="flex items-center rounded-md px-3.5 py-1.5 transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
|
20
|
+
:class="preference === '<%= value %>'
|
|
21
|
+
? 'bg-white shadow-xs dark:bg-neutral-700 dark:text-neutral-100'
|
|
22
|
+
: 'text-neutral-500 hover:bg-neutral-200/60 hover:text-black dark:text-neutral-400 dark:hover:bg-neutral-700/60'"
|
|
23
|
+
>
|
|
24
|
+
<%= render(IconComponent.new(icon, classes: '-ml-1 h-4 w-4')) %>
|
|
25
|
+
<span class="ml-1.5 text-sm"><%= label %></span>
|
|
26
|
+
</button>
|
|
27
|
+
<% end %>
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<% content_for(:title, 'Appearance settings') %>
|
|
2
|
+
<% content_for(:breadcrumbs) do %>
|
|
3
|
+
<%= render(BreadcrumbsComponent.new([ { title: 'Appearance settings', href: appearance_edit_path } ])) %>
|
|
4
|
+
<% end %>
|
|
5
|
+
<%= render layout: 'layouts/settings' do %>
|
|
6
|
+
<div class="space-y-6">
|
|
7
|
+
<%= render(HeadingComponent.new(title: 'Appearance settings', description: 'Update the appearance settings for your account', variant: 'small')) %>
|
|
8
|
+
<%= render 'settings/appearance/update_appearance_form' %>
|
|
9
|
+
</div>
|
|
10
|
+
<% end %>
|
data/stubs/hotwire/app/views/{profile/partials → settings/password}/_update_password_form.html.erb
RENAMED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
<%= turbo_frame_tag 'update_password_form' do %>
|
|
2
2
|
<section>
|
|
3
|
-
|
|
4
|
-
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
|
5
|
-
Update Password
|
|
6
|
-
</h2>
|
|
7
|
-
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
|
8
|
-
Ensure your account is using a long, random password to stay secure.
|
|
9
|
-
</p>
|
|
10
|
-
</header>
|
|
11
|
-
<%= form_with model: @update_password_form, url: password_update_path, method: "put", class: "mt-6 space-y-6" do |update_password_form| %>
|
|
3
|
+
<%= form_with model: @update_password_form, url: password_update_path, method: "put", class: "mt-6 space-y-6" do |update_password_form| %>
|
|
12
4
|
<!-- Current Password -->
|
|
13
5
|
<div>
|
|
14
6
|
<%= render(InputLabelComponent.new({ for: "update_password_current_password", value: "Current Password" })) %>
|
|
@@ -37,7 +29,7 @@
|
|
|
37
29
|
x-show="show"
|
|
38
30
|
x-transition
|
|
39
31
|
x-init="setTimeout(() => show = false, 2000)"
|
|
40
|
-
class="text-sm text-
|
|
32
|
+
class="text-sm text-muted-foreground">Saved.</p>
|
|
41
33
|
<% end %>
|
|
42
34
|
</div>
|
|
43
35
|
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<% content_for(:title, 'Password settings') %>
|
|
2
|
+
<% content_for(:breadcrumbs) do %>
|
|
3
|
+
<%= render(BreadcrumbsComponent.new([ { title: 'Password settings', href: password_edit_path } ])) %>
|
|
4
|
+
<% end %>
|
|
5
|
+
<%= render layout: 'layouts/settings' do %>
|
|
6
|
+
<div class="space-y-6">
|
|
7
|
+
<%= render(HeadingComponent.new(title: 'Update password', description: 'Ensure your account is using a long, random password to stay secure', variant: 'small')) %>
|
|
8
|
+
<%= render 'settings/password/update_password_form' %>
|
|
9
|
+
</div>
|
|
10
|
+
<% end %>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<div id="confirm_user_deletion_modal">
|
|
2
2
|
<%= render(ModalComponent.new({ name: "confirm-user-deletion", show: @delete_user_form.error_messages.has_key?(:password), focusable: true })) do %>
|
|
3
3
|
<%= form_with model: @delete_user_form, url: profile_destroy_path, method: "delete", class: "p-6" do %>
|
|
4
|
-
<h2 class="text-lg font-medium text-
|
|
4
|
+
<h2 class="text-lg font-medium text-foreground">
|
|
5
5
|
Are you sure you want to delete your account?
|
|
6
6
|
</h2>
|
|
7
|
-
<p class="mt-1 text-sm text-
|
|
7
|
+
<p class="mt-1 text-sm text-muted-foreground">
|
|
8
8
|
Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.
|
|
9
9
|
</p>
|
|
10
10
|
<div class="mt-6">
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
<%= turbo_frame_tag 'update_profile_information_form' do %>
|
|
2
2
|
<section>
|
|
3
|
-
|
|
4
|
-
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
|
5
|
-
Profile Information
|
|
6
|
-
</h2>
|
|
7
|
-
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
|
8
|
-
Update your account's profile information and email address.
|
|
9
|
-
</p>
|
|
10
|
-
</header>
|
|
11
|
-
<form id="send-verification" method="post" action="<%= verification_send_path %>">
|
|
3
|
+
<form id="send-verification" method="post" action="<%= verification_send_path %>">
|
|
12
4
|
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
|
|
13
5
|
</form>
|
|
14
6
|
<%= form_with model: @update_profile_information_form, url: profile_update_path, method: "patch", class: "mt-6 space-y-6" do %>
|
|
@@ -25,9 +17,9 @@
|
|
|
25
17
|
<%= render(InputErrorComponent.new({ class: "mt-2", message: @update_profile_information_form.error_messages[:email] })) %>
|
|
26
18
|
<% if User.include?(MustVerifyEmail) && !Current.auth.user.has_verified_email? %>
|
|
27
19
|
<div>
|
|
28
|
-
<p class="
|
|
20
|
+
<p class="mt-2 text-sm text-foreground">
|
|
29
21
|
Your email address is unverified.
|
|
30
|
-
<button form="send-verification" class="
|
|
22
|
+
<button form="send-verification" class="rounded-md text-sm text-muted-foreground underline outline-none hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50">
|
|
31
23
|
Click here to re-send the verification email.
|
|
32
24
|
</button>
|
|
33
25
|
</p>
|
|
@@ -49,7 +41,7 @@
|
|
|
49
41
|
x-show="show"
|
|
50
42
|
x-transition
|
|
51
43
|
x-init="setTimeout(() => show = false, 2000)"
|
|
52
|
-
class="text-sm text-
|
|
44
|
+
class="text-sm text-muted-foreground">Saved.</p>
|
|
53
45
|
<% end %>
|
|
54
46
|
</div>
|
|
55
47
|
<% end %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<% content_for(:title, 'Profile settings') %>
|
|
2
|
+
<% content_for(:breadcrumbs) do %>
|
|
3
|
+
<%= render(BreadcrumbsComponent.new([ { title: 'Profile settings', href: profile_edit_path } ])) %>
|
|
4
|
+
<% end %>
|
|
5
|
+
<%= render layout: 'layouts/settings' do %>
|
|
6
|
+
<div class="space-y-6">
|
|
7
|
+
<%= render(HeadingComponent.new(title: 'Profile information', description: 'Update your name and email address', variant: 'small')) %>
|
|
8
|
+
<%= render 'settings/profile/update_profile_information_form' %>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="space-y-6">
|
|
11
|
+
<%= render(HeadingComponent.new(title: 'Delete account', description: 'Delete your account and all of its resources', variant: 'small')) %>
|
|
12
|
+
<%= render 'settings/profile/delete_user_form' %>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class Settings::PasswordController < ApplicationController
|
|
2
|
+
skip_ensure_email_is_verified
|
|
3
|
+
|
|
4
|
+
def edit
|
|
5
|
+
render inertia: 'Settings/Password'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def update
|
|
9
|
+
form = UpdatePasswordForm.new(params.permit(:current_password, :password, :password_confirmation))
|
|
10
|
+
|
|
11
|
+
return redirect_to password_edit_path, inertia: { errors: form.error_messages } if form.invalid?
|
|
12
|
+
|
|
13
|
+
Current.auth.user.update(password: form.password)
|
|
14
|
+
|
|
15
|
+
redirect_back_or_to password_edit_path
|
|
16
|
+
end
|
|
17
|
+
end
|
data/stubs/inertia-common/app/controllers/{profile_controller.rb → settings/profile_controller.rb}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
class ProfileController < ApplicationController
|
|
1
|
+
class Settings::ProfileController < ApplicationController
|
|
2
2
|
skip_ensure_email_is_verified
|
|
3
3
|
|
|
4
4
|
def edit
|
|
5
|
-
render inertia: 'Profile
|
|
5
|
+
render inertia: 'Settings/Profile', props: {
|
|
6
6
|
mustVerifyEmail: User.include?(MustVerifyEmail),
|
|
7
7
|
status: flash[:status]
|
|
8
8
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
3
|
class PasswordUpdateTest < ActionDispatch::IntegrationTest
|
|
4
|
+
test 'password page is displayed' do
|
|
5
|
+
user = FactoryBot.create(:user)
|
|
6
|
+
|
|
7
|
+
acting_as(user).get password_edit_path
|
|
8
|
+
|
|
9
|
+
assert_response :success
|
|
10
|
+
end
|
|
11
|
+
|
|
4
12
|
test 'password can be updated' do
|
|
5
13
|
user = FactoryBot.create(:user)
|
|
6
14
|
|
|
@@ -10,7 +18,7 @@ class PasswordUpdateTest < ActionDispatch::IntegrationTest
|
|
|
10
18
|
password_confirmation: 'new-password'
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
assert_redirected_to
|
|
21
|
+
assert_redirected_to password_edit_path
|
|
14
22
|
assert BCrypt::Password.new(user.reload.password_digest).is_password?('new-password')
|
|
15
23
|
end
|
|
16
24
|
|
|
@@ -23,6 +31,6 @@ class PasswordUpdateTest < ActionDispatch::IntegrationTest
|
|
|
23
31
|
password_confirmation: 'new-password'
|
|
24
32
|
}
|
|
25
33
|
|
|
26
|
-
assert_redirected_to
|
|
34
|
+
assert_redirected_to password_edit_path
|
|
27
35
|
end
|
|
28
36
|
end
|