diff --git a/src/server/templates/admin.js b/src/server/templates/admin.js index 99c78be..de4e676 100644 --- a/src/server/templates/admin.js +++ b/src/server/templates/admin.js @@ -28,7 +28,10 @@ function loadWeekOrders(week) { document.getElementById('admin-stats').textContent = ''; fetch(ADMIN_URL + '?week=' + encodeURIComponent(week), { headers: adminHeaders() }) - .then(r => r.json()) + .then(r => { + if (!r.ok) throw new Error('HTTP ' + r.status); + return r.json(); + }) .then(data => renderAdminTable(data)) .catch(() => { tbody.innerHTML = 'Failed to load orders.'; @@ -187,7 +190,7 @@ function renderAdminTable(data) { MEALS.forEach(m => { menuPrices[m.name] = m.price; }); const newTotal = editNames.reduce((sum, name) => { const price = menuPrices[name] || 0; - return sum + price * (editQuantities[name] || 0); + return sum + window.FormUtils.employeePrice(price) * (editQuantities[name] || 0); }, 0); totalHtml = '$' + newTotal.toFixed(2); tr.appendChild(empTd); diff --git a/src/server/templates/form.js b/src/server/templates/form.js index d37fa23..a8eff56 100644 --- a/src/server/templates/form.js +++ b/src/server/templates/form.js @@ -26,6 +26,7 @@ let adminWeeks = []; let googleCredential = null; let googleUser = null; let reopenAtMs = null; +let countdownTimer = null; let mealsListDelegationBound = false; window.googleCredential = null; @@ -103,7 +104,9 @@ function handleCredentialResponse(response) { updateTotal(); checkDuplicateOrder(); - checkAdmin(); + checkAdmin().then(() => { + if (formClosed && !isAdmin) showClosedOverlay(); + }); } function signOut() { @@ -257,7 +260,7 @@ function updateMealCardQty(index) { const qty = quantities[index] || 0; card.classList.toggle('selected', qty > 0); const qtyInput = card.querySelector('.qty'); - if (qtyInput) qtyInput.value = qty; + if (qtyInput && qtyInput !== document.activeElement) qtyInput.value = qty; } function setQty(index, value) { @@ -286,14 +289,6 @@ function bindMealsListDelegation() { const delta = parseInt(btn.dataset.qtyDelta, 10); changeQty(index, delta); }); - list.addEventListener('change', (e) => { - const input = e.target.closest('.qty'); - if (!input) return; - const card = input.closest('[data-meal-index]'); - if (!card) return; - const index = parseInt(card.dataset.mealIndex, 10); - setQty(index, input.value); - }); list.addEventListener('input', (e) => { const input = e.target.closest('.qty'); if (!input) return; @@ -302,6 +297,13 @@ function bindMealsListDelegation() { const index = parseInt(card.dataset.mealIndex, 10); setQty(index, input.value); }); + list.addEventListener('focusout', (e) => { + const input = e.target.closest('.qty'); + if (!input) return; + const card = input.closest('[data-meal-index]'); + if (!card) return; + updateMealCardQty(parseInt(card.dataset.mealIndex, 10)); + }); } function updateTotal() { @@ -405,7 +407,7 @@ function showClosedOverlay() { const overlay = document.getElementById('closed-overlay'); overlay.classList.add('is-visible'); overlay.style.display = 'flex'; - document.querySelectorAll('.qty-control button').forEach(b => { b.disabled = true; }); + document.querySelectorAll('.qty-control button, .qty-control .qty').forEach(el => { el.disabled = true; }); const submitBtn = document.getElementById('submit-btn'); if (submitBtn) submitBtn.disabled = true; if (formStatusData) startCountdown(formStatusData); @@ -435,10 +437,10 @@ function adminBypassClosed() { } function checkAdmin() { - if (!ADMIN_URL) return; + if (!ADMIN_URL) return Promise.resolve(); const headers = {}; if (googleCredential) headers['Authorization'] = 'Bearer ' + googleCredential; - fetch(ADMIN_URL, { headers }) + return fetch(ADMIN_URL, { headers }) .then(r => r.ok ? r.json() : null) .then(data => { if (data && data.weeks) { @@ -520,7 +522,8 @@ function getEasternOffsetMs(date) { function startCountdown(data) { if (data && data.reopen_at) reopenAtMs = data.reopen_at * 1000; updateCountdown(); - setInterval(updateCountdown, 1000); + if (countdownTimer !== null) clearInterval(countdownTimer); + countdownTimer = setInterval(updateCountdown, 1000); } function updateCountdown() { @@ -561,8 +564,7 @@ async function checkFormStatus() { formStatusData = data; if (isAdmin) { adminBypassClosed(); - } else if (CONFIG.authMode === 'google') { - } else { + } else if (CONFIG.authMode !== 'google' || googleCredential) { showClosedOverlay(); } } diff --git a/tests/test_generate_form.py b/tests/test_generate_form.py index 29c3c1c..4c4bfc8 100644 --- a/tests/test_generate_form.py +++ b/tests/test_generate_form.py @@ -150,7 +150,6 @@ class TestGenerateFormStructural: blob = cfg_match.group(1) # Literal inside the CONFIG script would terminate early. assert "" not in blob - assert "<\\/script>