import React, { useEffect, useState } from "react"; import { useStore } from "@nanostores/react"; import { $user } from "../../store/auth"; import { checkAdminAccess, getAdminReports, adminTakeAction, adminCreateLabel, adminDeleteLabel, adminGetLabels, } from "../../api/client"; import type { ModerationReport, HydratedLabel } from "../../types"; import { Shield, CheckCircle, XCircle, AlertTriangle, Eye, ChevronDown, ChevronUp, Tag, FileText, Plus, Trash2, EyeOff, } from "lucide-react"; import { Avatar, EmptyState, Skeleton, Button } from "../../components/ui"; import { Link } from "react-router-dom"; const STATUS_COLORS: Record = { pending: "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300", resolved: "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300", dismissed: "bg-surface-100 text-surface-600 dark:bg-surface-800 dark:text-surface-400", escalated: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300", acknowledged: "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300", }; const REASON_LABELS: Record = { spam: "Spam", violation: "Rule Violation", misleading: "Misleading", sexual: "Inappropriate", rude: "Rude / Harassing", other: "Other", }; const LABEL_OPTIONS = [ { val: "sexual", label: "Sexual Content" }, { val: "nudity", label: "Nudity" }, { val: "violence", label: "Violence" }, { val: "gore", label: "Graphic Content" }, { val: "spam", label: "Spam" }, { val: "misleading", label: "Misleading" }, ]; type Tab = "reports" | "labels" | "actions"; export default function AdminModeration() { const user = useStore($user); const [isAdmin, setIsAdmin] = useState(false); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState("reports"); const [reports, setReports] = useState([]); const [pendingCount, setPendingCount] = useState(0); const [totalCount, setTotalCount] = useState(0); const [statusFilter, setStatusFilter] = useState("pending"); const [expandedReport, setExpandedReport] = useState(null); const [actionLoading, setActionLoading] = useState(null); const [labels, setLabels] = useState([]); const [labelSrc, setLabelSrc] = useState(""); const [labelUri, setLabelUri] = useState(""); const [labelVal, setLabelVal] = useState(""); const [labelSubmitting, setLabelSubmitting] = useState(false); const [labelSuccess, setLabelSuccess] = useState(false); const loadReports = async (status: string) => { const data = await getAdminReports(status || undefined); setReports(data.items); setPendingCount(data.pendingCount); setTotalCount(data.totalItems); }; const loadLabels = async () => { const data = await adminGetLabels(); setLabels(data.items || []); }; useEffect(() => { const init = async () => { const admin = await checkAdminAccess(); setIsAdmin(admin); if (admin) await loadReports("pending"); setLoading(false); }; init(); }, []); const handleTabChange = async (tab: Tab) => { setActiveTab(tab); if (tab === "labels") await loadLabels(); }; const handleFilterChange = async (status: string) => { setStatusFilter(status); await loadReports(status); }; const handleAction = async (reportId: number, action: string) => { setActionLoading(reportId); const success = await adminTakeAction({ reportId, action }); if (success) { await loadReports(statusFilter); setExpandedReport(null); } setActionLoading(null); }; const handleCreateLabel = async () => { if (!labelVal || (!labelSrc && !labelUri)) return; setLabelSubmitting(true); const success = await adminCreateLabel({ src: labelSrc || labelUri, uri: labelUri || undefined, val: labelVal, }); if (success) { setLabelSrc(""); setLabelUri(""); setLabelVal(""); setLabelSuccess(true); setTimeout(() => setLabelSuccess(false), 2000); if (activeTab === "labels") await loadLabels(); } setLabelSubmitting(false); }; const handleDeleteLabel = async (id: number) => { if (!window.confirm("Remove this label?")) return; const success = await adminDeleteLabel(id); if (success) setLabels((prev) => prev.filter((l) => l.id !== id)); }; if (loading) { return (
); } if (!user || !isAdmin) { return ( } title="Access Denied" message="You don't have permission to access the moderation dashboard." /> ); } return (

Moderation

{pendingCount} pending · {totalCount} total reports

{[ { id: "reports" as Tab, label: "Reports", icon: , }, { id: "actions" as Tab, label: "Actions", icon: , }, { id: "labels" as Tab, label: "Labels", icon: }, ].map((tab) => ( ))}
{activeTab === "reports" && ( <>
{["pending", "resolved", "dismissed", "escalated", ""].map( (status) => ( ), )}
{reports.length === 0 ? ( } title="No reports" message={ statusFilter === "pending" ? "No pending reports to review." : `No ${statusFilter || ""} reports found.` } /> ) : (
{reports.map((report) => (
{expandedReport === report.id && (
Reported User @{report.subject.handle || report.subject.did}
Reporter @{report.reporter.handle || report.reporter.did}
{report.reasonText && (
Details

{report.reasonText}

)} {report.subjectUri && (
Content URI

{report.subjectUri}

)} {report.status === "pending" && (
)}
)}
))}
)} )} {activeTab === "actions" && (

Apply Content Warning

Add a content warning label to a specific post or account. Users will see a blur overlay with the option to reveal.

setLabelSrc(e.target.value)} placeholder="did:plc:..." className="w-full px-3 py-2 text-sm rounded-lg border border-surface-200 dark:border-surface-700 bg-white dark:bg-surface-800 text-surface-900 dark:text-white placeholder:text-surface-400 focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500" />
setLabelUri(e.target.value)} placeholder="at://did:plc:.../at.margin.annotation/..." className="w-full px-3 py-2 text-sm rounded-lg border border-surface-200 dark:border-surface-700 bg-white dark:bg-surface-800 text-surface-900 dark:text-white placeholder:text-surface-400 focus:outline-none focus:ring-2 focus:ring-primary-500/20 focus:border-primary-500" />
{LABEL_OPTIONS.map((opt) => ( ))}
{labelSuccess && ( Label applied )}
)} {activeTab === "labels" && (
{labels.length === 0 ? ( } title="No labels" message="No content labels have been applied yet." /> ) : (
{labels.map((label) => (
{label.subject && ( )}
{label.val} {label.subject && ( @{label.subject.handle || label.subject.did} )}

{label.uri !== label.src ? label.uri : "Account-level label"}{" "} · {new Date(label.createdAt).toLocaleDateString()} · by @ {label.createdBy.handle || label.createdBy.did}

))}
)}
)}
); }