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

@@ -1,7 +1,7 @@
"use client";
import { motion } from "framer-motion";
import { PiggyBank } from "lucide-react";
import { Target } from "lucide-react";
interface Saving {
id: number;
@@ -17,31 +17,51 @@ interface Props {
}
function formatAmount(n: number): string {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
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-5 h-full flex flex-col"
className="glass-card p-6 h-full flex flex-col"
style={{
background: "rgba(99,102,241,0.04)",
border: "1px solid rgba(99,102,241,0.12)",
background: "rgba(245,158,11,0.04)",
border: "1px solid rgba(245,158,11,0.15)",
}}
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.21 }}
whileHover={{ scale: 1.01 }}
>
<div className="flex items-center gap-2 mb-4">
<PiggyBank size={18} color="#6366f1" />
<span
className="text-sm font-semibold"
style={{ color: "var(--text-primary)" }}
{/* 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)",
}}
>
Накопления
</span>
<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">
@@ -50,56 +70,57 @@ export default function SavingsCard({ savings }: Props) {
100,
Math.round((s.current_amount / s.target_amount) * 100)
);
const color = s.color || "#6366f1";
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: i * 0.1 }}
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-xs font-medium"
className="text-sm font-semibold"
style={{ color: "var(--text-primary)" }}
>
{s.name}
</span>
</div>
<span
className="text-xs font-semibold"
className="text-sm font-black"
style={{ color }}
>
{pct}%
</span>
</div>
{/* Progress bar with glow */}
<div className="progress-bar">
<motion.div
className="progress-fill"
className="h-full rounded-full"
style={{
background: `linear-gradient(90deg, ${color}aa, ${color})`,
width: `${pct}%`,
background: `linear-gradient(90deg, ${color}80, ${color})`,
boxShadow: `0 0 12px ${color}60`,
}}
initial={{ width: "0%" }}
animate={{ width: `${pct}%` }}
transition={{
duration: 1,
delay: i * 0.2,
duration: 1.2,
delay: 0.2 + i * 0.15,
ease: "easeOut",
}}
/>
</div>
<div
className="flex justify-between text-xs mt-1"
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>
<span>из {formatAmount(s.target_amount)} </span>
</div>
</motion.div>
);