Add admin truth history dashboard and training data ops views

This commit is contained in:
2569718930@qq.com
2026-04-03 01:27:54 +08:00
parent 781c247952
commit bbdba2540c
7 changed files with 1160 additions and 10 deletions
+89 -10
View File
@@ -1,5 +1,6 @@
"use client";
import Link from "next/link";
import { useCallback, useEffect, useMemo, useState } from "react";
import { AlertTriangle, Database, RefreshCcw, ShieldCheck, Wallet } from "lucide-react";
import { Badge } from "@/components/ui/badge";
@@ -558,10 +559,60 @@ export function OpsDashboard() {
}
}, [grantEmail, grantPoints, incidentReasonFilter, loadLeaderboard, loadMemberships, loadPaymentIncidents, loadUsers, searchQuery]);
const opsNav = [
{ id: "overview", label: "总览" },
{ id: "funnel", label: "转化漏斗" },
{ id: "probability", label: "EMOS 门禁" },
{ id: "training-data", label: "训练数据" },
{ id: "coverage", label: "覆盖状态" },
{ id: "payments", label: "支付异常" },
{ id: "members", label: "会员" },
{ id: "users", label: "用户与积分" },
];
return (
<main className="min-h-screen bg-slate-950 px-3 py-6 text-slate-100 sm:px-6 sm:py-8 lg:px-8">
<div className="mx-auto flex max-w-7xl flex-col gap-5 sm:gap-6">
<section className="flex flex-col gap-4 rounded-2xl border border-slate-800 bg-slate-900/80 p-4 shadow-2xl backdrop-blur-xl sm:rounded-3xl sm:p-6 lg:flex-row lg:items-end lg:justify-between">
<div className="mx-auto grid max-w-7xl gap-5 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-6">
<aside className="hidden lg:block">
<div className="sticky top-6 space-y-4 rounded-3xl border border-slate-800 bg-slate-900/80 p-4 shadow-2xl backdrop-blur-xl">
<div>
<div className="text-xs font-bold uppercase tracking-[0.2em] text-cyan-300">Ops Console</div>
<div className="mt-2 text-lg font-black text-slate-100">PolyWeather Admin</div>
<div className="mt-1 text-xs text-slate-500"></div>
</div>
<div className="space-y-2">
{opsNav.map((item) => (
<a
key={item.id}
href={`#${item.id}`}
className="flex items-center justify-between rounded-2xl border border-slate-800 bg-slate-950/60 px-3 py-2 text-sm text-slate-300 transition hover:border-cyan-400/40 hover:text-white"
>
<span>{item.label}</span>
<span className="text-[11px] text-slate-500">#{item.id}</span>
</a>
))}
<Link
href="/ops/truth-history"
className="flex items-center justify-between rounded-2xl border border-cyan-500/30 bg-cyan-500/10 px-3 py-2 text-sm text-cyan-100 transition hover:border-cyan-400/60 hover:bg-cyan-500/15 hover:text-white"
>
<span></span>
<span className="text-[11px] text-cyan-200/80">/ops/truth-history</span>
</Link>
</div>
<div className="grid gap-2">
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 px-3 py-2">
<div className="text-[11px] font-bold uppercase tracking-[0.16em] text-slate-500">Health</div>
<div className="mt-1 text-sm text-slate-200">{health?.status || "unknown"}</div>
</div>
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 px-3 py-2">
<div className="text-[11px] font-bold uppercase tracking-[0.16em] text-slate-500">EMOS</div>
<div className="mt-1 text-sm text-slate-200">{rolloutDecision?.decision || "hold"}</div>
</div>
</div>
</div>
</aside>
<div className="flex min-w-0 flex-col gap-5 sm:gap-6">
<section id="overview" className="flex flex-col gap-4 rounded-2xl border border-slate-800 bg-slate-900/80 p-4 shadow-2xl backdrop-blur-xl sm:rounded-3xl sm:p-6">
<div className="space-y-3">
<div className="flex flex-wrap items-center gap-3">
<Badge variant="secondary">Ops</Badge>
@@ -578,7 +629,7 @@ export function OpsDashboard() {
<div>
<h1 className="text-2xl font-black tracking-tight sm:text-3xl">PolyWeather Ops</h1>
<p className="mt-2 max-w-3xl text-sm text-slate-400">
</p>
</div>
</div>
@@ -589,6 +640,24 @@ export function OpsDashboard() {
{loading ? "加载中" : "刷新"}
</Button>
</div>
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 p-3">
<div className="text-xs font-bold uppercase tracking-[0.16em] text-slate-500">Health</div>
<div className="mt-2 text-2xl font-black text-slate-100">{health?.status || "unknown"}</div>
</div>
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 p-3">
<div className="text-xs font-bold uppercase tracking-[0.16em] text-slate-500">Truth Rows</div>
<div className="mt-2 text-2xl font-black text-slate-100">{truthRecords?.row_count ?? 0}</div>
</div>
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 p-3">
<div className="text-xs font-bold uppercase tracking-[0.16em] text-slate-500">EMOS Samples</div>
<div className="mt-2 text-2xl font-black text-slate-100">{trainingArtifacts?.emos_training_samples ?? 0}</div>
</div>
<div className="rounded-2xl border border-slate-800 bg-slate-950/70 p-3">
<div className="text-xs font-bold uppercase tracking-[0.16em] text-slate-500">LGBM Samples</div>
<div className="mt-2 text-2xl font-black text-slate-100">{trainingArtifacts?.lgbm_sample_count ?? 0}</div>
</div>
</div>
</section>
{error ? (
@@ -662,7 +731,7 @@ export function OpsDashboard() {
</Card>
</section>
<section className="grid gap-4 xl:grid-cols-[1.3fr_0.7fr]">
<section id="funnel" className="grid gap-4 xl:grid-cols-[1.3fr_0.7fr]">
<Card>
<CardHeader>
<CardTitle></CardTitle>
@@ -738,7 +807,7 @@ export function OpsDashboard() {
</Card>
</section>
<section className="grid gap-4 xl:grid-cols-[1.4fr_1fr]">
<section id="probability" className="grid gap-4 xl:grid-cols-[1.4fr_1fr]">
<Card>
<CardHeader>
<CardTitle>EMOS 线</CardTitle>
@@ -812,13 +881,22 @@ export function OpsDashboard() {
</Card>
</section>
<section className="grid gap-4 xl:grid-cols-[1.2fr_1.2fr_1fr]">
<section id="training-data" className="grid gap-4 xl:grid-cols-[1.2fr_1.2fr_1fr]">
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription> 14 </CardDescription>
</CardHeader>
<CardContent className="space-y-4 text-sm text-slate-300">
<div className="flex items-center justify-between gap-3 rounded-2xl border border-cyan-500/20 bg-cyan-500/5 px-3 py-2">
<div>
<div className="text-xs font-bold uppercase tracking-[0.16em] text-cyan-300">Truth Viewer</div>
<div className="mt-1 text-xs text-slate-400"></div>
</div>
<Button asChild variant="secondary" className="shrink-0">
<Link href="/ops/truth-history"></Link>
</Button>
</div>
<div className="grid gap-3 sm:grid-cols-2">
<MobileField label="truth rows" value={String(truthRecords?.row_count ?? 0)} mono />
<MobileField label="cities" value={String(truthRecords?.cities_count ?? 0)} mono />
@@ -902,7 +980,7 @@ export function OpsDashboard() {
</Card>
</section>
<section className="grid gap-4 xl:grid-cols-[1fr_1.4fr]">
<section id="coverage" className="grid gap-4 xl:grid-cols-[1fr_1.4fr]">
<Card>
<CardHeader>
<CardTitle>Wunderground </CardTitle>
@@ -1038,7 +1116,7 @@ export function OpsDashboard() {
</Card>
</section>
<Card>
<Card id="payments">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription> truth / feature / EMOS / LGBM DEB</CardDescription>
@@ -1113,7 +1191,7 @@ export function OpsDashboard() {
</CardContent>
</Card>
<Card>
<Card id="members">
<CardHeader>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div>
@@ -1287,7 +1365,7 @@ export function OpsDashboard() {
</CardContent>
</Card>
<section className="grid gap-4 xl:grid-cols-[1.1fr_0.9fr]">
<section id="users" className="grid gap-4 xl:grid-cols-[1.1fr_0.9fr]">
<Card>
<CardHeader>
<CardTitle></CardTitle>
@@ -1381,6 +1459,7 @@ export function OpsDashboard() {
</CardContent>
</Card>
</section>
</div>
</div>
</main>
);