"use client"; import { Activity, Flag, MessageSquare, Mic, Radio, ShieldAlert, Users, } from "lucide-react"; import { useEffect } from "react"; import { useAmbient } from "@/components/ambient/ambient-context"; import { AreaActivity, RadialGauge } from "@/components/charts"; import { GlassPanel } from "@/components/primitives"; import { ErrorState, LoadingState, SkeletonHero, SkeletonMetricRow, SkeletonPanel, } from "@/components/shared"; import { MetricTile, SectionHeader } from "@/components/shared/section"; import { useActivity, useStats, useTopReactions, useTopReactors, } from "@/hooks"; import { formatNumber } from "@/lib/format"; import type { DashboardStats } from "@/lib/types"; 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, isLoading, error } = useStats(initialStats); const { data: activity } = useActivity(14, initialActivity as never); const { data: reactors } = useTopReactors(); const { data: reactions } = useTopReactions(); const ambient = useAmbient(); 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 && isLoading) return (
); if (!stats) return ; const s = stats; const total = s.total_flagged + s.total_clean || 1; const cleanRatio = s.total_clean / total; return (
{/* Hero */}
GMW · Operations Grid

Ambient Field

Real-time moderation, voice & media presence across the monitored guild. {formatNumber(s.total_messages)} messages captured.

{deriveSignal(s).label}
} /> 0 ? "vermilion" : "neutral"} hint={`${s.today_flagged} today`} /> } /> } />
{/* Activity */} Activity & moderation } action={
messages flagged
} /> {activity ? ( ) : ( )}
{/* Two-column: channels + moderation */}
{s.top_channels.slice(0, 7).map((c) => { const pct = (c.message_count / (s.top_channels[0]?.message_count || 1)) * 100; return (
{c.channel_name ?? c.channel_id.slice(0, 8)}
{formatNumber(c.message_count)}
); })}
0.8 ? "signal" : cleanRatio > 0.6 ? "amber" : "vermilion" } label={`${Math.round(cleanRatio * 100)}%`} sublabel="clean" />
} label="Clean" value={formatNumber(s.total_clean)} /> } label="Flagged" value={formatNumber(s.total_flagged)} /> } label="Warned" value={formatNumber(s.total_warned)} />
{/* Reactors + reactions */}
{(reactors ?? []).slice(0, 6).map((r, i) => { const maxNet = reactors?.[0]?.net_count || 1; const pct = Math.max(4, Math.round((r.net_count / maxNet) * 100)); return (
{i + 1} {r.username}
+{formatNumber(r.net_count)}
); })} {(reactors ?? []).length === 0 && }
{(reactions ?? []).slice(0, 5).map((m) => (
{m.top_emojis.slice(0, 3).map((e, i) => ( {e.emoji} ))}
{m.content || "(no text)"}
{m.username} · {m.channel_name ?? m.channel_id.slice(0, 8)}
{m.reaction_count}
))} {(reactions ?? []).length === 0 && }
); } function Row({ icon, label, value, }: { icon: React.ReactNode; label: string; value: string; }) { return (
{icon} {label} {value}
); } function Mini({ label, value, tone, }: { label: string; value: number; tone: "signal" | "amber" | "vermilion"; }) { const color = tone === "vermilion" ? "text-vermilion" : tone === "amber" ? "text-amber" : "text-signal"; return (
{value}
{label}
); } function EmptyHint() { return (
Awaiting data…
); }