);
}
// Editable status control (used when a card's status can be changed, e.g. Projects).
const DP_STATUSES = ['Requested', 'Received', 'In review', 'Complete'];
function DPStatusControl({ value, onChange }) {
const [open, setOpen] = React.useState(false);
const ref = React.useRef(null);
React.useEffect(() => {
if (!open) return;
const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener('mousedown', onDoc);
return () => document.removeEventListener('mousedown', onDoc);
}, [open]);
return (
e.stopPropagation()}>
{open && (
{DP_STATUSES.map((o) => (
))}
)}
);
}
// THE data-product card — one component, used everywhere (Dashboard/Artifacts,
// Projects). Fields drive what renders; `onClick` makes it open a drawer,
// `onStatus` makes the Status editable. Missing narrative → "Click to add content".
function DPCard({ item, onClick, onStatus }) {
const hasAv = item.owner && item.owner !== '—';
return (
);
}
// Card view = a grid of DPCards (opens the drawer on click).
function OutputsCards({ items }) {
return (
{items.map((it) => ovOpen(it)} />)}
);
}
// The Finance hub's artifacts (mirrors the dashboard cards) — used by both the
// list view and the Collections/Finance card view, so cards match the Artifacts page.
window.FIN_ARTIFACTS = [
{ key: 'fin_summary', num: 'FIN-00', name: 'Executive Summary', date: 'Jul 12, 2026', owner: 'LP', ownerName: 'Linda Petrov', status: 'Validated', mod: '1 d',
cardMeta: 'Company overview · the context every other product reads',
narrative: 'Cascade Ridge Foods is a mid-market contract food manufacturer — 9 lines across Portland, OR and Lebanon, TN, co-packing natural snacks for ~55 CPG brands. FY2025 revenue $142.0M (+10.9%). The live story: recovery from the H2-25 commodity spike, Summit Trail’s churn offset by the Verdant Valley ramp, and the LEB-L5 line upgrade.' },
{ key: 'fin_pnl', num: 'FIN-01', name: 'P&L & Margin Bridge', date: 'Jul 12, 2026', owner: 'JP', ownerName: 'Jose Patel', status: 'Published', mod: '1 d',
cardMeta: 'Synced 1d ago · 2 source files',
narrative: 'FY2025 closed at $142.0M, up 10.9% — but the H2-25 oat & almond spike cut gross margin from 20.5% to 16.0% before pass-through pricing recovered it to 20.9% in H1-26. Q2-26 printed $43.9M at 22.8%, the strongest quarter on record.' },
{ key: 'fin_model', num: 'FIN-02', name: 'Consolidated Statements', date: 'Jul 12, 2026', owner: 'TG', ownerName: 'Trevor Garcia', status: 'Published', mod: '1 d',
cardMeta: 'Three-statement model · ties to the penny',
narrative: 'The balance sheet balances in all 33 periods and cash flow ties to balance-sheet cash to the penny. FY2026E lands at $160.0M / 5.7% net margin, building to $191.8M by FY2028E with cash growing from $6.5M to $32.7M.' },
{ key: 'fin_wc', num: 'FIN-03', name: 'Working Capital & AR', date: 'Jul 12, 2026', owner: 'TG', ownerName: 'Trevor Garcia', status: 'Published', mod: '1 d',
cardMeta: 'AR aging · DSO',
narrative: 'Open AR stands at $22.59M (47 days DSO) across 69 invoices — $18.3M current, $4.3M inside 30 days past due, nothing older. The one real stress, Summit Trail Snacks, was written off at $1.81M in Aug-25. Largest overdue balance: Verdant Valley at $1.4M — onboarding friction on the new anchor.' },
{ key: 'fin_budget', num: 'FIN-04', name: 'Budget vs Actual', date: 'Jul 12, 2026', owner: 'JP', ownerName: 'Jose Patel', status: 'Published', mod: '1 d',
cardMeta: 'H1-2026 · plan vs actual',
narrative: 'H1-2026 revenue of $78.0M beat plan by +2.9%, but on a mix shift: turnkey +15.4%, tolling -34.6%. Ingredients ran +6.5% over plan on the commodity tail; opex held under plan.' },
{ key: 'fin_customers', num: 'FIN-05', name: 'Customer Concentration & Revenue Build', date: 'Jul 12, 2026', owner: 'JP', ownerName: 'Jose Patel', status: 'Published', mod: '1 d',
cardMeta: 'Revenue build · concentration',
narrative: 'Top 10 customers = 56.4% of revenue; largest is Peak & Prairie at 10.4%. Summit Trail churned mid-2025; Verdant Valley is ramping to ~$18M/yr, the H1-26 growth engine.' },
];
// Eyebrow = the canonical unique DP id (same id the card, drawer, and
// library all show). Names may repeat; the id never collides.
window.FIN_ARTIFACTS.forEach(function (a) { a.num = window.dpId(a.key); });
Object.assign(window, { OutputsList, OutputsCards, DPCard });