/* 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 — Fixed Operations shared kit. // Modern, search-first building blocks shared by the redesigned Service RO // workspace and Parts Counter. The headline piece is , which // renders the exact journal entry an action posted — so the user can SEE the // general-ledger accounts move against each other in real time. // ── Money / format ────────────────────────────────────────────────────── const jbm = (n) => "$" + Number(n || 0).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); const jbm0 = (n) => "$" + Number(n || 0).toLocaleString("en-US", { maximumFractionDigits: 0 }); // ── Chart-of-accounts name lookup (the accounts the fixed-ops cascade touches) const GL_ACCT_NAMES = { "201": "Cash on Hand", "202": "Bank — Operating", "220": "A/R — Service & Parts", "240": "Used Car Inventory", "241": "Used Truck Inventory", "242": "Parts Inventory", "263": "Warranty Receivable", "300": "Accounts Payable — Trade", "324A": "Sales Tax Payable", "460A": "Customer Labor Sales", "462": "Warranty Labor Sales", "463": "Internal Labor Sales", "467": "Parts Sales — Repair Order", "482": "Counter Parts Sales", "483": "Wholesale Parts Sales", "660A": "Cost of Labor Sales", "667": "Cost of Parts — Repair Order", "682": "Cost of Counter Parts", "683": "Cost of Wholesale Parts", "688": "Parts Inventory Adjustment", "999": "Labor Applied / Clearing", }; const glName = (acct) => GL_ACCT_NAMES[acct] || "GL Account"; // Account "family" → drives the running-total chip color. const glKind = (acct) => { const a = String(acct); if (["201", "202", "220", "242", "263", "240", "241"].includes(a)) return "asset"; if (["300", "324A"].includes(a)) return "liab"; if (["460A", "462", "463", "467", "482", "483"].includes(a)) return "income"; if (["660A", "667", "682", "683", "688", "999"].includes(a)) return "cost"; return "other"; }; // Current GL balance of an account, summed from every posted journal entry. const jbGLBalance = (acct) => { if (!window.DMS) return 0; const jes = window.DMS.journalEntries() || []; let bal = 0; jes.forEach(je => (je.lines || []).forEach(l => { if (String(l.acct) === String(acct)) bal += (+l.dr || 0) - (+l.cr || 0); })); return +bal.toFixed(2); }; // ── KPI pill ────────────────────────────────────────────────────────────── const JBKpi = ({ label, value, sub, tone, icon }) => { const tones = { red: { br: "var(--jb-red)", fg: "var(--jb-red)" }, amber: { br: "#E2A93B", fg: "var(--jb-amber)" }, ok: { br: "var(--jb-green)", fg: "var(--jb-green)" }, blue: { br: "var(--jb-blue)", fg: "var(--jb-blue)" }, }; const c = tones[tone] || { br: "var(--jb-border)", fg: "var(--jb-navy)" }; return (
{icon && {icon}}{label}
{value}
{sub &&
{sub}
}
); }; // ── Search field (rounded, icon, clearable) ──────────────────────────────── // RC32 (lane ros, the owner's 10/26 report: Inventory showed "0 of 104 units" with the Search box holding his sign-in id): every search / // filter box tells the browser and password managers it is NOT a login field — autoComplete off, a non-login name, no autofill hints. const JB_NO_AUTOFILL = { autoComplete: "off", "data-lpignore": "true", "data-1p-ignore": "true", "data-form-type": "other", "data-k2b-search": "1" }; const JBSearch = ({ value, onChange, placeholder, autoFocus, right, name }) => (
⌕ onChange(e.target.value)} placeholder={placeholder} {...JB_NO_AUTOFILL} name={name || "k2b-find"} spellCheck={false} style={{ flex: 1, minWidth: 0, width: "100%", border: "none", outline: "none", background: "transparent", padding: "10px 0", fontSize: 14, color: "var(--jb-text)", boxShadow: "none" }} /> {value ? ( ) : null} {right}
); // An empty list CAUSED BY a search: say so and offer one click to clear it (never a bare empty table). // setQ("")} /> → No units match '257' · [Clear search] const JBSearchEmpty = ({ q, noun, onClear, style }) => (
No {noun || "records"} match '{String(q == null ? "" : q)}'
); // ── Filter chip ───────────────────────────────────────────────────────── const JBChip = ({ active, onClick, children, count, tone }) => { const aBg = tone === "red" ? "var(--jb-red)" : tone === "amber" ? "var(--jb-amber)" : "var(--jb-navy)"; return ( ); }; // ── Status badge ────────────────────────────────────────────────────────── const JB_STATUS = { Open: { bg: "var(--jb-blue-tint)", fg: "var(--jb-blue)", dot: "var(--jb-blue)" }, Closed: { bg: "var(--jb-green-tint)", fg: "#15803D", dot: "var(--jb-green)" }, Warranty: { bg: "var(--jb-amber-tint)", fg: "var(--jb-amber)", dot: "var(--jb-amber)" }, Internal: { bg: "#EFE9FB", fg: "#6D40B0", dot: "#8050B0" }, Customer: { bg: "var(--jb-blue-tint)", fg: "var(--jb-blue)", dot: "var(--jb-blue)" }, Aged: { bg: "var(--jb-red-tint)", fg: "var(--jb-red)", dot: "var(--jb-red)" }, }; const JBBadge = ({ kind, children }) => { const c = JB_STATUS[kind] || { bg: "var(--jb-surface-2)", fg: "var(--jb-text-dim)", dot: "var(--jb-text-muted)" }; return ( {children} ); }; // ── Assist card (role-aware recommendation, in the spirit of modern DMS AI) ─ const JBAssist = ({ tone = "blue", icon = "✨", title, body, action, onAction }) => { const tones = { blue: { bd: "#CBE0F4", bg: "linear-gradient(180deg,#F2F8FE,#EAF2FC)", fg: "var(--jb-blue)" }, amber: { bd: "#F0DBA8", bg: "linear-gradient(180deg,#FFF8E9,#FFF3D9)", fg: "var(--jb-amber)" }, green: { bd: "#BFE6C7", bg: "linear-gradient(180deg,#F0FBF1,#E4F6E6)", fg: "#15803D" }, red: { bd: "#F2C9CE", bg: "linear-gradient(180deg,#FDF1F2,#FCE6E8)", fg: "var(--jb-red)" }, }; const c = tones[tone] || tones.blue; return (
{icon}
{title}
{body &&
{body}
} {action && ( )}
); }; // ── Section header ────────────────────────────────────────────────────── const JBPanelHead = ({ children, right }) => (
{children} {right}
); // ════════════════════════════════════════════════════════════════════════ // GL IMPACT — the centerpiece. Renders the journal entry an action posted, // shows the running balance each touched account now reads, and proves the // entry balances (debits = credits). This is "accounts affecting each other" // made visible. // ════════════════════════════════════════════════════════════════════════ const JBGLImpact = ({ entry, effects, compact }) => { if (!entry) return null; const lines = entry.lines || []; const totDr = lines.reduce((s, l) => s + (+l.dr || 0), 0); const totCr = lines.reduce((s, l) => s + (+l.cr || 0), 0); const balanced = Math.abs(totDr - totCr) < 0.01; const kindColor = { asset: "var(--jb-blue)", liab: "var(--jb-amber)", income: "var(--jb-green)", cost: "var(--jb-red)", other: "var(--jb-text-muted)" }; return (
📒
Posted to General Ledger
{entry.id} · Journal {entry.jrn} · {entry.date}
{balanced ? "✓ In balance" : "⚠ Out of balance"}
{/* Column header */}
Account Debit Credit
{/* Lines */} {lines.map((l, i) => { const newBal = jbGLBalance(l.acct); return (
{l.acct} {glName(l.acct)}
{!compact && (
balance now {jbm(newBal)}
)}
{l.dr ? jbm(l.dr) : "—"}
{l.cr ? jbm(l.cr) : "—"}
); })} {/* Totals */}
Totals {jbm(totDr)} {jbm(totCr)}
{/* Downstream effects (inventory relieved, AR/warranty created, hours flagged…) */} {effects && effects.length > 0 && (
Also updated across the dealership
{effects.map((e, i) => (
✓ {e.label}{e.detail ? " — " + e.detail : ""}
))}
)}
); }; // Pull the most-recent journal entry posted by a given source (Service/Parts). const jbLatestJE = (src) => { if (!window.DMS) return null; const jes = window.DMS.journalEntries() || []; return jes.find(j => !src || j.src === src) || jes[0] || null; }; Object.assign(window, { jbm, jbm0, GL_ACCT_NAMES, glName, glKind, jbGLBalance, JBKpi, JBSearch, JBSearchEmpty, JB_NO_AUTOFILL, JBChip, JBBadge, JBAssist, JBPanelHead, JBGLImpact, jbLatestJE, });