/* 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 (
{label}
{sub}
{action && ( )}
); }; const DashClear = () => (
✓ Nothing to action.
); Object.assign(window, { Dashboard });