redesign: glassmorphism UI with big cards, 3-col layout, ambient orbs
All checks were successful
Deploy to Coolify / deploy (push) Successful in 4s

This commit is contained in:
Cosmo
2026-04-22 10:23:57 +00:00
parent 7e1e2cfd4d
commit ecf69400f6
11 changed files with 789 additions and 532 deletions

View File

@@ -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 (
<motion.div
className="glass-card p-5 h-full flex flex-col justify-between"
className="glass-card p-6 h-full flex flex-col justify-between"
style={
isHeating
isActive
? {
background: "rgba(244,63,94,0.06)",
border: "1px solid rgba(244,63,94,0.15)",
background: accentBg,
border: `1px solid ${accentBorder}`,
boxShadow: `0 0 40px ${accentGlow}`,
}
: {}
}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
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">
<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>
<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>
<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>
{/* 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>
<div className="flex items-center justify-between mt-4">
{/* Current temperature — BIG */}
<div className="my-4">
<div
className="text-xs"
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-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 }}
>
<Minus size={14} color="var(--text-primary)" />
<Minus size={16} color="var(--text-primary)" />
</motion.button>
<span
className="text-lg font-bold min-w-[48px] text-center"
style={{ color: "#6366f1" }}
className="text-2xl font-bold min-w-[56px] text-center"
style={{ color: accentColor }}
>
{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 }}
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={14} color="#6366f1" />
<Plus size={16} color={accentColor} />
</motion.button>
</div>
</div>