feat: add font styling and enhance city views

- Import Playfair Display and Raleway fonts for better typography
- Add a method to `City` model for fetching the latest weather art
- Revamp city index and show pages for improved layout and usability
- Update styles in the layout and main pages to enhance user experience

These changes promote visual consistency and enhance user interaction within the platform, providing a more engaging experience.
This commit is contained in:
songtianlun 2025-01-20 18:02:28 +08:00
parent a533390356
commit d570f43f95
10 changed files with 290 additions and 128 deletions

View File

@ -1,3 +1,8 @@
// Entry point for the build script in your package.json
import "@hotwired/turbo-rails"
import "@fontsource/playfair-display/400.css";
import "@fontsource/playfair-display/700.css";
import "@fontsource/raleway/400.css";
import "@fontsource/raleway/600.css";
import "./controllers"

View File

@ -20,4 +20,8 @@ class City < ApplicationRecord
["active", "country", "created_at", "id", "id_value", "last_image_generation", "last_weather_fetch", "latitude", "longitude", "name", "priority", "region", "slug", "timezone", "updated_at"]
end
def latest_weather_art
weather_arts.order(weather_date: :desc).first
end
end

View File

@ -1,15 +1,32 @@
<div class="space-y-8">
<h1 class="text-3xl font-bold">Cities</h1>
<div class="container mx-auto px-4 py-16">
<div class="text-center mb-16 space-y-4">
<h1 class="text-4xl md:text-5xl font-display font-bold">Explore Cities</h1>
<p class="text-xl text-base-content/70 max-w-2xl mx-auto">
Discover AI-generated weather art from cities around the world
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<% @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" %>
<% latest_art = city.weather_arts.last %>
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300 group">
<% if latest_art&.image&.attached? %>
<figure class="relative aspect-[16/9] overflow-hidden">
<%= image_tag latest_art.image,
class: "w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-500" %>
<div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent"></div>
</figure>
<% end %>
<div class="card-body relative">
<h2 class="card-title font-display text-2xl"><%= city.name %></h2>
<div class="text-base-content/70">
<p>Lat: <%= city.latitude %></p>
<p>Long: <%= city.longitude %></p>
</div>
<div class="card-actions justify-end mt-4">
<%= link_to "View Weather Art", city_path(city),
class: "btn btn-primary" %>
</div>
</div>
</div>

View File

@ -1,49 +1,91 @@
<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="min-h-screen">
<!-- 城市头部信息 -->
<section class="relative h-[40vh] overflow-hidden">
<% if @city.latest_weather_art&.image&.attached? %>
<%= image_tag @city.latest_weather_art.image,
class: "w-full h-full object-cover" %>
<div class="absolute inset-0 bg-gradient-to-t from-base-100 via-base-100/50 to-transparent"></div>
<% end %>
<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" %>
<div class="absolute inset-0 flex items-center">
<div class="container mx-auto px-4">
<div class="max-w-4xl">
<div class="flex items-center space-x-4 mb-4">
<%= link_to cities_path,
class: "btn btn-ghost btn-circle" do %>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" 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>
<% 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>
<h1 class="text-4xl md:text-5xl font-display font-bold"><%= @city.name %></h1>
</div>
<div class="stats bg-base-100/80 backdrop-blur-sm shadow">
<div class="stat">
<div class="stat-title">Latitude</div>
<div class="stat-value text-2xl"><%= @city.latitude %></div>
</div>
<div class="card-actions justify-end">
<%= link_to "View Details", city_weather_art_path(@city, art), class: "btn btn-primary" %>
<div class="stat">
<div class="stat-title">Longitude</div>
<div class="stat-value text-2xl"><%= @city.longitude %></div>
</div>
<div class="stat">
<div class="stat-title">Weather Arts</div>
<div class="stat-value text-2xl"><%= @city.weather_arts.count %></div>
</div>
</div>
</div>
<% end %>
</div>
</div>
</div>
</section>
<!-- 天气艺术历史记录 -->
<section class="container mx-auto px-4 py-16">
<div class="space-y-8">
<h2 class="text-3xl font-display font-bold">Weather Art History</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<% @city.weather_arts.order(weather_date: :desc).each do |art| %>
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300">
<figure class="relative aspect-[4/3] overflow-hidden">
<% if art.image.attached? %>
<%= image_tag art.image,
class: "w-full h-full object-cover transform hover:scale-105 transition-transform duration-500" %>
<% end %>
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/60 to-transparent">
<div class="text-white">
<div class="text-2xl font-bold"><%= art.temperature %>°C</div>
<div class="text-sm opacity-90"><%= art.weather_date.strftime("%B %d, %Y") %></div>
</div>
</div>
</figure>
<div class="card-body">
<h3 class="card-title font-display"><%= art.description %></h3>
<div class="grid grid-cols-2 gap-4 my-4 text-sm">
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" />
</svg>
<span>Humidity: <%= art.humidity %>%</span>
</div>
<div class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
<span>Wind: <%= art.wind_scale %></span>
</div>
</div>
<div class="card-actions justify-end">
<%= link_to "View Details", city_weather_art_path(@city, art),
class: "btn btn-primary btn-outline" %>
</div>
</div>
</div>
<% end %>
</div>
</div>
</section>
</div>

