kaze 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kaze/commands/install_command.rb +3 -1
- data/lib/kaze/commands/installs_hotwire_stack.rb +2 -2
- data/lib/kaze/commands/installs_inertia_stacks.rb +7 -7
- data/lib/kaze/version.rb +1 -1
- data/stubs/default/app/mailers/application_mailer.rb +1 -1
- data/stubs/default/app/mailers/user_mailer.rb +16 -1
- data/stubs/default/app/models/concerns/can_reset_password.rb +1 -1
- data/stubs/default/app/models/concerns/must_verify_email.rb +15 -0
- data/stubs/default/app/models/user.rb +1 -0
- data/stubs/default/app/views/user_mailer/reset_password.html.erb +2 -2
- data/stubs/default/app/views/user_mailer/verify_email.html.erb +33 -0
- data/stubs/default/config/routes.rb +4 -0
- data/stubs/default/db/migrate/20240101000000_create_users.rb +1 -0
- data/stubs/default/test/factories/users.rb +5 -0
- data/stubs/default/test/integration/auth/authentication_test.rb +4 -6
- data/stubs/default/test/integration/auth/email_verification_test.rb +40 -0
- data/stubs/default/test/integration/auth/password_reset_test.rb +3 -3
- data/stubs/default/test/integration/password_update_test.rb +2 -2
- data/stubs/default/test/integration/profile_test.rb +4 -4
- data/stubs/hotwire/app/components/modal_component.rb +5 -5
- data/stubs/hotwire/app/controllers/auth/authenticated_session_controller.rb +3 -2
- data/stubs/hotwire/app/controllers/auth/email_verification_notification_controller.rb +21 -0
- data/stubs/hotwire/app/controllers/auth/new_password_controller.rb +2 -2
- data/stubs/hotwire/app/controllers/auth/password_reset_link_controller.rb +1 -1
- data/stubs/hotwire/app/controllers/auth/registered_user_controller.rb +6 -2
- data/stubs/hotwire/app/controllers/auth/verified_email_controller.rb +23 -0
- data/stubs/hotwire/app/controllers/concerns/authenticate.rb +10 -0
- data/stubs/hotwire/app/controllers/concerns/validate_signature.rb +17 -0
- data/stubs/hotwire/app/controllers/password_controller.rb +1 -1
- data/stubs/hotwire/app/controllers/profile_controller.rb +2 -2
- data/stubs/hotwire/app/views/auth/verify_email.html.erb +23 -0
- data/stubs/hotwire/app/views/profile/partials/_delete_user_form.html.erb +1 -1
- data/stubs/inertia-common/app/controllers/auth/authenticated_session_controller.rb +3 -3
- data/stubs/inertia-common/app/controllers/auth/email_verification_notification_controller.rb +21 -0
- data/stubs/inertia-common/app/controllers/auth/new_password_controller.rb +2 -3
- data/stubs/inertia-common/app/controllers/auth/password_reset_link_controller.rb +3 -3
- data/stubs/inertia-common/app/controllers/auth/registered_user_controller.rb +6 -2
- data/stubs/inertia-common/app/controllers/auth/verified_email_controller.rb +23 -0
- data/stubs/inertia-common/app/controllers/concerns/authenticate.rb +10 -0
- data/stubs/inertia-common/app/controllers/concerns/validate_signature.rb +17 -0
- data/stubs/inertia-common/app/controllers/password_controller.rb +1 -1
- data/stubs/inertia-common/app/controllers/profile_controller.rb +2 -2
- data/stubs/inertia-common/test/integration/password_update_test.rb +2 -2
- data/stubs/inertia-common/test/integration/profile_test.rb +4 -4
- data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +1 -4
- data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +3 -5
- data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +4 -25
- data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +1 -4
- data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +1 -4
- data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +3 -5
- data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +3 -5
- data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +3 -5
- data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +1 -7
- data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +14 -52
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +3 -6
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +9 -23
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +1 -4
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +1 -4
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/VerifyEmail.tsx +47 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +2 -8
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +1 -5
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +7 -19
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +7 -15
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +7 -16
- data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +1 -2
- data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +1 -5
- data/stubs/inertia-react-ts/config/tailwind.config.js +1 -6
- data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +3 -1
- data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +1 -4
- data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +3 -13
- data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +10 -45
- data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +2 -6
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +4 -11
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +2 -10
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +1 -5
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +1 -4
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/VerifyEmail.vue +50 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +3 -11
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +2 -10
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +5 -9
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +2 -9
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +3 -13
- data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +2 -5
- data/stubs/inertia-vue-ts/config/tailwind.config.js +1 -6
- 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:
|
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
|
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
|
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
|
13
|
+
form = Auth::SendPasswordResetLinkForm.new(params.permit(:email))
|
14
14
|
|
15
|
-
return redirect_back_or_to password_request_path,
|
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,
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
14
|
-
|
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
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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"
|