feat: API analytics dashboard with i18n and theme support
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
This commit is contained in:
54
lib/utils.ts
Normal file
54
lib/utils.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export function formatNumber(n: number): string {
|
||||
return n.toLocaleString("en-US");
|
||||
}
|
||||
|
||||
export function formatTokens(n: number): string {
|
||||
if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(1)}B`;
|
||||
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
||||
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
|
||||
return String(n);
|
||||
}
|
||||
|
||||
export function formatUSD(n: number): string {
|
||||
if (n >= 1000) return `$${n.toLocaleString("en-US", { maximumFractionDigits: 0 })}`;
|
||||
if (n >= 1) return `$${n.toFixed(2)}`;
|
||||
if (n >= 0.01) return `$${n.toFixed(3)}`;
|
||||
return `$${n.toFixed(4)}`;
|
||||
}
|
||||
|
||||
export function formatDate(iso: string): string {
|
||||
return dayjs(iso).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
// 预设时间范围
|
||||
export type TimeRange = "today" | "7d" | "30d" | "all" | "custom";
|
||||
|
||||
export function getTimeRange(range: TimeRange): { start?: number; end?: number } {
|
||||
const now = dayjs();
|
||||
switch (range) {
|
||||
case "today":
|
||||
return { start: now.startOf("day").unix(), end: now.unix() };
|
||||
case "7d":
|
||||
return { start: now.subtract(7, "day").startOf("day").unix(), end: now.unix() };
|
||||
case "30d":
|
||||
return { start: now.subtract(30, "day").startOf("day").unix(), end: now.unix() };
|
||||
case "all":
|
||||
return {};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function buildQuery(
|
||||
base: string,
|
||||
params: Record<string, string | number | undefined>
|
||||
): string {
|
||||
const sp = new URLSearchParams();
|
||||
for (const [k, v] of Object.entries(params)) {
|
||||
if (v !== undefined && v !== "") sp.set(k, String(v));
|
||||
}
|
||||
const qs = sp.toString();
|
||||
return qs ? `${base}?${qs}` : base;
|
||||
}
|
||||
Reference in New Issue
Block a user