"use client" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Check, X, ShieldAlert, Search, Ban, UserCheck, CheckCircle } 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" import { Input } from "@/components/ui/input" import { Checkbox } from "@/components/ui/checkbox" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Textarea } from "@/components/ui/textarea" interface PendingNickname { id: number performance_id: number nickname: string suggested_by: number 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 } interface QueueStats { pending_nicknames: number pending_reports: number total_bans: number } interface UserLookup { id: number email: string username: string | null role: string is_active: boolean email_verified: boolean stats: { ratings: number reviews: number comments: number attendances: number reports_submitted: number } } export default function ModDashboardPage() { const [pendingNicknames, setPendingNicknames] = useState([]) const [pendingReports, setPendingReports] = useState([]) const [stats, setStats] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState("") // User lookup const [lookupQuery, setLookupQuery] = useState("") const [lookupUser, setLookupUser] = useState(null) const [lookupLoading, setLookupLoading] = useState(false) // Ban dialog const [banDialogOpen, setBanDialogOpen] = useState(false) const [banDuration, setBanDuration] = useState("24") const [banReason, setBanReason] = useState("") // Bulk selection const [selectedNicknames, setSelectedNicknames] = useState([]) const [selectedReports, setSelectedReports] = useState([]) useEffect(() => { fetchQueue() }, []) const fetchQueue = async () => { const token = localStorage.getItem("token") if (!token) { setError("Not authenticated") setLoading(false) return } try { const [nicknamesRes, reportsRes, statsRes] = await Promise.all([ fetch(`${getApiUrl()}/moderation/queue/nicknames`, { headers: { Authorization: `Bearer ${token}` } }), fetch(`${getApiUrl()}/moderation/queue/reports`, { headers: { Authorization: `Bearer ${token}` } }), fetch(`${getApiUrl()}/moderation/queue/stats`, { headers: { Authorization: `Bearer ${token}` } }) ]) if (nicknamesRes.status === 403 || reportsRes.status === 403) { setError("Access denied: Moderators only") setLoading(false) return } if (!nicknamesRes.ok || !reportsRes.ok) throw new Error("Failed to fetch queue") setPendingNicknames(await nicknamesRes.json()) setPendingReports(await reportsRes.json()) if (statsRes.ok) setStats(await statsRes.json()) } catch (err) { console.error(err) setError("Failed to load moderation queue") } finally { setLoading(false) } } const handleUserLookup = async () => { if (!lookupQuery.trim()) return const token = localStorage.getItem("token") if (!token) return setLookupLoading(true) try { const res = await fetch(`${getApiUrl()}/moderation/users/lookup?query=${encodeURIComponent(lookupQuery)}`, { headers: { Authorization: `Bearer ${token}` } }) if (res.ok) { setLookupUser(await res.json()) } else { setLookupUser(null) alert("User not found") } } catch (e) { console.error(e) } finally { setLookupLoading(false) } } const handleBanUser = async () => { if (!lookupUser) return const token = localStorage.getItem("token") if (!token) return try { const res = await fetch(`${getApiUrl()}/moderation/users/ban`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify({ user_id: lookupUser.id, duration_hours: parseInt(banDuration), reason: banReason }) }) if (res.ok) { setLookupUser({ ...lookupUser, is_active: false }) setBanDialogOpen(false) setBanReason("") } } catch (e) { console.error(e) } } const handleUnbanUser = async () => { if (!lookupUser) return const token = localStorage.getItem("token") if (!token) return try { const res = await fetch(`${getApiUrl()}/moderation/users/${lookupUser.id}/unban`, { method: "POST", headers: { Authorization: `Bearer ${token}` } }) if (res.ok) { setLookupUser({ ...lookupUser, is_active: true }) } } catch (e) { console.error(e) } } const handleNicknameAction = async (id: number, action: "approve" | "reject") => { const token = localStorage.getItem("token") if (!token) return try { const res = await fetch(`${getApiUrl()}/moderation/nicknames/${id}/${action}`, { method: "PUT", headers: { Authorization: `Bearer ${token}` } }) if (!res.ok) throw new Error(`Failed to ${action}`) setPendingNicknames(prev => prev.filter(n => n.id !== id)) } catch (err) { console.error(err) alert(`Error: ${err}`) } } const handleReportAction = async (id: number, action: "resolve" | "dismiss") => { const token = localStorage.getItem("token") if (!token) return try { 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}`) } } const handleBulkNicknames = async (action: "approve" | "reject") => { if (selectedNicknames.length === 0) return const token = localStorage.getItem("token") if (!token) return try { const res = await fetch(`${getApiUrl()}/moderation/nicknames/bulk`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify({ ids: selectedNicknames, action }) }) if (res.ok) { setPendingNicknames(prev => prev.filter(n => !selectedNicknames.includes(n.id))) setSelectedNicknames([]) } } catch (e) { console.error(e) } } const handleBulkReports = async (action: "resolve" | "dismiss") => { if (selectedReports.length === 0) return const token = localStorage.getItem("token") if (!token) return try { const res = await fetch(`${getApiUrl()}/moderation/reports/bulk`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, body: JSON.stringify({ ids: selectedReports, action }) }) if (res.ok) { setPendingReports(prev => prev.filter(r => !selectedReports.includes(r.id))) setSelectedReports([]) } } catch (e) { console.error(e) } } if (loading) return
Loading dashboard...
if (error) return
{error}
return (

Moderator Dashboard

{/* Stats Cards */} {stats && (

{stats.pending_nicknames}

Pending Nicknames

{stats.pending_reports}

Pending Reports

{stats.total_bans}

Banned Users

)} Reports ({pendingReports.length}) Nicknames ({pendingNicknames.length}) User Lookup User Reports {selectedReports.length > 0 && (
)}
{pendingReports.length === 0 ? (

No pending reports. Community is safe! 🛡️

) : (
{pendingReports.map(report => (
{ if (checked) { setSelectedReports([...selectedReports, report.id]) } else { setSelectedReports(selectedReports.filter(id => id !== report.id)) } }} />
{report.reason} {report.entity_type} #{report.entity_id}
{report.details && (

"{report.details}"

)}

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

))}
)}
Pending Nicknames {selectedNicknames.length > 0 && (
)}
{pendingNicknames.length === 0 ? (

No pending nicknames.

) : (
{pendingNicknames.map((item) => (
{ if (checked) { setSelectedNicknames([...selectedNicknames, item.id]) } else { setSelectedNicknames(selectedNicknames.filter(id => id !== item.id)) } }} />

"{item.nickname}"

Performance #{item.performance_id} • User #{item.suggested_by}

))}
)}
User Lookup
setLookupQuery(e.target.value)} className="pl-9" onKeyDown={(e) => e.key === "Enter" && handleUserLookup()} />
{lookupUser && (

{lookupUser.username || "No username"}

{lookupUser.email}

{lookupUser.role} {lookupUser.is_active ? ( Active ) : ( Banned )} {lookupUser.email_verified && ( Verified )}
{lookupUser.is_active ? ( ) : ( )}

{lookupUser.stats.ratings}

Ratings

{lookupUser.stats.reviews}

Reviews

{lookupUser.stats.comments}

Comments

{lookupUser.stats.attendances}

Shows

{lookupUser.stats.reports_submitted}

Reports

)}
{/* Ban Dialog */} Ban User

Banning {lookupUser?.email}