diff --git a/app/admin/weather_arts.rb b/app/admin/weather_arts.rb index 9fd96c3..5e44e0b 100644 --- a/app/admin/weather_arts.rb +++ b/app/admin/weather_arts.rb @@ -1,4 +1,9 @@ ActiveAdmin.register WeatherArt do + controller do + def find_resource + scoped_collection.friendly.find(params[:id]) + end + end # See permitted parameters documentation: # https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters # diff --git a/app/controllers/cities_controller.rb b/app/controllers/cities_controller.rb index 6a6367b..1fb8aa8 100644 --- a/app/controllers/cities_controller.rb +++ b/app/controllers/cities_controller.rb @@ -1,6 +1,18 @@ class CitiesController < ApplicationController def index @cities = City.all.order(:name) + @regions = Region.includes(:countries).order(:name) + @cities = City.includes(:country, country: :region).active.order(:name) + + if params[:region] + @current_region = Region.friendly.find(params[:region]) + @cities = @cities.by_region(@current_region.id) + end + + if params[:country] + @current_country = Country.friendly.find(params[:country]) + @cities = @cities.by_country(@current_country.id) + end end def show diff --git a/app/controllers/weather_arts_controller.rb b/app/controllers/weather_arts_controller.rb index 1f5a178..5ae79ec 100644 --- a/app/controllers/weather_arts_controller.rb +++ b/app/controllers/weather_arts_controller.rb @@ -1,6 +1,6 @@ class WeatherArtsController < ApplicationController def show @city = City.friendly.find(params[:city_id]) - @weather_art = @city.weather_arts.find(params[:id]) + @weather_art = @city.weather_arts.friendly.find(params[:slug]) end end diff --git a/app/models/city.rb b/app/models/city.rb index 28b42b9..86d8bf6 100644 --- a/app/models/city.rb +++ b/app/models/city.rb @@ -1,6 +1,6 @@ class City < ApplicationRecord extend FriendlyId - friendly_id :name, use: :slugged + friendly_id :slug_candidates, use: :slugged belongs_to :country has_many :weather_arts, dependent: :destroy @@ -11,10 +11,21 @@ class City < ApplicationRecord delegate :region, to: :country + scope :by_region, ->(region_id) { joins(:country).where(countries: { region_id: region_id }) } + scope :by_country, ->(country_id) { where(country_id: country_id) } + scope :active, -> { where(active: true) } + def to_s name end + def slug_candidates + [ + :name, + [ :country, :name ] + ] + end + def localized_name I18n.t("cities.#{name.parameterize.underscore}") end diff --git a/app/models/weather_art.rb b/app/models/weather_art.rb index fafa5ca..ca9c1e2 100644 --- a/app/models/weather_art.rb +++ b/app/models/weather_art.rb @@ -1,11 +1,21 @@ class WeatherArt < ApplicationRecord - belongs_to :city + extend FriendlyId + friendly_id :weather_date, use: :slugged + belongs_to :city has_one_attached :image validates :weather_date, presence: true validates :city_id, presence: true + def should_generate_new_friendly_id? + weather_date_changed? || city_id_changed? || super + end + + def to_s + "#{city.name} - #{weather_date.strftime('%Y-%m-%d')}" + end + def self.ransackable_associations(auth_object = nil) [ "city", "image_attachment", "image_blob" ] end diff --git a/app/views/cities/index.html.erb b/app/views/cities/index.html.erb index c72a81a..c995f4b 100644 --- a/app/views/cities/index.html.erb +++ b/app/views/cities/index.html.erb @@ -1,35 +1,119 @@ -
-
-

Explore Cities

-

- Discover AI-generated weather art from cities around the world -

+
+ + +
+
+

+ Explore Cities +

+

+ Discover AI-generated weather art from cities around the world +

+
-
- <% @cities.each do |city| %> - <% latest_art = city.weather_arts.last %> -
- <% if latest_art&.image&.attached? %> -
- <%= image_tag latest_art.image, - class: "w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-500" %> -
-
- <% end %> + +
+
+ <%= link_to "All Regions", + cities_path, + class: "btn btn-outline #{'btn-primary' unless @current_region}" %> -
-

