diff --git a/services/frontend/src/components/dashboard/live-stream.tsx b/services/frontend/src/components/dashboard/live-stream.tsx new file mode 100644 index 0000000..3502cd8 --- /dev/null +++ b/services/frontend/src/components/dashboard/live-stream.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; +import { GlassCard } from "@/components/glass/card"; +import { useWebSocket } from "@/lib/ws/context"; +import { cn } from "@/lib/utils"; + +interface LiveMessage { + id: string; + content: string; + username: string; + channelName?: string; + timestamp: string; + flagged?: boolean; +} + +export function LiveStream() { + const [messages, setMessages] = useState([]); + const scrollRef = useRef(null); + const ws = useWebSocket(); + + useEffect(() => { + const unsub = ws.on("message_created", (data: any) => { + const msg: LiveMessage = { + id: data.id, + content: data.content || "(attachment)", + username: data.username || "unknown", + channelName: data.channelName, + timestamp: new Date().toLocaleTimeString(), + flagged: data.ai_status === "flagged" || data.ai_status === "warn", + }; + setMessages((prev) => [msg, ...prev].slice(0, 50)); + }); + return () => unsub(); + }, [ws]); + + useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTop = 0; + } + }, [messages]); + + return ( + +
+ + + + + + Live Stream + +
+
+ {messages.length === 0 ? ( +
+ Waiting for messages... +
+ ) : ( + messages.map((msg) => ( +
+ + {msg.username} + + + {msg.content} + + + {msg.timestamp} + +
+ )) + )} +
+
+ ); +} diff --git a/services/frontend/src/components/dashboard/mod-queue.tsx b/services/frontend/src/components/dashboard/mod-queue.tsx new file mode 100644 index 0000000..ddd031e --- /dev/null +++ b/services/frontend/src/components/dashboard/mod-queue.tsx @@ -0,0 +1,70 @@ +"use client"; + +import { AlertCircle, Check, Trash2 } from "lucide-react"; +import { GlassCard } from "@/components/glass/card"; +import { cn } from "@/lib/utils"; + +interface ModQueueItem { + id: string; + content: string; + username: string; + severity: "low" | "medium" | "high" | "critical"; + reason: string; +} + +export function ModQueue({ items = [] }: { items?: ModQueueItem[] }) { + const severityColor = { + low: "text-accent-amber border-accent-amber/30", + medium: "text-accent-purple border-accent-purple/30", + high: "text-destructive border-destructive/40", + critical: "text-destructive border-destructive/60 bg-destructive/10", + }; + + return ( + +
+ + + Mod Queue + + {items.length > 0 && ( + + {items.length} pending + + )} +
+
+ {items.length === 0 ? ( +
+ No flagged messages +
+ ) : ( + items.map((item) => ( +
+
+ {item.username} + {item.severity} +
+

{item.content}

+

{item.reason}

+
+ + +
+
+ )) + )} +
+
+ ); +}