/* © 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. */ /* eslint-disable */ // K2B DMS — the OWNER panel (RC22, lane "own"). // • Owner panel (route "ownerpanel", in the menu ONLY for the owner): confirm it is you (a fresh code), my sign-in codes (phone, // authenticator, backup codes), who must use sign-in codes (MFA policy), the users (phone, reset codes, one-time pass, sign out, // reset password, turn the login off / on, extra / removed rights), sign everyone out, recent owner actions. // • RC34 (hlp): the Helper card — the daily limit for the helper's AI answers, the model, AI answers per role, today's spend, the question log. // • Owner overrides (route "owneroverrides", the owner AND the office books): the report the accountant reads — printable / CSV. // • window.k2bOwnerOverride: the "Owner override" dialog DMSCore's apiFetch opens when the server offers the OWNER an override for a // refusal: a reason, a fresh code if needed, then a single-use token bound to that exact request and the same request again. // There is NO hidden back door: every power here needs a fresh code, is recorded, and the overrides are on a report others read. // The server decides everything (src/routes/owner.js, src/lib/owner.js, src/lib/mfa.js); nothing is kept in the browser. const ownApi = async (path, method, body) => { try { const d = await apiFetch("/owner" + path, { method: method || "GET", body, noOwnerOverride: true }); return { ok: true, data: d || {} }; } catch (e) { return { ok: false, status: e.status || 0, data: e.body || {}, code: e.body && e.body.code, error: (e.body && e.body.error) || e.message || "Server unreachable" }; } }; const ownFmt = (d) => d ? new Date(d).toLocaleString([], { month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit" }) : "—"; const ownMoney = (c) => (c === null || c === undefined) ? "" : "$" + (Number(c) / 100).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); const ownToast = (m, t) => window.k2bToast && window.k2bToast(m, t || "ok"); const OWN_CARD = { background: "#FFF", border: "1px solid #E2E5EA", borderRadius: 8, padding: "12px 14px" }; const OWN_TH = { textAlign: "left", fontSize: 11, color: "#666", fontWeight: 700, padding: "6px 8px", borderBottom: "1px solid #E2E5EA", whiteSpace: "nowrap" }; const OWN_TD = { fontSize: 12.5, padding: "6px 8px", borderBottom: "1px solid #F0F1F4", verticalAlign: "top" }; const OWN_INPUT = { padding: "6px 8px", border: "1px solid #c0c8d4", fontSize: 13, fontFamily: "var(--font-ui)" }; const OWN_METHOD_LABEL = { sms: "Text message", totp: "Authenticator app", backup: "Backup code" }; const OwnSection = ({ title, sub, right, children, tag }) => (
{title}
{right}
{sub &&
{sub}
} {children}
); /* ── confirm it is the owner: a fresh code (text message / authenticator / backup code) ── */ const OwnStepUp = ({ me, onDone, compact }) => { const methods = [me && me.sms ? "sms" : null, me && me.totp ? "totp" : null, me && me.backupLeft ? "backup" : null].filter(Boolean); const [method, setMethod] = React.useState(methods[0] || "sms"); const [code, setCode] = React.useState(""); const [note, setNote] = React.useState(""); const [err, setErr] = React.useState(""); const [busy, setBusy] = React.useState(false); if (!methods.length) return
Set up your sign-in codes first (below).
; const send = async () => { setErr(""); setNote("Sending…"); const r = await ownApi("/stepup/send", "POST", {}); if (r.ok) setNote("Code sent to " + (r.data.sentTo || "your phone") + "."); else { setNote(""); setErr(r.error); } }; const go = async () => { if (busy) return; setBusy(true); setErr(""); const r = await ownApi("/stepup", "POST", { method, code: code.trim() }); setBusy(false); if (!r.ok) { setErr(r.error); return; } setCode(""); setNote(""); onDone && onDone(r.data); }; return (
{!compact &&
Owner powers need a sign-in code from the last 10 minutes.
}
{methods.map(m => )}
{method === "sms" && } setCode(e.target.value.slice(0, 20))} onKeyDown={(e) => { if (e.key === "Enter") go(); }} placeholder={method === "backup" ? "xxxxx-xxxxx" : "6-digit code"} style={{ ...OWN_INPUT, width: 140 }} autoComplete="one-time-code" />
{note &&
{note}
} {err &&
⚠ {err}
}
); }; /* ══════════════════════════ the Owner override dialog (opened by DMSCore apiFetch) ══════════════════════════ */ const OwnOverrideDialog = ({ ctx, done }) => { const [refusal, setRefusal] = React.useState(ctx.refusal); const [rules, setRules] = React.useState([]); // rules already granted in this dialog (a command can hit two) const [reason, setReason] = React.useState(""); const [account, setAccount] = React.useState((ctx.refusal.ownerOverride && ctx.refusal.ownerOverride.hint && ctx.refusal.ownerOverride.hint.suggest) || ""); const [status, setStatus] = React.useState(null); const [fresh, setFresh] = React.useState(!!(ctx.refusal.ownerOverride && ctx.refusal.ownerOverride.stepUpFresh)); const [busy, setBusy] = React.useState(false); const [err, setErr] = React.useState(""); const ov = refusal.ownerOverride || {}; const needAcct = [ov.rule].concat(rules).indexOf("deal.cash_balance_due") !== -1 || (Array.isArray(ov.needs) && ov.needs.indexOf("account") !== -1); // RC23 (corr): + deal.held_account const heldAcctRule = ov.rule === "deal.held_account"; React.useEffect(() => { (async () => { const r = await ownApi("/status"); if (r.ok) { setStatus(r.data); if (r.data.stepUp && r.data.stepUp.fresh) setFresh(true); } })(); }, []); const cancel = () => done({ ok: false, err: ctx.err }); const go = async () => { if (busy) return; if (reason.trim().length < 5) { setErr("Type the reason (a few words) — it goes on the Owner overrides report."); return; } if (needAcct && !account.trim()) { setErr(heldAcctRule ? "Pick the liability account (from the chart of accounts) the customer's money is held in." : "Pick the receivable account from the chart of accounts."); return; } setBusy(true); setErr(""); const all = rules.concat([ov.rule]).filter((x, i, a) => x && a.indexOf(x) === i); const hadKey = ctx.headers && ctx.headers["Idempotency-Key"]; const newKey = hadKey ? (window.DMS && window.DMS._newIdemKey ? window.DMS._newIdemKey("ov") : "ov-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10)) : ""; const t = await ownApi("/override-token", "POST", { method: "POST", path: "/api" + ctx.path, idemKey: newKey || undefined, body: ctx.reqBody, rules: all, reason: reason.trim(), params: needAcct ? { account: account.trim() } : undefined }); if (!t.ok) { setBusy(false); if (t.code === "stepup_required" || t.code === "owner_enroll_required") setFresh(false); setErr(t.error); return; } const headers = Object.assign({}, ctx.headers || {}, { "X-K2B-Override": t.data.token }); if (newKey) headers["Idempotency-Key"] = newKey; try { const res = await apiFetch(ctx.path, { method: "POST", body: ctx.reqBody, headers, noOwnerOverride: true }); setBusy(false); ownToast("Owner override recorded — " + all.length + " rule" + (all.length > 1 ? "s" : "") + " set aside. It is on the Owner overrides report.", "ok"); done({ ok: true, result: res }); } catch (e) { setBusy(false); const b = e && e.body; if (b && b.ownerOverride && all.indexOf(b.ownerOverride.rule) === -1) { // a second rule of the same command setRules(all); setRefusal(b); setErr(""); return; } done({ ok: false, err: e }); } }; return ( Cancel — keep the refusal{busy ? "Working…" : "Override and send"}}>
⚠ {refusal.error}
Owner override: {ov.label}{rules.length ? <> (also: {rules.length} rule{rules.length > 1 ? "s" : ""} already chosen) : null}
Only you (the owner) see this. The books still have to balance; the override, your reason and the amount go on the Owner overrides report the office and your accountant read, and into the audit log. The pass works once, for this exact request, for 5 minutes.
{!fresh &&
setFresh(true)} />
}
Reason (required)