fix: resolve all eslint errors (set-state-in-effect, nested components, no-explicit-any)
- Wrap synchronous setState calls in useEffect with startTransition to avoid cascading renders - Convert nested SortIcon components to renderSortIcon helper functions - Replace all `any` types with proper interfaces (OverviewData, TrendPoint, RankItem) - Remove unused formatTokens import in logs page - Add no-any rule to CLAUDE.md
This commit is contained in:
39
app/page.tsx
39
app/page.tsx
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { useEffect, useState, useCallback, startTransition } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { Zap, Hash, Users, Cpu, TrendingUp, BarChart3 } from "lucide-react";
|
||||
import { StatsCard } from "@/components/StatsCard";
|
||||
@@ -11,19 +11,40 @@ import { buildQuery } from "@/lib/utils";
|
||||
import { useTimeRange } from "@/lib/time-range-context";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
|
||||
interface OverviewData {
|
||||
total_calls: number;
|
||||
total_tokens: number;
|
||||
active_users: number;
|
||||
active_models: number;
|
||||
}
|
||||
|
||||
interface TrendPoint {
|
||||
date: string;
|
||||
calls: number;
|
||||
total_tokens: number;
|
||||
prompt_tokens: number;
|
||||
completion_tokens: number;
|
||||
}
|
||||
|
||||
interface RankItem {
|
||||
name: string;
|
||||
total_tokens: number;
|
||||
calls: number;
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { t } = useI18n();
|
||||
const { getEffectiveRange } = useTimeRange();
|
||||
const [granularity, setGranularity] = useState<"day" | "week" | "month">("day");
|
||||
const [trendMetric, setTrendMetric] = useState<"total_tokens" | "calls">("total_tokens");
|
||||
const [overview, setOverview] = useState<any>(null);
|
||||
const [trends, setTrends] = useState<any[]>([]);
|
||||
const [userRank, setUserRank] = useState<any[]>([]);
|
||||
const [modelRank, setModelRank] = useState<any[]>([]);
|
||||
const [overview, setOverview] = useState<OverviewData | null>(null);
|
||||
const [trends, setTrends] = useState<TrendPoint[]>([]);
|
||||
const [userRank, setUserRank] = useState<RankItem[]>([]);
|
||||
const [modelRank, setModelRank] = useState<RankItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
startTransition(() => setLoading(true));
|
||||
const { start, end } = getEffectiveRange();
|
||||
const tp = { start, end };
|
||||
const [ov, tr, ur, mr] = await Promise.all([
|
||||
@@ -32,7 +53,9 @@ export default function DashboardPage() {
|
||||
fetch(buildQuery("/api/rankings", { ...tp, type: "user", limit: 10 })).then(r => r.json()),
|
||||
fetch(buildQuery("/api/rankings", { ...tp, type: "model", limit: 10 })).then(r => r.json()),
|
||||
]);
|
||||
setOverview(ov); setTrends(tr); setUserRank(ur); setModelRank(mr); setLoading(false);
|
||||
startTransition(() => {
|
||||
setOverview(ov); setTrends(tr); setUserRank(ur); setModelRank(mr); setLoading(false);
|
||||
});
|
||||
}, [granularity, getEffectiveRange]);
|
||||
|
||||
useEffect(() => { fetchData(); }, [fetchData]);
|
||||
@@ -82,7 +105,7 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
<div className="flex gap-1 rounded-md p-0.5" style={{ background: "var(--row-hover)", border: "1px solid var(--surface-border)" }}>
|
||||
{([["total_tokens", t("metric.token")], ["calls", t("metric.calls")]] as const).map(([k, l]) => (
|
||||
<button key={k} onClick={() => setTrendMetric(k as any)}
|
||||
<button key={k} onClick={() => setTrendMetric(k)}
|
||||
className="px-2.5 py-1 text-xs rounded transition-colors"
|
||||
style={{
|
||||
background: trendMetric === k ? "var(--btn-active-bg)" : "transparent",
|
||||
|
||||
Reference in New Issue
Block a user