feat: add statistics card component and refactor views

- Introduce a new partial for displaying weather statistics in
  a consistent card format.
- Refactor the city show page to utilize the new statistics
  card partial, simplifying the code structure.
- Update layout and styling for improved UX and maintainability.

These changes enhance the maintainability of the codebase by
promoting reuse of the statistics card component and improving
the overall presentation of weather data for cities.
This commit is contained in:
songtianlun 2025-02-14 14:56:51 +08:00
parent f43a6b4698
commit 95f94cb73b
3 changed files with 117 additions and 118 deletions

View File

@ -0,0 +1,13 @@
<!-- app/views/cities/_stat_card.html.erb -->
<div class="stat bg-base-100 shadow-lg rounded-box hover:bg-base-300 transition-all duration-300">
<div class="flex items-center gap-2 mb-2">
<%= weather_stat_icon(stat[:icon]) %>
<div class="stat-title font-medium"><%= stat[:title] %></div>
</div>
<div class="stat-value text-base overflow-x-auto whitespace-nowrap scrollbar-thin scrollbar-thumb-base-300 scrollbar-track-base-100">
<div class="min-w-min pr-2">
<%= stat[:value] %>
</div>
</div>
<div class="stat-desc mt-1"><%= stat[:desc] %></div>
</div>

View File

