/* global React, PR, PRCover */ const { useState, useMemo } = React; const { I, Avatar, StagePill, DecisionPill, RiskDot, daysAgo, metricDelta } = PR; const { Cover } = PRCover; // ============ Table view ============ function TableView({ data, onOpen }) { const authorMap = Object.fromEntries(data.authors.map(a => [a.id, a])); const rows = data.hypotheses; return (
{rows.map(h => { const a = authorMap[h.author]; const m = metricDelta(h); const archived = h.decision === "kill"; return ( onOpen && onOpen(h.id)}> ); })}
ID Hypothesis Stage Genre Author Key Metric Risk Updated Decision
{h.id}
{h.title}
{h.pivotedFrom &&
↳ pivot from {h.pivotedFrom}
} {h.pivotedTo &&
→ pivoted to {h.pivotedTo}
}
{h.tags.slice(0, 2).map(t => {t})}
{h.keyMetric}
{m.display} / {h.target}
{daysAgo(h.updated)}
); } // ============ Kanban view ============ function KanbanView({ data, onOpen, onNew, onMove }) { const authorMap = Object.fromEntries(data.authors.map(a => [a.id, a])); const [dragOver, setDragOver] = useState(null); const byStage = useMemo(() => { const out = {}; data.stages.forEach(s => out[s.id] = []); data.hypotheses.forEach(h => { if (h.decision === "kill") return; out[h.stage]?.push(h); }); return out; }, [data]); const handleDragStart = (e, rndId) => { e.dataTransfer.setData("text/plain", rndId); e.dataTransfer.effectAllowed = "move"; }; const handleDragOver = (e, stageId) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; setDragOver(stageId); }; const handleDrop = (e, stageId) => { e.preventDefault(); setDragOver(null); const rndId = e.dataTransfer.getData("text/plain"); if (rndId && onMove) onMove(rndId, stageId); }; return (
{data.stages.map(s => { const items = byStage[s.id] || []; const isDropTarget = dragOver === s.id; return (
handleDragOver(e, s.id)} onDragLeave={() => setDragOver(null)} onDrop={(e) => handleDrop(e, s.id)} >
{items.length}
{I.plus}
{items.map(h => { const a = authorMap[h.author]; const m = metricDelta(h); return (
handleDragStart(e, h.id)} onClick={() => onOpen && onOpen(h.id)} > {h.coverUrl && (
)}
{h.id}
{h.title}
{h.tags.map(t => {t})}
{h.keyMetric}: {m.display}
); })} {items.length === 0 && (
{s.desc}
)}
); })}
); } // ============ Cards view ============ function CardsView({ data, onOpen }) { const authorMap = Object.fromEntries(data.authors.map(a => [a.id, a])); const rows = data.hypotheses.filter(h => h.decision !== "kill"); return (
{rows.map(h => { const a = authorMap[h.author]; const m = metricDelta(h); return (
onOpen && onOpen(h.id)}>
{h.id} · {daysAgo(h.updated)}
{h.title}
{h.coreLoop}
{h.tags.map(t => {t})}
{h.keyMetric}{" · "} {m.display} / {h.target}
); })}
); } window.PRViews = { TableView, KanbanView, CardsView };