Commit a88e34a0 by Tô Ngọc Ánh

Merge branch 'ID5-6-7' into 'master'

Create Job apply ID5-ID6-ID7

See merge request !12
parents b391b698 f4fb834b
Pipeline #971 failed with stages
in 0 seconds
...@@ -28,3 +28,5 @@ ...@@ -28,3 +28,5 @@
/lib/data /lib/data
/public/uploads/ /public/uploads/
.env
$(document).on('turbolinks:load', function() {
$('#applied_job_curriculum_vitae').change(function(){
$('#name_curriculum_vitae').text(this.files[0].name);
})
})
...@@ -13,58 +13,3 @@ ...@@ -13,58 +13,3 @@
*= require_tree . *= require_tree .
*= require_self *= require_self
*/ */
$main-color: #1da173;
.min-vh-80 {
min-height: 80vh;
}
.navbar {
background-color: $main-color;
}
.footer {
width: 100%;
height: 50px;
background-color: $main-color;
}
.divider {
width: 100px;
border-top: 2px solid $main-color
}
.vertical-divider {
position: relative;
padding: 0px 4px;
&::after {
content: '';
width: 1px;
height: 18px;
margin-left: 3px;
position: absolute;
border-right: 1px solid black;
top: 50%;
right: -3px;
transform: translateY(-50%);
}
&:last-child {
&::after {
display: none;
}
}
}
.card-img-rounded {
border-radius: 50%;
background-color: mediumseagreen;
height: 220px; width: 220px;
margin-top: 50px;
padding: 10px;
.card-img {
margin-top: 40px;
}
}
.fa-file-upload {
cursor: pointer;
}
\ No newline at end of file
.ribbon::before, .ribbon::after {
content: "";
position: absolute;
height: 41px;
top: 0;
}
.ribbon::before {
left: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-right: 20px solid transparent;
border-left: 20px solid white;
}
.ribbon::after {
right: 0;
border-top: 20px solid white;
border-bottom: 20px solid white;
border-right: 20px solid white;
border-left: 20px solid transparent;
}
\ No newline at end of file
@import 'base';
.min-vh-80 {
min-height: 80vh;
}
.navbar {
background-color: $main-color;
}
.footer {
width: 100%;
height: 50px;
background-color: $main-color;
}
.divider {
width: 100px;
border-top: 2px solid $main-color
}
.vertical-divider {
position: relative;
padding: 0px 4px;
&::after {
content: '';
width: 1px;
height: 18px;
margin-left: 3px;
position: absolute;
border-right: 1px solid black;
top: 50%;
right: -3px;
transform: translateY(-50%);
}
&:last-child {
&::after {
display: none;
}
}
}
@import 'base';
.card-img-rounded {
border-radius: 50%;
background-color: $main-color;
height: 220px; width: 220px;
margin-top: 50px;
padding: 10px;
.card-img {
margin-top: 40px;
}
}
class AppliedJobsController < ApplicationController
before_action :authenticate_user!
def new
job = Job.find_by(id: params[:job_id])
return redirect_to root_path if job.nil?
@applied_job = job.applied_jobs.new(full_name: current_user.full_name,
email: current_user.email)
end
def confirm
@applied_job = current_user.applied_jobs.new(applied_job_params)
@applied_job.curriculum_vitae = current_user.curriculum_vitae if params[:applied_job][:curriculum_vitae].nil?
return if @applied_job.valid?
flash.now[:error] = @applied_job.errors.full_messages
render :new
end
def finish
@applied_job = current_user.applied_jobs.new(applied_job_params)
@applied_job.curriculum_vitae.retrieve_from_cache!(params[:applied_job][:curriculum_vitae])
if @applied_job.save
AppliedJobMailer.with(applied_job: @applied_job).success_email.deliver_later
else
flash[:error] = @applied_job.errors.full_messages
render :new
end
end
private
def applied_job_params
params.require(:applied_job).permit(:job_id, :full_name, :email, :curriculum_vitae)
end
end
...@@ -9,5 +9,8 @@ class JobsController < ApplicationController ...@@ -9,5 +9,8 @@ class JobsController < ApplicationController
def show def show
@job = Job.find_by(id: params[:id]) @job = Job.find_by(id: params[:id])
return redirect_to root_path if @job.nil?
@is_not_applied = user_signed_in? ? current_user.applied_jobs.find_by(job_id: @job.id).blank? : true
end end
end end
...@@ -26,6 +26,6 @@ class Users::SessionsController < Devise::SessionsController ...@@ -26,6 +26,6 @@ class Users::SessionsController < Devise::SessionsController
# end # end
def after_sign_in_path_for(resource) def after_sign_in_path_for(resource)
my_page_path #your path stored_location_for(resource) || my_page_path
end end
end end
...@@ -5,6 +5,5 @@ class UsersController < ApplicationController ...@@ -5,6 +5,5 @@ class UsersController < ApplicationController
@user = User.find_by(id: params[:id]) @user = User.find_by(id: params[:id])
end end
def my_page def my_page; end
end
end end
module ApplicationHelper module ApplicationHelper
def is_current_user?(user) def current_user?(user)
current_user == user current_user == user
end end
end end
class ApplicationMailer < ActionMailer::Base class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com' default from: 'no-reply@venjob.com'
layout 'mailer' layout 'mailer'
end end
class AppliedJobMailer < ApplicationMailer
def success_email
@applied_job = params[:applied_job]
mail(to: @applied_job.email, subject: 'Thank you for apply with VenJOB', bcc: ENV['GMAIL_USERNAME'])
end
end
class AppliedJob < ApplicationRecord class AppliedJob < ApplicationRecord
EMAIL_REGEXN = /\A[a-zA-Z][\w\.]+@[a-zA-Z]+\.[a-zA-Z]+\z/
belongs_to :user belongs_to :user
belongs_to :job belongs_to :job
validates :full_name, presence: true, length: { maximum: 200 }
validates :email, presence: true, format: { with: EMAIL_REGEXN }
validates :curriculum_vitae, presence: true
validates :user, uniqueness: { scope: :job, message: 'has already applied this job' }
mount_uploader :curriculum_vitae, CurriculumVitaeUploader
end end
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<p>Dear <%= @applied_job.full_name %></p>
<br>
<p>Thank you for applied with VenJOB. Your applied job's information is as follow:</p>
<br>
<p>Job title: <%= @applied_job.job.title %></p>
<p>Location: <%= @applied_job.job.locations.first.city %></p>
<p>Company: <%= @applied_job.job.company.name %></p>
<br>
<p>Your submitted information:</p>
<p>Full Name: <%= @applied_job.full_name %></p>
<p>Email: <%= @applied_job.email %></p>
<p>CV Link: <%= link_to @applied_job.curriculum_vitae.identifier, URI.join(root_url, @applied_job.curriculum_vitae.url).to_s, target: '_blank' %></p>
<br>
<p>Thanks and best regards.</p>
</body>
</html>
<div class="alert alert-danger">
<a href="#" data-dismiss="alert" class="close">×</a>
<% flash[:error]&.each do |error| %>
<p class="mb-0"><%= error %></p>
<% end %>
</div>
<div class="col bg-success text-center ribbon">
<h5 class="text-white my-2">
<span class="badge badge-light"><%= stt %></span>
<%= name %>
</h5>
</div>
<div class="col bg-light text-center">
<h5 class="text-muted my-2">
<span class="badge badge-success"><%= stt %></span>
<%= name %>
</h5>
</div>
<div class="row my-5 border-top border-bottom">
<%= render partial: 'applied_jobs/ribbon_disable', locals: { stt: 1, name: 'New' } %>
<%= render partial: 'applied_jobs/ribbon_active', locals: { stt: 2, name: 'Confirm' } %>
<%= render partial: 'applied_jobs/ribbon_disable', locals: { stt: 3, name: 'Finish' } %>
</div>
<div class="row">
<div class="col-md-7 col-sm-12 mx-auto">
<%= form_for @applied_job, url: finish_applied_job_path do |f| %>
<%= f.hidden_field :job_id %>
<div class="row">
<div class="col-4">
<%= f.label :job_name, class: 'h5 font-weight-bold' %>
</div>
<div class="col-8">
<h5><%= @applied_job.job.title %></h5>
</div>
</div>
<div class="row">
<div class="col-4">
<%= f.label :full_name, class: 'h5 font-weight-bold' %>
</div>
<div class="col-8">
<h5><%= @applied_job.full_name %></h5>
<%= f.hidden_field :full_name %>
</div>
</div>
<div class="row">
<div class="col-4">
<%= f.label :email, class: 'h5 font-weight-bold' %>
</div>
<div class="col-8">
<h5><%= @applied_job.email %></h5>
<%= f.hidden_field :email %>
</div>
</div>
<div class="row">
<div class="col-4">
<h5 class="font-weight-bold">Curriculum Vitae</h5>
</div>
<div class="col-8">
<%= link_to @applied_job.curriculum_vitae.identifier, @applied_job.curriculum_vitae.url, download: @applied_job.curriculum_vitae.identifier %>
<%= link_to @applied_job.curriculum_vitae.url, target: '_blank', class: 'text-dark' do %>
<i class="fas fa-eye"></i>
<% end %>
<%= f.hidden_field :curriculum_vitae, class: 'd-none', value: @applied_job.curriculum_vitae.cache_name %>
</div>
</div>
<div class="row my-3">
<div class="col text-right">
<%= link_to 'Edit', new_applied_job_path(job_id: @applied_job.job_id), class: 'btn btn-outline-secondary btn-lg' %>
</div>
<div class="col text-left">
<%= f.submit "Done", class: 'btn btn-outline-success btn-lg' %>
</div>
<% end %>
</div>
</div>
</div>
<div class="row my-5 border-top border-bottom">
<%= render partial: 'applied_jobs/ribbon_disable', locals: { stt: 1, name: 'New' } %>
<%= render partial: 'applied_jobs/ribbon_disable', locals: { stt: 2, name: 'Confirm' } %>
<%= render partial: 'applied_jobs/ribbon_active', locals: { stt: 3, name: 'Finish' } %>
</div>
<h3 class="text-center mt-5">Thank you for apply.</h3>
<br>
<h3 class="text-center">Back to <%= link_to 'TOP', root_path, class: 'text-danger text-decorate-none' %> page.</a></h3>
<div class="row my-5 border-top border-bottom">
<%= render partial: 'applied_jobs/ribbon_active', locals: { stt: 1, name: 'New' } %>
<%= render partial: 'applied_jobs/ribbon_disable', locals: { stt: 2, name: 'Confirm' } %>
<%= render partial: 'applied_jobs/ribbon_disable', locals: { stt: 3, name: 'Finish' } %>
</div>
<%= render 'applied_jobs/errors' if flash[:error].present? %>
<div class="apply-form mx-5 px-5">
<%= form_for @applied_job, url: confirm_applied_job_path do |f| %>
<%= f.hidden_field :job_id %>
<h4 class="text-center text-success font-weight-bold mb-4"><%= @applied_job.job.title %></h4>
<div class="d-flex justify-content-between my-3">
<%= f.label :full_name, class: 'mt-2 font-weight-bold' %>
<%= f.text_field :full_name, class: 'form-control w-75', maxlength: 200, required: true %>
</div>
<div class="d-flex justify-content-between my-3">
<%= f.label :email, class: 'mt-2 font-weight-bold' %>
<%= f.text_field :email, class: 'form-control w-75', required: true %>
</div>
<div class="d-flex justify-content-between my-3">
<span class="mt-2 font-weight-bold">Curriculum Vitae</span>
<div class="w-75 mt-2">
<span id="name_curriculum_vitae" class="mr-2">
<%= link_to_if current_user.curriculum_vitae.present?, current_user.curriculum_vitae.identifier, current_user.curriculum_vitae.url, target: '_blank' do %>
<span>Please upload your CV!</span>
<% end %>
</span>
<%= f.label :curriculum_vitae do %>
<i class="fas fa-file-upload fa-lg"></i>
<% end %>
<%= f.file_field :curriculum_vitae, accept: '.doc, .pdf, .xls, .xlsx, .zip', class: 'd-none' %>
</div>
</div>
<div class="actions text-center my-3">
<%= f.submit "Confirm", class: 'btn btn-outline-success btn-lg' %>
</div>
<% end %>
</div>
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
<div class="form-group"> <div class="form-group">
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %> <%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
<% if @minimum_password_length %>
<% end %>
</div> </div>
<div class="form-group"> <div class="form-group">
......
<% if resource.errors.any? %> <% if resource.errors.any? %>
<div id="error_explanation"> <div id="error_explanation">
<h2>
<%= I18n.t("errors.messages.not_saved",
count: resource.errors.count,
resource: resource.class.model_name.human.downcase)
%>
</h2>
<ul> <ul>
<% resource.errors.full_messages.each do |message| %> <% resource.errors.full_messages.each do |message| %>
<li><%= message %></li> <li><%= message %></li>
......
...@@ -31,12 +31,16 @@ ...@@ -31,12 +31,16 @@
<%= @job.description.html_safe %> <%= @job.description.html_safe %>
</div> </div>
<div class="col-2"> <div class="col-2">
<%= link_to 'Apply Now', '#', class: 'btn btn-outline-success btn-lg' %> <%= link_to_if @is_not_applied, 'Apply Now', new_applied_job_path(job_id: @job.id), class: 'btn btn-outline-success btn-lg' do %>
<%= link_to 'Apply Now', '#', class: 'btn btn-outline-secondary btn-lg disabled' %>
<% end %>
</div> </div>
</div> </div>
<div class="row my-3"> <div class="row my-3">
<div class="col-6 text-right"> <div class="col-6 text-right">
<%= link_to 'Apply Now', '#', class: 'btn btn-outline-success btn-lg' %> <%= link_to_if @is_not_applied, 'Apply Now', new_applied_job_path(job_id: @job.id), class: 'btn btn-outline-success btn-lg' do %>
<%= link_to 'Apply Now', '#', class: 'btn btn-outline-secondary btn-lg disabled' %>
<% end %>
</div> </div>
<div class="col-6 text-left"> <div class="col-6 text-left">
<%= link_to 'Favorite', '#', class: 'btn btn-outline-danger btn-lg' %> <%= link_to 'Favorite', '#', class: 'btn btn-outline-danger btn-lg' %>
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
You don't have any curriculum vitae You don't have any curriculum vitae
<% end %> <% end %>
</p> </p>
<% if is_current_user?(user) %> <% if current_user?(user) %>
<div class="row"> <div class="row">
<div class="col text-right"> <div class="col text-right">
<%= link_to 'Update', edit_user_registration_path, class: 'btn btn-outline-primary btn-lg' %> <%= link_to 'Update', edit_user_registration_path, class: 'btn btn-outline-primary btn-lg' %>
......
# Load the Rails application. # Load the Rails application.
require_relative 'application' require_relative 'application'
# disable auto add field_with_errors class
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
html_tag.html_safe
end
# Initialize the Rails application. # Initialize the Rails application.
Rails.application.initialize! Rails.application.initialize!
...@@ -139,7 +139,7 @@ Devise.setup do |config| ...@@ -139,7 +139,7 @@ Devise.setup do |config|
# their account can't be confirmed with the token any more. # their account can't be confirmed with the token any more.
# Default is nil, meaning there is no restriction on how long a user can take # Default is nil, meaning there is no restriction on how long a user can take
# before confirming their account. # before confirming their account.
# config.confirm_within = 3.days config.confirm_within = 1.days
# If true, requires any email changes to be confirmed (exactly the same way as # If true, requires any email changes to be confirmed (exactly the same way as
# initial account confirmation) to be applied. Requires additional unconfirmed_email # initial account confirmation) to be applied. Requires additional unconfirmed_email
...@@ -171,7 +171,7 @@ Devise.setup do |config| ...@@ -171,7 +171,7 @@ Devise.setup do |config|
# Email regex used to validate email formats. It simply asserts that # Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly # one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity. # to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ config.email_regexp = /\A[a-zA-Z][\w\.]+@[a-zA-Z]+\.[a-zA-Z]+\z/
# ==> Configuration for :timeoutable # ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this # The time you want to timeout the user session without activity. After this
......
Rails.application.routes.draw do Rails.application.routes.draw do
devise_for :users, controllers: { sessions: 'users/sessions', registrations: 'users/registrations' }
root to: 'home#index' root to: 'home#index'
devise_for :users, controllers: { sessions: 'users/sessions', registrations: 'users/registrations' }
get 'cities', to: 'locations#index' get 'cities', to: 'locations#index'
get 'industries', to: 'industries#index' get 'industries', to: 'industries#index'
get 'detail/:id', to: 'jobs#show', as: :job get 'detail/:id', to: 'jobs#show', as: :job
get 'jobs/:model/:slug', to: 'jobs#index', as: :jobs get 'jobs/:model/:slug', to: 'jobs#index', as: :jobs
get 'my', to: 'users#my_page', as: :my_page get 'my', to: 'users#my_page', as: :my_page
resources :users, only: :show resources :users, only: :show
get 'apply', to: 'applied_jobs#new', as: :new_applied_job
post 'confirm', to: 'applied_jobs#confirm', as: :confirm_applied_job
post 'finish', to: 'applied_jobs#finish', as: :finish_applied_job
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end end
require 'test_helper'
class HomeControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get home_index_url
assert_response :success
end
end
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
user: one
job: one
full_name: MyString
email: MyString
curriculum_vitae: MyString
two:
user: two
job: two
full_name: MyString
email: MyString
curriculum_vitae: MyString
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
name: MyString
description: MyText
address: MyString
two:
name: MyString
description: MyText
address: MyString
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
user: one
job: one
two:
user: two
job: two
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
user: one
job: one
two:
user: two
job: two
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
name: MyString
two:
name: MyString
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
company: one
title: MyString
level: MyString
salary: MyString
experience: MyString
description: MyText
expiration_date: 2020-07-15
two:
company: two
title: MyString
level: MyString
salary: MyString
experience: MyString
description: MyText
expiration_date: 2020-07-15
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
area: MyString
city: MyString
two:
area: MyString
city: MyString
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
job: one
location: one
two:
job: two
location: two
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
email: MyString
full_name: MyString
curriculum_vitae: MyString
password: MyString
admin: false
two:
email: MyString
full_name: MyString
curriculum_vitae: MyString
password: MyString
admin: false
require 'test_helper'
class AppliedJobTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class CompanyTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class FavoriteTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class HistoryTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class IndustryTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class JobTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class LocationTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class LocationsJobTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
require 'test_helper'
class UserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment