feat(frontend): Ambient/WebGL console revamp + lint/type cleanup

Ground-up rebuild of the GMW frontend as an Ambient Field console:
- WebGL ambient background (Three.js shader, drifting motes, reduced-motion aware)
- Glassmorphism dark cyber theme across all 8 routes
- SSR page + client view split with SWR fallback; realtime via WebSocket
- Command palette (Cmd+K), chatbot FAB, guild/channel pickers
- Chart primitives: donut, radial-gauge, area-activity, sparkline, equalizer

Cleanup (review pass):
- Remove stray Puppeteer nav-test/nav-debug scripts
- Replace non-null assertions with guards (dashboard/moderation)
- Drop unused useGuilds fetches in messages/voice views
- Type implicit-any `let` declarations across pages
- Add a11y roles/labels to SVG charts and audio, tidy imports
This commit is contained in:
asepharyana
2026-08-15 20:03:55 +07:00
parent 3c2c1c3b15
commit 392db8eba1
47 changed files with 1178 additions and 590 deletions
@@ -1,27 +1,34 @@
"use client";
import { useEffect, useState } from "react";
import {
ShieldAlert,
CheckCircle2,
XCircle,
Clock,
Ban,
Trash2,
MicOff,
AlertTriangle,
UserX,
MessageSquareWarning,
Ban,
CheckCircle2,
Clock,
Filter,
MessageSquareWarning,
MicOff,
ShieldAlert,
Trash2,
UserX,
XCircle,
} from "lucide-react";
import {
useModerationStats,
useModerationActions,
} from "@/hooks";
import { useEffect, useState } from "react";
import { useAmbient } from "@/components/ambient/ambient-context";
import { GlassPanel, GlassCard, Badge, Select, type SelectOption } from "@/components/primitives";
import { SectionHeader, MetricTile, ErrorState, LoadingState } from "@/components/shared";
import { Donut } from "@/components/charts";
import {
Badge,
GlassPanel,
Select,
type SelectOption,
} from "@/components/primitives";
import {
ErrorState,
LoadingState,
MetricTile,
SectionHeader,
} from "@/components/shared";
import { useModerationActions, useModerationStats } from "@/hooks";
import { formatNumber } from "@/lib/format";
import type {
ModerationAction,
@@ -64,7 +71,7 @@ export function ModerationView({
const failedRate = stats ? stats.failed_rate * 100 : 0;
const byAction = stats?.by_action ?? {};
const segments = Object.entries(byAction).map(([k, v]) => ({
const segments = Object.entries(byAction).map(([k, _v]) => ({
value: 1,
color:
k === "ban_user" || k === "kick_user"
@@ -86,6 +93,7 @@ export function ModerationView({
if (error && !stats) return <ErrorState error={error} />;
if (!stats && isLoading) return <LoadingState label="Reading log" />;
if (!stats) return <ErrorState error={error ?? new Error("No data")} />;
const statusOpts: SelectOption[] = [
{ value: "", label: "All statuses" },
@@ -95,16 +103,39 @@ export function ModerationView({
];
const typeOpts: SelectOption[] = [
{ value: "", label: "All actions" },
...Object.keys(byAction).map((k) => ({ value: k, label: ACTION_LABEL[k as ModerationActionType] ?? k })),
...Object.keys(byAction).map((k) => ({
value: k,
label: ACTION_LABEL[k as ModerationActionType] ?? k,
})),
];
return (
<div className="space-y-5">
<div className="grid grid-cols-2 gap-3 md:grid-cols-4">
<MetricTile label="Total actions" value={formatNumber(stats!.total)} tone="signal" icon={<ShieldAlert className="size-3.5" />} />
<MetricTile label="Executed" value={formatNumber(stats!.executed)} tone="signal" icon={<CheckCircle2 className="size-3.5" />} />
<MetricTile label="Failed" value={formatNumber(stats!.failed)} tone={stats!.failed > 0 ? "vermilion" : "neutral"} icon={<XCircle className="size-3.5" />} />
<MetricTile label="Pending" value={formatNumber(stats!.pending)} tone={stats!.pending > 0 ? "amber" : "neutral"} icon={<Clock className="size-3.5" />} />
<MetricTile
label="Total actions"
value={formatNumber(stats.total)}
tone="signal"
icon={<ShieldAlert className="size-3.5" />}
/>
<MetricTile
label="Executed"
value={formatNumber(stats.executed)}
tone="signal"
icon={<CheckCircle2 className="size-3.5" />}
/>
<MetricTile
label="Failed"
value={formatNumber(stats.failed)}
tone={stats.failed > 0 ? "vermilion" : "neutral"}
icon={<XCircle className="size-3.5" />}
/>
<MetricTile
label="Pending"
value={formatNumber(stats?.pending)}
tone={stats?.pending > 0 ? "amber" : "neutral"}
icon={<Clock className="size-3.5" />}
/>
</div>
<div className="grid gap-5 lg:grid-cols-5">
@@ -112,7 +143,17 @@ export function ModerationView({
<SectionHeader eyebrow="health" title="Breakdown" />
<div className="flex items-center gap-5">
<Donut
segments={segments.length ? segments : [{ value: 1, color: "var(--color-ink-faint)", label: "none" }]}
segments={
segments.length
? segments
: [
{
value: 1,
color: "var(--color-ink-faint)",
label: "none",
},
]
}
centerLabel={`${Math.round(failedRate)}%`}
centerSub="fail rate"
/>
@@ -121,14 +162,22 @@ export function ModerationView({
const count = typeof v === "number" ? v : null;
return (
<div key={k} className="flex items-center gap-2.5">
<span className="text-ink-soft">{ACTION_ICON[k as ModerationActionType]}</span>
<span className="flex-1 text-ink-soft">{ACTION_LABEL[k as ModerationActionType] ?? k}</span>
{count !== null && <span className="mono text-ink">{count}</span>}
<span className="text-ink-soft">
{ACTION_ICON[k as ModerationActionType]}
</span>
<span className="flex-1 text-ink-soft">
{ACTION_LABEL[k as ModerationActionType] ?? k}
</span>
{count !== null && (
<span className="mono text-ink">{count}</span>
)}
</div>
);
})}
{Object.keys(byAction).length === 0 && (
<div className="text-xs text-ink-faint">No actions recorded yet.</div>
<div className="text-xs text-ink-faint">
No actions recorded yet.
</div>
)}
</div>
</div>
@@ -141,8 +190,20 @@ export function ModerationView({
action={
<div className="flex items-center gap-2">
<Filter className="size-3.5 text-ink-faint" />
<Select value={typeFilter} onChange={setTypeFilter} options={typeOpts} size="sm" className="w-36" />
<Select value={statusFilter} onChange={setStatusFilter} options={statusOpts} size="sm" className="w-32" />
<Select
value={typeFilter}
onChange={setTypeFilter}
options={typeOpts}
size="sm"
className="w-36"
/>
<Select
value={statusFilter}
onChange={setStatusFilter}
options={statusOpts}
size="sm"
className="w-32"
/>
</div>
}
/>
@@ -151,7 +212,9 @@ export function ModerationView({
<ActionRow key={a.id} a={a} />
))}
{(actions ?? []).length === 0 && (
<div className="py-10 text-center text-xs text-ink-faint">No matching actions.</div>
<div className="py-10 text-center text-xs text-ink-faint">
No matching actions.
</div>
)}
</div>
</GlassPanel>
@@ -162,26 +225,42 @@ export function ModerationView({
function ActionRow({ a }: { a: ModerationAction }) {
const tone =
a.status === "executed" ? "signal" : a.status === "failed" ? "vermilion" : "amber";
const icon = ACTION_ICON[a.action_type] ?? <AlertTriangle className="size-3.5" />;
a.status === "executed"
? "signal"
: a.status === "failed"
? "vermilion"
: "amber";
const icon = ACTION_ICON[a.action_type] ?? (
<AlertTriangle className="size-3.5" />
);
return (
<div className="flex items-start gap-3 rounded-[10px] border border-hairline bg-white/[0.03] p-3">
<span className={`mt-0.5 ${tone === "vermilion" ? "text-vermilion" : tone === "amber" ? "text-amber" : "text-signal"}`}>{icon}</span>
<span
className={`mt-0.5 ${tone === "vermilion" ? "text-vermilion" : tone === "amber" ? "text-amber" : "text-signal"}`}
>
{icon}
</span>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-ink">{a.username ?? "unknown"}</span>
<span className="text-sm font-semibold text-ink">
{a.username ?? "unknown"}
</span>
<Badge tone={tone}>{a.status}</Badge>
<span className="mono ml-auto text-[0.6rem] text-ink-faint">
{a.created_at ? new Date(a.created_at).toLocaleString() : "—"}
</span>
</div>
{a.reason && <div className="mt-0.5 text-xs text-ink-soft">{a.reason}</div>}
{a.reason && (
<div className="mt-0.5 text-xs text-ink-soft">{a.reason}</div>
)}
{a.content && (
<div className="mt-1 line-clamp-2 rounded-[8px] bg-white/[0.03] px-2 py-1 text-xs text-ink-faint">
{a.content}
</div>
)}
{a.error && <div className="mt-1 text-xs text-vermilion">{a.error}</div>}
{a.error && (
<div className="mt-1 text-xs text-vermilion">{a.error}</div>
)}
</div>
</div>
);