/** * Admin content reports management page. * * Lists user-submitted issue reports with filtering by status, * inline triage (acknowledge/resolve/dismiss), and admin notes. */ import { useEffect, useState } from "react"; import { fetchReports, updateReport, type ContentReport, } from "../api/public-client"; const STATUS_OPTIONS = [ { value: "", label: "All" }, { value: "open", label: "Open" }, { value: "acknowledged", label: "Acknowledged" }, { value: "resolved", label: "Resolved" }, { value: "dismissed", label: "Dismissed" }, ]; const STATUS_ACTIONS: Record = { open: [ { label: "Acknowledge", next: "acknowledged" }, { label: "Resolve", next: "resolved" }, { label: "Dismiss", next: "dismissed" }, ], acknowledged: [ { label: "Resolve", next: "resolved" }, { label: "Dismiss", next: "dismissed" }, { label: "Reopen", next: "open" }, ], resolved: [{ label: "Reopen", next: "open" }], dismissed: [{ label: "Reopen", next: "open" }], }; function formatDate(iso: string): string { return new Date(iso).toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }); } function reportTypeLabel(rt: string): string { return rt.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()); } export default function AdminReports() { const [reports, setReports] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [statusFilter, setStatusFilter] = useState(""); const [expandedId, setExpandedId] = useState(null); const [noteText, setNoteText] = useState(""); const [actionLoading, setActionLoading] = useState(null); const load = async () => { setLoading(true); setError(null); try { const res = await fetchReports({ status: statusFilter || undefined, limit: 100, }); setReports(res.items); setTotal(res.total); } catch (err) { setError(err instanceof Error ? err.message : "Failed to load reports"); } finally { setLoading(false); } }; useEffect(() => { void load(); }, [statusFilter]); const handleAction = async (reportId: string, newStatus: string) => { setActionLoading(reportId); try { const updated = await updateReport(reportId, { status: newStatus, ...(noteText.trim() ? { admin_notes: noteText.trim() } : {}), }); setReports((prev) => prev.map((r) => (r.id === reportId ? updated : r)), ); setNoteText(""); if (newStatus === "resolved" || newStatus === "dismissed") { setExpandedId(null); } } catch (err) { setError(err instanceof Error ? err.message : "Action failed"); } finally { setActionLoading(null); } }; const toggleExpand = (id: string) => { if (expandedId === id) { setExpandedId(null); setNoteText(""); } else { setExpandedId(id); const report = reports.find((r) => r.id === id); setNoteText(report?.admin_notes ?? ""); } }; return (

Content Reports

{total} report{total !== 1 ? "s" : ""} total

{/* Status filter */}
{STATUS_OPTIONS.map((opt) => ( ))}
{/* Content */} {loading ? (
Loading reports…
) : error ? (
Error: {error}
) : reports.length === 0 ? (
{statusFilter ? `No ${statusFilter} reports.` : "No reports yet."}
) : (
{reports.map((report) => (
toggleExpand(report.id)} >
{report.status} {reportTypeLabel(report.report_type)} {formatDate(report.created_at)}
{report.content_title && ( {report.content_title} )} {report.description.length > 120 ? report.description.slice(0, 120) + "…" : report.description}
{expandedId === report.id && (
Full description:

{report.description}

{report.page_url && ( )}
Type: {report.content_type} {report.content_id && ID: {report.content_id.slice(0, 8)}…} {report.resolved_at && ( Resolved: {formatDate(report.resolved_at)} )}
{/* Admin notes */}