feat: add state filtering to cities index

- Implement state filtering in the CitiesController
- Add new scope `by_state` in the City model
- Update cities index view to include state dropdown
- Add slug column to states for friendly URLs
- Update English locale to include 'All States'

This commit enhances the cities index page by allowing users to
filter cities based on the selected state. It introduces a new
scope in the City model for state filtering and updates the
view to present a dropdown for state selection. Additionally,
it adds a slug column to the states table to support friendly
URLs, improving the overall user experience.
This commit is contained in:
songtianlun 2025-02-26 09:48:39 +08:00
parent fe5c0d5113
commit f51dc8370b
7 changed files with 58 additions and 3 deletions

View File

@ -21,6 +21,11 @@ class CitiesController < ApplicationController
@cities = @cities.by_country(@current_country.id) if @current_country
end
if params[:state]
@current_state = State.friendly.find(params[:state])
@cities = @cities.by_state(@current_state.id) if @current_state
end
@cities = @cities.page(params[:page]).per(12)
respond_to do |format|

View File

@ -19,6 +19,7 @@ class City < ApplicationRecord
scope :by_region, ->(region_id) { joins(:country).where(countries: { region_id: region_id }) }
scope :by_country, ->(country_id) { where(country_id: country_id) }
scope :by_state, ->(state_id) { where(state_id: state_id) }
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }

View File

@ -1,4 +1,7 @@
class State < ApplicationRecord
extend FriendlyId
friendly_id :name, use: :slugged
belongs_to :country
has_many :cities

View File

@ -47,8 +47,10 @@
<div class="flex items-center gap-4">
<div class="dropdown">
<button class="btn btn-ghost gap-2">
<!-- 大洲/地区 (Region) SVG - 使用地球仪图标 -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<%= @current_region&.localized_name || t("text.all_regions") %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@ -76,8 +78,10 @@
<% if @current_region %>
<div class="dropdown dropdown-bottom">
<button class="btn btn-ghost gap-2">
<!-- 国家 (Country) SVG - 现代旗帜图标 -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 5a1 1 0 011-1h14a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h14a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6z"/>
</svg>
<%= @current_country&.localized_name || t("text.all_countries") %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@ -101,6 +105,39 @@
</ul>
</div>
<% end %>
<% if @current_country %>
<div class="dropdown dropdown-bottom">
<button class="btn btn-ghost gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
<%= @current_state&.name || t("text.all_states") %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<ul class="dropdown-content z-[1] menu p-2 shadow-lg bg-base-100 rounded-box w-52 max-h-80 overflow-y-auto flex-nowrap">
<li>
<%= link_to "#{t("text.all_in")} #{@current_country.localized_name}",
cities_path(region: @current_region.slug),
class: "#{@current_country ? '' : 'active'}" %>
</li>
<div class="divider my-1"></div>
<% @current_country.states.order(:name).each do |state| %>
<li>
<%= link_to "#{state.name}",
cities_path(region: @current_region.slug, country: @current_country.slug, state: state.slug),
class: "#{@current_state == state ? 'active' : ''}" %>
</li>
<% end %>
</ul>
</div>
<% end %>
</div>
<div class="text-sm text-base-content/70 hidden">

View File

@ -67,6 +67,7 @@ en:
search_cities: "Search cities..."
all_regions: "All Regions"
all_countries: "All Countries"
all_states: "All States"
all_in: "All in"
showing: "Showing"
weather_arts: "Weather Arts"

View File

@ -0,0 +1,6 @@
class AddSlugToStates < ActiveRecord::Migration[8.0]
def change
add_column :states, :slug, :string
add_index :states, :slug, unique: true
end
end

4
db/schema.rb generated
View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_02_11_035423) do
ActiveRecord::Schema[8.0].define(version: 2025_02_26_011649) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -205,7 +205,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_11_035423) do
t.string "wiki_data_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.index ["country_id"], name: "index_states_on_country_id"
t.index ["slug"], name: "index_states_on_slug", unique: true
end
create_table "subregions", force: :cascade do |t|