"use client"; /** * Dashboard scene — guild star + channel orbit live on the stage canvas; * this overlay adds the metric whisper (top-left), activity ribbon * (bottom-left), and moderation gauge (right) as floating panels. */ import { Activity, Flag, ShieldAlert } from "lucide-react"; import { useEffect, useMemo } from "react"; import { useAmbient } from "@/components/ambient/ambient-context"; import { AreaActivity, RadialGauge } from "@/components/charts"; import { ErrorState, LoadingState } from "@/components/shared"; import { useScenePublish } from "@/components/shell/scene-graph-context"; import { useActivity, useStats } from "@/hooks"; import { statsToGraph } from "@/lib/constellation/graph"; import { formatNumber } from "@/lib/format"; import type { DashboardStats } from "@/lib/types"; import { staggerDelay } from "@/lib/utils"; function deriveSignal(stats?: DashboardStats) { if (!stats) return { tone: "signal" as const, label: "nominal" }; const total = stats.total_flagged + stats.total_clean || 1; const ratio = stats.total_flagged / total; if (stats.moderation_overview.error > 0) return { tone: "vermilion" as const, label: "moderation fault" }; if (ratio > 0.25) return { tone: "vermilion" as const, label: "elevated flags" }; if (ratio > 0.1) return { tone: "amber" as const, label: "watch" }; return { tone: "signal" as const, label: "nominal" }; } export function DashboardView({ initialStats, initialActivity, }: { initialStats?: DashboardStats; initialActivity?: Awaited>["data"]; }) { const { data: stats, error } = useStats(initialStats); const { data: activity } = useActivity(14, initialActivity as never); const ambient = useAmbient(); const publish = useScenePublish(); const graph = useMemo( () => (stats ? statsToGraph(stats) : { nodes: [], edges: [] }), [stats], ); useEffect(() => { publish({ graph, focus: "guild" }); }, [graph, publish]); useEffect(() => { const s = deriveSignal(stats); ambient.set( s.tone, 0.3 + Math.min(0.5, (stats?.today_flagged ?? 0) / 50), s.label, ); }, [stats, ambient]); if (error && !stats) return ; if (!stats) return ; const s = stats; const total = s.total_flagged + s.total_clean || 1; const cleanRatio = s.total_clean / total; return (
{/* Metric whisper — top-left under brand */}

Operations · {deriveSignal(s).label}

0 ? "vermilion" : "ink"} hint={`${s.today_flagged} today`} />
{/* Activity ribbon — bottom-left */}

14-day signal

{activity ? ( ) : ( )}
{/* Moderation gauge — right */}

Trust

0.8 ? "signal" : cleanRatio > 0.6 ? "amber" : "vermilion" } label={`${Math.round(cleanRatio * 100)}%`} sublabel="clean" />

flagged {formatNumber(s.total_flagged)}

warned {formatNumber(s.total_warned)}

pending {s.moderation_overview.pending}

processing {s.moderation_overview.processing}

); } function WhisperRow({ label, value, tone, hint, }: { label: string; value: string; tone: "ink" | "signal" | "vermilion"; hint?: string; }) { const color = tone === "vermilion" ? "text-vermilion" : tone === "signal" ? "text-signal" : "text-ink"; return (

{label} {value} {hint ? ( {hint} ) : null}

); }