Commit f99a3f7b by Thanh Hung Pham

Merge branch 'user' into 'master'

User

See merge request !9
parents 45fb6219 f0256ebb
Pipeline #1408 failed with stages
in 0 seconds
......@@ -13,6 +13,7 @@ gem 'friendly_id', '~> 5.4', '>= 5.4.2'
gem 'babosa'
gem 'active_storage_validations'
gem 'mini_magick', '>= 4.9.5'
gem 'devise'
# Use sqlite3 as the database for Active Record
gem 'mysql2', '~> 0.5.3'
# Use Puma as the app server
......
......@@ -67,6 +67,7 @@ GEM
autoprefixer-rails (10.2.5.1)
execjs (> 0)
babosa (1.0.4)
bcrypt (3.1.16)
bindex (0.8.1)
bootsnap (1.7.5)
msgpack (~> 1.0)
......@@ -88,6 +89,12 @@ GEM
chronic (0.10.2)
concurrent-ruby (1.1.9)
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)
execjs (2.8.1)
ffi (1.15.3)
......@@ -129,6 +136,7 @@ GEM
nio4r (2.5.7)
nokogiri (1.11.7-x86_64-linux)
racc (~> 1.4)
orm_adapter (0.5.0)
popper_js (2.9.2)
public_suffix (4.0.6)
puma (5.3.2)
......@@ -172,6 +180,9 @@ GEM
rb-inotify (0.10.1)
ffi (~> 1.0)
regexp_parser (2.1.1)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rubyzip (2.3.1)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
......@@ -210,6 +221,8 @@ GEM
turbolinks-source (5.2.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.1.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
......@@ -243,6 +256,7 @@ DEPENDENCIES
bootstrap (~> 5.0.1)
byebug
capybara (>= 3.26)
devise
friendly_id (~> 5.4, >= 5.4.2)
jbuilder (~> 2.7)
kaminari (~> 1.2, >= 1.2.1)
......
$background-error-alert-color:#f2dede;
$border-error-alert-color:#eed3d7;
$error-alert-color: #b94a48;
$background-success-notice-color: #dff0d8;
$border-success-notice-color:#d6e9c6;
$success-notice-color: #468847;
\ No newline at end of file
@import "bootstrap";
@import "colors";
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700&display=swap');
#logo {
float: left;
margin-right: 10px;
......@@ -158,3 +160,17 @@ footer {
color: $danger;
}
}
.alert-error, .alert-alert {
background-color: $background-error-alert-color;
border-color: $border-error-alert-color;
color: $error-alert-color;
text-align: left;
}
.alert-success, .alert-notice {
background-color: $background-success-notice-color;
border-color: $border-success-notice-color;
color: $success-notice-color;
text-align: left;
}
\ No newline at end of file
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: %i[name cv])
devise_parameter_sanitizer.permit(:account_update, keys: %i[name cv])
end
end
class ApplyJobsController < ApplicationController
include ApplicationHelper
before_action :authenticate_user!
def new
job = Job.find_by(id: params[:job_id]) or not_found
......@@ -17,7 +19,7 @@ class ApplyJobsController < ApplicationController
def confirm
@apply_job = ApplyJob.new(apply_params)
@apply_job.user_id = User.find_by(id: 1).id
@apply_job.user_id = current_user.id
@apply_job.validate
if @apply_job.cv.attached? && @apply_job.errors[:cv].empty?
@blob = ActiveStorage::Blob.create_and_upload!(
......@@ -41,7 +43,7 @@ class ApplyJobsController < ApplicationController
def done
@apply_job = ApplyJob.new(apply_params)
@apply_job.user_id = User.find_by(id: 1).id
@apply_job.user_id = current_user.id
@job = Job.latest_jobs.find(apply_params[:job_id])
@apply_job.cv = ActiveStorage::Blob.find(session[:blob_id])
if @apply_job.save
......
class Users::ConfirmationsController < Devise::ConfirmationsController
protected
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
register_2_path
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
sign_in(resource)
my_path
end
end
class Users::PasswordsController < Devise::PasswordsController
protected
def after_resetting_password_path_for(resource)
root_path
end
# The path used after sending reset password instructions
def after_sending_reset_password_instructions_path_for(resource_name)
root_path
end
def reset_params
devise_parameter_sanitizer.permit(:account_update, keys: [:email])
end
end
class Users::RegistrationsController < Devise::RegistrationsController
def show
end
private
def after_update_path_for(resource)
my_path
end
# The path used after sign up for inactive accounts.
def after_inactive_sign_up_path_for(resource)
register_2_path
end
end
class Users::SessionsController < Devise::SessionsController
end
class UsersController < ApplicationController
before_action :authenticate_user!
def show
@user = current_user
end
end
\ No newline at end of file
......@@ -5,6 +5,6 @@ class ApplyJob < ApplicationRecord
validates :name, presence: true, length: { maximum: 200 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
validates :cv, presence: true, content_type: { in: 'application/pdf', message: 'must be a valid image format' },
validates :cv, presence: true, content_type: { in: 'application/pdf', message: 'must be a valid pdf format' },
size: { less_than: 5.megabytes, message: 'should be less than 5MB' }
end
class User < ApplicationRecord
has_one_attached :cv
has_many :apply_jobs, dependent: :destroy
has_many :favorite_jobs, dependent: :destroy
has_many :history_jobs, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:confirmable
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :name, length: { maximum: 200 }
validates :email, length: { maximum: 200 }, uniqueness: true, format: { with: VALID_EMAIL_REGEX }
validates :cv, content_type: { in: 'application/pdf', message: 'must be a valid pdf format' },
size: { less_than: 5.megabytes, message: 'should be less than 5MB' }
end
\ No newline at end of file
......@@ -3,7 +3,7 @@ h2.text-center
| CONFIRM INFORMATION
.col-md-6.offset-md-3.form-apply
= form_with(model: @apply_job, url: done_path, local: true) do |f|
= render 'shared/error_messages'
= render 'shared/error_messages', object: f.object
= f.hidden_field :job_id
= f.label :name
= f.text_field :name, required: true, disabled: true, length: { maximum: 200 },
......
......@@ -3,7 +3,7 @@ h2.text-center.form-title
| APPLY INFORMATION
.col-md-6.offset-md-3.form-apply
= form_with(model: @apply_job, url: confirm_path, local: true) do |f|
= render 'shared/error_messages'
= render 'shared/error_messages', object: f.object
= f.hidden_field :job_id
= f.label :name
= f.text_field :name, required: true, length: { maximum: 200 }, placeholder: "Type name...", class: 'form-control'
......
......@@ -4,11 +4,18 @@ header
= link_to (image_tag "logo.png", alt: "logo"), root_path, id: "logo"
#navbarExample01.collapse.navbar-collapse
ul.navbar-nav.ms-auto.mb-2.mb-lg-0
li.nav-item.active
= link_to "Login", '#'
li.nav-item
= link_to "Sign up", '#'
li.nav-item
= link_to "Favourite", '#'
li.nav-item
- if user_signed_in?
li
= link_to "Profile", my_path
li
= link_to "Favourite",'#'
li
= link_to "History", '#'
li
= link_to('Logout', destroy_user_session_path, method: :delete)
- else
li
= link_to('Login', new_user_session_path)
li.nav-item
= link_to "Register", new_user_registration_path
- if @apply_job.errors.any?
- if object.errors.any?
#error_explanation
.alert.alert-danger
| The form contains
= pluralize(@apply_job.errors.count, "error")
.alert.alert-danger.alert-dismissable
p
strong
| This form contains
= pluralize(object.errors.count, 'error')
| .
ul
- @apply_job.errors.full_messages.each do |msg|
- object.errors.full_messages.each do |msg|
li
= msg
\ No newline at end of file
.row.p-3
.col-md-6.offset-md-3.form-apply
= form_for(resource, as: resource_name) do |f|
= render 'shared/error_messages', object: resource
.card
.card-body
.field
= f.label :email
br
= f.email_field :email, autofocus: true, disabled: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email), class: 'form-control'
.actions.p-3.text-center
= link_to "Back to Login", new_user_session_path, class: "btn btn-primary"
\ No newline at end of file
p
| Welcome
= @email
| !
p
| You're on your way! Let's confirm your email address. By clicking on the following link, you are confirming your email address and agreeing to VeNJOB's Terms of Service.
p
= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token)
\ No newline at end of file
p
| Dear
= @resource.name
p
| You have a new password!
p
| Your password for signing in to VenJOB was recently changed. If you made this change, then we're all set.
p
| If you did not make this change, please reset your password to secure your account. Then reply to this email to notify us.
p
| Either way, feel free to reach out with any questions you might have. We're here to help.
p
| Best,
\ No newline at end of file
p
| Hello
= @resource.email
| !
p
| Click the link below to reset your password using our secure server:
p
= link_to 'Change my password', reset_password_url(reset_password_token: @token)
p
| If you did not request to have your password reset you can safely ignore this email. Rest assured your account is safe.
p
| Please also be noted that the above link is valid for 24 hours.
p
| Best,
\ No newline at end of file
h2.p-3.text-center
| Change your password
.row
.col-md-6.offset-md-3.form-apply
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f|
= render "shared/error_messages", object: resource
.card
.card-body
= f.hidden_field :reset_password_token
.field
= f.label :password, "New password"
br
- if @minimum_password_length
em
| (
= @minimum_password_length
| characters minimum)
br
= f.password_field :password, autofocus: true, autocomplete: "new-password", class: 'form-control'
.field
= f.label :password_confirmation, "Confirm new password"
br
= f.password_field :password_confirmation, autocomplete: "new-password", class: 'form-control'
.actions.p-2.text-center
= f.submit "Change my password", class: "btn btn-primary"
\ No newline at end of file
h2.p-3.text-center
| Forgot your password
.row
.col-md-6.offset-md-3.form-apply
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
= render "shared/error_messages", object: resource
.card
.card-body
.field
= f.label :email
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control'
.actions.p-3.text-center
= f.submit "Confirm you email", class: "btn btn-primary"
\ No newline at end of file
h2.p-3.text-center
| Edit My Page
.row
.col-md-6.offset-md-3.form-apply
/ = resource_name.to_s.humanize
= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f|
= render 'shared/error_messages', object: resource
.card
.card-body
.field
= f.label :email
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control'
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
div
| Currently waiting confirmation for:
= resource.unconfirmed_email
.field
= f.label :name, "Full Name"
br
= f.text_field :name, length: { maximum: 200 }, class: 'form-control'
.field
= f.label :password
- if @minimum_password_length
em
| (
= @minimum_password_length
| characters minimum)
br
= f.password_field :password, autocomplete: "new-password", class: 'form-control'
.field
= f.label :password_confirmation
br
= f.password_field :password_confirmation, autocomplete: "new-password", class: 'form-control'
.field
= f.label :current_password
i
| (we need your current password to confirm your changes)
br
= f.password_field :current_password, autocomplete: "current-password", class: 'form-control'
.field
= f.label :cv, "CV"
br
= f.file_field :cv, accept: 'application/pdf', size: { less_than: 5.megabytes, message:"should be less than 5MB" }
.actions.p-2.text-center
= f.submit "Update", class: "btn btn-primary"
\ No newline at end of file
h2.p-3.text-center
| Register
.row
.col-md-6.offset-md-3.form-apply
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= render 'shared/error_messages', object: resource
.card
.card-body
.field
= f.label :name, "Full Name"
br
= f.text_field :name, required: true, autofocus: true, length: { maximum: 200 }, class: 'form-control'
.field
= f.label :email
br
= f.email_field :email, required: true, length: { maximum: 200 }, autocomplete: "email", class: 'form-control'
.field
= f.label :password
- if @minimum_password_length
em
| (
= @minimum_password_length
| characters minimum)
br
= f.password_field :password, required: true, autocomplete: "new-password", class: 'form-control'
.field
= f.label :password_confirmation
br
= f.password_field :password_confirmation, required: true, autocomplete: "new-password", class: 'form-control'
.field
= f.label :cv, "CV"
br
= f.file_field :cv, accept: 'application/pdf', size: { less_than: 5.megabytes, message:"should be less than 5MB" }
.actions.p-3.text-center
= f.submit "Sign up", class: "btn btn-primary"
\ No newline at end of file
h5.p-5.fw-normal.border.bg-white
| Thank you for register our service, an confirmation email with registration link
has been sent to your email. Please check your email within 24 hours.
Please note that the registration link is only valid for 24 hours.
Over that period, you will have to register your email again.
\ No newline at end of file
h2.p-3.text-center
| Log in
.row
.col-md-6.offset-md-3.form-apply
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
.card
.card-body
.field
= f.label :email
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control'
.field
= f.label :password
br
= f.password_field :password, autocomplete: "current-password", class: 'form-control'
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
= link_to "Forgot your password?", new_user_password_path
.actions.p-2.text-center
= f.submit "Log in", class: "btn btn-primary btn-space"
= link_to "Register", new_user_registration_path, class: "btn btn-primary btn-space"
\ No newline at end of file
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
br
\ No newline at end of file
h2.p-3.text-center
| My Page
.row
.col-md-6.offset-md-3.form-apply
= form_with(scope: @user, local:true) do |f|
.card
.card-body
.field
= f.label :email
br
= f.email_field :email, disabled: true, class: 'form-control'
.field
= f.label :name, "Full Name"
br
= f.text_field :name, disabled: true, class: 'form-control'
.field
-if @user.cv.attached?
= f.label :cv, "CV"
br
= link_to @user.cv.filename, rails_blob_path(@user.cv, disposition: 'attachment')
.span.p-2.text-center
= link_to "Update", edit_user_registration_path, class: "btn btn-primary btn-space"
= link_to "My Jobs", "#", class: "btn btn-primary btn-space"
\ No newline at end of file
# 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: "Welcome To VeNJOB! Confirm Your Email"
reset_password_instructions:
subject: "VeNJOB Password Assistance"
unlock_instructions:
subject: "Unlock instructions"
email_changed:
subject: "Email Changed"
password_change:
subject: "VeNJOB Password Assistance: Your password has been reset"
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:
taken: "Please use a different Email "
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:"
......@@ -8,4 +8,20 @@ Rails.application.routes.draw do
get 'apply', to: 'apply_jobs#new'
post 'confirm', to: 'apply_jobs#confirm'
post 'done', to: 'apply_jobs#done'
devise_for :users, skip: %i[sessions registrations passwords], controllers: { confirmations: 'users/confirmations' }
get '/my', to: 'users#show'
devise_scope :user do
get 'register/1', to: 'users/registrations#new', as: :new_user_registration
get 'register/2', to: 'users/registrations#show'
get 'my/info', to: 'users/registrations#edit', as: :edit_user_registration
match '/my', to: 'users/registrations#create', as: :user_registration, via: [:post]
match '/my', to: 'users/registrations#update', via: %i[put patch]
get 'forgot_password', to: 'users/passwords#new', as: :new_user_password
get 'reset_password', to: 'users/passwords#edit', as: :edit_user_password
match 'reset_password', to: 'users/passwords#update', via: %i[put patch]
post 'reset_password', to: 'users/passwords#create', as: :user_password
get 'login', to: 'users/sessions#new', as: :new_user_session
post 'login', to: 'users/sessions#create', as: :user_session
delete 'logout', to: 'users/sessions#destroy', as: :destroy_user_session, via: Devise.mappings[:user].sign_out_via
end
end
\ No newline at end of file
class RemoveCvFromUers < ActiveRecord::Migration[6.1]
def change
remove_column :users, :cv
end
end
class AuthenticatableRegisterableTrackableConfirmableToUser < ActiveRecord::Migration[6.1]
def change
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
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
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_08_09_075523) do
ActiveRecord::Schema.define(version: 2021_08_18_152915) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name", null: false
......@@ -97,17 +97,6 @@ ActiveRecord::Schema.define(version: 2021_08_09_075523) do
t.index ["user_id"], name: "index_favorite_jobs_on_user_id"
end
create_table "friendly_id_slugs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.datetime "created_at"
t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true, length: { slug: 70, scope: 70 }
t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type", length: { slug: 140 }
t.index ["sluggable_type", "sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_type_and_sluggable_id"
end
create_table "history_jobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "job_id", null: false
......@@ -162,9 +151,19 @@ ActiveRecord::Schema.define(version: 2021_08_09_075523) do
create_table "users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name"
t.string "email"
t.binary "cv"
t.datetime "created_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 "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
......
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