kaze 0.5.0 → 0.6.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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kaze/commands/install_command.rb +8 -0
  3. data/lib/kaze/commands/installs_hotwire_stack.rb +5 -2
  4. data/lib/kaze/commands/installs_inertia_stacks.rb +13 -3
  5. data/lib/kaze/version.rb +1 -1
  6. data/stubs/default/app/forms/auth/login_form.rb +2 -8
  7. data/stubs/default/app/forms/update_profile_information_form.rb +1 -1
  8. data/stubs/default/app/models/auth.rb +57 -0
  9. data/stubs/default/app/models/current.rb +1 -1
  10. data/stubs/default/app/validators/current_password_validator.rb +1 -1
  11. data/stubs/default/app/views/layouts/mailer.html.erb +367 -372
  12. data/stubs/default/app/views/layouts/mailer.text.erb +1 -4
  13. data/stubs/default/app/views/user_mailer/reset_password.html.erb +21 -26
  14. data/stubs/default/test/factories/users.rb +7 -0
  15. data/stubs/default/test/integration/auth/authentication_test.rb +43 -0
  16. data/stubs/default/test/integration/auth/password_reset_test.rb +41 -0
  17. data/stubs/default/test/integration/auth/registration_test.rb +21 -0
  18. data/stubs/default/test/integration/password_update_test.rb +28 -0
  19. data/stubs/default/test/integration/profile_test.rb +51 -0
  20. data/stubs/default/test/test_helper.rb +38 -0
  21. data/stubs/hotwire/app/components/dropdown_component.html.erb +17 -18
  22. data/stubs/hotwire/app/components/modal_component.html.erb +55 -59
  23. data/stubs/hotwire/app/controllers/application_controller.rb +1 -0
  24. data/stubs/hotwire/app/controllers/auth/authenticated_session_controller.rb +10 -7
  25. data/stubs/hotwire/app/controllers/auth/new_password_controller.rb +3 -1
  26. data/stubs/hotwire/app/controllers/auth/password_reset_link_controller.rb +3 -1
  27. data/stubs/hotwire/app/controllers/auth/registered_user_controller.rb +4 -2
  28. data/stubs/hotwire/app/controllers/concerns/authenticate.rb +5 -20
  29. data/stubs/hotwire/app/controllers/concerns/redirect_if_authenticated.rb +19 -0
  30. data/stubs/hotwire/app/controllers/concerns/set_current_auth.rb +9 -0
  31. data/stubs/hotwire/app/controllers/password_controller.rb +1 -1
  32. data/stubs/hotwire/app/controllers/profile_controller.rb +6 -4
  33. data/stubs/hotwire/app/controllers/welcome_controller.rb +1 -1
  34. data/stubs/hotwire/app/javascript/application.js +3 -3
  35. data/stubs/hotwire/app/views/auth/forgot_password.html.erb +12 -17
  36. data/stubs/hotwire/app/views/auth/login.html.erb +0 -9
  37. data/stubs/hotwire/app/views/auth/register.html.erb +0 -13
  38. data/stubs/hotwire/app/views/auth/reset_password.html.erb +0 -7
  39. data/stubs/hotwire/app/views/dashboard/index.html.erb +9 -10
  40. data/stubs/hotwire/app/views/layouts/_navigation.html.erb +77 -87
  41. data/stubs/hotwire/app/views/layouts/application.html.erb +0 -9
  42. data/stubs/hotwire/app/views/layouts/guest.html.erb +0 -6
  43. data/stubs/hotwire/app/views/profile/edit.html.erb +19 -22
  44. data/stubs/hotwire/app/views/profile/partials/_delete_user_form.html.erb +32 -42
  45. data/stubs/hotwire/app/views/profile/partials/_update_password_form.html.erb +42 -55
  46. data/stubs/hotwire/app/views/profile/partials/_update_profile_information_form.html.erb +36 -46
  47. data/stubs/hotwire/app/views/welcome/index.html.erb +34 -46
  48. data/stubs/hotwire/config/tailwind.config.js +2 -2
  49. data/stubs/inertia-common/app/controllers/application_controller.rb +1 -0
  50. data/stubs/inertia-common/app/controllers/auth/authenticated_session_controller.rb +10 -7
  51. data/stubs/inertia-common/app/controllers/auth/new_password_controller.rb +3 -1
  52. data/stubs/inertia-common/app/controllers/auth/password_reset_link_controller.rb +3 -1
  53. data/stubs/inertia-common/app/controllers/auth/registered_user_controller.rb +4 -2
  54. data/stubs/inertia-common/app/controllers/concerns/authenticate.rb +5 -20
  55. data/stubs/inertia-common/app/controllers/concerns/handle_inertia_requests.rb +1 -1
  56. data/stubs/inertia-common/app/controllers/concerns/redirect_if_authenticated.rb +19 -0
  57. data/stubs/inertia-common/app/controllers/concerns/set_current_auth.rb +9 -0
  58. data/stubs/inertia-common/app/controllers/password_controller.rb +1 -1
  59. data/stubs/inertia-common/app/controllers/profile_controller.rb +5 -3
  60. data/stubs/inertia-common/app/controllers/welcome_controller.rb +1 -1
  61. data/stubs/inertia-common/test/integration/password_update_test.rb +28 -0
  62. data/stubs/inertia-common/test/integration/profile_test.rb +51 -0
  63. data/stubs/inertia-react-ts/app/javascript/Components/ApplicationLogo.tsx +13 -9
  64. data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +15 -12
  65. data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +20 -15
  66. data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +119 -87
  67. data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +14 -7
  68. data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +18 -7
  69. data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +60 -60
  70. data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +21 -16
  71. data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +20 -15
  72. data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +19 -14
  73. data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +22 -16
  74. data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +35 -24
  75. data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +157 -117
  76. data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +15 -15
  77. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +52 -49
  78. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +90 -82
  79. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +118 -115
  80. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +63 -60
  81. data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +23 -17
  82. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +31 -27
  83. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +109 -99
  84. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +121 -113
  85. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +76 -69
  86. data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +87 -63
  87. data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +32 -25
  88. data/stubs/inertia-react-ts/app/views/layouts/application.html.erb +0 -4
  89. data/stubs/inertia-react-ts/config/tailwind.config.js +2 -2
  90. data/stubs/inertia-react-ts/vite.config.ts +2 -5
  91. data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +10 -6
  92. data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +18 -18
  93. data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +5 -5
  94. data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +60 -57
  95. data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +9 -9
  96. data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +7 -7
  97. data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +6 -6
  98. data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +84 -74
  99. data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +12 -12
  100. data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +5 -5
  101. data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +12 -12
  102. data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +13 -13
  103. data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +13 -13
  104. data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +168 -136
  105. data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +15 -13
  106. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +56 -49
  107. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +78 -72
  108. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +101 -97
  109. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +71 -68
  110. data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +22 -14
  111. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +34 -30
  112. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +87 -83
  113. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +105 -98
  114. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +69 -59
  115. data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +74 -47
  116. data/stubs/inertia-vue-ts/app/views/layouts/application.html.erb +0 -4
  117. data/stubs/inertia-vue-ts/config/tailwind.config.js +2 -2
  118. data/stubs/inertia-vue-ts/vite.config.ts +2 -5
  119. metadata +18 -6
  120. data/stubs/hotwire/bin/vite +0 -27
  121. data/stubs/inertia-common/Procfile.dev +0 -3
  122. /data/stubs/{hotwire → default}/Procfile.dev +0 -0
  123. /data/stubs/hotwire/app/javascript/{alpinejs.js → alpinejs.stub} +0 -0
