From e157ae3ce4358d6f8e1d423da64182cf738574b4 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Tue, 25 Aug 2026 23:32:21 +0700 Subject: [PATCH] feat(frontend): add alert-priority HUD header with GSAP pulse to moderation route --- .../src/app/(dashboard)/moderation/view.tsx | 68 ++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/services/frontend/src/app/(dashboard)/moderation/view.tsx b/services/frontend/src/app/(dashboard)/moderation/view.tsx index 7209d185..64c35a23 100644 --- a/services/frontend/src/app/(dashboard)/moderation/view.tsx +++ b/services/frontend/src/app/(dashboard)/moderation/view.tsx @@ -1,5 +1,7 @@ "use client"; +import { useGSAP } from "@gsap/react"; +import gsap from "gsap"; import { AlertTriangle, Ban, @@ -13,7 +15,7 @@ import { UserX, XCircle, } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useAmbient } from "@/components/ambient/ambient-context"; import { CategoryDrilldown } from "@/components/CategoryDrilldown"; import { CoverageTiles } from "@/components/CoverageTiles"; @@ -58,6 +60,10 @@ import type { } from "@/lib/types"; import { staggerDelay } from "@/lib/utils"; +if (typeof window !== "undefined") { + gsap.registerPlugin(useGSAP); +} + const ACTION_ICON: Record = { delete_message: , mute_user: , @@ -127,6 +133,32 @@ export function ModerationView({ ); }, [failedRate, ambient]); + // Alert-priority HUD: a GSAP pulse loop on the inverted mono badge when + // there is anything pending review — cleared up when the queue empties. + const alertRef = useRef(null); + const pendingCount = stats?.pending ?? 0; + useGSAP( + () => { + if (!alertRef.current || pendingCount <= 0) return; + const prefersReduced = window.matchMedia( + "(prefers-reduced-motion: reduce)", + ).matches; + if (prefersReduced) return; + const tl = gsap.timeline({ repeat: -1, yoyo: true }); + tl.to(alertRef.current, { + opacity: 0.45, + duration: 0.7, + ease: "sine.inOut", + }); + return () => { + tl.kill(); + if (alertRef.current) + gsap.set(alertRef.current, { clearProps: "opacity" }); + }; + }, + { scope: alertRef, dependencies: [pendingCount] }, + ); + if (error && !stats) return void mutateStats()} />; if (!stats && isLoading) @@ -160,7 +192,39 @@ export function ModerationView({ ]; return ( -
+
+ {/* Alert-priority HUD header */} +
+
+
+ 0 ? "animate-ping bg-ink" : "bg-signal" + }`} + /> + 0 ? "bg-ink" : "bg-signal" + }`} + /> +
+

+ AUTO-MOD RADAR · ALERT_QUEUE +

+
+
0 + ? "border border-ink bg-ink text-canvas" + : "bg-surface text-ink-soft" + }`} + > + {pendingCount > 0 ? "PENDING" : "QUEUE CLEAR"} + {pendingCount > 0 && · {pendingCount}} +
+
+