kaze 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kaze/commands/install_command.rb +3 -1
  3. data/lib/kaze/commands/installs_hotwire_stack.rb +2 -2
  4. data/lib/kaze/commands/installs_inertia_stacks.rb +7 -7
  5. data/lib/kaze/version.rb +1 -1
  6. data/stubs/default/app/mailers/application_mailer.rb +1 -1
  7. data/stubs/default/app/mailers/user_mailer.rb +16 -1
  8. data/stubs/default/app/models/concerns/can_reset_password.rb +1 -1
  9. data/stubs/default/app/models/concerns/must_verify_email.rb +15 -0
  10. data/stubs/default/app/models/user.rb +1 -0
  11. data/stubs/default/app/views/user_mailer/reset_password.html.erb +2 -2
  12. data/stubs/default/app/views/user_mailer/verify_email.html.erb +33 -0
  13. data/stubs/default/config/routes.rb +4 -0
  14. data/stubs/default/db/migrate/20240101000000_create_users.rb +1 -0
  15. data/stubs/default/test/factories/users.rb +5 -0
  16. data/stubs/default/test/integration/auth/authentication_test.rb +4 -6
  17. data/stubs/default/test/integration/auth/email_verification_test.rb +40 -0
  18. data/stubs/default/test/integration/auth/password_reset_test.rb +3 -3
  19. data/stubs/default/test/integration/password_update_test.rb +2 -2
  20. data/stubs/default/test/integration/profile_test.rb +4 -4
  21. data/stubs/hotwire/app/components/modal_component.rb +5 -5
  22. data/stubs/hotwire/app/controllers/auth/authenticated_session_controller.rb +3 -2
  23. data/stubs/hotwire/app/controllers/auth/email_verification_notification_controller.rb +21 -0
  24. data/stubs/hotwire/app/controllers/auth/new_password_controller.rb +2 -2
  25. data/stubs/hotwire/app/controllers/auth/password_reset_link_controller.rb +1 -1
  26. data/stubs/hotwire/app/controllers/auth/registered_user_controller.rb +6 -2
  27. data/stubs/hotwire/app/controllers/auth/verified_email_controller.rb +23 -0
  28. data/stubs/hotwire/app/controllers/concerns/authenticate.rb +10 -0
  29. data/stubs/hotwire/app/controllers/concerns/validate_signature.rb +17 -0
  30. data/stubs/hotwire/app/controllers/password_controller.rb +1 -1
  31. data/stubs/hotwire/app/controllers/profile_controller.rb +2 -2
  32. data/stubs/hotwire/app/views/auth/verify_email.html.erb +23 -0
  33. data/stubs/hotwire/app/views/profile/partials/_delete_user_form.html.erb +1 -1
  34. data/stubs/inertia-common/app/controllers/auth/authenticated_session_controller.rb +3 -3
  35. data/stubs/inertia-common/app/controllers/auth/email_verification_notification_controller.rb +21 -0
  36. data/stubs/inertia-common/app/controllers/auth/new_password_controller.rb +2 -3
  37. data/stubs/inertia-common/app/controllers/auth/password_reset_link_controller.rb +3 -3
  38. data/stubs/inertia-common/app/controllers/auth/registered_user_controller.rb +6 -2
  39. data/stubs/inertia-common/app/controllers/auth/verified_email_controller.rb +23 -0
  40. data/stubs/inertia-common/app/controllers/concerns/authenticate.rb +10 -0
  41. data/stubs/inertia-common/app/controllers/concerns/validate_signature.rb +17 -0
  42. data/stubs/inertia-common/app/controllers/password_controller.rb +1 -1
  43. data/stubs/inertia-common/app/controllers/profile_controller.rb +2 -2
  44. data/stubs/inertia-common/test/integration/password_update_test.rb +2 -2
  45. data/stubs/inertia-common/test/integration/profile_test.rb +4 -4
  46. data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +1 -4
  47. data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +3 -5
  48. data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +4 -25
  49. data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +1 -4
  50. data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +1 -4
  51. data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +3 -5
  52. data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +3 -5
  53. data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +3 -5
  54. data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +1 -7
  55. data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +14 -52
  56. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +3 -6
  57. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +9 -23
  58. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +1 -4
  59. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +1 -4
  60. data/stubs/inertia-react-ts/app/javascript/Pages/Auth/VerifyEmail.tsx +47 -0
  61. data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +2 -8
  62. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +1 -5
  63. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +7 -19
  64. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +7 -15
  65. data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +7 -16
  66. data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +1 -2
  67. data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +1 -5
  68. data/stubs/inertia-react-ts/config/tailwind.config.js +1 -6
  69. data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +3 -1
  70. data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +1 -4
  71. data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +3 -13
  72. data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +10 -45
  73. data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +2 -6
  74. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +4 -11
  75. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +2 -10
  76. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +1 -5
  77. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +1 -4
  78. data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/VerifyEmail.vue +50 -0
  79. data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +3 -11
  80. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +2 -10
  81. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +5 -9
  82. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +2 -9
  83. data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +3 -13
  84. data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +2 -5
  85. data/stubs/inertia-vue-ts/config/tailwind.config.js +1 -6
  86. metadata +14 -2
