Files
smart-home-tablet/components/TopBar.tsx
Cosmo 9044869fa4
All checks were successful
Deploy to Coolify / deploy (push) Successful in 44s
feat: initial smart home dashboard
- Next.js 14 + TypeScript + Tailwind CSS
- Glassmorphism design with ambient orbs
- Cards: Light x2, Temperature, AirPurifier, Tasks, Weather, Savings
- Home Assistant integration (demo mode if no token)
- Vikunja tasks API
- Pulse savings API
- wttr.in weather
- Framer Motion animations
- Dark/light theme toggle
- Bottom navigation
- Dockerfile for deployment
2026-04-22 10:00:41 +00:00

130 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect } from "react";
import { motion } from "framer-motion";
import ThemeToggle from "./ThemeToggle";
function getWeatherEmoji(code: string): string {
const c = parseInt(code);
if (c === 113) return "☀️";
if (c === 116) return "⛅";
if (c === 119 || c === 122) return "☁️";
if (c >= 176 && c <= 182) return "🌦️";
if (c >= 185 && c <= 200) return "🌧️";
if (c >= 200 && c <= 210) return "⛈️";
if (c >= 210 && c <= 260) return "❄️";
if (c >= 260 && c <= 300) return "🌨️";
if (c >= 300 && c <= 400) return "🌧️";
return "🌤️";
}
interface Props {
isDark: boolean;
onToggleTheme: () => void;
weather: any;
}
export default function TopBar({ isDark, onToggleTheme, weather }: Props) {
const [time, setTime] = useState("");
const [date, setDate] = useState("");
useEffect(() => {
const update = () => {
const now = new Date();
setTime(
now.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" })
);
setDate(
now.toLocaleDateString("ru-RU", {
weekday: "long",
day: "numeric",
month: "long",
})
);
};
update();
const id = setInterval(update, 1000);
return () => clearInterval(id);
}, []);
return (
<motion.div
className="glass-card px-6 py-3 flex items-center justify-between no-select"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
{/* Time & Date */}
<div className="flex items-baseline gap-4">
<span
className="text-5xl font-bold tracking-tight"
style={{ color: "var(--text-primary)" }}
>
{time}
</span>
<span
className="text-sm font-medium capitalize"
style={{ color: "var(--text-secondary)" }}
>
{date}
</span>
</div>
{/* Weather */}
{weather && (
<motion.div
className="flex items-center gap-3 px-4 py-2 rounded-xl"
style={{
background: "rgba(99,102,241,0.1)",
border: "1px solid rgba(99,102,241,0.2)",
}}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.3 }}
>
<span className="text-2xl">
{getWeatherEmoji(weather.weatherCode)}
</span>
<div>
<div
className="text-xl font-bold"
style={{ color: "var(--text-primary)" }}
>
{weather.temp}°C
</div>
<div
className="text-xs"
style={{ color: "var(--text-secondary)" }}
>
Ощущается {weather.feelsLike}°
</div>
</div>
<div
className="text-xs ml-2 max-w-[80px] text-center leading-tight"
style={{ color: "var(--text-secondary)" }}
>
{weather.desc}
</div>
</motion.div>
)}
{/* Theme toggle */}
<div className="flex items-center gap-3">
{weather?.demo && (
<span
className="text-xs px-2 py-1 rounded-full"
style={{
background: "rgba(245,158,11,0.15)",
color: "#f59e0b",
border: "1px solid rgba(245,158,11,0.3)",
}}
>
Demo
</span>
)}
<ThemeToggle isDark={isDark} onToggle={onToggleTheme} />
</div>
</motion.div>
);
}