216 lines
6.0 KiB
JavaScript
216 lines
6.0 KiB
JavaScript
import { endpoints } from "./config.js";
|
|
import { t } from "./i18n.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 t("waiter.queue_title");
|
|
}
|
|
|
|
export function buildBillRequestQueueMessage(docType) {
|
|
const billState = getBillState();
|
|
const lines = [
|
|
{
|
|
label: t("bill.queue_payment"),
|
|
value: billState.payment || t("bill.payment_unknown"),
|
|
},
|
|
{
|
|
label: t("bill.queue_doc"),
|
|
value: docType === "faktura" ? t("bill.doc_invoice") : t("bill.doc_receipt"),
|
|
},
|
|
];
|
|
|
|
if (docType === "faktura") {
|
|
lines.push({ label: t("bill.queue_nip"), value: billState.nip || "—" });
|
|
lines.push({ label: t("bill.queue_company"), 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: t("bill.queue_address"), value: addressParts.join(", ") });
|
|
}
|
|
}
|
|
|
|
return formatGuestQueueMessage(t("bill.queue_title"), lines);
|
|
}
|
|
|
|
export function guestActionBlockedMessage(messageType) {
|
|
if (messageType === "waiter_call") {
|
|
return t("waiter.blocked");
|
|
}
|
|
return t("bill.blocked");
|
|
}
|
|
|
|
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" };
|
|
}
|
|
}
|