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>