Kiosk: self-heal a busy device on fresh page load
A browser refresh mid-checkin bypasses the Cancel button entirely (no React unmount cleanup runs), so a device stayed locked 'busy' with no route back except a manual admin reset — hit for real 2026-09-01. registerAndGoIdle() only runs on a genuine fresh mount, so if the device comes back 'busy' at exactly that moment, nothing on this browser is actually mid-checkin (state was just initialized) — self-correct via the same reset the Cancel button already uses, rather than trusting stale server-side status. Uses kioskReset (krequest, throws KioskSessionError on 401) not resetDevice (request, redirects to the staff /login page on 401) — the latter would be wrong on a kiosk tablet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
f4625e0860
commit
82f926d736
1 changed files with 20 additions and 6 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
import { UserCheck } from 'lucide-react'
|
||||
import { kioskLogin, kioskRegisterDevice, kioskHeartbeat, kioskStreamUrl, KioskSessionError } from '../../api'
|
||||
import { kioskLogin, kioskRegisterDevice, kioskHeartbeat, kioskStreamUrl, kioskReset, KioskSessionError } from '../../api'
|
||||
import type { Device } from '../../types'
|
||||
import CheckinFlow from './CheckinFlow'
|
||||
|
||||
|
|
@ -48,12 +48,26 @@ export default function Kiosk() {
|
|||
const storedId = localStorage.getItem(DEVICE_ID_KEY)
|
||||
const d = await kioskRegisterDevice(storedId)
|
||||
localStorage.setItem(DEVICE_ID_KEY, d.device_id)
|
||||
|
||||
// This effect only runs on a genuine fresh mount (empty-deps useEffect
|
||||
// below) — a browser refresh, crash, or power cycle. If the device
|
||||
// comes back 'busy' at exactly this moment, nothing on THIS browser
|
||||
// is actually mid-checkin (React state was just initialized), so
|
||||
// whatever 'busy' the server thinks is stale by definition — the
|
||||
// in-flow Cancel button only fires its own reset on a clean unmount,
|
||||
// which a raw page refresh never triggers at all. Self-correct here
|
||||
// rather than leaving the device invisibly locked with no route back
|
||||
// except a manual admin reset (hit for real 2026-09-01, see
|
||||
// project_checkin memory — refreshing mid-checkin instead of using
|
||||
// Cancel). Best-effort: even if the reset call itself fails, still
|
||||
// show idle locally so the tablet is at least usable again once
|
||||
// staff/an admin clears it some other way.
|
||||
if (d.status === 'busy') {
|
||||
await kioskReset(d.device_id).catch(() => {})
|
||||
d.status = 'idle'
|
||||
}
|
||||
|
||||
setDevice(d)
|
||||
// Always land on the idle screen regardless of the device's last known
|
||||
// status — if it was left 'busy' from a session that never completed
|
||||
// (tablet lost power mid-checkin), the idle screen's own SSE
|
||||
// connection is what would receive a fresh 'navigate' push anyway, and
|
||||
// a stuck busy device is fixed from the Devices admin page, not here.
|
||||
setView('idle')
|
||||
} catch (err) {
|
||||
if (err instanceof KioskSessionError) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue