Files
smart-home-tablet/components/cards/TemperatureCard.tsx
Cosmo ecf69400f6
All checks were successful
Deploy to Coolify / deploy (push) Successful in 4s
redesign: glassmorphism UI with big cards, 3-col layout, ambient orbs
2026-04-22 10:23:57 +00:00

159 lines
4.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 isActive = state !== "off";
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 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 (
<motion.div
className="glass-card p-6 h-full flex flex-col justify-between"
style={
isActive
? {
background: accentBg,
border: `1px solid ${accentBorder}`,
boxShadow: `0 0 40px ${accentGlow}`,
}
: {}
}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35, delay: 0.07 }}
whileHover={{ scale: 1.01 }}
>
{/* Icon row */}
<div className="flex items-start justify-between">
<motion.div
className="w-16 h-16 rounded-2xl flex items-center justify-center"
style={{
background: isActive ? `${accentColor}22` : "rgba(255,255,255,0.06)",
boxShadow: isActive ? `0 0 28px ${accentGlow}` : "none",
}}
>
<Thermometer
size={32}
color={isActive ? accentColor : "rgba(255,255,255,0.35)"}
strokeWidth={1.5}
/>
</motion.div>
{/* Status badge */}
<div
className="px-3 py-1 rounded-full text-xs font-semibold"
style={{
background: isActive ? `${accentColor}18` : "rgba(255,255,255,0.06)",
color: isActive ? accentColor : "var(--text-secondary)",
border: `1px solid ${isActive ? accentColor + "40" : "rgba(255,255,255,0.08)"}`,
}}
>
{isHeating ? "Нагрев" : isActive ? "Охлаждение" : "Выкл"}
</div>
</div>
{/* Current temperature — BIG */}
<div className="my-4">
<div
className="font-black leading-none tracking-tight"
style={{
fontSize: "56px",
color: isActive ? accentColor : "var(--text-primary)",
textShadow: isActive ? `0 0 40px ${accentColor}60` : "none",
}}
>
{currentTemp?.toFixed(1) ?? "—"}°
</div>
<div
className="text-sm mt-1"
style={{ color: "var(--text-secondary)" }}
>
текущая температура
</div>
</div>
{/* Target temperature controls */}
<div
className="flex items-center justify-between px-4 py-3 rounded-2xl"
style={{
background: "rgba(255,255,255,0.04)",
border: "1px solid rgba(255,255,255,0.07)",
}}
>
<span
className="text-sm font-medium"
style={{ color: "var(--text-secondary)" }}
>
Цель
</span>
<div className="flex items-center gap-3">
<motion.button
onClick={() => adjust(-0.5)}
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 }}
>
<Minus size={16} color="var(--text-primary)" />
</motion.button>
<span
className="text-2xl font-bold min-w-[56px] text-center"
style={{ color: accentColor }}
>
{target}°
</span>
<motion.button
onClick={() => adjust(0.5)}
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 }}
>
<Plus size={16} color={accentColor} />
</motion.button>
</div>
</div>
</motion.div>
);
}