<%= city.localized_name %>

-
-

Lat: <%= city.latitude %>

-

Long: <%= city.longitude %>

-
-
- <%= link_to "View Weather Art", city_path(city), - class: "btn btn-primary" %> -
-
+ <% @regions.each do |region| %> + <%= link_to region.name, + cities_path(region: region.slug), + class: "btn btn-outline #{'btn-primary' if @current_region == region}" %> + <% end %> +
+ + <% if @current_region %> +
+ <%= link_to "All Countries in #{@current_region.name}", + cities_path(region: @current_region.slug), + class: "btn btn-sm btn-ghost #{'btn-active' unless @current_country}" %> + + <% @current_region.countries.order(:name).each do |country| %> + <%= link_to country.name, + cities_path(region: @current_region.slug, country: country.slug), + class: "btn btn-sm btn-ghost #{'btn-active' if @current_country == country}" %> + <% end %>
<% end %> + + +
+ +
+
+ + +
+
+ <% @cities.each do |city| %> +
+ <% if city.latest_weather_art&.image&.attached? %> +
+ <%= image_tag city.latest_weather_art.image, + class: "w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-500" %> +
+
+

+ <%= city.name %> +

+

+ <%= city.country.name %>, <%= city.region.name %> +

+
+
+ <% else %> +
+

<%= city.name %>

+

+ <%= city.country.name %>, <%= city.region.name %> +

+
+ Lat: <%= city.latitude %> + Long: <%= city.longitude %> +
+
+ <% end %> + +
+
+
+ + + + + <%= city.timezone %> +
+ <% if city.latest_weather_art %> +
+ + + + <%= city.latest_weather_art.weather_date.strftime("%b %d, %Y") %> +
+ <% end %> +
+ +
+ <%= link_to "View Details", city_path(city), + class: "btn btn-primary btn-sm" %> +
+
+
+ <% end %> +
\ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 026b4a6..e5c5daa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,8 +1,8 @@ Rails.application.routes.draw do root "home#index" - resources :cities, only: [ :index, :show ] do - resources :weather_arts, only: [ :show ] + resources :cities, only: [:index, :show] do + resources :weather_arts, path: 'weather', only: [:show], param: :slug end # namespace :admin do diff --git a/db/migrate/20250122053220_add_slug_to_weather_arts.rb b/db/migrate/20250122053220_add_slug_to_weather_arts.rb new file mode 100644 index 0000000..de1b698 --- /dev/null +++ b/db/migrate/20250122053220_add_slug_to_weather_arts.rb @@ -0,0 +1,6 @@ +class AddSlugToWeatherArts < ActiveRecord::Migration[8.0] + def change + add_column :weather_arts, :slug, :string + add_index :weather_arts, :slug, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 3862182..b1c482f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,17 +10,14 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_01_21_020653) do - # These are extensions that must be enabled in order to support this database - enable_extension "pg_catalog.plpgsql" - +ActiveRecord::Schema[8.0].define(version: 2025_01_22_053220) do create_table "active_admin_comments", force: :cascade do |t| t.string "namespace" t.text "body" t.string "resource_type" - t.bigint "resource_id" + t.integer "resource_id" t.string "author_type" - t.bigint "author_id" + t.integer "author_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author" @@ -80,7 +77,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_21_020653) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "slug" - t.bigint "country_id", null: false + t.integer "country_id", null: false t.index ["country_id"], name: "index_cities_on_country_id" t.index ["slug"], name: "index_cities_on_slug", unique: true end @@ -89,7 +86,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_21_020653) do t.string "name" t.string "code" t.string "slug" - t.bigint "region_id", null: false + t.integer "region_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["code"], name: "index_countries_on_code", unique: true @@ -119,7 +116,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_21_020653) do end create_table "weather_arts", force: :cascade do |t| - t.bigint "city_id", null: false + t.integer "city_id", null: false t.date "weather_date" t.string "description" t.decimal "temperature" @@ -134,7 +131,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_21_020653) do t.text "prompt" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "slug" t.index ["city_id"], name: "index_weather_arts_on_city_id" + t.index ["slug"], name: "index_weather_arts_on_slug", unique: true end add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"