Files
smart-home-tablet/components/cards/TemperatureCard.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

133 lines
3.7 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, useCallback } from "react";
import { motion } from "framer-motion";
import { Thermometer, Plus, Minus } from "lucide-react";
import { setClimateTemp } from "@/lib/api";
interface Props {
entityId: string;
currentTemp?: number;
targetTemp?: number;
state: string;
onUpdate: () => void;
}
export default function TemperatureCard({
entityId,
currentTemp,
targetTemp,
state,
onUpdate,
}: Props) {
const [target, setTarget] = useState(targetTemp || 22);
const isHeating = state === "heat";
const adjust = useCallback(
async (delta: number) => {
const next = Math.min(30, Math.max(16, target + delta));
setTarget(next);
await setClimateTemp(entityId, next);
onUpdate();
},
[target, entityId, onUpdate]
);
const tempDiff = currentTemp ? currentTemp - target : 0;
return (
<motion.div
className="glass-card p-5 h-full flex flex-col justify-between"
style={
isHeating
? {
background: "rgba(244,63,94,0.06)",
border: "1px solid rgba(244,63,94,0.15)",
}
: {}
}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
whileHover={{ scale: 1.01 }}
>
<div className="flex items-start justify-between">
<div>
<div
className="w-10 h-10 rounded-xl flex items-center justify-center mb-3"
style={{
background: isHeating
? "rgba(244,63,94,0.15)"
: "rgba(255,255,255,0.06)",
}}
>
<Thermometer
size={20}
color={isHeating ? "#f43f5e" : "var(--text-secondary)"}
/>
</div>
<div
className="text-sm font-semibold"
style={{ color: "var(--text-primary)" }}
>
Термостат
</div>
<div
className="text-xs mt-0.5"
style={{ color: isHeating ? "#f43f5e" : "var(--text-secondary)" }}
>
{isHeating ? "Нагрев" : "Ожидание"}
</div>
</div>
<div className="text-right">
<div
className="text-3xl font-bold"
style={{ color: "var(--text-primary)" }}
>
{currentTemp?.toFixed(1) ?? "—"}°
</div>
<div
className="text-xs"
style={{ color: "var(--text-secondary)" }}
>
текущая
</div>
</div>
</div>
<div className="flex items-center justify-between mt-4">
<div
className="text-xs"
style={{ color: "var(--text-secondary)" }}
>
Целевая температура
</div>
<div className="flex items-center gap-3">
<motion.button
onClick={() => 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 }}
>
<Minus size={14} color="var(--text-primary)" />
</motion.button>
<span
className="text-lg font-bold min-w-[48px] text-center"
style={{ color: "#6366f1" }}
>
{target}°
</span>
<motion.button
onClick={() => 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 }}
>
<Plus size={14} color="#6366f1" />
</motion.button>
</div>
</div>
</motion.div>
);
}