Przebudowa działania geolokalizacji. Zgoda na menu bez lokalizacji.
This commit is contained in:
@@ -9,6 +9,7 @@ window.selectedAnimationHtml = null;
|
||||
|
||||
const params = new URLSearchParams(location.search);
|
||||
let hashParam = (params.get("h") || "").trim();
|
||||
const isStaffPreview = params.get("preview") === "staff";
|
||||
|
||||
// Jeśli brak hasha w URL – zapytaj użytkownika (np. do testów)
|
||||
if (!hashParam) {
|
||||
@@ -22,6 +23,8 @@ if (!hashParam) {
|
||||
}
|
||||
|
||||
let tableParam = ""; // Puste, zostanie uzupełnione przez backend
|
||||
let appAccessLevel = "none"; // "none" | "menu" | "full"
|
||||
let pendingProtectedAction = null; // "status" | "waiter" | "bill"
|
||||
const analyticsEndpoint = "../api/analytics.php";
|
||||
const guestActionQueueEndpoint = "../api/guest_action_queue.php";
|
||||
const analyticsSessionKey = "karczma_analytics_session_id";
|
||||
@@ -69,6 +72,10 @@ function deriveZoneFromTable(tableValue) {
|
||||
}
|
||||
|
||||
function trackEvent(eventName, payload = {}) {
|
||||
if (isStaffPreview) {
|
||||
return;
|
||||
}
|
||||
|
||||
const body = {
|
||||
eventName,
|
||||
sessionId: analyticsSessionId,
|
||||
@@ -433,9 +440,155 @@ function hideLoader() {
|
||||
if (bottomNav) {
|
||||
bottomNav.style.display = "";
|
||||
}
|
||||
updateNavAccessState();
|
||||
if (pendingProtectedAction && appAccessLevel === "full") {
|
||||
runPendingProtectedAction();
|
||||
}
|
||||
}, remaining);
|
||||
}
|
||||
|
||||
function updateNavAccessState() {
|
||||
const locked = appAccessLevel === "menu";
|
||||
["navStatus", "navWaiter", "navBill"].forEach((id) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.classList.toggle("nav-locked", locked);
|
||||
});
|
||||
const banner = document.getElementById("menuOnlyBanner");
|
||||
if (banner) banner.classList.toggle("hidden", appAccessLevel !== "menu");
|
||||
}
|
||||
|
||||
function showBottomNav() {
|
||||
const bottomNav = document.getElementById("bottomNav");
|
||||
if (bottomNav) bottomNav.style.display = "";
|
||||
updateNavAccessState();
|
||||
}
|
||||
|
||||
async function resolveTableLabel() {
|
||||
if (!hashParam) return;
|
||||
try {
|
||||
const response = await fetch(`../api/kds.php?h=${encodeURIComponent(hashParam)}`);
|
||||
const result = await response.json();
|
||||
if (result.status === "success" && result.tableName && result.tableName !== "") {
|
||||
tableLabel.textContent = result.tableName.toUpperCase().startsWith("STOLIK")
|
||||
? result.tableName
|
||||
: `Stolik ${result.tableName}`;
|
||||
tableParam = result.tableName;
|
||||
}
|
||||
} catch {
|
||||
// best effort
|
||||
}
|
||||
}
|
||||
|
||||
const GEO_GATE_LABELS = {
|
||||
status: "status zamówienia",
|
||||
waiter: "wezwanie kelnera",
|
||||
bill: "prośbę o rachunek",
|
||||
};
|
||||
|
||||
function configureGeoSecondaryButton(mode) {
|
||||
const menuOnlyBtn = document.getElementById("geoMenuOnlyBtn");
|
||||
if (!menuOnlyBtn) return;
|
||||
|
||||
if (mode === "back_to_menu") {
|
||||
menuOnlyBtn.style.display = "";
|
||||
menuOnlyBtn.textContent = "Wróć do menu";
|
||||
menuOnlyBtn.onclick = () => {
|
||||
document.getElementById("geoScreen")?.classList.add("hidden");
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
menuOnlyBtn.style.display = "";
|
||||
menuOnlyBtn.textContent = "Przeglądaj menu bez lokalizacji";
|
||||
menuOnlyBtn.onclick = () => enterMenuOnlyMode();
|
||||
}
|
||||
|
||||
function showGeoGateForAction(action) {
|
||||
const geoScreen = document.getElementById("geoScreen");
|
||||
const loadingScreen = document.getElementById("loadingScreen");
|
||||
const geoMsg = document.getElementById("geoMsg");
|
||||
const geoActionBtn = document.getElementById("geoActionBtn");
|
||||
|
||||
if (loadingScreen) loadingScreen.classList.add("hidden");
|
||||
if (geoScreen) geoScreen.classList.remove("hidden");
|
||||
|
||||
const feature = GEO_GATE_LABELS[action] || "tę funkcję";
|
||||
if (geoMsg) {
|
||||
geoMsg.innerHTML = `Aby skorzystać z <b>${feature}</b>, potwierdź, że jesteś w restauracji.<br><br>Prosimy o zgodę na dostęp do lokalizacji.`;
|
||||
}
|
||||
if (geoActionBtn) {
|
||||
geoActionBtn.disabled = false;
|
||||
geoActionBtn.textContent = "Sprawdź lokalizację";
|
||||
}
|
||||
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
|
||||
}
|
||||
|
||||
function requireFullAccess(action, onGranted) {
|
||||
if (appAccessLevel === "full") {
|
||||
onGranted();
|
||||
return;
|
||||
}
|
||||
pendingProtectedAction = action;
|
||||
trackEvent("geo_gate_prompted", { action });
|
||||
if (appAccessLevel === "menu") {
|
||||
trackEvent("geo_retry_from_menu", { action });
|
||||
}
|
||||
showGeoGateForAction(action);
|
||||
}
|
||||
|
||||
window.promptGeoForFullAccess = function () {
|
||||
pendingProtectedAction = pendingProtectedAction || "status";
|
||||
trackEvent("geo_gate_prompted", { action: pendingProtectedAction, source: "menu_banner" });
|
||||
trackEvent("geo_retry_from_menu", { action: pendingProtectedAction, source: "menu_banner" });
|
||||
showGeoGateForAction(pendingProtectedAction);
|
||||
};
|
||||
|
||||
function runPendingProtectedAction() {
|
||||
const action = pendingProtectedAction;
|
||||
pendingProtectedAction = null;
|
||||
if (!action) return;
|
||||
|
||||
setTimeout(() => {
|
||||
if (action === "status") switchTabInternal("status");
|
||||
else if (action === "waiter") openWaiterDialogInternal();
|
||||
else if (action === "bill") openBillDialogInternal();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function unlockFullApp() {
|
||||
appAccessLevel = "full";
|
||||
updateNavAccessState();
|
||||
document.getElementById("geoScreen")?.classList.add("hidden");
|
||||
|
||||
const loaderVisible = !document.getElementById("loadingScreen")?.classList.contains("hidden");
|
||||
const alreadyStarted = !!window.ordersInterval;
|
||||
|
||||
if (alreadyStarted && !loaderVisible) {
|
||||
trackEvent("session_start", { flow: "unlock_from_menu" });
|
||||
initUserProfile();
|
||||
fetchOrders();
|
||||
prefetchOpenBills();
|
||||
refreshGuestPendingActions();
|
||||
startGuestPendingPoll();
|
||||
runPendingProtectedAction();
|
||||
return;
|
||||
}
|
||||
|
||||
startApp();
|
||||
}
|
||||
|
||||
window.enterMenuOnlyMode = function () {
|
||||
trackEvent("menu_only_entered");
|
||||
appAccessLevel = "menu";
|
||||
pendingProtectedAction = null;
|
||||
|
||||
document.getElementById("geoScreen")?.classList.add("hidden");
|
||||
document.getElementById("loadingScreen")?.classList.add("hidden");
|
||||
showBottomNav();
|
||||
resolveTableLabel();
|
||||
switchTabInternal("menu");
|
||||
};
|
||||
|
||||
function updateUI(bills) {
|
||||
// Hide loader after minimum display time
|
||||
hideLoader();
|
||||
@@ -835,7 +988,13 @@ window.callWaiter = async function (type) {
|
||||
showToast("Kelner wkrótce do Ciebie podejdzie!");
|
||||
};
|
||||
|
||||
window.openWaiterDialog = async function () {
|
||||
window.openWaiterDialog = function () {
|
||||
requireFullAccess("waiter", () => {
|
||||
openWaiterDialogInternal();
|
||||
});
|
||||
};
|
||||
|
||||
async function openWaiterDialogInternal() {
|
||||
if (!(await ensureGuestActionAllowed("waiter_call"))) {
|
||||
return;
|
||||
}
|
||||
@@ -860,7 +1019,13 @@ window.proceedToBillPayment = async function () {
|
||||
goToStep("stepPayment");
|
||||
};
|
||||
|
||||
window.openBillDialog = async function () {
|
||||
window.openBillDialog = function () {
|
||||
requireFullAccess("bill", () => {
|
||||
openBillDialogInternal();
|
||||
});
|
||||
};
|
||||
|
||||
async function openBillDialogInternal() {
|
||||
await refreshGuestPendingActions();
|
||||
trackEvent("bill_dialog_opened");
|
||||
billState = { payment: '', doc: '', nip: '', company: null, selectedBillId: null };
|
||||
@@ -964,7 +1129,7 @@ window.goToStep = function (stepId) {
|
||||
};
|
||||
|
||||
// --- SPA NAVIGATION LOGIC ---
|
||||
window.switchTab = function (tabName) {
|
||||
function switchTabInternal(tabName) {
|
||||
// 1. Zdejmij .active z widoków i ikonek nav
|
||||
document.querySelectorAll('.view-section').forEach(el => el.classList.add('hidden'));
|
||||
document.querySelectorAll('.view-section').forEach(el => el.classList.remove('active'));
|
||||
@@ -983,11 +1148,10 @@ window.switchTab = function (tabName) {
|
||||
view.classList.add('active');
|
||||
document.getElementById('navStatus').classList.add('active');
|
||||
if (header) header.style.display = '';
|
||||
// Show greeting banner only if it was rendered initially or has content
|
||||
if (greetingBanner && greetingBanner.innerHTML.trim() !== '') greetingBanner.style.display = '';
|
||||
}
|
||||
else if (tabName === 'menu') {
|
||||
trackEvent("view_menu");
|
||||
trackEvent("view_menu", { flow: appAccessLevel === "menu" ? "menu_only" : "full_app" });
|
||||
const view = document.getElementById('menuView');
|
||||
view.classList.remove('hidden');
|
||||
view.classList.add('active');
|
||||
@@ -996,6 +1160,16 @@ window.switchTab = function (tabName) {
|
||||
if (greetingBanner) greetingBanner.style.display = 'none';
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
window.switchTab = function (tabName) {
|
||||
if (tabName === "menu") {
|
||||
switchTabInternal("menu");
|
||||
return;
|
||||
}
|
||||
if (tabName === "status") {
|
||||
requireFullAccess("status", () => switchTabInternal("status"));
|
||||
}
|
||||
};
|
||||
|
||||
// --- MENU LOGIC ---
|
||||
@@ -1312,6 +1486,8 @@ function haversineDistance(lat1, lon1, lat2, lon2) {
|
||||
}
|
||||
|
||||
function startApp() {
|
||||
appAccessLevel = "full";
|
||||
updateNavAccessState();
|
||||
document.getElementById("geoScreen").classList.add("hidden");
|
||||
document.getElementById("loadingScreen").classList.remove("hidden");
|
||||
trackEvent("session_start", { flow: "start_app" });
|
||||
@@ -1338,6 +1514,7 @@ function showGeoConsentScreen() {
|
||||
const loadingScreen = document.getElementById("loadingScreen");
|
||||
const geoMsg = document.getElementById("geoMsg");
|
||||
const geoActionBtn = document.getElementById("geoActionBtn");
|
||||
const menuOnlyBtn = document.getElementById("geoMenuOnlyBtn");
|
||||
|
||||
loadingScreen.classList.add("hidden");
|
||||
geoScreen.classList.remove("hidden");
|
||||
@@ -1350,6 +1527,9 @@ function showGeoConsentScreen() {
|
||||
geoActionBtn.disabled = false;
|
||||
geoActionBtn.textContent = "Udziel zgody / Sprawdź";
|
||||
}
|
||||
if (menuOnlyBtn) {
|
||||
configureGeoSecondaryButton("menu_only");
|
||||
}
|
||||
}
|
||||
|
||||
function isIOSDevice() {
|
||||
@@ -1365,7 +1545,7 @@ window.initGeolocation = function () {
|
||||
if (shouldBypassGeolocationHost()) {
|
||||
console.warn("Bypassing geolocation for trusted host.");
|
||||
trackEvent("geo_bypass_host", { host: window.location.hostname, reason: "trusted_host" });
|
||||
startApp();
|
||||
unlockFullApp();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1373,12 +1553,13 @@ window.initGeolocation = function () {
|
||||
const loadingScreen = document.getElementById("loadingScreen");
|
||||
const geoMsg = document.getElementById("geoMsg");
|
||||
const geoActionBtn = document.getElementById("geoActionBtn");
|
||||
const menuOnlyBtn = document.getElementById("geoMenuOnlyBtn");
|
||||
|
||||
const bypassHosts = ['localhost', '127.0.0.1', '192.168.20.84'];
|
||||
if (window.location.protocol === 'http:' && bypassHosts.includes(window.location.hostname)) {
|
||||
console.warn("Bypassing geolocation on local HTTP environment.");
|
||||
trackEvent("geo_bypass_host", { host: window.location.hostname, reason: "local_http" });
|
||||
startApp();
|
||||
unlockFullApp();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1392,11 +1573,13 @@ window.initGeolocation = function () {
|
||||
geoActionBtn.disabled = false;
|
||||
geoActionBtn.textContent = "Spróbuj ponownie";
|
||||
}
|
||||
if (menuOnlyBtn) configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
|
||||
return;
|
||||
}
|
||||
|
||||
loadingScreen.classList.add("hidden");
|
||||
geoScreen.classList.remove("hidden");
|
||||
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
|
||||
|
||||
if (!navigator.geolocation) {
|
||||
geoMsg.innerHTML = "Twoja przeglądarka nie wspiera geolokalizacji. Aplikacja wymaga nowszej przeglądarki.";
|
||||
@@ -1422,7 +1605,7 @@ window.initGeolocation = function () {
|
||||
|
||||
if (dist <= MAX_DISTANCE_METERS) {
|
||||
trackEvent("geo_check_passed", { distanceMeters: Math.round(dist), accuracyMeters: Math.round(accuracy) });
|
||||
startApp();
|
||||
unlockFullApp();
|
||||
// setTimeout(() => showToast(`Lokalizacja zweryfikowana (Dystans: ${Math.round(dist)}m, Dokładność: ${Math.round(accuracy)}m)`), 2000);
|
||||
} else {
|
||||
trackEvent("geo_check_failed", { reason: "outside_restaurant", distanceMeters: Math.round(dist), accuracyMeters: Math.round(accuracy) });
|
||||
@@ -1430,6 +1613,7 @@ window.initGeolocation = function () {
|
||||
geoActionBtn.disabled = false;
|
||||
geoActionBtn.textContent = "Spróbuj ponownie";
|
||||
}
|
||||
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
|
||||
geoMsg.innerHTML = `Wydaje się, że jesteś poza restauracją (ok. ${Math.round(dist)}m od nas).<br>Nasza aplikacja działa tylko na miejscu.<br><br>
|
||||
<small style="color: #888;">Debug: Twoja odległość: ${Math.round(dist)}m, Dokładność sygnału: ${Math.round(accuracy)}m</small><br><br>
|
||||
Jeśli to błąd GPS lub słaby sygnał, spróbuj ponownie za chwilę.`;
|
||||
@@ -1441,6 +1625,7 @@ window.initGeolocation = function () {
|
||||
geoActionBtn.disabled = false;
|
||||
geoActionBtn.textContent = "Spróbuj ponownie";
|
||||
}
|
||||
configureGeoSecondaryButton(appAccessLevel === "menu" ? "back_to_menu" : "menu_only");
|
||||
const deniedBecauseInsecure = /secure origins|only secure|https/i.test(String(error.message || ""));
|
||||
if (deniedBecauseInsecure) {
|
||||
geoMsg.innerHTML = `<b style="color: #ff6b6b;">Przeglądarka zablokowała lokalizację z powodu braku HTTPS.</b><br><br>
|
||||
|
||||
Reference in New Issue
Block a user