/* =============================================================
   CoreIQ Office — Receiving, LIVE.
   The goods-in worklist over real Aurora data: every supplier invoice
   whose stock has not fully landed, with the exception lines FLAGGED
   inline (missing barcode → scan it right here; post-invoice count →
   explained) instead of buried in a notes string. The PharmX poller
   auto-receives clean invoices; this screen exists for the residue
   that genuinely needs a human.

   Loaded AFTER office-screens-receiving.jsx, so this registration
   REPLACES the demo Receiving prototype under the same screen key.
   Uses shared bare globals: OIcon, Screen, StatusPill, window.KpiCard,
   window.useOffice, window.OfficeAPI.
   ============================================================= */
const { useState: rlvUseState, useEffect: rlvUseEffect, useCallback: rlvUseCallback } = React;

const rlvMoney = (c) => "$" + (Number(c || 0) / 100).toLocaleString("en-AU", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const rlvNum = (v) => Number(v || 0).toLocaleString("en-AU");
const rlvDate = (s) => (s ? String(s).slice(0, 10) : "—");

// ---- One invoice line inside the expanded panel ------------------
// The flag IS the UI: amber lines carry the fix inline.
function RlvPlanLine({ l, invoiceId, onFixed, pushToast }) {
  const [code, setCode] = rlvUseState("");
  const [busy, setBusy] = rlvUseState(false);
  const cols = "minmax(220px, 2fr) 110px 60px 90px minmax(230px, 1.6fr)";

  function saveBarcode() {
    let digits = code.replace(/\D/g, "");
    // A carton scan (GS1-128, AI(01)+GTIN-14) arrives as 16+ digits — take
    // the GTIN rather than rejecting the scan.
    if (digits.length >= 16 && digits.slice(0, 2) === "01") digits = digits.slice(2, 16);
    if (digits.length < 8 || digits.length > 14) {
      // Say what we actually saw — the operator can't fix "not a barcode".
      const why =
        digits.length === 0 ? "That scan had no digits — scan the barcode on the pack or carton" :
        digits.length >= 18 && digits.slice(0, 2) === "00" ? "That's a pallet SSCC label (AI 00) — scan the barcode on the pack or carton instead" :
        "Got " + digits.length + " digits (" + digits + ") — a product code is 8, 12, 13 or 14 digits; a carton code starting (01) also works";
      pushToast({ kind: "hold", icon: "alert", title: "That scan isn't a product barcode", meta: why });
      return;
    }
    if (busy) return;
    setBusy(true);
    window.OfficeAPI.setInvoiceLineBarcode(l.lineId, digits)
      .then(() => window.OfficeAPI.receiveSupplierInvoice(invoiceId))
      .then((r) => {
        setBusy(false);
        pushToast({ kind: "paid", icon: "check", title: "Line ranged & received", meta: l.name });
        onFixed(r);
      })
      .catch((e) => { setBusy(false); pushToast({ kind: "hold", icon: "alert", title: "Couldn't fix line", meta: String((e && e.message) || e) }); });
  }

  const flag =
    l.action === "received" ? <StatusPill kind="paid" label="On ledger"/> :
    l.action === "ready" ? <StatusPill kind="brand" label={"Will receive " + rlvNum(l.deltaUnits ?? l.quantity) + " units"}/> :
    l.action === "no-stock" ? <StatusPill kind="neutral" label="Charge only"/> :
    l.action === "counted-after" ? <StatusPill kind="hold" label="Counted after invoice"/> :
    <StatusPill kind="hold" label="Needs barcode"/>;

  return (
    <div>
      <div className="sup-grid-row sup-grid-row--body" style={{ gridTemplateColumns: cols }}>
        <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={l.name}>{l.name}</span>
        <span className="sup-grid-mono sup-grid-muted">{l.supplierCode || "—"}</span>
        <span className="sup-grid-num">{l.quantity}</span>
        <span className="sup-grid-num sup-grid-muted">{rlvMoney(l.lineTotalCents)}</span>
        <span style={{ display: "flex", gap: 8, alignItems: "center", justifyContent: "flex-end", flexWrap: "wrap" }}>
          {flag}
          {l.action === "needs-barcode" && (
            <span style={{ display: "inline-flex", gap: 6 }}>
              <input
                className="input"
                style={{ width: 160, height: 30, fontFamily: "var(--font-mono)", fontSize: "var(--fs-12)" }}
                placeholder="Scan barcode…"
                value={code}
                inputMode="numeric"
                onChange={(e) => setCode(e.target.value)}
                onKeyDown={(e) => { if (e.key === "Enter") saveBarcode(); }}
              />
              <button className="btn btn--primary btn--sm" disabled={busy || !code.trim()} onClick={saveBarcode}>
                {busy ? "Saving…" : "Save & receive"}
              </button>
            </span>
          )}
        </span>
      </div>
      {(l.action === "needs-barcode" || l.action === "counted-after") && l.reason && (
        <div style={{ display: "flex", alignItems: "center", gap: 6, padding: "0 14px 8px", fontSize: "var(--fs-12)", color: "var(--hold-text)", fontWeight: 500 }}>
          <OIcon name="alert" width={13} height={13} style={{ flexShrink: 0 }}/>
          {l.reason}
        </div>
      )}
    </div>
  );
}

// ---- One invoice row in the worklist ------------------------------
function RlvInvoiceRow({ v, onChanged, pushToast }) {
  const [open, setOpen] = rlvUseState(false);
  const [plan, setPlan] = rlvUseState(null);
  const [busy, setBusy] = rlvUseState(false);
  const cols = "140px minmax(180px, 1.4fr) 110px 70px 90px 110px 120px 120px";

  function loadPlan() {
    window.OfficeAPI.receivePlan(v.id).then(setPlan).catch(() => setPlan({ lines: [], error: true }));
  }
  function toggle() {
    const next = !open;
    setOpen(next);
    if (next && plan == null) loadPlan();
  }
  function receive(e) {
    e.stopPropagation();
    if (busy) return;
    setBusy(true);
    window.OfficeAPI.receiveSupplierInvoice(v.id)
      .then((r) => {
        setBusy(false);
        if (r.status === "received") pushToast({ kind: "paid", icon: "check", title: "Stock received", meta: r.received + " line" + (r.received === 1 ? "" : "s") + " · invoice " + v.number });
        else pushToast({ kind: "hold", icon: "alert", title: "Received, with items to check",
                         meta: r.received + " to stock · " + r.skipped + " parked in the Barcode queue" });
        loadPlan();
        if (!open) setOpen(true);
        onChanged();
      })
      .catch((e2) => { setBusy(false); pushToast({ kind: "hold", icon: "alert", title: "Couldn't receive", meta: String((e2 && e2.message) || e2) }); });
  }
  function onLineFixed() {
    loadPlan();
    onChanged();
  }

  const lineCols = "minmax(220px, 2fr) 110px 60px 90px minmax(230px, 1.6fr)";
  return (
    <div>
      <div className="sup-grid-row sup-grid-row--body" style={{ gridTemplateColumns: cols, cursor: "pointer" }} onClick={toggle}>
        <span className="sup-grid-mono sup-grid-strong">{v.number}</span>
        <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
          {v.supplierName || "—"}
          {v.sourceType === "pharmx" && <span className="badge badge--neutral" style={{ marginLeft: 6 }} title="Delivered electronically via PharmX">PharmX</span>}
        </span>
        <span className="sup-grid-muted">{rlvDate(v.date)}</span>
        <span className="sup-grid-num">{v.lineCount}</span>
        <span className="sup-grid-num">{v.unreceivedLines > 0 ? <strong>{v.unreceivedLines}</strong> : <span className="sup-grid-muted">0</span>}</span>
        <span className="sup-grid-num sup-grid-strong">{rlvMoney(v.totalCents)}</span>
        <span>
          {v.status === "review" ? <StatusPill kind="hold" label="Needs attention"/> :
           v.status === "pending" ? <StatusPill kind="neutral" label="Pending"/> :
           <StatusPill kind="paid" label="Received"/>}
        </span>
        <span style={{ display: "flex", gap: 6, justifyContent: "flex-end" }}>
          {(v.status === "pending" || v.status === "review") &&
            <button className="btn btn--primary btn--sm" onClick={receive} disabled={busy}>{busy ? "Receiving…" : "Receive"}</button>}
          <button className="btn btn--ghost btn--sm">{open ? "Hide" : "Lines"}</button>
        </span>
      </div>
      {open && (
        <div style={{ padding: "4px 14px 12px", background: "var(--surface-sunken, transparent)" }}>
          {plan == null && <div style={{ padding: 12, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>Loading…</div>}
          {plan && plan.error && <div style={{ padding: 12, fontSize: "var(--fs-13)", color: "var(--void-text)" }}>Couldn't load the line plan.</div>}
          {plan && !plan.error && (
            <div className="sup-grid">
              <div className="sup-grid-row sup-grid-row--head" style={{ gridTemplateColumns: lineCols }}>
                <span>Product</span><span>Reorder code</span><span className="sup-grid-num">Qty</span><span className="sup-grid-num">Line ex GST</span><span style={{ textAlign: "right" }}>Stock status</span>
              </div>
              {plan.lines.map((l) => (
                <RlvPlanLine key={l.lineId} l={l} invoiceId={v.id} onFixed={onLineFixed} pushToast={pushToast}/>
              ))}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ---- The worklist screen ------------------------------------------

// MANUAL INVOICE ENTRY — the store keys a direct (non-PharmX) supplier's
// invoice (Kirsty's ask 2026-08-04). Scan a barcode to autofill known
// products; unknown lines are typed and get ranged at Receive time via the
// normal scan flow. The printed grand total is entered from the paper — if
// the keyed lines disagree by >5c the invoice lands in "Needs attention"
// with the delta named, never silently absorbed.
function RlvEnterInvoice({ onClose, onCreated, pushToast }) {
  const emptyLine = () => ({ barcode: "", name: "", quantity: "1", cost: "", gstFree: false, known: null });
  const [suppliers, setSuppliers] = rlvUseState([]);
  const [supplierId, setSupplierId] = rlvUseState("");
  const [number, setNumber] = rlvUseState("");
  const [date, setDate] = rlvUseState(new Date().toISOString().slice(0, 10));
  const [total, setTotal] = rlvUseState("");
  const [lines, setLines] = rlvUseState([emptyLine()]);
  const [busy, setBusy] = rlvUseState(false);
  const [saveErr, setSaveErr] = rlvUseState(null);
  rlvUseEffect(() => {
    let alive = true;
    window.OfficeAPI.listSuppliers().then((ss) => { if (alive) setSuppliers(ss || []); }).catch(() => {});
    return () => { alive = false; };
  }, []);
  function setLine(i, patch) {
    setLines((ls) => ls.map((l, j) => (j === i ? Object.assign({}, l, patch) : l)));
  }
  function onScan(i, code) {
    const digits = String(code || "").replace(/\D/g, "");
    if (digits.length < 8) return;
    window.OfficeAPI.productByBarcode(digits)
      .then((prod) => setLine(i, {
        name: prod.name,
        known: prod.id,
        cost: (function (l) { return l.cost !== "" ? l.cost : prod.costPriceCents != null ? (prod.costPriceCents / 100).toFixed(2) : ""; })(lines[i]),
      }))
      .catch(() => setLine(i, { known: null }));
  }
  function addRowIfLast(i) { if (i === lines.length - 1) setLines((ls) => ls.concat([emptyLine()])); }
  const cents = (v) => Math.round(Number(v || 0) * 100);
  const real = lines.filter((l) => l.name.trim() !== "");
  const keyedSub = real.reduce((s2, l) => s2 + cents(l.cost) * (parseInt(l.quantity, 10) || 0), 0);
  const keyedTax = real.reduce((s2, l) => s2 + (l.gstFree ? 0 : Math.round(cents(l.cost) * (parseInt(l.quantity, 10) || 0) * 0.1)), 0);
  const keyed = keyedSub + keyedTax;
  const printed = cents(total);
  const delta = printed - keyed;
  const missing = [];
  if (!supplierId) missing.push("supplier");
  if (!number.trim()) missing.push("invoice number");
  if (total === "") missing.push("invoice total");
  if (real.length === 0) missing.push("at least one line with a description");
  else if (!real.every((l) => l.cost !== "")) missing.push("a unit cost on every line");
  else if (!real.every((l) => (parseInt(l.quantity, 10) || 0) > 0)) missing.push("a quantity on every line");
  const canSave = missing.length === 0 && !busy;
  function save() {
    if (!canSave) return;
    setBusy(true);
    window.OfficeAPI.createSupplierInvoice({
      supplierId,
      invoiceNumber: number.trim(),
      invoiceDate: date,
      totalIncGstCents: printed,
      lines: real.map((l) => ({
        name: l.name.trim(),
        barcode: l.barcode.trim() || null,
        quantity: parseInt(l.quantity, 10),
        unitCostExGstCents: cents(l.cost),
        gstFree: !!l.gstFree,
      })),
    }).then((r) => {
      pushToast(r.status === "review"
        ? { kind: "hold", icon: "alert", title: "Invoice saved — needs attention", meta: "Keyed lines differ from the printed total by $" + Math.abs(r.deltaCents / 100).toFixed(2) }
        : { kind: "paid", icon: "check-circle", title: "Invoice " + number.trim() + " saved", meta: "In Pending — hit Receive to land the stock" });
      onCreated();
    }).catch((e) => {
      setBusy(false);
      // The error belongs ON the form she is looking at. A toast alone left
      // the modal sitting there unchanged, which reads as "nothing happened".
      setSaveErr(String((e && e.message) || e));
      pushToast({ kind: "hold", icon: "alert", title: "Couldn't save invoice", meta: String((e && e.message) || e) });
    });
  }
  const lineCols = "150px minmax(200px, 2fr) 70px 100px 60px 90px";
  return (
    <div className="pd-modal-back" onClick={onClose}>
      <div className="pd-modal" style={{ width: 860, maxWidth: "96vw", maxHeight: "92vh", overflow: "auto" }} onClick={(e) => e.stopPropagation()}>
        <header className="pd-modal-head">
          <h3>Enter a supplier invoice</h3>
          <button className="btn btn--ghost btn--sm" onClick={onClose}><OIcon name="x"/></button>
        </header>
        <div className="pd-modal-body" style={{ gap: 12 }}>
          <div style={{ display: "grid", gridTemplateColumns: "2fr 1.2fr 1fr 1fr", gap: 10 }}>
            <div className="field">
              <label className="field-label">Supplier <span className="field-required">*</span></label>
              <select className="input select" autoFocus value={supplierId} onChange={(e) => setSupplierId(e.target.value)}>
                <option value="">Choose…</option>
                {suppliers.map((sp) => <option key={sp.id} value={sp.id}>{sp.name}</option>)}
              </select>
            </div>
            <div className="field">
              <label className="field-label">Invoice # <span className="field-required">*</span></label>
              <input className="input" value={number} onChange={(e) => setNumber(e.target.value)} placeholder="As printed"/>
            </div>
            <div className="field">
              <label className="field-label">Date</label>
              <input type="date" className="input" value={date} onChange={(e) => setDate(e.target.value)}/>
            </div>
            <div className="field">
              <label className="field-label">Invoice total (inc GST) <span className="field-required">*</span></label>
              <input className="input" inputMode="decimal" value={total} onChange={(e) => setTotal(e.target.value)} placeholder="0.00"/>
            </div>
          </div>
          <div>
            <div className="sup-grid-row sup-grid-row--head" style={{ gridTemplateColumns: lineCols, padding: "4px 0" }}>
              <span>Barcode (scan)</span><span>Product</span><span className="sup-grid-num">Qty</span>
              <span className="sup-grid-num">Cost ex GST</span><span>GST-free</span><span className="sup-grid-num">Line total</span>
            </div>
            {lines.map((l, i) => (
              <div key={i} className="sup-grid-row" style={{ gridTemplateColumns: lineCols, padding: "3px 0", alignItems: "center", display: "grid", gap: 8 }}>
                <input className="input" style={{ height: 30, fontFamily: "var(--font-mono)", fontSize: "var(--fs-12)" }}
                  value={l.barcode} placeholder="Scan…"
                  onChange={(e) => setLine(i, { barcode: e.target.value })}
                  onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); onScan(i, l.barcode); addRowIfLast(i); } }}
                  onBlur={() => l.barcode && !l.name && onScan(i, l.barcode)}/>
                <input className="input" style={{ height: 30 }} value={l.name}
                  placeholder={l.known ? "" : "Type the product name as printed"}
                  onChange={(e) => setLine(i, { name: e.target.value, known: null })}
                  onFocus={() => addRowIfLast(i)}/>
                <input className="input sup-grid-num" style={{ height: 30, textAlign: "right" }} inputMode="numeric" value={l.quantity}
                  onChange={(e) => setLine(i, { quantity: e.target.value })}/>
                <input className="input" style={{ height: 30, textAlign: "right" }} inputMode="decimal" value={l.cost} placeholder="0.00"
                  onChange={(e) => setLine(i, { cost: e.target.value })}/>
                <input type="checkbox" checked={l.gstFree} onChange={(e) => setLine(i, { gstFree: e.target.checked })}/>
                <span className="sup-grid-num" style={{ fontSize: "var(--fs-12)" }}>
                  {l.cost === "" ? "" : rlvMoney(cents(l.cost) * (parseInt(l.quantity, 10) || 0))}
                </span>
              </div>
            ))}
          </div>
          <div style={{ display: "flex", gap: 16, justifyContent: "flex-end", fontSize: "var(--fs-13)", alignItems: "center" }}>
            <span>Keyed: <b>{rlvMoney(keyed)}</b> inc GST</span>
            <span>Printed: <b>{total === "" ? "—" : rlvMoney(printed)}</b></span>
            {total !== "" && (
              <span style={Math.abs(delta) > 5 ? { color: "var(--hold-text)", fontWeight: 600 } : { color: "var(--paid-text)", fontWeight: 600 }}>
                {Math.abs(delta) <= 5 ? "✓ reconciles" : "off by " + rlvMoney(Math.abs(delta))}
              </span>
            )}
          </div>
          {saveErr && (
            <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 12px",
                          border: "1px solid var(--void)", borderRadius: "var(--radius-md)",
                          background: "var(--void-bg)", fontSize: "var(--fs-13)" }}>
              <OIcon name="alert" style={{ color: "var(--void-text)", flexShrink: 0 }}/>
              <div style={{ flex: 1 }}><b style={{ color: "var(--void-text)" }}>Not saved.</b> {saveErr}</div>
            </div>
          )}
        </div>
        <footer className="pd-modal-foot">
          <span style={{ marginRight: "auto", fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>
            {/* A disabled button with no reason is the other half of "nothing
                happened" — name what is still missing. */}
            {missing.length > 0
              ? "Still needed: " + missing.join(", ")
              : "Unknown products get set up when you Receive — scan them there."}
          </span>
          <button className="btn btn--ghost btn--sm" onClick={onClose}>Cancel</button>
          <button className="btn btn--primary btn--sm" disabled={!canSave} onClick={save}>
            {busy ? <span className="spinner"/> : <OIcon name="check"/>} Save invoice
          </button>
        </footer>
      </div>
    </div>
  );
}

// Choice shown by "Enter invoice" — key it by hand (the existing modal below)
// or upload a PDF/photo for AI parse + confirm (Kirsty's ask 2026-08-21).
function RlvEntryChoice({ onClose, onKeyByHand, onUpload }) {
  return (
    <div className="pd-modal-back" onClick={onClose}>
      <div className="pd-modal" style={{ width: 440 }} onClick={(e) => e.stopPropagation()}>
        <header className="pd-modal-head">
          <h3>Enter a supplier invoice</h3>
          <button className="btn btn--ghost btn--sm" onClick={onClose}><OIcon name="x"/></button>
        </header>
        <div className="pd-modal-body" style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          <button className="btn btn--primary" style={{ justifyContent: "flex-start", padding: "14px 16px", height: "auto" }} onClick={onUpload}>
            <OIcon name="doc"/>
            <span style={{ textAlign: "left" }}>
              <span style={{ display: "block", fontWeight: 600 }}>Upload a PDF or photo</span>
              <span style={{ display: "block", fontSize: 12, fontWeight: 400, opacity: 0.85 }}>Parsed automatically — you confirm before anything posts</span>
            </span>
          </button>
          <button className="btn btn--ghost" style={{ justifyContent: "flex-start", padding: "14px 16px", height: "auto" }} onClick={onKeyByHand}>
            <OIcon name="edit"/>
            <span style={{ textAlign: "left" }}>
              <span style={{ display: "block", fontWeight: 600 }}>Key it by hand</span>
              <span style={{ display: "block", fontSize: 12, fontWeight: 400, color: "var(--text-subtle)" }}>Type the supplier, lines and total yourself</span>
            </span>
          </button>
        </div>
      </div>
    </div>
  );
}

function ReceivingLive() {
  const { pushToast, setScreen } = window.useOffice();
  const [data, setData] = rlvUseState(null);
  const [err, setErr] = rlvUseState(null);
  const [tab, setTab] = rlvUseState("attention");
  const [entering, setEntering] = rlvUseState(false);
  const [choosingEntry, setChoosingEntry] = rlvUseState(false);

  const load = rlvUseCallback(() => {
    window.OfficeAPI.receivingWorklist()
      .then((d) => { setData(d); setErr(null); })
      .catch((e) => setErr(String((e && e.message) || e)));
  }, []);
  rlvUseEffect(() => { load(); }, [load]);

  const s = (data && data.summary) || {};
  const all = (data && data.invoices) || [];
  const FILTERS = [
    { k: "attention", l: "Needs attention", pred: (v) => v.status === "review", badge: s.needsAttention || null },
    { k: "pending", l: "Pending", pred: (v) => v.status === "pending" },
    { k: "received", l: "Received", pred: (v) => v.status === "received" },
    { k: "all", l: "All", pred: () => true },
  ];
  const shown = all.filter(FILTERS.find((f) => f.k === tab).pred);
  const cols = "140px minmax(180px, 1.4fr) 110px 70px 90px 110px 120px 120px";

  return (
    <Screen
      eyebrow="Stock · goods-in · live from Aurora"
      title="Receiving"
      subtabs={FILTERS.map((f) => ({ k: f.k, l: f.l, badge: f.badge }))}
      activeTab={tab}
      onTab={setTab}
      actions={
        <span style={{ display: "flex", gap: 8 }}>
          <button className="btn btn--primary btn--sm" onClick={() => setChoosingEntry(true)}><OIcon name="plus"/> Enter invoice</button>
          <button className="btn btn--ghost btn--sm" onClick={load}>Refresh</button>
        </span>
      }
    >
      {choosingEntry && (
        <RlvEntryChoice
          onClose={() => setChoosingEntry(false)}
          onKeyByHand={() => { setChoosingEntry(false); setEntering(true); }}
          onUpload={() => { setChoosingEntry(false); setScreen("invoiceCaptureUpload"); }}
        />
      )}
      {entering && (
        <RlvEnterInvoice
          pushToast={pushToast}
          onClose={() => setEntering(false)}
          onCreated={() => { setEntering(false); setTab("pending"); load(); }}
        />
      )}
      {err && <div style={{ padding: 12, fontSize: "var(--fs-13)", color: "var(--void-text)" }}>Couldn't load: {err}</div>}
      <div className="off-kpi-grid" style={{ marginBottom: 16 }}>
        <window.KpiCard label="Invoices to receive" value={rlvNum(s.toReceive)} delta={{ text: (s.needsAttention || 0) + " need attention" }}/>
        <window.KpiCard label="Lines waiting" value={rlvNum(s.linesWaiting)} delta={{ text: "not yet on the stock ledger" }}/>
        <window.KpiCard label="Units inbound" value={rlvNum(s.unitsInbound)} delta={{ text: "invoice units" }}/>
        <window.KpiCard label="Value inbound" value={rlvMoney(s.valueInboundCents)} delta={{ text: "ex GST, unreceived lines" }}/>
      </div>
      <div className="sup-sec">
        <div className="sup-sec-head">
          <span className="sup-sec-title">Supplier invoices</span>
          {data && <span className="sup-tab-count" style={{ marginLeft: 8 }}>{shown.length}</span>}
        </div>
        {data == null && !err && <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>Loading…</div>}
        {data && shown.length === 0 && (
          <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>
            {tab === "attention" ? "Nothing needs attention — the pipeline is receiving everything by itself." : "Nothing here."}
          </div>
        )}
        {data && shown.length > 0 && (
          <div className="sup-grid">
            <div className="sup-grid-row sup-grid-row--head" style={{ gridTemplateColumns: cols }}>
              <span>Invoice #</span><span>Supplier</span><span>Date</span>
              <span className="sup-grid-num">Lines</span><span className="sup-grid-num">Waiting</span>
              <span className="sup-grid-num">Total</span><span>Status</span><span/>
            </div>
            {shown.map((v) => <RlvInvoiceRow key={v.id} v={v} onChanged={load} pushToast={pushToast}/>)}
          </div>
        )}
      </div>
    </Screen>
  );
}

// Replaces the demo prototype registered by office-screens-receiving.jsx —
// this file loads after it, so the same key now points at the live screen.
// -----------------------------------------------------------------
// BARCODE QUEUE — the parking lot for receiving exceptions.
// Receiving never stops for an item it can't identify any more: the line is
// ranged from the invoice itself and the item lands here, so one person can
// work the queue with a scanner across every invoice at once instead of the
// store being blocked at the counter (store request 2026-08-12). They sell
// fine meanwhile — the till finds products by name — a barcode is only speed.
// -----------------------------------------------------------------
function BarcodeQueueScreen() {
  const { pushToast } = window.useOffice();
  const [rows, setRows] = rlvUseState(null);
  const [err, setErr] = rlvUseState(null);
  const [codes, setCodes] = rlvUseState({});
  const [busy, setBusy] = rlvUseState(null);
  const load = () => window.OfficeAPI.listBarcodeQueue()
    .then((d) => { setRows(d || []); setErr(null); })
    .catch((e) => setErr(String((e && e.message) || e)));
  rlvUseEffect(() => { load(); }, []);

  function attach(p) {
    const digits = String(codes[p.id] || "").replace(/[^0-9]/g, "");
    if (digits.length < 8) {
      pushToast({ kind: "hold", icon: "alert", title: "That doesn't look like a product barcode" });
      return;
    }
    setBusy(p.id);
    window.OfficeAPI.addBarcode(p.id, { value: digits, units: 1, isPrimary: true })
      .then(() => {
        setBusy(null);
        setCodes((c) => Object.assign({}, c, { [p.id]: "" }));
        pushToast({ kind: "paid", icon: "check", title: "Barcode added", meta: p.name + " — it scans at the till now" });
        return load();
      })
      .catch((e) => { setBusy(null); pushToast({ kind: "hold", icon: "alert", title: "Couldn't add it", meta: String((e && e.message) || e) }); });
  }

  const cols = "minmax(220px, 2fr) 150px 80px 110px 240px";
  return (
    <Screen eyebrow="Buying · live from Aurora" title="Barcode queue">
      {err && <div style={{ padding: 12, fontSize: "var(--fs-13)", color: "var(--void-text)" }}>Couldn't load: {err}</div>}
      {!err && rows == null && <div style={{ padding: 12, color: "var(--text-subtle)" }}>Loading…</div>}
      {!err && rows && rows.length === 0 && (
        <div className="off-card" style={{ padding: 20, color: "var(--text-subtle)" }}>
          Nothing waiting. Items land here when an invoice is received and the wholesaler sent no
          usable barcode — they sell by name in the meantime.
        </div>
      )}
      {!err && rows && rows.length > 0 && (
        <div className="off-card" style={{ padding: 0 }}>
          <div className="sup-grid-row sup-grid-row--head" style={{ gridTemplateColumns: cols }}>
            <span>Item</span><span>Supplier</span><span className="sup-grid-num">On hand</span>
            <span className="sup-grid-num">Last invoiced</span><span>Scan the pack</span>
          </div>
          {rows.map((r) => (
            <div key={r.id} className="sup-grid-row sup-grid-row--body" style={{ gridTemplateColumns: cols }}>
              <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={r.name}>{r.name}</span>
              <span className="sup-grid-muted">{r.supplierName || "—"}</span>
              <span className="sup-grid-num">{rlvNum(r.onHand)}</span>
              <span className="sup-grid-num sup-grid-muted">{r.lastInvoiced || "—"}</span>
              <span style={{ display: "flex", gap: 6 }}>
                <input className="input" style={{ width: 145, height: 30, fontFamily: "var(--font-mono)", fontSize: "var(--fs-12)" }}
                  placeholder="Scan…" inputMode="numeric" value={codes[r.id] || ""}
                  onChange={(e) => setCodes((c) => Object.assign({}, c, { [r.id]: e.target.value }))}
                  onKeyDown={(e) => { if (e.key === "Enter") attach(r); }}/>
                <button className="btn btn--primary btn--sm" disabled={busy === r.id || !(codes[r.id] || "").trim()}
                  onClick={() => attach(r)}>{busy === r.id ? "Saving…" : "Add"}</button>
              </span>
            </div>
          ))}
        </div>
      )}
    </Screen>
  );
}

// Replaces the demo prototype registered by office-screens-receiving.jsx —
// this file loads after it, so the same key now points at the live screen.
window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, {
  receiving: ReceivingLive,
  barcodeQueue: BarcodeQueueScreen,
});
