Next.js full-stack analytics dashboard for new-api. - Direct PostgreSQL readonly queries on logs table - 5 pages: Dashboard, Rankings, Aggregation, Logs, Detail - Dark/Light/System theme with CSS variables - Chinese/English i18n (default Chinese) - Recharts with dual Y-axis for input/output tokens - Lucide icons + Motion animations - Docker + docker-compose with external sinobridge network, port 8019
40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { motion } from "motion/react";
|
|
import { formatNumber, formatTokens } from "@/lib/utils";
|
|
import { type LucideIcon } from "lucide-react";
|
|
|
|
interface StatsCardProps {
|
|
title: string;
|
|
value: number;
|
|
format?: "number" | "tokens";
|
|
icon: LucideIcon;
|
|
delay?: number;
|
|
}
|
|
|
|
export function StatsCard({ title, value, format = "number", icon: Icon, delay = 0 }: StatsCardProps) {
|
|
const display = format === "tokens" ? formatTokens(value) : formatNumber(value);
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 16 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay, ease: [0.23, 1, 0.32, 1] }}
|
|
className="glass group p-5 transition-all duration-300"
|
|
>
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<p className="text-xs font-medium uppercase tracking-widest" style={{ color: "var(--text-muted)" }}>{title}</p>
|
|
<p className="mt-2 text-2xl font-semibold tracking-tight font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>
|
|
{display}
|
|
</p>
|
|
</div>
|
|
<div className="flex h-9 w-9 items-center justify-center rounded-lg transition-colors"
|
|
style={{ background: "var(--btn-active-bg)", border: "1px solid var(--surface-border)" }}>
|
|
<Icon className="h-4 w-4" style={{ color: "var(--accent)", opacity: 0.7 }} />
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
}
|