View File

@ -1,46 +1,60 @@
<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>
<!-- 首屏展示区 -->
<section class="h-screen-90 relative overflow-hidden">
<% if @featured_arts.first&.image&.attached? %>
<div class="absolute inset-0">
<%= image_tag @featured_arts.first.image, class: "w-full h-full object-cover" %>
<div class="absolute inset-0 bg-gradient-to-r from-base-100/90 to-base-100/50"></div>
</div>
<% end %>
</div>
<div class="container mx-auto px-4 h-full flex items-center relative">
<div class="max-w-2xl space-y-6">
<h1 class="text-5xl md:text-6xl font-display font-bold leading-tight">
Where Weather Meets<br>Artificial Intelligence
</h1>
<p class="text-xl text-base-content/70 font-sans">
Experience weather through the lens of AI-generated art,
bringing a new perspective to daily meteorological phenomena.
</p>
<%= link_to "Explore Cities", cities_path,
class: "btn btn-primary btn-lg mt-8 font-sans" %>
</div>
</div>
</section>
<!-- 最新天气艺术 -->
<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>
<section class="container mx-auto px-4 py-16 space-y-12">
<h2 class="text-3xl font-display font-bold text-center">Latest Weather Art</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<% @latest_arts.each do |art| %>
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow duration-300">
<figure class="relative aspect-[4/3] overflow-hidden">
<% if art.image.attached? %>
<%= image_tag art.image, class: "w-full h-48 object-cover" %>
<%= image_tag art.image, class: "w-full h-full object-cover transform hover:scale-105 transition-transform duration-500" %>
<% 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 class="flex justify-between items-start">
<div>
<h3 class="card-title font-display"><%= art.city.name %></h3>
<p class="text-base-content/70">
<%= art.weather_date.strftime("%B %d, %Y") %>
</p>
</div>
<div class="text-right">
<div class="text-2xl font-bold"><%= art.temperature %>°C</div>
<div class="text-sm text-base-content/70"><%= art.description %></div>
</div>
</div>
<div class="card-actions justify-end mt-4">
<%= link_to "View Details", city_weather_art_path(art.city, art),
class: "btn btn-primary btn-outline" %>
</div>
</div>
</div>
<% end %>
</div>
</div>
</section>
</div>

View File

@ -22,25 +22,30 @@
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
</head>
<body class="min-h-screen bg-base-100">
<div class="navbar bg-base-100">
<body class="min-h-screen bg-base-100 font-sans">
<!-- 导航栏 -->
<div class="navbar bg-base-100/80 backdrop-blur-sm fixed top-0 z-50">
<div class="container mx-auto">
<div class="flex-1">
<%= link_to "AI Weather Art", root_path, class: "btn btn-ghost normal-case text-xl" %>
<%= link_to root_path, class: "text-2xl font-display font-bold hover:text-primary transition-colors" do %>
AI Weather Art
<% end %>
</div>
<div class="flex-none">
<%= link_to "Cities", cities_path, class: "btn btn-ghost normal-case" %>
<%= link_to "Cities", cities_path, class: "btn btn-ghost font-sans" %>
</div>
</div>
</div>
<main class="container mx-auto px-4 py-8">
<!-- 主要内容 -->
<main class="pt-16">
<%= yield %>
</main>
<footer class="footer footer-center p-4 bg-base-200 text-base-content">
<!-- 页脚 -->
<footer class="footer footer-center p-8 bg-base-200 text-base-content mt-16">
<div>
<p>Copyright © 2024 - All rights reserved by AI Weather Art</p>
<p class="font-display">Copyright © 2024 - All rights reserved by AI Weather Art</p>
</div>
</footer>
</body>

View File

