refactor: ⚡ Modal layout and turbolinks optimization
Reload with turbolinks, error messages, confirm password validation Login and Signup modals
This commit is contained in:
@@ -58,4 +58,7 @@ group :test do
|
|||||||
gem 'webdrivers'
|
gem 'webdrivers'
|
||||||
# SimpleCov is a code coverage analysis tool for Ruby
|
# SimpleCov is a code coverage analysis tool for Ruby
|
||||||
gem 'simplecov'
|
gem 'simplecov'
|
||||||
|
|
||||||
|
# Simple console output formatter for SimpleCov
|
||||||
|
gem 'simplecov-console'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ GEM
|
|||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.7.0)
|
addressable (2.7.0)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
|
ansi (1.5.0)
|
||||||
arel (9.0.0)
|
arel (9.0.0)
|
||||||
bcrypt (3.1.16)
|
bcrypt (3.1.16)
|
||||||
bcrypt (3.1.16-java)
|
bcrypt (3.1.16-java)
|
||||||
@@ -157,6 +158,10 @@ GEM
|
|||||||
docile (~> 1.1)
|
docile (~> 1.1)
|
||||||
simplecov-html (~> 0.11)
|
simplecov-html (~> 0.11)
|
||||||
simplecov_json_formatter (~> 0.1)
|
simplecov_json_formatter (~> 0.1)
|
||||||
|
simplecov-console (0.9.1)
|
||||||
|
ansi
|
||||||
|
simplecov
|
||||||
|
terminal-table
|
||||||
simplecov-html (0.12.3)
|
simplecov-html (0.12.3)
|
||||||
simplecov_json_formatter (0.1.3)
|
simplecov_json_formatter (0.1.3)
|
||||||
spring (2.1.1)
|
spring (2.1.1)
|
||||||
@@ -170,6 +175,8 @@ GEM
|
|||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
|
terminal-table (3.0.1)
|
||||||
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
thor (1.1.0)
|
thor (1.1.0)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
thread_safe (0.3.6-java)
|
thread_safe (0.3.6-java)
|
||||||
@@ -180,6 +187,7 @@ GEM
|
|||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uglifier (4.2.0)
|
uglifier (4.2.0)
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
|
unicode-display_width (2.0.0)
|
||||||
web-console (3.7.0)
|
web-console (3.7.0)
|
||||||
actionview (>= 5.0)
|
actionview (>= 5.0)
|
||||||
activemodel (>= 5.0)
|
activemodel (>= 5.0)
|
||||||
@@ -221,6 +229,7 @@ DEPENDENCIES
|
|||||||
rails (~> 5.2.6)
|
rails (~> 5.2.6)
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
simplecov
|
simplecov
|
||||||
|
simplecov-console
|
||||||
spring
|
spring
|
||||||
spring-watcher-listen (~> 2.0.0)
|
spring-watcher-listen (~> 2.0.0)
|
||||||
turbolinks (~> 5)
|
turbolinks (~> 5)
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
- Setup
|
- Run migrations
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker-compose up -d db
|
||||||
docker-compose run --rm app bundle exec rails db:migrate
|
docker-compose run --rm app bundle exec rails db:migrate
|
||||||
docker-compose stop
|
|
||||||
```
|
```
|
||||||
- Run
|
- Run
|
||||||
```bash
|
```bash
|
||||||
@@ -15,7 +14,6 @@ docker-compose up
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
- Run tests
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose run --rm app bundle exec rails test
|
docker-compose run --rm app bundle exec rails test
|
||||||
```
|
```
|
||||||
@@ -37,5 +35,6 @@ docker-compose run --rm app rubocop
|
|||||||
- [x] Add userId key to link model
|
- [x] Add userId key to link model
|
||||||
- [x] Login and logout (sessions)
|
- [x] Login and logout (sessions)
|
||||||
- [x] User links view
|
- [x] User links view
|
||||||
- [ ] Modals layout
|
- [x] Modals layout
|
||||||
- [ ] Setup Redis for production cache_store
|
- [ ] Setup Redis for production cache_store
|
||||||
|
- [ ] Deployment CI
|
||||||
@@ -7,15 +7,15 @@ class SessionsController < ApplicationController
|
|||||||
@user = User.find_by(username: session_params[:username])
|
@user = User.find_by(username: session_params[:username])
|
||||||
if @user&.authenticate(session_params[:password])
|
if @user&.authenticate(session_params[:password])
|
||||||
session[:user_id] = @user.id
|
session[:user_id] = @user.id
|
||||||
redirect_to '/'
|
render json: nil, status: :ok
|
||||||
else
|
else
|
||||||
render json: nil, status: :unauthorized
|
render json: { username: ['Credentials not valid, try again or create an account'] }, status: :unauthorized
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
reset_session
|
reset_session
|
||||||
redirect_to '/'
|
render json: nil, status: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
|
|
||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
def create
|
def create
|
||||||
|
if user_params[:password] != params[:user][:confirm_password]
|
||||||
|
return render json: { password: ['Password not match with Confirm Password'] }, status: :bad_request
|
||||||
|
end
|
||||||
|
|
||||||
@user = User.create(user_params)
|
@user = User.create(user_params)
|
||||||
if @user.errors.any?
|
if @user.errors.any?
|
||||||
render json: @user.errors, status: :unprocessable_entity
|
render json: @user.errors, status: :unprocessable_entity
|
||||||
else
|
else
|
||||||
session[:user_id] = @user.id
|
session[:user_id] = @user.id
|
||||||
redirect_to '/'
|
render json: nil, status: :ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default class extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openSignupModal() {
|
openSignupModal() {
|
||||||
|
this.closeLoginModal()
|
||||||
this.signupModalTarget.classList.remove("hidden")
|
this.signupModalTarget.classList.remove("hidden")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,18 +22,38 @@ export default class extends Controller {
|
|||||||
|
|
||||||
onSignupSuccess() {
|
onSignupSuccess() {
|
||||||
this.closeSignupModal();
|
this.closeSignupModal();
|
||||||
|
Turbolinks.visit('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoginSuccess() {
|
onLoginSuccess() {
|
||||||
this.closeLoginModal();
|
this.closeLoginModal();
|
||||||
|
Turbolinks.visit('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(event) {
|
onError(event) {
|
||||||
const [data, ,] = event.detail
|
const [data, ,] = event.detail
|
||||||
|
|
||||||
const usernameError = `Username: ${data.username.join(' ')}`
|
const errors = []
|
||||||
const passwordError = `Password: ${data.username.join(' ')}`
|
|
||||||
|
|
||||||
alert(`${usernameError}, ${passwordError}`)
|
if (data.username) {
|
||||||
|
errors.push(`Username: ${data.username.join(' ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.password) {
|
||||||
|
errors.push(`Password: ${data.password.join(' ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(errors.join(','))
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmLogout(event) {
|
||||||
|
if (!window.confirm("Do you really want to leave?")) {
|
||||||
|
event.stopPropagation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccessLogout() {
|
||||||
|
Turbolinks.visit('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
a {
|
a, button, input[type=submit] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<div data-users-target="<%= target %>" class="fixed z-10 inset-0 overflow-y-auto hidden" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
||||||
|
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||||
|
<!--
|
||||||
|
Background overlay, show/hide based on modal state.
|
||||||
|
|
||||||
|
Entering: "ease-out duration-300"
|
||||||
|
From: "opacity-0"
|
||||||
|
To: "opacity-100"
|
||||||
|
Leaving: "ease-in duration-200"
|
||||||
|
From: "opacity-100"
|
||||||
|
To: "opacity-0"
|
||||||
|
-->
|
||||||
|
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<!-- This element is to trick the browser into centering the modal contents. -->
|
||||||
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Modal panel, show/hide based on modal state.
|
||||||
|
|
||||||
|
Entering: "ease-out duration-300"
|
||||||
|
From: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
To: "opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
Leaving: "ease-in duration-200"
|
||||||
|
From: "opacity-100 translate-y-0 sm:scale-100"
|
||||||
|
To: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
|
-->
|
||||||
|
<%= yield %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
<%= form_with model: Link.new, url: links_path(@link), data: { action: 'ajax:success->links#onCreateLinkSuccess ajax:error->links#onCreateLinkError' } do |f| %>
|
<%= form_with model: Link.new, url: links_path(@link), data: { action: 'ajax:success->links#onCreateLinkSuccess ajax:error->links#onCreateLinkError' } do |f| %>
|
||||||
<div class="col-span-3 sm:col-span-2">
|
<div class="col-span-3 sm:col-span-2">
|
||||||
<label for="company_website" class="block text-sm font-medium text-gray-700">
|
<label for="company_website" class="text-lg leading-6 font-medium text-gray-900">
|
||||||
Website
|
Shorten an url
|
||||||
</label>
|
</label>
|
||||||
<div class="mt-1 flex rounded-md shadow-sm">
|
<div class="mt-4 flex rounded-md shadow-sm mr-5">
|
||||||
<%= f.text_field :url, data: { target: "links.url" }, placeholder: true, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300"%>
|
<%= f.text_field :url, data: { target: "links.url" }, placeholder: "https://google.com", class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
|
<div class="px-4 py-3 text-right sm:px-6">
|
||||||
<%= f.submit "Generate", class: "inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %>
|
<%= f.submit "Generate", class: "inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<div data-links-target="userLinks">
|
|
||||||
<% if logged_in? %>
|
<% if logged_in? %>
|
||||||
<% @current_user.links.each do |link| %>
|
<h2 class="text-2xl leading-7 text-gray-900 sm:text-3xl sm:truncate m-3 mt-6">My links</h2>
|
||||||
|
<% @current_user.links.each do |link| %>
|
||||||
<%= render partial: "links/show", locals: { link: link } %>
|
<%= render partial: "links/show", locals: { link: link } %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
|
||||||
@@ -1,12 +1,4 @@
|
|||||||
<div class="flex flex-col">
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg m-3 p-6">
|
||||||
<div class="py-2 align-middle inline-block sm:px-6 lg:px-8">
|
<%= render partial: "links/form" %>
|
||||||
<div class="shadow sm:rounded-md sm:overflow-hidden">
|
<div data-links-target="output"></div>
|
||||||
<div class="px-4 py-5 bg-white space-y-6 sm:p-6">
|
|
||||||
<div class="grid grid-cols-3 gap-6">
|
|
||||||
<%= render partial: "links/form" %>
|
|
||||||
<div data-links-target="output"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,30 @@
|
|||||||
<div class="col-span-3 sm:col-span-2">
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg m-3">
|
||||||
<label for="about" class="block text-sm font-medium text-gray-700">
|
<div class="border-t border-gray-200">
|
||||||
Short url
|
<dl>
|
||||||
</label>
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
<a href="<%= link.short %>" class="mt-1 text-sm text-gray-600" target="_blank" rel="noreferrer"><%= link.short %></a>
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
</div>
|
Website
|
||||||
|
</dt>
|
||||||
<div class="col-span-3 sm:col-span-2">
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
<label for="about" class="block text-sm font-medium text-gray-700">
|
<a href="<%= link.url %>" class="mt-1 text-sm text-gray-600" target="_blank" rel="noreferrer"><%= link.url %></a>
|
||||||
Click counter
|
</dd>
|
||||||
</label>
|
</div>
|
||||||
<p class="mt-2 text-sm text-gray-500"><%= link.click_counter %></p>
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
|
Shortened url
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
<a href="<%= link.short %>" class="mt-1 text-sm text-gray-600" target="_blank" rel="noreferrer"><%= link.short %></a>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
|
Click counter
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
<%= link.click_counter %>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,61 +1,34 @@
|
|||||||
<div data-users-target="loginModal" class="fixed z-10 inset-0 overflow-y-auto hidden" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
<%= form_with url: '/login', data: { action: 'ajax:success->users#onLoginSuccess ajax:error->users#onError' }, class: "inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" do |f| %>
|
||||||
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||||
<!--
|
<div class="sm:flex sm:items-start">
|
||||||
Background overlay, show/hide based on modal state.
|
<div class="flex-1 mt-3 text-center sm:mt-0 sm:mx-4 sm:text-left">
|
||||||
|
<h3 class="text-xl leading-6 font-medium text-gray-900" id="modal-title">
|
||||||
Entering: "ease-out duration-300"
|
Login
|
||||||
From: "opacity-0"
|
</h3>
|
||||||
To: "opacity-100"
|
<div class="flex-col mt-4">
|
||||||
Leaving: "ease-in duration-200"
|
<div class="my-2">
|
||||||
From: "opacity-100"
|
<label for="username" class="block text-sm font-medium text-gray-700">
|
||||||
To: "opacity-0"
|
Username
|
||||||
-->
|
</label>
|
||||||
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
|
<%= f.text_field :username, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||||
|
|
||||||
<!-- This element is to trick the browser into centering the modal contents. -->
|
|
||||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Modal panel, show/hide based on modal state.
|
|
||||||
|
|
||||||
Entering: "ease-out duration-300"
|
|
||||||
From: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
To: "opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
Leaving: "ease-in duration-200"
|
|
||||||
From: "opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
To: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
-->
|
|
||||||
<%= form_with url: '/login', data: { action: 'ajax:success->users#onLoginSuccess ajax:error->users#onError' }, class: "inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" do |f| %>
|
|
||||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
|
||||||
<div class="sm:flex sm:items-start">
|
|
||||||
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
|
||||||
<!-- Heroicon name: outline/exclamation -->
|
|
||||||
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
<div class="my-2">
|
||||||
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
<label for="password" class="block text-sm font-medium text-gray-700">
|
||||||
Login
|
Password
|
||||||
</h3>
|
</label>
|
||||||
<div class="mt-2">
|
<div class="mt-1 flex rounded-md shadow-sm">
|
||||||
<p class="text-sm text-gray-500">
|
<%= f.password_field :password, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||||
Are you sure you want to deactivate your account? All of your data will be permanently removed. This action cannot be undone.
|
|
||||||
</p>
|
|
||||||
<%= f.label :username%><br>
|
|
||||||
<%= f.text_field :username%><br>
|
|
||||||
<%= f.label :password%><br>
|
|
||||||
<%= f.password_field :password%><br>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
</div>
|
||||||
<%= f.submit "Login", class: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
|
<button type="button" data-action="click->users#openSignupModal" class="block text-sm font-medium pl-4 pt-6 underline">Create an account</button>
|
||||||
<button data-action="users#closeLoginModal" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<%end%>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||||
|
<%= f.submit "Login", class: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
|
||||||
|
<button data-action="click->users#closeLoginModal" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<%end%>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div data-controller="users">
|
||||||
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||||
|
<% unless logged_in? %>
|
||||||
|
<button data-action="click->users#openLoginModal" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
<% end %>
|
||||||
|
<%= render partial: "users/show" %>
|
||||||
|
</div>
|
||||||
|
<%= render partial: "users/new", layout: "layouts/modal", locals: { target: 'signupModal' } %>
|
||||||
|
<%= render partial: "sessions/new", layout: "layouts/modal", locals: { target: 'loginModal' } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-controller="links">
|
||||||
|
<%= render partial: "links/new" %>
|
||||||
|
<%= render partial: "links/index" %>
|
||||||
|
</div>
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<div data-controller="users">
|
|
||||||
<button data-action="users#openLoginModal">Login</button>
|
|
||||||
<button data-action="users#openSignupModal">Sign Up</button>
|
|
||||||
|
|
||||||
<%= render partial: "users/show" %>
|
|
||||||
<%= render partial: "users/new" %>
|
|
||||||
<%= render partial: "sessions/new" %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-controller="links">
|
|
||||||
<%= render partial: "links/new" %>
|
|
||||||
<%= render partial: "links/index" %>
|
|
||||||
</div>
|
|
||||||
@@ -1,61 +1,39 @@
|
|||||||
<div data-users-target="signupModal" class="fixed z-10 inset-0 overflow-y-auto hidden" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
<%= form_with model: User.new, url: users_path(@user), data: { action: 'ajax:success->users#onSignupSuccess ajax:error->users#onError' }, class: "inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" do |f| %>
|
||||||
<div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||||
<!--
|
<div class="flex-1 mt-3 text-center sm:mt-0 sm:mx-4 sm:text-left">
|
||||||
Background overlay, show/hide based on modal state.
|
<h3 class="text-xl leading-6 font-medium text-gray-900" id="modal-title">
|
||||||
|
Sign Up
|
||||||
Entering: "ease-out duration-300"
|
</h3>
|
||||||
From: "opacity-0"
|
<div class="flex-col mt-4">
|
||||||
To: "opacity-100"
|
<div class="my-2">
|
||||||
Leaving: "ease-in duration-200"
|
<label for="username" class="block text-sm font-medium text-gray-700">
|
||||||
From: "opacity-100"
|
Username
|
||||||
To: "opacity-0"
|
</label>
|
||||||
-->
|
<%= f.text_field :username, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||||
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
|
</div>
|
||||||
|
<div class="my-2">
|
||||||
<!-- This element is to trick the browser into centering the modal contents. -->
|
<label for="password" class="block text-sm font-medium text-gray-700">
|
||||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span>
|
Password
|
||||||
|
</label>
|
||||||
<!--
|
<div class="mt-1 flex rounded-md shadow-sm">
|
||||||
Modal panel, show/hide based on modal state.
|
<%= f.password_field :password, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||||
|
|
||||||
Entering: "ease-out duration-300"
|
|
||||||
From: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
To: "opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
Leaving: "ease-in duration-200"
|
|
||||||
From: "opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
To: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
-->
|
|
||||||
<%= form_with model: User.new, url: users_path(@user), data: { action: 'ajax:success->users#onSignupSuccess ajax:error->users#onError' }, class: "inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" do |f| %>
|
|
||||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
|
||||||
<div class="sm:flex sm:items-start">
|
|
||||||
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
|
||||||
<!-- Heroicon name: outline/exclamation -->
|
|
||||||
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
</div>
|
||||||
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
<div class="my-2">
|
||||||
Deactivate account
|
<label for="confirm_password" class="block text-sm font-medium text-gray-700">
|
||||||
</h3>
|
Confirm password
|
||||||
<div class="mt-2">
|
</label>
|
||||||
<p class="text-sm text-gray-500">
|
<div class="mt-1 flex rounded-md shadow-sm">
|
||||||
Are you sure you want to deactivate your account? All of your data will be permanently removed. This action cannot be undone.
|
<%= f.password_field :confirm_password, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300" %>
|
||||||
</p>
|
|
||||||
<%= f.label :username%><br>
|
|
||||||
<%= f.text_field :username%><br>
|
|
||||||
<%= f.label :password%><br>
|
|
||||||
<%= f.password_field :password%><br>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
</div>
|
||||||
<%= f.submit "Submit", class: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
|
|
||||||
<button data-action="users#closeSignupModal" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<%end%>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||||
|
<%= f.submit "Sign Up", class: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
|
||||||
|
<button data-action="click->users#closeSignupModal" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<%end%>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<% if logged_in? %>
|
<% if logged_in? %>
|
||||||
<h1>You are Logged In, <%= @current_user.username %></h1>
|
<%= link_to "Logout", '/session/logout', data: { action: 'ajax:before->users#confirmLogout ajax:success->users#onSuccessLogout' }, remote: true, class: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm" %>
|
||||||
<%= button_to "Logout", '/logout', method: :post %>
|
<h1 class="px-2 py-2 text-sm font-medium">You are Logged In, <%= @current_user.username %></h1>
|
||||||
<%end%>
|
<%end%>
|
||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
root 'sessions#welcome'
|
root 'sessions#index'
|
||||||
|
|
||||||
get '/:slug', to: 'links#redirect', as: :short
|
get '/:slug', to: 'links#redirect', as: :short
|
||||||
|
get 'session/logout', to: 'sessions#destroy', as: :logout
|
||||||
|
|
||||||
post 'login', to: 'sessions#create', as: :login
|
post 'login', to: 'sessions#create', as: :login
|
||||||
post 'logout', to: 'sessions#destroy', as: :logout
|
|
||||||
|
|
||||||
resources :links, only: %i[create]
|
resources :links, only: %i[create]
|
||||||
resources :users, only: %i[create]
|
resources :users, only: %i[create]
|
||||||
|
|||||||
Reference in New Issue
Block a user