// api.jsx — API client. Replaces the prototype's mock MC_DATA with real // calls to the FastAPI backend. Loaded before the dashboard/app. let _tokenGetter = null; async function _authHeaders() { if (!_tokenGetter) return {}; try { const t = await _tokenGetter(); return t ? { Authorization: `Bearer ${t}` } : {}; } catch { return {}; } } async function _json(method, url, body) { const opts = { method, headers: {} }; if (body !== undefined) { opts.headers["Content-Type"] = "application/json"; opts.body = JSON.stringify(body); } Object.assign(opts.headers, await _authHeaders()); const r = await fetch(url, opts); if (r.status === 401) throw { unauthorized: true }; if (!r.ok) throw new Error((await r.json().catch(() => ({}))).detail || r.statusText); return r.json(); } window.MC_API = { // Auth plumbing: Clerk session-token getter is injected by app.jsx. setTokenGetter: (fn) => { _tokenGetter = fn; }, config: () => _json("GET", "/api/config"), authStatus: () => _json("GET", "/api/auth"), login: (password) => _json("POST", "/api/login", { password }), bootstrap: () => _json("GET", "/api/bootstrap"), matter: (id) => _json("GET", `/api/matters/${id}`), quickSample: (sample) => _json("POST", "/api/quick-sample", { sample }), override: (id, patch) => _json("POST", `/api/matters/${id}/override`, patch), run: (id, apiKey) => _json("POST", `/api/matters/${id}/run`, { apiKey }), exportUrl: (id) => `/api/matters/${id}/export.xlsx`, // Bearer-auth-safe export: a plain can't carry the Authorization // header, so fetch the blob and trigger a client-side download. downloadExport: async (id, name) => { const r = await fetch(`/api/matters/${id}/export.xlsx`, { headers: await _authHeaders(), }); if (r.status === 401) throw { unauthorized: true }; if (!r.ok) throw new Error(r.statusText); const blob = await r.blob(); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${(name || "matter").replace(/[^A-Za-z0-9._-]+/g, "_")}_assessment.xlsx`; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 4000); }, createMatter: async (form) => { const r = await fetch("/api/matters", { method: "POST", body: form, headers: await _authHeaders(), }); if (r.status === 401) throw { unauthorized: true }; if (!r.ok) throw new Error((await r.json().catch(() => ({}))).detail || r.statusText); return r.json(); }, proposeMapping: async (file) => { const fd = new FormData(); fd.append("file", file); const r = await fetch("/api/intake/propose", { method: "POST", body: fd, headers: await _authHeaders(), }); if (r.status === 401) throw { unauthorized: true }; if (!r.ok) throw new Error((await r.json().catch(() => ({}))).detail || r.statusText); return r.json(); }, createFromMapping: async ({ file, name, mapping, params }) => { const fd = new FormData(); fd.append("file", file); fd.append("name", name); fd.append("mapping", JSON.stringify(mapping)); fd.append("params", JSON.stringify(params || {})); const r = await fetch("/api/matters/from-mapping", { method: "POST", body: fd, headers: await _authHeaders(), }); if (r.status === 401) throw { unauthorized: true }; if (!r.ok) throw new Error((await r.json().catch(() => ({}))).detail || r.statusText); return r.json(); }, };