Skip to content

Commit b3f552b

Browse files
authored
feat: location filter by city instead of country (#168)
1 parent 1f47aaf commit b3f552b

File tree

18 files changed

+110
-43
lines changed

18 files changed

+110
-43
lines changed

app/components/developers/query_component.html.erb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,27 +103,27 @@
103103
<div data-controller="toggle accessibility" data-toggle-visibility-class="hidden">
104104
<div class="border-t border-gray-200 py-6">
105105
<h3 class="-mx-2 -my-3 flow-root">
106-
<%= render CollapseControlComponent.new(t(".country.title"), collapsed: collapse_location?) %>
106+
<%= render CollapseControlComponent.new(t(".city.title"), collapsed: collapse_location?) %>
107107
</h3>
108108
<%= tag.div id: "location-accordion", "data-toggle-target": "element", class: [hidden: collapse_location?] do %>
109109
<div class="space-y-4 pt-6">
110-
<%= form.collection_check_boxes(:countries, top_countries, :first, :last, {include_hidden: false}) do |b| %>
110+
<%= form.collection_check_boxes(:cities, top_cities, :first, :last, {include_hidden: false}) do |b| %>
111111
<div class="flex items-center">
112-
<%= b.check_box checked: country_selected?(b.object), class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500" %>
112+
<%= b.check_box checked: city_selected?(b.object), class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500" %>
113113
<%= b.label class: "ml-3 text-blue-950 text-sm" %>
114114
</div>
115115
<% end %>
116116
</div>
117117
<div data-controller="toggle accessibility" data-toggle-visibility-class="hidden">
118118
<div class="pt-6">
119119
<h5 class="-mx-2 -my-3 flow-root">
120-
<%= render CollapseControlComponent.new(t(".all_countries.title"), collapsed: collapse_all_locations?, subcomponent: true) %>
120+
<%= render CollapseControlComponent.new(t(".all_cities.title"), collapsed: collapse_all_locations?, subcomponent: true) %>
121121
</h5>
122122
<%= tag.div id: "all-locations-accordion", "data-toggle-target": "element", class: [hidden: collapse_all_locations?] do %>
123123
<div class="space-y-4 pt-6">
124-
<%= form.collection_check_boxes(:countries, countries, :first, :last, {include_hidden: false}) do |b| %>
124+
<%= form.collection_check_boxes(:cities, cities, :first, :last, {include_hidden: false}) do |b| %>
125125
<div class="flex items-center">
126-
<%= b.check_box checked: country_selected?(b.object), class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500" %>
126+
<%= b.check_box checked: city_selected?(b.object), class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500" %>
127127
<%= b.label class: "ml-3 text-blue-950 text-sm" %>
128128
</div>
129129
<% end %>

app/components/developers/query_component.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ def initialize(query:, user:, form_id:)
1010
@form_id = form_id
1111
end
1212

13+
def city_selected?(city_pair)
14+
query.cities.include?(city_pair.first)
15+
end
16+
17+
def top_cities
18+
Location.top_cities.map { |k| [k, k] }
19+
end
20+
21+
def cities
22+
Location.not_top_cities.map { |k| [k, k] }
23+
end
24+
1325
def country_selected?(country_pair)
1426
query.countries.include?(country_pair.first)
1527
end
@@ -83,12 +95,12 @@ def collapse_specialties?
8395
end
8496

8597
def collapse_location?
86-
query.countries.empty?
98+
query.cities.empty?
8799
end
88100

89101
def collapse_all_locations?
90102
@collapse_all_locations ||=
91-
(Location.not_top_countries & query.countries).none?
103+
(Location.not_top_cities & query.cities).none?
92104
end
93105

94106
def collapse_timezone?

app/components/developers/query_mobile_component.html.erb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,28 +98,28 @@
9898
</div>
9999
<div data-controller="toggle" data-toggle-visibility-class="hidden" class="border-t border-gray-200 px-4 py-6">
100100
<h3 class="-mx-2 -my-3 flow-root">
101-
<%= render CollapseControlComponent.new(t("developers.query_component.country.title"), collapsed: true) %>
101+
<%= render CollapseControlComponent.new(t("developers.query_component.city.title"), collapsed: true) %>
102102
</h3>
103-
<div id="top-countries-filter-section" data-toggle-target="element" class="hidden pt-6">
103+
<div id="top-cities-filter-section" data-toggle-target="element" class="hidden pt-6">
104104
<div class="space-y-6">
105-
<%= form.collection_check_boxes(:countries, top_countries, :first, :last, {include_hidden: false}) do |b| %>
105+
<%= form.collection_check_boxes(:cities, top_cities, :first, :last, {include_hidden: false}) do |b| %>
106106
<div class="flex items-center">
107-
<%= b.check_box class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500", checked: country_selected?(b.object) %>
107+
<%= b.check_box class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500", checked: city_selected?(b.object) %>
108108
<%= b.label class: "ml-3 pr-6 text-sm font-medium text-blue-900 whitespace-nowrap" %>
109109
</div>
110110
<% end %>
111111
</div>
112112
<div data-controller="toggle" data-toggle-visibility-class="hidden">
113113
<div class="pt-6">
114114
<h5 class="-mx-2 -my-3 flow-root">
115-
<%= render CollapseControlComponent.new(t("developers.query_component.all_countries.title"), collapsed: true, subcomponent: true) %>
115+
<%= render CollapseControlComponent.new(t("developers.query_component.all_cities.title"), collapsed: true, subcomponent: true) %>
116116
</h5>
117117
</div>
118-
<div id="all-countries-filter-section" data-toggle-target="element" class="hidden">
118+
<div id="all-cities-filter-section" data-toggle-target="element" class="hidden">
119119
<div class="space-y-4 pt-6">
120-
<%= form.collection_check_boxes(:countries, countries, :first, :last, {include_hidden: false}) do |b| %>
120+
<%= form.collection_check_boxes(:cities, cities, :first, :last, {include_hidden: false}) do |b| %>
121121
<div class="flex items-center">
122-
<%= b.check_box class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500", checked: country_selected?(b.object) %>
122+
<%= b.check_box class: "h-4 w-4 border-gray-300 rounded text-blue-950 focus:ring-blue-500", checked: city_selected?(b.object) %>
123123
<%= b.label class: "ml-3 pr-6 text-sm font-medium text-blue-900 whitespace-nowrap" %>
124124
</div>
125125
<% end %>

app/controllers/developers_controller.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ def index
77
@developers_count = SignificantFigure.new(Developer.actively_looking_or_open.count).rounded
88
@query = DeveloperQuery.new(permitted_attributes([:developers, :query]).merge(user: current_user))
99
@meta = Developers::Meta.new(query: @query, count: @developers_count)
10+
1011
Analytics::SearchQuery.create!(permitted_attributes([:developers, :query]))
1112

1213
if @query.empty_search?

app/models/developer.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class Developer < ApplicationRecord
7272
joins(:location).where(locations: {country: countries})
7373
end
7474

75+
scope :filter_by_cities, ->(cities) do
76+
joins(:location).where(locations: {city: cities})
77+
end
78+
7579
scope :actively_looking_or_open, -> { where(search_status: [:actively_looking, :open, nil]) }
7680
scope :featured, -> { where("featured_at >= ?", FEATURE_LENGTH.ago).order(featured_at: :desc) }
7781
scope :newest_first, -> { order(created_at: :desc) }

app/models/developers/meta.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ def freelance?
5454
!query.role_types.include?(:full_time_employment)
5555
end
5656

57+
def city
58+
if query.cities.one?
59+
query.cities.first
60+
end
61+
end
62+
5763
def country
5864
if query.countries.one?
5965
query.countries.first

app/models/developers/query_path.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ def all
2626

2727
private
2828

29+
def top_cities
30+
@top_cities ||= Location.top_cities
31+
end
32+
2933
def top_countries
3034
@top_countries ||= Location.top_countries
3135
end

app/models/location.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ class Location < ApplicationRecord
1818
.pluck(:country)
1919
end
2020

21+
scope :top_cities, ->(limit = ENV.fetch("TOP_CITIES", 10)) do
22+
group(:city)
23+
.where.not(city: nil)
24+
.order("count_all DESC")
25+
.limit(limit)
26+
.count
27+
.keys
28+
end
29+
30+
scope :not_top_cities, ->(limit = ENV.fetch("TOP_CITIES", 10)) do
31+
where.not(city: top_cities(limit))
32+
.select(:city)
33+
.distinct
34+
.order(:city)
35+
.pluck(:city)
36+
end
37+
2138
validates :time_zone, presence: true
2239
validates :utc_offset, presence: true
2340
validate :valid_coordinates

app/policies/developers/query_policy.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def default_attributes
1919
role_types: [],
2020
badges: [],
2121
utc_offsets: [],
22-
countries: []
22+
countries: [],
23+
cities: []
2324
]
2425
end
2526

