diff --git a/app/globals.css b/app/globals.css index 0c7f81f..9529074 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,24 +1,24 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap'); @tailwind base; @tailwind components; @tailwind utilities; :root { - --bg: #0a0a0f; - --card-bg: rgba(255, 255, 255, 0.05); + --bg: #090912; + --card-bg: rgba(255, 255, 255, 0.04); --card-border: rgba(255, 255, 255, 0.08); --text-primary: rgba(255, 255, 255, 0.95); - --text-secondary: rgba(255, 255, 255, 0.5); + --text-secondary: rgba(255, 255, 255, 0.45); --accent: #6366f1; --accent-2: #8b5cf6; } .light { --bg: #f0f0f8; - --card-bg: rgba(255, 255, 255, 0.8); - --card-border: rgba(0, 0, 0, 0.08); + --card-bg: rgba(255, 255, 255, 0.75); + --card-border: rgba(0, 0, 0, 0.07); --text-primary: rgba(15, 15, 30, 0.95); - --text-secondary: rgba(15, 15, 30, 0.5); + --text-secondary: rgba(15, 15, 30, 0.45); } * { @@ -49,7 +49,7 @@ body { border: 1px solid var(--card-border); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); - border-radius: 20px; + border-radius: 24px; transition: background 0.3s ease, border-color 0.3s ease; } @@ -61,34 +61,35 @@ body { background: transparent; } ::-webkit-scrollbar-thumb { - background: rgba(99, 102, 241, 0.4); + background: rgba(99, 102, 241, 0.35); border-radius: 2px; } -/* Custom toggle switch */ +/* Big toggle switch (60×32) */ .toggle-track { position: relative; - width: 52px; - height: 28px; - border-radius: 14px; + width: 60px; + height: 32px; + border-radius: 16px; cursor: pointer; transition: background-color 0.3s ease; + flex-shrink: 0; } .toggle-thumb { position: absolute; top: 3px; left: 3px; - width: 22px; - height: 22px; + width: 26px; + height: 26px; border-radius: 50%; background: white; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35); transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } .toggle-on .toggle-thumb { - transform: translateX(24px); + transform: translateX(28px); } /* Custom range slider */ @@ -113,9 +114,9 @@ input[type='range']::-webkit-slider-thumb { width: 20px; height: 20px; border-radius: 50%; - background: #6366f1; + background: #f59e0b; cursor: pointer; - box-shadow: 0 0 8px rgba(99, 102, 241, 0.6); + box-shadow: 0 0 10px rgba(245, 158, 11, 0.7); transition: transform 0.15s ease; } @@ -127,10 +128,10 @@ input[type='range']::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%; - background: #6366f1; + background: #f59e0b; cursor: pointer; border: none; - box-shadow: 0 0 8px rgba(99, 102, 241, 0.6); + box-shadow: 0 0 10px rgba(245, 158, 11, 0.7); } /* Ambient orbs */ @@ -150,48 +151,78 @@ input[type='range']::-moz-range-thumb { /* Progress bar */ .progress-bar { - height: 8px; - border-radius: 4px; - background: rgba(255, 255, 255, 0.08); + height: 10px; + border-radius: 5px; + background: rgba(255, 255, 255, 0.07); overflow: hidden; } .light .progress-bar { - background: rgba(0, 0, 0, 0.08); + background: rgba(0, 0, 0, 0.07); } .progress-fill { height: 100%; - border-radius: 4px; + border-radius: 5px; background: linear-gradient(90deg, #6366f1, #8b5cf6); - transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); + transition: width 0.8s cubic-bezier(0.34, 1.56, 0.64, 1); } /* Glow effects */ -.glow-indigo { - box-shadow: 0 0 20px rgba(99, 102, 241, 0.3); -} - -.glow-emerald { - box-shadow: 0 0 20px rgba(16, 185, 129, 0.3); -} - -.glow-rose { - box-shadow: 0 0 20px rgba(244, 63, 94, 0.3); -} - .glow-amber { - box-shadow: 0 0 20px rgba(245, 158, 11, 0.3); + box-shadow: 0 0 24px rgba(245, 158, 11, 0.4); +} + +.glow-blue { + box-shadow: 0 0 24px rgba(59, 130, 246, 0.4); +} + +.glow-green { + box-shadow: 0 0 24px rgba(16, 185, 129, 0.4); +} + +.glow-purple { + box-shadow: 0 0 24px rgba(139, 92, 246, 0.4); } /* Modal backdrop */ .modal-backdrop { position: fixed; inset: 0; - background: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(8px); + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(10px); z-index: 100; display: flex; align-items: center; justify-content: center; } + +/* Spin animation for air purifier */ +@keyframes spin-slow { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.spin-slow { + animation: spin-slow 4s linear infinite; +} + +/* Orb animations */ +@keyframes orbMove1 { + 0%, 100% { transform: translate(0, 0) scale(1); } + 33% { transform: translate(40px, -30px) scale(1.05); } + 66% { transform: translate(-20px, 20px) scale(0.97); } +} +@keyframes orbMove2 { + 0%, 100% { transform: translate(0, 0) scale(1); } + 33% { transform: translate(-50px, 30px) scale(1.08); } + 66% { transform: translate(30px, -20px) scale(0.95); } +} +@keyframes orbMove3 { + 0%, 100% { transform: translate(0, 0) scale(1); } + 50% { transform: translate(20px, 40px) scale(1.04); } +} +@keyframes orbMove4 { + 0%, 100% { transform: translate(0, 0) scale(1); } + 50% { transform: translate(-30px, -20px) scale(1.06); } +} diff --git a/app/page.tsx b/app/page.tsx index 2ee4489..8c36e7f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -10,8 +10,20 @@ import AirPurifierCard from "@/components/cards/AirPurifierCard"; import TasksCard from "@/components/cards/TasksCard"; import WeatherCard from "@/components/cards/WeatherCard"; import SavingsCard from "@/components/cards/SavingsCard"; +import WeatherSavingsCard from "@/components/cards/WeatherSavingsCard"; import { useHA, useWeather, useTasks, useSavings } from "@/hooks/useHA"; +// Stagger container variants +const containerVariants = { + hidden: {}, + visible: { transition: { staggerChildren: 0.07 } }, +}; + +const cardVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { opacity: 1, y: 0, transition: { duration: 0.35 } }, +}; + export default function Home() { const [isDark, setIsDark] = useState(true); const [activeTab, setActiveTab] = useState("home"); @@ -54,39 +66,47 @@ export default function Home() { > {/* Ambient orbs */}
+
@@ -109,7 +129,7 @@ export default function Home() { exit={{ opacity: 0 }} > + {/* ═══════════════ HOME TAB ═══════════════ */} {activeTab === "home" && ( - {/* Row 1 */} - {/* Свет Гостиная */} - + {/* 3-col grid: + Row 1: [Свет] [Климат] [Задачи] + Row 2: [Воздух ×2] [Погода+Накопления] + */} +
+ {/* Свет Гостиная */} + + + - {/* Свет Спальня */} - + {/* Термостат */} + + + - {/* Температура */} - + {/* Задачи */} + + + - {/* Очиститель воздуха */} - + {/* Очиститель воздуха — 2 колонки */} + + + - {/* Row 2 */} - {/* Задачи — 2 колонки */} -
- + {/* Погода + Накопления — правый нижний */} + + +
- - {/* Погода */} - - - {/* Накопления */} - )} + {/* ═══════════════ DEVICES TAB ═══════════════ */} {activeTab === "devices" && ( - - - -
+ + + + + + + + + + -
+
)} + {/* ═══════════════ TASKS TAB ═══════════════ */} {activeTab === "tasks" && ( )} + {/* ═══════════════ SETTINGS TAB ═══════════════ */} {activeTab === "settings" && ( {[ - { - label: "HA URL", - value: - process.env.NEXT_PUBLIC_APP_URL - ? "Настроен" - : "http://192.168.31.110:8123", - }, + { label: "HA URL", value: "Настроен" }, { label: "HA Token", value: isDemo ? "❌ Не настроен" : "✅ Настроен" }, { label: "Vikunja", value: "✅ Подключён" }, { label: "Pulse API", value: "✅ Подключён" }, ].map((item) => (
- + {item.label} - + {item.value}
diff --git a/components/BottomNav.tsx b/components/BottomNav.tsx index 3c7d66e..5373c1c 100644 --- a/components/BottomNav.tsx +++ b/components/BottomNav.tsx @@ -9,16 +9,17 @@ interface Props { } const TABS = [ - { id: "home", label: "Главная", icon: Home }, - { id: "devices", label: "Устройства", icon: Cpu }, - { id: "tasks", label: "Задачи", icon: CheckSquare }, - { id: "settings", label: "Настройки", icon: Settings }, + { id: "home", label: "Главная", icon: Home, color: "#6366f1" }, + { id: "devices", label: "Устройства", icon: Cpu, color: "#3b82f6" }, + { id: "tasks", label: "Задачи", icon: CheckSquare, color: "#8b5cf6" }, + { id: "settings", label: "Настройки", icon: Settings, color: "#10b981" }, ]; export default function BottomNav({ active, onChange }: Props) { return ( onChange(tab.id)} - className="flex flex-col items-center gap-1 px-6 py-2 rounded-xl relative" - whileTap={{ scale: 0.88 }} - style={{ - background: isActive - ? "rgba(99,102,241,0.15)" - : "transparent", - }} + className="flex flex-col items-center gap-1.5 px-8 py-2 rounded-2xl relative" + whileTap={{ scale: 0.85 }} > {isActive && ( )} {tab.label} diff --git a/components/TopBar.tsx b/components/TopBar.tsx index d524ba0..64d85c7 100644 --- a/components/TopBar.tsx +++ b/components/TopBar.tsx @@ -36,7 +36,7 @@ export default function TopBar({ isDark, onToggleTheme, weather }: Props) { ); setDate( now.toLocaleDateString("ru-RU", { - weekday: "long", + weekday: "short", day: "numeric", month: "long", }) @@ -50,6 +50,7 @@ export default function TopBar({ isDark, onToggleTheme, weather }: Props) { return ( {time} @@ -70,41 +71,35 @@ export default function TopBar({ isDark, onToggleTheme, weather }: Props) {
- {/* Weather */} + {/* Weather pill */} {weather && ( - + {getWeatherEmoji(weather.weatherCode)}
{weather.temp}°C
- Ощущается {weather.feelsLike}° + {weather.desc}
-
- {weather.desc} -
)} @@ -112,7 +107,7 @@ export default function TopBar({ isDark, onToggleTheme, weather }: Props) {
{weather?.demo && ( m.id === currentMode)?.color || "#06b6d4"; + const activeMode = MODES.find((m) => m.id === currentMode) || MODES[0]; + const accentColor = isOn ? activeMode.color : "rgba(255,255,255,0.3)"; return ( -
-
-
+ {/* Animated icon */} +
+ - - - -
+ + +
+ +
Очиститель воздуха
- {isOn ? currentMode : "Выключен"} + {isOn ? activeMode.label : "Выключен"}
+ {/* Toggle */}
- {isOn && ( - + {/* Mode buttons */} + + {isOn && ( + + {MODES.map((mode) => { + const isActive = currentMode === mode.id; + return ( + handleMode(mode.id)} + className="flex-1 py-2.5 rounded-2xl text-sm font-semibold" + style={ + isActive + ? { + background: `${mode.color}22`, + border: `1.5px solid ${mode.color}60`, + color: mode.color, + boxShadow: `0 0 14px ${mode.color}30`, + } + : { + background: "rgba(255,255,255,0.05)", + border: "1px solid rgba(255,255,255,0.08)", + color: "var(--text-secondary)", + } + } + whileTap={{ scale: 0.88 }} + > + {mode.label} + + ); + })} + + )} + + + {/* Offline state bottom fill */} + {!isOn && ( +
{MODES.map((mode) => ( - handleMode(mode.id)} - className="flex-1 py-2 rounded-xl text-xs font-medium" - style={ - currentMode === mode.id - ? { - background: `${mode.color}25`, - border: `1px solid ${mode.color}60`, - color: mode.color, - } - : { - background: "rgba(255,255,255,0.06)", - border: "1px solid rgba(255,255,255,0.08)", - color: "var(--text-secondary)", - } - } - whileTap={{ scale: 0.9 }} + className="flex-1 py-2.5 rounded-2xl text-sm font-semibold text-center" + style={{ + background: "rgba(255,255,255,0.03)", + border: "1px solid rgba(255,255,255,0.05)", + color: "rgba(255,255,255,0.15)", + }} > {mode.label} - +
))} -
+
)} ); diff --git a/components/cards/LightCard.tsx b/components/cards/LightCard.tsx index f117d94..ce3d387 100644 --- a/components/cards/LightCard.tsx +++ b/components/cards/LightCard.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useCallback } from "react"; -import { motion } from "framer-motion"; +import { motion, AnimatePresence } from "framer-motion"; import { Lightbulb } from "lucide-react"; import { toggleLight, setLightBrightness } from "@/lib/api"; import { getBrightnessPct, pctToBrightness } from "@/lib/ha"; @@ -47,50 +47,42 @@ export default function LightCard({ return ( + {/* Top row: icon + toggle */}
-
-
- -
-
- {name} -
-
- {isOn ? (showSlider ? `${localBrightness}%` : "Включён") : "Выключен"} -
-
+ {/* Icon */} + + + {/* Toggle */}
- {showSlider && isOn && ( - +
-
- Яркость - {localBrightness}% -
-
-
- setLocalBrightness(parseInt(e.target.value))} - onMouseUp={(e) => handleBrightnessChange(parseInt((e.target as HTMLInputElement).value))} - onTouchEnd={(e) => handleBrightnessChange(parseInt((e.target as HTMLInputElement).value))} - className="w-full relative z-10" - style={{ background: "transparent" }} - /> -
- - )} + {name} +
+
+ {isOn ? (showSlider ? `Яркость ${localBrightness}%` : "Включён") : "Выключен"} +
+ + {/* Brightness slider */} + + {showSlider && isOn && ( + +
+
+ setLocalBrightness(parseInt(e.target.value))} + onMouseUp={(e) => + handleBrightnessChange(parseInt((e.target as HTMLInputElement).value)) + } + onTouchEnd={(e) => + handleBrightnessChange(parseInt((e.target as HTMLInputElement).value)) + } + className="w-full relative z-10" + style={{ background: "transparent" }} + /> +
+ + )} + +
); } diff --git a/components/cards/SavingsCard.tsx b/components/cards/SavingsCard.tsx index 08b8e29..ce17025 100644 --- a/components/cards/SavingsCard.tsx +++ b/components/cards/SavingsCard.tsx @@ -1,7 +1,7 @@ "use client"; import { motion } from "framer-motion"; -import { PiggyBank } from "lucide-react"; +import { Target } from "lucide-react"; interface Saving { id: number; @@ -17,31 +17,51 @@ interface Props { } function formatAmount(n: number): string { - if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; - if (n >= 1_000) return `${Math.round(n / 1_000)}K`; + if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}М`; + if (n >= 1_000) return `${Math.round(n / 1_000)}К`; return String(n); } +const COLORS = ["#f59e0b", "#3b82f6", "#10b981", "#8b5cf6", "#f43f5e"]; + export default function SavingsCard({ savings }: Props) { return ( -
- - +
- Накопления - + +
+
+
+ Накопления +
+
+ {savings.length} {savings.length === 1 ? "цель" : "целей"} +
+
@@ -50,56 +70,57 @@ export default function SavingsCard({ savings }: Props) { 100, Math.round((s.current_amount / s.target_amount) * 100) ); - const color = s.color || "#6366f1"; + const color = s.color || COLORS[i % COLORS.length]; return (
{s.icon && {s.icon}} {s.name}
{pct}%
+ {/* Progress bar with glow */}
{formatAmount(s.current_amount)} ₽ - цель: {formatAmount(s.target_amount)} ₽ + из {formatAmount(s.target_amount)} ₽
); diff --git a/components/cards/TasksCard.tsx b/components/cards/TasksCard.tsx index 546cbcd..e7f2831 100644 --- a/components/cards/TasksCard.tsx +++ b/components/cards/TasksCard.tsx @@ -2,7 +2,7 @@ import { useState, useCallback } from "react"; import { motion, AnimatePresence } from "framer-motion"; -import { CheckSquare, Square, Plus } from "lucide-react"; +import { CheckCircle2, Circle, Plus, ListTodo } from "lucide-react"; import { createTask, toggleTask } from "@/lib/api"; import AddTaskModal from "../AddTaskModal"; @@ -49,57 +49,84 @@ export default function TasksCard({ tasks, onUpdate }: Props) { return ( <> + {/* Header */}
-
-
- - +
+ +
+
+
- Задачи сегодня - -
-
- {pending.length} осталось из {localTasks.length} + Задачи +
+
+ {pending.length > 0 + ? `${pending.length} из ${localTasks.length} осталось` + : "Всё готово!"} +
+ + {/* Add button */} setModalOpen(true)} - className="w-8 h-8 rounded-lg flex items-center justify-center" + className="w-10 h-10 rounded-xl flex items-center justify-center" style={{ - background: "linear-gradient(135deg, #6366f1, #8b5cf6)", - boxShadow: "0 0 12px rgba(99,102,241,0.4)", + background: "linear-gradient(135deg, #8b5cf6, #6366f1)", + boxShadow: "0 0 18px rgba(139,92,246,0.45)", }} - whileTap={{ scale: 0.85 }} + whileTap={{ scale: 0.82 }} > - +
-
- + {/* Task list */} +
+ {localTasks.length === 0 && ( -
🎉
+
🎉
- Всё сделано! + Всё сделано на сегодня!
+ setModalOpen(true)} + className="mt-2 px-5 py-2.5 rounded-xl text-sm font-semibold" + style={{ + background: "linear-gradient(135deg, rgba(139,92,246,0.25), rgba(99,102,241,0.2))", + border: "1px solid rgba(139,92,246,0.35)", + color: "#8b5cf6", + }} + whileTap={{ scale: 0.9 }} + > + + Добавить задачу +
)} @@ -107,32 +134,36 @@ export default function TasksCard({ tasks, onUpdate }: Props) { handleToggle(task)} whileTap={{ scale: 0.97 }} > - {task.done ? ( - - ) : ( - - )} + + {task.done ? ( + + ) : ( + + )} + diff --git a/components/cards/TemperatureCard.tsx b/components/cards/TemperatureCard.tsx index d54cf09..b73fe0b 100644 --- a/components/cards/TemperatureCard.tsx +++ b/components/cards/TemperatureCard.tsx @@ -22,6 +22,7 @@ export default function TemperatureCard({ }: Props) { const [target, setTarget] = useState(targetTemp || 22); const isHeating = state === "heat"; + const isActive = state !== "off"; const adjust = useCallback( async (delta: number) => { @@ -33,97 +34,122 @@ export default function TemperatureCard({ [target, entityId, onUpdate] ); - const tempDiff = currentTemp ? currentTemp - target : 0; + const accentColor = isHeating ? "#f43f5e" : "#3b82f6"; + const accentBg = isHeating ? "rgba(244,63,94,0.08)" : "rgba(59,130,246,0.08)"; + const accentBorder = isHeating ? "rgba(244,63,94,0.2)" : "rgba(59,130,246,0.2)"; + const accentGlow = isHeating ? "rgba(244,63,94,0.25)" : "rgba(59,130,246,0.2)"; return ( + {/* Icon row */}
-
-
- -
-
- Термостат -
-
- {isHeating ? "Нагрев" : "Ожидание"} -
-
+ + + -
-
- {currentTemp?.toFixed(1) ?? "—"}° -
-
- текущая -
+ {/* Status badge */} +
+ {isHeating ? "Нагрев" : isActive ? "Охлаждение" : "Выкл"}
-
+ {/* Current temperature — BIG */} +
+ {currentTemp?.toFixed(1) ?? "—"}° +
+
- Целевая температура + текущая температура
+
+ + {/* Target temperature controls */} +
+ + Цель + +
adjust(-0.5)} - className="w-8 h-8 rounded-lg flex items-center justify-center" - style={{ background: "rgba(255,255,255,0.08)" }} - whileTap={{ scale: 0.85 }} + className="w-10 h-10 rounded-xl flex items-center justify-center" + style={{ + background: "rgba(255,255,255,0.08)", + border: "1px solid rgba(255,255,255,0.1)", + }} + whileTap={{ scale: 0.82 }} > - + + {target}° + adjust(0.5)} - className="w-8 h-8 rounded-lg flex items-center justify-center" - style={{ background: "rgba(99,102,241,0.2)" }} - whileTap={{ scale: 0.85 }} + className="w-10 h-10 rounded-xl flex items-center justify-center" + style={{ + background: `${accentColor}22`, + border: `1px solid ${accentColor}50`, + }} + whileTap={{ scale: 0.82 }} > - +
diff --git a/components/cards/WeatherCard.tsx b/components/cards/WeatherCard.tsx index fc4bc10..8c3287e 100644 --- a/components/cards/WeatherCard.tsx +++ b/components/cards/WeatherCard.tsx @@ -30,14 +30,11 @@ export default function WeatherCard({ weather }: Props) { if (!weather) { return ( -
+
Загрузка погоды...
@@ -46,91 +43,93 @@ export default function WeatherCard({ weather }: Props) { return ( -
+ {/* Location */} +
+ 📍 Санкт-Петербург +
+ + {/* Current weather */} +
+
+ {getWeatherEmoji(weather.weatherCode)} +
- 🌍 Санкт-Петербург + {weather.temp}°
-
- - {getWeatherEmoji(weather.weatherCode)} - -
-
- {weather.temp}°C -
-
- {weather.desc} -
-
+
+ {weather.desc}
-
-
- - - {weather.humidity}% - -
-
- - - {weather.windSpeed} км/ч - -
+
+ + {/* Stats */} +
+
+ + + {weather.humidity}% + +
+
+ + + {weather.windSpeed} км/ч + +
+
+ Ощущается {weather.feelsLike}°
{/* Forecast */}
- {(weather.forecast || []).map((day: any, i: number) => ( + {(weather.forecast || []).slice(0, 3).map((day: any, i: number) => (
- {i === 0 ? "Сегодня" : formatDate(day.date)} -
-
- {getWeatherEmoji(day.weatherCode)} + {i === 0 ? "Сег." : formatDate(day.date)}
+
{getWeatherEmoji(day.weatherCode)}
- {day.maxTemp}° / {day.minTemp}° + {day.maxTemp}°/{day.minTemp}°
))} diff --git a/components/cards/WeatherSavingsCard.tsx b/components/cards/WeatherSavingsCard.tsx new file mode 100644 index 0000000..a6a6458 --- /dev/null +++ b/components/cards/WeatherSavingsCard.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import WeatherCard from "./WeatherCard"; +import SavingsCard from "./SavingsCard"; + +interface Props { + weather: any; + savings: any[]; +} + +export default function WeatherSavingsCard({ weather, savings }: Props) { + const [view, setView] = useState<"weather" | "savings">("weather"); + + return ( +
+ {/* Toggle pills */} +
+ {[ + { id: "weather", label: "🌤 Погода" }, + { id: "savings", label: "💰 Цели" }, + ].map((tab) => ( + setView(tab.id as "weather" | "savings")} + className="flex-1 py-1.5 rounded-xl text-xs font-semibold relative" + style={{ + color: view === tab.id ? "var(--text-primary)" : "var(--text-secondary)", + }} + whileTap={{ scale: 0.95 }} + > + {view === tab.id && ( + + )} + {tab.label} + + ))} +
+ + {/* Content */} +
+ + {view === "weather" ? ( + + + + ) : ( + + + + )} + +
+
+ ); +}