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
42 lines
1.4 KiB
TypeScript
42 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { type TimeRange } from "@/lib/utils";
|
|
import { useI18n } from "@/lib/i18n";
|
|
import { motion } from "motion/react";
|
|
|
|
export function TimeRangeSelector({
|
|
value,
|
|
onChange,
|
|
}: {
|
|
value: TimeRange;
|
|
onChange: (v: TimeRange) => void;
|
|
}) {
|
|
const { t } = useI18n();
|
|
const ranges: { label: string; value: TimeRange }[] = [
|
|
{ label: t("time.today"), value: "today" },
|
|
{ label: t("time.7d"), value: "7d" },
|
|
{ label: t("time.30d"), value: "30d" },
|
|
{ label: t("time.all"), value: "all" },
|
|
];
|
|
|
|
return (
|
|
<div className="flex gap-1 rounded-lg p-1" style={{ background: "var(--row-hover)", border: "1px solid var(--surface-border)" }}>
|
|
{ranges.map((r) => (
|
|
<button key={r.value} onClick={() => onChange(r.value)}
|
|
className="relative px-3 py-1.5 text-xs font-medium rounded-md transition-colors"
|
|
style={{ color: value === r.value ? "var(--text-accent)" : "var(--text-muted)" }}
|
|
>
|
|
{value === r.value && (
|
|
<motion.div layoutId="time-range-bg"
|
|
className="absolute inset-0 rounded-md"
|
|
style={{ background: "var(--btn-active-bg)", border: "1px solid var(--surface-border)" }}
|
|
transition={{ type: "spring", stiffness: 400, damping: 30 }}
|
|
/>
|
|
)}
|
|
<span className="relative z-10">{r.label}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|