@@ -1,33 +1,37 @@
1
- import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
2
- import DeleteUserForm from './Partials/DeleteUserForm';
3
- import UpdatePasswordForm from './Partials/UpdatePasswordForm';
4
- import UpdateProfileInformationForm from './Partials/UpdateProfileInformationForm';
5
- import { Head } from '@inertiajs/react';
6
- import { PageProps } from '@/types';
1
+ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
2
+ import DeleteUserForm from './Partials/DeleteUserForm'
3
+ import UpdatePasswordForm from './Partials/UpdatePasswordForm'
4
+ import UpdateProfileInformationForm from './Partials/UpdateProfileInformationForm'
5
+ import { Head } from '@inertiajs/react'
6
+ import { PageProps } from '@/types'
7
7
 
8
8
  export default function Edit({ auth }: PageProps) {
9
- return (
10
- <AuthenticatedLayout
11
- user={auth.user}
12
- header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Profile</h2>}
13
- >
14
- <Head title="Profile" />
9
+ return (
10
+ <AuthenticatedLayout
11
+ user={auth.user}
12
+ header={
13
+ <h2 className="font-semibold text-xl text-gray-800 leading-tight">
14
+ Profile
15
+ </h2>
16
+ }
17
+ >
18
+ <Head title="Profile" />
15
19
 
16
- <div className="py-12">
17
- <div className="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
18
- <div className="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
19
- <UpdateProfileInformationForm className="max-w-xl" />
20
- </div>
20
+ <div className="py-12">
21
+ <div className="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
22
+ <div className="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
23
+ <UpdateProfileInformationForm className="max-w-xl" />
24
+ </div>
21
25
 
22
- <div className="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
23
- <UpdatePasswordForm className="max-w-xl" />
24
- </div>
26
+ <div className="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
27
+ <UpdatePasswordForm className="max-w-xl" />
28
+ </div>
25
29
 
26
- <div className="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
27
- <DeleteUserForm className="max-w-xl" />
28
- </div>
29
- </div>
30
- </div>
31
- </AuthenticatedLayout>
32
- );
30
+ <div className="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
31
+ <DeleteUserForm className="max-w-xl" />
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </AuthenticatedLayout>
36
+ )
33
37
  }
