/* eslint-disable */
/* © 2026 K2B NETWORK SOLUTIONS LLC. All rights reserved.
Proprietary and confidential. Unauthorized copying, distribution, or use of
this file, via any medium, is strictly prohibited. Original work product. */
// JBREWS DMS — GM Morning Dashboard.
// The exception report the General Manager reviews every morning (spec §11).
// Reads live from DMSCore. Each exception is actionable — clicking the action
// runs the cascade (fund a deal, pay off floorplan, submit a warranty claim,
// receive a factory incentive) which posts to the GL and clears the item.
const Dashboard = ({ go }) => {
const [, force] = React.useReducer(n => n + 1, 0);
React.useEffect(() => DMS.subscribe(force), []);
// RC35 (brd): a store with its GM franchise items turned off (SPEC 4.6 / D9) shows no GM warranty-claim / factory-incentive card or
// tile (the server keeps the commands working); hidden only once the store is known, as the GM-only menu items (app.jsx)
React.useEffect(() => (window.K2BStore && window.K2BStore.onChange ? window.K2BStore.onChange(force) : undefined), []);
const gmItems = !(window.K2BStore && window.K2BStore.known() && !window.K2BStore.franchise());
const x = DMS.exceptions();
const k = DMS.kpis();
const money = (n) => "$" + Math.round(n).toLocaleString();
const today = new Date().toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" });
const act = async (fn, arg, opts) => { // RC20 (fops): Close RO is a server command (a promise) — the toast shows the committed answer
const r = await fn(arg, opts);
window.k2bToast && window.k2bToast(r.msg, r.ok ? "ok" : "warn", 3200);
};
// ONE PATH: post / fund only through the shared helpers (one gate, they toast).
const postDeal = (id) => window.jbPostDeal ? window.jbPostDeal(id) : (window.k2bToast && window.k2bToast("Ledger offline.", "warn"));
const fundDeal = (id) => window.jbFundDeal ? window.jbFundDeal(id) : (window.k2bToast && window.k2bToast("Ledger offline.", "warn"));
// Pipeline counts by stage (Desked · Cash · F&I · Posted · Funded).
const stages = DMS.DEAL_STAGES || ["Desked", "Cash", "F&I", "Posted", "Funded"];
const allDeals = (DMS.deals() || []).filter(d => d.status !== "Dead");
const stageCount = stages.reduce((m, s) => { m[s] = allDeals.filter(d => (DMS.dealStage ? DMS.dealStage(d) : d.stage) === s).length; return m; }, {});
const stageTone = { Desked: "amber", Cash: "green", "F&I": "navy", Posted: "navy", Funded: "green" };
// count total open exceptions for the headline
const totalExceptions =
x.soldNotFunded.length + x.soldNotDelivered.length + x.notPosted.length +
x.agedROs.length + (gmItems ? x.unsubmittedWarranty.length + x.agedWarranty.length + x.agedFactory.length : 0) +
x.negativeParts.length + x.noBinParts.length +
x.obsoleteParts.length + x.soReceivedNotInstalled.length + x.floorplanDue.length +
x.internalROsOpen.length + x.agedInventory.length;
return (
{/* Header */}
Morning Report · {today}
Good morning. Here's the store.
{totalExceptions === 0
? "✓ All clear — no open exceptions"
: `⚠ ${totalExceptions} item${totalExceptions === 1 ? "" : "s"} need attention`}
{/* KPI strip */}
go("deal")} />
go("deal")} />
go("inventory")} />
0 ? "amber" : "muted"} onClick={() => go("accounting")} />
0 ? "red" : "muted"} />
{gmItems ? 0 ? "amber" : "muted"} onClick={() => go("service")} /> : null}
{/* Pipeline KPI row — one path: Desked → Cash / F&I → Posted → Funded */}
Pipeline
{stages.map((s, i) => (
{i > 0 && {s === "F&I" ? "/" : "→"}}
go("deal")} title={`${stageCount[s]} deal${stageCount[s] === 1 ? "" : "s"} at ${s} — open the Deal Pipeline`}
style={{ display: "flex", alignItems: "baseline", gap: 6, padding: "6px 12px", cursor: "pointer",
background: "var(--jb-surface)", border: "1px solid var(--jb-border)", borderRadius: "var(--jb-r-md)", boxShadow: "var(--jb-shadow-sm)", minWidth: 90 }}>
{stageCount[s]}
{s === "Funded" ? "Funded ✓" : s}
))}
{/* Exception groups */}
{/* Money out the door */}
{x.soldNotFunded.map(d => (
fundDeal(d.id)} />
))}
{x.floorplanDue.map(f => (
{ // RC33 (mpx): a SERVER command — paid by ACH / wire (the bank's confirmation) or by a check the server writes
const ref = window.prompt(`Pay off ${f.stock} — ${money(f.amount)} to ${f.lender || "the lender"}.\n\nPaid by ACH / wire: type the bank's confirmation number.\nLeave it blank to write a CHECK instead (print it in Check Writing).`, "");
if (ref === null) return;
act(DMS.payoffFloorplan, f.stock, String(ref).trim() ? { method: "ach", ref: String(ref).trim() } : { method: "check" });
}} />
))}
{x.notPosted.map(d => (
postDeal(d.id)} />
))}
{x.soldNotFunded.length + x.floorplanDue.length + x.notPosted.length === 0 && }
{/* Deals pipeline */}
{x.soldNotDelivered.map(d => (
postDeal(d.id)} />
))}
{x.soldNotDelivered.length === 0 && }
{/* Service */}
{x.openROs.map(r => {
const days = Math.floor((Date.now() - new Date(r.opened)) / 86400000);
const aged = days >= 7;
return (
act(DMS.closeRO, r.id)} />
);
})}
{x.openROs.length === 0 && }
{/* Warranty (GM franchise items) */}
{gmItems ?
{x.unsubmittedWarranty.map(w => (
act(DMS.submitWarrantyClaim, w.claim)} />
))}
{x.agedWarranty.map(w => (
))}
{x.unsubmittedWarranty.length + x.agedWarranty.length === 0 && }
: null}
{/* Factory receivables (GM franchise items) */}
{gmItems ?
{x.openFactory.map(f => (
= 30 ? " — AGED" : "")}
tone={f.age >= 30 ? "red" : "muted"}
action="Receive" onAction={() => { // RC33 (mpx): a SERVER command — the amount GM paid (blank = the full claim)
const a = window.prompt(`Receive ${f.program} on ${f.deal} — ${money(f.claimed)} claimed.\n\nAmount GM paid (leave blank for the full claim):`, "");
if (a === null) return;
act(DMS.receiveFactoryIncentive, f.deal, String(a).trim() ? { amount: String(a).trim() } : {});
}} />
))}
{x.openFactory.length === 0 && }
: null}
{/* Parts */}
{x.negativeParts.map(p => (
))}
{x.soReceivedNotInstalled.map(p => (
))}
{x.noBinParts.map(p => (
))}
{x.obsoleteParts.map(p => (
))}
{x.negativeParts.length + x.noBinParts.length + x.obsoleteParts.length + x.soReceivedNotInstalled.length === 0 && }
{/* Inventory aging */}
{x.agedInventory.map(u => (
= 120 ? "red" : "amber"}
action="View" onAction={() => go("inventory")} />
))}
{x.agedInventory.length === 0 && }
{/* Payroll */}
{x.unpaidCommissions.map(c => (
go("payroll")} />
))}
{x.unpaidCommissions.length === 0 && }
{/* Footer actions */}
go("getstarted")}>Getting Started guide ▸
go("salesmenu")}>All Modules ▸
{/* Audit C-05: admin-only, typed confirmation, and an honest label —
this REPLACES the shared ledger for every station, not just this PC. */}
);
};
const DashKpi = ({ label, value, sub, tone = "muted", onClick }) => {
const colors = {
navy: "var(--jb-navy)", green: "var(--jb-green)", amber: "#9A6700", red: "#C03546", muted: "var(--jb-text)",
};
return (
{label}
{value}
{sub &&
{sub}
}
);
};
const DashGroup = ({ title, accent, count, children }) => (
{title}
0 ? accent : "var(--jb-surface-2)",
color: count > 0 ? "#FFF" : "var(--jb-text-muted)",
display: "inline-flex", alignItems: "center", justifyContent: "center",
fontSize: 12, fontWeight: 700,
}}>{count}
{children}
);
const DashRow = ({ label, sub, tone = "muted", action, onAction }) => {
const dot = { red: "#C03546", amber: "#D9920A", navy: "#1B66A8", green: "#16A34A", muted: "#9AA3B8" }[tone];
return (
);
};
const DashClear = () => (
✓ Nothing to action.
);
Object.assign(window, { Dashboard });