/* global React, Icon, Eyebrow, Badge, Button, api */ const { useState: useStateRQ, useEffect: useEffectRQ } = React; function ReviewQueue({ onChanged }){ const [tab, setTab] = useStateRQ("pending"); const [clips, setClips] = useStateRQ([]); const [loading, setLoading] = useStateRQ(true); const [busyId, setBusyId] = useStateRQ(null); const [viewsDraft, setViewsDraft] = useStateRQ({}); const [reasonDraft, setReasonDraft] = useStateRQ({}); const reload = async () => { setLoading(true); const r = tab === "pending" ? await api.listPendingClips() : await api.listAllClips(); setClips(r.error ? [] : r.data); setLoading(false); }; useEffectRQ(() => { reload(); }, [tab]); const setViews = (id, v) => setViewsDraft(d => ({...d, [id]: v})); const setReason = (id, v) => setReasonDraft(d => ({...d, [id]: v})); const approve = async (clip) => { const draft = viewsDraft[clip.id]; const views = draft !== undefined && draft !== "" ? Number(draft) : (clip.views || 0); if (Number.isNaN(views) || views < 0) return; setBusyId(clip.id); await api.reviewClip(clip.id, { status: "approved", views }); setBusyId(null); await reload(); onChanged && onChanged(); }; const reject = async (clip) => { const reason = reasonDraft[clip.id] || "Doesn't meet brief"; setBusyId(clip.id); await api.reviewClip(clip.id, { status: "rejected", rejection_reason: reason }); setBusyId(null); await reload(); onChanged && onChanged(); }; const updateViewsOnly = async (clip) => { const draft = viewsDraft[clip.id]; if (draft === undefined || draft === "") return; const views = Number(draft); if (Number.isNaN(views) || views < 0) return; setBusyId(clip.id); await api.reviewClip(clip.id, { status: clip.status, views }); setBusyId(null); setViewsDraft(d => { const n = {...d}; delete n[clip.id]; return n; }); await reload(); onChanged && onChanged(); }; return (
REVIEW QUEUE
{tab==="pending" ? "Approve / reject submitted clips" : "All clip submissions"}
{[{k:"pending",l:"Pending"},{k:"all",l:"All"}].map(t => ( ))}
{loading ? (
Loading…
) : clips.length === 0 ? (
{tab === "pending" ? "Inbox zero. New submissions will appear here." : "No clips submitted yet."}
) : (
{clips.map(c => { const camp = c.campaigns || {}; const pr = c.profiles || {}; const tone = c.status === "approved" ? "approved" : c.status === "rejected" ? "rejected" : "pending"; return (
{c.url} {c.status}
{pr.handle || pr.display_name || "anon"} · {camp.name || "—"} · {c.platform || "other"} · {new Date(c.submitted_at).toLocaleString(undefined, {month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}
{c.notes &&
"{c.notes}"
} {c.status === "rejected" && c.rejection_reason && (
Rejected: {c.rejection_reason}
)}
{c.status === "pending" && ( <>
REJECT setReason(c.id, e.target.value)} style={{width:"100%",height:34,padding:"0 10px",marginTop:6,fontFamily:"Geist,sans-serif",fontSize:12,border:"1px solid #FECACA",borderRadius:8,outline:"none"}}/>
)} {c.status !== "pending" && ( )} {c.earned > 0 &&
Earned ${Number(c.earned).toFixed(2)}
}
); })}
)}
); } window.ReviewQueue = ReviewQueue;