From 1f8fe21eaf79edc15a79d34024ad4e10b499fda3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 30 May 2026 17:38:18 +0000 Subject: [PATCH] feat: two-step bar charge flow with full-screen PIN overlay Staff enter amount and note, click Charge; a full-screen patron-facing screen appears showing the charge total, member name, and a large PIN input that auto-focuses. PIN errors stay on the overlay; Cancel returns to the amount form so staff can adjust before handing the device back. https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7 --- static/app.js | 38 +++++++++++++++++++---- static/index.html | 23 +++++++++++--- static/style.css | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/static/app.js b/static/app.js index b8f1e62..9755b5a 100644 --- a/static/app.js +++ b/static/app.js @@ -370,18 +370,44 @@ function clearBarSelection() { barMember = null; document.getElementById('barForm').classList.add('hidden'); document.getElementById('barAmount').value = ''; - document.getElementById('barPin').value = ''; - document.getElementById('barNote').value = ''; + document.getElementById('barNote').value = ''; + hidePinOverlay(); setMsg('barMsg', '', ''); } -async function doCharge() { +function hidePinOverlay() { + document.getElementById('pinOverlay').classList.add('hidden'); + document.getElementById('barPin').value = ''; + setMsg('pinMsg', '', ''); +} + +function cancelPin() { + hidePinOverlay(); + // return focus to amount so staff can adjust + document.getElementById('barAmount').focus(); +} + +function prepareCharge() { + if (!barMember) return; + const amount = toMinor('barAmount'); + if (!amount) { setMsg('barMsg', 'Enter a valid amount.', 'err'); return; } + setMsg('barMsg', '', ''); + // populate overlay + document.getElementById('pinAmount').textContent = + 'Charge: ' + fmtAmount(amount); + document.getElementById('pinMember').textContent = barMember.name; + document.getElementById('barPin').value = ''; + setMsg('pinMsg', '', ''); + document.getElementById('pinOverlay').classList.remove('hidden'); + document.getElementById('barPin').focus(); +} + +async function confirmCharge() { if (!barMember) return; const amount = toMinor('barAmount'); const pin = document.getElementById('barPin').value; const note = document.getElementById('barNote').value.trim(); - if (!amount) { setMsg('barMsg', 'Enter a valid amount.', 'err'); return; } - if (!pin) { setMsg('barMsg', 'PIN required.', 'err'); return; } + if (!pin) { setMsg('pinMsg', 'PIN required.', 'err'); return; } try { const r = await apiFetch('/charge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -390,7 +416,7 @@ async function doCharge() { window.open(`/receipt/${r.entry_id}`, '_blank'); setMsg('barMsg', `Charge complete. New balance: ${r.new_balance_display}`, 'ok'); clearBarSelection(); - } catch (err) { setMsg('barMsg', err.message, 'err'); } + } catch (err) { setMsg('pinMsg', err.message, 'err'); } } // --------------------------------------------------------------------------- diff --git a/static/index.html b/static/index.html index 4463b3c..27951dc 100644 --- a/static/index.html +++ b/static/index.html @@ -165,21 +165,34 @@ -
- - -
- +
+ + +