Files
GMW/services/frontend/src/app/(dashboard)/moderation/view.tsx
T

182 lines
5.7 KiB
TypeScript
Raw Normal View History

"use client";
import { AlertTriangle, CheckCircle2, Shield } from "lucide-react";
import { useEffect, useState } from "react";
import { useAmbient } from "@/components/ambient/ambient-context";
import { LiveModerationFeed } from "@/components/LiveModerationFeed";
import { ModerationHeatmap } from "@/components/ModerationHeatmap";
import { GlassPanel } from "@/components/primitives";
import {
ErrorState,
MetricTile,
PageTransition,
SectionHeader,
SkeletonMetricRow,
SkeletonPanel,
} from "@/components/shared";
import {
useHourlyModeration,
useModerationActions,
useModerationStats,
} from "@/hooks";
import { useStaggerReveal } from "@/hooks/use-gsap-animation";
import { formatNumber } from "@/lib/format";
import type {
ModerationAction,
ModerationCoverage,
ModerationStats,
} from "@/lib/types";
export function ModerationView({
initialStats,
initialActions,
initialCoverage: _initialCoverage,
}: {
initialStats?: ModerationStats;
initialActions?: ModerationAction[];
initialCoverage?: ModerationCoverage;
}) {
const {
data: stats,
isLoading,
error,
mutate,
} = useModerationStats(initialStats);
const { data: actions } = useModerationActions(
undefined,
undefined,
initialActions,
);
const { data: hourly } = useHourlyModeration();
const ambient = useAmbient();
const [filterMode, setFilterMode] = useState<"all" | "flagged" | "clean">(
"all",
);
const containerRef = useStaggerReveal<HTMLDivElement>(".mod-tile", {
stagger: 0.035,
y: 8,
dependencies: [stats],
});
const executed = stats?.executed ?? 0;
const failed = stats?.failed ?? 0;
const total = stats?.total || 1;
const failedRatio = failed / total;
useEffect(() => {
if (failedRatio > 0.15) ambient.set("vermilion", 0.5, "high failure rate");
else if (failedRatio > 0.05) ambient.set("amber", 0.35, "moderate issues");
else ambient.set("signal", 0.25, "moderation nominal");
}, [failedRatio, ambient]);
if (error && !stats)
return <ErrorState error={error} onRetry={() => void mutate()} />;
if (!stats && isLoading)
return (
<div className="space-y-4">
<SkeletonMetricRow cols={3} />
<SkeletonPanel rows={6} />
</div>
);
if (!stats) return null;
return (
<PageTransition>
<div ref={containerRef} className="space-y-4">
{/* Tactical HUD Header Bar */}
<div className="mod-tile flex flex-wrap items-center justify-between gap-3 border-b border-hairline pb-3">
<div className="flex items-center gap-2.5">
<span
className={`h-2 w-2 rounded-full ${
failedRatio > 0.1
? "bg-vermilion shadow-[0_0_8px_var(--color-vermilion-glow)]"
: "bg-signal shadow-[0_0_8px_var(--color-signal-glow)]"
}`}
/>
<h1 className="font-mono text-xs font-semibold tracking-wide text-ink uppercase">
Moderation Intelligence Console
</h1>
</div>
<div className="flex items-center gap-1.5 rounded-[6px] border border-hairline bg-surface-2 p-0.5">
{(["all", "flagged", "clean"] as const).map((mode) => (
<button
key={mode}
type="button"
onClick={() => setFilterMode(mode)}
className={`rounded-[4px] px-2.5 py-1 font-mono text-[10px] font-medium transition-all ${
filterMode === mode
? "bg-surface text-ink border border-hairline-focus shadow-xs"
: "text-ink-muted hover:text-ink"
}`}
>
{mode.toUpperCase()}
</button>
))}
</div>
</div>
{/* Primary Metric Grid */}
<div className="mod-tile grid gap-3 sm:grid-cols-3">
<MetricTile
label="Total Audited"
value={formatNumber(stats.total)}
hint="Continuous LLM inspection"
icon={<Shield className="size-4" />}
/>
<MetricTile
label="Clean Executed"
value={formatNumber(stats.executed)}
hint={`${Math.round((executed / total) * 100)}% execution rate`}
tone="signal"
icon={<CheckCircle2 className="size-4" />}
/>
<MetricTile
label="Pending / Failed"
value={formatNumber(stats.pending + stats.failed)}
hint={`${stats.failed} failed items`}
tone={failedRatio > 0.05 ? "vermilion" : "neutral"}
icon={<AlertTriangle className="size-4" />}
/>
</div>
{/* Live Moderation Stream */}
<div className="mod-tile">
<GlassPanel>
<SectionHeader
eyebrow="Realtime Feed"
title="Live Stream Audit Log"
/>
<div className="mt-3">
<LiveModerationFeed
actions={
actions?.filter((r) => {
if (filterMode === "flagged") return r.status === "failed";
if (filterMode === "clean") return r.status === "executed";
return true;
}) ?? []
}
/>
</div>
</GlassPanel>
</div>
{/* Heatmap Section */}
{hourly && hourly.length > 0 && (
<div className="mod-tile">
<GlassPanel>
<SectionHeader
eyebrow="Distribution"
title="Hourly Incident Heatmap"
/>
<div className="mt-3">
<ModerationHeatmap hours={hourly} />
</div>
</GlassPanel>
</div>
)}
</div>
</PageTransition>
);
}