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

141 lines
4.1 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 { motion } from "framer-motion";
import { Target } from "lucide-react";
interface Saving {
id: number;
name: string;
current_amount: number;
target_amount: number;
color?: string;
icon?: string;
}
interface Props {
savings: Saving[];
}
function formatAmount(n: number): string {
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 (
<motion.div
className="glass-card p-6 h-full flex flex-col"
style={{
background: "rgba(245,158,11,0.04)",
border: "1px solid rgba(245,158,11,0.15)",
}}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35, delay: 0.21 }}
whileHover={{ scale: 1.01 }}
>
{/* Header */}
<div className="flex items-center gap-3 mb-5">
<div
className="w-10 h-10 rounded-xl flex items-center justify-center"
style={{
background: "rgba(245,158,11,0.2)",
boxShadow: "0 0 16px rgba(245,158,11,0.3)",
}}
>
<Target size={20} color="#f59e0b" />
</div>
<div>
<div
className="text-base font-bold"
style={{ color: "var(--text-primary)" }}
>
Накопления
</div>
<div
className="text-xs"
style={{ color: "var(--text-secondary)" }}
>
{savings.length} {savings.length === 1 ? "цель" : "целей"}
</div>
</div>
</div>
<div className="flex-1 space-y-4">
{savings.map((s, i) => {
const pct = Math.min(
100,
Math.round((s.current_amount / s.target_amount) * 100)
);
const color = s.color || COLORS[i % COLORS.length];
return (
<motion.div
key={s.id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 + i * 0.1 }}
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
{s.icon && <span className="text-base">{s.icon}</span>}
<span
className="text-sm font-semibold"
style={{ color: "var(--text-primary)" }}
>
{s.name}
</span>
</div>
<span
className="text-sm font-black"
style={{ color }}
>
{pct}%
</span>
</div>
{/* Progress bar with glow */}
<div className="progress-bar">
<motion.div
className="h-full rounded-full"
style={{
background: `linear-gradient(90deg, ${color}80, ${color})`,
boxShadow: `0 0 12px ${color}60`,
}}
initial={{ width: "0%" }}
animate={{ width: `${pct}%` }}
transition={{
duration: 1.2,
delay: 0.2 + i * 0.15,
ease: "easeOut",
}}
/>
</div>
<div
className="flex justify-between text-xs mt-1.5"
style={{ color: "var(--text-secondary)" }}
>
<span>{formatAmount(s.current_amount)} </span>
<span>из {formatAmount(s.target_amount)} </span>
</div>
</motion.div>
);
})}
{savings.length === 0 && (
<div
className="text-center py-6 text-sm"
style={{ color: "var(--text-secondary)" }}
>
Нет данных о накоплениях
</div>
)}
</div>
</motion.div>
);
}