Commit eabd44f2 by Mai Hoang Thai Ha

done

parent 0cb127f4
@import "bootstrap";
@import "font-awesome";
@import "./scss/style.scss";
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*=
*= require top
*= require_self
*= require_tree .
*= require font-awesome
*/
\ No newline at end of file
.ribbon {
text-align: center;
ul {
list-style: none;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: 0;
padding: 0;
.item {
display: block;
float: left;
width: 30%;
height: 100%;
background: #f3f5fa;
text-align: center;
padding: 8px;
position: relative;
font-size: 16px;
font-weight: 700;
text-decoration: none;
color: #808080;
.circle {
display: inline-flex;
-ms-flex-line-pack: center;
align-content: center;
-ms-flex-pack: center;
justify-content: center;
width: 1.6em;
height: 1.6em;
padding: 0.25em 0;
margin-right: 10px;
line-height: 1em;
font-size: 1em;
color: #808080;
background-color: #e0e0e0;
border-radius: 50%;
}
&:after {
content: "";
border-top: 21px solid transparent;
border-bottom: 21px solid transparent;
border-left: 21px solid #f3f5fa;
position: absolute;
right: -21px;
top: 0;
z-index: 1;
}
&:last-child::after {
display: none;
}
&:first-child::before {
content: "";
border-top: 21px solid transparent;
border-bottom: 21px solid transparent;
border-left: 21px solid #fff;
position: absolute;
left: 0;
top: 0;
}
}
.active {
background: #0d6efd;
color: #fff;
.circle {
color: #0d6efd;
background-color: #fff;
}
&::after {
border-left-color: #0d6efd;
color: #fff;
}
}
}
}
.btn-height {
height: 50px;
}
.invalid {
.form-msg {
color: #f33a58;
}
input {
border-color: #f33a58;
}
}
.logout {
border: none;
padding: 0;
background-color: white;
outline: none;
}
.flash {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 1000;
background-color: white;
}
.hide {
display: none;
}
.alert-notice {
color: #155724;
background-color: #d4edda;
border-color: #c3e6cb;
}
\ No newline at end of file
// Place all the styles related to the Favorite_industries controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/
......@@ -42,7 +42,7 @@ select {
border: 1px solid #EDEDED;
border-radius: 0;
background-color: $white;
background-image: url("../images/icons/arrow-down.png");
// background-image: url("../images/icons/arrow-down.png");
background-repeat: no-repeat;
background-position: right center;
background-size: auto;
......
......@@ -113,8 +113,11 @@
@media #{$small-mobile} {
& .salary-type {
margin-left: auto;
& .badge {
font-size: 10px;
}
}
& .content {
flex: 1 0 100%;
......
class FavoriteIndustriesController < ApplicationController
before_action :authenticate_user!
before_action :load_industry, only: %i[create destroy]
def index
@favorite_industries = current_user.favorite_industries
.page(params[:page]).per(Job::JOB_PER_PAGE)
end
def create
@favorite_industry = current_user.favorite_industries.build(industry_id: @industry.id)
respond_to :js if @favorite_industry.save
end
def destroy
@favorite_industry = current_user.favorite_industries.find_by(industry_id: @industry.id)
respond_to :js if @favorite_industry.destroy
end
private
def load_industry
@industry = Industry.find_by(id: params[:industry_id])
end
end
class FavoriteJobsController < ApplicationController
# before_action :logged_in_user
before_action :authenticate_user!
before_action :load_job, only: %i[create destroy]
......
class JobsController < ApplicationController
before_action :history, only: :show
before_action :views_count, only: :show
before_action :salary_search
before_action :city_industry_list
before_action :name, only: :index
......@@ -31,6 +32,13 @@ class JobsController < ApplicationController
history.update(updated_at: Time.current)
end
def views_count
job = Job.find_by(id: params[:id])
count = job.views_count + 1
job.update_attribute :views_count, count
end
def name
@name = if params[:city_slug]
City.find_by(slug: params[:city_slug]).name
......
class TopController < ApplicationController
before_action :salary_search
before_action :city_industry_list
before_action :solr
def index
solr = Solr.new(params)
@latest_jobs = get_jobs(solr.latest_jobs)
@total_job = solr.query_all['response']['numFound']
@top_cities = solr.facet_query('city_id')['vietnam'].take(City::TOP_JOB_COUNT)
@top_industries = solr.facet_query('industry_id').take(Industry::TOP_JOB_COUNT)
@latest_jobs = get_jobs(@solr.latest_jobs)
@attractive_jobs = Job.top_views
@total_job = @solr.query_all['response']['numFound']
@top_cities = @solr.facet_query('city_id')['vietnam'].take(City::TOP_JOB_COUNT)
@top_industries = @solr.facet_query('industry_id').take(Industry::TOP_JOB_COUNT)
@member = User.count
@resume = ApplyJob.count
@company = Company.count
suggest
end
private
def suggest
return unless user_signed_in? && current_user.industries?
industry_ids = current_user.fav_industries_ids
@suggest_jobs = get_jobs(@solr.suggest_jobs(industry_ids))
end
def solr
@solr = Solr.new(params)
end
def get_jobs(query)
jobs_ids = query['response']['docs'].map { |j| j['job_id'] }
Job.eager_load(:cities, :cities_jobs, :company).find(jobs_ids)
Job.includes(:cities, :cities_jobs, :company).find(jobs_ids)
end
end
module FavoriteIndustriesHelper
end
......@@ -8,7 +8,7 @@
$body = $('body'),
$mainWrapper = $('.main-wrapper'),
$headerHeight = $('.header').outerHeight();
/*______________________________ */
/*--
Custom script to call Background
Image form html data attribute
......@@ -38,6 +38,8 @@
}
});
/*--
Mobile OffCanvas Open & Close
-----------------------------------*/
......@@ -89,19 +91,6 @@
mobileOffCanvasMenu();
/*--
Login & Register Modal
-----------------------------------*/
$('#loginSignupModal').on('show.bs.modal', function (event) {
var $button = $(event.relatedTarget),
$target = $button.data('target-sub'),
$modal = $(this);
$modal.find('.nav-link').removeClass('active');
$modal.find('.nav-link[href="' + $target + '"]').addClass('active');
$modal.find('.tab-pane').removeClass('active show');
$modal.find('.tab-pane' + $target).addClass('active show');
})
/*--
Slick Slider Activation
-----------------------------------*/
......@@ -241,97 +230,9 @@
});
/*--
MailChimp
-----------------------------------*/
$('#mc-form').ajaxChimp({
language: 'en',
callback: mailChimpResponse,
// ADD YOUR MAILCHIMP URL BELOW HERE!
url: 'http://devitems.us11.list-manage.com/subscribe/post?u=6bbb9b6f5827bd842d9640c82&amp;id=05d85f18ef'
});
function mailChimpResponse(resp) {
if (resp.result === 'success') {
$('.mailchimp-success').html('' + resp.msg).fadeIn(900);
$('.mailchimp-error').fadeOut(400);
} else if (resp.result === 'error') {
$('.mailchimp-error').html('' + resp.msg).fadeIn(900);
}
}
/*--
Scroll Up
-----------------------------------*/
$.scrollUp({
scrollText: '<i class="fa fa-long-arrow-up"></i>',
});
/*--
Google Map
-----------------------------------*/
$('.google-map').each(function () {
if ($(this).length) {
var $this = $(this),
$id = $this.attr('id'),
$zoom = $this.data('zoom') ? $this.data('zoom') : 12,
$lat = $this.data('lat') ? $this.data('lat') : 21.0277214,
$long = $this.data('long') ? $this.data('long') : 105.8342015;
function initialize() {
var mapOptions = {
zoom: $zoom,
scrollwheel: false,
center: new google.maps.LatLng($lat, $long),
};
var map = new google.maps.Map(document.getElementById($id), mapOptions);
var marker = new google.maps.Marker({
position: map.getCenter(),
map: map
//animation: google.maps.Animation.BOUNCE
});
}
google.maps.event.addDomListener(window, 'load', initialize);
}
});
/*--
Ajax Contact Form
-----------------------------------*/
$(function () {
// Get the form.
var form = $('#contact-form');
// Get the messages div.
var formMessages = $('.form-messege');
// Set up an event listener for the contact form.
$(form).submit(function (e) {
// Stop the browser from submitting the form.
e.preventDefault();
// Serialize the form data.
var formData = $(form).serialize();
// Submit the form using AJAX.
$.ajax({
type: 'POST',
url: $(form).attr('action'),
data: formData
})
.done(function (response) {
// Make sure that the formMessages div has the 'success' class.
formMessages.removeClass('error text-danger').addClass('success text-success mt-3').text(response);
// Clear the form.
form.find('input:not([type="submit"]), textarea').val('');
})
.fail(function (data) {
// Make sure that the formMessages div has the 'error' class.
formMessages.removeClass('success text-success').addClass('error text-danger mt-3');
// Set the message text.
if (data.responseText !== '') {
formMessages.text(data.responseText);
} else {
formMessages.text('Oops! An error occured and your message could not be sent.');
}
});
});
});
})(jQuery);
\ No newline at end of file
})(jQuery);
class FavoriteIndustry < ApplicationRecord
belongs_to :user
belongs_to :industry
end
......@@ -2,6 +2,8 @@ class Industry < ApplicationRecord
TOP_JOB_COUNT = 9
has_and_belongs_to_many :jobs
has_many :favorite_industries, dependent: :destroy
validates :slug, presence: true, uniqueness: { case_sensitive: true }
......
......@@ -14,4 +14,8 @@ class Job < ApplicationRecord
has_many :apply_jobs, dependent: :destroy
has_many :favorite_jobs, dependent: :destroy
has_many :history_jobs, dependent: :destroy
def self.top_views
includes(:cities, :cities_jobs, :company).order(views_count: :desc).limit(LATEST_JOBS_LIMIT)
end
end
......@@ -7,6 +7,7 @@ class User < ApplicationRecord
has_many :apply_jobs, dependent: :destroy
has_many :favorite_jobs, dependent: :destroy
has_many :history_jobs, dependent: :destroy
has_many :favorite_industries, dependent: :destroy
has_one_attached :cv
......@@ -32,6 +33,20 @@ class User < ApplicationRecord
@favorite_job_ids.include?(job.id)
end
def fav_industry?(industry_slug)
industry_id = Industry.find_by(slug: industry_slug).id
@fav_industry_ids ||= Set.new(favorite_industries.pluck(:industry_id))
@fav_industry_ids.include?(industry_id)
end
def industries?
favorite_industries.exists?
end
def fav_industries_ids
favorite_industries.pluck(:industry_id)
end
protected
def password_required?
......
......@@ -87,6 +87,16 @@ class Solr
send_request(q, fq)
end
def suggest_jobs(arr)
parsed = arr.join(' ')
q = "industry_id: #{parsed}"
fq = []
facet = nil
sort = 'max_salary desc'
rows = 5
send_request(q, fq, facet, sort, rows)
end
def facet_query(facet_param)
q = '*:*'
fq = '*:*'
......
......@@ -2,88 +2,87 @@
- provide(:title, 'All applies jobs')
/ search
.container.mt-5
= form_with(url: admin_applies_jobs_path, method: :get, local: true) do |f|
.row.mb-2.form-group
.col-2
= f.label :email, 'Email', class: 'form-label'
.col-10
= f.text_field :email, class: 'form-control', value: params[:email]
.row.mb-2.form-group
.col-2
= f.label :city, 'City'
.col-10
= f.select :city, options_for_select(@city, selected: params[:city]), { include_blank: 'Select city' }, { class: 'form-select' }
.row.mb-2.form-group
.col-2
= f.label :industry, 'Industry', class: 'form-label'
.col-10
= f.select :industry, options_for_select(@industry, selected: params[:industry]), { include_blank: 'Select industry' }, { class:'form-select' }
= render 'shared/page_heading', title: 'Applies jobs', url: history_jobs_path
.section.section-padding
.container
= form_with(url: admin_applies_jobs_path, method: :get, local: true) do |f|
.row.mb-2.form-group
.col-2
= f.label :email, 'Email', class: 'form-label'
.col-10
= f.text_field :email, class: 'form-control', value: params[:email]
.row.mb-2.form-group
.col-2
= f.label :city, 'City'
.col-10
= f.select :city, options_for_select(@city, selected: params[:city]), { include_blank: 'Select city' }, { class: 'form-select' }
.row.mb-2.form-group
.col-2
= f.label :industry, 'Industry', class: 'form-label'
.col-10
= f.select :industry, options_for_select(@industry, selected: params[:industry]), { include_blank: 'Select industry' }, { class:'form-select' }
.row.mb-2.form-group
.col-6
.row
.col-4
= f.label :date_start, 'From', class: 'form-label'
.col-8
= f.date_field :date_start, value: params[:date_start], class: 'form-control'
.col-6
.row
.col-4
= f.label :date_end, 'To', class: 'form-label'
.col-8
= f.date_field :date_end, value: params[:date_end], class: 'form-control'
.row
.col-6.d-flex.justify-content-center
= f.submit 'Search', name: 'search', class: 'btn btn-primary w-50 my-4 btn-height',data: { disable_with: false }
.col-6.d-flex.justify-content-center
= f.submit 'Export', name: 'csv',class: 'btn btn-primary w-50 my-4 btn-height',data: { disable_with: false }
.row.mb-2.form-group
.col-6
.row
.col-4
= f.label :date_start, 'From', class: 'form-label'
.col-8
= f.date_field :date_start, value: params[:date_start], class: 'form-control'
.col-6
.row
.col-4
= f.label :date_end, 'To', class: 'form-label'
.col-8
= f.date_field :date_end, value: params[:date_end], class: 'form-control'
.row
.col-6.d-flex.justify-content-center
= f.submit 'Search', name: 'search', class: 'btn btn-primary w-50 my-4 btn-height',data: { disable_with: false }
.col-6.d-flex.justify-content-center
= f.submit 'Export', name: 'csv',class: 'btn btn-primary w-50 my-4 btn-height',data: { disable_with: false }
/result
.container
h2.my-5.text-center
| All applies jobs
.container
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @apply_jobs
.page-info.p-2
= paginate @apply_jobs
.container
- @apply_jobs.each do |apply|
.apply-item
.apply-job-title
= link_to apply.job.title, apply.job, class: 'job-title fs-3 text-decoration-none text-reset'
.apply-user-name.text-secondary
p.fw-bold
| Candidate Name:
span.fw-normal.text-dark
= apply.user_name
.apply-cv
p.fw-bold.text-secondary
| Candidate' CV:
- if apply.cv.attached?
span.fw-normal
= link_to apply.cv.filename, rails_blob_path(apply.cv, disposition: "attachment")
- else
/result
.container.mb-3
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @apply_jobs
.page-info.p-2
= paginate @apply_jobs
.container
- @apply_jobs.each do |apply|
.apply-item
.apply-job-title
= link_to apply.job.title, apply.job, class: 'job-title fs-3 text-decoration-none text-reset'
.apply-user-name.text-secondary
p.fw-bold
| Candidate Name:
span.fw-normal.text-dark
= 'cv file not uploaded yet'
.d-flex.align-items-center.justify-content-between
.apply-user-email
= apply.user_name
.apply-cv
p.fw-bold.text-secondary
| Candidate's Email:
span.fw-normal.text-dark
= apply.email
.apply-date
p.fst-italic.text-secondary
| Applied at:
span.fw-normal.text-dark
= apply.created_at.strftime('%d/%m/%Y')
hr
.container
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @apply_jobs
.page-info.p-2
= paginate @apply_jobs
| Candidate' CV:
- if apply.cv.attached?
span.fw-normal
= link_to apply.cv.filename, rails_blob_path(apply.cv, disposition: "attachment")
- else
span.fw-normal.text-dark
= 'cv file not uploaded yet'
.d-flex.align-items-center.justify-content-between
.apply-user-email
p.fw-bold.text-secondary
| Candidate's Email:
span.fw-normal.text-dark
= apply.email
.apply-date
p.fst-italic.text-secondary
| Applied at:
span.fw-normal.text-dark
= apply.created_at.strftime('%d/%m/%Y')
hr
.container
.no-padding.d-flex.align-items-center.flex-column
.page-info.p-2
= page_entries_info @apply_jobs
.page-info.p-2
= paginate @apply_jobs
......@@ -5,14 +5,14 @@
.section.section-padding
.container
h2
| Resend confirmation instructions
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
= render "admins/shared/error_messages", resource: resource
.field
= f.label :email
br
= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email)
.actions
= f.submit "Resend confirmation instructions"
= render "admins/shared/links"
\ No newline at end of file
.row.justify-content-center
.col-lg-6.col-sm-6.col-12.flex-grow-1.mb-4
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
= render "admins/shared/error_messages", resource: resource
.field
= f.label :email
br
= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email)
.actions
= f.submit "Resend confirmation instructions"
= render "admins/shared/links"
\ No newline at end of file
- provide(:title, 'Admin Forgot password')
= render 'shared/page_heading', title: 'Forgot password', url: login_path
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Forgot password
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
= render 'admins/shared/error_messages', resource: resource
.field.row.form-group.mb-4
.col-2
= f.label :email, class: 'form-label'
.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 }
.section.section-padding
.container
.row.justify-content-center
.col-lg-6.col-sm-6.col-12.flex-grow-1.mb-4
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
= render 'admins/shared/error_messages', resource: resource
.field.row.form-group.mb-4
.col-lg-2.col-sm-2.col-12
= f.label :email, class: 'form-label'
.col-lg-10.col-sm-10.col-12
= 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-lg-10.col-12.d-flex.justify-content-around
= f.submit 'Confirm your email', class: 'btn btn-primary w-100 my-4', data: { disable_with: false }
javascript:
Validator({
......
- provide(:title, 'Admin Sign Up')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= render 'users/shared/error_messages', resource: resource
.field.row.form-group.mb-4
.col-2
= f.label :email, class: 'form-label'
.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'
.col-10
= f.password_field :password, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-2
= f.label :password_confirmation, class: 'form-label'
.col-10
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.actions
= f.submit 'Sign up', class: 'btn btn-primary w-100 my-5'
= render 'shared/page_heading', title: 'Sign up', url: new_admin_registration_path
.section.section-padding
.container
.row.justify-content-center
.col-lg-6.col-sm-6.col-12.flex-grow-1.mb-4
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= render 'users/shared/error_messages', resource: resource
.field.row.form-group.mb-4
.col-lg-2.col-sm-2.col-12
= f.label :email, class: 'form-label'
.col-lg-10.col-sm-10.col-12
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-lg-2.col-sm-2.col-12
= f.label :password, class: 'form-label'
.col-lg-10.col-sm-10.col-12
= f.password_field :password, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-lg-2.col-sm-2.col-12
= f.label :password_confirmation, class: 'form-label'
.col-lg-10.col-sm-10.col-12
= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control mb-2'
span.form-msg
.actions.row.justify-content-end
.col-lg-10.col-12.d-flex.justify-content-around
= f.submit 'Sign up', class: 'btn btn-primary w-100 my-5'
javascript:
Validator({
......
- provide(:title, 'Log in')
.container
.row.justify-content-center
.col-6
h2.text-center.my-5
| Log in Admin
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
.field.row.form-group.mb-4
.col-2
= f.label :email, class: 'form-label'
.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'
.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 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'
= render 'shared/page_heading', title: 'Admin Login', url: admin_login_path
.section.section-padding
.container
.row.justify-content-center
.col-lg-6.col-sm-6.col-12.flex-grow-1.mb-4
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
.field.row.form-group.mb-4
.col-lg-2.col-sm-2.col-12
= f.label :email, class: 'form-label'
.col-lg-10.col-sm-10.col-12
= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control mb-2'
span.form-msg
.field.row.form-group.mb-4
.col-lg-2.col-sm-2.col-12
= f.label :password, class: 'form-label'
.col-lg-10.col-sm-10.col-12
= 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 mx-2'
.d-flex.justify-content-between
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
= link_to 'Forgot password?', new_admin_password_path, class: 'text-decoration-none'
= link_to 'Sign up', new_registration_path(resource_name), class: 'text-decoration-none'
.actions.row.justify-content-end
.col-lg-10.col-12.d-flex.justify-content-around
= f.submit 'Log in', class: 'btn btn-primary w-100', data: { disable_with: false }
javascript:
Validator({
......
- provide(:title, 'Confirmation')
.container
= render 'ribbon'
= render 'shared/page_heading', title: 'Confirm', url: '#'
.container
h1.my-5.text-center
| Confirmation
.col
= form_with(model: @apply, scope: :apply_job, url: done_job_path, local: true) do |f|
.row.mb-5
= f.hidden_field :job_id, value: @job.id
= f.hidden_field :blob_id, value: @blob.id
.col-2
= f.label :name, 'Full name:', class: 'form-label label'
.col-10
span.mx-2.label.form-control
= @apply.user_name
= f.hidden_field :user_name, value: @apply.user_name
.section
.container
= render 'ribbon'
.row.mb-5
.col-2
= f.label :email, 'Email:', class: 'form-label label'
.col-10
span.mx-2.label.form-control
= @apply.email
= f.hidden_field :email, value: @apply.email
.row.mb-5
.col-2
= f.label :cv, 'Cv: ', class: 'form-label label'
.col-10
span.form-control
= url_for(@apply.cv)
= link_to 'Edit', apply_job_path(job_id: @job.id), class: 'btn btn-secondary w-25 btn-height mr-5 my-5'
.container
.col
= form_with(model: @apply, scope: :apply_job, url: done_job_path, local: true) do |f|
.row.mb-5
= f.hidden_field :job_id, value: @job.id
= f.hidden_field :blob_id, value: @blob.id
.col-2
= f.label :name, 'Full name:', class: 'form-label label'
.col-10
span.mx-2.label.form-control
= @apply.user_name
= f.hidden_field :user_name, value: @apply.user_name
= f.submit 'Confirm', class: 'btn btn-primary w-25 btn-height mx-5 my-5'
.row.mb-5
.col-2
= f.label :email, 'Email:', class: 'form-label label'
.col-10
span.mx-2.label.form-control
= @apply.email
= f.hidden_field :email, value: @apply.email
.row.mb-5
.col-2
= f.label :cv, 'Cv: ', class: 'form-label label'
.col-10
span
= url_for(@apply.cv)
= link_to 'Edit', apply_job_path(job_id: @job.id), class: 'btn btn-secondary w-25 btn-height mr-5 my-5'
= f.submit 'Confirm', class: 'btn btn-primary w-25 btn-height mx-5 my-5'
- provide(:title, 'Done')
.container
=render 'ribbon'
= render 'shared/page_heading', title: 'Done', url: '#'
.container.text-center
h3.my-4
| You have successfully applied for the #{@job.title}
p Good luck! Thank you for using our service
= link_to 'Keep looking for another job', root_path
.section.py-5
.container
=render 'ribbon'
.container.text-center
h3.my-4
| You have successfully applied for the #{@job.title}
p Good luck! Thank you for using our service
= link_to 'Keep looking for another job', root_path
- provide(:title, 'Apply job')
.container
= render 'ribbon'
.container
h1.my-5.text-center
| Apply Form
p.fs-5.fw-bold.mb-4.text-center
= @job.title
.col
= form_with(model: @apply, url: confirm_job_path, local: true, id: 'apply-form') do |f|
= render 'shared/error_messages', object: f.object
.row.mb-5.form-group
= f.hidden_field :job_id, value: @job.id
.col-2
= f.label :user_name, 'Full name', class: 'form-label label'
.col-10
= f.text_field :user_name, value: @apply.user_name || @current_user.name, class: 'form-control'
span.form-msg
.row.mb-5.form-group
.col-2
= f.label :email, class: 'form-label label'
.col-10
= f.text_field :email, value: @apply.email || @current_user.email, class: 'form-control'
span.form-msg
.row.mb-5.form-group
/ - if @user.cv.attached?
/ = url_for(@user.cv)
.col-2
= f.label :cv, class: 'form-label label'
.col-10
= f.file_field :cv, files: @blob, accept: ApplyJob::ACCEPT_CONTENT_TYPE, class: 'form-control'
span.form-msg
br
= f.submit 'Confirm', class: 'btn btn-primary w-25 my-4 btn-height',data: { disable_with: false }
= render 'shared/page_heading', title: 'Apply form', url: '#'
.section
.container.py-5
= render 'ribbon'
.container
h3.fs-5.fw-bold.mb-4.text-center
= @job.title
.col
= form_with(model: @apply, url: confirm_job_path, local: true, id: 'apply-form') do |f|
= render 'shared/error_messages', object: f.object
.row.mb-5.form-group
= f.hidden_field :job_id, value: @job.id
.col-2
= f.label :user_name, 'Full name', class: 'form-label label'
.col-10
= f.text_field :user_name, value: @apply.user_name || @current_user.name, class: 'form-control'
span.form-msg
.row.mb-5.form-group
.col-2
= f.label :email, class: 'form-label label'
.col-10
= f.text_field :email, value: @apply.email || @current_user.email, class: 'form-control'
span.form-msg
.row.mb-5.form-group
/ - if @user.cv.attached?
/ = url_for(@user.cv)
.col-2
= f.label :cv, class: 'form-label label'
.col-10
= f.file_field :cv, files: @blob, accept: ApplyJob::ACCEPT_CONTENT_TYPE
span.form-msg
br
= f.submit 'Confirm', class: 'btn btn-primary w-25 my-4 btn-height',data: { disable_with: false }
javascript:
Validator({
......
div id="favorite-industry-#{industry_id}"
= button_to favorite_industries_path(industry_id: industry_id, format: :js),
method: :post,
remote: true,
class: 'btn' do
i.fa.fa-heart-o.mr-1
\ No newline at end of file
div id="unfavorite-industry-#{industry_id}"
= button_to favorite_industry_path(current_user, industry_id: industry_id, format: :js),
method: :delete,
remote: true,
class: 'btn' do
i.fa.fa-heart.mr-1
\ No newline at end of file
$("#favorite-industry-<%= @industry.id %>").html("<%= escape_javascript(render('unfavorite', industry_id: @industry.id)) %>");
$("#unfavorite-industry-<%= @industry.id %>").html("<%= escape_javascript(render('favorite', industry_id: @industry.id)) %>");
......@@ -8,6 +8,18 @@
- @job_quantity_by_industry.each do |industry|
.col-lg-6.col-sm-6.col-12.text-center
.mt-5
h3.h4.mb-2.see-more-text= link_to industry[0], industry_jobs_path(industry_slug: industry[1]) , class:'text-decoration-none fw-normal text-reset'
p.text-muted.mb-0= pluralize(industry[2], 'job')
.row
.col-10
h5.mb-2.see-more-text= link_to industry[0], industry_jobs_path(industry_slug: industry[1]) , class:'text-decoration-none fw-normal text-reset'
p.text-muted.mb-0= pluralize(industry[2], 'job')
.col-2
- if user_signed_in? && current_user.fav_industry?(industry[1])
= render 'favorite_industries/unfavorite', industry_id: Industry.find_by(slug: industry[1]).id
- else
= render 'favorite_industries/favorite', industry_id: Industry.find_by(slug: industry[1]).id
hr.divider.my-4
/ h3.h4.mb-2.see-more-text= link_to industry[0], industry_jobs_path(industry_slug: industry[1]) , class:'text-decoration-none fw-normal text-reset'
/ p.text-muted.mb-0= pluralize(industry[2], 'job')
/ hr.divider.my-4
......@@ -16,7 +16,9 @@
.col-lg-3.col-sm-6.col-12
.footer-widget
.footer-widget-about
img[src="assets/images/logo/logo-light.png" alt=""]
= link_to image_tag('logo.png', alt: 'Zigexn logo', width: '150'),
- root_path
p
| Lorem ipsum dolor sit amet consecte tur adipisicing elit. Maiores officiis quod quo id inventore quis.
ul.footer-socail
......@@ -35,52 +37,43 @@
.col-lg-3.col-sm-6.col-12
.footer-widget
h6.title
| Quick links
| Column 1
.footer-widget-link
ul
li
a[href="#"]
| Post New Job
= link_to 'Item link', '#'
li
a[href="#"]
| Jobs List
= link_to 'Item link', '#'
li
a[href="#"]
| Candidate List
= link_to 'Item link', '#'
li
a[href="#"]
| Employer List
= link_to 'Item link', '#'
li
a[href="#"]
| Browse Categories
= link_to 'Item link', '#'
.col-lg-3.col-sm-6.col-12
.footer-widget
h6.title
| Tranding Jobs
| Column 2
.footer-widget-link
ul
li
a[href="#"]
| Designer
= link_to 'Item link', '#'
li
a[href="#"]
| UI & UX Expert
= link_to 'Item link', '#'
li
a[href="#"]
| Develpoer
= link_to 'Item link', '#'
li
a[href="#"]
| iOS developer
= link_to 'Item link', '#'
li
a[href="#"]
| Front-End developer
= link_to 'Item link', '#'
.col-lg-3.col-sm-6.col-12
.footer-widget
h6.title
| Newsletter
.footer-widget-newsletter
p
| Subscribe to Lawson to get all latest Job, Resume, Company Listing & Blog post to stay update.
| Lorem ipsum dolor sit amet consecte tur adipisicing elit. Maiores officiis quod quo id inventore quis.
form#mc-form.mc-form
input#mc-email[autocomplete="off" type="email" placeholder="Enter your e-mail address"]
button#mc-submit.btn
......@@ -95,4 +88,3 @@
.col-12
p.footer-copyright.text-center
| This is a sample website. Some functions won't work
= link_to "Log out", destroy_user_session_path, method: :delete
......@@ -37,10 +37,10 @@ header.header
.header-logo
= link_to image_tag('logo.png', alt: 'Zigexn logo', width: '150'),
- root_path
.col-auto.d-lg-none.d-flex.align-items-center
button.offcanvas-toggle
span
- unless user_signed_in? || admin_signed_in?
.col-auto.d-lg-none.d-flex.align-items-center
button.offcanvas-toggle
span
.header-links.col-auto.order-lg-3
= link_to "Log in", login_path
span or
......@@ -64,8 +64,9 @@ header.header
ul.sub-menu
li.has-children
= link_to "Applies job", admin_applies_jobs_path
hr
li.has-children
= button_to "Sign out", destroy_admin_session_path, method: :delete
= button_to "Sign out", destroy_admin_session_path, class: 'logout', method: :delete
- if user_signed_in?
li
= link_to "Profile", user_profile_path
......@@ -78,9 +79,9 @@ header.header
= link_to "Favorite", favorite_jobs_path
li.has-children
= link_to "Profile", user_profile_path
hr
li.has-children
= button_to "Log out", destroy_user_session_path, method: :delete
= link_to "Log out", destroy_user_session_path, data: {method: 'delete'}
= button_to "Log out", destroy_user_session_path, class: 'logout', method: :delete
#offcanvas.offcanvas-section
button.offcanvas-close[data-target="#offcanvas"]
......
......@@ -12,8 +12,13 @@ html
= yield(:head)
body
= render 'layouts/header'
- flash.each do |message_type, message|
div class=("text-center alert alert-#{message_type}") = message
.flash
- flash.each do |message_type, message|
div class=("m-0 text-center alert alert-#{message_type}")
= message
.text-center
a.btn-close close
= yield
= render 'layouts/footer'
= javascript_pack_tag 'main'
......@@ -7,7 +7,7 @@
li.breadcrumb-item
= link_to 'Home', root_path
li.breadcrumb-item.active
= link_to title, url
= link_to title, url: '#'
- if params[:city_slug] || params[:industry_slug]
li.breadcrumb-item.active
= name
\ No newline at end of file
.section.section-padding
.container
.section-title
h2.title
= title
p
= desc
.job-list-wrap
- jobs.each do |job|
= link_to job, class: 'job-list' do
.company-logo.col-auto
= image_tag('company-logo', width: '70', height: '70')
.salary-type.col-auto.order-sm-3
span.salary-range
= job.salary
span.badge.badge-success
= job.job_type
.content.col
h6.title
= job.title
ul.meta
li
strong.text-primary
= job.company.name
li
i.fa.fa-map-marker
- city_list = job.cities
= show_location(job.cities)
.text-center.mt-4.mt-lg-5
= link_to 'View All Jobs', jobs_path , class: 'btn btn-primary'
......@@ -17,37 +17,29 @@
= render 'shared/search'
/ latest jobs
.section.section-padding
= render 'section_jobs', title: 'Latest Jobs',
desc: "Here's the most recent job listed on the website.",
jobs: @latest_jobs
/ quotes
.section.section-padding.bg-parallax[data-bg-image="assets/banner-5.jpg" data-overlay="50"]
.container
.section-title
h2.title
| Latest Jobs
p
| Here's the most recent job listed on the website.
.job-list-wrap
- @latest_jobs.each do |job|
= link_to job, class: 'job-list' do
.company-logo.col-auto
= image_tag('company-logo', width: '70', height: '70')
.salary-type.col-auto.order-sm-3
span.salary-range
= job.salary
span.badge.badge-success
= job.job_type
.content.col
h6.title
= job.title
ul.meta
li
strong.text-primary
= job.company.name
li
i.fa.fa-map-marker
- city_list = job.cities
= show_location(job.cities)
.text-center.mt-4.mt-lg-5
= link_to 'View All Jobs', jobs_path , class: 'btn btn-primary'
.funfact-wrap.row
.funfact.col-12
span.counter
| Something quotes
span.title
| Something quotes
/ suggest or topviews
- if user_signed_in? && current_user.industries?
= render 'section_jobs', title: 'Recommend for you',
desc: 'Here are the hot jobs based on your favorite industries',
jobs: @suggest_jobs
- else
= render 'section_jobs', title: 'Attractive jobs',
desc: "Here's the most viewed jobs",
jobs: @attractive_jobs
/ count
.section.section-padding.bg-parallax[data-bg-image="assets/banner-2.jpg" data-overlay="50"]
.container
......
......@@ -15,4 +15,4 @@
= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email)
.actions
= f.submit "Resend confirmation instructions", class: 'btn btn-primary'
= render "users/shared/links"
\ No newline at end of file
= render "users/shared/links"
- provide(:title, 'Sign Up')
= render 'shared/page_heading', title: 'Signup', url: signup_path
= render 'shared/page_heading', title: 'Sign up', url: signup_path
.section.section-padding
......
.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, 'Send email')
= render 'shared/page_heading', title: 'Check you email'
.section.section-padding
.container.text-center
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
......@@ -24,10 +24,11 @@ Rails.application.routes.draw do
end
root 'top#index'
resources :cities, only: %i[index]
resources :industries, only: %i[index]
resources :favorite_jobs, only: %i[index create destroy]
resources :jobs, only: %i[index show] do
resources :cities, only: %i[index]
resources :industries, only: %i[index]
resources :favorite_jobs, only: %i[index create destroy]
resources :favorite_industries, only: %i[index create destroy]
resources :jobs, only: %i[index show] do
collection do
get 'city/:city_slug', action: :index, as: :city
get 'industry/:industry_slug', action: :index, as: :industry
......
class AddViewsCountToJobs < ActiveRecord::Migration[6.1]
def change
add_column :jobs, :views_count, :integer
end
end
class CreateFavoriteIndustries < ActiveRecord::Migration[6.1]
def change
create_table :favorite_industries do |t|
t.references :user, null: false, foreign_key: true
t.references :industry, null: false, foreign_key: true
t.timestamps
end
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_09_27_111928) do
ActiveRecord::Schema.define(version: 2021_12_04_210000) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name", null: false
......@@ -91,6 +91,15 @@ ActiveRecord::Schema.define(version: 2021_09_27_111928) do
t.datetime "updated_at", precision: 6, null: false
end
create_table "favorite_industries", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "industry_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["industry_id"], name: "index_favorite_industries_on_industry_id"
t.index ["user_id"], name: "index_favorite_industries_on_user_id"
end
create_table "favorite_jobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "job_id", null: false
t.bigint "user_id", null: false
......@@ -143,6 +152,7 @@ ActiveRecord::Schema.define(version: 2021_09_27_111928) do
t.datetime "updated_at", precision: 6, null: false
t.integer "min_salary"
t.integer "max_salary"
t.integer "views_count"
t.index ["company_id"], name: "index_jobs_on_company_id"
end
......@@ -183,6 +193,8 @@ ActiveRecord::Schema.define(version: 2021_09_27_111928) do
add_foreign_key "apply_jobs", "users"
add_foreign_key "cities_jobs", "cities"
add_foreign_key "cities_jobs", "jobs"
add_foreign_key "favorite_industries", "industries"
add_foreign_key "favorite_industries", "users"
add_foreign_key "favorite_jobs", "jobs"
add_foreign_key "favorite_jobs", "users"
add_foreign_key "history_jobs", "jobs"
......
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
# Character.create(name: 'Luke', movie: movies.first)
# reset views count of jobs
Job.find_in_batches do |jobs|
jobs.each do |job|
job.update_attribute :views_count, 0
end
end
require 'rsolr'
namespace :solr do
# rails solr:[task]
desc 'solr index data'
task add_data: :environment do
logger.info "start at #{Time.current}"
......
......@@ -116,6 +116,7 @@ namespace :crawler do
benefit: benefits,
requirement: requirement,
other_info: other_info,
views_count: 0,
company_id: company_object.id })
industry_objects = industries.map { |industry| Industry.find_or_create_by(name: industry, slug: industry.to_slug) }
job_object.industries << industry_objects
......@@ -123,6 +124,7 @@ namespace :crawler do
cities = job_page.css('.job-detail-content .detail-box .map p a').map(&:text)
city_objects = cities.map { |city| City.find_or_create_by(name: city, slug: city.to_slug) }
job_object.cities << city_objects
rescue URI::InvalidURIError => e
puts "[Error] #{e.message}"
logger.error "URI must be ascii only : #{url}"
......
......@@ -2,6 +2,7 @@
"name": "ven-job",
"private": true,
"dependencies": {
"@babel/plugin-proposal-private-methods": "^7.16.0",
"@popperjs/core": "^2.9.2",
"@rails/actioncable": "^6.0.0",
"@rails/activestorage": "^6.0.0",
......
require "test_helper"
class FavoriteIndustriesControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
user: one
industry: one
two:
user: two
industry: two
require "test_helper"
class FavoriteIndustryTest < 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