mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-10-07 16:18:54 +00:00
fix(form): address admin and form review findings
This commit is contained in:
parent
2f88a4a315
commit
e00d74ebad
3 changed files with 26 additions and 22 deletions
|
|
@ -28,7 +28,10 @@ function loadWeekOrders(week) {
|
||||||
document.getElementById('admin-stats').textContent = '';
|
document.getElementById('admin-stats').textContent = '';
|
||||||
|
|
||||||
fetch(ADMIN_URL + '?week=' + encodeURIComponent(week), { headers: adminHeaders() })
|
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))
|
.then(data => renderAdminTable(data))
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
tbody.innerHTML = '<tr><td colspan="5" class="admin-error">Failed to load orders.</td></tr>';
|
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; });
|
MEALS.forEach(m => { menuPrices[m.name] = m.price; });
|
||||||
const newTotal = editNames.reduce((sum, name) => {
|
const newTotal = editNames.reduce((sum, name) => {
|
||||||
const price = menuPrices[name] || 0;
|
const price = menuPrices[name] || 0;
|
||||||
return sum + price * (editQuantities[name] || 0);
|
return sum + window.FormUtils.employeePrice(price) * (editQuantities[name] || 0);
|
||||||
}, 0);
|
}, 0);
|
||||||
totalHtml = '$' + newTotal.toFixed(2);
|
totalHtml = '$' + newTotal.toFixed(2);
|
||||||
tr.appendChild(empTd);
|
tr.appendChild(empTd);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ let adminWeeks = [];
|
||||||
let googleCredential = null;
|
let googleCredential = null;
|
||||||
let googleUser = null;
|
let googleUser = null;
|
||||||
let reopenAtMs = null;
|
let reopenAtMs = null;
|
||||||
|
let countdownTimer = null;
|
||||||
let mealsListDelegationBound = false;
|
let mealsListDelegationBound = false;
|
||||||
|
|
||||||
window.googleCredential = null;
|
window.googleCredential = null;
|
||||||
|
|
@ -103,7 +104,9 @@ function handleCredentialResponse(response) {
|
||||||
|
|
||||||
updateTotal();
|
updateTotal();
|
||||||
checkDuplicateOrder();
|
checkDuplicateOrder();
|
||||||
checkAdmin();
|
checkAdmin().then(() => {
|
||||||
|
if (formClosed && !isAdmin) showClosedOverlay();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function signOut() {
|
function signOut() {
|
||||||
|
|
@ -257,7 +260,7 @@ function updateMealCardQty(index) {
|
||||||
const qty = quantities[index] || 0;
|
const qty = quantities[index] || 0;
|
||||||
card.classList.toggle('selected', qty > 0);
|
card.classList.toggle('selected', qty > 0);
|
||||||
const qtyInput = card.querySelector('.qty');
|
const qtyInput = card.querySelector('.qty');
|
||||||
if (qtyInput) qtyInput.value = qty;
|
if (qtyInput && qtyInput !== document.activeElement) qtyInput.value = qty;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setQty(index, value) {
|
function setQty(index, value) {
|
||||||
|
|
@ -286,14 +289,6 @@ function bindMealsListDelegation() {
|
||||||
const delta = parseInt(btn.dataset.qtyDelta, 10);
|
const delta = parseInt(btn.dataset.qtyDelta, 10);
|
||||||
changeQty(index, delta);
|
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) => {
|
list.addEventListener('input', (e) => {
|
||||||
const input = e.target.closest('.qty');
|
const input = e.target.closest('.qty');
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
|
|
@ -302,6 +297,13 @@ function bindMealsListDelegation() {
|
||||||
const index = parseInt(card.dataset.mealIndex, 10);
|
const index = parseInt(card.dataset.mealIndex, 10);
|
||||||
setQty(index, input.value);
|
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() {
|
function updateTotal() {
|
||||||
|
|
@ -405,7 +407,7 @@ function showClosedOverlay() {
|
||||||
const overlay = document.getElementById('closed-overlay');
|
const overlay = document.getElementById('closed-overlay');
|
||||||
overlay.classList.add('is-visible');
|
overlay.classList.add('is-visible');
|
||||||
overlay.style.display = 'flex';
|
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');
|
const submitBtn = document.getElementById('submit-btn');
|
||||||
if (submitBtn) submitBtn.disabled = true;
|
if (submitBtn) submitBtn.disabled = true;
|
||||||
if (formStatusData) startCountdown(formStatusData);
|
if (formStatusData) startCountdown(formStatusData);
|
||||||
|
|
@ -435,10 +437,10 @@ function adminBypassClosed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAdmin() {
|
function checkAdmin() {
|
||||||
if (!ADMIN_URL) return;
|
if (!ADMIN_URL) return Promise.resolve();
|
||||||
const headers = {};
|
const headers = {};
|
||||||
if (googleCredential) headers['Authorization'] = 'Bearer ' + googleCredential;
|
if (googleCredential) headers['Authorization'] = 'Bearer ' + googleCredential;
|
||||||
fetch(ADMIN_URL, { headers })
|
return fetch(ADMIN_URL, { headers })
|
||||||
.then(r => r.ok ? r.json() : null)
|
.then(r => r.ok ? r.json() : null)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data && data.weeks) {
|
if (data && data.weeks) {
|
||||||
|
|
@ -520,7 +522,8 @@ function getEasternOffsetMs(date) {
|
||||||
function startCountdown(data) {
|
function startCountdown(data) {
|
||||||
if (data && data.reopen_at) reopenAtMs = data.reopen_at * 1000;
|
if (data && data.reopen_at) reopenAtMs = data.reopen_at * 1000;
|
||||||
updateCountdown();
|
updateCountdown();
|
||||||
setInterval(updateCountdown, 1000);
|
if (countdownTimer !== null) clearInterval(countdownTimer);
|
||||||
|
countdownTimer = setInterval(updateCountdown, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCountdown() {
|
function updateCountdown() {
|
||||||
|
|
@ -561,8 +564,7 @@ async function checkFormStatus() {
|
||||||
formStatusData = data;
|
formStatusData = data;
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
adminBypassClosed();
|
adminBypassClosed();
|
||||||
} else if (CONFIG.authMode === 'google') {
|
} else if (CONFIG.authMode !== 'google' || googleCredential) {
|
||||||
} else {
|
|
||||||
showClosedOverlay();
|
showClosedOverlay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,6 @@ class TestGenerateFormStructural:
|
||||||
blob = cfg_match.group(1)
|
blob = cfg_match.group(1)
|
||||||
# Literal </script> inside the CONFIG script would terminate early.
|
# Literal </script> inside the CONFIG script would terminate early.
|
||||||
assert "</script>" not in blob
|
assert "</script>" not in blob
|
||||||
assert "<\\/script><script>alert(1)<\\/script>" in blob
|
|
||||||
# Parsed JSON still contains the hostile meal name.
|
# Parsed JSON still contains the hostile meal name.
|
||||||
cfg = json.loads(blob)
|
cfg = json.loads(blob)
|
||||||
assert any(
|
assert any(
|
||||||
|
|
@ -281,7 +280,7 @@ class TestGenerateFormPlaywright:
|
||||||
# Reset first card via type-in
|
# Reset first card via type-in
|
||||||
qty = page.locator('[data-meal-index="0"] .qty')
|
qty = page.locator('[data-meal-index="0"] .qty')
|
||||||
qty.fill("-3")
|
qty.fill("-3")
|
||||||
qty.dispatch_event("change")
|
qty.blur()
|
||||||
page.wait_for_function(
|
page.wait_for_function(
|
||||||
"() => document.querySelector('[data-meal-index=\"0\"] .qty').value === '0'"
|
"() => document.querySelector('[data-meal-index=\"0\"] .qty').value === '0'"
|
||||||
)
|
)
|
||||||
|
|
@ -292,7 +291,7 @@ class TestGenerateFormPlaywright:
|
||||||
"""() => {
|
"""() => {
|
||||||
document.querySelectorAll('[data-meal-index] .qty').forEach((input) => {
|
document.querySelectorAll('[data-meal-index] .qty').forEach((input) => {
|
||||||
input.value = '0';
|
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'"
|
"() => document.getElementById('count-display').textContent === '0 meals'"
|
||||||
)
|
)
|
||||||
qty.fill("2")
|
qty.fill("2")
|
||||||
qty.dispatch_event("change")
|
qty.blur()
|
||||||
page.wait_for_function(
|
page.wait_for_function(
|
||||||
"() => document.getElementById('count-display').textContent === '2 meals'"
|
"() => document.getElementById('count-display').textContent === '2 meals'"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue