Compare commits
2 Commits
8517905b68
...
b9801aeb6b
Author | SHA1 | Date | |
---|---|---|---|
b9801aeb6b | |||
e5743a5e3f |
1
.idea/today_ai_weather.iml
generated
1
.idea/today_ai_weather.iml
generated
@ -69,6 +69,7 @@
|
|||||||
<orderEntry type="library" scope="PROVIDED" name="et-orbi (v1.2.11, mise: 3.3.5) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="et-orbi (v1.2.11, mise: 3.3.5) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="formtastic (v5.0.0, mise: 3.3.5) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="formtastic (v5.0.0, mise: 3.3.5) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="formtastic_i18n (v0.7.0, mise: 3.3.5) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="formtastic_i18n (v0.7.0, mise: 3.3.5) [gem]" level="application" />
|
||||||
|
<orderEntry type="library" scope="PROVIDED" name="friendly_id (v5.5.1, mise: 3.3.5) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="fugit (v1.11.1, mise: 3.3.5) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="fugit (v1.11.1, mise: 3.3.5) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="globalid (v1.2.1, mise: 3.3.5) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="globalid (v1.2.1, mise: 3.3.5) [gem]" level="application" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="has_scope (v0.8.2, mise: 3.3.5) [gem]" level="application" />
|
<orderEntry type="library" scope="PROVIDED" name="has_scope (v0.8.2, mise: 3.3.5) [gem]" level="application" />
|
||||||
|
1
Gemfile
1
Gemfile
@ -43,6 +43,7 @@ gem "thruster", require: false
|
|||||||
# gem "image_processing", "~> 1.2"
|
# gem "image_processing", "~> 1.2"
|
||||||
gem 'devise', '~> 4.9'
|
gem 'devise', '~> 4.9'
|
||||||
gem 'activeadmin', '~> 3.2'
|
gem 'activeadmin', '~> 3.2'
|
||||||
|
gem 'friendly_id', '~> 5.5'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
|
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
|
||||||
|
@ -135,6 +135,8 @@ GEM
|
|||||||
formtastic (5.0.0)
|
formtastic (5.0.0)
|
||||||
actionpack (>= 6.0.0)
|
actionpack (>= 6.0.0)
|
||||||
formtastic_i18n (0.7.0)
|
formtastic_i18n (0.7.0)
|
||||||
|
friendly_id (5.5.1)
|
||||||
|
activerecord (>= 4.0.0)
|
||||||
fugit (1.11.1)
|
fugit (1.11.1)
|
||||||
et-orbi (~> 1, >= 1.2.11)
|
et-orbi (~> 1, >= 1.2.11)
|
||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
@ -428,6 +430,7 @@ DEPENDENCIES
|
|||||||
cssbundling-rails
|
cssbundling-rails
|
||||||
debug
|
debug
|
||||||
devise (~> 4.9)
|
devise (~> 4.9)
|
||||||
|
friendly_id (~> 5.5)
|
||||||
jbuilder
|
jbuilder
|
||||||
jsbundling-rails
|
jsbundling-rails
|
||||||
kamal
|
kamal
|
||||||
|
54
app/admin/cities.rb
Normal file
54
app/admin/cities.rb
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
ActiveAdmin.register City 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
|
||||||
|
#
|
||||||
|
# Uncomment all parameters which should be permitted for assignment
|
||||||
|
#
|
||||||
|
permit_params :name, :country, :latitude, :longitude, :active, :priority, :timezone, :region, :last_weather_fetch, :last_image_generation, :slug
|
||||||
|
#
|
||||||
|
# or
|
||||||
|
#
|
||||||
|
# permit_params do
|
||||||
|
# permitted = [:name, :country, :latitude, :longitude, :active, :priority, :timezone, :region, :last_weather_fetch, :last_image_generation, :slug]
|
||||||
|
# permitted << :other if params[:action] == 'create' && current_user.admin?
|
||||||
|
# permitted
|
||||||
|
# end
|
||||||
|
|
||||||
|
index do
|
||||||
|
selectable_column
|
||||||
|
id_column
|
||||||
|
column :name
|
||||||
|
column :slug
|
||||||
|
column :latitude
|
||||||
|
column :longitude
|
||||||
|
column :active
|
||||||
|
column :created_at
|
||||||
|
actions
|
||||||
|
end
|
||||||
|
|
||||||
|
filter :name
|
||||||
|
filter :active
|
||||||
|
|
||||||
|
form do |f|
|
||||||
|
f.inputs do
|
||||||
|
f.input :active
|
||||||
|
f.input :name
|
||||||
|
f.input :country, as: :String
|
||||||
|
f.input :latitude
|
||||||
|
f.input :longitude
|
||||||
|
f.input :priority
|
||||||
|
f.input :timezone
|
||||||
|
f.input :region
|
||||||
|
f.input :last_weather_fetch
|
||||||
|
f.input :last_image_generation
|
||||||
|
end
|
||||||
|
f.actions
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
78
app/admin/weather_arts.rb
Normal file
78
app/admin/weather_arts.rb
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
ActiveAdmin.register WeatherArt do
|
||||||
|
|
||||||
|
# See permitted parameters documentation:
|
||||||
|
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
|
||||||
|
#
|
||||||
|
# Uncomment all parameters which should be permitted for assignment
|
||||||
|
#
|
||||||
|
# permit_params :city_id, :weather_date, :description, :temperature, :feeling_temp, :humidity, :wind_scale, :wind_speed, :precipitation, :pressure, :visibility, :cloud, :prompt
|
||||||
|
#
|
||||||
|
# or
|
||||||
|
#
|
||||||
|
# permit_params do
|
||||||
|
# permitted = [:city_id, :weather_date, :description, :temperature, :feeling_temp, :humidity, :wind_scale, :wind_speed, :precipitation, :pressure, :visibility, :cloud, :prompt]
|
||||||
|
# permitted << :other if params[:action] == 'create' && current_user.admin?
|
||||||
|
# permitted
|
||||||
|
# end
|
||||||
|
permit_params :city_id, :weather_date, :description, :temperature,
|
||||||
|
:feeling_temp, :humidity, :wind_scale, :wind_speed,
|
||||||
|
:precipitation, :pressure, :visibility, :cloud,
|
||||||
|
:prompt, :image
|
||||||
|
|
||||||
|
remove_filter :image_attachment, :image_blob
|
||||||
|
|
||||||
|
index do
|
||||||
|
selectable_column
|
||||||
|
id_column
|
||||||
|
column :city
|
||||||
|
column :weather_date
|
||||||
|
column :description
|
||||||
|
column :temperature
|
||||||
|
column :image do |weather_art|
|
||||||
|
image_tag(weather_art.image, size: '100x100') if weather_art.image.attached?
|
||||||
|
end
|
||||||
|
actions
|
||||||
|
end
|
||||||
|
|
||||||
|
show do
|
||||||
|
attributes_table do
|
||||||
|
row :city
|
||||||
|
row :weather_date
|
||||||
|
row :description
|
||||||
|
row :temperature
|
||||||
|
row :feeling_temp
|
||||||
|
row :humidity
|
||||||
|
row :wind_scale
|
||||||
|
row :wind_speed
|
||||||
|
row :precipitation
|
||||||
|
row :pressure
|
||||||
|
row :visibility
|
||||||
|
row :cloud
|
||||||
|
row :prompt
|
||||||
|
row :image do |weather_art|
|
||||||
|
image_tag(weather_art.image) if weather_art.image.attached?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
form do |f|
|
||||||
|
f.inputs do
|
||||||
|
f.input :city
|
||||||
|
f.input :weather_date
|
||||||
|
f.input :description
|
||||||
|
f.input :temperature
|
||||||
|
f.input :feeling_temp
|
||||||
|
f.input :humidity
|
||||||
|
f.input :wind_scale
|
||||||
|
f.input :wind_speed
|
||||||
|
f.input :precipitation
|
||||||
|
f.input :pressure
|
||||||
|
f.input :visibility
|
||||||
|
f.input :cloud
|
||||||
|
f.input :prompt
|
||||||
|
f.input :image, as: :file
|
||||||
|
end
|
||||||
|
f.actions
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
9
app/controllers/cities_controller.rb
Normal file
9
app/controllers/cities_controller.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
class CitiesController < ApplicationController
|
||||||
|
def index
|
||||||
|
@cities = City.all.order(:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@city = City.friendly.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
6
app/controllers/home_controller.rb
Normal file
6
app/controllers/home_controller.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class HomeController < ApplicationController
|
||||||
|
def index
|
||||||
|
@latest_arts = WeatherArt.includes(:city).order(created_at: :desc).limit(6)
|
||||||
|
@featured_arts = WeatherArt.includes(:city).order(created_at: :desc).limit(5)
|
||||||
|
end
|
||||||
|
end
|
6
app/controllers/weather_arts_controller.rb
Normal file
6
app/controllers/weather_arts_controller.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class WeatherArtsController < ApplicationController
|
||||||
|
def show
|
||||||
|
@city = City.friendly.find(params[:city_id])
|
||||||
|
@weather_art = @city.weather_arts.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
2
app/helpers/cities_helper.rb
Normal file
2
app/helpers/cities_helper.rb
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
module CitiesHelper
|
||||||
|
end
|
2
app/helpers/home_helper.rb
Normal file
2
app/helpers/home_helper.rb
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
module HomeHelper
|
||||||
|
end
|
2
app/helpers/weather_arts_helper.rb
Normal file
2
app/helpers/weather_arts_helper.rb
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
module WeatherArtsHelper
|
||||||
|
end
|
@ -3,4 +3,9 @@ class AdminUser < ApplicationRecord
|
|||||||
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
||||||
devise :database_authenticatable,
|
devise :database_authenticatable,
|
||||||
:recoverable, :rememberable, :validatable
|
:recoverable, :rememberable, :validatable
|
||||||
|
|
||||||
|
def self.ransackable_attributes(auth_object = nil)
|
||||||
|
# 列出你想要允许搜索的属性
|
||||||
|
%w[created_at email id updated_at]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
23
app/models/city.rb
Normal file
23
app/models/city.rb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
class City < ApplicationRecord
|
||||||
|
extend FriendlyId
|
||||||
|
friendly_id :name, use: :slugged
|
||||||
|
|
||||||
|
has_many :weather_arts, dependent: :destroy
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
|
validates :latitude, presence: true
|
||||||
|
validates :longitude, presence: true
|
||||||
|
|
||||||
|
def should_generate_new_friendly_id?
|
||||||
|
name_changed? || super
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.ransackable_associations(auth_object = nil)
|
||||||
|
[ "weather_arts" ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.ransackable_attributes(auth_object = nil)
|
||||||
|
["active", "country", "created_at", "id", "id_value", "last_image_generation", "last_weather_fetch", "latitude", "longitude", "name", "priority", "region", "slug", "timezone", "updated_at"]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
16
app/models/weather_art.rb
Normal file
16
app/models/weather_art.rb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
class WeatherArt < ApplicationRecord
|
||||||
|
belongs_to :city
|
||||||
|
|
||||||
|
has_one_attached :image
|
||||||
|
|
||||||
|
validates :weather_date, presence: true
|
||||||
|
validates :city_id, presence: true
|
||||||
|
|
||||||
|
def self.ransackable_associations(auth_object = nil)
|
||||||
|
["city", "image_attachment", "image_blob"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.ransackable_attributes(auth_object = nil)
|
||||||
|
[ "city_id", "cloud", "created_at", "description", "feeling_temp", "humidity", "id", "id_value", "precipitation", "pressure", "prompt", "temperature", "updated_at", "visibility", "weather_date", "wind_scale", "wind_speed" ]
|
||||||
|
end
|
||||||
|
end
|
18
app/views/cities/index.html.erb
Normal file
18
app/views/cities/index.html.erb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<div class="space-y-8">
|
||||||
|
<h1 class="text-3xl font-bold">Cities</h1>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<% @cities.each do |city| %>
|
||||||
|
<div class="card bg-base-200">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title"><%= city.name %></h2>
|
||||||
|
<p>Latitude: <%= city.latitude %></p>
|
||||||
|
<p>Longitude: <%= city.longitude %></p>
|
||||||
|
<div class="card-actions justify-end">
|
||||||
|
<%= link_to "View Weather Art", city_path(city), class: "btn btn-primary" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
49
app/views/cities/show.html.erb
Normal file
49
app/views/cities/show.html.erb
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<div class="space-y-8">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<h1 class="text-3xl font-bold"><%= @city.name %></h1>
|
||||||
|
<%= link_to "Back to Cities", cities_path, class: "btn btn-ghost" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats shadow">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Latitude</div>
|
||||||
|
<div class="stat-value"><%= @city.latitude %></div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Longitude</div>
|
||||||
|
<div class="stat-value"><%= @city.longitude %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h2 class="text-2xl font-bold">Weather Art History</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<% @city.weather_arts.order(weather_date: :desc).each do |art| %>
|
||||||
|
<div class="card bg-base-200">
|
||||||
|
<figure>
|
||||||
|
<% if art.image.attached? %>
|
||||||
|
<%= image_tag art.image, class: "w-full h-48 object-cover" %>
|
||||||
|
<% end %>
|
||||||
|
</figure>
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title"><%= art.weather_date.strftime("%Y-%m-%d") %></h3>
|
||||||
|
<p><%= art.description %></p>
|
||||||
|
<div class="stats stats-vertical shadow">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Temperature</div>
|
||||||
|
<div class="stat-value"><%= art.temperature %>°C</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Humidity</div>
|
||||||
|
<div class="stat-value"><%= art.humidity %>%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions justify-end">
|
||||||
|
<%= link_to "View Details", city_weather_art_path(@city, art), class: "btn btn-primary" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
46
app/views/home/index.html.erb
Normal file
46
app/views/home/index.html.erb
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<div class="space-y-8">
|
||||||
|
<!-- 头部标题 -->
|
||||||
|
<div class="text-center space-y-4">
|
||||||
|
<h1 class="text-4xl font-bold">AI Weather Art</h1>
|
||||||
|
<p class="text-xl text-base-content/70">Discover the beauty of weather through AI-generated art</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 轮播图 -->
|
||||||
|
<div class="carousel w-full rounded-box">
|
||||||
|
<% WeatherArt.last(5).each_with_index do |art, index| %>
|
||||||
|
<div id="slide<%= index %>" class="carousel-item relative w-full">
|
||||||
|
<% if art.image.attached? %>
|
||||||
|
<%= image_tag art.image, class: "w-full aspect-video object-cover" %>
|
||||||
|
<% end %>
|
||||||
|
<div class="absolute flex justify-between transform -translate-y-1/2 left-5 right-5 top-1/2">
|
||||||
|
<a href="#slide<%= index == 0 ? 4 : index - 1 %>" class="btn btn-circle">❮</a>
|
||||||
|
<a href="#slide<%= index == 4 ? 0 : index + 1 %>" class="btn btn-circle">❯</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 最新天气艺术 -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<h2 class="text-2xl font-bold">Latest Weather Art</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
<% WeatherArt.last(6).each do |art| %>
|
||||||
|
<div class="card bg-base-200">
|
||||||
|
<figure>
|
||||||
|
<% if art.image.attached? %>
|
||||||
|
<%= image_tag art.image, class: "w-full h-48 object-cover" %>
|
||||||
|
<% end %>
|
||||||
|
</figure>
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title"><%= art.city.name %></h3>
|
||||||
|
<p><%= art.weather_date.strftime("%Y-%m-%d") %></p>
|
||||||
|
<p><%= art.description %></p>
|
||||||
|
<div class="card-actions justify-end">
|
||||||
|
<%= link_to "View Details", city_weather_art_path(art.city, art), class: "btn btn-primary" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -18,12 +18,30 @@
|
|||||||
<link rel="apple-touch-icon" href="/icon.png">
|
<link rel="apple-touch-icon" href="/icon.png">
|
||||||
|
|
||||||
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
||||||
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
|
||||||
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
|
||||||
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="min-h-screen bg-base-100">
|
||||||
|
<div class="navbar bg-base-100">
|
||||||
|
<div class="container mx-auto">
|
||||||
|
<div class="flex-1">
|
||||||
|
<%= link_to "AI Weather Art", root_path, class: "btn btn-ghost normal-case text-xl" %>
|
||||||
|
</div>
|
||||||
|
<div class="flex-none">
|
||||||
|
<%= link_to "Cities", cities_path, class: "btn btn-ghost normal-case" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main class="container mx-auto px-4 py-8">
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="footer footer-center p-4 bg-base-200 text-base-content">
|
||||||
|
<div>
|
||||||
|
<p>Copyright © 2024 - All rights reserved by AI Weather Art</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
46
app/views/weather_arts/show.html.erb
Normal file
46
app/views/weather_arts/show.html.erb
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<div class="space-y-8">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<h1 class="text-3xl font-bold"><%= @weather_art.city.name %> - <%= @weather_art.weather_date.strftime("%Y-%m-%d") %></h1>
|
||||||
|
<%= link_to "Back to City", city_path(@weather_art.city), class: "btn btn-ghost" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card lg:card-side bg-base-200 shadow-xl">
|
||||||
|
<figure class="lg:w-1/2">
|
||||||
|
<% if @weather_art.image.attached? %>
|
||||||
|
<%= image_tag @weather_art.image, class: "w-full h-full object-cover" %>
|
||||||
|
<% end %>
|
||||||
|
</figure>
|
||||||
|
<div class="card-body lg:w-1/2">
|
||||||
|
<h2 class="card-title"><%= @weather_art.description %></h2>
|
||||||
|
|
||||||
|
<div class="stats stats-vertical shadow">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Temperature</div>
|
||||||
|
<div class="stat-value"><%= @weather_art.temperature %>°C</div>
|
||||||
|
<div class="stat-desc">Feels like <%= @weather_art.feeling_temp %>°C</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Wind</div>
|
||||||
|
<div class="stat-value"><%= @weather_art.wind_scale %></div>
|
||||||
|
<div class="stat-desc"><%= @weather_art.wind_speed %> km/h</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Humidity</div>
|
||||||
|
<div class="stat-value"><%= @weather_art.humidity %>%</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title">Visibility</div>
|
||||||
|
<div class="stat-value"><%= @weather_art.visibility %> km</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<h3 class="font-bold">AI Prompt:</h3>
|
||||||
|
<p class="text-sm"><%= @weather_art.prompt %></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -4,12 +4,12 @@ ActiveAdmin.setup do |config|
|
|||||||
# Set the title that is displayed on the main layout
|
# Set the title that is displayed on the main layout
|
||||||
# for each of the active admin pages.
|
# for each of the active admin pages.
|
||||||
#
|
#
|
||||||
config.site_title = "Today Ai Weather"
|
config.site_title = "Today Ai Weather Admin"
|
||||||
|
|
||||||
# Set the link url for the title. For example, to take
|
# Set the link url for the title. For example, to take
|
||||||
# users to your main site. Defaults to no link.
|
# users to your main site. Defaults to no link.
|
||||||
#
|
#
|
||||||
# config.site_title_link = "/"
|
config.site_title_link = "/"
|
||||||
|
|
||||||
# Set an optional image to be displayed for the header
|
# Set an optional image to be displayed for the header
|
||||||
# instead of a string (overrides :site_title)
|
# instead of a string (overrides :site_title)
|
||||||
|
107
config/initializers/friendly_id.rb
Normal file
107
config/initializers/friendly_id.rb
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# FriendlyId Global Configuration
|
||||||
|
#
|
||||||
|
# Use this to set up shared configuration options for your entire application.
|
||||||
|
# Any of the configuration options shown here can also be applied to single
|
||||||
|
# models by passing arguments to the `friendly_id` class method or defining
|
||||||
|
# methods in your model.
|
||||||
|
#
|
||||||
|
# To learn more, check out the guide:
|
||||||
|
#
|
||||||
|
# http://norman.github.io/friendly_id/file.Guide.html
|
||||||
|
|
||||||
|
FriendlyId.defaults do |config|
|
||||||
|
# ## Reserved Words
|
||||||
|
#
|
||||||
|
# Some words could conflict with Rails's routes when used as slugs, or are
|
||||||
|
# undesirable to allow as slugs. Edit this list as needed for your app.
|
||||||
|
config.use :reserved
|
||||||
|
|
||||||
|
config.reserved_words = %w[new edit index session login logout users admin
|
||||||
|
stylesheets assets javascripts images]
|
||||||
|
|
||||||
|
# This adds an option to treat reserved words as conflicts rather than exceptions.
|
||||||
|
# When there is no good candidate, a UUID will be appended, matching the existing
|
||||||
|
# conflict behavior.
|
||||||
|
|
||||||
|
# config.treat_reserved_as_conflict = true
|
||||||
|
|
||||||
|
# ## Friendly Finders
|
||||||
|
#
|
||||||
|
# Uncomment this to use friendly finders in all models. By default, if
|
||||||
|
# you wish to find a record by its friendly id, you must do:
|
||||||
|
#
|
||||||
|
# MyModel.friendly.find('foo')
|
||||||
|
#
|
||||||
|
# If you uncomment this, you can do:
|
||||||
|
#
|
||||||
|
# MyModel.find('foo')
|
||||||
|
#
|
||||||
|
# This is significantly more convenient but may not be appropriate for
|
||||||
|
# all applications, so you must explicitly opt-in to this behavior. You can
|
||||||
|
# always also configure it on a per-model basis if you prefer.
|
||||||
|
#
|
||||||
|
# Something else to consider is that using the :finders addon boosts
|
||||||
|
# performance because it will avoid Rails-internal code that makes runtime
|
||||||
|
# calls to `Module.extend`.
|
||||||
|
#
|
||||||
|
# config.use :finders
|
||||||
|
#
|
||||||
|
# ## Slugs
|
||||||
|
#
|
||||||
|
# Most applications will use the :slugged module everywhere. If you wish
|
||||||
|
# to do so, uncomment the following line.
|
||||||
|
#
|
||||||
|
# config.use :slugged
|
||||||
|
#
|
||||||
|
# By default, FriendlyId's :slugged addon expects the slug column to be named
|
||||||
|
# 'slug', but you can change it if you wish.
|
||||||
|
#
|
||||||
|
# config.slug_column = 'slug'
|
||||||
|
#
|
||||||
|
# By default, slug has no size limit, but you can change it if you wish.
|
||||||
|
#
|
||||||
|
# config.slug_limit = 255
|
||||||
|
#
|
||||||
|
# When FriendlyId can not generate a unique ID from your base method, it appends
|
||||||
|
# a UUID, separated by a single dash. You can configure the character used as the
|
||||||
|
# separator. If you're upgrading from FriendlyId 4, you may wish to replace this
|
||||||
|
# with two dashes.
|
||||||
|
#
|
||||||
|
# config.sequence_separator = '-'
|
||||||
|
#
|
||||||
|
# Note that you must use the :slugged addon **prior** to the line which
|
||||||
|
# configures the sequence separator, or else FriendlyId will raise an undefined
|
||||||
|
# method error.
|
||||||
|
#
|
||||||
|
# ## Tips and Tricks
|
||||||
|
#
|
||||||
|
# ### Controlling when slugs are generated
|
||||||
|
#
|
||||||
|
# As of FriendlyId 5.0, new slugs are generated only when the slug field is
|
||||||
|
# nil, but if you're using a column as your base method can change this
|
||||||
|
# behavior by overriding the `should_generate_new_friendly_id?` method that
|
||||||
|
# FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
|
||||||
|
# more like 4.0.
|
||||||
|
# Note: Use(include) Slugged module in the config if using the anonymous module.
|
||||||
|
# If you have `friendly_id :name, use: slugged` in the model, Slugged module
|
||||||
|
# is included after the anonymous module defined in the initializer, so it
|
||||||
|
# overrides the `should_generate_new_friendly_id?` method from the anonymous module.
|
||||||
|
#
|
||||||
|
# config.use :slugged
|
||||||
|
# config.use Module.new {
|
||||||
|
# def should_generate_new_friendly_id?
|
||||||
|
# slug.blank? || <your_column_name_here>_changed?
|
||||||
|
# end
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# FriendlyId uses Rails's `parameterize` method to generate slugs, but for
|
||||||
|
# languages that don't use the Roman alphabet, that's not usually sufficient.
|
||||||
|
# Here we use the Babosa library to transliterate Russian Cyrillic slugs to
|
||||||
|
# ASCII. If you use this, don't forget to add "babosa" to your Gemfile.
|
||||||
|
#
|
||||||
|
# config.use Module.new {
|
||||||
|
# def normalize_friendly_id(text)
|
||||||
|
# text.to_slug.normalize! :transliterations => [:russian, :latin]
|
||||||
|
# end
|
||||||
|
# }
|
||||||
|
end
|
@ -1,4 +1,20 @@
|
|||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
|
root "home#index"
|
||||||
|
|
||||||
|
resources :cities, only: [ :index, :show ] do
|
||||||
|
resources :weather_arts, only: [ :show ]
|
||||||
|
end
|
||||||
|
|
||||||
|
# namespace :admin do
|
||||||
|
# resources :cities
|
||||||
|
# resources :weather_arts
|
||||||
|
# root to: "cities#index"
|
||||||
|
# end
|
||||||
|
|
||||||
|
get "weather_arts/show"
|
||||||
|
get "cities/index"
|
||||||
|
get "cities/show"
|
||||||
|
get "home/index"
|
||||||
devise_for :admin_users, ActiveAdmin::Devise.config
|
devise_for :admin_users, ActiveAdmin::Devise.config
|
||||||
ActiveAdmin.routes(self)
|
ActiveAdmin.routes(self)
|
||||||
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
|
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
|
||||||
|
18
db/migrate/20250119022826_create_cities.rb
Normal file
18
db/migrate/20250119022826_create_cities.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
class CreateCities < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
create_table :cities do |t|
|
||||||
|
t.string :name
|
||||||
|
t.string :country
|
||||||
|
t.float :latitude
|
||||||
|
t.float :longitude
|
||||||
|
t.boolean :active
|
||||||
|
t.integer :priority
|
||||||
|
t.string :timezone
|
||||||
|
t.string :region
|
||||||
|
t.datetime :last_weather_fetch
|
||||||
|
t.datetime :last_image_generation
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
6
db/migrate/20250119032146_add_slug_to_cities.rb
Normal file
6
db/migrate/20250119032146_add_slug_to_cities.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class AddSlugToCities < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :cities, :slug, :string
|
||||||
|
add_index :cities, :slug, unique: true
|
||||||
|
end
|
||||||
|
end
|
21
db/migrate/20250119032158_create_friendly_id_slugs.rb
Normal file
21
db/migrate/20250119032158_create_friendly_id_slugs.rb
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIGRATION_CLASS =
|
||||||
|
if ActiveRecord::VERSION::MAJOR >= 5
|
||||||
|
ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
|
||||||
|
else
|
||||||
|
ActiveRecord::Migration
|
||||||
|
end
|
||||||
|
|
||||||
|
class CreateFriendlyIdSlugs < MIGRATION_CLASS
|
||||||
|
def change
|
||||||
|
create_table :friendly_id_slugs do |t|
|
||||||
|
t.string :slug, null: false
|
||||||
|
t.integer :sluggable_id, null: false
|
||||||
|
t.string :sluggable_type, limit: 50
|
||||||
|
t.string :scope
|
||||||
|
t.datetime :created_at
|
||||||
|
end
|
||||||
|
add_index :friendly_id_slugs, [:sluggable_type, :sluggable_id]
|
||||||
|
add_index :friendly_id_slugs, [:slug, :sluggable_type], length: {slug: 140, sluggable_type: 50}
|
||||||
|
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: {slug: 70, sluggable_type: 50, scope: 70}, unique: true
|
||||||
|
end
|
||||||
|
end
|
21
db/migrate/20250119032316_create_weather_arts.rb
Normal file
21
db/migrate/20250119032316_create_weather_arts.rb
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
class CreateWeatherArts < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
create_table :weather_arts do |t|
|
||||||
|
t.references :city, null: false, foreign_key: true
|
||||||
|
t.date :weather_date
|
||||||
|
t.string :description
|
||||||
|
t.decimal :temperature
|
||||||
|
t.decimal :feeling_temp
|
||||||
|
t.decimal :humidity
|
||||||
|
t.string :wind_scale
|
||||||
|
t.decimal :wind_speed
|
||||||
|
t.decimal :precipitation
|
||||||
|
t.decimal :pressure
|
||||||
|
t.decimal :visibility
|
||||||
|
t.decimal :cloud
|
||||||
|
t.text :prompt
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,57 @@
|
|||||||
|
# This migration comes from active_storage (originally 20170806125915)
|
||||||
|
class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
# Use Active Record's configured type for primary and foreign keys
|
||||||
|
primary_key_type, foreign_key_type = primary_and_foreign_key_types
|
||||||
|
|
||||||
|
create_table :active_storage_blobs, id: primary_key_type do |t|
|
||||||
|
t.string :key, null: false
|
||||||
|
t.string :filename, null: false
|
||||||
|
t.string :content_type
|
||||||
|
t.text :metadata
|
||||||
|
t.string :service_name, null: false
|
||||||
|
t.bigint :byte_size, null: false
|
||||||
|
t.string :checksum
|
||||||
|
|
||||||
|
if connection.supports_datetime_with_precision?
|
||||||
|
t.datetime :created_at, precision: 6, null: false
|
||||||
|
else
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
t.index [ :key ], unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :active_storage_attachments, id: primary_key_type do |t|
|
||||||
|
t.string :name, null: false
|
||||||
|
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
|
||||||
|
t.references :blob, null: false, type: foreign_key_type
|
||||||
|
|
||||||
|
if connection.supports_datetime_with_precision?
|
||||||
|
t.datetime :created_at, precision: 6, null: false
|
||||||
|
else
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
|
||||||
|
t.foreign_key :active_storage_blobs, column: :blob_id
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :active_storage_variant_records, id: primary_key_type do |t|
|
||||||
|
t.belongs_to :blob, null: false, index: false, type: foreign_key_type
|
||||||
|
t.string :variation_digest, null: false
|
||||||
|
|
||||||
|
t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
|
||||||
|
t.foreign_key :active_storage_blobs, column: :blob_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def primary_and_foreign_key_types
|
||||||
|
config = Rails.configuration.generators
|
||||||
|
setting = config.options[config.orm][:primary_key_type]
|
||||||
|
primary_key_type = setting || :primary_key
|
||||||
|
foreign_key_type = setting || :bigint
|
||||||
|
[ primary_key_type, foreign_key_type ]
|
||||||
|
end
|
||||||
|
end
|
81
db/schema.rb
generated
81
db/schema.rb
generated
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 2025_01_18_163460) do
|
ActiveRecord::Schema[8.0].define(version: 2025_01_19_032348) do
|
||||||
create_table "active_admin_comments", force: :cascade do |t|
|
create_table "active_admin_comments", force: :cascade do |t|
|
||||||
t.string "namespace"
|
t.string "namespace"
|
||||||
t.text "body"
|
t.text "body"
|
||||||
@ -25,6 +25,34 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_18_163460) do
|
|||||||
t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource"
|
t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "active_storage_attachments", force: :cascade do |t|
|
||||||
|
t.string "name", null: false
|
||||||
|
t.string "record_type", null: false
|
||||||
|
t.bigint "record_id", null: false
|
||||||
|
t.bigint "blob_id", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
|
||||||
|
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "active_storage_blobs", force: :cascade do |t|
|
||||||
|
t.string "key", null: false
|
||||||
|
t.string "filename", null: false
|
||||||
|
t.string "content_type"
|
||||||
|
t.text "metadata"
|
||||||
|
t.string "service_name", null: false
|
||||||
|
t.bigint "byte_size", null: false
|
||||||
|
t.string "checksum"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "active_storage_variant_records", force: :cascade do |t|
|
||||||
|
t.bigint "blob_id", null: false
|
||||||
|
t.string "variation_digest", null: false
|
||||||
|
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
create_table "admin_users", force: :cascade do |t|
|
create_table "admin_users", force: :cascade do |t|
|
||||||
t.string "email", default: "", null: false
|
t.string "email", default: "", null: false
|
||||||
t.string "encrypted_password", default: "", null: false
|
t.string "encrypted_password", default: "", null: false
|
||||||
@ -36,4 +64,55 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_18_163460) do
|
|||||||
t.index ["email"], name: "index_admin_users_on_email", unique: true
|
t.index ["email"], name: "index_admin_users_on_email", unique: true
|
||||||
t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
|
t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "cities", force: :cascade do |t|
|
||||||
|
t.string "name"
|
||||||
|
t.string "country"
|
||||||
|
t.float "latitude"
|
||||||
|
t.float "longitude"
|
||||||
|
t.boolean "active"
|
||||||
|
t.integer "priority"
|
||||||
|
t.string "timezone"
|
||||||
|
t.string "region"
|
||||||
|
t.datetime "last_weather_fetch"
|
||||||
|
t.datetime "last_image_generation"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.string "slug"
|
||||||
|
t.index ["slug"], name: "index_cities_on_slug", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "friendly_id_slugs", force: :cascade do |t|
|
||||||
|
t.string "slug", null: false
|
||||||
|
t.integer "sluggable_id", null: false
|
||||||
|
t.string "sluggable_type", limit: 50
|
||||||
|
t.string "scope"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
|
||||||
|
t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
|
||||||
|
t.index ["sluggable_type", "sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_type_and_sluggable_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "weather_arts", force: :cascade do |t|
|
||||||
|
t.integer "city_id", null: false
|
||||||
|
t.date "weather_date"
|
||||||
|
t.string "description"
|
||||||
|
t.decimal "temperature"
|
||||||
|
t.decimal "feeling_temp"
|
||||||
|
t.decimal "humidity"
|
||||||
|
t.string "wind_scale"
|
||||||
|
t.decimal "wind_speed"
|
||||||
|
t.decimal "precipitation"
|
||||||
|
t.decimal "pressure"
|
||||||
|
t.decimal "visibility"
|
||||||
|
t.decimal "cloud"
|
||||||
|
t.text "prompt"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["city_id"], name: "index_weather_arts_on_city_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||||
|
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||||
|
add_foreign_key "weather_arts", "cities"
|
||||||
end
|
end
|
||||||
|
35
db/seeds.rb
35
db/seeds.rb
@ -8,3 +8,38 @@
|
|||||||
# MovieGenre.find_or_create_by!(name: genre_name)
|
# MovieGenre.find_or_create_by!(name: genre_name)
|
||||||
# end
|
# end
|
||||||
AdminUser.create!(email: 'admin@example.com', password: 'password', password_confirmation: 'password') if Rails.env.development?
|
AdminUser.create!(email: 'admin@example.com', password: 'password', password_confirmation: 'password') if Rails.env.development?
|
||||||
|
|
||||||
|
guangzhou = City.create!(
|
||||||
|
name: 'Guangzhou',
|
||||||
|
country: 'China',
|
||||||
|
latitude: 23.1291,
|
||||||
|
longitude: 113.2644,
|
||||||
|
active: true,
|
||||||
|
priority: 50,
|
||||||
|
timezone: 'Asia/Shanghai',
|
||||||
|
region: 'Asia',
|
||||||
|
last_weather_fetch: 10.hours.ago,
|
||||||
|
last_image_generation: 10.hours.ago
|
||||||
|
)
|
||||||
|
|
||||||
|
guangzhou_weather_art = WeatherArt.create!(
|
||||||
|
city: guangzhou,
|
||||||
|
weather_date: Date.today,
|
||||||
|
description: 'Sunny with some clouds',
|
||||||
|
temperature: 28.5,
|
||||||
|
feeling_temp: 30.2,
|
||||||
|
humidity: 65,
|
||||||
|
wind_scale: "3级",
|
||||||
|
wind_speed: 15,
|
||||||
|
precipitation: 0,
|
||||||
|
pressure: 1013,
|
||||||
|
visibility: 10,
|
||||||
|
cloud: 30,
|
||||||
|
prompt: "A sunny day in Guangzhou with modern buildings under blue sky and white clouds, digital art style"
|
||||||
|
)
|
||||||
|
|
||||||
|
guangzhou_weather_art.image.attach(
|
||||||
|
io: File.open("db/seeds/images/sample-guangzhou-weather-art.png"),
|
||||||
|
filename: "sample-guangzhou-weather-art.png",
|
||||||
|
content_type: "image/png"
|
||||||
|
)
|
BIN
db/seeds/images/sample-guangzhou-weather-art.png
Normal file
BIN
db/seeds/images/sample-guangzhou-weather-art.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 MiB |
49
package-lock.json
generated
49
package-lock.json
generated
@ -15,6 +15,7 @@
|
|||||||
"tailwindcss": "^3.4.17"
|
"tailwindcss": "^3.4.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"daisyui": "^4.12.23",
|
||||||
"esbuild": "^0.24.2"
|
"esbuild": "^0.24.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -545,6 +546,17 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-selector-tokenizer": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"fastparse": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cssesc": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
@ -557,6 +569,36 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/culori": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/daisyui": {
|
||||||
|
"version": "4.12.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.12.23.tgz",
|
||||||
|
"integrity": "sha512-EM38duvxutJ5PD65lO/AFMpcw+9qEy6XAZrTpzp7WyaPeO/l+F/Qiq0ECHHmFNcFXh5aVoALY4MGrrxtCiaQCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-selector-tokenizer": "^0.8",
|
||||||
|
"culori": "^3",
|
||||||
|
"picocolors": "^1",
|
||||||
|
"postcss-js": "^4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.9.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/daisyui"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
@ -666,6 +708,13 @@
|
|||||||
"node": ">=8.6.0"
|
"node": ">=8.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fastparse": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.18.0",
|
"version": "1.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"name": "app",
|
"name": "app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"daisyui": "^4.12.23",
|
||||||
"esbuild": "^0.24.2"
|
"esbuild": "^0.24.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -4,5 +4,8 @@ module.exports = {
|
|||||||
'./app/helpers/**/*.rb',
|
'./app/helpers/**/*.rb',
|
||||||
'./app/assets/stylesheets/**/*.css',
|
'./app/assets/stylesheets/**/*.css',
|
||||||
'./app/javascript/**/*.js'
|
'./app/javascript/**/*.js'
|
||||||
]
|
],
|
||||||
|
plugins: [
|
||||||
|
require('daisyui'),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
13
test/controllers/cities_controller_test.rb
Normal file
13
test/controllers/cities_controller_test.rb
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class CitiesControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
test "should get index" do
|
||||||
|
get cities_index_url
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should get show" do
|
||||||
|
get cities_show_url
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
end
|
8
test/controllers/home_controller_test.rb
Normal file
8
test/controllers/home_controller_test.rb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class HomeControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
test "should get index" do
|
||||||
|
get home_index_url
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
end
|
8
test/controllers/weather_arts_controller_test.rb
Normal file
8
test/controllers/weather_arts_controller_test.rb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class WeatherArtsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
test "should get show" do
|
||||||
|
get weather_arts_show_url
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
end
|
29
test/fixtures/cities.yml
vendored
Normal file
29
test/fixtures/cities.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||||
|
|
||||||
|
#one:
|
||||||
|
# name: MyString
|
||||||
|
# country: MyString
|
||||||
|
# slug: MyString
|
||||||
|
# latitude: 1.5
|
||||||
|
# longitude: 1.5
|
||||||
|
# active: false
|
||||||
|
# priority: 1
|
||||||
|
# timezone: MyString
|
||||||
|
# population: 1
|
||||||
|
# region: MyString
|
||||||
|
# last_weather_fetch: 2025-01-19 10:28:26
|
||||||
|
# last_image_generation: 2025-01-19 10:28:26
|
||||||
|
#
|
||||||
|
#two:
|
||||||
|
# name: MyString
|
||||||
|
# country: MyString
|
||||||
|
# slug: MyString
|
||||||
|
# latitude: 1.5
|
||||||
|
# longitude: 1.5
|
||||||
|
# active: false
|
||||||
|
# priority: 1
|
||||||
|
# timezone: MyString
|
||||||
|
# population: 1
|
||||||
|
# region: MyString
|
||||||
|
# last_weather_fetch: 2025-01-19 10:28:26
|
||||||
|
# last_image_generation: 2025-01-19 10:28:26
|
31
test/fixtures/weather_arts.yml
vendored
Normal file
31
test/fixtures/weather_arts.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||||
|
|
||||||
|
one:
|
||||||
|
city: one
|
||||||
|
weather_date: 2025-01-19
|
||||||
|
description: MyString
|
||||||
|
temperature: 9.99
|
||||||
|
feeling_temp: 9.99
|
||||||
|
humidity: 9.99
|
||||||
|
wind_scale: MyString
|
||||||
|
wind_speed: 9.99
|
||||||
|
precipitation: 9.99
|
||||||
|
pressure: 9.99
|
||||||
|
visibility: 9.99
|
||||||
|
cloud: 9.99
|
||||||
|
prompt: MyText
|
||||||
|
|
||||||
|
two:
|
||||||
|
city: two
|
||||||
|
weather_date: 2025-01-19
|
||||||
|
description: MyString
|
||||||
|
temperature: 9.99
|
||||||
|
feeling_temp: 9.99
|
||||||
|
humidity: 9.99
|
||||||
|
wind_scale: MyString
|
||||||
|
wind_speed: 9.99
|
||||||
|
precipitation: 9.99
|
||||||
|
pressure: 9.99
|
||||||
|
visibility: 9.99
|
||||||
|
cloud: 9.99
|
||||||
|
prompt: MyText
|
7
test/models/city_test.rb
Normal file
7
test/models/city_test.rb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class CityTest < ActiveSupport::TestCase
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
7
test/models/weather_art_test.rb
Normal file
7
test/models/weather_art_test.rb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class WeatherArtTest < ActiveSupport::TestCase
|
||||||
|
# test "the truth" do
|
||||||
|
# assert true
|
||||||
|
# end
|
||||||
|
end
|
34
yarn.lock
34
yarn.lock
@ -467,11 +467,34 @@ cross-spawn@^7.0.0:
|
|||||||
shebang-command "^2.0.0"
|
shebang-command "^2.0.0"
|
||||||
which "^2.0.1"
|
which "^2.0.1"
|
||||||
|
|
||||||
|
css-selector-tokenizer@^0.8:
|
||||||
|
version "0.8.0"
|
||||||
|
resolved "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz"
|
||||||
|
integrity sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==
|
||||||
|
dependencies:
|
||||||
|
cssesc "^3.0.0"
|
||||||
|
fastparse "^1.1.2"
|
||||||
|
|
||||||
cssesc@^3.0.0:
|
cssesc@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
|
||||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||||
|
|
||||||
|
culori@^3:
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz"
|
||||||
|
integrity sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==
|
||||||
|
|
||||||
|
daisyui@^4.12.23:
|
||||||
|
version "4.12.23"
|
||||||
|
resolved "https://registry.npmjs.org/daisyui/-/daisyui-4.12.23.tgz"
|
||||||
|
integrity sha512-EM38duvxutJ5PD65lO/AFMpcw+9qEy6XAZrTpzp7WyaPeO/l+F/Qiq0ECHHmFNcFXh5aVoALY4MGrrxtCiaQCQ==
|
||||||
|
dependencies:
|
||||||
|
css-selector-tokenizer "^0.8"
|
||||||
|
culori "^3"
|
||||||
|
picocolors "^1"
|
||||||
|
postcss-js "^4"
|
||||||
|
|
||||||
detect-libc@^1.0.3:
|
detect-libc@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
|
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
|
||||||
@ -554,6 +577,11 @@ fast-glob@^3.3.2:
|
|||||||
merge2 "^1.3.0"
|
merge2 "^1.3.0"
|
||||||
micromatch "^4.0.8"
|
micromatch "^4.0.8"
|
||||||
|
|
||||||
|
fastparse@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz"
|
||||||
|
integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
|
||||||
|
|
||||||
fastq@^1.6.0:
|
fastq@^1.6.0:
|
||||||
version "1.18.0"
|
version "1.18.0"
|
||||||
resolved "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz"
|
resolved "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz"
|
||||||
@ -808,7 +836,7 @@ path-scurry@^1.11.1:
|
|||||||
lru-cache "^10.2.0"
|
lru-cache "^10.2.0"
|
||||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||||
|
|
||||||
picocolors@^1.0.1, picocolors@^1.1.1:
|
picocolors@^1, picocolors@^1.0.1, picocolors@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
|
||||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||||
@ -837,7 +865,7 @@ postcss-import@^15.1.0:
|
|||||||
read-cache "^1.0.0"
|
read-cache "^1.0.0"
|
||||||
resolve "^1.1.7"
|
resolve "^1.1.7"
|
||||||
|
|
||||||
postcss-js@^4.0.1:
|
postcss-js@^4, postcss-js@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz"
|
||||||
integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==
|
integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==
|
||||||
@ -928,7 +956,7 @@ run-parallel@^1.1.9:
|
|||||||
|
|
||||||
sass@^1.83.4:
|
sass@^1.83.4:
|
||||||
version "1.83.4"
|
version "1.83.4"
|
||||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.4.tgz#5ccf60f43eb61eeec300b780b8dcb85f16eec6d1"
|
resolved "https://registry.npmjs.org/sass/-/sass-1.83.4.tgz"
|
||||||
integrity sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==
|
integrity sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar "^4.0.0"
|
chokidar "^4.0.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user