@@ -2,17 +2,17 @@ class Auth::AuthenticatedSessionController < ApplicationController
2
2
  include RedirectIfAuthenticated
3
3
 
4
4
  skip_authenticate only: %i[new create]
5
- skip_redirect_if_authenticated only: %i[destroy]
5
+ skip_redirect_if_authenticated only: :destroy
6
+ skip_ensure_email_is_verified only: :destroy
6
7
 
7
8
  def new
8
9
  render inertia: 'Auth/Login', props: {
9
- canResetPassword: true,
10
10
  status: flash[:status]
11
11
  }
12
12
  end
13
13
 
14
14
  def create
15
- form = Auth::LoginForm.new params.permit(:email, :password, :remember)
15
+ form = Auth::LoginForm.new(params.permit(:email, :password, :remember))
16
16
 
17
17
  form.authenticate
18
18
 
@@ -0,0 +1,21 @@
1
+ class Auth::EmailVerificationNotificationController < ApplicationController
2
+ skip_ensure_email_is_verified
3
+
4
+ before_action { redirect_to dashboard_path unless User.include?(MustVerifyEmail) }
5
+
6
+ def new
7
+ return redirect_to dashboard_path if Current.auth.user.has_verified_email?
8
+
9
+ render inertia: 'Auth/VerifyEmail', props: {
10
+ status: flash[:status]
11
+ }
12
+ end
13
+
14
+ def create
15
+ return redirect_to dashboard_path if Current.auth.user.has_verified_email?
16
+
17
+ Current.auth.user.send_email_verification_notification
18
+
19
+ redirect_back_or_to dashboard_path, flash: { status: 'verification-link-sent' }
20
+ end
21
+ end
@@ -10,11 +10,10 @@ class Auth::NewPasswordController < ApplicationController
10
10
  end
11
11
 
12
12
  def create
13
- form = Auth::NewPasswordForm.new params.permit(:token, :password, :password_confirmation)
13
+ form = Auth::NewPasswordForm.new(params.permit(:token, :password, :password_confirmation))
14
14
 
15
15
  return redirect_to login_path, flash: { status: 'Your password has been reset.' } if form.reset?
16
16
 
17
- redirect_back_or_to password_reset_path(token: form.token),
18
- inertia: { errors: form.error_messages }
17
+ redirect_back_or_to password_reset_path(token: form.token), inertia: { errors: form.error_messages }
19
18
  end
20
19
  end
@@ -10,10 +10,10 @@ class Auth::PasswordResetLinkController < ApplicationController
10
10
  end
11
11
 
12
12
  def create
13
- form = Auth::SendPasswordResetLinkForm.new params.permit(:email)
13
+ form = Auth::SendPasswordResetLinkForm.new(params.permit(:email))
14
14
 
15
- return redirect_back_or_to password_request_path, inertia: { errors: form.error_messages } unless form.send_reset_link?
15
+ return redirect_back_or_to password_request_path, flash: { status: 'We have emailed your password reset link.' } if form.send_reset_link?
16
16
 
17
- redirect_back_or_to password_request_path, flash: { status: 'We have emailed your password reset link.' }
17
+ redirect_back_or_to password_request_path, inertia: { errors: form.error_messages }
18
18
  end
19
19
  end
@@ -8,13 +8,17 @@ class Auth::RegisteredUserController < ApplicationController
8
8
  end
