feat: new layout, rooms row, fix weather+HA, fix BottomNav overflow
All checks were successful
Deploy to Coolify / deploy (push) Successful in 5s

- Remove TasksCard and SavingsCard from home tab
- New grid layout: lights+thermostat row 1, purifier+weather row 2
- Add RoomsRow component with room navigation
- Fix HA entity mapping: fan.zhimi_rmb1_9528_air_purifier → fan.air_purifier
- Add real entity aliases for HA route
- Fix weather route: add timeout, better error handling
- Fix BottomNav: use 100dvh + flex-shrink-0
- TopBar: accept isDemo prop, show Demo badge in header
- WeatherCard: compact prop, better loading/error states
- globals.css: add no-scrollbar utility
This commit is contained in:
Cosmo
2026-04-22 10:33:20 +00:00
parent ecf69400f6
commit 088cd35ea6
7 changed files with 375 additions and 152 deletions

View File

@@ -1,27 +1,52 @@
export const dynamic = 'force-dynamic';
import { NextResponse } from "next/server";
function getWeatherCode(desc: string): string {
const d = desc.toLowerCase();
if (d.includes("overcast")) return "122";
if (d.includes("partly cloudy") || d.includes("partly") ) return "116";
if (d.includes("cloudy") || d.includes("cloud")) return "119";
if (d.includes("drizzle")) return "185";
if (d.includes("rain") || d.includes("shower")) return "305";
if (d.includes("snow") || d.includes("blizzard")) return "230";
if (d.includes("thunder") || d.includes("storm")) return "200";
if (d.includes("fog") || d.includes("mist")) return "248";
if (d.includes("sunny") || d.includes("clear")) return "113";
return "116";
}
export async function GET() {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
const res = await fetch(
"https://wttr.in/Saint+Petersburg?format=j1",
{
next: { revalidate: 600 },
headers: { "User-Agent": "SmartHomeDashboard/1.0" },
signal: controller.signal,
cache: "no-store",
headers: {
"User-Agent": "SmartHomeDashboard/1.0",
"Accept": "application/json",
},
}
);
if (!res.ok) throw new Error("Weather fetch failed");
clearTimeout(timeout);
if (!res.ok) throw new Error(`wttr responded ${res.status}`);
const data = await res.json();
const current = data.current_condition[0];
const days = data.weather.slice(0, 3).map((day: any) => {
const desc = day.hourly[4]?.weatherDesc?.[0]?.value || "";
const days = (data.weather || []).slice(0, 3).map((day: any) => {
const hourly = day.hourly?.[4] || day.hourly?.[0] || {};
const desc = hourly.weatherDesc?.[0]?.value || "";
const code = hourly.weatherCode || getWeatherCode(desc);
return {
date: day.date,
maxTemp: day.maxtempC,
minTemp: day.mintempC,
desc,
weatherCode: day.hourly[4]?.weatherCode || "113",
weatherCode: String(code),
};
});
@@ -30,20 +55,22 @@ export async function GET() {
feelsLike: current.FeelsLikeC,
humidity: current.humidity,
desc: current.weatherDesc[0]?.value || "",
weatherCode: current.weatherCode,
weatherCode: String(current.weatherCode),
windSpeed: current.windspeedKmph,
forecast: days,
});
} catch (e) {
console.error("Weather fetch error:", e);
return NextResponse.json(
{
temp: "—",
feelsLike: "—",
humidity: "—",
desc: "Нет данных",
weatherCode: "113",
weatherCode: "116",
windSpeed: "—",
forecast: [],
error: String(e),
},
{ status: 200 }
);