fix(form): address admin and form review findings

This commit is contained in:
Adam Moussa 2026-07-30 18:41:02 -04:00
parent 2f88a4a315
commit e00d74ebad
No known key found for this signature in database
3 changed files with 26 additions and 22 deletions

View file

@ -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 = '<tr><td colspan="5" class="admin-error">Failed to load orders.</td></tr>';
@ -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);

View file

@ -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();
}
}

View file

@ -150,7 +150,6 @@ class TestGenerateFormStructural:
blob = cfg_match.group(1)
# Literal </script> inside the CONFIG script would terminate early.
assert "</script>" not in blob
assert "<\\/script><script>alert(1)<\\/script>" in blob
# Parsed JSON still contains the hostile meal name.
cfg = json.loads(blob)
assert any(
@ -281,7 +280,7 @@ class TestGenerateFormPlaywright:
# Reset first card via type-in
qty = page.locator('[data-meal-index="0"] .qty')
qty.fill("-3")
qty.dispatch_event("change")
qty.blur()
page.wait_for_function(
"() => document.querySelector('[data-meal-index=\"0\"] .qty').value === '0'"
)
@ -292,7 +291,7 @@ class TestGenerateFormPlaywright:
"""() => {
document.querySelectorAll('[data-meal-index] .qty').forEach((input) => {
input.value = '0';
input.dispatchEvent(new Event('change', { bubbles: true }));
input.dispatchEvent(new Event('input', { bubbles: true }));
});
}"""
)
@ -300,7 +299,7 @@ class TestGenerateFormPlaywright:
"() => document.getElementById('count-display').textContent === '0 meals'"
)
qty.fill("2")
qty.dispatch_event("change")
qty.blur()
page.wait_for_function(
"() => document.getElementById('count-display').textContent === '2 meals'"
)