elmeg-demo/frontend/app/mod/page.tsx

154 lines
6.4 KiB
TypeScript

"use client"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Check, X, ShieldAlert } from "lucide-react"
import { useEffect, useState } from "react"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { getApiUrl } from "@/lib/api-config"
interface PendingNickname {
id: number
performance_id: number
nickname: string
suggested_by: number
created_at: string
}
export default function ModDashboardPage() {
const [pendingNicknames, setPendingNicknames] = useState<PendingNickname[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState("")
useEffect(() => {
fetchQueue()
}, [])
const fetchQueue = async () => {
const token = localStorage.getItem("token")
if (!token) {
setError("Not authenticated")
setLoading(false)
return
}
try {
const res = await fetch(`${getApiUrl()}/moderation/queue/nicknames`, {
headers: { Authorization: `Bearer ${token}` }
})
if (res.status === 403) {
setError("Access denied: Moderators only")
setLoading(false)
return
}
if (!res.ok) throw new Error("Failed to fetch queue")
const data = await res.json()
setPendingNicknames(data)
} catch (err) {
console.error(err)
setError("Failed to load moderation queue")
} finally {
setLoading(false)
}
}
const handleAction = 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: "POST",
headers: { Authorization: `Bearer ${token}` }
})
if (!res.ok) throw new Error(`Failed to ${action}`)
// Remove from list
setPendingNicknames(prev => prev.filter(n => n.id !== id))
} catch (err) {
console.error(err)
alert(`Error: ${err}`)
}
}
if (loading) return <div className="p-8 text-center">Loading dashboard...</div>
if (error) return <div className="p-8 text-center text-red-500 font-bold">{error}</div>
return (
<div className="flex flex-col gap-6">
<h1 className="text-3xl font-bold tracking-tight flex items-center gap-2">
<ShieldAlert className="h-8 w-8 text-primary" />
Moderator Dashboard
</h1>
<Tabs defaultValue="nicknames">
<TabsList>
<TabsTrigger value="nicknames">Nicknames ({pendingNicknames.length})</TabsTrigger>
<TabsTrigger value="reports">Reports (0)</TabsTrigger>
</TabsList>
<TabsContent value="nicknames">
<Card>
<CardHeader>
<CardTitle>Pending Nicknames</CardTitle>
</CardHeader>
<CardContent>
{pendingNicknames.length === 0 ? (
<p className="text-muted-foreground py-8 text-center">No pending nicknames. Good job!</p>
) : (
<div className="space-y-4">
{pendingNicknames.map((item) => (
<div key={item.id} className="flex items-center justify-between border p-4 rounded-lg">
<div>
<p className="font-bold text-lg">"{item.nickname}"</p>
<p className="text-sm text-muted-foreground">
Performance #{item.performance_id} User #{item.suggested_by}
</p>
<p className="text-xs text-muted-foreground mt-1">
{new Date(item.created_at).toLocaleDateString()}
</p>
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="outline"
className="text-green-600 hover:text-green-700 hover:bg-green-50"
onClick={() => handleAction(item.id, "approve")}
>
<Check className="h-4 w-4 mr-1" /> Approve
</Button>
<Button
size="sm"
variant="outline"
className="text-red-600 hover:text-red-700 hover:bg-red-50"
onClick={() => handleAction(item.id, "reject")}
>
<X className="h-4 w-4 mr-1" /> Reject
</Button>
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
</TabsContent>
<TabsContent value="reports">
<Card>
<CardHeader>
<CardTitle>User Reports</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">Report queue coming soon...</p>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
)
}