// Record side panels — Details, Comments, Data Lineage, Version History. // Opened from the record context (3-dots) menu. Layouts mirror the macro // mockups in uploads/pasted-1779091716223-0.png. const { useState: useStateRP, useEffect: useEffectRP, useRef: useRefRP, createContext: createContextRP, useContext: useContextRP } = React; /* ===== Output context — current output (Dashboard / Report / Slide) ===== Created here so both the record body (collection.jsx) and the side panels (this file) can read/write it without prop-drilling. Side panels use it to default their per-output filter to whatever output is currently visible. */ const OutputContext = createContextRP(null); function OutputProvider({ children, newRecord, setNewRecordName, onOpenAgentChat, generating, genStageIdx, genStages, agentGenerated, onTemplateGenerate, onTemplateCreateEmpty, templateArtifact }) { const [output, setOutput] = useStateRP('dashboard'); // current active output key const [openPanel, setOpenPanel] = useStateRP(null); // 'details' | 'comments' | 'lineage' | 'history' | null // Extra outputs the user adds via the "+" on the chip row. Each entry: // { key, label, state: 'empty' | 'done' }. Only meaningful when the record // has already been generated (agentGenerated=true). const [extraOutputs, setExtraOutputs] = useStateRP([]); const nextIdRef = useRefRP(1); // Reset extras whenever the record context resets (e.g. user navigates away // and creates a brand new record). useEffectRP(() => { if (!agentGenerated) { setExtraOutputs([]); nextIdRef.current = 1; } }, [agentGenerated]); const addOutput = () => { const id = nextIdRef.current++; const key = `new-${id}`; setExtraOutputs((arr) => { // First extra is just "New output"; subsequent ones get an index suffix // so chips remain distinguishable. const label = arr.length === 0 ? 'New output' : `New output ${arr.length + 1}`; return [...arr, { key, label, state: 'empty' }]; }); setOutput(key); }; const setOutputState = (key, state) => { setExtraOutputs((arr) => arr.map((o) => (o.key === key ? { ...o, state } : o))); }; const removeOutput = (key) => { setExtraOutputs((arr) => arr.filter((o) => o.key !== key)); setOutput('dashboard'); }; return ( {children} ); } function useOutput() { return useContextRP(OutputContext); } /* Pretty labels for filter dropdowns */ const OUTPUT_LABELS = { dashboard: 'Dashboard', report: 'Report', slide: 'Slide' }; const OUTPUT_FILTER_OPTIONS = ['All outputs', 'Dashboard', 'Report', 'Slide']; const labelFromKey = (k) => OUTPUT_LABELS[k] || k; const keyFromLabel = (l) => Object.keys(OUTPUT_LABELS).find((k) => OUTPUT_LABELS[k] === l) || null; /* ===== Shared shell ===== */ function PanelShell({ title, onClose, toolbar, footer, children, bodyScrolls = true, className = '' }) { // Lock scroll while open useEffectRP(() => { const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = prev; }; }, []); return (
); } /* ===== Simple select pill (display-only, click-through is decorative) ===== */ function FilterSelect({ value, onChange, options }) { const [open, setOpen] = useStateRP(false); const wrapRef = useRefRP(null); useEffectRP(() => { if (!open) return; const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); }; document.addEventListener('mousedown', onDoc); return () => document.removeEventListener('mousedown', onDoc); }, [open]); return (
{open && (
{options.map((o) => ( ))}
)}
); } /* ========================================================================= 1. DETAILS PANEL ========================================================================= */ function EditableProp({ value, options, onChange }) { const [open, setOpen] = useStateRP(false); const wrapRef = useRefRP(null); useEffectRP(() => { if (!open) return; const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); }; const onKey = (e) => { if (e.key === 'Escape') setOpen(false); }; document.addEventListener('mousedown', onDoc); document.addEventListener('keydown', onKey); return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); }; }, [open]); return (
{open && (
{options.map((o) => ( ))}
)}
); } const DETAILS_USED_IN = [ { t: 'Finance reporting', sub: 'Q1 2026 dashboards' }, { t: 'MV Owner submissions', sub: 'Monthly progress packets' }, { t: 'TAGP GP meetings', sub: 'Meeting #13 – #14 decks' }, { t: 'Executive summaries', sub: 'AWST Q1 2026 brief' }, ]; function RecordDetailsPanel({ onClose }) { const ctx = useOutput(); const isEmpty = ctx?.isEmpty; const recordName = ctx?.newRecord?.name || 'Untitled record'; const [descOpen, setDescOpen] = useStateRP(true); const [propsOpen, setPropsOpen] = useStateRP(true); const [usedInOpen, setUsedInOpen] = useStateRP(true); const [status, setStatus] = useStateRP(isEmpty ? 'Draft' : 'Need review'); const [priority, setPriority] = useStateRP('Medium'); const [kb, setKb] = useStateRP(isEmpty ? 'Pending review' : 'Included'); return (
{descOpen && (
{isEmpty ? (

No description yet. Add one once the agent or a template has filled this record with a first artifact.

) : (

Aggregated dashboard of the Cedar Narrows Water Supply Tunnel project (Fraser River Crossing, Metro Vancouver Contract RFP No. 20-354). Synthesises the latest monthly, weekly, and daily shift reports — safety metrics, pipe installation progress, active workfronts, manpower, and open commercial items — into a single live view for the TAGP team and the Owner.

)}
)}
{propsOpen && (
{isEmpty ? (
Name
{recordName}
Owner
James Anderson
Team
Marketing Team
Status
Priority
Knowledge Base
Location
Proxa / Traylor / Cedar Narrows WST
Skills used
Created
Just now · by James Anderson
Last updated
Just now
) : (
Owner
James Anderson
Team
TAGP — Project Controls
Status
Priority
Knowledge Base
Location
Proxa / Traylor / Cedar Narrows WST
Skills used
report-aggregator (v1.04)
construction-kpi-extractor (v1.02)
Created
January 20, 2022 · 09:15
Last updated
4 min ago by Vera Hanna
)}
)}
{usedInOpen && (
{isEmpty ? (
Not used anywhere yet
Once this record has an artifact, places that reference it will show up here.
) : ( )}
)}
); } /* ========================================================================= 2. COMMENTS PANEL ========================================================================= */ const RP_COMMENTS = [ { id: 'c1', who: 'Lorri Warf', ago: '1 day ago', output: 'dashboard', avatar: 'LW', body: 'Forecast Substantial Completion at Nov 8, 2027 — please verify against the Rev.08 baseline before sending to Metro Vancouver. The positive variance vs. CO-14 SC is worth calling out.' }, { id: 'c2', who: 'Oliver Smith', ago: '3 days ago', output: 'dashboard', avatar: 'OS', body: 'Cellular grout backfill is sitting at 4.5%. Confirm with Joe before publishing the next monthly that the slag-mix throughput projection still holds for April.' }, { id: 'c3', who: 'Mia Chen', ago: '5 days ago', output: 'dashboard', avatar: 'MC', body: 'SVC dewatering pump test results look good. Adding Well #11 at −18 m is reflected in the workfronts card; archive Wells #1 and #4 once decommissioned.' }, { id: 'c4', who: 'Elena Martinez', ago: '6 days ago', output: 'report', avatar: 'EM', body: 'In the "Financial position" section, please re-word the PCO-008 paragraph — current draft reads as accepting CMC\'s framing. Stick to the partial-payment narrative.' }, { id: 'c5', who: 'Rina Patel', ago: '1 week ago', output: 'report', avatar: 'RP', body: 'Hero photo for the report should be the NVC excavation shot, not the tunnel-welding one. Excavation reads better as a "where the project is now" image.' }, { id: 'c6', who: 'James Anderson', ago: '2 weeks ago', output: 'report', avatar: 'JA', body: 'Bullet under "Safety performance" — let\'s replace "no lost-time incidents in March" with the YTD figure. Owner cares about cumulative, not monthly.' }, { id: 'c7', who: 'Oliver Smith', ago: '2 weeks ago', output: 'slide', avatar: 'OS', body: 'NLTI of 14.3 reads alarming on the slide without a comparison number. Add the 4.8 project-to-date average underneath or drop the metric.' }, { id: 'c8', who: 'Mia Chen', ago: '3 weeks ago', output: 'slide', avatar: 'MC', body: 'Workfronts = 7 is fine but the slide is too quiet. Consider swapping "Active workfronts" for "Pipe welded %" — it carries the story better.' }, { id: 'c9', who: 'Oliver Smith', ago: '4 weeks ago', output: 'dashboard', avatar: 'OS', body: 'Manpower bar widths feel exaggerated for the smaller crews — consider switching to a log scale or fixing the bar max to the largest crew on site.' }, ]; function Avatar({ initials }) { return {initials}; } function RecordCommentsPanel({ onClose, initialScope }) { const ctx = useOutput(); const output = ctx?.output || 'dashboard'; const isEmpty = ctx?.isEmpty; const [scope, setScope] = useStateRP(initialScope || labelFromKey(output)); const [draft, setDraft] = useStateRP(''); const toolbar = isEmpty ? null : ( ); const footer = (