Commit 9e711633 by Tô Ngọc Ánh

Merge branch 'id4-job-list' into 'master'

ID4 Create Job List

See merge request !9
parents e2407a16 4299f1aa
Pipeline #836 failed with stages
in 0 seconds
......@@ -59,10 +59,13 @@ group :test do
gem 'chromedriver-helper'
end
gem 'rubyzip', group: :production
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
##
gem 'nokogiri'
gem 'whenever', require: false
gem 'kaminari'
##
......@@ -88,6 +88,18 @@ GEM
io-like (0.3.1)
jbuilder (2.10.0)
activesupport (>= 5.0.0)
kaminari (1.2.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.1)
kaminari-activerecord (= 1.2.1)
kaminari-core (= 1.2.1)
kaminari-actionview (1.2.1)
actionview
kaminari-core (= 1.2.1)
kaminari-activerecord (1.2.1)
activerecord
kaminari-core (= 1.2.1)
kaminari-core (1.2.1)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
......@@ -204,11 +216,13 @@ DEPENDENCIES
coffee-rails (~> 4.2)
dotenv-rails
jbuilder (~> 2.5)
kaminari
listen (>= 3.0.5, < 3.2)
mysql2 (>= 0.4.4, < 0.6.0)
nokogiri
puma (~> 3.11)
rails (~> 5.2.4, >= 5.2.4.3)
rubyzip
sass-rails (~> 5.0)
selenium-webdriver
spring
......
class JobsController < ApplicationController
def index
@locations = Location.select(:id, :city)
@industries = Industry.select(:id, :name)
object = params[:model].classify.constantize.find_by_slug(params[:slug])
@keyword = object.try(:name) || object.try(:city)
@jobs = object.jobs.all.includes(:company, :locations, :industries).page(params[:page])
end
end
require './lib/common/convert_slug'
class Industry < ApplicationRecord
include ConvertSlug
scope :with_count_job, -> { joins(:jobs).group(:industry_id).select('industries.*, count(jobs.id) as job_count') }
scope :top_industries, ->(number) { with_count_job.order(Arel.sql('job_count DESC')).limit(number) }
has_and_belongs_to_many :jobs
before_save :add_slug, unless: :slug
private
def add_slug
self.slug = "#{self.to_slug(self.name)}-#{Time.now.to_i}#{rand(10000)}"
end
end
class Job < ApplicationRecord
NUMBER_LATEST_JOB = 6
WORDS_SHORT_DESCRIPTION = 250
belongs_to :company
has_many :applied_jobs
......
require './lib/common/convert_slug.rb'
class Location < ApplicationRecord
include ConvertSlug
scope :with_count_job, -> { joins(:jobs).group(:location_id).select('locations.*, count(jobs.id) as job_count') }
scope :top_locations, ->(number) { with_count_job.order(Arel.sql('job_count DESC')).limit(number) }
scope :international, -> { where(oversea: true) }
......@@ -8,4 +12,12 @@ class Location < ApplicationRecord
has_many :locations_jobs
has_many :jobs, through: :locations_jobs
before_save :add_slug, unless: :slug
private
def add_slug
self.slug = "#{self.to_slug(self.city)}-#{Time.now.to_i}#{rand(10000)}"
end
end
......@@ -8,8 +8,8 @@
<% job.locations.each do |location| %>
<%= location.city %>
<% end %>
<strong>Salary: </strong><%= job.salary %>
</p>
<p><strong>Salary: </strong><%= job.salary %></p>
</div>
</div>
</div>
<div class='banner_top'>
<%= image_tag 'venjob-banner',class: 'banner_img'%>
<%= image_tag 'venjob-banner.jpg',class: 'banner_img'%>
<div class='overlay'></div>
<div class='box_seach text-center'>
<h3>We have <%= @total_jobs %> jobs for you</h3>
<%= render 'shared/searchbar' %>
<%= render 'shared/searchbar', my_class: nil %>
</div>
</div>
<div id='latest-jobs' class='my-4 text-center'>
......
<div class='col-4 my-2'>
<div class='card'>
<%= link_to '#', class: 'card-body text-decoration-none' do %>
<%= link_to jobs_path(model: 'industry', slug: industry.slug), class: 'card-body text-decoration-none' do %>
<h5 class='card-title font-weight-bold'><%= industry.name %></h5>
<p class='card-text'><%= pluralize(industry.job_count, 'Job') %></p>
<% end %>
......
<div class='card flex-md-row align-items-center my-2'>
<div class='card-body'>
<%= link_to job.title, '#', class: 'card-title font-weight-bold text-decoration-none' %>
<p class='card-text'><%= job.company.name %></p>
<p class='mb-0'>
<strong>Work place:</strong>
<% job.locations.each do |location| %>
<%= location.city %>
<% end %>
</p>
<p><strong>Salary: </strong><%= job.salary %></p>
<p class='card-text'><%= strip_tags(job.description).truncate(Job::WORDS_SHORT_DESCRIPTION) %></p>
</div>
<div class='btn-favorite p-2'>
<%= link_to 'Favorite', '#', class: 'btn btn-outline-danger btn-lg' %>
</div>
</div>
<div class='px-5'>
<%= render 'shared/searchbar', my_class: 'd-flex flex-column flex-md-row' %>
</div>
<div>
<div class='message text-center'>
<h3>We found <%= pluralize(@jobs.total_count, 'result') %> for "<%= @keyword %>" </h3>
</div>
<hr>
<div class='content'>
<%= paginate @jobs %>
<%= render partial: 'jobs/job', collection: @jobs %>
<%= paginate @jobs %>
</div>
</div>
<li class="page-item">
<%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote, class: 'page-link' %>
</li>
<li class='page-item disabled'>
<%= link_to raw(t 'views.pagination.truncate'), '#', class: 'page-link' %>
</li>
<li class="page-item">
<%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, remote: remote, class: 'page-link' %>
</li>
<li class="page-item">
<%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, rel: 'next', remote: remote, class: 'page-link' %>
</li>
<% if page.current? %>
<li class="page-item active">
<%= content_tag :a, page, data: { remote: remote }, rel: page.rel, class: 'page-link' %>
</li>
<% else %>
<li class="page-item">
<%= link_to page, url, remote: remote, rel: page.rel, class: 'page-link' %>
</li>
<% end %>
<%= paginator.render do %>
<nav>
<ul class="pagination justify-content-end">
<%= first_page_tag unless current_page.first? %>
<%= prev_page_tag unless current_page.first? %>
<% each_page do |page| %>
<% if page.left_outer? || page.right_outer? || page.inside_window? %>
<%= page_tag page %>
<% elsif !page.was_truncated? -%>
<%= gap_tag %>
<% end %>
<% end %>
<%= next_page_tag unless current_page.last? %>
<%= last_page_tag unless current_page.last? %>
</ul>
</nav>
<% end %>
<li class="page-item">
<%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote, class: 'page-link' %>
</li>
......@@ -9,9 +9,11 @@
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body class='container'>
<body>
<%= render 'shared/header' %>
<%= yield %>
<div class='container'>
<%= yield %>
</div>
<%= render 'shared/footer' %>
</body>
</html>
<div class='col-4 my-2'>
<div class='card'>
<%= link_to '#', class: 'card-body text-decoration-none' do %>
<%= link_to jobs_path(model: 'location', slug: location.slug), class: 'card-body text-decoration-none' do %>
<h5 class='card-title font-weight-bold'><%= location.city %></h5>
<p class='card-text'><%= pluralize(location.job_count, 'Job') %></p>
<% end %>
......
......@@ -20,7 +20,7 @@
<div id='international' class='text-center'>
<h2>International</h2>
<hr>
<div class="row">
<div class='row'>
<%= render partial: 'locations/location', collection: @internal_cities_lists %>
</div>
</div>
<%= form_tag '#', method: :get, class: 'mt-4' do %>
<%= search_field_tag :search, params[:keyword], placeholder: 'Search', class: 'form-control my-2' %>
<%= select_tag :industry, options_from_collection_for_select(@industries, :id, :name, '1'), class: 'form-control my-2' %>
<%= select_tag :location, options_from_collection_for_select(@locations, :id, :city, '1'), class: 'form-control my-2' %>
<%= submit_tag 'Search', class: 'btn btn-outline-success my-2 my-sm-0' %>
<%= form_tag '#', method: :get, class: "mt-4 form-group #{my_class}" do %>
<%= search_field_tag :search, params[:keyword], placeholder: 'Search', class: 'form-control m-2' %>
<%= select_tag :industry, options_from_collection_for_select(@industries, :id, :name, '1'), class: 'form-control m-2' %>
<%= select_tag :location, options_from_collection_for_select(@locations, :id, :city, '1'), class: 'form-control m-2' %>
<%= submit_tag 'Search', class: 'btn btn-outline-success m-2' %>
<% end %>
......@@ -52,3 +52,5 @@ production:
database: VeNJob_AnhTN_production
username: VeNJob_AnhTN
password: <%= ENV['VENJOB_ANHTN_DATABASE_PASSWORD'] %>
url: <%= ENV['JAWSDB_URL'] %>
\ No newline at end of file
# frozen_string_literal: true
Kaminari.configure do |config|
config.default_per_page = 20
# config.max_per_page = nil
config.window = 3
# config.outer_window = 0
# config.left = 0
# config.right = 0
# config.page_method_name = :page
# config.param_name = :page
# config.max_pages = nil
# config.params_on_first_page = false
end
......@@ -2,5 +2,6 @@ Rails.application.routes.draw do
root to: 'home#index'
get 'cities', to: 'locations#index'
get 'industries', to: 'industries#index'
get 'jobs/:model/:slug', to: 'jobs#index', as: :jobs
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
class AddSlugToIndustries < ActiveRecord::Migration[5.2]
def change
add_column :industries, :slug, :string
add_index :industries, :slug, unique: true
end
end
class AddSlugToLocations < ActiveRecord::Migration[5.2]
def change
add_column :locations, :slug, :string
add_index :locations, :slug, unique: true
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_07_20_075150) do
ActiveRecord::Schema.define(version: 2020_08_05_034659) do
create_table "applied_jobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
t.bigint "user_id"
......@@ -55,6 +55,8 @@ ActiveRecord::Schema.define(version: 2020_07_20_075150) do
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.index ["slug"], name: "index_industries_on_slug", unique: true
end
create_table "industries_jobs", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
......@@ -82,6 +84,8 @@ ActiveRecord::Schema.define(version: 2020_07_20_075150) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "oversea"
t.string "slug"
t.index ["slug"], name: "index_locations_on_slug", unique: true
end
create_table "locations_jobs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
......
module ConvertSlug
def to_slug(str)
str = str.to_s.strip.downcase
accents = {
%w[à á ạ ả ã â ầ ấ ậ ẩ ẫ ă ằ ắ ặ ẳ ẵ] => 'a',
%w[è é ẹ ẻ ẽ ê ề ế ệ ể ễ] => 'e',
%w[ì í ị ỉ ĩ] => 'i',
%w[ò ó ọ ỏ õ ô ồ ố ộ ổ ỗ ơ ờ ớ ợ ở ỡ] => 'o',
%w[ù ú ụ ủ ũ ư ừ ứ ự ử ữ] => 'u',
%w[ỳ ý ỵ ỷ ỹ] => 'y',
%w[đ] => 'd'
}
accents.each do |ac, rep|
ac.each do |s|
str = str.tr(s, rep)
end
end
str.parameterize
end
end
......@@ -13,7 +13,7 @@ class CsvImport
next if row['category'].blank? || row['category'].match(/^[0-9]+$/).present?
title = row['name'].strip
company = Company.find_or_create_by(name: row['company name']) do |c|
company = Company.find_or_create_by(name: row['company name'].strip) do |c|
c.description = "Contact email: #{row['contact email']}\n"\
"Contact name: #{row['contact name']}\n"\
"Contact phone: #{row['contact phone']}"
......
namespace :create_slug do
desc 'Create slug model Location'
task locations: :environment do
Location.all.each do |location|
location.update_attributes(slug: "#{location.to_slug(location.city)}-#{Time.now.to_i}#{rand(10000)}")
end
end
desc 'Create slug model Industry'
task industries: :environment do
Industry.all.each do |industry|
industry.update_attributes(slug: "#{industry.to_slug(industry.name)}-#{Time.now.to_i}#{rand(10000)}")
end
end
desc 'Create slug to all model'
task all: %i[locations industries]
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