@ -1,45 +1,83 @@
<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 class="min-h-screen">
<!-- 返回导航 -->
<div class="container mx-auto px-4 py-8">
<%= link_to city_path(@weather_art.city),
class: "btn btn-ghost gap-2" 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>
Back to <%= @weather_art.city.name %>
<% end %>
</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 class="container mx-auto px-4 pb-16">
<div class="max-w-6xl mx-auto">
<!-- 头部信息 -->
<div class="text-center space-y-4 mb-12">
<h1 class="text-4xl md:text-5xl font-display font-bold">
<%= @weather_art.city.name %>
</h1>
<p class="text-xl text-base-content/70">
<%= @weather_art.weather_date.strftime("%B %d, %Y") %>
</p>
</div>
<div class="mt-4">
<h3 class="font-bold">AI Prompt:</h3>
<p class="text-sm"><%= @weather_art.prompt %></p>
<!-- 主要卡片 -->
<div class="card lg:card-side bg-base-100 shadow-2xl">
<figure class="lg:w-1/2 relative aspect-square lg:aspect-auto">
<% 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 font-display text-2xl mb-6">
<%= @weather_art.description %>
</h2>
<!-- 天气数据网格 -->
<div class="grid grid-cols-2 gap-6">
<div class="stat bg-base-200 rounded-box">
<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 bg-base-200 rounded-box">
<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 bg-base-200 rounded-box">
<div class="stat-title">Humidity</div>
<div class="stat-value"><%= @weather_art.humidity %>%</div>
</div>
<div class="stat bg-base-200 rounded-box">
<div class="stat-title">Visibility</div>
<div class="stat-value"><%= @weather_art.visibility %> km</div>
</div>
<div class="stat bg-base-200 rounded-box">
<div class="stat-title">Pressure</div>
<div class="stat-value"><%= @weather_art.pressure %> hPa</div>
</div>
<div class="stat bg-base-200 rounded-box">
<div class="stat-title">Cloud Cover</div>
<div class="stat-value"><%= @weather_art.cloud %>%</div>
</div>
</div>
<!-- AI Prompt -->
<div class="mt-8 bg-base-200 p-6 rounded-box">
<h3 class="font-display font-bold text-lg mb-3">AI Prompt</h3>
<p class="text-base-content/70"><%= @weather_art.prompt %></p>
</div>
</div>
</div>
</div>
</div>

View File

@ -11,6 +11,8 @@
},
"dependencies": {
"@activeadmin/activeadmin": "^3.2.5",
"@fontsource/playfair-display": "^5.1.1",
"@fontsource/raleway": "^5.1.1",
"@hotwired/stimulus": "^3.2.2",
"@hotwired/turbo-rails": "^8.0.12",
"autoprefixer": "^10.4.20",

View File

@ -5,7 +5,32 @@ module.exports = {
'./app/assets/stylesheets/**/*.css',
'./app/javascript/**/*.js'
],
theme: {
extend: {
fontFamily: {
'display': ['"Playfair Display"', 'serif'],
'sans': ['Raleway', 'sans-serif'],
},
height: {
'screen-90': '90vh',
}
},
},
plugins: [
require('daisyui'),
],
daisyui: {
themes: [
{
light: {
...require("daisyui/src/theming/themes")["[data-theme=light]"],
"primary": "#2A4365",
"secondary": "#805AD5",
"accent": "#38B2AC",
"neutral": "#2D3748",
"base-100": "#F7FAFC",
},
},
],
},
}

View File

@ -141,6 +141,16 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b"
integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==
"@fontsource/playfair-display@^5.1.1":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@fontsource/playfair-display/-/playfair-display-5.1.1.tgz#5ff331317a84120794ce2a377d2a2032852984a4"
integrity sha512-BCIovoAYzgs6UIve00tjutzEDJ+iP+nMOHQP6joSmheE3WGM7m8JU/btq/iUOjV9HK5E4F8NgzdRIPfi1kWzjA==
"@fontsource/raleway@^5.1.1":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@fontsource/raleway/-/raleway-5.1.1.tgz#7b6cde17ab807407abc367ebaf16c8b4c7d18fb9"
integrity sha512-gCLRJLqdUYI7BDSzvoMFOSNvS4NXUIqSdzQ7PTEqoQDHWLTcoA0xhXgljivmVbJ2ziJq1NZO7tYpDLMlIFe66g==
"@hotwired/stimulus@^3.2.2":
version "3.2.2"
resolved "https://registry.npmjs.org/@hotwired/stimulus/-/stimulus-3.2.2.tgz"