app/queries/developer_query.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def initialize(options = {})
1111
@sort = options.delete(:sort)
1212
@specialty_ids = options.delete(:specialty_ids)
1313
@countries = options.delete(:countries)
14+
@cities = options.delete(:cities)
1415
@utc_offsets = options.delete(:utc_offsets)
1516
@role_types = options.delete(:role_types)
1617
@role_levels = options.delete(:role_levels)
@@ -21,7 +22,7 @@ def initialize(options = {})
2122
end
2223

2324
def filters
24-
@filters = {sort:, utc_offsets:, role_types:, role_levels:, include_not_interested:, search_query:, countries:,
25+
@filters = {sort:, utc_offsets:, role_types:, role_levels:, include_not_interested:, search_query:, countries:, cities:,
2526
badges:}
2627
end
2728

@@ -45,6 +46,10 @@ def sort
4546
(@sort.to_s.downcase.to_sym == :recommended) ? :recommended : :newest
4647
end
4748

49+
def cities
50+
@cities.to_a.reject(&:blank?)
51+
end
52+
4853
def countries
4954
@countries.to_a.reject(&:blank?)
5055
end
@@ -79,6 +84,7 @@ def all_search_params
7984
sort: @sort,
8085
specialty_ids: @specialty_ids,
8186
countries: @countries,
87+
cities: @cities,
8288
search_query:,
8389
utc_offsets: @utc_offsets,
8490
role_types: @role_types,
@@ -98,6 +104,7 @@ def empty_search?
98104
role_levels.empty? &&
99105
search_query.blank? &&
100106
countries.blank? &&
107+
cities.blank? &&
101108
badges.blank? &&
102109
specialty_ids.empty? &&
103110
!include_not_interested
@@ -109,6 +116,7 @@ def query_and_paginate
109116
@_records = Developer.includes(:role_type, :specialties).with_attached_avatar.visible
110117
sort_records
111118
country_filter_records
119+
city_filter_records
112120
utc_offset_filter_records
113121
role_type_filter_records
114122
role_level_filter_records
@@ -153,6 +161,10 @@ def sort_records
153161
end
154162
end
155163

164+
def city_filter_records
165+
@_records.merge!(Developer.filter_by_cities(cities)) if cities.any?
166+
end
167+
156168
def country_filter_records
157169
@_records.merge!(Developer.filter_by_countries(countries)) if countries.any?
158170
end

0 commit comments

Comments
 (0)