feat(access): add End Session button to re-lock a tap-in session

A Bolt Card tap loads the holder's card for the whole session, so an
unattended idle menu is transactable by the next person until the 60s
IDLE_LOCK_TIMEOUT fires. Give the holder an explicit re-lock:

- END_SESSION event on `idle`, guarded to the active gate, targets
  `locked` (whose entry already clears the access session + loaded card).
  No-op on a gate-disabled machine that rests at idle.
- endSession() store action; IdleView shows a destructive-styled
  "End Session" button top-right only while accessControl.enabled.
- Tests: END_SESSION re-locks when the gate is active; no-op when off.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
This commit is contained in:
Padreug 2026-08-15 12:30:45 +02:00
commit d35faf1c93
5 changed files with 53 additions and 0 deletions

View file

@ -1639,6 +1639,15 @@ export const useAtmStore = defineStore('atm', () => {
send({ type: 'DEV_UNLOCK' })
}
/**
* End the current tap-in session on demand and re-lock immediately (drops the
* loaded Bolt Card via `locked`'s entry), instead of waiting out the idle
* timeout. No-op unless the gate is active and we're on the idle menu.
*/
function endSession() {
send({ type: 'END_SESSION' })
}
// Convenience methods for common events
function selectCashIn() {
send({ type: 'SELECT_CASH_IN' })
@ -1799,6 +1808,7 @@ export const useAtmStore = defineStore('atm', () => {
grantAccess,
denyAccess,
devUnlock,
endSession,
// Actions
initialize,

View file

@ -183,6 +183,21 @@ function handleCashOut() {
?
</Button>
<!-- End-session button (top-right) — only while the access gate is engaged.
A tap-in loads the holder's Bolt Card for the whole session, so give
them an explicit way to re-lock the moment they're done instead of
relying on the idle timeout (which would leave the card usable by the
next person in the meantime). -->
<Button
v-if="atmStore.accessControl.enabled"
variant="outline"
size="kiosk"
class="absolute top-4 right-4 lg:top-8 lg:right-8 h-14 min-h-0 gap-2 rounded-full border-2 border-destructive/50 px-4 text-sm font-bold text-destructive lg:h-20 lg:px-8 lg:text-2xl"
@click="atmStore.endSession()"
>
🔒 End Session
</Button>
<!-- Debug toggle (only visible when debug bar is hidden, never in production) -->
<Button
v-if="!atmStore.debugMode && atmStore.allowMockFallback"