Files
sofa/components/stats-summary.tsx
T
jakeandClaude Opus 4.6 b6aaad243f UX overhaul: motion animations, command palette, warm cinema theme
Add premium "Late Night Screening Room" experience with warm indigo/amber
palette, framer-motion spring animations, Cmd+K command palette with TMDB
search, keyboard shortcuts (G H, G S, ?, W, M, 1-5), sonner toasts with
optimistic updates, skeleton loading states, stats dashboard, search
autocomplete with filter tabs, cinematic backdrop with film grain, season
progress bars, and staggered card reveal animations throughout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:30:35 -05:00

92 lines
2.1 KiB
TypeScript

"use client";
import {
IconCheck,
IconLibrary,
IconMovie,
IconPlayerPlay,
} from "@tabler/icons-react";
import { motion } from "motion/react";
import { useEffect, useState } from "react";
interface Stats {
moviesThisMonth: number;
episodesThisWeek: number;
librarySize: number;
completed: number;
}
const statDefs = [
{
key: "moviesThisMonth" as const,
label: "Movies This Month",
icon: IconMovie,
},
{
key: "episodesThisWeek" as const,
label: "Episodes This Week",
icon: IconPlayerPlay,
},
{
key: "librarySize" as const,
label: "In Library",
icon: IconLibrary,
},
{
key: "completed" as const,
label: "Completed",
icon: IconCheck,
},
];
export function StatsSummary() {
const [stats, setStats] = useState<Stats | null>(null);
useEffect(() => {
fetch("/api/feed/stats")
.then((r) => r.json())
.then((data) => setStats(data))
.catch(() => {});
}, []);
if (!stats) return null;
return (
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
{statDefs.map((def, i) => {
const Icon = def.icon;
const value = stats[def.key];
return (
<motion.div
key={def.key}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{
type: "spring",
stiffness: 300,
damping: 24,
delay: i * 0.08,
}}
className="rounded-xl border border-border/30 bg-card/50 p-4"
>
<div className="flex items-center gap-2">
<Icon size={14} className="text-primary" />
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
{def.label}
</span>
</div>
<motion.p
className="mt-2 font-display text-2xl tracking-tight"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: i * 0.08 + 0.2 }}
>
{value}
</motion.p>
</motion.div>
);
})}
</div>
);
}