/* 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 — Vehicle Inventory (INVSTK). // Browse new/used stock, filter, look at detail. Empty by default — use // "+ Add New" or the Import Tool to bring stock in. const INV_STOCK = []; const EMPTY_UNIT = { stock: "", vin: "", yr: "", mk: "", mdl: "", body: "", color: "", miles: 0, cost: 0, list: 0, days: 0, type: "N", status: "", }; const TYPE_LABEL = { N: "New", U: "Used" }; const STATUS_COLOR = { STOCK: "var(--k2b-green)", HOLD: "#B26A00", TRANS: "var(--k2b-blue-data-dk)", SOLD: "var(--k2b-maroon)", AGED: "var(--k2b-maroon)", }; const inv$ = (n) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); // ── Vehicle Profit Timeline — life of the unit (reads DMS.profitTimeline) ── const InvTimeline = ({ stock }) => { const D = window.DMS; const [open, setOpen] = React.useState(false); if (!D || !D.profitTimeline) return null; const tl = D.profitTimeline(stock); if (!tl.found) return null; const v$ = (n) => n == null ? "—" : "$" + Number(n).toLocaleString("en-US", { maximumFractionDigits: 0 }); const fmtD = (d) => d ? new Date(d).toLocaleDateString("en-US", { month: "short", day: "numeric" }) : "—"; const KIND = { buy: "#11337A", recon: "#9A6700", list: "#1B66A8", sold: "#7C3AED", post: "#1B66A8", fund: "#0E7A37", comm: "#0E7A37", title: "#555" }; return (
Profit Timeline {tl.daysToSale != null ? `${tl.daysToSale}d to sale` : `${tl.daysHeld}d held`}
{open && (
{tl.events.length === 0 ? No dated events yet. : tl.events.map((e, i) => { const prev = i > 0 ? tl.events[i - 1] : null; const gap = prev ? Math.max(0, Math.floor((new Date(e.date) - new Date(prev.date)) / 86400000)) : 0; return (
{fmtD(e.date)} {e.label}{e.detail ? · {e.detail} : ""} {prev && gap > 0 && = 30 ? "var(--k2b-maroon)" : "#aaa", fontSize: 11 }}>+{gap}d}
); })}
{tl.deal && (
Front gross= 0 ? "var(--k2b-green)" : "var(--k2b-maroon)" }}>{v$(tl.front)} Back gross{v$(tl.back)} {tl.finalized ? "Gross FINALIZED" : "Gross (pending fund)"} {v$(tl.gross)}
)}
)}
); }; // ── Vehicle Truth Box — retail-readiness gate (reads DMS.truthBox) ── const InvTruthBox = ({ stock, readOnly }) => { const D = window.DMS; const [, force] = React.useReducer(n => n + 1, 0); React.useEffect(() => (D && D.subscribe) ? D.subscribe(force) : undefined, []); if (!D || !D.truthBox) return null; const tb = D.truthBox(stock); if (!tb.found) return null; const toggle = (key, cur) => { if (!readOnly) D.setUnitReadiness(stock, { [key]: !cur }); }; return (
Vehicle Truth Box {tb.canSell ? "✓ CAN BE SOLD" : "✕ CANNOT DELIVER"}
{tb.items.map(it => (
{it.label}{it.required ? "" : (opt)} {it.na ? ( n/a ) : ( )}
))} {!tb.canSell && tb.reasons.length > 0 && (
Blocks delivery: {tb.reasons.join(" · ")}
)}
); }; // ── Printable forms: Window Sticker (Monroney) + Vehicle Spec Sheet ── // Tolerant of both field conventions (mk/mdl/yr/miles/list and make/model/year). const _uf = (u, ...keys) => { for (const k of keys) if (u[k] != null && u[k] !== "") return u[k]; return ""; }; const _u$ = (n) => "$" + (Number(n) || 0).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); const _uNum = (v) => Number(String(v).replace(/[^0-9.\-]/g, "")) || 0; const printWindowSticker = (u) => { if (!u || !window.k2bPrint) return; const yr = _uf(u, "yr", "year"), mk = _uf(u, "mk", "make"), mdl = _uf(u, "mdl", "model"); const trim = _uf(u, "trim"), body = _uf(u, "body"), color = _uf(u, "color", "extColor"); const vin = _uf(u, "vin"), stock = _uf(u, "stock"); const isNew = /^n/i.test(String(_uf(u, "type"))) || /new/i.test(_uf(u, "status")); const msrp = _uNum(_uf(u, "msrp", "list", "asking", "price")); const dest = 1795; // Believable standard-equipment + option groups (deterministic from stock). const STD = ["Driver & passenger front airbags, side-curtain airbags", "Anti-lock brakes (ABS) with electronic stability control", "Rear-vision camera with dynamic guidelines", "Tire pressure monitoring system", "LED daytime running lamps", "Air conditioning, single-zone", "Power windows / locks / mirrors", "AM/FM with Bluetooth & USB"]; const OPTS = isNew ? [ { name: "Convenience Package — remote start, heated seats", price: 1495 }, { name: "Driver Confidence Package — blind-zone alert, rear cross-traffic", price: 945 }, { name: "Premium audio & 11\" touchscreen", price: 750 }, ] : []; const optTotal = OPTS.reduce((s, o) => s + o.price, 0); const total = msrp + dest + optTotal; const head = invHead(); const html = `${head}
${isNew ? "MONRONEY LABEL — NEW VEHICLE" : "WINDOW STICKER — USED VEHICLE"}
${[yr, mk, mdl, trim].filter(Boolean).join(" ")} · Stock ${stock} · VIN ${vin}
Exterior Color
${color || "—"}
Body Style
${body || "—"}
Odometer
${_uNum(_uf(u, "miles", "odometer")).toLocaleString()} mi
Stock Type
${isNew ? "New" : "Pre-Owned"}

Standard Equipment

Pricing

${OPTS.map(o => ``).join("")}
Manufacturer's Suggested Retail Price${isNew ? "" : " (original)"}${_u$(msrp)}
+ ${o.name}${_u$(o.price)}
Destination & Delivery Charge${_u$(dest)}
TOTAL ${isNew ? "MSRP" : "PRICE"}${_u$(total)}

Fuel economy estimates, manufacturer's warranty information, safety ratings, and the standard/optional equipment list are provided per federal Monroney (15 U.S.C. 1232) requirements. ${isNew ? "" : "Pre-owned vehicle — see accompanying FTC Buyers Guide for warranty status."} Vehicle subject to dealer prep and applicable taxes & fees.

${K2BPrint.esc(K2BPrint.party.print())}Window Sticker · ${stock}
`; window.k2bPrint(html, { title: "Window Sticker " + stock }); }; const printSpecSheet = (u) => { if (!u || !window.k2bPrint) return; const yr = _uf(u, "yr", "year"), mk = _uf(u, "mk", "make"), mdl = _uf(u, "mdl", "model"); const vin = _uf(u, "vin"), stock = _uf(u, "stock"); const cost = _uNum(_uf(u, "cost")), list = _uNum(_uf(u, "asking", "list", "msrp", "price")); const days = u.stockedDate || u.age != null ? invDays(u) : _uNum(_uf(u, "days")); const cell = (l, v, mono) => `
${l}
${v || "—"}
`; const head = invHead(); const html = `${head}
VEHICLE SPECIFICATION SHEET
${[yr, mk, mdl].filter(Boolean).join(" ")} · Stock ${stock}

Vehicle Identity

${cell("VIN", vin, true)}${cell("Stock #", stock, true)}
${cell("Year / Make", [yr, mk].filter(Boolean).join(" "))}${cell("Model / Trim", [mdl, _uf(u, "trim")].filter(Boolean).join(" "))}
${cell("Body Style", _uf(u, "body"))}${cell("Exterior Color", _uf(u, "color", "extColor"))}
${cell("Odometer", _uNum(_uf(u, "miles", "odometer")).toLocaleString() + " mi", true)}${cell("Stock Type", (/^n/i.test(String(_uf(u, "type"))) || /new/i.test(_uf(u, "status"))) ? "New" : "Pre-Owned")}

Powertrain & Mechanical

${cell("Engine", _uf(u, "engine") || "—")}${cell("Transmission", _uf(u, "trans") || "Automatic")}
${cell("Drivetrain", _uf(u, "drive") || "—")}${cell("Fuel Type", _uf(u, "fuel") || "Gasoline")}
${_uf(u, "description") ? `

Description

${invEsc(_uf(u, "description"))}

` : ""}

Inventory & Pricing${invIsSales() ? "" : " (internal)"}

${invIsSales() ? "" : ``} ${invIsSales() ? "" : ``}
Invoice / Acquisition Cost${_u$(cost)}
List / Retail${_u$(list)}
Spread${_u$(list - cost)}
Days in Inventory${days}
${K2BPrint.esc(K2BPrint.party.print())}Spec Sheet · ${stock} · internal use
`; window.k2bPrint(html, { title: "Spec Sheet " + stock }); }; // ── Shared helpers (unit schema: see DMSCore "INVENTORY — import …") ── const invD = () => window.DMS || null; // Role gates. "inventory" = may edit units; "inventoryView" = look only (Sales). const invCanEdit = () => { const D = invD(); if (!D || !D.can) return true; if (D.hasRole && !D.hasRole()) return !(D.isRestricted && D.isRestricted()); return D.can("inventory"); }; const invCanGL = () => { const D = invD(); return !!(D && D.can && D.can("glEdit")); }; const invCanDesk = () => { const D = invD(); if (!D || !D.can) return true; if (D.hasRole && !D.hasRole()) return !(D.isRestricted && D.isRestricted()); return D.can("desk"); }; const invIsSales = () => { const D = invD(); return !!(D && D.roleIs && D.roleIs("Sales")); }; const invIsNew = (u) => /^n/i.test(String((u && u.type) || "")); const invDays = (u) => { if (u && u.stockedDate) { const t = new Date(u.stockedDate).getTime(); if (!isNaN(t)) return Math.max(0, Math.floor((Date.now() - t) / 86400000)); } return +(u && (u.age != null ? u.age : u.days)) || 0; }; const invStatusCode = (u) => { const s = String((u && u.status) || ""); if (window.DealCalc ? window.DealCalc.unitGone(u) : /^sold/i.test(s.trim())) return "SOLD"; // RC28 fix round: the ONE on-lot reading if (/hold/i.test(s)) return "HOLD"; if (/trans/i.test(s)) return "TRANS"; return invDays(u) > 90 ? "AGED" : "STOCK"; }; const invEsc = (s) => String(s == null ? "" : s).replace(/&/g, "&").replace(//g, ">"); const invHead = () => (typeof dealerHeadHtml === "function" ? dealerHeadHtml() : (window.dealerHeadHtml ? window.dealerHeadHtml() : "")); const invMoney = (n) => "$" + inv$(+n || 0); const invAcctLabel = (acct) => { const D = invD(); const n = D && D.acctName ? D.acctName(acct) : ""; return n || "(not in chart of accounts)"; }; const invToast = (msg, kind, ms) => { if (window.k2bToast) window.k2bToast(msg, kind || "info", ms || 3200); }; // Hand a unit to Desking (jb_desk_unit pointer — see ONE PATH contract). // Inventory -> VIN Decode: the unit's VIN goes along (pointer jb_vin_decode, read once by the VIN Decode screen). The unit's own facts // (stock #, colour, miles, new/used) are kept whatever the decode says; its specs travel as hints the decoder may replace. const invVinDecodeOpen = (u, go) => { if (u && u.vin) { const ptr = { vin: String(u.vin), stock: u.stock || "", type: /^n/i.test(String(u.type || u.condition || "")) ? "N" : "U", color: u.color || "", miles: u.miles == null ? "" : String(u.miles), yr: u.yr || "", mk: u.mk || "", mdl: u.mdl || "", trim: u.trim || "", body: u.body || "", engine: u.engine || "", trans: u.trans || "", drive: u.drive || "", fuel: u.fuel || "", typed: ["stock", "color", "miles"] }; try { localStorage.setItem("jb_vin_decode", JSON.stringify(ptr)); } catch (e) {} window.jbVinDecodePrefill = ptr; } go && go("vin"); }; const invDeskUnit = (u, go) => { if (!u || !u.stock) { invToast("Select a unit first.", "warn"); return; } const D = invD(); const chk = D && D.unitOpenItems ? D.unitOpenItems(u.stock) : { clean: true }; if (!chk.clean) invToast(`⚠ ${u.stock} has open items: ${chk.items.map(x => x.label).join(" · ")}`, "warn", 5000); window.jbDeskUnit = { stock: u.stock, vin: u.vin, yr: u.yr, mk: u.mk, mdl: u.mdl, list: +(u.asking || u.list || 0), cost: +u.cost || 0, type: u.type, miles: +u.miles || 0 }; try { localStorage.setItem("jb_desk_unit", JSON.stringify(window.jbDeskUnit)); } catch (e) {} go && go("desking"); }; // ── Printable inventory list (F9) — the currently filtered rows ── const invPrintList = (rows, filterLabel) => { if (!window.k2bPrint) { invToast("Print preview is not available in this session.", "warn"); return; } const showCost = !invIsSales(); const tot = rows.reduce((s, r) => ({ cost: s.cost + (+r.cost || 0), list: s.list + (+r.list || 0) }), { cost: 0, list: 0 }); const th = (t, right) => `${t}`; const td = (t, right, mono) => `${t}`; const body = rows.map(r => `${td(invEsc(r.stock), false, true)}${td(invEsc([r.yr, r.mk, r.mdl, r.trim].filter(Boolean).join(" ")))}${td(invEsc(r.vin), false, true)}${td(r.type === "N" ? "New" : "Used")}${td((+r.miles || 0).toLocaleString(), true)}${td(r.days, true)}${showCost ? td(invMoney(r.cost), true) : ""}${td(invMoney(r.list), true)}${td(invEsc(r.status))}`).join(""); const now = new Date(); const html = `${invHead()}
VEHICLE INVENTORY LIST
${invEsc(filterLabel || "All units")} · Printed ${now.toLocaleDateString("en-US")} ${now.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })}
${th("Stock #")}${th("Year / Make / Model")}${th("VIN")}${th("N/U")}${th("Miles", 1)}${th("Days", 1)}${showCost ? th("Cost", 1) : ""}${th("Asking", 1)}${th("Status")}${body || ``}
No units match the current filter.
${showCost ? `` : ""}
Units${rows.length}
Total cost${invMoney(tot.cost)}
Total asking${invMoney(tot.list)}
${invEsc(K2BPrint.party.print())}Inventory List · ${rows.length} unit${rows.length === 1 ? "" : "s"}
`; window.k2bPrint(html, { title: "Inventory List" }); }; // Small action button that (unlike K2BButton) forwards data-inv / title / disabled. const InvBtn = ({ children, onClick, primary, inv, title, style, disabled }) => ( ); // Row styling: whole row is the click target (hover + focus ring + "Open ▸"). const INV_ROW_CSS = ` .inv-row { cursor: pointer; } .inv-row > td { transition: background .08s; } .inv-row:hover > td { background: #FFF3C4 !important; } .inv-row:focus { outline: none; } .inv-row:focus-visible > td { box-shadow: inset 0 2px 0 #2A6FDB, inset 0 -2px 0 #2A6FDB; } .inv-row .inv-open { display: inline-block; padding: 0 8px; border: 1px solid #9FB6D6; border-radius: 4px; background: #EEF4FB; color: #1B4F8A; font-weight: 700; font-size: 11px; white-space: nowrap; } .inv-row:hover .inv-open { background: #1B4F8A; color: #FFFFFF; border-color: #1B4F8A; } .inv-tab { border: 1px solid #9AA4B5; border-bottom: none; background: #E6E9EF; color: #1D2A4A; padding: 5px 14px; font-weight: 700; font-size: 12px; cursor: pointer; border-radius: 6px 6px 0 0; } .inv-tab[aria-selected="true"] { background: #FFFFFF; color: #8A1F1F; box-shadow: inset 0 3px 0 #8A1F1F; } .inv-je-row { cursor: pointer; } .inv-je-row:hover > td { background: #FFF3C4; } `; // ═════════════════════════════════════════════════════════════════════════ // MARKET PRICE + VIN FILL (MarketCheck) // The browser never calls MarketCheck and never holds a key: every request goes to OUR server (/api/marketcheck/...), which keeps the key, // saves each answer for 24 hours, enforces who may see what and the monthly / daily spending limits. The server decides the role (status.features); // this file only hides what the server would refuse anyway. // Names here start with invMc / InvMc (all .jsx.txt files share ONE global scope). // ═════════════════════════════════════════════════════════════════════════ const INV_MC_NOT_ON = "Market price is not switched on yet - ask the owner to add the MarketCheck key."; const invMcNormVin = (v) => String(v == null ? "" : v).toUpperCase().replace(/[\s-]+/g, ""); const invMcVinOk = (v) => /^[A-HJ-NPR-Z0-9]{17}$/.test(invMcNormVin(v)); const invMcCall = (path, opts) => { if (typeof apiFetch !== "function") return Promise.reject(Object.assign(new Error("offline"), { status: 0 })); return apiFetch("/marketcheck" + path, opts); }; const invMcCount = (n) => Number(n || 0).toLocaleString("en-US"); const invMcAge = (min, iso) => { // "just now" | "12 min ago" | "3 h ago" | "2 d ago" let m = Number(min); if (!isFinite(m) || min == null) m = iso ? Math.round((Date.now() - new Date(iso).getTime()) / 60000) : NaN; if (!isFinite(m)) return ""; m = Math.max(0, m); if (m < 2) return "just now"; if (m < 90) return m + " min ago"; if (m < 36 * 60) return Math.round(m / 60) + " h ago"; return Math.round(m / 1440) + " d ago"; }; const invMcWhen = (iso) => { if (!iso) return ""; const d = new Date(iso); return isNaN(d.getTime()) ? "" : d.toLocaleString("en-US", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }); }; // Any failure -> { code, text, retryable, retryAfterSec } in plain English. Never a raw error, a status line or a stack trace. const invMcText = (code, serverText, mgmt, retryAfterSec) => { const when = (/(?:start|works?) again on ([^.]+)\./i.exec(String(serverText || "")) || [])[1]; const reset = when ? ` Lookups start again on ${when}.` : ""; switch (code) { case "not_configured": case "not_installed": return INV_MC_NOT_ON; case "bad_vin": return "A market price needs a complete 17-character VIN (no I, O or Q)."; case "miles_required": return "Enter the mileage first - a market price needs the odometer reading."; case "monthly_cap": return mgmt ? `The monthly MarketCheck call limit has been reached.${reset} Saved prices still work. Ask the owner to raise the limit if the MarketCheck plan was upgraded.` : "The store's market price lookups for this month are used up. Saved prices still work. Ask a manager."; case "monthly_budget": return mgmt ? `The monthly MarketCheck spending limit has been reached.${reset} Saved prices still work. Ask the owner to raise the limit.` : "The store's market price lookups for this month are used up. Saved prices still work. Ask a manager."; case "quota_exhausted": return mgmt ? `MarketCheck says the plan's monthly quota is used up.${reset} Saved prices still work. Ask the owner about upgrading the plan.` : "The store's market price lookups for this month are used up. Saved prices still work. Ask a manager."; case "daily_cap": return mgmt ? "The daily MarketCheck limit for the whole store has been reached. Saved prices still work. Try again tomorrow, or ask the owner to raise the limit." : "Today's market price lookups for the store are used up. Saved prices still work. Try again tomorrow or ask a manager."; case "user_cap": return mgmt ? "You have used your MarketCheck lookups for today. Saved prices still work. Try again tomorrow, or ask the owner to raise the limit." : "You have used today's market price lookups. Saved prices still work. Ask a manager."; case "per_second": case "rate_limited": return retryAfterSec ? `MarketCheck is busy right now. Try again in about ${retryAfterSec} second${retryAfterSec === 1 ? "" : "s"}.` : "MarketCheck is busy right now. Try again in a few seconds."; case "upstream_auth": return mgmt ? "MarketCheck did not accept the key. Ask the owner to check it." : "Market price is not available right now. Ask a manager."; case "bad_check_digit": case "not_in_inventory": case "sold_unit": case "forbidden": case "bad_params": { const t = String(serverText || ""); return t && !/bash |\.sh\b|MARKETCHECK_|api_key|Error:|\bat \S+ \(/i.test(t) ? t : "That lookup is not available."; } default: return "Market price is not available right now. Try again in a minute."; } }; const invMcFail = (e, mgmt) => { const b = (e && e.body) || {}; let code = String(b.code || ""); if (!code) code = e && e.status === 503 ? "not_configured" : e && e.status === 404 ? "not_installed" : e && e.status === 429 ? "rate_limited" : "unavailable"; const ra = Math.round(Number(b.retryAfterSec) || 0); return { code, text: invMcText(code, b.error, !!mgmt, ra), retryable: !!b.retryable || /^(per_second|rate_limited|unavailable|upstream_error|upstream_timeout|upstream_dropped|network|bad_response)$/.test(code), retryAfterSec: ra }; }; // Who we are talking to + what the server lets us do (management bits show only when the SERVER says so). let invMcStatusMemo = { who: "", at: 0, p: null, val: null }; const invMcWho = () => { const D = window.DMS; return D && D.userId ? D.userId() : ""; }; const invMcLoadStatus = (force) => { const who = invMcWho(), m = invMcStatusMemo; if (!force && m.p && m.who === who && Date.now() - m.at < 45000) return m.p; const off = { ok: false, unavailable: true, configured: false, features: {} }; const p = invMcCall("/status").then(s => (s && s.ok === true) ? s : off).catch(() => off).then(s => { if (invMcStatusMemo.p === p) invMcStatusMemo.val = s; return s; }); invMcStatusMemo = { who, at: Date.now(), p, val: null }; return p; }; const invMcUseStatus = () => { const [st, setSt] = React.useState(() => (invMcStatusMemo.who === invMcWho() ? invMcStatusMemo.val : null)); const alive = React.useRef(true); React.useEffect(() => { alive.current = true; invMcLoadStatus().then(s => { if (alive.current) setSt(s); }); return () => { alive.current = false; }; }, []); const reload = React.useCallback(() => invMcLoadStatus(true).then(s => { if (alive.current) setSt(s); return s; }), []); return [st, reload]; }; const invMcIsMgmt = (st) => !!(st && st.ok && st.features && st.features.comparables); const invMcOn = (st) => !!(st && st.ok && st.configured); // The server says this role gets no market prices at all (roles without the 'inventory' capability, e.g. Service Advisor): show nothing, ask nothing. const invMcNoAccess = (st) => !!(st && st.ok && st.features && st.features.marketPrice === false); // ── VIN decode -> form fields ──────────────────────────────────────────── const INV_MC_LABEL = { year: "Year", make: "Make", model: "Model", trim: "Trim", body: "Body", drivetrain: "Drivetrain", engine: "Engine", transmission: "Transmission", fuel: "Fuel", options: "Options", msrp: "MSRP" }; const invMcPick = (ck, d) => { const t = (x) => (x == null || String(x).trim() === "" ? null : String(x).trim()); switch (ck) { case "year": { const n = parseInt(d.year, 10); return n >= 1980 && n <= 2100 ? n : null; } case "make": case "model": case "trim": case "body": return t(d[ck]) ? t(d[ck]).toUpperCase() : null; case "drivetrain": case "engine": case "transmission": case "fuel": return t(d[ck]); case "options": { const names = (d.packages || []).concat((d.options || []).map(o => o && (o.name || o.code))).map(t).filter(Boolean); const seen = []; names.forEach(n => { if (!seen.some(s => s.toLowerCase() === n.toLowerCase())) seen.push(n); }); return seen.length ? seen.join(", ") : null; } case "msrp": { const n = Number(d.msrp); return n > 0 ? Math.round(n) : null; } default: return null; } }; // When MarketCheck is off or cannot answer, the SERVER falls back to NHTSA (the free government decoder) and then to what the VIN itself says // (?fallback=1 on the decode route, src/lib/vindecode.js): the answer then carries decoder / decoderLabel and a plain-English `marketcheck` note. const INV_MC_SILENT = /^(not_configured|not_installed|unavailable|upstream_auth|upstream_error|upstream_timeout|upstream_dropped|network|bad_response|rejected|internal|forbidden)$/; // cfg: { vin, enabled, keys:{ decodeKey: formKey }, read(), write(patch), isBlank(formKey, raw), shape:{ formKey: fn } } const InvMcUseVinFill = (cfg) => { const [stt, setStt] = React.useState({ phase: "idle" }); const R = React.useRef({}); R.current.cfg = cfg; const seq = React.useRef(0), doneVin = React.useRef(""), wrote = React.useRef({}); const vin = invMcNormVin(cfg.vin); const ok = cfg.enabled !== false && invMcVinOk(vin); // Fills from a decode of VIN v. A field that still holds exactly what the decode of ANOTHER VIN wrote, and that this answer does not fill, // is put back to what it was before (that value belonged to the other vehicle) - also when this VIN gave nothing at all (data = null). const fillFrom = (c, data, v) => { const cur = c.read(), patch = {}, filled = [], cleared = []; const other = !!wrote.current["|vin"] && wrote.current["|vin"] !== v; Object.keys(c.keys).forEach(ck => { const fk = c.keys[ck]; let val = data ? invMcPick(ck, data) : null; const ours = wrote.current[fk] !== undefined && String(cur[fk]) === String(wrote.current[fk]); // still exactly what the last decode put there const from = ours && wrote.current[fk + "|from"] !== undefined ? wrote.current[fk + "|from"] : cur[fk]; if (val == null || val === "") { if (ours && other) { patch[fk] = from; cleared.push({ fk, label: INV_MC_LABEL[ck] || fk, was: cur[fk], to: from }); delete wrote.current[fk]; delete wrote.current[fk + "|from"]; } return; } if (c.shape && c.shape[fk]) val = c.shape[fk](val); const blank = c.isBlank ? c.isBlank(fk, cur[fk]) : (cur[fk] === "" || cur[fk] == null); if (!blank && !ours) return; // never overwrite what a person typed patch[fk] = val; filled.push({ fk, label: INV_MC_LABEL[ck] || fk, from, to: val }); }); filled.forEach(f => { wrote.current[f.fk] = f.to; wrote.current[f.fk + "|from"] = f.from; }); if (filled.length) wrote.current["|vin"] = v; else if (other && !Object.keys(wrote.current).some(k => k[0] !== "|" && k.indexOf("|from") < 0)) delete wrote.current["|vin"]; if (Object.keys(patch).length) c.write(patch); return { filled, cleared }; }; const run = React.useCallback(async (v, o) => { doneVin.current = v; const my = ++seq.current; const confirmed = !!(o && o.confirm); // "VIN is correct" ticked: MarketCheck may be asked although the check digit fails setStt({ phase: "busy", vin: v }); let r = null, err = null; try { r = await invMcCall("/decode/" + v + "?fallback=1" + (confirmed ? "&confirm=1" : "")); } catch (e) { err = invMcFail(e, false); } if (my !== seq.current || invMcNormVin(R.current.cfg.vin) !== v) return; // the VIN changed while we waited const c = R.current.cfg; if (r && r.ok && r.data) { const { filled, cleared } = fillFrom(c, r.data, v); const byMc = !r.decoder || r.decoder === "marketcheck"; const how = r.source === "live" ? "looked up just now" : r.source === "cache" ? "cached " + (invMcAge(r.ageMinutes, r.fetchedAt) || "earlier") : (r.cached ? "saved earlier" : "looked up just now"); const obs = byMc ? (r.data.observationText || (r.data.decoded === false ? "MarketCheck has no specs for this VIN." : null)) : [r.data.decoded === false ? r.data.observationText : null, r.marketcheck && r.marketcheck.message].filter(Boolean).join(" ") || null; setStt({ phase: filled.length ? "filled" : (r.data.decoded === false ? "obs" : "nothing"), vin: v, filled, cleared, how, src: r.decoderLabel || "MarketCheck", obs, byMc, warn: r.warning || null, cdOk: r.checkDigitOk !== false, confirmed }); return; } const { cleared } = fillFrom(c, null, v); // this VIN gave nothing: the other VIN's details go if (err && INV_MC_SILENT.test(err.code)) { setStt(cleared.length ? { phase: "cleared", vin: v, cleared } : { phase: "idle", vin: v }); return; } setStt({ phase: "msg", vin: v, text: err ? err.text : "", kind: err && /^(bad_check_digit|bad_vin)$/.test(err.code) ? "warn" : "info", cleared }); }, []); React.useEffect(() => { if (!ok) { if (doneVin.current && doneVin.current !== vin) { doneVin.current = ""; seq.current++; // a complete VIN that will not be looked up (wrong letters, or the unit's own VIN again on the Edit tab): the other VIN's details go const cl = vin.length >= 17 ? fillFrom(R.current.cfg, null, vin).cleared : []; setStt(cl.length ? { phase: "cleared", vin, cleared: cl } : { phase: "idle" }); } return; } if (doneVin.current === vin) return; const t = setTimeout(() => { if (doneVin.current !== vin) run(vin); }, 450); return () => clearTimeout(t); }, [vin, ok]); const onBlur = () => { if (ok && doneVin.current !== vin) run(vin); }; const undo = () => { const c = R.current.cfg; if (!stt.filled) return; const cur = c.read(), back = {}; stt.filled.forEach(f => { if (String(cur[f.fk]) === String(f.to)) { back[f.fk] = f.from; delete wrote.current[f.fk]; delete wrote.current[f.fk + "|from"]; } }); // only fields the person has not changed since c.write(back); const n = Object.keys(back).length; setStt({ phase: "undone", vin: stt.vin, count: n, kept: stt.filled.length - n }); }; const again = () => { if (ok) run(vin); }; const confirm = () => { if (ok) run(vin, { confirm: true }); }; // "VIN is correct": decode again, MarketCheck included return { stt, onBlur, undo, again, confirm }; }; const InvMcVinNote = ({ fill }) => { const s = fill && fill.stt; if (!s || s.phase === "idle") return null; const box = (bg, bd, children, phase) => (
{children}
); const link = (label, fn, d) => ; const gone = s.cleared && s.cleared.length ?
The VIN changed, so the {s.cleared.map(f => f.label).join(", ")} filled in for the previous VIN {s.cleared.length === 1 ? "was" : "were"} cleared.
: null; // the check-digit warning, with a real "VIN is correct" box (only the free decoders were used until it is ticked) const warn = s.warn ? (
{s.warn} {s.cdOk === false && !s.confirmed && s.byMc === false && fill.confirm && }
) : null; if (s.phase === "busy") return box("#EEF2F8", "#B8C4D8", "Looking up this VIN…"); if (s.phase === "cleared") return box("#FFF6DA", "#E3C77A", gone); if (s.phase === "filled") { return box("#EAF6EC", "#7DB887", <> ✓ Filled from {s.src}{s.how ? " - " + s.how : ""} {link("Undo", fill.undo, "undo")}
{s.filled.map(f => f.label).join(", ")}. Anything already typed was left alone.
{s.obs &&
{s.byMc === false ? "" : "MarketCheck note: "}{s.obs}
} {warn} {gone} ); } if (s.phase === "obs") return box("#FFF6DA", "#E3C77A", <>{s.byMc === false ? "" : "MarketCheck could not decode this VIN: "}{s.obs} You can still type the details by hand.{warn}{gone}); if (s.phase === "nothing") return box("#EEF2F8", "#B8C4D8", <>{s.src || "MarketCheck"} knows this VIN. {s.how ? "(" + s.how + ") " : ""}Nothing to fill - the fields already have values.{s.obs ? " " + (s.byMc === false ? "" : "MarketCheck note: ") + s.obs : ""}{warn}{gone}); if (s.phase === "undone") return box("#F2F2F2", "#B0B0B0", <>Undone{s.count ? " - " + s.count + " field" + (s.count === 1 ? "" : "s") + " cleared" : ""}{s.kept ? "; " + s.kept + " you changed " + (s.kept === 1 ? "was" : "were") + " kept" : ""}.{link("Fill again", fill.again, "again")}); if (s.phase === "msg" && s.text) return box(s.kind === "warn" ? "#FFF6DA" : "#F2F2F2", s.kind === "warn" ? "#E3C77A" : "#B0B0B0", <>{s.text}{gone}); if (s.phase === "msg" && gone) return box("#FFF6DA", "#E3C77A", gone, "cleared"); return null; }; // Which stock-in / add-unit / edit fields take which decoded value; keys = decodeKey -> form field. // (MSRP and prices are deliberately NOT filled: they are money fields, and an empty asking price falls back to MSRP on the stock-in screen.) const INV_MC_KEYS_STD = { year: "yr", make: "mk", model: "mdl", trim: "trim", body: "body", drivetrain: "drive", engine: "engine", transmission: "trans", fuel: "fuel", options: "options" }; const invMcBlankRaw = (k, raw) => raw == null || String(raw).trim() === "" || ((k === "yr" || k === "msrp") && !(parseFloat(String(raw).replace(/[$,]/g, "")) > 0)); // ── market price blocks ────────────────────────────────────────────────── // Per-VIN newest saved price for the list column (server: POST /cached - never calls MarketCheck, costs nothing). const invMcLoadCached = async (vins) => { const out = {}; for (let i = 0; i < vins.length; i += 300) { try { const r = await invMcCall("/cached", { method: "POST", body: { vins: vins.slice(i, i + 300) } }); if (r && r.items) Object.assign(out, r.items); } catch (e) { /* the column just stays blank */ } } return out; }; const InvMcChip = ({ children, color, bg, style }) => ( {children} ); // List cells. "Our price" and "Market price" are two separate columns; Difference is management only. const InvMcMarketCell = ({ r, item, on, busy, cell }) => { const td = (content, title) => {content}; const dash = (t) => td(—, t); if (r.status === "SOLD") return dash("Sold - no market price lookup"); if (r.type === "N") { // New units: no market price - MSRP, flagged return r.msrp > 0 ? td(<>{inv$(r.msrp)}MSRP, "New vehicle: the factory MSRP is shown, not a market price") : td(—MSRP, "New vehicle: open the unit to see the MSRP"); } if (!invMcVinOk(r.vin)) return dash("A market price needs a full 17-character VIN"); if (!on) return dash(INV_MC_NOT_ON); if (!item) return busy ? td(…, "Loading saved market price") : dash("No saved market price yet - open the unit to look one up"); if (item.predictedPrice == null) return td(none, "MarketCheck has no market price for this vehicle"); const age = invMcAge(item.ageMinutes, item.fetchedAt); const old = item.fresh === false; return td(<>{inv$(+item.predictedPrice)}
{old ? "old - " : ""}{age}
, old ? `Saved ${age} - open the unit to look it up again` : `Market price as of ${age}`); }; const InvMcDiffCell = ({ r, item, cell }) => { const mp = item && item.predictedPrice != null ? +item.predictedPrice : null; const td = (content, title) => {content}; if (r.type === "N" || r.status === "SOLD" || mp == null || !(r.list > 0)) return td(—); const diff = r.list - mp, pct = mp ? (diff / mp) * 100 : 0; const col = Math.abs(pct) < 1 ? "var(--k2b-navy)" : diff > 0 ? "var(--k2b-maroon)" : "var(--k2b-green)"; return td({(diff >= 0 ? "+" : "-") + inv$(Math.abs(diff))} ({(pct >= 0 ? "+" : "-") + Math.abs(pct).toFixed(1)}%), `Our price is ${inv$(Math.abs(diff))} ${diff >= 0 ? "above" : "below"} the market price`); }; // Unit detail: the market price block. Sales (and every non-management role) get price + "as of"; management also gets the difference, // predicted range, comparables, listing history and the Refresh button. One lookup opens with the unit (server: saved answer first). const InvMarketBlock = ({ u, sales, onMarket }) => { const [st, reloadSt] = invMcUseStatus(); const mgmt = invMcIsMgmt(st); const vin = invMcNormVin(u.vin), isNew = invIsNew(u), sold = window.DealCalc.unitGone(u); // RC28 fix round const asking = +(u.asking || u.list || 0); const [s, setS] = React.useState({ phase: "wait" }); // wait | off | sold | novin | new | loading | ok | err const [hist, setHist] = React.useState(null); // { phase, data, source, ... } const [showRecent, setShowRecent] = React.useState(false); const [raw, setRaw] = React.useState(null); // Admin only: { phase, text } - the saved raw MarketCheck answer (to check field names) const alive = React.useRef(true); React.useEffect(() => { alive.current = true; return () => { alive.current = false; }; }, []); const loadNew = async () => { // New units: no market price - MSRP (flagged) const own = +u.msrp || 0; if (own > 0 || sales) { setS({ phase: "new", msrp: own, from: "the unit record" }); return; } setS({ phase: "new", msrp: 0, from: "", busy: true }); try { const r = await invMcCall("/decode/" + vin); const m = r && r.data && Number(r.data.msrp) > 0 ? Math.round(Number(r.data.msrp)) : 0; if (alive.current) setS({ phase: "new", msrp: m, from: m ? "MarketCheck VIN decode" : "", at: r && r.fetchedAt, src: r && r.source }); } catch (e) { if (alive.current) setS({ phase: "new", msrp: 0, from: "", err: invMcFail(e, mgmt) }); } }; const loadHist = async (refresh) => { setHist(h => ({ ...(h || {}), phase: "loading" })); try { const r = await invMcCall("/history/" + vin + (refresh ? "" : "?cacheOnly=1")); if (alive.current) setHist({ phase: "ok", data: r.data, source: r.source, fetchedAt: r.fetchedAt, ageMinutes: r.ageMinutes }); } catch (e) { const f = invMcFail(e, true); if (alive.current) setHist(!refresh && (e && e.body && e.body.code) === "not_cached" ? { phase: "none" } : { phase: "err", err: f }); } }; const load = async (refresh) => { setS(p => ({ ...p, phase: p.r ? "ok" : "loading", busy: true, err: null })); try { const r = await invMcCall("/predict/" + vin + (refresh ? "?refresh=1" : "")); if (!alive.current) return; if (r && r.skipped) { loadNew(); return; } setS({ phase: "ok", r, busy: false }); const mp = r && r.data ? r.data.predictedPrice : null; if (onMarket) onMarket(vin, { predictedPrice: mp == null ? null : mp, fetchedAt: r.fetchedAt, ageMinutes: r.ageMinutes, fresh: true, source: r.source }); if (refresh || r.source === "live") reloadSt(); // "calls left" on the Refresh button if (r.data && r.data.comparables) loadHist(false); // management only: show saved history if there is one (free) } catch (e) { if (alive.current) setS(p => ({ phase: "err", err: invMcFail(e, mgmt), r: p.r, busy: false })); } }; React.useEffect(() => { if (!st) return; if (invMcNoAccess(st)) { setS({ phase: "none" }); return; } // not part of this role: no block, no lookup if (!invMcOn(st)) { setS({ phase: "off" }); return; } if (sold) { setS({ phase: "sold" }); return; } if (!invMcVinOk(vin)) { setS({ phase: "novin" }); return; } if (isNew) { loadNew(); return; } load(false); }, [!!st, st && st.configured, vin]); const openRaw = async () => { setRaw({ phase: "loading" }); try { const j = await invMcCall("/raw/predict/" + vin); let t = JSON.stringify(j.raw, null, 2) || ""; const n = t.length; if (n > 40000) t = t.slice(0, 40000) + `\n… (cut here - ${invMcCount(n)} characters in total)`; if (alive.current) setRaw({ phase: "ok", text: t }); } catch (e) { if (alive.current) setRaw({ phase: "err", text: invMcFail(e, true).text }); } }; const r = s.r, d = r && r.data; const price = d && d.predictedPrice != null ? +d.predictedPrice : null; const diff = price != null && asking > 0 ? asking - price : null; const pct = diff != null && price ? (diff / price) * 100 : null; const left = st && st.remaining ? st.remaining.month : null; const box = { border: "1px solid #C9CED8", borderRadius: 6, padding: "8px 12px" }; const tile = { flex: "1 1 130px", minWidth: 0, border: "1px solid #D5D9DF", borderRadius: 5, padding: "4px 8px", background: "#FAFBFC" }; const tl = { fontSize: 11, fontWeight: 700, color: "var(--k2b-maroon)" }; const tv = { fontFamily: "'Courier Prime', monospace", fontWeight: 700, fontSize: 15, color: "var(--k2b-navy)" }; const small = { fontSize: 11, color: "#667", fontFamily: "var(--font-ui)" }; const msg = (text, kind) =>
{text}
; if (s.phase === "none" || invMcNoAccess(st)) return null; let body; if (s.phase === "wait" || s.phase === "loading") body =
Loading market price…
; else if (s.phase === "off") body = msg(INV_MC_NOT_ON); else if (s.phase === "sold") body = msg("This unit is sold - there is no market price lookup for it."); else if (s.phase === "novin") body = msg("A market price needs the full 17-character VIN. Add it on the Edit Details tab."); else if (s.phase === "new") { body = (
Our price
{invMoney(asking)}
MSRP MSRP
{s.busy ? "…" : (s.msrp > 0 ? invMoney(s.msrp) : "—")}
{s.from &&
from {s.from}
}
New vehicles are not priced against the market. The factory MSRP is shown instead - it is the manufacturer's list price, not a market price.
{s.err && msg(s.err.text, "warn")}
); } else { const retry = s.err && s.err.retryable ? : null; const errBox = s.err ?
{s.err.text}{retry}
: null; const asOf = r ? (mgmt ? `as of ${invMcWhen(r.fetchedAt)} (${r.source === "live" ? "live" : "cached"}${r.source === "cache" ? ", " + invMcAge(r.ageMinutes, r.fetchedAt) : ""})` : `as of ${invMcAge(r.ageMinutes, r.fetchedAt)}`) : ""; const stat = d && d.comparableStats ? d.comparableStats.price : null; const comps = d && Array.isArray(d.comparables) ? d.comparables : []; const hasCol = (k) => comps.some(c => c[k] != null && c[k] !== ""); const recent = d && d.recentComparables ? d.recentComparables : null; const cth = { padding: "2px 8px", textAlign: "right", fontSize: 11, color: "var(--k2b-maroon)", background: "#F0F2F5", borderBottom: "1px solid #9AA4B5", position: "sticky", top: 0, whiteSpace: "nowrap" }; const ctd = { padding: "2px 8px", textAlign: "right", borderBottom: "1px solid #EEE", fontFamily: "'Courier Prime', monospace", fontSize: 12 }; const money0 = (n) => n == null ? "—" : "$" + Number(n).toLocaleString("en-US", { maximumFractionDigits: 0 }); body = (
{errBox} {r && price == null && msg(mgmt ? ((d && d.note) || "MarketCheck has no market price for this vehicle - it found no comparable listings.") : "No market price is available for this vehicle right now.")} {r && (
Our price
{invMoney(asking)}
Market price
{price != null ? invMoney(price) : "—"}
{asOf}
{mgmt && diff != null && (
Difference
0 ? "var(--k2b-maroon)" : "var(--k2b-green)" }}>{(diff >= 0 ? "+" : "-") + invMoney(Math.abs(diff))} ({(pct >= 0 ? "+" : "-") + Math.abs(pct).toFixed(1)}%)
{Math.abs(pct) < 1 ? "at market" : diff > 0 ? "our price is above market" : "our price is below market"}
)}
)} {mgmt && d && d.comparables && (
{+d.msrp > 0 && MSRP {money0(d.msrp)}} {d.low != null && d.high != null && Typical range of comparables {money0(d.low)} - {money0(d.high)}} {stat && stat.median != null && Median {money0(stat.median)}} Comparables found {invMcCount(d.comparablesCount)}{d.comparablesShown && d.comparablesShown < d.comparablesCount ? ` (${d.comparablesShown} listed)` : ""}
{d.comparablesCount === 0 && comps.length === 0 && price != null && msg("MarketCheck found no comparable listings for this vehicle - the price is a model estimate only.", "warn")} {comps.length > 0 && (
{hasCol("dealer") && } {hasCol("distanceMi") && } {hasCol("daysOnMarket") && } {hasCol("trim") && } {comps.map((c, i) => ( {hasCol("dealer") && } {hasCol("distanceMi") && } {hasCol("daysOnMarket") && } {hasCol("trim") && } ))}
PriceMilesDealerDistanceDays on marketTrim
{money0(c.price)}{c.miles == null ? "—" : invMcCount(c.miles)}{[c.dealer, [c.city, c.state].filter(Boolean).join(", ")].filter(Boolean).join(" - ") || "—"}{c.distanceMi == null ? "—" : c.distanceMi + " mi"}{c.daysOnMarket == null ? "—" : c.daysOnMarket}{c.trim || "—"}
)} {recent && (recent.numFound > 0 || (recent.listings || []).length > 0) && (
Recently listed asking prices (not sales): {invMcCount(recent.numFound || (recent.listings || []).length)} listing{(recent.numFound || (recent.listings || []).length) === 1 ? "" : "s"} {recent.stats && recent.stats.median != null ? <>, median {money0(recent.stats.median)} : null} {(recent.listings || []).length > 0 && } {showRecent && (
{recent.listings.map((c, i) => )}
Asking priceMilesDays listed
{money0(c.price)}{c.miles == null ? "—" : invMcCount(c.miles)}{c.daysOnMarket == null ? "—" : c.daysOnMarket}
)}
)}
Listing history{" "} {(!hist || hist.phase === "none") && <>not looked up yet. } {hist && hist.phase === "loading" && loading…} {hist && hist.phase === "err" && {hist.err.text}} {hist && hist.phase === "ok" && hist.data && (hist.data.noHistory || !hist.data.summary ? MarketCheck has no listing history for this VIN. : ( listed {invMcCount(hist.data.summary.timesListed)} time{hist.data.summary.timesListed === 1 ? "" : "s"} {hist.data.summary.firstSeen ? `, first seen ${new Date(hist.data.summary.firstSeen).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}` : ""} {hist.data.summary.lowestPrice != null ? `, lowest ${money0(hist.data.summary.lowestPrice)}, highest ${money0(hist.data.summary.highestPrice)}` : ""} {hist.data.summary.priceDrops != null ? `, ${hist.data.summary.priceDrops} price drop${hist.data.summary.priceDrops === 1 ? "" : "s"}` : ""} {hist.data.summary.lastMiles != null ? `, last odometer ${invMcCount(hist.data.summary.lastMiles)}` : ""} ({hist.source === "live" ? "live" : "cached " + invMcAge(hist.ageMinutes, hist.fetchedAt)}) ))}
)}
); } return (
Market price {mgmt && st.features.raw && r && } {mgmt && st.features.refresh && !isNew && !sold && invMcVinOk(vin) && invMcOn(st) && ( )}
{body} {raw && ( setRaw(null)} buttons={ setRaw(null)}>Close}> {raw.phase === "loading" ?
Loading…
: raw.phase === "err" ?
{raw.text}
:
{raw.text}
}
)}
); }; // Management: "Refresh market prices" for the whole lot. Always asks first, with the dry-run numbers; real runs go 25 VINs at a time. const InvMcRefreshDialog = ({ onClose, onDone }) => { const [ph, setPh] = React.useState("plan"); // plan | run | done | err const [plan, setPlan] = React.useState(null); const [err, setErr] = React.useState(null); const [prog, setProg] = React.useState({ done: 0, total: 0, live: 0, cached: 0, failed: 0 }); const [final, setFinal] = React.useState(null); const stop = React.useRef(false), alive = React.useRef(true), ran = React.useRef(false); React.useEffect(() => { alive.current = true; return () => { alive.current = false; stop.current = true; if (ran.current && onDone) onDone(); }; }, []); React.useEffect(() => { invMcCall("/refresh-all", { method: "POST", body: { dryRun: true } }) .then(r => { if (alive.current) setPlan(r); }) .catch(e => { if (alive.current) { setErr(invMcFail(e, true)); setPh("err"); } }); }, []); const go = async () => { const vins = plan.needVins.slice(0, plan.canRunNow), size = plan.batchSize || 25; stop.current = false; ran.current = true; setPh("run"); let live = 0, cached = 0, failed = 0, msgOut = "", doneN = 0; for (let i = 0; i < vins.length; i += size) { if (stop.current) { msgOut = "Stopped."; break; } try { const r = await invMcCall("/refresh-all", { method: "POST", body: { dryRun: false, vins: vins.slice(i, i + size) } }); live += r.live || 0; cached += r.fromCache || 0; failed += r.failed || 0; doneN = Math.min(vins.length, i + size); if (alive.current) setProg({ done: doneN, total: vins.length, live, cached, failed }); if (r.stoppedBy) { msgOut = r.message; break; } } catch (e) { const f = invMcFail(e, true); msgOut = f.text; failed++; break; } } if (alive.current) { setFinal(msgOut || `Done: ${live} new price${live === 1 ? "" : "s"}, ${cached} already fresh, ${failed} failed.`); setPh("done"); } }; const b = plan && plan.budget; const fits = plan && plan.fits; const buttons = ( <> { stop.current = true; onClose(); }}>{ph === "done" || ph === "err" || (ph === "plan" && plan && plan.needLiveCalls === 0) ? "Close" : "Cancel"} {ph === "plan" && plan && plan.needLiveCalls > 0 && plan.canRunNow > 0 && ( )} {ph === "run" && } ); return (
{ph === "plan" && !plan &&
Checking what a refresh would cost…
} {ph === "err" && err &&
{err.text}
} {ph === "plan" && plan && ( <>
{invMcCount(plan.needLiveCalls)} call{plan.needLiveCalls === 1 ? "" : "s"} needed, {invMcCount(b.monthRemaining)} left this month
{plan.message}
Used units in stock that can be priced{invMcCount(plan.eligible)} Already have a price from the last 24 hours{invMcCount(plan.alreadyFresh)} Calls left today{invMcCount(b.dayRemaining)} {b.budgetRemainingUsd != null && <>Spending limit left this monthabout ${Number(b.budgetRemainingUsd).toFixed(2)}} Estimated cost of this refreshabout ${Number(plan.estCostUsd || 0).toFixed(2)}
{plan.estCostNote || "Estimate from MarketCheck list prices - the MarketCheck invoice is the truth."}
{plan.skippedCount > 0 &&
{plan.skippedCount} unit{plan.skippedCount === 1 ? "" : "s"} skipped (sold, new, no valid VIN or no odometer reading).
} {!fits && plan.needLiveCalls > 0 &&
That is more than the limits allow right now{plan.canRunNow > 0 ? ` - only the first ${plan.canRunNow} can be refreshed` : ""}. Ask the owner to raise the limit if you need all of them.
} )} {ph === "run" &&
Refreshing {prog.done} of {prog.total || (plan && Math.min(plan.canRunNow, plan.needVins.length))}… ({prog.live} new, {prog.cached} already fresh, {prog.failed} failed)
} {ph === "done" &&
{final}
}
); }; const Inventory = ({ onClose, go }) => { const [type, setType] = React.useState("ALL"); const [status, setStatus] = React.useState("ALL"); const [search, setSearch] = React.useState(""); const [selStock, setSelStock] = React.useState(""); // last-opened unit (row highlight) const [openUnit, setOpenUnit] = React.useState(() => { // { stock, tab } — the unit detail panel const s = window.jbInvOpen; window.jbInvOpen = null; // Stock-In "View in INVSTK" hand-off return s ? { stock: s, tab: "overview" } : null; }); const [modal, setModal] = React.useState(null); // null | "add" const [flash, setFlash] = React.useState(""); // Re-render whenever the DMS ledger changes (stock-in, edits, deal posting, …) const [, force] = React.useReducer(n => n + 1, 0); React.useEffect(() => (window.DMS && window.DMS.subscribe) ? window.DMS.subscribe(force) : undefined, []); const canEdit = invCanEdit(), canDesk = invCanDesk(), sales = invIsSales(); // Market price (MarketCheck). The list only ever reads SAVED prices (free); a live lookup happens when a unit is opened or a manager presses Refresh. const [mcSt, mcReloadSt] = invMcUseStatus(); const mcMgmt = invMcIsMgmt(mcSt), mcOn = invMcOn(mcSt), mcShow = !invMcNoAccess(mcSt); // mcShow false: this role gets no market price column const [mcPrices, setMcPrices] = React.useState({}); // VIN -> { predictedPrice, fetchedAt, ageMinutes, fresh } const [mcBusy, setMcBusy] = React.useState(false); const mcAsked = React.useRef(new Set()); const [mcTick, setMcTick] = React.useState(0); // Live units straight from the DMS ledger (one source of truth). const dmsUnits = (window.DMS ? window.DMS.inventory() : []).map(u => ({ stock: u.stock, vin: u.vin || "", yr: u.yr || "", mk: String(u.mk || "").toUpperCase(), mdl: String(u.mdl || "").toUpperCase(), trim: u.trim || "", body: String(u.body || "").toUpperCase(), color: String(u.color || u.extColor || "").toUpperCase(), miles: +u.miles || 0, cost: +u.cost || 0, list: +(u.asking || u.list || 0), days: invDays(u), type: invIsNew(u) ? "N" : "U", status: invStatusCode(u), msrp: +u.msrp || 0, })); const ALL_STOCK = [...INV_STOCK.filter(x => !dmsUnits.some(d => d.stock === x.stock)), ...dmsUnits]; const rows = ALL_STOCK.filter(r => (type === "ALL" || r.type === type) && (status === "ALL" || r.status === status) && (search.trim() === "" || (r.stock + " " + r.vin + " " + r.yr + " " + r.mk + " " + r.mdl + " " + r.color).toLowerCase().includes(search.trim().toLowerCase())) ); const isEmpty = rows.length === 0; const selRow = rows.find(r => r.stock === selStock) || null; // Lazy: saved prices for the units on screen that can have one (used, in stock, full VIN) - one request, no MarketCheck call. const mcWanted = (r) => r.type === "U" && r.status !== "SOLD" && invMcVinOk(r.vin); const mcVinsKey = rows.filter(mcWanted).map(r => invMcNormVin(r.vin)).join(","); React.useEffect(() => { if (!mcOn || !mcShow || !mcVinsKey) return; const need = mcVinsKey.split(",").filter(v => !mcAsked.current.has(v)); if (!need.length) return; need.forEach(v => mcAsked.current.add(v)); setMcBusy(true); invMcLoadCached(need).then(items => { setMcPrices(p => ({ ...p, ...items })); setMcBusy(false); }); }, [mcOn, mcShow, mcVinsKey, mcTick]); const mcOnMarket = (vin, item) => setMcPrices(p => ({ ...p, [vin]: item })); const mcReloadAll = () => { mcAsked.current = new Set(); setMcTick(n => n + 1); mcReloadSt(); }; const mcLeft = mcSt && mcSt.remaining ? mcSt.remaining : null; const mcPausedCode = mcMgmt && mcLeft ? (mcSt.exhausted ? "quota_exhausted" : +mcLeft.month <= 0 ? "monthly_cap" : (mcLeft.budgetUsd != null && +mcLeft.budgetUsd <= 0) ? "monthly_budget" : +mcLeft.day <= 0 ? "daily_cap" : "") : ""; const mcCols = !mcShow ? 0 : mcMgmt ? 2 : 1; // Market price (roles that get it) + Difference (management) const toast = (msg) => { setFlash(msg); setTimeout(() => setFlash(""), 2800); }; const open = (stock, tab) => { if (!stock) return; setSelStock(stock); setOpenUnit({ stock, tab: tab || "overview" }); }; const filterLabel = [type === "ALL" ? "New & used" : TYPE_LABEL[type], status === "ALL" ? "all statuses" : "status " + status, search.trim() ? `search "${search.trim()}"` : ""].filter(Boolean).join(" · "); const printList = () => invPrintList(rows, filterLabel); // F9 prints the list; F2 jumps to search — only while no pop-up is open. React.useEffect(() => { const onKey = (e) => { if (document.querySelector("[data-k2b-modal], .k2b-print-overlay")) return; if (e.key === "F9") { e.preventDefault(); printList(); } else if (e.key === "F2") { e.preventDefault(); const i = document.querySelector("[data-inv='search']"); i && i.focus(); } }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }); const handleAdded = async (unit) => { // RC33 (mpx): the stock-in is a SERVER command — await the committed answer setModal(null); setType("ALL"); setStatus("ALL"); setSearch(""); // Same ledger path as the Stock-In screen: unit lands in DMS inventory AND // its purchase JE posts (cash purchase, no floorplan — use Stock In to floor). if (window.DMS && window.DMS.stockInVehicle) { const r = await window.DMS.stockInVehicle({ stock: unit.stock, vin: unit.vin, yr: unit.yr, mk: unit.mk.trim(), mdl: unit.mdl, body: unit.body, color: unit.color, miles: unit.miles, type: unit.type, cost: unit.cost, asking: unit.list, floorAmt: 0, }); if (!r.ok) { toast(r.msg); setModal("add"); return; } if (window.DMS.updateUnit && (unit.description || unit.msrp)) window.DMS.updateUnit(unit.stock, { description: unit.description || "", msrp: +unit.msrp || 0 }); const more = {}; ["trim", "drive", "engine", "trans", "fuel", "options"].forEach(k => { if (unit[k]) more[k] = unit[k]; }); // from the VIN decode (all optional) if (window.DMS.updateUnit && Object.keys(more).length) window.DMS.updateUnit(unit.stock, more); toast(`${unit.stock} stocked in${r.id ? " — JE " + r.id + " posted to GL" : ""}.`); setSelStock(unit.stock); return; } INV_STOCK.unshift(unit); force(); toast(`Unit ${unit.stock} added to inventory.`); }; const th = (label, w, right) => ( {label} ); const colCount = (sales ? 10 : 11) + mcCols; return (
{}} onMax={() => {}} /> {flash && (
✓ {flash}
)} {/* Body: filters + list + KPIs. The list scrolls; the footer below is a normal flex footer. */}
{/* top filter strip */}
Type: {["ALL","N","U"].map(t => ( ))} Status: {["ALL","STOCK","HOLD","TRANS","SOLD","AGED"].map(s => ( ))} Search: setSearch(e.target.value)} placeholder="stock / VIN / model / color" {...JB_NO_AUTOFILL} name="k2b-inv-find" spellCheck={false} style={{ background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 6px", width: 210, color: "var(--k2b-navy)", fontFamily: "'Courier Prime', monospace" }} />
{rows.length} of {ALL_STOCK.length} units · click a unit to open it {search && }
{/* market price notices */} {mcSt && !mcOn && mcShow && (
{INV_MC_NOT_ON}
)} {mcPausedCode && (
{invMcText(mcPausedCode, "", true, 0)}
)} {/* unit list — every row opens the unit detail */}
{th("T", 26)}{th("Stock #", 90)}{th("Year / Make / Model")}{th("VIN", 170)}{th("Color", 96)} {th("Miles", 70, true)}{!sales && th("Cost", 92, true)} {mcShow && } {mcMgmt && } {th("Days", 46, true)}{th("Status", 60)}{th("", 64)} {isEmpty && ( )} {rows.map((r, i) => { const isSel = r.stock === selStock; const base = isSel ? "#D6E6F7" : (i % 2 ? "#F4F4F4" : "#FFFFFF"); const cell = { padding: "3px 6px", background: base, borderBottom: "1px solid #EEE" }; return ( open(r.stock)} onDoubleClick={() => open(r.stock)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); open(r.stock); } else if (e.key === "ArrowDown" || e.key === "ArrowUp") { e.preventDefault(); const sib = e.key === "ArrowDown" ? e.currentTarget.nextElementSibling : e.currentTarget.previousElementSibling; if (sib && sib.focus) { sib.focus(); setSelStock(sib.getAttribute("data-stock") || ""); } } }} style={{ color: "var(--k2b-blue-data-dk)" }}> {!sales && } {mcShow && } {mcMgmt && } ); })}
Our priceMarket priceDiff vs market
🚗
{ALL_STOCK.length ? (search ? No units match '{search}' · : "No units match the current filter.") : "No vehicles in inventory yet."}
{!ALL_STOCK.length && canEdit && ( <>
Add your first vehicle with F3-Add Unit, or bring your existing lot in from your old DMS using the Import / Export tool.
setModal("add")} style={{ background: "#FFE9B0", fontWeight: 700 }}>+ Add Vehicle go && go("uploadtool")}>Import Inventory ▸
)}
{r.type} {r.stock} {r.yr} {r.mk} {r.mdl} {r.vin} {r.color} {r.miles.toLocaleString()}{inv$(r.cost)}{inv$(r.list)} 90 ? "var(--k2b-maroon)" : r.days > 60 ? "#B26A00" : "var(--k2b-blue-data-dk)", fontWeight: r.days > 90 ? 700 : 400 }}>{r.days} {r.status} Open ▸
{/* aging / KPIs */}
{[ ["New units", ALL_STOCK.filter(r => r.type === "N").length, "var(--k2b-blue-data-dk)"], ["Used units", ALL_STOCK.filter(r => r.type === "U").length, "var(--k2b-blue-data-dk)"], ["Avg days", Math.round(ALL_STOCK.reduce((s, r) => s + r.days, 0) / (ALL_STOCK.length || 1)), "var(--k2b-navy)"], ["Aged >90d", ALL_STOCK.filter(r => r.days > 90).length, "var(--k2b-maroon)"], sales ? ["Total asking", "$" + (ALL_STOCK.reduce((s, r) => s + r.list, 0) / 1000).toFixed(0) + "K", "var(--k2b-green)"] : ["Total inv cost", "$" + (ALL_STOCK.reduce((s, r) => s + r.cost, 0) / 1000).toFixed(0) + "K", "var(--k2b-green)"], ].map(([label, val, color], i) => (
{label}
{val}
))}
{/* F-key footer — a normal flex footer (never absolutely positioned); wraps on narrow screens */}
ESC-Close { const i = document.querySelector("[data-inv='search']"); i && i.focus(); }}>F2-Lookup {canEdit && setModal("add")}>F3-Add Unit} {canEdit && go && go("stockin")}>Stock In (Floorplan) ▸} selRow ? open(selRow.stock, canEdit ? "edit" : "overview") : toast("Click a unit in the list first.")}>{canEdit ? "F4-Edit Unit" : "F4-View Unit"} {canDesk && selRow ? invDeskUnit((window.DMS && window.DMS._unit(selRow.stock)) || selRow, go) : toast("Click a unit in the list first.")}>F5-Desk Unit} selRow ? printWindowSticker((window.DMS && window.DMS._unit(selRow.stock)) || selRow) : toast("Click a unit in the list first.")}>F6-Window Stkr invVinDecodeOpen(selRow ? ((window.DMS && window.DMS._unit(selRow.stock)) || selRow) : null, go)}>F8-VIN Decode {mcMgmt && mcSt.features.refreshAll && mcOn && setModal("mcrefresh")}>Refresh market prices} F9-Print Inventory List
{modal === "add" && setModal(null)} onAdd={handleAdded} />} {modal === "mcrefresh" && setModal(null)} onDone={mcReloadAll} />} {openUnit && setOpenUnit(null)} />}
); }; // ───────────────────────────────────────────────────────────────────────── // VEHICLE 360 — one stock #, every department (connected system) const InvVehicle360 = ({ u, go, sales }) => { const D = window.DMS; if (!D || !u || !u.stock) return null; const deal = (D.deals() || []).find(d => d.stock === u.stock) || null; const isInternal = (r) => /internal|recon|recondition|lot|make-ready|makeready/i.test((r.type || "") + " " + (r.payType || "") + " " + (r.payer || "")); const ros = ((D.repairOrders && D.repairOrders()) || []).filter(r => (r.stock && r.stock === u.stock) || (u.vin && r.vin && r.vin === u.vin && isInternal(r))); const reconCost = ros.reduce((s, r) => s + (+r.total || +r.totalCost || 0), 0); const fp = ((D.floorplanPayoffs && D.floorplanPayoffs()) || []).find(f => f.stock === u.stock) || null; const floored = +u.floorAmt || (fp ? +fp.amount : 0); // RC28 (fx, gate 2b): the deal's cost lines (Profit panel) come off the gross the same way as on the desk and in the posting const caV = deal && window.DealCalc && Array.isArray(deal.costAdds) && deal.costAdds.length ? window.DealCalc.costAddsOf(deal) : null; const front = deal ? ((+deal.salePrice || 0) - (+u.cost || 0) - reconCost - ((+deal.tradeAllow || 0) - (+deal.tradeAcv || 0)) - (caV ? caV.frontCents / 100 : 0)) : null; const back = deal ? (+deal.fiGross || ((+deal.vscSale || 0) + (+deal.gapSale || 0))) - (caV ? caV.backCents / 100 : 0) : null; const cb = !sales && D.costBasis ? D.costBasis(u.stock) : null; return (
Vehicle 360 · connected
Deal {deal ? <>{deal.id}{deal.buyer} {deal.postedToGL ? "POSTED" : (deal.status || "WORKING")} : no deal — unit in stock}
Recon {ros.length ? `${ros.length} RO${ros.length !== 1 ? "s" : ""}` : "none"} {reconCost ? invMoney(reconCost) : ""}
{ros.slice(0, 3).map(r => (
{r.id}{/closed|paid/i.test(r.status || "") ? "closed" : (r.status || "open")}
))}
Floorplan {floored ? invMoney(floored) + (u.floorLender ? " · " + u.floorLender : "") : "not floored"}
{cb && cb.found && (
True Cost Basis
{cb.lines.map((l, i) => (
{l.label}{invMoney(l.amount)}
))}
Total Cost Basis{invMoney(cb.total)}
)} {deal && !sales && (
Front Gross= 0 ? "var(--k2b-green)" : "var(--k2b-maroon)", fontWeight: 700 }}>{invMoney(front)} Back Gross{invMoney(back)} Total Gross{invMoney((front || 0) + (back || 0))}
)}
{deal && }
); }; // ── Edit Details form atoms (module scope so inputs keep focus while typing) ── const INV_EDIT_FIELDS = [ { group: "Vehicle", fields: [ { k: "vin", label: "VIN", w: 2 }, { k: "yr", label: "Year" }, { k: "mk", label: "Make" }, { k: "mdl", label: "Model" }, { k: "trim", label: "Trim" }, { k: "body", label: "Body style" }, { k: "type", label: "New / Used", options: ["New", "Used"] }, { k: "color", label: "Exterior color" }, { k: "intColor", label: "Interior color" }, { k: "miles", label: "Mileage", num: true }, ] }, { group: "Pricing (unit record)", fields: [ { k: "cost", label: "Cost", num: true, money: true }, { k: "asking", label: "Retail / asking price", num: true, money: true }, { k: "msrp", label: "MSRP", num: true, money: true }, { k: "pack", label: "Pack", num: true, money: true }, { k: "recon", label: "Recon", num: true, money: true }, ] }, { group: "Mechanical", fields: [ { k: "engine", label: "Engine" }, { k: "trans", label: "Transmission" }, { k: "drive", label: "Drivetrain" }, { k: "fuel", label: "Fuel" }, ] }, { group: "Lot", fields: [ { k: "status", label: "Status", options: ["In Stock", "Hold", "In Transit"] }, { k: "lot", label: "Lot / location" }, { k: "keyTag", label: "Key tag" }, ] }, { group: "Description & notes", fields: [ { k: "description", label: "Description", area: true }, { k: "options", label: "Options / equipment", area: true }, { k: "notes", label: "Internal notes", area: true }, ] }, ]; const invDraftFrom = (u) => { const d = {}; INV_EDIT_FIELDS.forEach(g => g.fields.forEach(f => { let v = u ? u[f.k] : ""; if (f.k === "type") v = invIsNew(u) ? "New" : "Used"; if (f.k === "color") v = (u && (u.color || u.extColor)) || ""; if (Array.isArray(v)) v = v.join(", "); d[f.k] = v == null ? "" : String(v); })); return d; }; const invNum = (s) => { const t = String(s == null ? "" : s).replace(/[$,\s]/g, ""); if (t === "") return 0; const n = parseFloat(t); return isNaN(n) ? NaN : n; }; const InvDetField = ({ f, value, orig, onChange, readOnly, onBlur }) => { const changed = String(value) !== String(orig); const base = { width: "100%", boxSizing: "border-box", border: "1px solid " + (changed ? "#C8862A" : "#9AA4B5"), borderRadius: 3, background: readOnly ? "#F2F2F2" : (changed ? "#FFF6DA" : "#FFFFFF"), color: "var(--k2b-navy)", padding: "3px 6px", fontFamily: "'Courier Prime', monospace", fontSize: 12.5, textAlign: f.num ? "right" : "left" }; let input; if (f.options) { const opts = f.options.includes(value) || !value ? f.options : [value, ...f.options]; input = ; } else if (f.area) { input =