// Cascade Ridge Foods — Finance Dashboard component.
// FRAMEWORK: EB's CX_DASH document (cards + drawer + full record + data sheets),
// injected exactly like TenneyDashboard (the locked Playbook §B method).
// CONTENT: window.FIN_DASH (tabs/finance_dashboard_content.js). This file wires
// content into framework; it defines NO framework behavior of its own.
//
// The drawer/full-record chrome is injected ONCE into a persistent body-level
// container so openDrawer() works from ANY tab, not just the Dashboard.
function ensureFinCxGlobal() {
if (document.getElementById('fin-cx-global')) return true;
const d = window.CX_DASH;
const fin = window.FIN_DASH;
if (!d || !fin) return false;
// Framework CSS (scoped under .cx-root), once.
if (!document.getElementById('cx-dash-style')) {
const st = document.createElement('style');
st.id = 'cx-dash-style';
st.textContent = d.css;
document.head.appendChild(st);
}
// Reuse EB's drawer + full-record chrome verbatim; only the crumb text
// (hub name) is content and gets swapped.
const chromeStart = d.html.indexOf('
Data'
).join(
'' +
'' +
''
);
const holder = document.createElement('div');
holder.id = 'fin-cx-global';
holder.innerHTML =
// Hidden copy of the framework document's non-chrome sections (nav rail,
// section hosts). The framework's boot scripts expect these ids to exist.
'
' + d.html.slice(0, chromeStart) + '
' +
'
' + chrome + '
';
document.body.appendChild(holder);
// Boot the framework scripts exactly once (globals: openDrawer, openFull,
// recordContent, sheetData, …).
if (!window.__CX_DASH_BOOTED) {
window.__CX_DASH_BOOTED = true;
d.scripts.forEach((code) => {
const s = document.createElement('script');
s.textContent = code;
holder.appendChild(s);
});
}
// Register Finance content into the framework's registries (classic scripts
// share top-level lexical scope, so recordContent/ARTIFACT_ID/sheetData
// are reachable from a new classic script).
if (!window.__FIN_DASH_REGISTERED) {
window.__FIN_DASH_REGISTERED = true;
const reg = document.createElement('script');
reg.textContent =
"Object.assign(recordContent, window.FIN_DASH.records);" +
"Object.assign(ARTIFACT_ID, window.FIN_DASH.artifactIds);" +
"Object.assign(sheetData, window.FIN_DASH.sheets);" +
// Generic: let any other hub register its content pack into the same
// framework registries (cards → openDrawer → drawer), so Contracts etc.
// reuse the identical chrome. Idempotent per pack name.
"window.__cxPacks = window.__cxPacks || {};" +
"window.registerCxPack = function (pack) {" +
" if (!pack || window.__cxPacks[pack.name]) return;" +
" window.__cxPacks[pack.name] = true;" +
" if (pack.records) Object.assign(recordContent, pack.records);" +
" if (pack.artifactIds) Object.assign(ARTIFACT_ID, pack.artifactIds);" +
" if (pack.sheets) Object.assign(sheetData, pack.sheets);" +
"};" +
// Extra views: extend the framework's switchView for the record's
// 'control' (.control-view) and 'about'/Context (.about-view) states.
"(function () {" +
" var _sv = switchView;" +
" var EXTRA = { about: '.about-view', control: '.control-view' };" +
" switchView = function (view) {" +
" var root = activeRecordRoot();" +
" if (!root) return _sv(view);" +
" var hasData = !!root.querySelector('.data-view');" +
" Object.keys(EXTRA).forEach(function (k) {" +
" var el = root.querySelector(EXTRA[k]);" +
" if (el) el.classList.toggle('active', view === k);" +
" });" +
" if (EXTRA[view]) {" +
" if (!root.querySelector(EXTRA[view])) return;" +
" var r = root.querySelector('.report-view'); if (r) r.classList.add('hidden');" +
" var dv = root.querySelector('.data-view'); if (dv) dv.classList.remove('active');" +
" document.querySelectorAll('.view-toggle').forEach(function (t) {" +
" t.querySelectorAll('button').forEach(function (b) { b.classList.toggle('active', b.dataset.view === view); });" +
" });" +
" return;" +
" }" +
// Records without a Data view: 'data' is a no-op and 'report' must
// recover the report-view (original switchView bails without .data-view).
" if (!hasData) {" +
" if (view === 'data') { view = 'report'; }" +
" var rv = root.querySelector('.report-view'); if (rv) rv.classList.remove('hidden');" +
" document.querySelectorAll('.view-toggle').forEach(function (t) {" +
" t.querySelectorAll('button').forEach(function (b) { b.classList.toggle('active', b.dataset.view === 'report'); });" +
" });" +
" return;" +
" }" +
" _sv(view);" +
" };" +
"})();" +
// Sheet formatting pass (content-level): right-align currency/percent
// strings, bold Summary label rows. Runs after each render.
"(function () {" +
" var _rs = renderSheet;" +
" renderSheet = function (idx) {" +
" _rs(idx);" +
" var wrap = activeRecordRoot().querySelector('.sheet-wrap');" +
" if (!wrap) return;" +
" wrap.querySelectorAll('td').forEach(function (td) {" +
" var t = td.textContent;" +
" if (t.charAt(0) === '$' || /^[0-9.]+%$/.test(t)) td.classList.add('num');" +
" if (t === 'Total / Average' || t === 'Summary') td.classList.add('tn-sheet-label');" +
" });" +
" wrap.querySelectorAll('th').forEach(function (th) {" +
" var sp = th.querySelector('span');" +
" if (sp) th.textContent = sp.textContent;" +
" });" +
" };" +
"})();";
holder.appendChild(reg);
}
return true;
}
function FinanceCRFDashboard() {
const hostRef = React.useRef(null);
React.useEffect(() => {
const fin = window.FIN_DASH;
const host = hostRef.current;
if (!ensureFinCxGlobal() || !fin || !host) return;
host.innerHTML =
'