9
9
 
10
10
  def create
11
- form = Auth::RegisterForm.new params.permit(:name, :email, :password, :password_confirmation)
11
+ form = Auth::RegisterForm.new(params.permit(:name, :email, :password, :password_confirmation))
12
12
 
13
13
  return redirect_to register_path, inertia: { errors: form.error_messages } if form.invalid?
14
14
 
15
15
  user = User.create(name: form.name, email: form.email, password: form.password)
16
16
 
17
- Current.auth.login user
17
+ if User.include?(MustVerifyEmail) && !user.has_verified_email?
18
+ user.send_email_verification_notification
19
+ end
20
+
21
+ Current.auth.login(user)
18
22
 
19
23
  redirect_to dashboard_path
20
24
  end
@@ -0,0 +1,23 @@
1
+ class Auth::VerifiedEmailController < ApplicationController
2
+ include ValidateSignature
3
+
4
+ skip_ensure_email_is_verified
5
+
6
+ before_action { redirect_to dashboard_path unless User.include?(MustVerifyEmail) }
7
+
8
+ def create
9
+ return redirect_to dashboard_path(verified: '1') if Current.auth.user.has_verified_email?
10
+
11
+ Current.auth.user.mark_email_as_verified if email_verification_request_is_authorized?
12
+
13
+ redirect_to dashboard_path(verified: '1')
14
+ end
15
+
16
+ private
17
+
18
+ def email_verification_request_is_authorized?
19
+ return false if !ActiveSupport::SecurityUtils.secure_compare Current.auth.user.id.to_s, params[:id]
20
+
21
+ ActiveSupport::SecurityUtils.secure_compare Digest::SHA1.hexdigest(Current.auth.user.email), params[:hash]
22
+ end
23
+ end
@@ -3,11 +3,17 @@ module Authenticate
3
3
 
4
4
  included do
5
5
  before_action :authenticate!
6
+ before_action :ensure_email_is_verified! if User.include?(MustVerifyEmail)
6
7
  end
7
8
 
8
9
  class_methods do
9
10
  def skip_authenticate(**options)
10
11
  skip_before_action :authenticate!, **options
12
+ skip_ensure_email_is_verified(**options)
13
+ end
14
+
15
+ def skip_ensure_email_is_verified(**options)
16
+ skip_before_action :ensure_email_is_verified!, **options if User.include?(MustVerifyEmail)
11
17
  end
12
18
  end
13
19
 
@@ -16,4 +22,8 @@ module Authenticate
16
22
  def authenticate!
17
23
  redirect_to login_path unless Current.auth.check?
18
24
  end
25
+
26
+ def ensure_email_is_verified!
27
+ redirect_to verification_notice_path unless Current.auth.user && Current.auth.user.has_verified_email?
28
+ end
19
29
  end
@@ -0,0 +1,17 @@
1
+ module ValidateSignature
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_action do
6
+ render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found unless has_valid_signature?
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ def has_valid_signature?
13
+ request.original_url.split('?')[0] == ActiveSupport::MessageVerifier.new(ENV.fetch('RAILS_MASTER_KEY', '')).verify(params[:signature])
14
+ rescue
15
+ false
16
+ end
17
+ end
@@ -1,6 +1,6 @@
1
1
  class PasswordController < ApplicationController
2
2
  def update
3
- form = UpdatePasswordForm.new params.permit(:current_password, :password, :password_confirmation)
3
+ form = UpdatePasswordForm.new(params.permit(:current_password, :password, :password_confirmation))
4
4
 
5
5
  return redirect_to profile_edit_path, inertia: { errors: form.error_messages } if form.invalid?
6
6
 
@@ -6,7 +6,7 @@ class ProfileController < ApplicationController
6
6
  end
7
7
 
8
8
  def update
9
- form = UpdateProfileInformationForm.new params.permit(:name, :email)
9
+ form = UpdateProfileInformationForm.new(params.permit(:name, :email))
10
10
 
11
11
  return redirect_to profile_edit_path, inertia: { errors: form.error_messages } if form.invalid?
12
12
 
@@ -16,7 +16,7 @@ class ProfileController < ApplicationController
16
16
  end
17
17
 
18
18
  def destroy
19
- form = DeleteUserForm.new params.permit(:password)
19
+ form = DeleteUserForm.new(params.permit(:password))
20
20
 
