import React, { useEffect, useState } from 'react' function tagList(item) { return Array.isArray(item?.tags) ? item.tags.filter(Boolean) : [] } function categoryList(item) { return Array.isArray(item?.categories) ? item.categories.filter(Boolean) : [] } function creatorName(item) { return item?.user?.username || item?.user?.name || 'Unknown creator' } function isMature(item) { return Boolean(item?.nsfw || item?.is_mature) } function formatDate(value) { if (!value) { return null } const date = new Date(value) if (Number.isNaN(date.getTime())) { return null } return date.toLocaleString() } function PreviewImage({ src, alt, className }) { const [failed, setFailed] = useState(false) useEffect(() => { setFailed(false) }, [src]) if (!src || failed) { return (
No preview
) } return ( {alt} setFailed(true)} /> ) } function Badge({ children, tone = 'slate' }) { const tones = { slate: 'border-white/10 bg-white/5 text-white/70', amber: 'border-amber-300/20 bg-amber-400/10 text-amber-100', rose: 'border-rose-300/20 bg-rose-400/10 text-rose-100', emerald: 'border-emerald-300/20 bg-emerald-400/10 text-emerald-100', } return ( {children} ) } export default function AdminUploadQueue() { const [items, setItems] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [notes, setNotes] = useState({}) const [selected, setSelected] = useState(null) const [busyId, setBusyId] = useState(null) const loadPending = async () => { setLoading(true) setError('') try { const [legacyResponse, artworkResponse] = await Promise.all([ window.axios.get('/api/admin/uploads/pending'), window.axios.get('/api/admin/artwork-review/pending'), ]) const legacyItems = Array.isArray(legacyResponse?.data?.data) ? legacyResponse.data.data.map((item) => ({ ...item, queue: 'upload' })) : [] const artworkItems = Array.isArray(artworkResponse?.data?.data) ? artworkResponse.data.data.map((item) => ({ ...item, queue: 'artwork' })) : [] setItems([...artworkItems, ...legacyItems]) } catch (loadError) { setError(loadError?.response?.data?.message || 'Failed to load moderation queue.') } finally { setLoading(false) } } useEffect(() => { loadPending() }, []) useEffect(() => { if (!selected) { return undefined } const onKeyDown = (event) => { if (event.key === 'Escape') { setSelected(null) } } window.addEventListener('keydown', onKeyDown) return () => window.removeEventListener('keydown', onKeyDown) }, [selected]) const moderate = async (item, action) => { const id = item?.id if (!id || busyId) { return } setBusyId(id) try { const payload = { note: String(notes[id] || '') } const base = item.queue === 'artwork' ? '/api/admin/artwork-review' : '/api/admin/uploads' await window.axios.post(`${base}/${id}/${action}`, payload) setItems((prev) => prev.filter((candidate) => !(candidate.id === id && candidate.queue === item.queue))) setSelected((current) => (current?.id === id && current?.queue === item.queue ? null : current)) } catch (moderateError) { setError(moderateError?.response?.data?.message || `Failed to ${action} upload.`) } finally { setBusyId(null) } } const renderMeta = (item, { compact = false } = {}) => { const tags = tagList(item) const categories = categoryList(item) const createdAt = formatDate(item.created_at) return (
{item.title || '(untitled upload)'}
{item.queue === 'artwork' ? 'Artwork' : 'Draft upload'} {isMature(item) ? NSFW : null}
{creatorName(item)} · {item.type} · {item.id}
{createdAt ?
{createdAt}
: null} {item.description ? (

{item.description}

) : (

No description

)} {categories.length > 0 ? (
{categories.join(' · ')}
) : null} {tags.length > 0 ? (
{(compact ? tags.slice(0, 10) : tags).map((tag) => ( {tag} ))}
) : (
No tags
)} {item.moderation_note ? (
Review reasons: {item.moderation_note}
) : null}
) } const renderActions = (item) => (
setNotes((prev) => ({ ...prev, [item.id]: event.target.value }))} placeholder="Moderation note" className="w-full rounded-lg border border-white/15 bg-white/10 px-3 py-2 text-xs text-white" />
) return (

Pending Upload Moderation

{loading ?

Loading…

: null} {error ?

{error}

: null} {!loading && items.length === 0 ?

No pending uploads.

: null} {selected ? (
setSelected(null)} >
event.stopPropagation()} >
{selected.title || '(untitled upload)'}
{creatorName(selected)} · {selected.type} · {selected.id}
Description

{selected.description || 'No description'}

Categories

{categoryList(selected).join(', ') || 'None'}

Tags
{tagList(selected).length > 0 ? tagList(selected).map((tag) => ( {tag} )) : None}
Visibility

{selected.visibility || 'n/a'}

Maturity

{isMature(selected) ? 'NSFW / mature' : (selected.maturity_status || 'safe')}

License

{selected.license || 'n/a'}

Submitted

{formatDate(selected.created_at) || 'n/a'}

{selected.moderation_note ? (
Review reasons

{selected.moderation_note}

) : null}
{renderActions(selected)}
) : null}
) }