Commit 6c571aa7 by Mai Hoang Thai Ha

create sign up, login, profile, edit, forgot password

parent a92c2bab
...@@ -63,4 +63,6 @@ gem 'slim-rails', '~> 3.2' ...@@ -63,4 +63,6 @@ gem 'slim-rails', '~> 3.2'
gem 'kaminari', :git => 'https://github.com/kaminari/kaminari' gem 'kaminari', :git => 'https://github.com/kaminari/kaminari'
gem 'nokogiri', '~> 1.11', '>= 1.11.7' gem 'nokogiri', '~> 1.11', '>= 1.11.7'
gem 'httparty', '~> 0.18.1' gem 'httparty', '~> 0.18.1'
gem 'active_storage_validations', '~> 0.9.5' gem 'active_storage_validations', '~> 0.9.5'
\ No newline at end of file gem 'devise', '~> 4.8'
gem 'jquery-rails', '~> 4.4'
\ No newline at end of file
...@@ -82,6 +82,7 @@ GEM ...@@ -82,6 +82,7 @@ GEM
addressable (2.8.0) addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
ast (2.4.2) ast (2.4.2)
bcrypt (3.1.16)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.7.5) bootsnap (1.7.5)
msgpack (~> 1.0) msgpack (~> 1.0)
...@@ -102,6 +103,12 @@ GEM ...@@ -102,6 +103,12 @@ GEM
coderay (1.1.3) coderay (1.1.3)
concurrent-ruby (1.1.9) concurrent-ruby (1.1.9)
crass (1.0.6) crass (1.0.6)
devise (4.8.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
erubi (1.10.0) erubi (1.10.0)
ffi (1.15.3) ffi (1.15.3)
globalid (0.4.2) globalid (0.4.2)
...@@ -113,6 +120,10 @@ GEM ...@@ -113,6 +120,10 @@ GEM
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jbuilder (2.11.2) jbuilder (2.11.2)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
listen (3.5.1) listen (3.5.1)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
...@@ -134,6 +145,7 @@ GEM ...@@ -134,6 +145,7 @@ GEM
nio4r (2.5.7) nio4r (2.5.7)
nokogiri (1.11.7-x86_64-linux) nokogiri (1.11.7-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
orm_adapter (0.5.0)
parallel (1.20.1) parallel (1.20.1)
parser (3.0.2.0) parser (3.0.2.0)
ast (~> 2.4.1) ast (~> 2.4.1)
...@@ -185,6 +197,9 @@ GEM ...@@ -185,6 +197,9 @@ GEM
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
regexp_parser (2.1.1) regexp_parser (2.1.1)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5) rexml (3.2.5)
rubocop (1.18.3) rubocop (1.18.3)
parallel (~> 1.10) parallel (~> 1.10)
...@@ -242,6 +257,8 @@ GEM ...@@ -242,6 +257,8 @@ GEM
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (2.0.0) unicode-display_width (2.0.0)
uniform_notifier (1.14.2) uniform_notifier (1.14.2)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.1.0) web-console (4.1.0)
actionview (>= 6.0.0) actionview (>= 6.0.0)
activemodel (>= 6.0.0) activemodel (>= 6.0.0)
...@@ -272,8 +289,10 @@ DEPENDENCIES ...@@ -272,8 +289,10 @@ DEPENDENCIES
bullet (~> 6.1, >= 6.1.4) bullet (~> 6.1, >= 6.1.4)
byebug byebug
capybara (>= 3.26) capybara (>= 3.26)
devise (~> 4.8)
httparty (~> 0.18.1) httparty (~> 0.18.1)
jbuilder (~> 2.7) jbuilder (~> 2.7)
jquery-rails (~> 4.4)
kaminari! kaminari!
listen (~> 3.3) listen (~> 3.3)
mysql2 (~> 0.5) mysql2 (~> 0.5)
......
...@@ -167,4 +167,4 @@ footer { ...@@ -167,4 +167,4 @@ footer {
} }
} }
// cities list // cities list
\ No newline at end of file
// Place all the styles related to the Users controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/
.main-body {
padding: 15px;
margin-top:20px;
color: #1a202c;
text-align: left;
background-color: #e2e8f0;
}
.card {
box-shadow: 0 1px 3px 0 rgba(0,0,0,.1), 0 1px 2px 0 rgba(0,0,0,.06);
}
.card {
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border: 0 solid rgba(0,0,0,.125);
border-radius: .25rem;
}
.card-body {
flex: 1 1 auto;
min-height: 1px;
padding: 1rem;
}
.gutters-sm {
margin-right: -8px;
margin-left: -8px;
}
.gutters-sm>.col, .gutters-sm>[class*=col-] {
padding-right: 8px;
padding-left: 8px;
}
.mb-3, .my-3 {
margin-bottom: 1rem!important;
}
.bg-gray-300 {
background-color: #e2e8f0;
}
.h-100 {
height: 100%!important;
}
.shadow-none {
box-shadow: none!important;
}
\ No newline at end of file
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit :sign_up, keys: %i[name address phone email password]
devise_parameter_sanitizer.permit :account_update, keys: %i[name email address phone password current_password]
end
private
def logged_in_user
unless user_signed_in?
flash[:danger] = 'Please log in.'
redirect_to new_user_session_path
end
end
end end
class AppliesController < ApplicationController class AppliesController < ApplicationController
before_action :load_job, only: %i[new confirm create] before_action :load_job, only: %i[new confirm create]
before_action :logged_in_user
def index def index
end end
def create def create
@user = User.find_by(id: 1) @user = current_user
@apply = @job.apply_jobs.build(apply_params) @apply = @job.apply_jobs.build(apply_params)
@apply.user_id = @user.id @apply.user_id = @user.id
@apply.cv.attach(params[:apply_job][:cv]) @apply.cv.attach(params[:apply_job][:cv])
if @apply.save if @apply.save
UserMailer.apply_job(@user, @job, @apply).deliver_now UserMailer.apply_job(@user, @job, @apply).deliver_now
flash.now[:info] = 'Job application information has been sent to your email' flash.now[:info] = 'Job application information has been sent to your email'
...@@ -25,7 +25,7 @@ class AppliesController < ApplicationController ...@@ -25,7 +25,7 @@ class AppliesController < ApplicationController
end end
def confirm def confirm
@user = User.find_by(id: 1) @user = current_user
@apply = @job.apply_jobs.build(apply_params) @apply = @job.apply_jobs.build(apply_params)
@apply.user_id = @user.id @apply.user_id = @user.id
if @apply.valid? if @apply.valid?
......
class UsersController < ApplicationController
before_action :logged_in_user
def show
@user = User.find(params[:id])
end
end
module UsersHelper
def gravatar_for(user, options = { size: 150 })
size = options[:size]
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "rounded-circle")
end
end
...@@ -15,3 +15,8 @@ ActiveStorage.start() ...@@ -15,3 +15,8 @@ ActiveStorage.start()
import "bootstrap" import "bootstrap"
window.bootstrap = require("bootstrap"); window.bootstrap = require("bootstrap");
import "../stylesheets/application.scss"; import "../stylesheets/application.scss";
//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require turbolinks
//= require_tree .
class User < ApplicationRecord class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :apply_jobs, dependent: :destroy has_many :apply_jobs, dependent: :destroy
has_many :favorite_jobs, dependent: :destroy has_many :favorite_jobs, dependent: :destroy
has_many :history_jobs, dependent: :destroy has_many :history_jobs, dependent: :destroy
......
...@@ -3,8 +3,15 @@ header.navbar.navbar-fixed-top.navbar-inverse ...@@ -3,8 +3,15 @@ header.navbar.navbar-fixed-top.navbar-inverse
= link_to image_tag("logo.png", alt: "Zigexn logo", width: "150"), = link_to image_tag("logo.png", alt: "Zigexn logo", width: "150"),
- root_path - root_path
nav nav
ul.nav.navbar-nav.navbar-right ul.nav.navbar-nav.navbar-right.text-light
li - if user_signed_in?
= link_to "Log in", '#' li
li = link_to "Profile", current_user
= link_to "Sign up", '#' li
\ No newline at end of file = link_to "Log out", destroy_user_session_path, method: :delete
- else
li
= link_to "Log in", new_user_session_path
li
= link_to "Sign up", new_user_registration_path
...@@ -9,9 +9,15 @@ html ...@@ -9,9 +9,15 @@ html
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload'
= render 'layouts/shim' = render 'layouts/shim'
= yield(:head)
body body
= render 'layouts/header' = render 'layouts/header'
.container-fluid style ="min-height:80vh;" .container-fluid style ="min-height:80vh;"
/ .container-fluid class=("text-center alert alert-#{notice}")
/ p.notice
/ = notice
/ p.alert
/ = alert
- flash.each do |message_type, message| - flash.each do |message_type, message|
div class=("text-center alert alert-#{message_type}") = message div class=("text-center alert alert-#{message_type}") = message
= yield = yield
......
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
</div>
<div class="actions">
<%= f.submit "Resend confirmation instructions" %>
</div>
<% end %>
<%= render "users/shared/links" %>
<p>Welcome <%= @email %>!</p>
<p>You can confirm your account email through the link below:</p>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
<p>Hello <%= @email %>!</p>
<% if @resource.try(:unconfirmed_email?) %>
<p>We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.</p>
<% else %>
<p>We're contacting you to notify you that your email has been changed to <%= @resource.email %>.</p>
<% end %>
<p>Hello <%= @resource.email %>!</p>
<p>We're contacting you to notify you that your password has been changed.</p>
<p>Hello <%= @resource.email %>!</p>
<p>Someone has requested a link to change your password. You can do this through the link below.</p>
<p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
<p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p>
<p>Hello <%= @resource.email %>!</p>
<p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
<p>Click the link below to unlock your account:</p>
<p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p>
- provide(:title, "Change password")
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Change your password
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f|
= render "users/shared/error_messages", resource: resource
= f.hidden_field :reset_password_token
.field
= f.label :password, "New password", class: 'form-label'
br/
- if @minimum_password_length
em
| (#{@minimum_password_length} characters minimum)
br/
= f.password_field :password, autofocus: true, autocomplete: "new-password", class: 'form-control mb-2'
.field
= f.label :password_confirmation, "Confirm new password", class: 'form-label'
br/
= f.password_field :password_confirmation, autocomplete: "new-password", class: 'form-control mb-2'
.actions
= f.submit "Change my password", class: 'btn btn-primary w-100 my-4'
= render "users/shared/links"
- provide(:title, "Forgot password")
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Forgot your password?
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
= render "users/shared/error_messages", resource: resource
.field
= f.label :email, class: 'form-label'
br/
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control mb-2'
.actions
= f.submit "Send me reset password instructions", class: 'btn btn-primary w-100 my-4'
= render "users/shared/links"
- provide(:title, "Edit profile")
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Edit
= resource_name.to_s.humanize
= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f|
= render "users/shared/error_messages", resource: resource
.field
= f.label :email, class: 'form-label'
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control mb-2'
.field
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
div
| Currently waiting confirmation for:
= resource.unconfirmed_email
.field
= f.label :name, class: 'form-label'
br
= f.text_field :name, autofocus: true, autocomplete: "name", class: 'form-control mb-2'
.field
= f.label :address, class: 'form-label'
br
= f.text_field :address, autofocus: true, autocomplete: "address", class: 'form-control mb-2'
.field
= f.label :phone, class: 'form-label'
br
= f.text_field :phone, autofocus: true, autocomplete: "phone", class: 'form-control mb-2'
.field
= f.label :password, class: 'form-label'
i
| (leave blank if you don't want to change it)
br
= f.password_field :password, autocomplete: "new-password", class: 'form-control mb-2'
- if @minimum_password_length
em
= @minimum_password_length
| characters minimum
.field
= f.label :password_confirmation, class: 'form-label'
br
= f.password_field :password_confirmation, autocomplete: "new-password", class: 'form-control mb-2'
.field
= f.label :current_password, class: 'form-label'
i
| (we need your current password to confirm your changes)
br
= f.password_field :current_password, autocomplete: "current-password", class: 'form-control mb-2'
.actions
= f.submit "Done", class: "btn btn-primary w-100 my-4"
hr
h3
| Delete my account
p
| Click the button below to delete account
= button_to "Delete", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete, class: "btn btn-primary w-100 my-4"
= link_to "Home", :back
\ No newline at end of file
- provide(:title, "Sign Up")
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= render "users/shared/error_messages", resource: resource
.field
= f.label :email, class: 'form-label'
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control mb-2'
.field
= f.label :name, class: 'form-label'
br
= f.text_field :name, autofocus: true, autocomplete: "name", class: 'form-control mb-2'
.field
= f.label :address, class: 'form-label'
br
= f.text_field :address, autofocus: true, autocomplete: "address", class: 'form-control mb-2'
.field
= f.label :phone, class: 'form-label'
br
= f.text_field :phone, autofocus: true, autocomplete: "phone", class: 'form-control mb-2'
.field
= f.label :password, class: 'form-label'
- if @minimum_password_length
em
| (
= @minimum_password_length
| characters minimum)
br
= f.password_field :password, autocomplete: "new-password", class: 'form-control mb-2'
.field
= f.label :password_confirmation, class: 'form-label'
br
= f.password_field :password_confirmation, autocomplete: "new-password", class: 'form-control mb-2'
.actions
= f.submit "Sign up", class: "btn btn-primary w-100 my-5"
= render "users/shared/links"
\ No newline at end of file
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Log in
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
.field
= f.label :email, class: 'form-label'
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control mb-2'
.field
= f.label :password, class: 'form-label'
br
= f.password_field :password, autocomplete: "current-password", class: 'form-control mb-2'
- if devise_mapping.rememberable?
.field
= f.check_box :remember_me
= f.label :remember_me, class: 'form-label'
.actions
= f.submit "Log in", class: "btn btn-primary w-100 my-5"
= render "users/shared/links"
\ No newline at end of file
- if resource.errors.any?
#error_explanation.bg_danger
.alert.alert-danger
= I18n.t("errors.messages.not_saved", count: resource.errors.count, resource: resource.class.model_name.human.downcase)
ul.mb-3
- resource.errors.full_messages.each do |message|
li.text-danger
= message
\ No newline at end of file
- if controller_name != 'sessions'
= link_to "Log in", new_session_path(resource_name), class: 'text-decoration-none'
br/
- if devise_mapping.registerable? && controller_name != 'registrations'
= link_to "Sign up", new_registration_path(resource_name), class: 'text-decoration-none'
br/
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
= link_to "Forgot your password?", new_password_path(resource_name), class: 'text-decoration-none'
br/
- if devise_mapping.confirmable? && controller_name != 'confirmations'
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: 'text-decoration-none'
br/
- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name), class: 'text-decoration-none'
br/
- if devise_mapping.omniauthable?
- resource_class.omniauth_providers.each do |provider|
= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), method: :post, class: 'text-decoration-none'
br/
- content_for :head do
= stylesheet_link_tag 'users'
.container
.main-body
/! Breadcrumb
nav.main-breadcrumb aria-label="breadcrumb"
ol.breadcrumb
li.breadcrumb-item
= link_to 'Home', root_path
li.breadcrumb-item
= link_to 'User', '#'
li.breadcrumb-item.active aria-current="page" User Profile
/! /Breadcrumb
.row.gutters-sm
.col-md-4.mb-3
.card
.card-body
.d-flex.flex-column.align-items-center.text-center
= gravatar_for @user
/ img.rounded-circle alt="Admin" src="https://bootdey.com/img/Content/avatar/avatar7.png" width="150" /
.mt-3
h4
= @user.name
p.text-secondary.mb-1 Full Stack Developer
p.text-muted.font-size-sm
= @user.address
button.btn.btn-primary Follow
button.btn.btn-outline-primary Message
.card.mt-3
ul.list-group.list-group-flush
li.list-group-item.d-flex.justify-content-between.align-items-center.flex-wrap
h6.mb-0
svg.feather.feather-globe.mr-2.icon-inline fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox=("0 0 24 24") width="24" xmlns="http://www.w3.org/2000/svg"
circle cx="12" cy="12" r="10"
line x1="2" x2="22" y1="12" y2="12"
path d=("M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z")
| Website
span.text-secondary none
li.list-group-item.d-flex.justify-content-between.align-items-center.flex-wrap
h6.mb-0
svg.feather.feather-github.mr-2.icon-inline fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox=("0 0 24 24") width="24" xmlns="http://www.w3.org/2000/svg"
path d=("M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22")
| Github
span.text-secondary none
li.list-group-item.d-flex.justify-content-between.align-items-center.flex-wrap
h6.mb-0
svg.feather.feather-twitter.mr-2.icon-inline.text-info fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox=("0 0 24 24") width="24" xmlns="http://www.w3.org/2000/svg"
path d=("M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z")
| Twitter
span.text-secondary none
li.list-group-item.d-flex.justify-content-between.align-items-center.flex-wrap
h6.mb-0
svg.feather.feather-instagram.mr-2.icon-inline.text-danger fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox=("0 0 24 24") width="24" xmlns="http://www.w3.org/2000/svg"
rect height="20" rx="5" ry="5" width="20" x="2" y="2"
path d=("M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z")
line x1="17.5" x2="17.51" y1="6.5" y2="6.5"
| Instagram
span.text-secondary none
li.list-group-item.d-flex.justify-content-between.align-items-center.flex-wrap
h6.mb-0
svg.feather.feather-facebook.mr-2.icon-inline.text-primary fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox=("0 0 24 24") width="24" xmlns="http://www.w3.org/2000/svg"
path d=("M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z")
| Facebook
span.text-secondary none
.col-md-8
.card.mb-3
.card-body
.row
.col-sm-3
h6.mb-0 Full Name
.col-sm-9.text-secondary
= @user.name
hr/
.row
.col-sm-3
h6.mb-0 Email
.col-sm-9.text-secondary
= @user.email
hr/
.row
.col-sm-3
h6.mb-0 Phone
.col-sm-9.text-secondary
= @user.phone
hr/
.row
.col-sm-3
h6.mb-0 Address
.col-sm-9.text-secondary
= @user.address
hr/
.row
.col-sm-12
= link_to 'Edit profile', edit_user_registration_path, class:'btn btn-info'
.row.gutters-sm
.col-sm-6.mb-3
.card.h-100
.card-body
h6.d-flex.align-items-center.mb-3
i.material-icons.text-info.mr-2> assignment
span.d-block.m-auto
| Project Status
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="80" role="progressbar" style=("width: 80%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="72" role="progressbar" style=("width: 72%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="89" role="progressbar" style=("width: 89%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="55" role="progressbar" style=("width: 55%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="66" role="progressbar" style=("width: 66%")
.col-sm-6.mb-3
.card.h-100
.card-body
h6.d-flex.align-items-center.mb-3
i.material-icons.text-info.mr-2> assignment
span.d-block.m-auto
| Project Status
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="80" role="progressbar" style=("width: 80%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="72" role="progressbar" style=("width: 72%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="89" role="progressbar" style=("width: 89%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="55" role="progressbar" style=("width: 55%")
small Skill
.progress.mb-3 style=("height: 5px")
.progress-bar.bg-primary aria-valuemax="100" aria-valuemin="0" aria-valuenow="66" role="progressbar" style=("width: 66%")
<h2>Resend unlock instructions</h2>
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="actions">
<%= f.submit "Resend unlock instructions" %>
</div>
<% end %>
<%= render "users/shared/links" %>
# frozen_string_literal: true
# Assuming you have not yet modified this file, each configuration option below
# is set to its default value. Note that some are commented out while others
# are not: uncommented lines are intended to protect your configuration from
# breaking changes in upgrades (i.e., in the event that future versions of
# Devise change the default values for those options).
#
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
# The secret key used by Devise. Devise uses this key to generate
# random tokens. Changing this key will render invalid all existing
# confirmation, reset password and unlock tokens in the database.
# Devise will use the `secret_key_base` as its `secret_key`
# by default. You can change it below and use your own secret key.
# config.secret_key = '407ad24ac0a2efe7fed12ee5dd498977eaf7bc8fbe3e25c76f6b1921285b8799ae241371ade745424630ccbce1e690ca333d83329bd2222a4f12e8f21f0e983e'
# ==> Controller configuration
# Configure the parent class to the devise controllers.
# config.parent_controller = 'DeviseController'
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
# Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base'
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
# :mongoid (bson_ext recommended) by default. Other ORMs may be
# available as additional gems.
require 'devise/orm/active_record'
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
# config.authentication_keys = [:email]
# Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the
# find_for_authentication method and considered in your model lookup. For instance,
# if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
# The same considerations mentioned for authentication_keys also apply to request_keys.
# config.request_keys = []
# Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [:email]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [:email]
# Tell if authentication through request.params is enabled. True by default.
# It can be set to an array that will enable params authentication only for the
# given strategies, for example, `config.params_authenticatable = [:database]` will
# enable it only for database (email + password) authentication.
# config.params_authenticatable = true
# Tell if authentication through HTTP Auth is enabled. False by default.
# It can be set to an array that will enable http authentication only for the
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication.
# For API-only applications to support authentication "out-of-the-box", you will likely want to
# enable this with :database unless you are using a custom strategy.
# The supported strategies are:
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
# The realm used in Http Basic Authentication. 'Application' by default.
# config.http_authentication_realm = 'Application'
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
# By default Devise will store the user in session. You can skip storage for
# particular strategies by setting this option.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing skip: :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth]
# By default, Devise cleans up the CSRF token on authentication to
# avoid CSRF token fixation attacks. This means that, when using AJAX
# requests for sign in and sign up, you need to get a new CSRF token
# from the server. You can disable this option at your own risk.
# config.clean_up_csrf_token_on_authentication = true
# When false, Devise will not attempt to reload routes on eager load.
# This can reduce the time taken to boot the app but if your application
# requires the Devise mappings to be loaded during boot time the application
# won't boot properly.
# config.reload_routes = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 12. If
# using other algorithms, it sets how many times you want the password to be hashed.
# The number of stretches used for generating the hashed password are stored
# with the hashed password. This allows you to change the stretches without
# invalidating existing passwords.
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments. Note that, for bcrypt (the default
# algorithm), the cost increases exponentially with the number of stretches (e.g.
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
config.stretches = Rails.env.test? ? 1 : 12
# Set up a pepper to generate the hashed password.
# config.pepper = 'ba2de49a7b95e82815e8ec185a44fe5bdd81d96b43fe027fc55e2c4d12647b318ae5b0af6ca8b6fc4e3c38b10d534589e9b91a1d1619c1e7b508eba9d2940a95'
# Send a notification to the original email when the user's email is changed.
# config.send_email_changed_notification = false
# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false
# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
# confirming their account. For instance, if set to 2.days, the user will be
# able to access the website for two days without confirming their account,
# access will be blocked just in the third day.
# You can also set it to nil, which will allow the user to access the website
# without confirming their account.
# Default is 0.days, meaning the user cannot access the website without
# confirming their account.
# config.allow_unconfirmed_access_for = 2.days
# A period that the user is allowed to confirm their account before their
# token becomes invalid. For example, if set to 3.days, the user can confirm
# their account within 3 days after the mail was sent, but on the fourth day
# their account can't be confirmed with the token any more.
# Default is nil, meaning there is no restriction on how long a user can take
# before confirming their account.
# config.confirm_within = 3.days
# If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed, new email is stored in
# unconfirmed_email column, and copied to email column on successful confirmation.
config.reconfirmable = true
# Defines which key will be used when confirming an account
# config.confirmation_keys = [:email]
# ==> Configuration for :rememberable
# The time the user will be remembered without asking for credentials again.
# config.remember_for = 2.weeks
# Invalidates all the remember me tokens when the user signs out.
config.expire_all_remember_me_on_sign_out = true
# If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false
# Options to be passed to the created cookie. For instance, you can set
# secure: true in order to force SSL only cookies.
# config.rememberable_options = {}
# ==> Configuration for :validatable
# Range for password length.
config.password_length = 6..128
# Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
# time the user will be asked for credentials again. Default is 30 minutes.
# config.timeout_in = 30.minutes
# ==> Configuration for :lockable
# Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :none = No lock strategy. You should handle locking by yourself.
# config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account
# config.unlock_keys = [:email]
# Defines which strategy will be used to unlock an account.
# :email = Sends an unlock link to the user email
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself.
# config.unlock_strategy = :both
# Number of authentication tries before locking an account if lock_strategy
# is failed attempts.
# config.maximum_attempts = 20
# Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour
# Warn on the last attempt before the account is locked.
# config.last_attempt_warning = true
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [:email]
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
config.reset_password_within = 6.hours
# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
# config.sign_in_after_reset_password = true
# ==> Configuration for :encryptable
# Allow you to use another hashing or encryption algorithm besides bcrypt (default).
# You can use :sha1, :sha512 or algorithms from others authentication tools as
# :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
# for default behavior) and :restful_authentication_sha1 (then you should set
# stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
#
# Require the `devise-encryptable` gem when using anything other than bcrypt
# config.encryptor = :sha512
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
# "users/sessions/new". It's turned off by default because it's slower if you
# are using only default views.
config.scoped_views = true
# Configure the default scope given to Warden. By default it's the first
# devise role declared in your routes (usually :user).
# config.default_scope = :user
# Set this configuration to false if you want /users/sign_out to sign out
# only the current scope. By default, Devise signs out all scopes.
# config.sign_out_all_scopes = true
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html, should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
# is mountable, there are some extra configurations to be taken into account.
# The following options are available, assuming the engine is mounted as:
#
# mount MyEngine, at: '/my_engine'
#
# The router that invoked `devise_for`, in the example above, would be:
# config.router_name = :my_engine
#
# When using OmniAuth, Devise cannot automatically set OmniAuth path,
# so you need to do it manually. For the users scope, it would be:
# config.omniauth_path_prefix = '/my_engine/users/auth'
# ==> Turbolinks configuration
# If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly:
#
# ActiveSupport.on_load(:devise_failure_app) do
# include Turbolinks::Controller
# end
# ==> Configuration for :registerable
# When set to false, does not sign a user in automatically after their password is
# changed. Defaults to true, so a user is signed in automatically after changing a password.
# config.sign_in_after_change_password = true
end
# Additional translations at https://github.com/heartcombo/devise/wiki/I18n
en:
devise:
confirmations:
confirmed: "Your email address has been successfully confirmed."
send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
failure:
already_authenticated: "You are already signed in."
inactive: "Your account is not activated yet."
invalid: "Invalid %{authentication_keys} or password."
locked: "Your account is locked."
last_attempt: "You have one more attempt before your account is locked."
not_found_in_database: "Invalid %{authentication_keys} or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing."
unconfirmed: "You have to confirm your email address before continuing."
mailer:
confirmation_instructions:
subject: "Confirmation instructions"
reset_password_instructions:
subject: "Reset password instructions"
unlock_instructions:
subject: "Unlock instructions"
email_changed:
subject: "Email Changed"
password_change:
subject: "Password Changed"
omniauth_callbacks:
failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
success: "Successfully authenticated from %{kind} account."
passwords:
no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
updated: "Your password has been changed successfully. You are now signed in."
updated_not_active: "Your password has been changed successfully."
registrations:
destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
signed_up: "Welcome! You have signed up successfully."
signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
updated: "Your account has been updated successfully."
updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again."
sessions:
signed_in: "Signed in successfully."
signed_out: "Signed out successfully."
already_signed_out: "Signed out successfully."
unlocks:
send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
unlocked: "Your account has been unlocked successfully. Please sign in to continue."
errors:
messages:
already_confirmed: "was already confirmed, please try signing in"
confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
expired: "has expired, please request a new one"
not_found: "not found"
not_locked: "was not locked"
not_saved:
one: "1 error prohibited this %{resource} from being saved:"
other: "%{count} errors prohibited this %{resource} from being saved:"
Rails.application.routes.draw do Rails.application.routes.draw do
devise_for :users
root 'top#index' root 'top#index'
resources :cities, param: :slug, only: %i[index show] do resources :cities, param: :slug, only: %i[index show] do
...@@ -9,6 +10,8 @@ Rails.application.routes.draw do ...@@ -9,6 +10,8 @@ Rails.application.routes.draw do
end end
resources :jobs, only: %i[index show] resources :jobs, only: %i[index show]
resources :users, only: %i[index show]
get '/apply', to: 'applies#new' get '/apply', to: 'applies#new'
post '/confirm', to: 'applies#confirm' post '/confirm', to: 'applies#confirm'
......
...@@ -15,6 +15,5 @@ class CreateUsers < ActiveRecord::Migration[6.1] ...@@ -15,6 +15,5 @@ class CreateUsers < ActiveRecord::Migration[6.1]
t.timestamps t.timestamps
end end
add_index :users, :email, unique: true add_index :users, :email, unique: true
add_index :users, :password_reset_digest, unique: true
end end
end end
class RemoveFieldFromUsers < ActiveRecord::Migration[6.1]
def change
remove_column :users, :password_digest, :string
remove_column :users, :remember_digest, :string
remove_column :users, :admin, :boolean
remove_column :users, :activation_digest, :string
remove_column :users, :activated, :boolean
remove_column :users, :activated_at, :datetime
remove_column :users, :password_reset_digest, :string
remove_column :users, :password_reset_sent_at, :datetime
end
end
# frozen_string_literal: true
class AddDeviseToUsers < ActiveRecord::Migration[6.1]
def self.up
change_table :users do |t|
## Database authenticatable
# t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
# Uncomment below if timestamps were not included in your original model.
# t.timestamps null: false
end
# add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
def self.down
# By default, we don't want to make any assumption about how to roll back a migration when your
# model already existed. Please edit below which fields you would like to remove in this migration.
raise ActiveRecord::IrreversibleMigration
end
end
class AddInfoToUser < ActiveRecord::Migration[6.1]
def change
add_column :users, :phone, :string
add_column :users, :address, :string
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_07_29_140527) do ActiveRecord::Schema.define(version: 2021_08_05_105601) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
...@@ -130,18 +130,16 @@ ActiveRecord::Schema.define(version: 2021_07_29_140527) do ...@@ -130,18 +130,16 @@ ActiveRecord::Schema.define(version: 2021_07_29_140527) do
create_table "users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| create_table "users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name" t.string "name"
t.string "email", null: false t.string "email", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "password_reset_digest"
t.datetime "password_reset_sent_at"
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.string "phone"
t.string "address"
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["password_reset_digest"], name: "index_users_on_password_reset_digest", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
......
require "test_helper"
class UsersControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment