From 499a9fa352716a167447c7cf4520b03d7a3852ea Mon Sep 17 00:00:00 2001 From: fullsizemalt <106900403+fullsizemalt@users.noreply.github.com> Date: Sun, 21 Dec 2025 02:49:11 -0800 Subject: [PATCH] feat(frontend): Implement Admin Dashboard for moderation --- frontend/app/mod/page.tsx | 129 ++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 27 deletions(-) diff --git a/frontend/app/mod/page.tsx b/frontend/app/mod/page.tsx index 1186a49..d3f199c 100644 --- a/frontend/app/mod/page.tsx +++ b/frontend/app/mod/page.tsx @@ -2,10 +2,11 @@ import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Check, X, ShieldAlert } from "lucide-react" +import { Check, X, ShieldAlert, AlertTriangle } from "lucide-react" import { useEffect, useState } from "react" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { getApiUrl } from "@/lib/api-config" +import { Badge } from "@/components/ui/badge" interface PendingNickname { id: number @@ -15,8 +16,20 @@ interface PendingNickname { created_at: string } +interface PendingReport { + id: number + entity_type: string + entity_id: number + reason: string + details?: string + user_id: number + created_at: string + status: string +} + export default function ModDashboardPage() { const [pendingNicknames, setPendingNicknames] = useState([]) + const [pendingReports, setPendingReports] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState("") @@ -33,20 +46,21 @@ export default function ModDashboardPage() { } try { - const res = await fetch(`${getApiUrl()}/moderation/queue/nicknames`, { - headers: { Authorization: `Bearer ${token}` } - }) + const [nicknamesRes, reportsRes] = await Promise.all([ + fetch(`${getApiUrl()}/moderation/queue/nicknames`, { headers: { Authorization: `Bearer ${token}` } }), + fetch(`${getApiUrl()}/moderation/queue/reports`, { headers: { Authorization: `Bearer ${token}` } }) + ]) - if (res.status === 403) { + if (nicknamesRes.status === 403 || reportsRes.status === 403) { setError("Access denied: Moderators only") setLoading(false) return } - if (!res.ok) throw new Error("Failed to fetch queue") + if (!nicknamesRes.ok || !reportsRes.ok) throw new Error("Failed to fetch queue") - const data = await res.json() - setPendingNicknames(data) + setPendingNicknames(await nicknamesRes.json()) + setPendingReports(await reportsRes.json()) } catch (err) { console.error(err) setError("Failed to load moderation queue") @@ -55,13 +69,14 @@ export default function ModDashboardPage() { } } - const handleAction = async (id: number, action: "approve" | "reject") => { + const handleNicknameAction = async (id: number, action: "approve" | "reject") => { const token = localStorage.getItem("token") if (!token) return try { + // Note: Updated backend uses PUT for nicknames const res = await fetch(`${getApiUrl()}/moderation/nicknames/${id}/${action}`, { - method: "POST", + method: "PUT", headers: { Authorization: `Bearer ${token}` } }) @@ -75,22 +90,93 @@ export default function ModDashboardPage() { } } + const handleReportAction = async (id: number, action: "resolve" | "dismiss") => { + const token = localStorage.getItem("token") + if (!token) return + + try { + // Note: Updated backend uses PUT for reports + const res = await fetch(`${getApiUrl()}/moderation/reports/${id}/${action}`, { + method: "PUT", + headers: { Authorization: `Bearer ${token}` } + }) + + if (!res.ok) throw new Error(`Failed to ${action}`) + + setPendingReports(prev => prev.filter(r => r.id !== id)) + } catch (err) { + console.error(err) + alert(`Error: ${err}`) + } + } + if (loading) return
Loading dashboard...
if (error) return
{error}
return ( -
+

Moderator Dashboard

- + + Reports ({pendingReports.length}) Nicknames ({pendingNicknames.length}) - Reports (0) + + + + User Reports + + + {pendingReports.length === 0 ? ( +

No pending reports. Community is safe! 🛡️

+ ) : ( +
+ {pendingReports.map(report => ( +
+
+
+ + {report.reason} + + {report.entity_type} #{report.entity_id} +
+ {report.details && ( +

"{report.details}"

+ )} +

+ Reported by User #{report.user_id} • {new Date(report.created_at).toLocaleString()} +

+
+
+ + +
+
+ ))} +
+ )} +
+
+
+ @@ -98,7 +184,7 @@ export default function ModDashboardPage() { {pendingNicknames.length === 0 ? ( -

No pending nicknames. Good job!

+

No pending nicknames.

) : (
{pendingNicknames.map((item) => ( @@ -117,7 +203,7 @@ export default function ModDashboardPage() { size="sm" variant="outline" className="text-green-600 hover:text-green-700 hover:bg-green-50" - onClick={() => handleAction(item.id, "approve")} + onClick={() => handleNicknameAction(item.id, "approve")} > Approve @@ -125,7 +211,7 @@ export default function ModDashboardPage() { size="sm" variant="outline" className="text-red-600 hover:text-red-700 hover:bg-red-50" - onClick={() => handleAction(item.id, "reject")} + onClick={() => handleNicknameAction(item.id, "reject")} > Reject @@ -137,17 +223,6 @@ export default function ModDashboardPage() { - - - - - User Reports - - -

Report queue coming soon...

-
-
-
)