@ -1,19 +1,18 @@
<div class="relative min-h-screen bg-base-200">
<!-- 背景效果 -->
<% if @city.latest_weather_art&.image&.attached? %>
<div class="fixed inset-0 -z-10">
<%= image_tag @city.latest_weather_art.webp_image.processed,
class: "absolute w-full h-full object-cover scale-110 filter blur-2xl opacity-25" %>
<div class="absolute inset-0 bg-gradient-to-b from-base-200/90 to-base-200/70 backdrop-blur-md"></div>
</div>
<% end %>
<div class="min-h-screen bg-base-100">
<!-- 页面标题和背景 -->
<div class="relative">
<!-- 背景图像和渐变 -->
<% if @city.latest_weather_art&.image&.attached? %>
<div class="absolute inset-0 h-[60vh] overflow-hidden">
<%= image_tag @city.latest_weather_art.webp_image.processed,
class: "w-full h-full object-cover object-center" %>
<div class="absolute inset-0 bg-gradient-to-b from-base-100/40 via-base-100/80 to-base-100"></div>
</div>
<% end %>
<!-- 主要内容 -->
<div class="relative z-10">
<!-- 返回导航 -->
<div class="container mx-auto px-4 py-6">
<div class="relative max-w-3xl mx-auto px-4 pt-4 ">
<%= link_to cities_path,
class: "btn btn-ghost btn-lg gap-2 bg-base-100/50 backdrop-blur-sm hover:bg-base-100/70 transition-all duration-300" do %>
class: "inline-flex items-center btn btn-ghost gap-2 pt-6 mb-8" do %>
<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="M15 19l-7-7 7-7" />
</svg>
@ -21,132 +20,75 @@
<% end %>
</div>
<!-- 城市信息头部 -->
<div class="container mx-auto px-4 mb-12">
<div class="max-w-4xl mx-auto text-center space-y-6">
<h1 class="text-4xl md:text-6xl font-display font-bold">
<span class="bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
<div class="relative pt-8 pb-32">
<div class="container mx-auto px-4">
<div class="max-w-3xl mx-auto text-center space-y-6">
<h1 class="text-5xl md:text-6xl font-display font-bold leading-tight">
<%= @city.localized_name %>
</span>
</h1>
</h1>
<div class="flex flex-wrap justify-center items-center gap-3">
<div class="badge badge-lg badge-primary gap-2">
<%= "#{@city&.country&.emoji + " " || ""}#{@city&.country&.name}" %>, <%= @city.region %>
<div class="flex flex-wrap justify-center items-center gap-3">
<div class="badge badge-lg badge-primary gap-2">
<%= "#{@city&.country&.emoji + " " || ""}#{@city&.country&.name}" %>
</div>
<div class="badge badge-lg badge-secondary gap-2">
<%= @city&.state&.name %>
</div>
</div>
<div class="badge badge-lg badge-secondary gap-2">
<%#= @city.timezone.present? ? Time.current.in_time_zone(@city.timezone).strftime("%Y-%m-%d %H:%M") : "Timezone undefined" %>
<%= @city&.state&.name %>
<!-- 主要统计信息 -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-8">
<%= render partial: 'cities/stat_card', collection: [
{
icon: 'temperature',
title: 'Latest Weather',
value: "#{@city.latest_weather_art&.temperature}°C",
desc: @city.latest_weather_art&.description
},
{
icon: 'location',
title: 'Coordinates',
value: "#{@city.latitude}°N, #{@city.longitude}°E",
desc: 'Geographical Location'
},
{
icon: 'history',
title: 'Records',
value: @city.weather_arts.count,
desc: 'Total Weather Arts'
}
], as: :stat %>
</div>
<%= render 'cities/admin_panel' %>
</div>
<!-- 主要统计信息 -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-8">
<div class="stat bg-base-100/80 backdrop-blur-sm shadow-lg rounded-box hover:bg-base-100/90 transition-all duration-300">
<div class="flex items-center gap-2 mb-2">
<%= weather_stat_icon("temperature") %>
<div class="stat-title font-medium">Latest Weather</div>
</div>
<div class="stat-value text-2xl"><%= @city.latest_weather_art&.temperature %>°C</div>
<div class="stat-desc mt-1"><%= @city.latest_weather_art&.description %></div>
</div>
<div class="stat bg-base-100/80 backdrop-blur-sm shadow-lg rounded-box hover:bg-base-100/90 transition-all duration-300">
<div class="flex items-center gap-2 mb-2">
<%= weather_stat_icon("location") %>
<div class="stat-title font-medium">Coordinates</div>
</div>
<div class="stat-value text-xl">
<%= @city.latitude %>°N,
<%= @city.longitude %>°E
</div>
<div class="stat-desc mt-1">Geographical Location</div>
</div>
<div class="stat bg-base-100/80 backdrop-blur-sm shadow-lg rounded-box hover:bg-base-100/90 transition-all duration-300">
<div class="flex items-center gap-2 mb-2">
<%= weather_stat_icon("history") %>
<div class="stat-title font-medium">Records</div>
</div>
<div class="stat-value text-2xl"><%= @city.weather_arts.count %></div>
<div class="stat-desc mt-1">Total Weather Arts</div>
</div>
</div>
<%= render 'cities/admin_panel' %>
</div>
</div>
</div>
<!-- 天气艺术历史记录 -->
<div class="container mx-auto px-4 pb-16">
<!-- 天气艺术历史记录 -->
<div class="bg-base-100">
<div class="container mx-auto px-4 py-16">
<div class="max-w-7xl mx-auto space-y-8">
<!-- 标题和更新时间 -->
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<h2 class="text-3xl font-display font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Weather Art History
</h2>
<div class="card bg-base-100/80 backdrop-blur-sm shadow-lg p-4">
<h2 class="text-3xl font-display font-bold">Weather Art History</h2>
<div class="card bg-base-100 p-4">
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="font-medium">Last Updated:</span>
<span class="text-base-content/70">
<%= time_ago_in_words(@city.last_weather_fetch) if @city.last_weather_fetch %>
</span>
<span>Last Updated: <%= time_ago_in_words(@city.last_weather_fetch) if @city.last_weather_fetch %></span>
</div>
</div>
</div>
<!-- 天气艺术卡片网格 -->
<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-100/80 backdrop-blur-sm shadow-xl hover:shadow-2xl transition-all duration-300 hover:-translate-y-1">
<figure class="relative aspect-video overflow-hidden">
<% if art.image.attached? %>
<%= image_tag art.preview_image.processed,
class: "w-full h-full object-cover transform hover:scale-105 transition-transform duration-500" %>
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent">
<div class="flex items-center justify-between text-white">
<div class="text-2xl font-bold"><%= art.temperature %>°C</div>
<div class="text-right">
<div class="font-medium"><%= art.formatted_time(:date) %></div>
<div class="text-sm opacity-80"><%= art.formatted_time(:time, true) %></div>
</div>
</div>
</div>
<% end %>
</figure>
<div class="card-body">
<h3 class="card-title font-display">
<%= weather_description_icon(art.description) %>
<%= art.description %>
</h3>
<div class="grid grid-cols-2 gap-4 my-4">
<div class="flex items-center gap-2 text-sm">
<%= weather_stat_icon("humidity") %>
<span>Humidity: <%= art.humidity %>%</span>
</div>
<div class="flex items-center gap-2 text-sm">
<%= weather_stat_icon("wind") %>
<span>Wind: <%= art.wind_scale %></span>
</div>
</div>
<%= link_to city_weather_art_path(@city, art),
class: "btn btn-primary btn-block" do %>
View Details
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
<% end %>
</div>
</div>
<% end %>
<%= render partial: 'weather_arts/card', collection: @city.weather_arts.order(weather_date: :desc), as: :weather_art %>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,44 @@
<!-- app/views/weather_arts/_card.html.erb -->
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 hover:-translate-y-1">
<figure class="relative aspect-video overflow-hidden">
<% if weather_art.image.attached? %>
<%= image_tag weather_art.preview_image.processed,
class: "w-full h-full object-cover transform hover:scale-105 transition-transform duration-500" %>
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent">
<div class="flex items-center justify-between text-white">
<div class="text-2xl font-bold"><%= weather_art.temperature %>°C</div>
<div class="text-right">
<div class="font-medium"><%= weather_art.formatted_time(:date) %></div>
<div class="text-sm opacity-80"><%= weather_art.formatted_time(:time, true) %></div>
</div>
</div>
</div>
<% end %>
</figure>
<div class="card-body">
<h3 class="card-title font-display">
<%= weather_description_icon(weather_art.description) %>
<%= weather_art.description %>
</h3>
<div class="grid grid-cols-2 gap-4 my-4">
<div class="flex items-center gap-2 text-sm">
<%= weather_stat_icon("humidity") %>
<span>Humidity: <%= weather_art.humidity %>%</span>
</div>
<div class="flex items-center gap-2 text-sm">
<%= weather_stat_icon("wind") %>
<span>Wind: <%= weather_art.wind_scale %></span>
</div>
</div>
<%= link_to city_weather_art_path(weather_art.city, weather_art),
class: "btn btn-primary btn-block" do %>
View Details
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
<% end %>
</div>
</div>