Commit 424b9d2b by phuctmZigexn

Merge branch 'Task/15_favorite_job_ID8_history_ID9' into 'master'

Feature favorite and history

See merge request !15
parents a06436f1 3a2d621e
......@@ -65,95 +65,35 @@ footer {
}
}
// Top page
// search
// latest-job
// flash message
/*flash*/
.alert-error {
background-color: #f2dede;
border-color: #eed3d7;
color: #b94a48;
text-align: left;
}
.latest-job {
.job-item {
font-size: 14px;
.job-title {
text-decoration: none;
color: black;
font-size: 20px;
font-weight: 500;
}
.job-caption {
line-height: 18px;
.job-company {
text-decoration: none;
font-size: 14px;
color: $gray-700;
}
.job-salary {
color: #008563;
margin: 0;
}
.job-locations {
ul {
list-style: none;
margin: 0 0 6px 0;
padding: 0;
color: $gray-700;
}
}
.job-desc {
overflow: hidden;
text-overflow: ellipsis;
line-height: 18px;
-webkit-line-clamp: 2;
max-height: 36px;
display: -webkit-box;
-webkit-box-orient: vertical;
}
}
}
.alert-alert {
background-color: #f2dede;
border-color: #eed3d7;
color: #b94a48;
text-align: left;
}
// top_cities
.alert-success {
background-color: #dff0d8;
border-color: #d6e9c6;
color: #468847;
text-align: left;
}
.top_cities {
.city-item {
margin-bottom: 12px;
a {
font-size: 18px;
text-decoration: none;
color: #287ab9;
margin: 10px 0;
}
}
.all-cities-btn {
color: #287ab9;
font-size: 20px;
text-decoration: none;
}
.all-cities-btn:hover {
text-decoration: underline;
}
.alert-notice {
background-color: #dff0d8;
border-color: #d6e9c6;
color: #468847;
text-align: left;
}
// top_industries
.top_industries {
.industry-item {
margin-bottom: 12px;
a {
font-size: 18px;
text-decoration: none;
color: #287ab9;
margin: 10px 0;
}
}
.all-industries-btn {
color: #287ab9;
font-size: 20px;
text-decoration: none;
}
.all-industries-btn:hover {
text-decoration: underline;
}
}
// cities list
......@@ -9,7 +9,7 @@ class ApplicationController < ActionController::Base
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]
devise_parameter_sanitizer.permit :account_update, keys: %i[name email address phone password cv]
end
private
......
class AppliesController < ApplicationController
before_action :authenticate_user!
before_action :load_job, only: %i[new confirm create]
def index
@jobs = current_user.apply_jobs.eager_load(job: %i[cities cities_jobs company])
.page(params[:page]).per(Job::JOB_PER_PAGE)
end
def new
if use_apply_job_session?
build_params = session[:apply_job]
......@@ -11,7 +17,6 @@ class AppliesController < ApplicationController
def confirm
@apply = current_user.apply_jobs.build(apply_params)
@apply.job = @job
if @apply.valid?
@blob = ActiveStorage::Blob.create_and_upload!(
......@@ -28,7 +33,6 @@ class AppliesController < ApplicationController
def create
@apply = current_user.apply_jobs.build(apply_params)
@apply.job = @job
@apply.cv = ActiveStorage::Blob.find(session[:blob_id])
if @apply.save
......@@ -54,10 +58,6 @@ class AppliesController < ApplicationController
params.require(:apply_job).permit(:user_name, :email, :cv, :job_id)
end
def current_user
@current_user ||= User.first
end
def use_apply_job_session?
request.referer&.include?('confirm') && session[:apply_job].present?
end
......
class FavoriteJobsController < ApplicationController
before_action :logged_in_user
before_action :load_job, only: %i[create destroy]
def index
@favorite_jobs = current_user.favorite_jobs.eager_load(job: %i[cities cities_jobs company])
.page(params[:page]).per(Job::JOB_PER_PAGE)
end
def create
@favorite_job = current_user.favorite_jobs.build(job_id: @job.id)
respond_to :js if @favorite_job.save
end
def destroy
@favorite_job = current_user.favorite_jobs.find_by(job_id: @job.id)
respond_to :js if @favorite_job.destroy
end
private
def load_job
@job = Job.find_by(id: params[:job_id])
end
end
class HistoryJobsController < ApplicationController
before_action :logged_in_user
def index
@history_jobs = current_user.history_jobs.eager_load(job: %i[cities cities_jobs company])
.page(params[:page]).per(Job::JOB_PER_PAGE)
end
end
class JobsController < ApplicationController
before_action :history, only: :show
def index
if job_params.present?
search
......@@ -14,6 +16,13 @@ class JobsController < ApplicationController
private
def history
return unless user_signed_in?
history = current_user.history_jobs.find_or_initialize_by(job_id: params[:id])
history.update(updated_at: Time.current)
end
def search
if job_params.key?(:model) && job_params.key?(:slug) # search by model
model = params[:model].classify.constantize
......
# frozen_string_literal: true
class Users::ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
# POST /resource/confirmation
# def create
# super
# end
# GET /resource/confirmation?confirmation_token=abcdef
# def show
# super
# end
protected
def after_confirmation_path_for(resource_name, resource)
sign_in(resource)
token = resource.send(:set_reset_password_token)
signup_infomation_path(reset_password_token: token)
end
end
# frozen_string_literal: true
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# You should configure your model like this:
# devise :omniauthable, omniauth_providers: [:twitter]
# You should also create an action method in this controller like this:
# def twitter
# end
# More info at:
# https://github.com/heartcombo/devise#omniauth
# GET|POST /resource/auth/twitter
# def passthru
# super
# end
# GET|POST /users/auth/twitter/callback
# def failure
# super
# end
# protected
# The path used when OmniAuth fails
# def after_omniauth_failure_path_for(scope)
# super(scope)
# end
end
# frozen_string_literal: true
class Users::PasswordsController < Devise::PasswordsController
# GET /resource/password/new
# def new
# super
# end
# POST /resource/password
# def create
# super
# end
# GET /resource/password/edit?reset_password_token=abcdef
# def edit
# super
# end
# PUT /resource/password
# def update
# super
# end
# protected
# def after_resetting_password_path_for(resource)
# super(resource)
# end
# The path used after sending reset password instructions
# def after_sending_reset_password_instructions_path_for(resource_name)
# super(resource_name)
# end
end
# frozen_string_literal: true
class Users::RegistrationsController < Devise::RegistrationsController
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
def information
self.resource = resource_class.new
signed_in_resource
render :infomation
end
def send_mail; end
protected
def update_resource(resource, params)
resource.update_without_password(params)
end
def after_inactive_sign_up_path_for(resource)
scope = Devise::Mapping.find_scope!(resource)
router_name = Devise.mappings[scope].router_name
context = router_name ? send(router_name) : self
context.respond_to?(:send_signup_mail_path) ? context.send_signup_mail_path : "/"
end
end
# frozen_string_literal: true
class Users::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
# def new
# super
# end
# POST /resource/sign_in
# def create
# super
# end
# DELETE /resource/sign_out
# def destroy
# super
# end
# protected
# If you have extra params to permit, append them to the sanitizer.
# def configure_sign_in_params
# devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
# end
end
# frozen_string_literal: true
class Users::UnlocksController < Devise::UnlocksController
# GET /resource/unlock/new
# def new
# super
# end
# POST /resource/unlock
# def create
# super
# end
# GET /resource/unlock?unlock_token=abcdef
# def show
# super
# end
# protected
# The path used after sending unlock password instructions
# def after_sending_unlock_instructions_path_for(resource)
# super(resource)
# end
# The path used after unlocking the resource
# def after_unlock_path_for(resource)
# super(resource)
# end
end
class UsersController < ApplicationController
before_action :logged_in_user
# before_action :logged_in_user
def show
@user = User.find(params[:id])
@user = current_user
end
end
module DeviseHelper
def devise_error_messages!
return '' if resource.errors.empty?
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
html = <<-HTML
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<button type="button" class="close" data-dismiss="alert">
<span aria-hidden="true">&times;</span>
</button>
<strong>
#{pluralize(resource.errors.count, 'error')} must be fixed
</strong>
#{messages}
</div>
HTML
html.html_safe
end
end
......@@ -3,6 +3,11 @@
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
......
......@@ -78,21 +78,39 @@ Validator = function(options) {
}
}
Validator.isRequired = function (selector) {
Validator.isRequired = function (selector, message) {
return {
selector: selector,
test: function (value) {
return value.trim() ? undefined : "This field can't be blank"
return value.trim() ? undefined : message || "This field can't be blank"
}
}
}
Validator.isEmail = function (selector) {
Validator.isEmail = function (selector, message) {
return {
selector: selector,
test: function (value) {
var regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(value) ? undefined : 'Email invalid';
return regex.test(value) ? undefined : message || 'Email invalid';
}
}
}
Validator.minLenght = function (selector, min, message) {
return {
selector: selector,
test: function (value) {
return value.length >= min ? undefined : message || 'field too short';
}
}
}
Validator.isConfirmed = function (selector, getConfirmValue, message) {
return {
selector: selector,
test: function (value) {
return value == getConfirmValue() ? undefined : message || 'Incorrect password';
}
}
}
......
class HistoryJob < ApplicationRecord
before_save :history_count
belongs_to :job
belongs_to :user
private
def history_count
history_list = user.history_jobs.order(updated_at: :desc)
history_list.last.destroy if history_list.count >= 20
end
end
class Job < ApplicationRecord
scope :jobs_of, ->(model, user_id) do
joins(model.to_sym)
.where("#{model.to_sym}.user_id = #{user_id}")
.eager_load(:cities, :cities_jobs, :company)
end
LATEST_JOBS_LIMIT = 5
JOB_PER_PAGE = 20
......@@ -7,9 +13,9 @@ class Job < ApplicationRecord
belongs_to :company
has_many :apply_jobs, dependent: :destroy
has_many :favorite_jobs, dependent: :destroy
has_many :history_job, dependent: :destroy
has_many :history_jobs, dependent: :destroy
def self.sort_by_date(page: 1, per_page: limit)
def self.sort_by_date(page: 1, per_page: 1)
includes(:cities, :cities_jobs, :company).order(created_at: :desc).page(page).per(per_page).references(:cities)
end
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
ACCEPT_CONTENT_TYPE = 'application/pdf, application/msword, application/zip, application/xls, application/xlsx'.freeze
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
:recoverable, :rememberable, :validatable, :confirmable
has_many :apply_jobs, dependent: :destroy
has_many :favorite_jobs, dependent: :destroy
has_many :history_jobs, dependent: :destroy
has_one_attached :cv
validates :cv, content_type: { in: ACCEPT_CONTENT_TYPE,
message: 'must be a valid cv format' },
size:
{ less_than: 5.megabytes,
message: 'should be less than 5MB' }
def update_without_password(params, *options)
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = update(params, *options)
clean_up_passwords
result
end
def favorite?(job)
@favorite_job_ids ||= Set.new(favorite_jobs.pluck(:job_id))
@favorite_job_ids.include?(job.id)
end
protected
def password_required?
confirmed? ? super : false
end
end
.job-item
.job-head.d-flex.align-items-center.justify-content-between
= link_to applied_job.job.title, applied_job.job, class: 'job-title fs-3 text-decoration-none text-reset'
- if user_signed_in? && current_user.favorite?(applied_job.job)
= render 'shared/unfavorite', job_id: applied_job.job.id
- else
= render 'shared/favorite', job_id: applied_job.job.id
.job-caption
= link_to applied_job.job.company.name, '#', class: 'job-company text-decoration-none text-secondary'
p.job-salary.text-success
| Salary: #{applied_job.job.salary}
ul.list-unstyled
= show_location(applied_job.job.cities)
.job-desc
= truncate(simple_format(applied_job.job.description), escape: false, length: 250)
.applied-at
strong Applied at:
= applied_job.updated_at.strftime('%d/%m/%Y')
hr.my-4
- provide(:title, 'User Apply Jobs')
.container.py-5
h1.text-center.my-job-label My Job
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @jobs
.page-info.p-2
= paginate @jobs
= render partial: "my_jobs", collection: @jobs, as: :applied_job
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @jobs
.page-info.p-2
= paginate @jobs
$("#favorite-<%= @job.id %>").html("<%= escape_javascript(render('shared/unfavorite', job_id: @job.id)) %>");
$("#unfavorite-<%= @job.id %>").html("<%= escape_javascript(render('shared/favorite', job_id: @job.id)) %>");
- provide(:title, 'History page')
- if @favorite_jobs.empty?
h2 No jobs in your history
- else
= render partial: 'shared/jobs_list_index', object: @favorite_jobs, as: 'jobs'
- provide(:title, 'History page')
- if @history_jobs.empty?
h2 No jobs in your history
- else
= render partial: 'shared/jobs_list_index', object: @history_jobs, as: 'jobs'
......@@ -15,7 +15,12 @@
- @jobs.each do |job|
/ job
.job-item
.job-head.d-flex.align-items-center.justify-content-between
= link_to job.title, job, class: 'job-title fs-3 text-decoration-none text-reset'
- if user_signed_in? && current_user.favorite?(job)
= render 'shared/unfavorite', job_id: job.id
- else
= render 'shared/favorite', job_id: job.id
.job-caption
= link_to job.company.name, '#', class: 'job-company text-decoration-none text-secondary'
p.job-salary.text-success
......
......@@ -85,4 +85,7 @@ ruby:
= info
.job-apply.d-flex.align-items-center.justify-content-between
= link_to 'Apply for this job', apply_job_path(job_id: @job.id), class: 'btn btn-primary'
= link_to 'Favorite', '#', class: 'btn btn-primary'
- if user_signed_in? && current_user.favorite?(@job)
= render 'shared/unfavorite', job_id: @job.id
- else
= render 'shared/favorite', job_id: @job.id
......@@ -6,11 +6,17 @@ header.navbar.navbar-fixed-top.navbar-inverse
ul.nav.navbar-nav.navbar-right.text-light
- if user_signed_in?
li
= link_to "Profile", current_user
= link_to "Apply jobs", apply_jobs_path
li
= link_to "History", history_jobs_path
li
= link_to "Favorite", favorite_jobs_path
li
= link_to "Profile", user_profile_path
li
= link_to "Log out", destroy_user_session_path, method: :delete
- else
li
= link_to "Log in", new_user_session_path
= link_to "Log in", login_path
li
= link_to "Sign up", new_user_registration_path
= link_to "Sign up", signup_path
......@@ -13,11 +13,6 @@ html
body
= render 'layouts/header'
.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|
div class=("text-center alert alert-#{message_type}") = message
= yield
......
div id="favorite-#{job_id}"
= link_to favorite_jobs_path(job_id: job_id, format: :js),
method: :post,
remote: true,
class: 'btn btn-primary'
| Favorite
.container.py-5
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info jobs
.page-info.p-2
= paginate jobs
.container
= form_with(url: apply_job_path, method: :get) do |f|
- jobs.each do |item|
.row
.col-1.job-check.align-self-center
= f.radio_button :job_id, item.job.id, class: 'btn-check', id: "option-#{item.job.id}"
= f.label 'Choose', class: 'btn btn-outline-primary', for: "option-#{item.job.id}"
.job-item.col-10
.job-head
= link_to item.job.title, item.job, class: 'job-title fs-3 text-decoration-none text-reset'
.job-caption
= link_to item.job.company.name, '#', class: 'job-company text-decoration-none text-secondary'
p.job-salary.text-success
| Salary: #{item.job.salary}
ul.list-unstyled
= show_location(item.job.cities)
.job-desc
= truncate(simple_format(item.job.description), escape: false, length: 250)
.job-fav.col-1
- if user_signed_in? && current_user.favorite?(item.job)
= render 'shared/unfavorite', job_id: item.job.id
- else
= render 'shared/favorite', job_id: item.job.id
hr.my-4
.d-flex.justify-content-center
= f.submit 'Apply job', class: 'btn btn-primary w-50'
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info jobs
.page-info.p-2
= paginate jobs
div id="unfavorite-#{job_id}"
= link_to favorite_job_path(current_user, job_id: job_id, format: :js),
method: :delete,
remote: true,
class: 'btn btn-secondary'
| UnFavorite
<h2><%= t "devise.invitations.edit.header" %></h2>
<%= form_for(resource, as: resource_name, url: invitation_path(resource_name), html: { method: :put }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<%= f.hidden_field :invitation_token, readonly: true %>
<% if f.object.class.require_password_on_accepting %>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<% end %>
<div class="actions">
<%= f.submit t("devise.invitations.edit.submit_button") %>
</div>
<% end %>
<h2><%= t "devise.invitations.new.header" %></h2>
<%= form_for(resource, as: resource_name, url: invitation_path(resource_name), html: { method: :post }) do |f| %>
<%= render "users/shared/error_messages", resource: resource %>
<% resource.class.invite_key_fields.each do |field| -%>
<div class="field">
<%= f.label field %><br />
<%= f.text_field field %>
</div>
<% end -%>
<div class="actions">
<%= f.submit t("devise.invitations.new.submit_button") %>
</div>
<% end %>
<p><%= t("devise.mailer.invitation_instructions.hello", email: @resource.email) %></p>
<p><%= t("devise.mailer.invitation_instructions.someone_invited_you", url: root_url) %></p>
<p><%= link_to t("devise.mailer.invitation_instructions.accept"), accept_invitation_url(@resource, invitation_token: @token) %></p>
<% if @resource.invitation_due_at %>
<p><%= t("devise.mailer.invitation_instructions.accept_until", due_date: l(@resource.invitation_due_at, format: :'devise.mailer.invitation_instructions.accept_until_format')) %></p>
<% end %>
<p><%= t("devise.mailer.invitation_instructions.ignore") %></p>
<%= t("devise.mailer.invitation_instructions.hello", email: @resource.email) %>
<%= t("devise.mailer.invitation_instructions.someone_invited_you", url: root_url) %>
<%= accept_invitation_url(@resource, invitation_token: @token) %>
<% if @resource.invitation_due_at %>
<%= t("devise.mailer.invitation_instructions.accept_until", due_date: l(@resource.invitation_due_at, format: :'devise.mailer.invitation_instructions.accept_until_format')) %>
<% end %>
<%= t("devise.mailer.invitation_instructions.ignore") %>
<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
| Dear #{@resource.email}!
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= link_to 'Change my password', reset_password_url(reset_password_token: @token)
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
- provide(:title, "Change password")
- provide(:title, 'Change password')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Change your password
| Set 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
= 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"
.field.row.mb-5.form-group
.col-4
= f.label :password, 'New password', class: 'form-label'
.col-8
= f.password_field :password, autofocus: true, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.mb-5.form-group
.col-4
= f.label :password_confirmation, 'Password Confirmation', class: 'form-label'
.col-8
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.actions.row.justify-content-end
.col-8
= f.submit 'Set my password', class: 'btn btn-primary w-50 my-4', data: { disable_with: false }
javascript:
Validator({
form: '#new_user',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#user_password'),
Validator.minLenght('#user_password', 6, 'Use 6 characters or more for your password'),
Validator.isRequired('#user_password_confirmation'),
Validator.isConfirmed('#user_password_confirmation', function() {
return document.querySelector('#new_user #user_password').value;
}, 'Those passwords didn’t match.'),
]
})
- provide(:title, "Forgot password")
- provide(:title, 'Forgot password')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Forgot your password?
| Forgot 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
= render 'users/shared/error_messages', resource: resource
.field.row.form-group.mb-4
.col-2
= 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"
.col-10
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
- if devise_mapping.confirmable? && controller_name != 'confirmations'
p
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name), class: 'text-decoration-none'
.actions.row.justify-content-end
.col-10.d-flex.justify-content-center
= f.submit 'Confirm your email', class: 'btn btn-primary w-75 my-4', data: { disable_with: false }
javascript:
Validator({
form: '#new_user',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#user_email'),
Validator.isEmail('#user_email')
]
})
- provide(:title, "Edit profile")
- provide(:title, 'Edit profile')
.container
.row.justify-content-center
.col-6
.col-8
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
= render 'users/shared/error_messages', resource: resource
.field.row.mb-2.form-group
.col-3
= f.label :email, class: 'form-label'
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control mb-2'
.field
.col-9
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
div
| Currently waiting confirmation for:
= resource.unconfirmed_email
.field
.field.row.mb-2.form-group
.col-3
= f.label :name, class: 'form-label'
br
= f.text_field :name, autofocus: true, autocomplete: "name", class: 'form-control mb-2'
.field
.col-9
= f.text_field :name, autofocus: true, autocomplete: 'name', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :address, class: 'form-label'
br
= f.text_field :address, autofocus: true, autocomplete: "address", class: 'form-control mb-2'
.field
.col-9
= f.text_field :address, autofocus: true, autocomplete: 'address', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :phone, class: 'form-label'
br
= f.text_field :phone, autofocus: true, autocomplete: "phone", class: 'form-control mb-2'
.field
.col-9
= f.text_field :phone, autofocus: true, autocomplete: 'phone', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :cv, class: 'form-label'
.col-9
= f.file_field :cv, accept: ApplyJob::ACCEPT_CONTENT_TYPE, class: 'form-control', direct_upload: true
.row.my-4.justify-content-end
.col-9
hr
p
|(leave password blank if you don't want to change it)
.field.row.mb-2.form-group
.col-3
= 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
.col-9
= f.password_field :password, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= 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"
.col-9
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.actions.row.justify-content-end
.col-9
= f.submit 'Done', class: 'btn btn-primary w-50 my-4', data: { disable_with: false }
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
= 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
javascript:
Validator({
form: '#edit_user',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#user_email'),
Validator.isEmail('#user_email'),
Validator.isRequired('#user_name')
]
})
\ No newline at end of file
- provide(:title, 'Edit profile')
.container
.row.justify-content-center
.col-8
h2.text-center.my-5
| Register
= 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.row.mb-2.form-group
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
div
| Currently waiting confirmation for:
= resource.unconfirmed_email
.field.row.mb-2.form-group
.col-3
= f.label :name, class: 'form-label'
.col-9
= f.text_field :name, autofocus: true, autocomplete: 'name', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :address, class: 'form-label'
.col-9
= f.text_field :address, autofocus: true, autocomplete: 'address', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :phone, class: 'form-label'
.col-9
= f.text_field :phone, autofocus: true, autocomplete: 'phone', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :password, class: 'form-label'
.col-9
= f.password_field :password, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :password_confirmation, class: 'form-label'
.col-9
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.mb-2.form-group
.col-3
= f.label :cv, class: 'form-label'
.col-9
= f.file_field :cv, accept: ApplyJob::ACCEPT_CONTENT_TYPE, class: 'form-control', direct_upload: true
.actions.row.justify-content-end
.col-9
= f.submit 'Register', class: 'btn btn-primary w-50 my-4', data: { disable_with: false }
javascript:
Validator({
form: '#new_user',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#user_name'),
Validator.isRequired('#user_password'),
Validator.isRequired('#user_password_confirmation'),
Validator.isConfirmed('#user_password_confirmation', function () {
return document.querySelector('#new_user #user_password').value;
}, 'password confirmation does not match')
]
})
\ No newline at end of file
- provide(:title, "Sign Up")
- provide(:title, 'Sign Up')
.container
.row.justify-content-center
......@@ -6,36 +6,36 @@
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
= 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'
= 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
= f.submit 'Sign up', class: 'btn btn-primary w-100 my-5'
= render 'users/shared/links'
\ No newline at end of file
.container.text-center
h2.my-5
| Register
p
| 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.
p
| Please note that the registration link is only valid for 24 hours.
p
| Over that period, you will have to register your email again.
\ No newline at end of file
- provide(:title, 'Log in')
.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
.field.row.form-group.mb-4
.col-2
= f.label :email, class: 'form-label'
br
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control mb-2'
.field
.col-10
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-2
= f.label :password, class: 'form-label'
br
= f.password_field :password, autocomplete: "current-password", class: 'form-control mb-2'
.col-10
= f.password_field :password, autocomplete: 'current-password', class: 'form-control mb-2'
span.form-msg
- 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
= f.label :remember_me, class: 'form-label mx-2'
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
= link_to 'Forgot password?', forgot_password_path, class: 'text-decoration-none'
.actions.row.justify-content-end
.col-10.d-flex.justify-content-around
= f.submit 'Log in', class: 'btn btn-primary w-25', data: { disable_with: false }
= link_to 'Sign up', new_registration_path(resource_name), class: 'text-decoration-none w-25 btn btn-secondary'
javascript:
Validator({
form: '#new_user',
errorSelector: '.form-msg',
rules: [
Validator.isRequired('#user_email'),
Validator.isEmail('#user_email'),
Validator.isRequired('#user_password')
]
})
- if controller_name != 'sessions'
= link_to "Log in", new_session_path(resource_name), class: 'text-decoration-none'
= 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'
= link_to 'Sign up', new_registration_path(resource_name), class: 'text-decoration-none btn btn-primary'
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'
= 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'
......
......@@ -3,13 +3,13 @@
.container
.main-body
/! Breadcrumb
nav.main-breadcrumb aria-label="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
li.breadcrumb-item.active aria-current='page' User Profile
/! /Breadcrumb
.row.gutters-sm
.col-md-4.mb-3
......@@ -17,7 +17,7 @@
.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" /
/ img.rounded-circle alt='Admin' src='https://bootdey.com/img/Content/avatar/avatar7.png' width='150' /
.mt-3
h4
= @user.name
......@@ -30,36 +30,36 @@
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")
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")
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")
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"
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")
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
......@@ -88,11 +88,19 @@
h6.mb-0 Address
.col-sm-9.text-secondary
= @user.address
hr/
.row
.col-sm-3
h6.mb-0 CV
.col-sm-9.text-secondary
- if @user.cv.attached?
= link_to @user.cv.filename, rails_blob_path(@user.cv, disposition: "attachment")
- else
= 'cv file not uploaded yet'
hr/
.row
.col-sm-12
= link_to 'Edit profile', edit_user_registration_path, class:'btn btn-info'
= link_to 'Edit profile', edit_profile_path, class:'btn btn-info'
.row.gutters-sm
.col-sm-6.mb-3
.card.h-100
......@@ -102,20 +110,20 @@
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%")
.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%")
.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%")
.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%")
.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%")
.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
......@@ -124,17 +132,17 @@
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%")
.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%")
.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%")
.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%")
.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%")
.progress.mb-3 style=('height: 5px')
.progress-bar.bg-primary aria-valuemax='100' aria-valuemin='0' aria-valuenow='66' role='progressbar' style=('width: 66%')
......@@ -24,7 +24,7 @@ Devise.setup do |config|
# 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'
config.mailer_sender = 'user@venjob.com'
# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
......@@ -134,6 +134,55 @@ Devise.setup do |config|
# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false
# ==> Configuration for :invitable
# The period the generated invitation token is valid.
# After this period, the invited resource won't be able to accept the invitation.
# When invite_for is 0 (the default), the invitation won't expire.
# config.invite_for = 24.hours
# Number of invitations users can send.
# - If invitation_limit is nil, there is no limit for invitations, users can
# send unlimited invitations, invitation_limit column is not used.
# - If invitation_limit is 0, users can't send invitations by default.
# - If invitation_limit n > 0, users can send n invitations.
# You can change invitation_limit column for some users so they can send more
# or less invitations, even with global invitation_limit = 0
# Default: nil
# config.invitation_limit = 5
# The key to be used to check existing users when sending an invitation
# and the regexp used to test it when validate_on_invite is not set.
# config.invite_key = { email: /\A[^@]+@[^@]+\z/ }
# config.invite_key = { email: /\A[^@]+@[^@]+\z/, username: nil }
# Ensure that invited record is valid.
# The invitation won't be sent if this check fails.
# Default: false
# config.validate_on_invite = true
# Resend invitation if user with invited status is invited again
# Default: true
# config.resend_invitation = false
# The class name of the inviting model. If this is nil,
# the #invited_by association is declared to be polymorphic.
# Default: nil
# config.invited_by_class_name = 'User'
# The foreign key to the inviting model (if invited_by_class_name is set)
# Default: :invited_by_id
# config.invited_by_foreign_key = :invited_by_id
# The column name used for counter_cache column. If this is nil,
# the #invited_by association is declared without counter_cache.
# Default: nil
# config.invited_by_counter_cache = :invitations_count
# Auto-login after the user accepts the invite. If this is false,
# the user will need to manually log in after accepting the invite.
# Default: true
# config.allow_insecure_sign_in_after_accept = 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
......@@ -224,7 +273,7 @@ Devise.setup do |config|
# 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
config.reset_password_within = 24.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.
......
......@@ -41,7 +41,8 @@ en:
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."
# 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."
signed_up_but_unconfirmed: ""
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."
......
Rails.application.routes.draw do
devise_for :users
devise_for :users, controllers: {
sessions: 'users/sessions',
confirmations: 'users/confirmations',
registrations: 'users/registrations'
}
devise_scope :user do
get '/login', to: 'devise/sessions#new', as: :login
get '/my/info', to: 'devise/registrations#edit', as: :edit_profile
get '/forgot_password', to: 'devise/passwords#new', as: :forgot_password
get '/reset_password', to: 'devise/passwords#edit', as: :reset_password
get '/register/1', to: 'devise/registrations#new', as: :signup
get '/register/2', to: 'users/registrations#send_mail', as: :send_signup_mail
get '/register/3', to: 'users/registrations#information', as: :signup_infomation
end
root 'top#index'
resources :cities, only: %i[index]
resources :industries, only: %i[index]
get '/jobs/:model/:slug', to: 'jobs#index', as: :job_list
resources :favorite_jobs, only: %i[index create destroy]
resources :jobs, only: %i[index show]
resources :users, only: %i[index show]
get '/jobs/:model/:slug', to: 'jobs#index', as: :job_list
get '/my', to: 'users#show', as: :user_profile
get '/my/jobs', to: 'applies#index', as: :apply_jobs
get '/apply', to: 'applies#new', as: :apply_job
post '/confirm', to: 'applies#confirm', as: :confirm_job
post '/done', to: 'applies#create', as: :done_job
get '/history', to: 'history_jobs#index', as: :history_jobs
end
.container.text-center
h2.my-5
| Register
p
| 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.
p
| Please note that the registration link is only valid for 24 hours.
p
| Over that period, you will have to register your email again.
\ No newline at end of file
......@@ -8,6 +8,5 @@ class RemoveFieldFromUsers < ActiveRecord::Migration[6.1]
remove_column :users, :activated_at, :datetime
remove_column :users, :password_reset_digest, :string
remove_column :users, :password_reset_sent_at, :datetime
end
end
......@@ -21,7 +21,7 @@ class AddDeviseToUsers < ActiveRecord::Migration[6.1]
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# # Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
......
class DeviseInvitableAddToUsers < ActiveRecord::Migration[6.1]
def up
change_table :users do |t|
t.string :invitation_token
t.datetime :invitation_created_at
t.datetime :invitation_sent_at
t.datetime :invitation_accepted_at
t.integer :invitation_limit
t.references :invited_by, polymorphic: true
t.integer :invitations_count, default: 0
t.index :invitation_token, unique: true # for invitable
t.index :invited_by_id
end
end
def down
change_table :users do |t|
t.remove_references :invited_by, polymorphic: true
t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
end
end
end
class AddConfirmableToUsers < ActiveRecord::Migration[6.1]
def change
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_column :users, :unconfirmed_email, :string
add_index :users, :confirmation_token, unique: true
end
end
class AddIndexToFavoriteJobs < ActiveRecord::Migration[6.1]
def change
add_index :favorite_jobs, %i[job_id job_id], unique: true
end
end
class RemoveDeviseInvitable < ActiveRecord::Migration[6.1]
def change
def change
remove_column :users, :invitation_token, :string
remove_column :users, :invitation_created_at, :datetime
remove_column :users, :invitation_sent_at, :datetime
remove_column :users, :invitation_accepted_at, :datetime
remove_column :users, :invitation_limit, :integer
remove_references :users, :invited_by, :references
remove_column :users, :invitations_count, :integer
remove_column :users, :invitation_token, :index
remove_column :users, :invited_by_id, :index
end
end
end
class RemoveIndexFormFavorite < ActiveRecord::Migration[6.1]
def change
remove_index :favorite_jobs, name: :index_favorite_jobs_on_job_id_and_job_id
end
end
class AddNewIndexToFavorite < ActiveRecord::Migration[6.1]
def change
add_index :favorite_jobs, %i[job_id user_id], 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_05_105601) do
ActiveRecord::Schema.define(version: 2021_09_07_064156) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name", null: false
......@@ -80,6 +80,7 @@ ActiveRecord::Schema.define(version: 2021_08_05_105601) do
t.bigint "user_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["job_id", "user_id"], name: "index_favorite_jobs_on_job_id_and_user_id", unique: true
t.index ["job_id"], name: "index_favorite_jobs_on_job_id"
t.index ["user_id"], name: "index_favorite_jobs_on_user_id"
end
......@@ -138,7 +139,23 @@ ActiveRecord::Schema.define(version: 2021_08_05_105601) do
t.datetime "remember_created_at"
t.string "phone"
t.string "address"
t.string "invitation_token"
t.datetime "invitation_created_at"
t.datetime "invitation_sent_at"
t.datetime "invitation_accepted_at"
t.integer "invitation_limit"
t.string "invited_by_type"
t.bigint "invited_by_id"
t.integer "invitations_count", default: 0
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 ["invitation_token"], name: "index_users_on_invitation_token", unique: true
t.index ["invited_by_id"], name: "index_users_on_invited_by_id"
t.index ["invited_by_type", "invited_by_id"], name: "index_users_on_invited_by"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
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