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.
- checksums.yaml +4 -4
- data/lib/kaze/commands/install_command.rb +8 -0
- data/lib/kaze/commands/installs_hotwire_stack.rb +5 -2
- data/lib/kaze/commands/installs_inertia_stacks.rb +13 -3
- data/lib/kaze/version.rb +1 -1
- data/stubs/default/app/forms/auth/login_form.rb +2 -8
- data/stubs/default/app/forms/update_profile_information_form.rb +1 -1
- data/stubs/default/app/models/auth.rb +57 -0
- data/stubs/default/app/models/current.rb +1 -1
- data/stubs/default/app/validators/current_password_validator.rb +1 -1
- data/stubs/default/app/views/layouts/mailer.html.erb +367 -372
- data/stubs/default/app/views/layouts/mailer.text.erb +1 -4
- data/stubs/default/app/views/user_mailer/reset_password.html.erb +21 -26
- data/stubs/default/test/factories/users.rb +7 -0
- data/stubs/default/test/integration/auth/authentication_test.rb +43 -0
- data/stubs/default/test/integration/auth/password_reset_test.rb +41 -0
- data/stubs/default/test/integration/auth/registration_test.rb +21 -0
- data/stubs/default/test/integration/password_update_test.rb +28 -0
- data/stubs/default/test/integration/profile_test.rb +51 -0
- data/stubs/default/test/test_helper.rb +38 -0
- data/stubs/hotwire/app/components/dropdown_component.html.erb +17 -18
- data/stubs/hotwire/app/components/modal_component.html.erb +55 -59
- data/stubs/hotwire/app/controllers/application_controller.rb +1 -0
- data/stubs/hotwire/app/controllers/auth/authenticated_session_controller.rb +10 -7
- data/stubs/hotwire/app/controllers/auth/new_password_controller.rb +3 -1
- data/stubs/hotwire/app/controllers/auth/password_reset_link_controller.rb +3 -1
- data/stubs/hotwire/app/controllers/auth/registered_user_controller.rb +4 -2
- data/stubs/hotwire/app/controllers/concerns/authenticate.rb +5 -20
- data/stubs/hotwire/app/controllers/concerns/redirect_if_authenticated.rb +19 -0
- data/stubs/hotwire/app/controllers/concerns/set_current_auth.rb +9 -0
- data/stubs/hotwire/app/controllers/password_controller.rb +1 -1
- data/stubs/hotwire/app/controllers/profile_controller.rb +6 -4
- data/stubs/hotwire/app/controllers/welcome_controller.rb +1 -1
- data/stubs/hotwire/app/javascript/application.js +3 -3
- data/stubs/hotwire/app/views/auth/forgot_password.html.erb +12 -17
- data/stubs/hotwire/app/views/auth/login.html.erb +0 -9
- data/stubs/hotwire/app/views/auth/register.html.erb +0 -13
- data/stubs/hotwire/app/views/auth/reset_password.html.erb +0 -7
- data/stubs/hotwire/app/views/dashboard/index.html.erb +9 -10
- data/stubs/hotwire/app/views/layouts/_navigation.html.erb +77 -87
- data/stubs/hotwire/app/views/layouts/application.html.erb +0 -9
- data/stubs/hotwire/app/views/layouts/guest.html.erb +0 -6
- data/stubs/hotwire/app/views/profile/edit.html.erb +19 -22
- data/stubs/hotwire/app/views/profile/partials/_delete_user_form.html.erb +32 -42
- data/stubs/hotwire/app/views/profile/partials/_update_password_form.html.erb +42 -55
- data/stubs/hotwire/app/views/profile/partials/_update_profile_information_form.html.erb +36 -46
- data/stubs/hotwire/app/views/welcome/index.html.erb +34 -46
- data/stubs/hotwire/config/tailwind.config.js +2 -2
- data/stubs/inertia-common/app/controllers/application_controller.rb +1 -0
- data/stubs/inertia-common/app/controllers/auth/authenticated_session_controller.rb +10 -7
- data/stubs/inertia-common/app/controllers/auth/new_password_controller.rb +3 -1
- data/stubs/inertia-common/app/controllers/auth/password_reset_link_controller.rb +3 -1
- data/stubs/inertia-common/app/controllers/auth/registered_user_controller.rb +4 -2
- data/stubs/inertia-common/app/controllers/concerns/authenticate.rb +5 -20
- data/stubs/inertia-common/app/controllers/concerns/handle_inertia_requests.rb +1 -1
- data/stubs/inertia-common/app/controllers/concerns/redirect_if_authenticated.rb +19 -0
- data/stubs/inertia-common/app/controllers/concerns/set_current_auth.rb +9 -0
- data/stubs/inertia-common/app/controllers/password_controller.rb +1 -1
- data/stubs/inertia-common/app/controllers/profile_controller.rb +5 -3
- data/stubs/inertia-common/app/controllers/welcome_controller.rb +1 -1
- data/stubs/inertia-common/test/integration/password_update_test.rb +28 -0
- data/stubs/inertia-common/test/integration/profile_test.rb +51 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ApplicationLogo.tsx +13 -9
- data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +15 -12
- data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +20 -15
- data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +119 -87
- data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +14 -7
- data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +18 -7
- data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +60 -60
- data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +21 -16
- data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +20 -15
- data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +19 -14
- data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +22 -16
- data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +35 -24
- data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +157 -117
- data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +15 -15
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +52 -49
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +90 -82
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +118 -115
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +63 -60
- data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +23 -17
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +31 -27
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +109 -99
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +121 -113
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +76 -69
- data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +87 -63
- data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +32 -25
- data/stubs/inertia-react-ts/app/views/layouts/application.html.erb +0 -4
- data/stubs/inertia-react-ts/config/tailwind.config.js +2 -2
- data/stubs/inertia-react-ts/vite.config.ts +2 -5
- data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +10 -6
- data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +18 -18
- data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +5 -5
- data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +60 -57
- data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +9 -9
- data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +7 -7
- data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +6 -6
- data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +84 -74
- data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +12 -12
- data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +5 -5
- data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +12 -12
- data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +13 -13
- data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +13 -13
- data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +168 -136
- data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +15 -13
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +56 -49
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +78 -72
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +101 -97
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +71 -68
- data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +22 -14
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +34 -30
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +87 -83
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +105 -98
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +69 -59
- data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +74 -47
- data/stubs/inertia-vue-ts/app/views/layouts/application.html.erb +0 -4
- data/stubs/inertia-vue-ts/config/tailwind.config.js +2 -2
- data/stubs/inertia-vue-ts/vite.config.ts +2 -5
- metadata +18 -6
- data/stubs/hotwire/bin/vite +0 -27
- data/stubs/inertia-common/Procfile.dev +0 -3
- /data/stubs/{hotwire → default}/Procfile.dev +0 -0
- /data/stubs/hotwire/app/javascript/{alpinejs.js → alpinejs.stub} +0 -0
@@ -1,13 +1,10 @@
|
|
1
1
|
<!-- Header -->
|
2
2
|
<%= ENV.fetch("APP_NAME", "Rails") %>: <%= root_url %>
|
3
|
-
|
4
3
|
<!-- Body -->
|
5
4
|
<%= yield %>
|
6
|
-
|
7
5
|
<!-- Subcopy -->
|
8
6
|
<% if content_for?(:subcopy) %>
|
9
|
-
<%= yield :subcopy %>
|
7
|
+
<%= yield :subcopy %>
|
10
8
|
<% end %>
|
11
|
-
|
12
9
|
<!-- Footer -->
|
13
10
|
© <%= Time.new.year %> <%= ENV.fetch("APP_NAME", "Rails") %>. All rights reserved.
|
@@ -1,39 +1,34 @@
|
|
1
1
|
<!-- Greeting -->
|
2
2
|
<h1>Hello!</h1>
|
3
|
-
|
4
3
|
<!-- Intro Lines -->
|
5
4
|
<p>You are receiving this email because we received a password reset request for your account.</p>
|
6
|
-
|
7
5
|
<!-- Action Button -->
|
8
6
|
<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
9
|
-
<tr>
|
10
|
-
<td align="center">
|
11
|
-
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
|
12
|
-
<tr>
|
13
|
-
<td align="center">
|
14
|
-
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
|
15
|
-
<tr>
|
16
|
-
<td>
|
17
|
-
<a href="<%= password_reset_url(token: params[:token]) %>" class="button button-primary" target="_blank" rel="noopener">Reset Password</a>
|
18
|
-
</td>
|
19
|
-
</tr>
|
7
|
+
<tr>
|
8
|
+
<td align="center">
|
9
|
+
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
|
10
|
+
<tr>
|
11
|
+
<td align="center">
|
12
|
+
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
|
13
|
+
<tr>
|
14
|
+
<td>
|
15
|
+
<a href="<%= password_reset_url(token: params[:token]) %>" class="button button-primary" target="_blank" rel="noopener">Reset Password</a>
|
16
|
+
</td>
|
17
|
+
</tr>
|
18
|
+
</table>
|
19
|
+
</td>
|
20
|
+
</tr>
|
21
|
+
</table>
|
22
|
+
</td>
|
23
|
+
</tr>
|
20
24
|
</table>
|
21
|
-
</td>
|
22
|
-
</tr>
|
23
|
-
</table>
|
24
|
-
</td>
|
25
|
-
</tr>
|
26
|
-
</table>
|
27
|
-
|
28
25
|
<!-- Outro Lines -->
|
29
26
|
<p>This password reset link will expire in 60 minutes.</p>
|
30
|
-
|
31
27
|
<p>If you did not request a password reset, no further action is required.</p>
|
32
|
-
|
33
28
|
<!-- Salutation -->
|
34
|
-
<p>Regards,<br
|
35
|
-
|
29
|
+
<p>Regards,<br>
|
30
|
+
<%= ENV.fetch("APP_NAME", "Rails") %></p>
|
36
31
|
<!-- Subcopy -->
|
37
32
|
<% content_for :subcopy do %>
|
38
|
-
<p>If you're having trouble clicking the "Reset Password" button, copy and paste the URL below into your web browser: <span class="break-all"><%= link_to password_reset_url(token: params[:token]), password_reset_url(token: params[:token]) %><span></p>
|
39
|
-
<% end %>
|
33
|
+
<p>If you're having trouble clicking the "Reset Password" button, copy and paste the URL below into your web browser: <span class="break-all"><%= link_to password_reset_url(token: params[:token]), password_reset_url(token: params[:token]) %><span></p>
|
34
|
+
<% end %>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Auth::AuthenticationTest < ActionDispatch::IntegrationTest
|
4
|
+
test 'login screen can be rendered' do
|
5
|
+
get login_path
|
6
|
+
|
7
|
+
assert_response :success
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'users can authenticate using the login screen' do
|
11
|
+
user = FactoryBot.create :user
|
12
|
+
|
13
|
+
post login_path, params: {
|
14
|
+
email: user.email,
|
15
|
+
password: 'password'
|
16
|
+
}
|
17
|
+
|
18
|
+
assert_authenticated
|
19
|
+
assert_redirected_to dashboard_path
|
20
|
+
end
|
21
|
+
|
22
|
+
test 'users cannot authenticate with invalid password' do
|
23
|
+
user = FactoryBot.create :user
|
24
|
+
|
25
|
+
post login_path, params: {
|
26
|
+
email: user.email,
|
27
|
+
password: 'wrong-password'
|
28
|
+
}
|
29
|
+
|
30
|
+
assert_guest
|
31
|
+
end
|
32
|
+
|
33
|
+
test 'users can logout' do
|
34
|
+
user = FactoryBot.create :user
|
35
|
+
|
36
|
+
acting_as user
|
37
|
+
|
38
|
+
post logout_path
|
39
|
+
|
40
|
+
assert_guest
|
41
|
+
assert_redirected_to '/'
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Auth::PasswordResetTest < ActionDispatch::IntegrationTest
|
4
|
+
test 'reset password link screen can be rendered' do
|
5
|
+
get password_request_path
|
6
|
+
|
7
|
+
assert_response :success
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'reset password link can be requested' do
|
11
|
+
user = FactoryBot.create :user
|
12
|
+
token = user.generate_token_for(:password_reset)
|
13
|
+
email = UserMailer.with(user: user, token: token).reset_password
|
14
|
+
|
15
|
+
post password_email_path, params: { email: user.email }
|
16
|
+
|
17
|
+
assert_enqueued_email_with UserMailer, :reset_password, params: { user: user, token: token } do
|
18
|
+
email.deliver_later
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
test 'reset password screen can be rendered' do
|
23
|
+
user = FactoryBot.create :user
|
24
|
+
|
25
|
+
get password_reset_path(token: user.generate_token_for(:password_reset))
|
26
|
+
|
27
|
+
assert_response :success
|
28
|
+
end
|
29
|
+
|
30
|
+
test 'password can be reset_with_valid_token' do
|
31
|
+
user = FactoryBot.create :user
|
32
|
+
|
33
|
+
post password_store_path, params: {
|
34
|
+
token: user.generate_token_for(:password_reset),
|
35
|
+
password: 'password',
|
36
|
+
password_confirmation: 'password'
|
37
|
+
}
|
38
|
+
|
39
|
+
assert_redirected_to login_path
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Auth::RegistrationTest < ActionDispatch::IntegrationTest
|
4
|
+
test 'registration screen can be rendered' do
|
5
|
+
get register_path
|
6
|
+
|
7
|
+
assert_response :success
|
8
|
+
end
|
9
|
+
|
10
|
+
test 'new users can register' do
|
11
|
+
post register_path, params: {
|
12
|
+
name: 'Test User',
|
13
|
+
email: 'test@example.com',
|
14
|
+
password: 'password',
|
15
|
+
password_confirmation: 'password'
|
16
|
+
}
|
17
|
+
|
18
|
+
assert_authenticated
|
19
|
+
assert_redirected_to dashboard_url
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PasswordUpdateTest < ActionDispatch::IntegrationTest
|
4
|
+
test 'password can be updated' do
|
5
|
+
user = FactoryBot.create :user
|
6
|
+
|
7
|
+
acting_as(user).put password_update_path, params: {
|
8
|
+
current_password: 'password',
|
9
|
+
password: 'new-password',
|
10
|
+
password_confirmation: 'new-password'
|
11
|
+
}
|
12
|
+
|
13
|
+
assert_redirected_to profile_edit_path
|
14
|
+
assert BCrypt::Password.new(user.reload.password_digest).is_password?('new-password')
|
15
|
+
end
|
16
|
+
|
17
|
+
test 'correct password must be provided to update password' do
|
18
|
+
user = FactoryBot.create :user
|
19
|
+
|
20
|
+
acting_as(user).put password_update_path, params: {
|
21
|
+
current_password: 'wrong-password',
|
22
|
+
password: 'new-password',
|
23
|
+
password_confirmation: 'new-password'
|
24
|
+
}
|
25
|
+
|
26
|
+
assert_response :unprocessable_entity
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ProfileTest < ActionDispatch::IntegrationTest
|
4
|
+
test 'profile page is displayed' do
|
5
|
+
user = FactoryBot.create :user
|
6
|
+
|
7
|
+
acting_as(user).get profile_edit_path
|
8
|
+
|
9
|
+
assert_response :success
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'profile information can be updated' do
|
13
|
+
user = FactoryBot.create :user
|
14
|
+
|
15
|
+
acting_as(user).patch profile_edit_path, params: {
|
16
|
+
name: 'Test User',
|
17
|
+
email: 'test@example.com'
|
18
|
+
}
|
19
|
+
|
20
|
+
assert_redirected_to profile_edit_path
|
21
|
+
|
22
|
+
user.reload
|
23
|
+
|
24
|
+
assert_equal 'Test User', user.name
|
25
|
+
assert_equal 'test@example.com', user.email
|
26
|
+
end
|
27
|
+
|
28
|
+
test 'user can delete their account' do
|
29
|
+
user = FactoryBot.create :user
|
30
|
+
|
31
|
+
acting_as(user).delete profile_destroy_path, params: {
|
32
|
+
password: 'password'
|
33
|
+
}
|
34
|
+
|
35
|
+
assert_redirected_to '/'
|
36
|
+
|
37
|
+
assert_guest
|
38
|
+
assert_raise(ActiveRecord::RecordNotFound) { user.reload }
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'correct password must be provided to delete account' do
|
42
|
+
user = FactoryBot.create :user
|
43
|
+
|
44
|
+
acting_as(user).delete profile_destroy_path, params: {
|
45
|
+
password: 'wrong-password'
|
46
|
+
}
|
47
|
+
|
48
|
+
assert_response :unprocessable_entity
|
49
|
+
assert_not_nil user.reload
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
ENV['RAILS_ENV'] ||= 'test'
|
2
|
+
require_relative '../config/environment'
|
3
|
+
require 'rails/test_help'
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
class TestCase
|
7
|
+
# Run tests in parallel with specified workers
|
8
|
+
parallelize(workers: :number_of_processors)
|
9
|
+
|
10
|
+
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
11
|
+
fixtures :all
|
12
|
+
|
13
|
+
# Add more helper methods to be used by all tests here...
|
14
|
+
def acting_as(user)
|
15
|
+
post login_path, params: { email: user.email, password: 'password' }
|
16
|
+
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_auth
|
21
|
+
Auth.new('web', session)
|
22
|
+
end
|
23
|
+
|
24
|
+
def assert_authenticated
|
25
|
+
assert is_authenticated, 'The user is not authenticated'
|
26
|
+
end
|
27
|
+
|
28
|
+
def assert_guest
|
29
|
+
assert_not is_authenticated, 'The user is authenticated'
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def is_authenticated
|
35
|
+
current_auth.check?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,20 +1,19 @@
|
|
1
|
-
<div class="relative" x-data="{ open: false }"
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
<%= content %>
|
18
|
-
</div>
|
1
|
+
<div class="relative" x-data="{ open: false }" x-on:click.outside="open = false" x-on:close.stop="open = false">
|
2
|
+
<div x-on:click="open = ! open">
|
3
|
+
<%= trigger %>
|
4
|
+
</div>
|
5
|
+
<div x-show="open"
|
6
|
+
x-transition:enter="transition ease-out duration-200"
|
7
|
+
x-transition:enter-start="opacity-0 scale-95"
|
8
|
+
x-transition:enter-end="opacity-100 scale-100"
|
9
|
+
x-transition:leave="transition ease-in duration-75"
|
10
|
+
x-transition:leave-start="opacity-100 scale-100"
|
11
|
+
x-transition:leave-end="opacity-0 scale-95"
|
12
|
+
class="absolute z-50 mt-2 <%= @width %> rounded-md shadow-lg <%= @alignment_classes %>"
|
13
|
+
style="display: none;"
|
14
|
+
x-on:click="open = false">
|
15
|
+
<div class="rounded-md ring-1 ring-black ring-opacity-5 <%= @content_classes %>">
|
16
|
+
<%= content %>
|
19
17
|
</div>
|
18
|
+
</div>
|
20
19
|
</div>
|
@@ -1,62 +1,58 @@
|
|
1
1
|
<div
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
2
|
+
x-data="{
|
3
|
+
show: <%= @show %>,
|
4
|
+
focusables() {
|
5
|
+
// All focusable element types...
|
6
|
+
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
|
7
|
+
return [...$el.querySelectorAll(selector)]
|
8
|
+
// All non-disabled elements...
|
9
|
+
.filter(el => ! el.hasAttribute('disabled'))
|
10
|
+
},
|
11
|
+
firstFocusable() { return this.focusables()[0] },
|
12
|
+
lastFocusable() { return this.focusables().slice(-1)[0] },
|
13
|
+
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
|
14
|
+
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
|
15
|
+
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
|
16
|
+
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
|
17
|
+
}"
|
18
|
+
x-init="$watch('show', value => {
|
19
|
+
if (value) {
|
20
|
+
document.body.classList.add('overflow-y-hidden')
|
21
|
+
<%= @attributes.has_key?(:focusable) ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' %>
|
22
|
+
} else {
|
23
|
+
document.body.classList.remove('overflow-y-hidden')
|
24
|
+
}
|
25
|
+
})"
|
26
|
+
x-on:open-modal.window="$event.detail == '<%= @name %>' ? show = true : null"
|
27
|
+
x-on:close-modal.window="$event.detail == '<%= @name %>' ? show = false : null"
|
28
|
+
x-on:close.stop="show = false"
|
29
|
+
x-on:keydown.escape.window="show = false"
|
30
|
+
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
|
31
|
+
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
|
32
|
+
x-show="show"
|
33
|
+
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
|
34
|
+
style="display: <%= @show ? 'block' : 'none' %>">
|
35
|
+
<div
|
32
36
|
x-show="show"
|
33
|
-
class="fixed inset-0
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
55
|
-
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
56
|
-
x-transition:leave="ease-in duration-200"
|
57
|
-
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
58
|
-
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
59
|
-
>
|
60
|
-
<%= content %>
|
61
|
-
</div>
|
37
|
+
class="fixed inset-0 transform transition-all"
|
38
|
+
x-on:click="show = false"
|
39
|
+
x-transition:enter="ease-out duration-300"
|
40
|
+
x-transition:enter-start="opacity-0"
|
41
|
+
x-transition:enter-end="opacity-100"
|
42
|
+
x-transition:leave="ease-in duration-200"
|
43
|
+
x-transition:leave-start="opacity-100"
|
44
|
+
x-transition:leave-end="opacity-0">
|
45
|
+
<div class="absolute inset-0 bg-gray-500 dark:bg-gray-900 opacity-75"></div>
|
46
|
+
</div>
|
47
|
+
<div
|
48
|
+
x-show="show"
|
49
|
+
class="mb-6 bg-white dark:bg-gray-800 rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full <%= @max_width %> sm:mx-auto"
|
50
|
+
x-transition:enter="ease-out duration-300"
|
51
|
+
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
52
|
+
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
53
|
+
x-transition:leave="ease-in duration-200"
|
54
|
+
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
55
|
+
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
|
56
|
+
<%= content %>
|
57
|
+
</div>
|
62
58
|
</div>
|
@@ -1,5 +1,8 @@
|
|
1
1
|
class Auth::AuthenticatedSessionController < ApplicationController
|
2
|
-
|
2
|
+
include RedirectIfAuthenticated
|
3
|
+
|
4
|
+
skip_authenticate only: %i[new create]
|
5
|
+
skip_redirect_if_authenticated only: %i[destroy]
|
3
6
|
|
4
7
|
layout 'guest'
|
5
8
|
|
@@ -12,18 +15,18 @@ class Auth::AuthenticatedSessionController < ApplicationController
|
|
12
15
|
def create
|
13
16
|
@form = Auth::LoginForm.new params.permit(:email, :password)
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
return render 'auth/login', status: :unprocessable_entity if user.nil?
|
18
|
+
@form.authenticate
|
18
19
|
|
19
|
-
login user
|
20
|
+
return render 'auth/login', status: :unprocessable_entity if Current.auth.user.nil?
|
20
21
|
|
21
22
|
redirect_to dashboard_path
|
22
23
|
end
|
23
24
|
|
24
25
|
def destroy
|
25
|
-
logout
|
26
|
+
Current.auth.logout
|
27
|
+
|
28
|
+
reset_session
|
26
29
|
|
27
|
-
redirect_to
|
30
|
+
redirect_to '/'
|
28
31
|
end
|
29
32
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
class Auth::RegisteredUserController < ApplicationController
|
2
|
-
|
2
|
+
include RedirectIfAuthenticated
|
3
|
+
|
4
|
+
skip_authenticate
|
3
5
|
|
4
6
|
layout 'guest'
|
5
7
|
|
@@ -16,7 +18,7 @@ class Auth::RegisteredUserController < ApplicationController
|
|
16
18
|
|
17
19
|
user = User.create(name: @form.name, email: @form.email, password: @form.password)
|
18
20
|
|
19
|
-
login user
|
21
|
+
Current.auth.login user
|
20
22
|
|
21
23
|
redirect_to dashboard_path
|
22
24
|
end
|
@@ -2,33 +2,18 @@ module Authenticate
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
before_action :
|
5
|
+
before_action :authenticate!
|
6
6
|
end
|
7
7
|
|
8
8
|
class_methods do
|
9
|
-
def
|
10
|
-
skip_before_action :
|
9
|
+
def skip_authenticate(**options)
|
10
|
+
skip_before_action :authenticate!, **options
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
Current.user = user
|
19
|
-
else
|
20
|
-
redirect_to login_path
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def login(user)
|
25
|
-
Current.user = user
|
26
|
-
reset_session
|
27
|
-
session[:user_id] = user.id
|
28
|
-
end
|
29
|
-
|
30
|
-
def logout
|
31
|
-
Current.user = nil
|
32
|
-
reset_session
|
16
|
+
def authenticate!
|
17
|
+
redirect_to login_path unless Current.auth.check?
|
33
18
|
end
|
34
19
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RedirectIfAuthenticated
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
before_action :redirect_if_authenticated!
|
6
|
+
end
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
def skip_redirect_if_authenticated(**options)
|
10
|
+
skip_before_action :redirect_if_authenticated!, **options
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def redirect_if_authenticated!
|
17
|
+
redirect_to dashboard_path if Current.auth.check?
|
18
|
+
end
|
19
|
+
end
|
@@ -4,7 +4,7 @@ class PasswordController < ApplicationController
|
|
4
4
|
|
5
5
|
return render partial: 'profile/partials/update_password_form', status: :unprocessable_entity if @update_password_form.invalid?
|
6
6
|
|
7
|
-
Current.user.update(password: @update_password_form.password)
|
7
|
+
Current.auth.user.update(password: @update_password_form.password)
|
8
8
|
|
9
9
|
redirect_back_or_to profile_edit_path, flash: { status: 'password-updated' }
|
10
10
|
end
|