import { endpoints } from "./config.js"; import { showToast } from "./toast.js"; import { getBillState, getCachedOpenBills, getGuestPendingActions, getHashParam, getTableParam, setCachedOpenBills, setGuestPendingAction, } from "./state.js"; let guestPendingPollTimer = null; const GUEST_PENDING_POLL_MS = 15000; export function cacheOpenBills(bills) { setCachedOpenBills(bills); } function getQueueOperatorFields() { const bills = getCachedOpenBills(); if (!bills.length) { return { otwierajacyImie: "", otwierajacyNazwisko: "" }; } let bill = bills[0]; const billState = getBillState(); if (billState.selectedBillId) { const selected = bills.find((b) => b.id === billState.selectedBillId); if (selected) bill = selected; } const o = bill.otwierajacy || {}; return { otwierajacyImie: String(o.imie || "").trim(), otwierajacyNazwisko: String(o.nazwisko || "").trim(), }; } export async function prefetchOpenBills() { const hashParam = getHashParam(); if (!hashParam) return; try { const res = await fetch(`${endpoints.bills}?h=${encodeURIComponent(hashParam)}`); const result = await res.json(); if (result.status === "success") { cacheOpenBills(result.data); } } catch { // best effort } } /** * Komunikat do kolejki KDS — zwykły tekst, wiersze oddzielone \n (w JSON jako entery). * @param {string} title * @param {{ label?: string, value?: string }[]} lines */ export function formatGuestQueueMessage(title, lines = []) { const rows = (Array.isArray(lines) ? lines : []) .map((line) => { const label = String(line?.label ?? "").trim(); const value = String(line?.value ?? "").trim(); if (!label && !value) return ""; if (label && value) return `${label} ${value}`; return label || value; }) .filter(Boolean); if (!rows.length) { return title; } return `${title}\n${rows.join("\n")}`; } export function buildWaiterCallQueueMessage() { return "Przywołanie kelnera"; } export function buildBillRequestQueueMessage(docType) { const billState = getBillState(); const lines = [ { label: "Forma płatności:", value: billState.payment || "nieznana" }, { label: "Dokument:", value: docType === "faktura" ? "faktura" : "paragon" }, ]; if (docType === "faktura") { lines.push({ label: "NIP:", value: billState.nip || "—" }); lines.push({ label: "Firma:", value: billState.company?.name || "—" }); const addressParts = [ billState.company?.street, [billState.company?.zip, billState.company?.city].filter(Boolean).join(" "), ].filter(Boolean); if (addressParts.length) { lines.push({ label: "Adres:", value: addressParts.join(", ") }); } } return formatGuestQueueMessage("Prośba o rachunek", lines); } export function guestActionBlockedMessage(messageType) { if (messageType === "waiter_call") { return "Kelner został już wezwany. Poczekaj, aż obsługa potwierdzi zgłoszenie na panelu."; } return "Prośba o rachunek została już wysłana. Poczekaj, aż obsługa ją obsłuży."; } export function updateGuestActionNavState() { const guestPendingActions = getGuestPendingActions(); const waiterNav = document.querySelector(".bottom-nav .action-call"); const billNav = document.querySelector(".bottom-nav .action-bill"); if (waiterNav) { waiterNav.classList.toggle("nav-action-pending", guestPendingActions.waiter_call); } if (billNav) { billNav.classList.toggle("nav-action-pending", guestPendingActions.bill_request); } } export async function refreshGuestPendingActions() { const hashParam = getHashParam(); const tableParam = getTableParam(); const guestPendingActions = getGuestPendingActions(); if (!hashParam && !tableParam) { return guestPendingActions; } const params = new URLSearchParams(); if (hashParam) params.set("h", hashParam); if (tableParam) params.set("tableId", tableParam); try { const res = await fetch(`${endpoints.guestActionQueue}?${params.toString()}`); const result = await res.json(); if (result.status === "success" && result.pending) { setGuestPendingAction("waiter_call", !!result.pending.waiter_call); setGuestPendingAction("bill_request", !!result.pending.bill_request); updateGuestActionNavState(); } } catch { // best effort } return getGuestPendingActions(); } export function startGuestPendingPoll() { if (guestPendingPollTimer) return; guestPendingPollTimer = setInterval(() => { if (!getHashParam() && !getTableParam()) return; refreshGuestPendingActions(); }, GUEST_PENDING_POLL_MS); } export async function ensureGuestActionAllowed(messageType) { await refreshGuestPendingActions(); if (!getGuestPendingActions()[messageType]) { return true; } showToast(guestActionBlockedMessage(messageType)); return false; } export async function queueGuestAction(messageType, messageText, extra = {}) { const operator = getQueueOperatorFields(); const body = { tableId: getTableParam() || null, qrHash: getHashParam() || null, messageType, messageText, otwierajacyImie: operator.otwierajacyImie, otwierajacyNazwisko: operator.otwierajacyNazwisko, extra, }; try { const res = await fetch(endpoints.guestActionQueue, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), keepalive: true, }); const result = await res.json().catch(() => ({})); if (res.status === 409 || result.code === "pending_on_kds") { setGuestPendingAction(messageType, true); updateGuestActionNavState(); return { ok: false, reason: "pending" }; } if (res.status === 429 || result.code === "rate_limited") { return { ok: false, reason: "error" }; } if (!res.ok || result.status !== "success") { return { ok: false, reason: "error" }; } setGuestPendingAction(messageType, true); updateGuestActionNavState(); return { ok: true }; } catch { return { ok: false, reason: "error" }; } }