From eed8db5865c567dd7258370f811c6f6305ac680f Mon Sep 17 00:00:00 2001 From: Cosmo Date: Wed, 22 Apr 2026 19:35:46 +0000 Subject: [PATCH] fix: wind speed in m/s, redesigned weather modal with hero section and forecast cards --- app/api/weather/route.ts | 2 +- components/TopBar.tsx | 273 +++++++++++++++++++++++---------------- 2 files changed, 166 insertions(+), 109 deletions(-) diff --git a/app/api/weather/route.ts b/app/api/weather/route.ts index a35d359..53ac63e 100644 --- a/app/api/weather/route.ts +++ b/app/api/weather/route.ts @@ -77,7 +77,7 @@ export async function GET() { humidity: String(current.relative_humidity_2m), desc: wmoToDesc(current.weather_code), weatherCode: wmoToWttrCode(current.weather_code), - windSpeed: String(Math.round(current.wind_speed_10m)), + windSpeed: String(Math.round(current.wind_speed_10m / 3.6)), forecast, }); } catch (e) { diff --git a/components/TopBar.tsx b/components/TopBar.tsx index 5f7c3d0..fc54630 100644 --- a/components/TopBar.tsx +++ b/components/TopBar.tsx @@ -1,7 +1,7 @@ 'use client' import { useState, useEffect } from 'react' -import { Droplets, Wind, Thermometer, X } from 'lucide-react' +import { Droplets, Wind, Thermometer, X, CloudRain, Snowflake, CloudLightning, Cloud, Sun, CloudSun } from 'lucide-react' interface WeatherData { temp: string @@ -26,11 +26,13 @@ interface TopBarProps { function getWeatherIcon(desc: string): string { const d = desc?.toLowerCase() || '' if (d.includes('ясно') || d.includes('солнеч')) return '☀️' - if (d.includes('облач')) return '⛅' + if (d.includes('облач') || d.includes('перем')) return '⛅' if (d.includes('пасмурн')) return '☁️' - if (d.includes('дождь') || d.includes('морос')) return '🌧️' - if (d.includes('снег')) return '❄️' + if (d.includes('морос')) return '🌦️' + if (d.includes('дождь') || d.includes('ливен')) return '🌧️' + if (d.includes('снег')) return '🌨️' if (d.includes('гроз')) return '⛈️' + if (d.includes('туман')) return '🌫️' return '🌤️' } @@ -45,6 +47,16 @@ function formatDate(date: Date): string { return `${weekday}, ${day} ${month}` } +function getWindDesc(ms: number): string { + if (ms <= 1) return 'Штиль' + if (ms <= 3) return 'Тихий' + if (ms <= 5) return 'Лёгкий' + if (ms <= 8) return 'Умеренный' + if (ms <= 11) return 'Свежий' + if (ms <= 14) return 'Сильный' + return 'Шторм' +} + export default function TopBar({ weather, sensors }: TopBarProps) { const [time, setTime] = useState(() => new Date()) const [showModal, setShowModal] = useState(false) @@ -54,6 +66,8 @@ export default function TopBar({ weather, sensors }: TopBarProps) { return () => clearInterval(t) }, []) + const windMs = weather ? parseInt(weather.windSpeed) || 0 : 0 + return ( <>
{formatTime(time)} {formatDate(time)} @@ -93,16 +102,11 @@ export default function TopBar({ weather, sensors }: TopBarProps) { {/* Right: sensors + weather */}
- {/* Room sensors */} {sensors && (
@@ -119,20 +123,15 @@ export default function TopBar({ weather, sensors }: TopBarProps) {
)} - {/* Weather widget */} {weather && ( -
- + {/* Hero section */}
- {[ - { icon: , label: 'Влажность', value: `${weather.humidity}%` }, - { icon: , label: 'Ветер', value: `${weather.windSpeed} км/ч` }, - { icon: , label: 'Ощущается', value: `${weather.feelsLike}°` }, - ].map(item => ( -
-
{item.icon}
-
{item.value}
-
{item.label}
+ {/* Background emoji */} +
+ {getWeatherIcon(weather.desc)} +
+ +
+
+ {getWeatherIcon(weather.desc)} +
+
+ {weather.temp}° +
+
+ {weather.desc} +
+
- ))} + +
- {weather.forecast && weather.forecast.length > 0 && ( - <> -
- Прогноз -
-
- {weather.forecast.map(day => { - const d = new Date(day.date) - const label = d.toLocaleDateString('ru-RU', { weekday: 'short', day: 'numeric', month: 'short' }) - return ( -
- {label} - {getWeatherIcon(day.desc)} - {day.desc} - - {day.maxTemp}° / {day.minTemp}° - -
- ) - })} -
- - )} + {/* Details */} +
+ + {/* Stats grid */} +
+ {[ + { + icon: , + bg: 'rgba(251,146,60,0.1)', + label: 'Ощущается', + value: `${weather.feelsLike}°`, + }, + { + icon: , + bg: 'rgba(59,130,246,0.1)', + label: 'Влажность', + value: `${weather.humidity}%`, + }, + { + icon: , + bg: 'rgba(34,211,238,0.1)', + label: getWindDesc(windMs), + value: `${weather.windSpeed} м/с`, + }, + ].map(item => ( +
+
+ {item.icon} +
+
{item.value}
+
{item.label}
+
+ ))} +
+ + {/* Forecast */} + {weather.forecast && weather.forecast.length > 0 && ( + <> +
+ Прогноз на ближайшие дни +
+
+ {weather.forecast.map(day => { + const d = new Date(day.date) + const isToday = d.toDateString() === new Date().toDateString() + const weekday = d.toLocaleDateString('ru-RU', { weekday: 'long' }) + const dateStr = d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' }) + return ( +
+ {/* Icon */} + {getWeatherIcon(day.desc)} + + {/* Day info */} +
+
+ {isToday ? 'Сегодня' : weekday} +
+
+ {dateStr} · {day.desc} +
+
+ + {/* Temps */} +
+ + {day.maxTemp}° + + + {day.minTemp}° + +
+
+ ) + })} +
+ + )} +
)}