/* global React, PR, PRCover, PRComments, PRCreatives */ const { useState, useEffect, useRef } = React; const { I, Avatar, StagePill, DecisionPill, RiskDot, formatDate, daysAgo, metricDelta } = PR; const { Cover } = PRCover; const { CommentsPanel } = PRComments; const { CreativesPanel } = PRCreatives; // ── Pencil icon ─────────────────────────────────────────────────────────────── function PencilIcon() { return ( ); } // ── Cover edit overlay ──────────────────────────────────────────────────────── function CoverEditOverlay({ onSave }) { const [open, setOpen] = useState(false); const [uploading, setUploading] = useState(false); const [dragOver, setDragOver] = useState(false); const [error, setError] = useState(null); const fileInputRef = useRef(null); const panelRef = useRef(null); const uploadFile = async (file) => { if (!file || !file.type.startsWith("image/")) { setError("Only images allowed"); return; } setUploading(true); setError(null); try { const form = new FormData(); form.append("file", file); const resp = await fetch("/api/upload", { method: "POST", body: form }); if (!resp.ok) { const e = await resp.json().catch(() => ({})); throw new Error(e.detail || "Upload failed"); } const { url } = await resp.json(); await onSave(url); setOpen(false); } catch (e) { setError(e.message); } finally { setUploading(false); } }; // Paste anywhere while panel is open useEffect(() => { if (!open) return; const handler = (e) => { const items = e.clipboardData?.items; if (!items) return; for (const item of items) { if (item.type.startsWith("image/")) { e.preventDefault(); uploadFile(item.getAsFile()); return; } } }; document.addEventListener("paste", handler); return () => document.removeEventListener("paste", handler); }, [open]); // Click outside closes panel useEffect(() => { if (!open) return; const handler = (e) => { if (panelRef.current && !panelRef.current.contains(e.target)) setOpen(false); }; setTimeout(() => document.addEventListener("mousedown", handler), 0); return () => document.removeEventListener("mousedown", handler); }, [open]); return (
{open && (
{error &&
{error}
}
!uploading && fileInputRef.current?.click()} onDragOver={(e) => { e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={(e) => { e.preventDefault(); setDragOver(false); const f = e.dataTransfer.files[0]; if (f) uploadFile(f); }} > {uploading ? "Uploading…" : "Click · drag & drop · Ctrl+V to paste"}
{ const f = e.target.files[0]; if (f) uploadFile(f); e.target.value = ""; }} />
)}
); } // ── Generic inline-editable field ───────────────────────────────────────────── function InlineField({ value, onSave, type = "text", options = [], placeholder = "Click to edit…", children, rows = 3, style, className = "" }) { const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(""); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const mouseDownPos = useRef(null); const inputRef = useRef(null); useEffect(() => { if (editing && inputRef.current) { inputRef.current.focus(); const el = inputRef.current; if (el.setSelectionRange && el.value) { el.setSelectionRange(el.value.length, el.value.length); } } }, [editing]); const startEdit = (e) => { const sel = window.getSelection(); if (sel && sel.toString().length > 0) return; if (mouseDownPos.current) { const dx = Math.abs(e.clientX - mouseDownPos.current.x); const dy = Math.abs(e.clientY - mouseDownPos.current.y); if (dx > 4 || dy > 4) return; } setDraft(value ?? ""); setError(null); setEditing(true); }; const save = async () => { if (saving) return; setSaving(true); setError(null); try { await onSave(draft); setEditing(false); } catch (err) { setError(err.message || "Failed to save"); } finally { setSaving(false); } }; const cancel = () => { setEditing(false); setError(null); }; const handleKeyDown = (e) => { if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); cancel(); return; } if (type !== "textarea" && e.key === "Enter") { e.preventDefault(); save(); return; } if (type === "textarea" && e.key === "Enter" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); save(); return; } }; if (editing) { return (
{type === "textarea" ? (