kaze 1.4.0 → 2.0.1

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.
Files changed (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -1
  3. data/README.md +1 -1
  4. data/lib/kaze/commands/app/base_stack.rb +12 -2
  5. data/lib/kaze/commands/app/hotwire_stack.rb +3 -1
  6. data/lib/kaze/commands/app/inertia_react_stack.rb +6 -2
  7. data/lib/kaze/commands/app/inertia_vue_stack.rb +6 -2
  8. data/lib/kaze/commands/app.rb +9 -2
  9. data/lib/kaze/version.rb +1 -1
  10. data/stubs/default/config/routes.rb +11 -4
  11. data/stubs/default/test/integration/appearance_test.rb +11 -0
  12. data/stubs/default/test/integration/password_update_test.rb +9 -1
  13. data/stubs/hotwire/app/assets/tailwind/application.css +119 -1
  14. data/stubs/hotwire/app/components/breadcrumbs_component.rb +32 -0
  15. data/stubs/hotwire/app/components/danger_button_component.rb +1 -1
  16. data/stubs/hotwire/app/components/dropdown_component.html.erb +2 -2
  17. data/stubs/hotwire/app/components/dropdown_component.rb +5 -1
  18. data/stubs/hotwire/app/components/dropdown_link_component.rb +1 -1
  19. data/stubs/hotwire/app/components/heading_component.rb +19 -0
  20. data/stubs/hotwire/app/components/icon_component.rb +30 -0
  21. data/stubs/hotwire/app/components/input_error_component.rb +1 -1
  22. data/stubs/hotwire/app/components/input_label_component.rb +3 -3
  23. data/stubs/hotwire/app/components/modal_component.html.erb +2 -2
  24. data/stubs/hotwire/app/components/primary_button_component.rb +1 -1
  25. data/stubs/hotwire/app/components/secondary_button_component.rb +1 -1
  26. data/stubs/hotwire/app/components/sidebar_menu_button_component.rb +25 -0
  27. data/stubs/hotwire/app/components/text_input_component.rb +1 -1
  28. data/stubs/hotwire/app/components/user_avatar_component.rb +27 -0
  29. data/stubs/hotwire/app/controllers/application_controller.rb +1 -0
  30. data/stubs/hotwire/app/controllers/concerns/sidebar_state.rb +14 -0
  31. data/stubs/hotwire/app/controllers/settings/appearance_controller.rb +7 -0
  32. data/stubs/hotwire/app/controllers/settings/password_controller.rb +19 -0
  33. data/stubs/hotwire/app/controllers/{profile_controller.rb → settings/profile_controller.rb} +3 -4
  34. data/stubs/hotwire/app/views/auth/login.html.erb +1 -1
  35. data/stubs/hotwire/app/views/dashboard/index.html.erb +6 -11
  36. data/stubs/hotwire/app/views/layouts/_settings.html.erb +26 -0
  37. data/stubs/hotwire/app/views/layouts/_sidebar.html.erb +56 -0
  38. data/stubs/hotwire/app/views/layouts/_sidebar_header.html.erb +11 -0
  39. data/stubs/hotwire/app/views/layouts/_sidebar_user.html.erb +34 -0
  40. data/stubs/hotwire/app/views/layouts/application.html.erb +24 -15
  41. data/stubs/hotwire/app/views/settings/appearance/_update_appearance_form.html.erb +28 -0
  42. data/stubs/hotwire/app/views/settings/appearance/edit.html.erb +10 -0
  43. data/stubs/hotwire/app/views/{profile/partials → settings/password}/_update_password_form.html.erb +2 -10
  44. data/stubs/hotwire/app/views/settings/password/edit.html.erb +10 -0
  45. data/stubs/hotwire/app/views/{profile/partials → settings/profile}/_confirm_user_deletion_modal.html.erb +2 -2
  46. data/stubs/hotwire/app/views/settings/profile/_delete_user_form.html.erb +6 -0
  47. data/stubs/hotwire/app/views/{profile/partials → settings/profile}/_update_profile_information_form.html.erb +4 -12
  48. data/stubs/hotwire/app/views/{profile → settings/profile}/destroy.turbo_stream.erb +1 -1
  49. data/stubs/hotwire/app/views/settings/profile/edit.html.erb +14 -0
  50. data/stubs/inertia-common/app/controllers/concerns/handle_inertia_requests.rb +4 -1
  51. data/stubs/inertia-common/app/controllers/settings/appearance_controller.rb +7 -0
  52. data/stubs/inertia-common/app/controllers/settings/password_controller.rb +17 -0
  53. data/stubs/inertia-common/app/controllers/{profile_controller.rb → settings/profile_controller.rb} +2 -2
  54. data/stubs/inertia-common/test/integration/appearance_test.rb +11 -0
  55. data/stubs/inertia-common/test/integration/password_update_test.rb +10 -2
  56. data/stubs/inertia-react-ts/app/assets/tailwind/application.css +120 -1
  57. data/stubs/inertia-react-ts/app/javascript/Components/AppLogo.tsx +17 -0
  58. data/stubs/inertia-react-ts/app/javascript/Components/AppShell.tsx +9 -0
  59. data/stubs/inertia-react-ts/app/javascript/Components/AppSidebar.tsx +65 -0
  60. data/stubs/inertia-react-ts/app/javascript/Components/AppSidebarHeader.tsx +14 -0
  61. data/stubs/inertia-react-ts/app/javascript/Components/Breadcrumbs.tsx +43 -0
  62. data/stubs/inertia-react-ts/app/javascript/Components/Heading.tsx +19 -0
  63. data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +1 -1
  64. data/stubs/inertia-react-ts/app/javascript/Components/NavFooter.tsx +32 -0
  65. data/stubs/inertia-react-ts/app/javascript/Components/NavMain.tsx +32 -0
  66. data/stubs/inertia-react-ts/app/javascript/Components/NavUser.tsx +39 -0
  67. data/stubs/inertia-react-ts/app/javascript/Components/UserInfo.tsx +23 -0
  68. data/stubs/inertia-react-ts/app/javascript/Components/UserMenuContent.tsx +57 -0
  69. data/stubs/inertia-react-ts/app/javascript/Components/ui/avatar.tsx +51 -0
  70. data/stubs/inertia-react-ts/app/javascript/Components/ui/breadcrumb.tsx +109 -0
  71. data/stubs/inertia-react-ts/app/javascript/Components/ui/button.tsx +49 -0
  72. data/stubs/inertia-react-ts/app/javascript/Components/ui/checkbox.tsx +27 -0
  73. data/stubs/inertia-react-ts/app/javascript/Components/ui/dialog.tsx +109 -0
  74. data/stubs/inertia-react-ts/app/javascript/Components/ui/dropdown-menu.tsx +217 -0
  75. data/stubs/inertia-react-ts/app/javascript/Components/ui/input.tsx +21 -0
  76. data/stubs/inertia-react-ts/app/javascript/Components/ui/label.tsx +19 -0
  77. data/stubs/inertia-react-ts/app/javascript/Components/ui/separator.tsx +26 -0
  78. data/stubs/inertia-react-ts/app/javascript/Components/ui/sheet.tsx +137 -0
  79. data/stubs/inertia-react-ts/app/javascript/Components/ui/sidebar.tsx +719 -0
  80. data/stubs/inertia-react-ts/app/javascript/Components/ui/skeleton.tsx +13 -0
  81. data/stubs/inertia-react-ts/app/javascript/Components/ui/tooltip.tsx +55 -0
  82. data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +19 -127
  83. data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +1 -1
  84. data/stubs/inertia-react-ts/app/javascript/Layouts/SettingsLayout.tsx +57 -0
  85. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +6 -6
  86. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +17 -12
  87. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +14 -14
  88. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +10 -10
  89. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/VerifyEmail.tsx +2 -2
  90. data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +13 -8
  91. data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Appearance.tsx +33 -0
  92. data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.tsx +107 -0
  93. data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Partials/UpdateAppearanceForm.tsx +41 -0
  94. data/stubs/inertia-react-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdatePasswordForm.tsx +12 -20
  95. data/stubs/inertia-react-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdateProfileInformationForm.tsx +13 -21
  96. data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Password.tsx +33 -0
  97. data/stubs/inertia-react-ts/app/javascript/Pages/Settings/Profile.tsx +35 -0
  98. data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +6 -1
  99. data/stubs/inertia-react-ts/app/javascript/hooks/use-initials.tsx +23 -0
  100. data/stubs/inertia-react-ts/app/javascript/hooks/use-mobile-navigation.tsx +11 -0
  101. data/stubs/inertia-react-ts/app/javascript/hooks/use-mobile.tsx +36 -0
  102. data/stubs/inertia-react-ts/app/javascript/lib/appearance.ts +1 -2
  103. data/stubs/inertia-react-ts/app/javascript/lib/utils.ts +6 -0
  104. data/stubs/inertia-react-ts/app/javascript/types/global.d.ts +2 -2
  105. data/stubs/inertia-react-ts/app/javascript/types/index.d.ts +14 -0
  106. data/stubs/inertia-react-ts/components.json +21 -0
  107. data/stubs/inertia-react-ts/package.json +13 -0
  108. data/stubs/inertia-vue-ts/app/assets/tailwind/application.css +120 -1
  109. data/stubs/inertia-vue-ts/app/javascript/Components/AppLogo.vue +17 -0
  110. data/stubs/inertia-vue-ts/app/javascript/Components/AppShell.vue +12 -0
  111. data/stubs/inertia-vue-ts/app/javascript/Components/AppSidebar.vue +65 -0
  112. data/stubs/inertia-vue-ts/app/javascript/Components/AppSidebarHeader.vue +20 -0
  113. data/stubs/inertia-vue-ts/app/javascript/Components/Breadcrumbs.vue +32 -0
  114. data/stubs/inertia-vue-ts/app/javascript/Components/Heading.vue +15 -0
  115. data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +1 -1
  116. data/stubs/inertia-vue-ts/app/javascript/Components/NavFooter.vue +33 -0
  117. data/stubs/inertia-vue-ts/app/javascript/Components/NavMain.vue +32 -0
  118. data/stubs/inertia-vue-ts/app/javascript/Components/NavUser.vue +40 -0
  119. data/stubs/inertia-vue-ts/app/javascript/Components/UserInfo.vue +25 -0
  120. data/stubs/inertia-vue-ts/app/javascript/Components/UserMenuContent.vue +47 -0
  121. data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/Avatar.vue +18 -0
  122. data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/AvatarFallback.vue +21 -0
  123. data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/AvatarImage.vue +16 -0
  124. data/stubs/inertia-vue-ts/app/javascript/Components/ui/avatar/index.ts +3 -0
  125. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/Breadcrumb.vue +17 -0
  126. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
  127. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
  128. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbLink.vue +21 -0
  129. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbList.vue +17 -0
  130. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
  131. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
  132. data/stubs/inertia-vue-ts/app/javascript/Components/ui/breadcrumb/index.ts +7 -0
  133. data/stubs/inertia-vue-ts/app/javascript/Components/ui/button/Button.vue +31 -0
  134. data/stubs/inertia-vue-ts/app/javascript/Components/ui/button/index.ts +34 -0
  135. data/stubs/inertia-vue-ts/app/javascript/Components/ui/checkbox/Checkbox.vue +35 -0
  136. data/stubs/inertia-vue-ts/app/javascript/Components/ui/checkbox/index.ts +1 -0
  137. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/Dialog.vue +15 -0
  138. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogClose.vue +12 -0
  139. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogContent.vue +52 -0
  140. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogDescription.vue +23 -0
  141. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogFooter.vue +25 -0
  142. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogHeader.vue +14 -0
  143. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogOverlay.vue +26 -0
  144. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogTitle.vue +23 -0
  145. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/DialogTrigger.vue +12 -0
  146. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dialog/index.ts +9 -0
  147. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenu.vue +15 -0
  148. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuContent.vue +37 -0
  149. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
  150. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
  151. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
  152. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  153. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/DropdownMenuTrigger.vue +14 -0
  154. data/stubs/inertia-vue-ts/app/javascript/Components/ui/dropdown-menu/index.ts +7 -0
  155. data/stubs/inertia-vue-ts/app/javascript/Components/ui/input/Input.vue +50 -0
  156. data/stubs/inertia-vue-ts/app/javascript/Components/ui/input/index.ts +1 -0
  157. data/stubs/inertia-vue-ts/app/javascript/Components/ui/label/Label.vue +26 -0
  158. data/stubs/inertia-vue-ts/app/javascript/Components/ui/label/index.ts +1 -0
  159. data/stubs/inertia-vue-ts/app/javascript/Components/ui/separator/Separator.vue +29 -0
  160. data/stubs/inertia-vue-ts/app/javascript/Components/ui/separator/index.ts +1 -0
  161. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/Sheet.vue +19 -0
  162. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetClose.vue +15 -0
  163. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetContent.vue +62 -0
  164. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetDescription.vue +21 -0
  165. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetFooter.vue +16 -0
  166. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetHeader.vue +15 -0
  167. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetOverlay.vue +21 -0
  168. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetTitle.vue +21 -0
  169. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/SheetTrigger.vue +15 -0
  170. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sheet/index.ts +8 -0
  171. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/Sidebar.vue +96 -0
  172. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarContent.vue +18 -0
  173. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarFooter.vue +18 -0
  174. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroup.vue +18 -0
  175. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupAction.vue +27 -0
  176. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupContent.vue +18 -0
  177. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarGroupLabel.vue +25 -0
  178. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarHeader.vue +18 -0
  179. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarInput.vue +22 -0
  180. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarInset.vue +21 -0
  181. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenu.vue +18 -0
  182. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuAction.vue +35 -0
  183. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuBadge.vue +26 -0
  184. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuButton.vue +48 -0
  185. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
  186. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuItem.vue +18 -0
  187. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
  188. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSub.vue +22 -0
  189. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
  190. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  191. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarProvider.vue +82 -0
  192. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarRail.vue +33 -0
  193. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarSeparator.vue +19 -0
  194. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/SidebarTrigger.vue +28 -0
  195. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/index.ts +60 -0
  196. data/stubs/inertia-vue-ts/app/javascript/Components/ui/sidebar/utils.ts +19 -0
  197. data/stubs/inertia-vue-ts/app/javascript/Components/ui/skeleton/Skeleton.vue +17 -0
  198. data/stubs/inertia-vue-ts/app/javascript/Components/ui/skeleton/index.ts +1 -0
  199. data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/Tooltip.vue +19 -0
  200. data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipContent.vue +34 -0
  201. data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipProvider.vue +14 -0
  202. data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/TooltipTrigger.vue +15 -0
  203. data/stubs/inertia-vue-ts/app/javascript/Components/ui/tooltip/index.ts +4 -0
  204. data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +17 -147
  205. data/stubs/inertia-vue-ts/app/javascript/Layouts/SettingsLayout.vue +57 -0
  206. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +7 -7
  207. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +10 -12
  208. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +12 -14
  209. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +8 -10
  210. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/VerifyEmail.vue +3 -3
  211. data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +15 -10
  212. data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Appearance.vue +33 -0
  213. data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/DeleteUserForm.vue +96 -0
  214. data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Partials/UpdateAppearanceForm.vue +42 -0
  215. data/stubs/inertia-vue-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdatePasswordForm.vue +12 -20
  216. data/stubs/inertia-vue-ts/app/javascript/Pages/{Profile → Settings}/Partials/UpdateProfileInformationForm.vue +12 -20
  217. data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Password.vue +33 -0
  218. data/stubs/inertia-vue-ts/app/javascript/Pages/Settings/Profile.vue +40 -0
  219. data/stubs/inertia-vue-ts/app/javascript/composables/useInitials.ts +29 -0
  220. data/stubs/inertia-vue-ts/app/javascript/lib/appearance.ts +1 -2
  221. data/stubs/inertia-vue-ts/app/javascript/lib/utils.ts +6 -0
  222. data/stubs/inertia-vue-ts/app/javascript/types/index.d.ts +14 -0
  223. data/stubs/inertia-vue-ts/app/javascript/types/vue-shims.d.ts +10 -0
  224. data/stubs/inertia-vue-ts/components.json +20 -0
  225. data/stubs/inertia-vue-ts/package.json +8 -1
  226. data/stubs/inertia-vue-ts/vite.config.ts +25 -1
  227. metadata +171 -46
  228. data/stubs/hotwire/app/components/nav_link_component.rb +0 -15
  229. data/stubs/hotwire/app/components/responsive_nav_link_component.rb +0 -15
  230. data/stubs/hotwire/app/controllers/password_controller.rb +0 -13
  231. data/stubs/hotwire/app/views/layouts/_navigation.html.erb +0 -82
  232. data/stubs/hotwire/app/views/profile/edit.html.erb +0 -29
  233. data/stubs/hotwire/app/views/profile/partials/_delete_user_form.html.erb +0 -14
  234. data/stubs/hotwire/app/views/profile/partials/_update_appearance_form.html.erb +0 -39
  235. data/stubs/inertia-common/app/controllers/password_controller.rb +0 -13
  236. data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +0 -11
  237. data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +0 -20
  238. data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +0 -100
  239. data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +0 -14
  240. data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +0 -66
  241. data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +0 -21
  242. data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +0 -20
  243. data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +0 -21
  244. data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +0 -22
  245. data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +0 -32
  246. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +0 -37
  247. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +0 -102
  248. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateAppearanceForm.tsx +0 -47
  249. data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +0 -29
  250. data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +0 -7
  251. data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +0 -75
  252. data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +0 -16
  253. data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +0 -12
  254. data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +0 -109
  255. data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +0 -21
  256. data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +0 -7
  257. data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +0 -21
  258. data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +0 -19
  259. data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +0 -23
  260. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +0 -43
  261. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +0 -99
  262. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateAppearanceForm.vue +0 -47
@@ -0,0 +1,42 @@
1
+ <script setup lang="ts">
2
+ import { ref, type Component } from 'vue'
3
+ import { Monitor, Moon, Sun } from '@lucide/vue'
4
+ import { getAppearance, setAppearance, type Appearance } from '@/lib/appearance'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ const options: { value: Appearance; icon: Component; label: string }[] = [
8
+ { value: 'light', icon: Sun, label: 'Light' },
9
+ { value: 'dark', icon: Moon, label: 'Dark' },
10
+ { value: 'system', icon: Monitor, label: 'System' },
11
+ ]
12
+
13
+ const appearance = ref<Appearance>(getAppearance())
14
+
15
+ const select = (value: Appearance) => {
16
+ appearance.value = value
17
+ setAppearance(value)
18
+ }
19
+ </script>
20
+
21
+ <template>
22
+ <div class="inline-flex gap-1 rounded-lg bg-neutral-100 p-1 dark:bg-neutral-800">
23
+ <button
24
+ v-for="option in options"
25
+ :key="option.value"
26
+ type="button"
27
+ :aria-pressed="appearance === option.value"
28
+ :class="
29
+ cn(
30
+ 'flex items-center rounded-md px-3.5 py-1.5 transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50',
31
+ appearance === option.value
32
+ ? 'bg-white shadow-xs dark:bg-neutral-700 dark:text-neutral-100'
33
+ : 'text-neutral-500 hover:bg-neutral-200/60 hover:text-black dark:text-neutral-400 dark:hover:bg-neutral-700/60',
34
+ )
35
+ "
36
+ @click="select(option.value)"
37
+ >
38
+ <component :is="option.icon" class="-ml-1 h-4 w-4" />
39
+ <span class="ml-1.5 text-sm">{{ option.label }}</span>
40
+ </button>
41
+ </div>
42
+ </template>
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import InputError from '@/Components/InputError.vue'
3
- import InputLabel from '@/Components/InputLabel.vue'
4
- import PrimaryButton from '@/Components/PrimaryButton.vue'
5
- import TextInput from '@/Components/TextInput.vue'
3
+ import { Button } from '@/Components/ui/button'
4
+ import { Input } from '@/Components/ui/input'
5
+ import { Label } from '@/Components/ui/label'
6
6
  import { useForm } from '@inertiajs/vue3'
7
7
  import { ref } from 'vue'
8
8
  import { password_update_path } from '@/routes'
@@ -38,19 +38,11 @@ const updatePassword = () => {
38
38
 
39
39
  <template>
40
40
  <section>
41
- <header>
42
- <h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Update Password</h2>
43
-
44
- <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
45
- Ensure your account is using a long, random password to stay secure.
46
- </p>
47
- </header>
48
-
49
- <form @submit.prevent="updatePassword" class="mt-6 space-y-6">
41
+ <form @submit.prevent="updatePassword" class="space-y-6">
50
42
  <div>
51
- <InputLabel for="current_password" value="Current Password" />
43
+ <Label for="current_password">Current Password</Label>
52
44
 
53
- <TextInput
45
+ <Input
54
46
  id="current_password"
55
47
  ref="currentPasswordInput"
56
48
  v-model="form.current_password"
@@ -63,9 +55,9 @@ const updatePassword = () => {
63
55
  </div>
64
56
 
65
57
  <div>
66
- <InputLabel for="password" value="New Password" />
58
+ <Label for="password">New Password</Label>
67
59
 
68
- <TextInput
60
+ <Input
69
61
  id="password"
70
62
  ref="passwordInput"
71
63
  v-model="form.password"
@@ -78,9 +70,9 @@ const updatePassword = () => {
78
70
  </div>
79
71
 
80
72
  <div>
81
- <InputLabel for="password_confirmation" value="Confirm Password" />
73
+ <Label for="password_confirmation">Confirm Password</Label>
82
74
 
83
- <TextInput
75
+ <Input
84
76
  id="password_confirmation"
85
77
  v-model="form.password_confirmation"
86
78
  type="password"
@@ -92,7 +84,7 @@ const updatePassword = () => {
92
84
  </div>
93
85
 
94
86
  <div class="flex items-center gap-4">
95
- <PrimaryButton :disabled="form.processing">Save</PrimaryButton>
87
+ <Button :disabled="form.processing">Save</Button>
96
88
 
97
89
  <Transition
98
90
  enter-active-class="transition ease-in-out"
@@ -100,7 +92,7 @@ const updatePassword = () => {
100
92
  leave-active-class="transition ease-in-out"
101
93
  leave-to-class="opacity-0"
102
94
  >
103
- <p v-if="form.recentlySuccessful" class="text-sm text-gray-600 dark:text-gray-400">Saved.</p>
95
+ <p v-if="form.recentlySuccessful" class="text-sm text-muted-foreground">Saved.</p>
104
96
  </Transition>
105
97
  </div>
106
98
  </form>
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import InputError from '@/Components/InputError.vue'
3
- import InputLabel from '@/Components/InputLabel.vue'
4
- import PrimaryButton from '@/Components/PrimaryButton.vue'
5
- import TextInput from '@/Components/TextInput.vue'
3
+ import { Button } from '@/Components/ui/button'
4
+ import { Input } from '@/Components/ui/input'
5
+ import { Label } from '@/Components/ui/label'
6
6
  import { Link, useForm, usePage } from '@inertiajs/vue3'
7
7
  import { profile_update_path, verification_send_path } from '@/routes'
8
8
 
@@ -21,19 +21,11 @@ const form = useForm({
21
21
 
22
22
  <template>
23
23
  <section>
24
- <header>
25
- <h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">Profile Information</h2>
26
-
27
- <p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
28
- Update your account's profile information and email address.
29
- </p>
30
- </header>
31
-
32
- <form @submit.prevent="form.patch(profile_update_path())" class="mt-6 space-y-6">
24
+ <form @submit.prevent="form.patch(profile_update_path())" class="space-y-6">
33
25
  <div>
34
- <InputLabel for="name" value="Name" />
26
+ <Label for="name">Name</Label>
35
27
 
36
- <TextInput
28
+ <Input
37
29
  id="name"
38
30
  type="text"
39
31
  class="mt-1 block w-full"
@@ -47,9 +39,9 @@ const form = useForm({
47
39
  </div>
48
40
 
49
41
  <div>
50
- <InputLabel for="email" value="Email" />
42
+ <Label for="email">Email</Label>
51
43
 
52
- <TextInput
44
+ <Input
53
45
  id="email"
54
46
  type="email"
55
47
  class="mt-1 block w-full"
@@ -62,13 +54,13 @@ const form = useForm({
62
54
  </div>
63
55
 
64
56
  <div v-if="mustVerifyEmail && user.email_verified_at === null">
65
- <p class="text-sm mt-2 text-gray-800 dark:text-gray-200">
57
+ <p class="mt-2 text-sm text-foreground">
66
58
  Your email address is unverified.
67
59
  <Link
68
60
  :href="verification_send_path()"
69
61
  method="post"
70
62
  as="button"
71
- class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
63
+ class="rounded-md text-sm text-muted-foreground underline outline-none hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50"
72
64
  >
73
65
  Click here to re-send the verification email.
74
66
  </Link>
@@ -83,7 +75,7 @@ const form = useForm({
83
75
  </div>
84
76
 
85
77
  <div class="flex items-center gap-4">
86
- <PrimaryButton :disabled="form.processing">Save</PrimaryButton>
78
+ <Button :disabled="form.processing">Save</Button>
87
79
 
88
80
  <Transition
89
81
  enter-active-class="transition ease-in-out"
@@ -91,7 +83,7 @@ const form = useForm({
91
83
  leave-active-class="transition ease-in-out"
92
84
  leave-to-class="opacity-0"
93
85
  >
94
- <p v-if="form.recentlySuccessful" class="text-sm text-gray-600 dark:text-gray-400">Saved.</p>
86
+ <p v-if="form.recentlySuccessful" class="text-sm text-muted-foreground">Saved.</p>
95
87
  </Transition>
96
88
  </div>
97
89
  </form>
@@ -0,0 +1,33 @@
1
+ <script setup lang="ts">
2
+ import { Head } from '@inertiajs/vue3'
3
+ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'
4
+ import SettingsLayout from '@/Layouts/SettingsLayout.vue'
5
+ import Heading from '@/Components/Heading.vue'
6
+ import UpdatePasswordForm from './Partials/UpdatePasswordForm.vue'
7
+ import { password_edit_path } from '@/routes'
8
+ import { BreadcrumbItem } from '@/types'
9
+
10
+ const breadcrumbs: BreadcrumbItem[] = [
11
+ {
12
+ title: 'Password settings',
13
+ href: password_edit_path(),
14
+ },
15
+ ]
16
+ </script>
17
+
18
+ <template>
19
+ <Head title="Password settings" />
20
+
21
+ <AuthenticatedLayout :breadcrumbs="breadcrumbs">
22
+ <SettingsLayout>
23
+ <div class="space-y-6">
24
+ <Heading
25
+ variant="small"
26
+ title="Update password"
27
+ description="Ensure your account is using a long, random password to stay secure"
28
+ />
29
+ <UpdatePasswordForm />
30
+ </div>
31
+ </SettingsLayout>
32
+ </AuthenticatedLayout>
33
+ </template>
@@ -0,0 +1,40 @@
1
+ <script setup lang="ts">
2
+ import { Head } from '@inertiajs/vue3'
3
+ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'
4
+ import SettingsLayout from '@/Layouts/SettingsLayout.vue'
5
+ import Heading from '@/Components/Heading.vue'
6
+ import DeleteUserForm from './Partials/DeleteUserForm.vue'
7
+ import UpdateProfileInformationForm from './Partials/UpdateProfileInformationForm.vue'
8
+ import { profile_edit_path } from '@/routes'
9
+ import { BreadcrumbItem } from '@/types'
10
+
11
+ defineProps<{
12
+ mustVerifyEmail?: boolean
13
+ status?: string
14
+ }>()
15
+
16
+ const breadcrumbs: BreadcrumbItem[] = [
17
+ {
18
+ title: 'Profile settings',
19
+ href: profile_edit_path(),
20
+ },
21
+ ]
22
+ </script>
23
+
24
+ <template>
25
+ <Head title="Profile settings" />
26
+
27
+ <AuthenticatedLayout :breadcrumbs="breadcrumbs">
28
+ <SettingsLayout>
29
+ <div class="space-y-6">
30
+ <Heading variant="small" title="Profile information" description="Update your name and email address" />
31
+ <UpdateProfileInformationForm :must-verify-email="mustVerifyEmail" :status="status" />
32
+ </div>
33
+
34
+ <div class="space-y-6">
35
+ <Heading variant="small" title="Delete account" description="Delete your account and all of its resources" />
36
+ <DeleteUserForm />
37
+ </div>
38
+ </SettingsLayout>
39
+ </AuthenticatedLayout>
40
+ </template>
@@ -0,0 +1,29 @@
1
+ export type UseInitialsReturn = {
2
+ getInitials: (fullName?: string) => string
3
+ }
4
+
5
+ function getInitial(name: string): string {
6
+ return Array.from(name)[0] ?? ''
7
+ }
8
+
9
+ export function getInitials(fullName?: string): string {
10
+ if (!fullName) {
11
+ return ''
12
+ }
13
+
14
+ const names = fullName.trim().split(/\s+/u).filter(Boolean)
15
+
16
+ if (names.length === 0) {
17
+ return ''
18
+ }
19
+
20
+ if (names.length === 1) {
21
+ return getInitial(names[0]).toUpperCase()
22
+ }
23
+
24
+ return `${getInitial(names[0])}${getInitial(names[names.length - 1])}`.toUpperCase()
25
+ }
26
+
27
+ export function useInitials(): UseInitialsReturn {
28
+ return { getInitials }
29
+ }
@@ -10,8 +10,7 @@ export function getAppearance(): Appearance {
10
10
 
11
11
  export function applyAppearance(appearance: Appearance = getAppearance()): void {
12
12
  const dark =
13
- appearance === 'dark' ||
14
- (appearance === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)
13
+ appearance === 'dark' || (appearance === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)
15
14
 
16
15
  document.documentElement.classList.toggle('dark', dark)
17
16
  }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -1,3 +1,5 @@
1
+ import { Component } from 'vue'
2
+
1
3
  export interface User {
2
4
  id: number
3
5
  name: string
@@ -5,8 +7,20 @@ export interface User {
5
7
  email_verified_at?: string
6
8
  }
7
9
 
10
+ export interface NavItem {
11
+ title: string
12
+ href: string
13
+ icon?: Component
14
+ }
15
+
16
+ export interface BreadcrumbItem {
17
+ title: string
18
+ href: string
19
+ }
20
+
8
21
  export type PageProps<T extends Record<string, unknown> = Record<string, unknown>> = T & {
9
22
  auth: {
10
23
  user: User
11
24
  }
25
+ sidebar_open: boolean
12
26
  }
@@ -0,0 +1,10 @@
1
+ // Lets plain `tsc` resolve single-file components imported from .ts modules,
2
+ // such as the `index.ts` barrels the ui components use. vue-tsc would give
3
+ // richer types, but it does not currently run against TypeScript 7.
4
+ declare module '*.vue' {
5
+ import type { DefineComponent } from 'vue'
6
+
7
+ const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, unknown>
8
+
9
+ export default component
10
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://shadcn-vue.com/schema.json",
3
+ "style": "new-york",
4
+ "typescript": true,
5
+ "tailwind": {
6
+ "config": "",
7
+ "css": "app/assets/tailwind/application.css",
8
+ "baseColor": "neutral",
9
+ "cssVariables": true,
10
+ "prefix": ""
11
+ },
12
+ "aliases": {
13
+ "components": "@/Components",
14
+ "ui": "@/Components/ui",
15
+ "utils": "@/lib/utils",
16
+ "lib": "@/lib",
17
+ "composables": "@/composables"
18
+ },
19
+ "iconLibrary": "lucide"
20
+ }
@@ -8,10 +8,17 @@
8
8
  "devDependencies": {
9
9
  "@headlessui/vue": "^1.7.23",
10
10
  "@inertiajs/vue3": "^3.9.1",
11
+ "@lucide/vue": "^1.61.0",
11
12
  "@types/node": "^26.6.5",
12
13
  "@vitejs/plugin-vue": "^6.0.10",
14
+ "@vueuse/core": "^12.8.2",
13
15
  "axios": "^1.6.4",
14
- "typescript": "^7.0.2",
16
+ "class-variance-authority": "^0.7.1",
17
+ "clsx": "^2.1.1",
18
+ "reka-ui": "^2.11.0",
19
+ "tailwind-merge": "^3.7.0",
20
+ "tw-animate-css": "^1.4.0",
21
+ "typescript": "^5.9.3",
15
22
  "vite": "^8.3.4",
16
23
  "vite-plugin-rails": "^0.6.0",
17
24
  "vue": "^3.5.44",
@@ -1,3 +1,4 @@
1
+ import fs from 'node:fs'
1
2
  import { defineConfig } from 'vite'
2
3
  import Rails from 'vite-plugin-rails'
3
4
  import vue from '@vitejs/plugin-vue'
@@ -6,5 +7,28 @@ export default defineConfig({
6
7
  resolve: {
7
8
  dedupe: ['axios'],
8
9
  },
9
- plugins: [Rails(), vue()],
10
+ plugins: [
11
+ Rails(),
12
+ vue({
13
+ script: {
14
+ // Components that type their props from another package -- the ui
15
+ // primitives do this with reka-ui -- need the SFC compiler to read
16
+ // files to resolve those types. It does not assume filesystem access,
17
+ // so hand it one.
18
+ fs: {
19
+ // statSync, not existsSync: a barrel import like `from "."` probes the
20
+ // directory itself, and claiming it exists makes the compiler try to
21
+ // read a directory.
22
+ fileExists: (file: string) => {
23
+ try {
24
+ return fs.statSync(file).isFile()
25
+ } catch {
26
+ return false
27
+ }
28
+ },
29
+ readFile: (file: string) => fs.readFileSync(file, 'utf-8'),
30
+ },
31
+ },
32
+ }),
33
+ ],
10
34
  })