21
21
  return redirect_back_or_to profile_edit_path, inertia: { errors: form.error_messages } if form.invalid?
22
22
 
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class PasswordUpdateTest < ActionDispatch::IntegrationTest
4
4
  test 'password can be updated' do
5
- user = FactoryBot.create :user
5
+ user = FactoryBot.create(:user)
6
6
 
7
7
  acting_as(user).put password_update_path, params: {
8
8
  current_password: 'password',
@@ -15,7 +15,7 @@ class PasswordUpdateTest < ActionDispatch::IntegrationTest
15
15
  end
16
16
 
17
17
  test 'correct password must be provided to update password' do
18
- user = FactoryBot.create :user
18
+ user = FactoryBot.create(:user)
19
19
 
20
20
  acting_as(user).put password_update_path, params: {
21
21
  current_password: 'wrong-password',
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class ProfileTest < ActionDispatch::IntegrationTest
4
4
  test 'profile page is displayed' do
5
- user = FactoryBot.create :user
5
+ user = FactoryBot.create(:user)
6
6
 
7
7
  acting_as(user).get profile_edit_path
8
8
 
@@ -10,7 +10,7 @@ class ProfileTest < ActionDispatch::IntegrationTest
10
10
  end
11
11
 
12
12
  test 'profile information can be updated' do
13
- user = FactoryBot.create :user
13
+ user = FactoryBot.create(:user)
14
14
 
15
15
  acting_as(user).patch profile_edit_path, params: {
16
16
  name: 'Test User',
@@ -26,7 +26,7 @@ class ProfileTest < ActionDispatch::IntegrationTest
26
26
  end
27
27
 
28
28
  test 'user can delete their account' do
29
- user = FactoryBot.create :user
29
+ user = FactoryBot.create(:user)
30
30
 
31
31
  acting_as(user).delete profile_destroy_path, params: {
32
32
  password: 'password'
@@ -39,7 +39,7 @@ class ProfileTest < ActionDispatch::IntegrationTest
39
39
  end
40
40
 
41
41
  test 'correct password must be provided to delete account' do
42
- user = FactoryBot.create :user
42
+ user = FactoryBot.create(:user)
43
43
 
44
44
  acting_as(user).delete profile_destroy_path, params: {
45
45
  password: 'wrong-password'
@@ -1,9 +1,6 @@
1
1
  import { InputHTMLAttributes } from 'react'
2
2
 
3
- export default function Checkbox({
4
- className = '',
5
- ...props
6
- }: InputHTMLAttributes<HTMLInputElement>) {
3
+ export default function Checkbox({ className = '', ...props }: InputHTMLAttributes<HTMLInputElement>) {
7
4
  return (
8
5
  <input
9
6
  {...props}
@@ -9,11 +9,9 @@ export default function DangerButton({
9
9
  return (
10
10
  <button
11
11
  {...props}
12
- className={
13
- `inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150 ${
14
- disabled && 'opacity-25'
15
- } ${className}`
16
- }
12
+ className={`inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150 ${
13
+ disabled && 'opacity-25'
14
+ } ${className}`}
17
15
  disabled={disabled}
18
16
  >
19
17
  {children}
@@ -1,12 +1,4 @@
1
- import {
2
- useState,
3
- createContext,
4
- useContext,
5
- Fragment,
6
- PropsWithChildren,
7
- Dispatch,
8
- SetStateAction,
9
- } from 'react'
1
+ import { useState, createContext, useContext, Fragment, PropsWithChildren, Dispatch, SetStateAction } from 'react'
10
2
  import { Link, InertiaLinkProps } from '@inertiajs/react'
11
3
  import { Transition } from '@headlessui/react'
12
4
 
@@ -41,12 +33,7 @@ const Trigger = ({ children }: PropsWithChildren) => {
41
33
  <>
42
34
  <div onClick={toggleOpen}>{children}</div>
43
35
 
44
- {open && (
45
- <div
46
- className="fixed inset-0 z-40"
47
- onClick={() => setOpen(false)}
48
- ></div>
49
- )}
36
+ {open && <div className="fixed inset-0 z-40" onClick={() => setOpen(false)}></div>}
50
37
  </>
51
38
  )
52
39
  }
@@ -93,22 +80,14 @@ const Content = ({
93
80
  className={`absolute z-50 mt-2 rounded-md shadow-lg ${alignmentClasses} ${widthClasses}`}
94
81
  onClick={() => setOpen(false)}
95
82
  >
96
- <div
97
- className={`rounded-md ring-1 ring-black ring-opacity-5 ${contentClasses}`}
98
- >
99
- {children}
100
- </div>
83
+ <div className={`rounded-md ring-1 ring-black ring-opacity-5 ${contentClasses}`}>{children}</div>
101
84
  </div>
102
85
  </Transition>
103
86
  </>
104
87
  )
105
88
  }
106
89
 
107
- const DropdownLink = ({
108
- className = '',
109
- children,
110
- ...props
111
- }: InertiaLinkProps) => {
90
+ const DropdownLink = ({ className = '', children, ...props }: InertiaLinkProps) => {
112
91
  return (
113
92
  <Link
114
93
  {...props}
@@ -6,10 +6,7 @@ export default function InputError({
6
6
  ...props
7
7
  }: HTMLAttributes<HTMLParagraphElement> & { message?: string }) {
8
8
  return message ? (
9
- <p
10
- {...props}
11
- className={`text-sm text-red-600 dark:text-red-400 ${className}`}
12
- >
9
+ <p {...props} className={`text-sm text-red-600 dark:text-red-400 ${className}`}>
13
10
  {message}
14
11
  </p>
15
12
  ) : null
@@ -7,10 +7,7 @@ export default function InputLabel({
7
7
  ...props
8
8
  }: LabelHTMLAttributes<HTMLLabelElement> & { value?: string }) {
9
9
  return (
10
- <label
11
- {...props}
12
- className={`block font-medium text-sm text-gray-700 dark:text-gray-300 ${className}`}
13
- >
10
+ <label {...props} className={`block font-medium text-sm text-gray-700 dark:text-gray-300 ${className}`}>
14
11
  {value ? value : children}
15
12
  </label>
16
13
  )
@@ -9,13 +9,11 @@ export default function NavLink({
9
9
  return (
10
10
  <Link
11
11
  {...props}
12
- className={
13
- `inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium leading-5 transition duration-150 ease-in-out focus:outline-none ${
14
- active
12
+ className={`inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium leading-5 transition duration-150 ease-in-out focus:outline-none ${
13
+ active
15
14
  ? 'border-indigo-400 dark:border-indigo-600 text-gray-900 dark:text-gray-100 focus:border-indigo-700 '
16
15
  : 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 '
17
- } ${className}`
18
- }
16
+ } ${className}`}
19
17
  >
20
18
  {children}
21
19
  </Link>
@@ -9,11 +9,9 @@ export default function PrimaryButton({
9
9
  return (
10
10
  <button
11
11
  {...props}
12
- className={
13
- `inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150 ${
14
- disabled && 'opacity-25'
15
- } ${className}`
16
- }
12
+ className={`inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150 ${
13
+ disabled && 'opacity-25'
14
+ } ${className}`}
17
15
  disabled={disabled}
18
16
  >
19
17
  {children}
@@ -11,11 +11,9 @@ export default function SecondaryButton({
11
11
  <button
12
12
  {...props}
13
13
  type={type}
14
- className={
15
- `inline-flex items-center px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150 ${
16
- disabled && 'opacity-25'
17
- } ${className}`
18
- }
14
+ className={`inline-flex items-center px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150 ${
15
+ disabled && 'opacity-25'
16
+ } ${className}`}
19
17
  disabled={disabled}
20
18
  >
21
19
  {children}
@@ -1,10 +1,4 @@
1
- import {
2
- forwardRef,
3
- useEffect,
4
- useImperativeHandle,
5
- useRef,
6
- InputHTMLAttributes,
7
- } from 'react'
1
+ import { forwardRef, useEffect, useImperativeHandle, useRef, InputHTMLAttributes } from 'react'
8
2
 
9
3
  export default forwardRef(function TextInput(
10
4
  {
@@ -12,8 +12,7 @@ export default function Authenticated({
12
12
  header,
13
13
  children,
14
14
  }: PropsWithChildren<{ user: User; header?: ReactNode }>) {
15
- const [showingNavigationDropdown, setShowingNavigationDropdown] =
16
- useState(false)
15
+ const [showingNavigationDropdown, setShowingNavigationDropdown] = useState(false)
17
16
 
18
17
  const { pathname = '' } = typeof window !== 'undefined' ? window.location : {}
19
18
 
@@ -30,10 +29,7 @@ export default function Authenticated({
30
29
  </div>
31
30
 
32
31
  <div className="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
33
- <NavLink
34
- href={dashboard_path()}
35
- active={pathname.match(/dashboard/) != null}
36
- >
32
+ <NavLink href={dashboard_path()} active={pathname.match(/dashboard/) != null}>
37
33
  Dashboard
38
34
  </NavLink>
39
35
  </div>
@@ -67,14 +63,8 @@ export default function Authenticated({
67
63
  </Dropdown.Trigger>
68
64
 
69
65
  <Dropdown.Content>
70
- <Dropdown.Link href={profile_edit_path()}>
71
- Profile
72
- </Dropdown.Link>
73
- <Dropdown.Link
74
- href={logout_path()}
75
- method="post"
76
- as="button"
77
- >
66
+ <Dropdown.Link href={profile_edit_path()}>Profile</Dropdown.Link>
67
+ <Dropdown.Link href={logout_path()} method="post" as="button">
78
68
  Log Out
79
69
  </Dropdown.Link>
80
70
  </Dropdown.Content>
@@ -84,32 +74,19 @@ export default function Authenticated({
84
74
 
85
75
  <div className="-me-2 flex items-center sm:hidden">
86
76
  <button
87
- onClick={() =>
88
- setShowingNavigationDropdown(
89
- (previousState) => !previousState,
90
- )
91
- }
77
+ onClick={() => setShowingNavigationDropdown((previousState) => !previousState)}
92
78
  className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out"
93
79
  >
94
- <svg
95
- className="h-6 w-6"
96
- stroke="currentColor"
97
- fill="none"
98
- viewBox="0 0 24 24"
99
- >
80
+ <svg className="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
100
81
  <path
101
- className={
102
- !showingNavigationDropdown ? 'inline-flex' : 'hidden'
103
- }
82
+ className={!showingNavigationDropdown ? 'inline-flex' : 'hidden'}
104
83
  strokeLinecap="round"
105
84
  strokeLinejoin="round"
106
85
  strokeWidth="2"
107
86
  d="M4 6h16M4 12h16M4 18h16"
108
87
  />
109
88
  <path
110
- className={
111
- showingNavigationDropdown ? 'inline-flex' : 'hidden'
112
- }
89
+ className={showingNavigationDropdown ? 'inline-flex' : 'hidden'}
113
90
  strokeLinecap="round"
114
91
  strokeLinejoin="round"
115
92
  strokeWidth="2"
@@ -121,34 +98,21 @@ export default function Authenticated({
121
98
  </div>
122
99
  </div>
123
100
 
124
- <div
125
- className={
126
- (showingNavigationDropdown ? 'block' : 'hidden') + ' sm:hidden'
127
- }
128
- >
101
+ <div className={(showingNavigationDropdown ? 'block' : 'hidden') + ' sm:hidden'}>
129
102
  <div className="pt-2 pb-3 space-y-1">
130
- <ResponsiveNavLink
131
- href={dashboard_path()}
132
- active={pathname.match(/dashboard/) != null}
133
- >
103
+ <ResponsiveNavLink href={dashboard_path()} active={pathname.match(/dashboard/) != null}>
134
104
  Dashboard
135
105
  </ResponsiveNavLink>
136
106
  </div>
137
107
 
138
108
  <div className="pt-4 pb-1 border-t border-gray-200">
139
109
  <div className="px-4">
140
- <div className="font-medium text-base text-gray-800">
141
- {user.name}
142
- </div>
143
- <div className="font-medium text-sm text-gray-500">
144
- {user.email}
145
- </div>
110
+ <div className="font-medium text-base text-gray-800">{user.name}</div>
111
+ <div className="font-medium text-sm text-gray-500">{user.email}</div>
146
112
  </div>
147
113
 
148
114
  <div className="mt-3 space-y-1">
149
- <ResponsiveNavLink href={profile_edit_path()}>
150
- Profile
151
- </ResponsiveNavLink>
115
+ <ResponsiveNavLink href={profile_edit_path()}>Profile</ResponsiveNavLink>
152
116
  <ResponsiveNavLink method="post" href={logout_path()} as="button">
153
117
  Log Out
154
118
  </ResponsiveNavLink>
@@ -159,9 +123,7 @@ export default function Authenticated({
159
123
 
160
124
  {header && (
161
125
  <header className="bg-white shadow">
162
- <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
163
- {header}
164
- </div>
126
+ <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">{header}</div>
165
127
  </header>
166
128
  )}
167
129
 
@@ -22,14 +22,11 @@ export default function ForgotPassword({ status }: { status?: string }) {
22
22
  <Head title="Forgot Password" />
23
23
 
24
24
  <div className="mb-4 text-sm text-gray-600">
25
- Forgot your password? No problem. Just let us know your email address
26
- and we will email you a password reset link that will allow you to
27
- choose a new one.
25
+ Forgot your password? No problem. Just let us know your email address and we will email you a password reset
26
+ link that will allow you to choose a new one.
28
27
  </div>
29
28
 
30
- {status && (
31
- <div className="mb-4 font-medium text-sm text-green-600">{status}</div>
32
- )}
29
+ {status && <div className="mb-4 font-medium text-sm text-green-600">{status}</div>}
33
30
 
34
31
  <form onSubmit={submit}>
35
32
  <TextInput
@@ -8,13 +8,7 @@ import TextInput from '@/Components/TextInput'
8
8
  import { Head, Link, useForm } from '@inertiajs/react'
9
9
  import { login_path, password_request_path } from '@/routes'
10
10
 
11
- export default function Login({
12
- status,
13
- canResetPassword,
14
- }: {
15
- status?: string
16
- canResetPassword: boolean
17
- }) {
11
+ export default function Login({ status }: { status?: string }) {
18
12
  const { data, setData, post, processing, errors, reset } = useForm({
19
13
  email: '',
20
14
  password: '',
@@ -37,9 +31,7 @@ export default function Login({
37
31
  <GuestLayout>
38
32
  <Head title="Log in" />
39
33
 
40
- {status && (
41
- <div className="mb-4 font-medium text-sm text-green-600">{status}</div>
42
- )}
34
+ {status && <div className="mb-4 font-medium text-sm text-green-600">{status}</div>}
43
35
 
44
36
  <form onSubmit={submit}>
45
37
  <div>
@@ -77,24 +69,18 @@ export default function Login({
77
69
 
78
70
  <div className="block mt-4">
79
71
  <label className="flex items-center">
80
- <Checkbox
81
- name="remember"
82
- checked={data.remember}
83
- onChange={(e) => setData('remember', e.target.checked)}
84
- />
72
+ <Checkbox name="remember" checked={data.remember} onChange={(e) => setData('remember', e.target.checked)} />
85
73
  <span className="ms-2 text-sm text-gray-600">Remember me</span>
86
74
  </label>
87
75
  </div>
88
76
 
89
77
  <div className="flex items-center justify-end mt-4">
90
- {canResetPassword && (
91
- <Link
92
- href={password_request_path()}
93
- className="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
94
- >
95
- Forgot your password?
96
- </Link>
97
- )}
78
+ <Link
79
+ href={password_request_path()}
80
+ className="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
81
+ >
82
+ Forgot your password?
83
+ </Link>
98
84
 
99
85
  <PrimaryButton className="ms-4" disabled={processing}>
100
86
  Log in
@@ -84,10 +84,7 @@ export default function Register() {
84
84
  </div>
85
85
 
86
86
  <div className="mt-4">
87
- <InputLabel
88
- htmlFor="password_confirmation"
89
- value="Confirm Password"
90
- />
87
+ <InputLabel htmlFor="password_confirmation" value="Confirm Password" />
91
88
 
92
89
  <TextInput
93
90
  id="password_confirmation"
@@ -49,10 +49,7 @@ export default function ResetPassword({ token }: { token: string }) {
49
49
  </div>
50
50
 
51
51
  <div className="mt-4">
52
- <InputLabel
53
- htmlFor="password_confirmation"
54
- value="Confirm Password"
55
- />
52
+ <InputLabel htmlFor="password_confirmation" value="Confirm Password" />
56
53
 
57
54
  <TextInput
58
55
  type="password"