@@ -1,100 +1,110 @@
1
- import { useRef, useState, FormEventHandler } from 'react';
2
- import DangerButton from '@/Components/DangerButton';
3
- import InputError from '@/Components/InputError';
4
- import InputLabel from '@/Components/InputLabel';
5
- import Modal from '@/Components/Modal';
6
- import SecondaryButton from '@/Components/SecondaryButton';
7
- import TextInput from '@/Components/TextInput';
8
- import { useForm } from '@inertiajs/react';
9
- import { profile_destroy_path } from '@/routes';
10
-
11
- export default function DeleteUserForm({ className = '' }: { className?: string }) {
12
- const [confirmingUserDeletion, setConfirmingUserDeletion] = useState(false);
13
- const passwordInput = useRef<HTMLInputElement>(null);
14
-
15
- const {
16
- data,
17
- setData,
18
- delete: destroy,
19
- processing,
20
- reset,
21
- errors,
22
- } = useForm({
23
- password: '',
24
- });
25
-
26
- const confirmUserDeletion = () => {
27
- setConfirmingUserDeletion(true);
28
- };
29
-
30
- const deleteUser: FormEventHandler = (e) => {
31
- e.preventDefault();
32
-
33
- destroy(profile_destroy_path(), {
34
- preserveScroll: true,
35
- onSuccess: () => closeModal(),
36
- onError: () => passwordInput.current?.focus(),
37
- onFinish: () => reset(),
38
- });
39
- };
40
-
41
- const closeModal = () => {
42
- setConfirmingUserDeletion(false);
43
-
44
- reset();
45
- };
46
-
47
- return (
48
- <section className={`space-y-6 ${className}`}>
49
- <header>
50
- <h2 className="text-lg font-medium text-gray-900">Delete Account</h2>
51
-
52
- <p className="mt-1 text-sm text-gray-600">
53
- Once your account is deleted, all of its resources and data will be permanently deleted. Before
54
- deleting your account, please download any data or information that you wish to retain.
55
- </p>
56
- </header>
57
-
58
- <DangerButton onClick={confirmUserDeletion}>Delete Account</DangerButton>
59
-
60
- <Modal show={confirmingUserDeletion} onClose={closeModal}>
61
- <form onSubmit={deleteUser} className="p-6">
62
- <h2 className="text-lg font-medium text-gray-900">
63
- Are you sure you want to delete your account?
64
- </h2>
65
-
66
- <p className="mt-1 text-sm text-gray-600">
67
- Once your account is deleted, all of its resources and data will be permanently deleted. Please
68
- enter your password to confirm you would like to permanently delete your account.
69
- </p>
70
-
71
- <div className="mt-6">
72
- <InputLabel htmlFor="password" value="Password" className="sr-only" />
73
-
74
- <TextInput
75
- id="password"
76
- type="password"
77
- name="password"
78
- ref={passwordInput}
79
- value={data.password}
80
- onChange={(e) => setData('password', e.target.value)}
81
- className="mt-1 block w-3/4"
82
- isFocused
83
- placeholder="Password"
84
- />
85
-
86
- <InputError message={errors.password} className="mt-2" />
87
- </div>
88
-
89
- <div className="mt-6 flex justify-end">
90
- <SecondaryButton onClick={closeModal}>Cancel</SecondaryButton>
91
-
92
- <DangerButton className="ms-3" disabled={processing}>
93
- Delete Account
94
- </DangerButton>
95
- </div>
96
- </form>
97
- </Modal>
98
- </section>
99
- );
1
+ import { useRef, useState, FormEventHandler } from 'react'
2
+ import DangerButton from '@/Components/DangerButton'
3
+ import InputError from '@/Components/InputError'
4
+ import InputLabel from '@/Components/InputLabel'
5
+ import Modal from '@/Components/Modal'
6
+ import SecondaryButton from '@/Components/SecondaryButton'
7
+ import TextInput from '@/Components/TextInput'
8
+ import { useForm } from '@inertiajs/react'
9
+ import { profile_destroy_path } from '@/routes'
10
+
11
+ export default function DeleteUserForm({
12
+ className = '',
13
+ }: {
14
+ className?: string
15
+ }) {
16
+ const [confirmingUserDeletion, setConfirmingUserDeletion] = useState(false)
17
+ const passwordInput = useRef<HTMLInputElement>(null)
18
+
19
+ const {
20
+ data,
21
+ setData,
22
+ delete: destroy,
23
+ processing,
24
+ reset,
25
+ errors,
26
+ } = useForm({
27
+ password: '',
28
+ })
29
+
30
+ const confirmUserDeletion = () => {
31
+ setConfirmingUserDeletion(true)
32
+ }
33
+
34
+ const deleteUser: FormEventHandler = (e) => {
35
+ e.preventDefault()
36
+
37
+ destroy(profile_destroy_path(), {
38
+ preserveScroll: true,
39
+ onSuccess: () => closeModal(),
40
+ onError: () => passwordInput.current?.focus(),
41
+ onFinish: () => reset(),
42
+ })
43
+ }
44
+
45
+ const closeModal = () => {
46
+ setConfirmingUserDeletion(false)
47
+
48
+ reset()
49
+ }
50
+
51
+ return (
52
+ <section className={`space-y-6 ${className}`}>
53
+ <header>
54
+ <h2 className="text-lg font-medium text-gray-900">Delete Account</h2>
55
+
56
+ <p className="mt-1 text-sm text-gray-600">
57
+ Once your account is deleted, all of its resources and data will be
58
+ permanently deleted. Before deleting your account, please download any
59
+ data or information that you wish to retain.
60
+ </p>
61
+ </header>
62
+
63
+ <DangerButton onClick={confirmUserDeletion}>Delete Account</DangerButton>
64
+
65
+ <Modal show={confirmingUserDeletion} onClose={closeModal}>
66
+ <form onSubmit={deleteUser} className="p-6">
67
+ <h2 className="text-lg font-medium text-gray-900">
68
+ Are you sure you want to delete your account?
69
+ </h2>
70
+
71
+ <p className="mt-1 text-sm text-gray-600">
72
+ Once your account is deleted, all of its resources and data will be
73
+ permanently deleted. Please enter your password to confirm you would
74
+ like to permanently delete your account.
75
+ </p>
76
+
77
+ <div className="mt-6">
78
+ <InputLabel
79
+ htmlFor="password"
80
+ value="Password"
81
+ className="sr-only"
82
+ />
83
+
84
+ <TextInput
85
+ id="password"
86
+ type="password"
87
+ name="password"
88
+ ref={passwordInput}
89
+ value={data.password}
90
+ onChange={(e) => setData('password', e.target.value)}
91
+ className="mt-1 block w-3/4"
92
+ isFocused
93
+ placeholder="Password"
94
+ />
95
+
96
+ <InputError message={errors.password} className="mt-2" />
97
+ </div>
98
+
99
+ <div className="mt-6 flex justify-end">
100
+ <SecondaryButton onClick={closeModal}>Cancel</SecondaryButton>
101
+
102
+ <DangerButton className="ms-3" disabled={processing}>
103
+ Delete Account
104
+ </DangerButton>
105
+ </div>
106
+ </form>
107
+ </Modal>
108
+ </section>
109
+ )
100
110
  }
@@ -1,114 +1,122 @@
1
- import { useRef, FormEventHandler } from 'react';
2
- import InputError from '@/Components/InputError';
3
- import InputLabel from '@/Components/InputLabel';
4
- import PrimaryButton from '@/Components/PrimaryButton';
5
- import TextInput from '@/Components/TextInput';
6
- import { useForm } from '@inertiajs/react';
7
- import { Transition } from '@headlessui/react';
8
- import { password_update_path } from '@/routes';
9
-
10
- export default function UpdatePasswordForm({ className = '' }: { className?: string }) {
11
- const passwordInput = useRef<HTMLInputElement>(null);
12
- const currentPasswordInput = useRef<HTMLInputElement>(null);
13
-
14
- const { data, setData, errors, put, reset, processing, recentlySuccessful } = useForm({
15
- current_password: '',
16
- password: '',
17
- password_confirmation: '',
18
- });
19
-
20
- const updatePassword: FormEventHandler = (e) => {
21
- e.preventDefault();
22
-
23
- put(password_update_path(), {
24
- preserveScroll: true,
25
- onSuccess: () => reset(),
26
- onError: (errors) => {
27
- if (errors.password) {
28
- reset('password', 'password_confirmation');
29
- passwordInput.current?.focus();
30
- }
31
-
32
- if (errors.current_password) {
33
- reset('current_password');
34
- currentPasswordInput.current?.focus();
35
- }
36
- },
37
- });
38
- };
39
-
40
- return (
41
- <section className={className}>
42
- <header>
43
- <h2 className="text-lg font-medium text-gray-900">Update Password</h2>
44
-
45
- <p className="mt-1 text-sm text-gray-600">
46
- Ensure your account is using a long, random password to stay secure.
47
- </p>
48
- </header>
49
-
50
- <form onSubmit={updatePassword} className="mt-6 space-y-6">
51
- <div>
52
- <InputLabel htmlFor="current_password" value="Current Password" />
53
-
54
- <TextInput
55
- id="current_password"
56
- ref={currentPasswordInput}
57
- value={data.current_password}
58
- onChange={(e) => setData('current_password', e.target.value)}
59
- type="password"
60
- className="mt-1 block w-full"
61
- autoComplete="current-password"
62
- />
63
-
64
- <InputError message={errors.current_password} className="mt-2" />
65
- </div>
66
-
67
- <div>
68
- <InputLabel htmlFor="password" value="New Password" />
69
-
70
- <TextInput
71
- id="password"
72
- ref={passwordInput}
73
- value={data.password}
74
- onChange={(e) => setData('password', e.target.value)}
75
- type="password"
76
- className="mt-1 block w-full"
77
- autoComplete="new-password"
78
- />
79
-
80
- <InputError message={errors.password} className="mt-2" />
81
- </div>
82
-
83
- <div>
84
- <InputLabel htmlFor="password_confirmation" value="Confirm Password" />
85
-
86
- <TextInput
87
- id="password_confirmation"
88
- value={data.password_confirmation}
89
- onChange={(e) => setData('password_confirmation', e.target.value)}
90
- type="password"
91
- className="mt-1 block w-full"
92
- autoComplete="new-password"
93
- />
94
-
95
- <InputError message={errors.password_confirmation} className="mt-2" />
96
- </div>
97
-
98
- <div className="flex items-center gap-4">
99
- <PrimaryButton disabled={processing}>Save</PrimaryButton>
100
-
101
- <Transition
102
- show={recentlySuccessful}
103
- enter="transition ease-in-out"
104
- enterFrom="opacity-0"
105
- leave="transition ease-in-out"
106
- leaveTo="opacity-0"
107
- >
108
- <p className="text-sm text-gray-600">Saved.</p>
109
- </Transition>
110
- </div>
111
- </form>
112
- </section>
113
- );
1
+ import { useRef, FormEventHandler } from 'react'
2
+ import InputError from '@/Components/InputError'
3
+ import InputLabel from '@/Components/InputLabel'
4
+ import PrimaryButton from '@/Components/PrimaryButton'
5
+ import TextInput from '@/Components/TextInput'
6
+ import { useForm } from '@inertiajs/react'
7
+ import { Transition } from '@headlessui/react'
8
+ import { password_update_path } from '@/routes'
9
+
10
+ export default function UpdatePasswordForm({
11
+ className = '',
12
+ }: {
13
+ className?: string
14
+ }) {
15
+ const passwordInput = useRef<HTMLInputElement>(null)
16
+ const currentPasswordInput = useRef<HTMLInputElement>(null)
17
+
18
+ const { data, setData, errors, put, reset, processing, recentlySuccessful } =
19
+ useForm({
20
+ current_password: '',
21
+ password: '',
22
+ password_confirmation: '',
23
+ })
24
+
25
+ const updatePassword: FormEventHandler = (e) => {
26
+ e.preventDefault()
27
+
28
+ put(password_update_path(), {
29
+ preserveScroll: true,
30
+ onSuccess: () => reset(),
31
+ onError: (errors) => {
32
+ if (errors.password) {
33
+ reset('password', 'password_confirmation')
34
+ passwordInput.current?.focus()
35
+ }
36
+
37
+ if (errors.current_password) {
38
+ reset('current_password')
39
+ currentPasswordInput.current?.focus()
40
+ }
41
+ },
42
+ })
43
+ }
44
+
45
+ return (
46
+ <section className={className}>
47
+ <header>
48
+ <h2 className="text-lg font-medium text-gray-900">Update Password</h2>
49
+
50
+ <p className="mt-1 text-sm text-gray-600">
51
+ Ensure your account is using a long, random password to stay secure.
52
+ </p>
53
+ </header>
54
+
55
+ <form onSubmit={updatePassword} className="mt-6 space-y-6">
56
+ <div>
57
+ <InputLabel htmlFor="current_password" value="Current Password" />
58
+
59
+ <TextInput
60
+ id="current_password"
61
+ ref={currentPasswordInput}
62
+ value={data.current_password}
63
+ onChange={(e) => setData('current_password', e.target.value)}
64
+ type="password"
65
+ className="mt-1 block w-full"
66
+ autoComplete="current-password"
67
+ />
68
+
69
+ <InputError message={errors.current_password} className="mt-2" />
70
+ </div>
71
+
72
+ <div>
73
+ <InputLabel htmlFor="password" value="New Password" />
74
+
75
+ <TextInput
76
+ id="password"
77
+ ref={passwordInput}
78
+ value={data.password}
79
+ onChange={(e) => setData('password', e.target.value)}
80
+ type="password"
81
+ className="mt-1 block w-full"
82
+ autoComplete="new-password"
83
+ />
84
+
85
+ <InputError message={errors.password} className="mt-2" />
86
+ </div>
87
+
88
+ <div>
89
+ <InputLabel
90
+ htmlFor="password_confirmation"
91
+ value="Confirm Password"
92
+ />
93
+
94
+ <TextInput
95
+ id="password_confirmation"
96
+ value={data.password_confirmation}
97
+ onChange={(e) => setData('password_confirmation', e.target.value)}
98
+ type="password"
99
+ className="mt-1 block w-full"
100
+ autoComplete="new-password"
101
+ />
102
+
103
+ <InputError message={errors.password_confirmation} className="mt-2" />
104
+ </div>
105
+
106
+ <div className="flex items-center gap-4">
107
+ <PrimaryButton disabled={processing}>Save</PrimaryButton>
108
+
109
+ <Transition
110
+ show={recentlySuccessful}
111
+ enter="transition ease-in-out"
112
+ enterFrom="opacity-0"
113
+ leave="transition ease-in-out"
114
+ leaveTo="opacity-0"
115
+ >
116
+ <p className="text-sm text-gray-600">Saved.</p>
117
+ </Transition>
118
+ </div>
119
+ </form>
120
+ </section>
121
+ )
114
122
  }