Commit 0d885e60 by Ngô Trung Hưng

create func login logout mypage

parent fdadf9b7
Pipeline #881 canceled with stages
in 0 seconds
...@@ -31,6 +31,8 @@ gem 'whenever' ...@@ -31,6 +31,8 @@ gem 'whenever'
gem 'kaminari' gem 'kaminari'
gem 'breadcrumbs_on_rails' gem 'breadcrumbs_on_rails'
gem 'draper' gem 'draper'
gem 'devise'
gem 'carrierwave'
# Use ActiveStorage variant # Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8' # gem 'mini_magick', '~> 4.8'
......
...@@ -51,6 +51,7 @@ GEM ...@@ -51,6 +51,7 @@ GEM
archive-zip (0.12.0) archive-zip (0.12.0)
io-like (~> 0.3.0) io-like (~> 0.3.0)
arel (9.0.0) arel (9.0.0)
bcrypt (3.1.15)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.4.7) bootsnap (1.4.7)
msgpack (~> 1.0) msgpack (~> 1.0)
...@@ -66,6 +67,13 @@ GEM ...@@ -66,6 +67,13 @@ GEM
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
regexp_parser (~> 1.5) regexp_parser (~> 1.5)
xpath (~> 3.2) xpath (~> 3.2)
carrierwave (2.1.0)
activemodel (>= 5.0.0)
activesupport (>= 5.0.0)
addressable (~> 2.6)
image_processing (~> 1.1)
mimemagic (>= 0.3.0)
mini_mime (>= 0.1.3)
childprocess (3.0.0) childprocess (3.0.0)
chromedriver-helper (2.1.1) chromedriver-helper (2.1.1)
archive-zip (~> 0.10) archive-zip (~> 0.10)
...@@ -80,6 +88,12 @@ GEM ...@@ -80,6 +88,12 @@ GEM
coffee-script-source (1.12.2) coffee-script-source (1.12.2)
concurrent-ruby (1.1.6) concurrent-ruby (1.1.6)
crass (1.0.6) crass (1.0.6)
devise (4.7.2)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
draper (4.0.1) draper (4.0.1)
actionpack (>= 5.0) actionpack (>= 5.0)
activemodel (>= 5.0) activemodel (>= 5.0)
...@@ -93,6 +107,9 @@ GEM ...@@ -93,6 +107,9 @@ GEM
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
i18n (1.8.5) i18n (1.8.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
image_processing (1.11.0)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
io-like (0.3.1) io-like (0.3.1)
jbuilder (2.10.0) jbuilder (2.10.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
...@@ -121,6 +138,7 @@ GEM ...@@ -121,6 +138,7 @@ GEM
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
method_source (1.0.0) method_source (1.0.0)
mimemagic (0.3.5) mimemagic (0.3.5)
mini_magick (4.10.1)
mini_mime (1.0.2) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
minitest (5.14.1) minitest (5.14.1)
...@@ -129,6 +147,7 @@ GEM ...@@ -129,6 +147,7 @@ GEM
nio4r (2.5.2) nio4r (2.5.2)
nokogiri (1.10.10) nokogiri (1.10.10)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
orm_adapter (0.5.0)
public_suffix (4.0.5) public_suffix (4.0.5)
puma (3.12.6) puma (3.12.6)
rack (2.2.3) rack (2.2.3)
...@@ -165,6 +184,11 @@ GEM ...@@ -165,6 +184,11 @@ GEM
regexp_parser (1.7.1) regexp_parser (1.7.1)
request_store (1.5.0) request_store (1.5.0)
rack (>= 1.4) rack (>= 1.4)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
ruby-vips (2.0.17)
ffi (~> 1.9)
ruby_dep (1.5.0) ruby_dep (1.5.0)
rubyzip (2.3.0) rubyzip (2.3.0)
sass (3.7.4) sass (3.7.4)
...@@ -202,6 +226,8 @@ GEM ...@@ -202,6 +226,8 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (4.2.0) uglifier (4.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
warden (1.2.8)
rack (>= 2.0.6)
web-console (3.7.0) web-console (3.7.0)
actionview (>= 5.0) actionview (>= 5.0)
activemodel (>= 5.0) activemodel (>= 5.0)
...@@ -223,8 +249,10 @@ DEPENDENCIES ...@@ -223,8 +249,10 @@ DEPENDENCIES
breadcrumbs_on_rails breadcrumbs_on_rails
byebug byebug
capybara (>= 2.15) capybara (>= 2.15)
carrierwave
chromedriver-helper chromedriver-helper
coffee-rails (~> 4.2) coffee-rails (~> 4.2)
devise
draper draper
jbuilder (~> 2.5) jbuilder (~> 2.5)
kaminari kaminari
......
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
!function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return g({type:O.error,iconClass:m().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=m()),v=e("#"+t.containerId),v.length?v:(n&&(v=d(t)),v)}function o(e,t,n){return g({type:O.info,iconClass:m().iconClasses.info,message:e,optionsOverride:n,title:t})}function s(e){C=e}function i(e,t,n){return g({type:O.success,iconClass:m().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return g({type:O.warning,iconClass:m().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e,t){var o=m();v||n(o),u(e,o,t)||l(o)}function c(t){var o=m();return v||n(o),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function l(t){for(var n=v.children(),o=n.length-1;o>=0;o--)u(e(n[o]),t)}function u(t,n,o){var s=!(!o||!o.force)&&o.force;return!(!t||!s&&0!==e(":focus",t).length)&&(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0)}function d(t){return v=e("<div/>").attr("id",t.containerId).addClass(t.positionClass),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'<button type="button">&times;</button>',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1}}function f(e){C&&C(e)}function g(t){function o(e){return null==e&&(e=""),e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function s(){c(),u(),d(),p(),g(),C(),l(),i()}function i(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}I.attr("aria-live",e)}function a(){E.closeOnHover&&I.hover(H,D),!E.onclick&&E.tapToDismiss&&I.click(b),E.closeButton&&j&&j.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),E.onCloseClick&&E.onCloseClick(e),b(!0)}),E.onclick&&I.click(function(e){E.onclick(e),b()})}function r(){I.hide(),I[E.showMethod]({duration:E.showDuration,easing:E.showEasing,complete:E.onShown}),E.timeOut>0&&(k=setTimeout(b,E.timeOut),F.maxHideTime=parseFloat(E.timeOut),F.hideEta=(new Date).getTime()+F.maxHideTime,E.progressBar&&(F.intervalId=setInterval(x,10)))}function c(){t.iconClass&&I.addClass(E.toastClass).addClass(y)}function l(){E.newestOnTop?v.prepend(I):v.append(I)}function u(){if(t.title){var e=t.title;E.escapeHtml&&(e=o(t.title)),M.append(e).addClass(E.titleClass),I.append(M)}}function d(){if(t.message){var e=t.message;E.escapeHtml&&(e=o(t.message)),B.append(e).addClass(E.messageClass),I.append(B)}}function p(){E.closeButton&&(j.addClass(E.closeClass).attr("role","button"),I.prepend(j))}function g(){E.progressBar&&(q.addClass(E.progressClass),I.prepend(q))}function C(){E.rtl&&I.addClass("rtl")}function O(e,t){if(e.preventDuplicates){if(t.message===w)return!0;w=t.message}return!1}function b(t){var n=t&&E.closeMethod!==!1?E.closeMethod:E.hideMethod,o=t&&E.closeDuration!==!1?E.closeDuration:E.hideDuration,s=t&&E.closeEasing!==!1?E.closeEasing:E.hideEasing;if(!e(":focus",I).length||t)return clearTimeout(F.intervalId),I[n]({duration:o,easing:s,complete:function(){h(I),clearTimeout(k),E.onHidden&&"hidden"!==P.state&&E.onHidden(),P.state="hidden",P.endTime=new Date,f(P)}})}function D(){(E.timeOut>0||E.extendedTimeOut>0)&&(k=setTimeout(b,E.extendedTimeOut),F.maxHideTime=parseFloat(E.extendedTimeOut),F.hideEta=(new Date).getTime()+F.maxHideTime)}function H(){clearTimeout(k),F.hideEta=0,I.stop(!0,!0)[E.showMethod]({duration:E.showDuration,easing:E.showEasing})}function x(){var e=(F.hideEta-(new Date).getTime())/F.maxHideTime*100;q.width(e+"%")}var E=m(),y=t.iconClass||E.iconClass;if("undefined"!=typeof t.optionsOverride&&(E=e.extend(E,t.optionsOverride),y=t.optionsOverride.iconClass||y),!O(E,t)){T++,v=n(E,!0);var k=null,I=e("<div/>"),M=e("<div/>"),B=e("<div/>"),q=e("<div/>"),j=e(E.closeHtml),F={intervalId:null,hideEta:null,maxHideTime:null},P={toastId:T,state:"visible",startTime:new Date,options:E,map:t};return s(),r(),a(),f(P),E.debug&&console&&console.log(P),I}}function m(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),w=void 0))}var v,C,w,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:c,error:t,getContainer:n,info:o,options:{},subscribe:s,success:i,version:"2.1.3",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)});
//# sourceMappingURL=toastr.js.map
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
...@@ -1349,4 +1349,172 @@ $main-color: #23303D; ...@@ -1349,4 +1349,172 @@ $main-color: #23303D;
font-weight: bold; font-weight: bold;
color: rgb(211, 48, 48); color: rgb(211, 48, 48);
} }
.new_user {
width: 100%;
}
.box_form {
width: 80%;
margin: auto;
margin-top: 10vh;
margin-bottom: 35vh;
padding: 15px 50px;
height: auto;
border-radius: 6px;
box-shadow: 0px 1px 5px 2px rgba($color: #999, $alpha: 0.7);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
width: 80px;
height: 20px;
background-color: red;
transform: rotate(315deg);
top: 10px;
left: -20px;
}
&::after {
content: '';
position: absolute;
width: 80px;
height: 20px;
background-color: red;
transform: rotate(135deg);
bottom: 5px;
right: -20px;
}
}
.box_btn-reg {
width: 50%;
margin: auto;
}
.box_ful_link_reg {
text-align: center;
}
#error_explanation ul li{
list-style: none;
color: red;
font-size: 15px;
font-style: italic;
}
.pppp {
width: 100% !important;
}
.list_email_app {
ul {
list-style: none;
display: flex;
}
li {
margin: 5px 15px;
&:nth-child(2) {
a {
color: red;
}
}
&:nth-child(3) {
a {
color: rgb(101, 73, 226);
}
}
&:nth-child(4) {
a {
color: rgb(25, 110, 238);
}
}
&:hover {
a {
text-decoration: none;
transition: 0.4s;
font-style: italic;
}
}
}
}
.bg_img {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-image:asset-url('11.jpg');
}
.box_my_page {
position: relative;
top: 10vh;
width: 90%;
margin: auto;
border-radius: 5px;
border: 1px solid rgba(#5d4708, 0.5);
z-index: 22;
box-shadow: 0px 1px 5px 1px rgba($color: #636363, $alpha: 0.8);
&::before {
content: '';
position: absolute;
width: 20px;
height: 300px;
background-color: #d39e00;
transform: translateY(-260px);
top: 0;
left: 8%;
}
&::after {
content: '';
position: absolute;
width: 20px;
height: 300px;
background-color: #d39e00;
transform: translateY(-260px);
top: 0;
z-index: -1;
right: 8%;
}
}
.box_my_page_header {
position: relative;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
background-color: #f1a752;
text-align: center;
height: 40px;
h4 {
font-size: 22px;
color: #000;
line-height: 40px;
}
&::before {
content: '';
position: absolute;
width: 20px;
height: 20px;
background-color: #5d4708;
border-radius: 50%;
top: 5px;
left: 8%;
}
&::after {
content: '';
position: absolute;
width: 20px;
height: 20px;
background-color: #5d4708;
border-radius: 50%;
top: 5px;
right: 8%;
}
}
.box_my_page_content {
padding: 10px 35px 20px;
font-size: 16px;
font-family: 'Raleway', sans-serif;
font-weight: bold;
}
.link_ct.pdf {
color: rgb(134, 94, 18);
}
#edit_user{
margin: auto;
}
\ No newline at end of file
...@@ -138,4 +138,9 @@ ...@@ -138,4 +138,9 @@
} }
} }
.btn-info-custom {
width: 100% !important;
}
.btn-custom-padding {
margin: 5px 5px 0px 0px;
}
// Place all the styles related to the registrations controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
// Place all the styles related to the users controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
# frozen_string_literal: true
# Application controller
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:name, :email, :cv, :password) }
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:name, :email, :cv, :password, :current_password) }
end
end end
class RegistrationsController < Devise::RegistrationsController
def after_inactive_sign_up_path_for(resource)
confirm_sign_up_path(code: 2)
end
end
# frozen_string_literal: true
# User controller
class UsersController < ApplicationController
def confirm_sign_up
render :confirm
end
def my_page
if user_signed_in?
render :my_page
else
redirect_to root_path
end
end
end
class RegistrationDecorator < Draper::Decorator
delegate_all
# Define presentation-specific methods here. Helpers are accessed through
# `helpers` (aka `h`). You can override attributes, for example:
#
# def created_at
# helpers.content_tag :span, class: 'time' do
# object.created_at.strftime("%a %m/%d/%y")
# end
# end
end
class UserDecorator < Draper::Decorator
delegate_all
# Define presentation-specific methods here. Helpers are accessed through
# `helpers` (aka `h`). You can override attributes, for example:
#
# def created_at
# helpers.content_tag :span, class: 'time' do
# object.created_at.strftime("%a %m/%d/%y")
# end
# end
end
...@@ -6,4 +6,15 @@ module ApplicationHelper ...@@ -6,4 +6,15 @@ module ApplicationHelper
base_title = 'VenJob' base_title = 'VenJob'
page_title.empty? ? base_title : "#{base_title} | #{page_title}" page_title.empty? ? base_title : "#{base_title} | #{page_title}"
end end
def custom_bootstrap_flash
flash_messages = []
flash.each do |type, message|
type = 'success' if type == 'notice'
type = 'error' if type == 'alert'
text = "<script>toastr.#{type}('#{message}');</script>"
flash_messages << text.html_safe if message
end
flash_messages.join("\n").html_safe
end
end end
module RegistrationsHelper
end
module UsersHelper
end
...@@ -2,9 +2,16 @@ ...@@ -2,9 +2,16 @@
# Description/Explanation of Person class # Description/Explanation of Person class
class User < ApplicationRecord class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
mount_uploader :cv, CvUploader
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
has_many :applied_jobs has_many :applied_jobs
has_many :jobs, through: :applied_jobs has_many :jobs, through: :applied_jobs
has_many :histories has_many :histories
has_many :favorites has_many :favorites
validates_length_of :name, within: 8..200
validates_length_of :email, within: 8..200
end end
class CvUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def size_range
0..5.megabyte
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url(*args)
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process scale: [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process resize_to_fit: [50, 50]
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_whitelist
%w(doc pdf xls xlsx zip)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= render "devise/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 "devise/shared/links" %>
<p>Welcome <%= @email %>!</p>
<p>By clicking on the following link, you are confirming your email address and agreeing to VeNJOB's Terms of Service.</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>
<h2>Change your password</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= f.hidden_field :reset_password_token %>
<div class="field">
<%= f.label :password, "New password" %><br />
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em><br />
<% end %>
<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Change my password" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<h2>Forgot your password?</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
<%= render "devise/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 "Send me reset password instructions" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<div class="container">
<div class="box_form">
<div class="row">
<div class="col-lg-12">
<h2 style="text-align: center">Edit my info</h2><hr>
</div>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-9">
<%= render 'devise/shared/error_messages', resource: resource %>
</div>
<div class="col-md-3">
<%= f.label :name %>:
</div>
<div class="col-md-9">
<%= f.text_field :name, autofocus: true, class: 'form-control' %>
</div>
</div><br>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="row">
<div class="col-md-3">
<%= f.label :email %>:
</div>
<div class="col-md-9">
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :cv %>:
</div>
<div class="col-md-9">
<%= f.file_field :cv, autofocus: true, class: 'pppp' %>
<i style="font-size: 15px; color: #666">Current file: <%= current_user.cv.identifier %></i>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :password %>:
</div>
<div class="col-md-9">
<%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
<i style="font-size: 15px; color: #666">Leave blank if you don't want to change it</i>
<% if @minimum_password_length %>
<em style="font-size: 15px; color: #666">(<%="Password have a minimum of #{@minimum_password_length} characters"%>)</em>
<% end %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :password_confirm %>:
</div>
<div class="col-md-9">
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control' %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :current_password %>:
</div>
<div class="col-md-9">
<%= f.password_field :current_password, autocomplete: 'current-password', class: 'form-control' %>
<i style="font-size: 15px; color: #666">We need your current password to confirm your changes</i>
</div>
</div><br>
<div class="row">
<div class="col-md-4">
<%= link_to 'Back', :back, class: 'btn btn-warning btn-info-custom' %>
</div>
<div class="col-md-4">
<%= f.submit 'Update', class: 'btn btn-info btn-info-custom' %>
</div>
<% end %>
<div class="col-md-4">
<%= button_to 'Cancel my account', registration_path(resource_name), class: 'btn btn-danger btn-info-custom', data: { confirm: "Are you sure?" }, method: :delete %>
</div>
</div>
</div>
<hr>
</div>
</div>
<div class="container">
<div class="box_form">
<div class="row">
<div class="col-lg-12">
<h2 style="text-align: center">Registration</h2><hr>
</div>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-9">
<%= render 'devise/shared/error_messages', resource: resource %>
</div>
<div class="col-md-3">
<%= f.label :name %>:
</div>
<div class="col-md-9">
<%= f.text_field :name, autofocus: true, class: 'form-control' %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :email %>:
</div>
<div class="col-md-9">
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :cv %>:
</div>
<div class="col-md-9">
<%= f.file_field :cv, autofocus: true, class: 'pppp' %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :password %>:
</div>
<div class="col-md-9">
<%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
<% if @minimum_password_length %>
<em style="font-size: 15px;">(<%="Password have a minimum of #{@minimum_password_length} characters"%>)</em>
<% end %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :password_confirm %>:
</div>
<div class="col-md-9">
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control' %>
</div>
</div><br>
<div class="row">
<div class="col-md-8"></div>
<div class="col-md-4">
<%= f.submit 'Sign up', class: 'btn btn-info btn-info-custom' %>
</div>
</div>
<% end %>
</div>
</div>
</div>
<div class="container">
<div class="box_form">
<div class="row">
<div class="col-lg-12">
<h2 style="text-align: center">Login</h2><hr>
</div>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-9">
<%= render 'devise/shared/error_messages', resource: resource %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :'Email:' %>
</div>
<div class="col-md-9">
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
</div>
</div><br>
<div class="row">
<div class="col-md-3">
<%= f.label :password %>:
</div>
<div class="col-md-9">
<%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
</div>
</div><br>
<div class="row">
<div class="col-lg-3 col-md-3">
</div>
<div class="col-lg-3">
<% if devise_mapping.rememberable? %>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end %>
</div>
<div class="col-lg-6">
<%= f.submit 'Login', class: 'btn btn-info btn-info-custom' %>
</div>
</div><br>
<% end %>
</div>
</div>
</div>
<div style="width: 100%; height: 5vh;"></div>
<% if resource.errors.any? %>
<div id="error_explanation">
<ul>
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% flash[:error] = 'Vui lòng kiểm tra lại thông tin' %>
<% end %>
</ul>
</div>
<% end %>
<%- if controller_name != 'sessions' %>
<%= link_to "Log in", new_session_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to "Forgot your password?", new_password_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %><br />
<% end %>
<%- 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) %><br />
<% end %>
<%- 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) %><br />
<% end %>
<% end %>
<h2>Resend unlock instructions</h2>
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<%= render "devise/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 "devise/shared/links" %>
...@@ -10,17 +10,26 @@ ...@@ -10,17 +10,26 @@
<div class="header_top_menu_right"> <div class="header_top_menu_right">
<ul class="list_menu_header_right"> <ul class="list_menu_header_right">
<li class="list_item_menu_header_right"> <li class="list_item_menu_header_right">
<%= link_to 'Yêu thích', '#', class: 'link_item_menu_header_right' %> <%= link_to 'Favorite', '#', class: 'link_item_menu_header_right' %>
</li> </li>
<li class="list_item_menu_header_right"> <li class="list_item_menu_header_right">
<%= link_to 'Lịch sử', '#', class: 'link_item_menu_header_right' %> <%= link_to 'History', '#', class: 'link_item_menu_header_right' %>
</li> </li>
<% if user_signed_in? %>
<li class="list_item_menu_header_right"> <li class="list_item_menu_header_right">
<%= link_to 'Đăng nhập', '#', class: 'link_item_menu_header_right' %> <%= link_to 'My page', my_page_path, class: 'link_item_menu_header_right' %>
</li> </li>
<li class="list_item_menu_header_right"> <li class="list_item_menu_header_right">
<%= link_to 'Đăng ký', '#', class: 'link_item_menu_header_right' %> <%= link_to 'Log out', destroy_user_session_path, method: :delete, class: 'link_item_menu_header_right' %>
</li> </li>
<% else %>
<li class="list_item_menu_header_right">
<%= link_to 'Sign in', new_user_session_path, class: 'link_item_menu_header_right' %>
</li>
<li class="list_item_menu_header_right">
<%= link_to 'Register', new_user_registration_path, class: 'link_item_menu_header_right' %>
</li>
<% end %>
</ul> </ul>
</div> </div>
<%# mobile %> <%# mobile %>
...@@ -30,17 +39,27 @@ ...@@ -30,17 +39,27 @@
<div class = "menu_mobile_vertical"> <div class = "menu_mobile_vertical">
<ul class = "list_menu_mobile_vertical"> <ul class = "list_menu_mobile_vertical">
<li class = "item_menu_mobile_vertical"> <li class = "item_menu_mobile_vertical">
<%= link_to 'Yêu thích', '#', class: 'link_item_menu_mobile' %> <%= link_to 'Favorite', '#', class: 'link_item_menu_mobile' %>
</li>
<li class = "item_menu_mobile_vertical">
<%= link_to 'History', '#', class: 'link_item_menu_mobile' %>
</li>
<% if user_signed_in? %>
<li class = "item_menu_mobile_vertical">
<%= link_to 'My page', my_page_path, class: 'link_item_menu_mobile' %>
</li> </li>
<li class = "item_menu_mobile_vertical"> <li class = "item_menu_mobile_vertical">
<%= link_to 'Lịch sử', '#', class: 'link_item_menu_mobile' %> <%= link_to 'Log out', destroy_user_session_path, method: :delete, class: 'link_item_menu_mobile' %>
</li> </li>
<% else %>
<li class = "item_menu_mobile_vertical"> <li class = "item_menu_mobile_vertical">
<%= link_to 'Đăng nhập', '#', class: 'link_item_menu_mobile' %> <%= link_to 'Sign in', new_user_session_path, class: 'link_item_menu_mobile' %>
</li> </li>
<li class = "item_menu_mobile_vertical"> <li class = "item_menu_mobile_vertical">
<%= link_to 'Đăng ký', '#', class: 'link_item_menu_mobile' %> <%= link_to 'Register', new_user_registration_path, class: 'link_item_menu_mobile' %>
</li> </li>
<% end %>
</ul> </ul>
</div> </div>
<div> <div>
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
<body> <body>
<div class = "app"> <div class = "app">
<%= render 'layouts/header' %> <%= render 'layouts/header' %>
<%= custom_bootstrap_flash %>
<% flash.discard %>
<%= render 'layouts/padding' %> <%= render 'layouts/padding' %>
<%= yield %> <%= yield %>
<%= render 'layouts/footer' %> <%= render 'layouts/footer' %>
......
<div class="container">
<div class="box_form">
<h3>VENJOB</h3>
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 emai again.</p>
<div class="list_email_app">
<ul>
<li>
<span>You're using: </span>
</li>
<li>
<a href="https://mail.google.com/" target="_blank"><i class="fas fa-inbox"></i> Gmail</a>
</li>
<li>
<a href="https://login.yahoo.com/" target="_blank"><i class="fab fa-yahoo"></i> Yahoo</a>
</li>
<li>
<a href="https://outlook.live.com/" target="_blank"><i class="fab fa-windows"></i> Outlook</a>
</li>
</ul>
</div>
</div>
</div>
<div style="width: 100%; height: 15vh;"></div>
<div class="container">
<div class="box_my_page">
<div class="row">
<div class="col-lg-12">
<div class="box_my_page_header">
<h4>My page</h4>
</div>
</div>
<div class="box_my_page_content">
<hr>
<div class="row">
<div class="col-lg-12">
<span>Email: <%= user_signed_in? ? current_user.name : 'N/A'%> </span>
</div>
<div class="col-lg-12">
<span>Name: <%= user_signed_in? ? current_user.email : 'N/A'%></span>
</div>
<div class="col-lg-12">
CV: <%= link_to user_signed_in? ? current_user.cv.identifier : 'N/A', current_user.cv.url, target: '_blank', class: 'link_ct pdf' %>
<%= link_to '<i class="fas fa-download"></i>'.html_safe, current_user.cv.url, download: current_user.cv.identifier %>
</div>
<hr>
<div class="col-lg-6">
<%= link_to 'Update', edit_user_registration_path, class: 'btn btn-info btn-custom-padding'%>
<%= link_to 'My jobs', '#', class: 'btn btn-warning btn-custom-padding'%>
</div>
</div>
<hr>
</div>
</div>
</div>
</div>
<div style="width: 100%; height: 55vh"></div>
Rails.application.configure do Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
config.action_mailer.default_url_options = { host: 'localhost', port: 1234 }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default :charset => "utf-8"
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
user_name: 'haint5055@gmail.com',
password: 'Hai123456',
domain: 'gmail.com',
address: 'smtp.gmail.com',
port: '587',
authentication: :plain,
enable_starttls_auto: true,
ssl: true
}
# In the development environment your application's code is reloaded on # In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development # every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes. # since you don't have to restart the web server when you make code changes.
......
# 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:"
# frozen_string_literal: true # frozen_string_literal: true
Rails.application.routes.draw do Rails.application.routes.draw do
devise_for :users, controllers: {registrations: 'registrations'}
root 'home#index' root 'home#index'
get 'register/:code', to: 'users#confirm_sign_up', as: :confirm_sign_up
get 'industries', to: 'industry#index', as: :industry_index get 'industries', to: 'industry#index', as: :industry_index
get 'cities', to: 'city#index', as: :city_index get 'cities', to: 'city#index', as: :city_index
# Details job # Details job
get 'detail/:id', to: 'job#detail', as: :detail_job get 'detail/:id', to: 'job#detail', as: :detail_job
# Search # Search
get 'jobs/:model/:slug', to: 'job#index', as: :jobs get 'jobs/:model/:slug', to: 'job#index', as: :jobs
# My page
get 'my', to: 'users#my_page', as: :my_page
# Rails error # Rails error
match '/404', to: 'error#page_not_found', via: :all match '/404', to: 'error#page_not_found', via: :all
match '/500', to: 'error#internal_server_error', via: :all match '/500', to: 'error#internal_server_error', via: :all
......
# frozen_string_literal: true
class AddDeviseToUsers < ActiveRecord::Migration[5.2]
remove_column :users, :password_digest, :string
def self.up
change_table :users do |t|
## Database authenticatable
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
...@@ -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: 2020_08_05_075807) do ActiveRecord::Schema.define(version: 2020_08_11_005812) do
create_table "applied_jobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t| create_table "applied_jobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
t.bigint "user_id" t.bigint "user_id"
...@@ -96,11 +96,20 @@ ActiveRecord::Schema.define(version: 2020_08_05_075807) do ...@@ -96,11 +96,20 @@ ActiveRecord::Schema.define(version: 2020_08_05_075807) do
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t| create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
t.string "email" t.string "email"
t.string "name" t.string "name"
t.string "password_digest"
t.text "cv" t.text "cv"
t.boolean "admin", default: false t.boolean "admin", default: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", 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 ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end end
end end
require 'test_helper'
class RegistrationsControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class RegistrationDecoratorTest < Draper::TestCase
end
require 'test_helper'
class UserDecoratorTest < Draper::TestCase
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