/** SmOS booking + payments API (same origin on uvicorn; falls back to :7860). */ window.SmOS_API = (function () { const FALLBACK = "http://127.0.0.1:7860"; const MODULE_ID = (window.SmOS_Module && window.SmOS_Module.id) || "SmOS_CC"; function bases() { const list = []; if (location.origin && !location.origin.startsWith("file:")) list.push(location.origin); if (!list.includes(FALLBACK)) list.push(FALLBACK); return list; } async function postBooking(payload) { const body = { module_id: MODULE_ID, ...payload }; let lastErr = null; for (const base of bases()) { try { const res = await fetch(`${base}/api/bookings`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (res.ok) return await res.json(); const err = await res.json().catch(() => ({})); const detail = err.detail; const msg = Array.isArray(detail) ? detail.map((d) => d.msg || d).join("; ") : detail || `HTTP ${res.status}`; lastErr = new Error(msg); } catch (e) { lastErr = e; } } throw lastErr || new Error("Booking API unavailable"); } async function createMolliePayment(payload) { const body = { module_id: MODULE_ID, ...payload }; let lastErr = null; for (const base of bases()) { try { const res = await fetch(`${base}/api/modules/SmOS_CC/mollie/create`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const data = await res.json().catch(() => ({})); if (res.ok) return data; const detail = data.detail; const msg = Array.isArray(detail) ? detail.map((d) => d.msg || d).join("; ") : detail || `HTTP ${res.status}`; lastErr = new Error(msg); if (res.status === 400 || res.status === 503) break; } catch (e) { lastErr = e; } } throw lastErr || new Error("Mollie checkout unavailable"); } async function mollieStatus(paymentId) { let lastErr = null; const q = `?payment_id=${encodeURIComponent(paymentId || "")}`; for (const base of bases()) { try { const res = await fetch(`${base}/api/modules/SmOS_CC/mollie/status${q}`, { cache: "no-store", }); const data = await res.json().catch(() => ({})); if (res.ok) return data; const detail = data.detail; const msg = Array.isArray(detail) ? detail.map((d) => d.msg || d).join("; ") : detail || `HTTP ${res.status}`; lastErr = new Error(msg); } catch (e) { lastErr = e; } } throw lastErr || new Error("Mollie status unavailable"); } async function occupiedTables(date) { let lastErr = null; const q = date ? `?date=${encodeURIComponent(date)}` : ""; for (const base of bases()) { try { const res = await fetch(`${base}/api/modules/${MODULE_ID}/tables/occupied${q}`); if (res.ok) { const data = await res.json(); return Array.isArray(data.occupied) ? data.occupied.map(String) : []; } lastErr = new Error(`HTTP ${res.status}`); } catch (e) { lastErr = e; } } if (lastErr) console.warn("occupiedTables", lastErr); return []; } async function lookupAddresses(postcode) { let lastErr = null; const q = `?postcode=${encodeURIComponent(postcode || "")}`; for (const base of bases()) { try { const res = await fetch(`${base}/api/modules/${MODULE_ID}/addresses${q}`); const data = await res.json().catch(() => ({})); if (res.ok) return data; const detail = data.detail; const msg = Array.isArray(detail) ? detail.map((d) => d.msg || d).join("; ") : detail || `HTTP ${res.status}`; lastErr = new Error(msg); if (res.status === 400 || res.status === 404 || res.status === 503) break; } catch (e) { lastErr = e; } } throw lastErr || new Error("Address lookup unavailable"); } return { postBooking, createMolliePayment, mollieStatus, occupiedTables, lookupAddresses, bases, moduleId: MODULE_ID, }; })();