kaze 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }