import { endpoints, loaderMinMs } from "./config.js"; import { getLang, t } from "./i18n.js"; import { runPendingProtectedAction, updateNavAccessState } from "./access.js"; import { refreshGuestPendingActions, startGuestPendingPoll, } from "./queue.js"; import { getAppAccessLevel, getHashParam, getPendingProtectedAction, getTableParam, setTableParam, } from "./state.js"; const loadingScreen = document.getElementById("loadingScreen"); const loaderMsg = document.getElementById("loaderMsg"); const tableLabel = document.getElementById("tableLabel"); const prepStatus = document.getElementById("prepStatus"); const progressBar = document.getElementById("progressBar"); const statusMeta = document.getElementById("statusMeta"); const itemsList = document.getElementById("itemsList"); const emptyState = document.getElementById("emptyState"); const metaFooter = document.getElementById("metaFooter"); const statusIcon = document.getElementById("statusIcon"); const historyKey = "stolik2_global_history"; const historySection = document.getElementById("historySection"); const historyList = document.getElementById("historyList"); const SIX_MONTHS_MS = 180 * 24 * 60 * 60 * 1000; const LOADER_MIN_MS = loaderMinMs; const loadStartTime = Date.now(); function getLoaderMsgs() { return [t("loader.msg1"), t("loader.msg2"), t("loader.msg3"), t("loader.msg4")]; } let msgIdx = 0; const msgInterval = setInterval(() => { const msgs = getLoaderMsgs(); msgIdx = (msgIdx + 1) % msgs.length; if (loaderMsg) loaderMsg.textContent = msgs[msgIdx]; }, 4000); function formatTableLabel(name) { const raw = String(name || "").trim(); if (!raw) return t("table.label", { name: "" }).trim(); if (raw.toUpperCase().startsWith("STOLIK") || raw.toUpperCase().startsWith("TABLE") || raw.toUpperCase().startsWith("TISCH")) { return raw; } return t("table.label", { name: raw }); } // Initial State if (getTableParam()) { tableLabel.textContent = formatTableLabel(getTableParam()); } /** Storage key must follow current hash (fallback: table), not a frozen empty tableParam at init. */ export function getOrderStorageKey() { const key = (getHashParam() || getTableParam() || "unknown").toLowerCase(); return `stolik2_state_${key}`; } export function hideLoader() { const elapsed = Date.now() - loadStartTime; const remaining = Math.max(0, LOADER_MIN_MS - elapsed); setTimeout(() => { loadingScreen.classList.add("hidden"); clearInterval(msgInterval); const bottomNav = document.getElementById("bottomNav"); if (bottomNav) { bottomNav.style.display = ""; } updateNavAccessState(); if (getPendingProtectedAction() && getAppAccessLevel() === "full") { runPendingProtectedAction(); } }, remaining); } export async function resolveTableLabel() { const hashParam = getHashParam(); if (!hashParam) return; try { const response = await fetch(`${endpoints.kds}?h=${encodeURIComponent(hashParam)}`); const result = await response.json(); if (result.status === "success" && result.tableName && result.tableName !== "") { tableLabel.textContent = formatTableLabel(result.tableName); setTableParam(result.tableName); } } catch { // best effort } } export function updateUI(bills) { // Hide loader after minimum display time hideLoader(); const allArticles = bills.flatMap((b) => (Array.isArray(b?.Articles) ? b.Articles : [])); const items = mergeWithPersistedItems(allArticles); renderGlobalHistory(); if (items.length === 0) { showEmptyState(); return; } renderItems(items); updateStatus(bills, items); } function showEmptyState() { const title = document.getElementById("ordersTitle"); if (title) title.classList.add("hidden"); emptyState.classList.remove("hidden"); itemsList.innerHTML = ""; prepStatus.textContent = t("status.none"); statusIcon.textContent = "馃崈"; progressBar.style.width = "0%"; statusMeta.textContent = t("status.none_meta"); // Historia mo偶e istnie膰 nawet gdy brak bie偶膮cych pozycji renderGlobalHistory(); } function normalizeArticleName(rawName) { const name = String(rawName || t("orders.item_fallback")); // Usuwa gramatury typu: "300G", "250 G", "500/200/150G". const withoutWeight = name.replace( /\b\d+(?:[.,]\d+)?(?:\s*\/\s*\d+(?:[.,]\d+)?)*\s*[gG]\b/g, "" ); return withoutWeight .replace(/\s{2,}/g, " ") .replace(/\s+([,.;:!?])/g, "$1") .trim() || t("orders.item_fallback"); } function loadPersistedItems() { try { const raw = localStorage.getItem(getOrderStorageKey()); if (!raw) return []; const parsed = JSON.parse(raw); return Array.isArray(parsed) ? parsed : []; } catch { return []; } } function savePersistedItems(items) { try { localStorage.setItem(getOrderStorageKey(), JSON.stringify(items)); } catch { // brak miejsca/tryb prywatny } } function loadGlobalHistory() { try { const raw = localStorage.getItem(historyKey); if (!raw) return []; const parsed = JSON.parse(raw); return Array.isArray(parsed) ? parsed : []; } catch { return []; } } function saveGlobalHistory(entries) { const now = Date.now(); const cleaned = entries .filter((e) => e && e.name && Number.isFinite(e.archivedAt)) .filter((e) => now - e.archivedAt <= SIX_MONTHS_MS); try { localStorage.setItem(historyKey, JSON.stringify(cleaned)); } catch { // ignore storage errors } } function addItemsToGlobalHistory(items, sourceTable) { if (!items.length) return; const now = Date.now(); const existing = loadGlobalHistory(); // zapobiegamy dok艂adnym duplikatom (ta sama nazwa + qty + st贸艂 blisko czasu) const dedupWindowMs = 2 * 60 * 1000; const toAdd = items .filter((item) => { return !existing.some( (h) => h.name === item.name && Number(h.qty) === Number(item.qty) && String(h.sourceTable || "") === String(sourceTable || "") && Math.abs((h.archivedAt || 0) - now) <= dedupWindowMs ); }) .map((item) => ({ name: item.name, qty: item.qty, sourceTable: sourceTable || "?", archivedAt: now, })); if (!toAdd.length) return; saveGlobalHistory([...existing, ...toAdd]); } export function renderGlobalHistory() { const now = Date.now(); const lang = getLang(); const history = loadGlobalHistory() .filter((e) => now - (e.archivedAt || 0) <= SIX_MONTHS_MS) .sort((a, b) => (b.archivedAt || 0) - (a.archivedAt || 0)); saveGlobalHistory(history); if (!history.length) { historySection.classList.add("hidden"); historyList.innerHTML = ""; return; } historySection.classList.remove("hidden"); historyList.innerHTML = ""; history.forEach((entry) => { const dt = new Date(entry.archivedAt || Date.now()); const div = document.createElement("div"); div.className = "item-card archived ready"; div.innerHTML = `