Restyle Queue kiosk to match Frontdesk kiosk design.
Deploy Ladill Queue / deploy (push) Successful in 35s

Welcome screen, choice cards, form progress, and success state now follow the same lobby-tablet visual language with idle reset.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-29 23:31:29 +00:00
co-authored by Cursor
parent 492eec9cda
commit 4a608fedf2
7 changed files with 280 additions and 268 deletions
+73 -35
View File
@@ -39,36 +39,98 @@ function pickVoice(voices, locale, preference) {
export function registerKioskFlow(Alpine) {
Alpine.data('kioskFlow', (config = {}) => ({
step: 'select',
step: 'welcome',
queueId: null,
customerName: '',
customerPhone: '',
ticket: null,
error: null,
loading: false,
idleTimer: null,
countdownInterval: null,
resetCountdown: null,
resetTimer: null,
countdownTimer: null,
issueUrl: config.issueUrl,
csrf: config.csrf,
queues: config.queues ?? [],
collectName: config.collectName ?? false,
collectPhone: config.collectPhone ?? false,
resetSeconds: config.resetSeconds ?? 15,
welcomeMessage: config.welcomeMessage ?? 'Tap a service below to get your ticket',
welcomeMessage: config.welcomeMessage ?? null,
startTimer() {
this.resetIdleTimer();
},
resetIdleTimer() {
clearTimeout(this.idleTimer);
clearInterval(this.countdownInterval);
this.resetCountdown = this.resetSeconds;
this.countdownInterval = setInterval(() => {
this.resetCountdown -= 1;
if (this.resetCountdown <= 0) {
clearInterval(this.countdownInterval);
this.countdownInterval = null;
}
}, 1000);
this.idleTimer = setTimeout(() => this.reset(), this.resetSeconds * 1000);
},
clearIdleTimer() {
clearTimeout(this.idleTimer);
clearInterval(this.countdownInterval);
this.idleTimer = null;
this.countdownInterval = null;
this.resetCountdown = null;
},
beginKiosk() {
this.resetIdleTimer();
if (this.queues.length === 0) {
this.step = 'select';
return;
}
init() {
if (this.queues.length === 1) {
this.queueId = this.queues[0].id;
if (! this.collectName && ! this.collectPhone) {
this.issue();
return;
}
}
this.step = 'select';
},
goBack() {
this.resetIdleTimer();
this.error = null;
if (this.step === 'details') {
this.step = 'select';
return;
}
if (this.step === 'select') {
this.step = 'welcome';
this.queueId = null;
}
},
selectQueue(id) {
this.queueId = id;
this.error = null;
this.resetIdleTimer();
if (! this.collectName && ! this.collectPhone) {
this.issue();
return;
}
@@ -82,6 +144,7 @@ export function registerKioskFlow(Alpine) {
this.loading = true;
this.error = null;
this.resetIdleTimer();
try {
const res = await fetch(this.issueUrl, {
@@ -105,7 +168,7 @@ export function registerKioskFlow(Alpine) {
this.ticket = data.data;
this.step = 'ticket';
this.scheduleReset();
this.resetIdleTimer();
} catch (e) {
this.error = e.message || 'Something went wrong';
} finally {
@@ -113,41 +176,16 @@ export function registerKioskFlow(Alpine) {
}
},
scheduleReset() {
this.clearResetTimers();
this.resetCountdown = this.resetSeconds;
this.countdownTimer = setInterval(() => {
this.resetCountdown -= 1;
if (this.resetCountdown <= 0) {
this.clearResetTimers();
}
}, 1000);
this.resetTimer = setTimeout(() => this.reset(), this.resetSeconds * 1000);
},
clearResetTimers() {
if (this.resetTimer) {
clearTimeout(this.resetTimer);
this.resetTimer = null;
}
if (this.countdownTimer) {
clearInterval(this.countdownTimer);
this.countdownTimer = null;
}
this.resetCountdown = null;
},
reset() {
this.clearResetTimers();
this.step = 'select';
this.queueId = this.queues.length === 1 ? this.queues[0].id : null;
this.clearIdleTimer();
this.step = 'welcome';
this.queueId = null;
this.customerName = '';
this.customerPhone = '';
this.ticket = null;
this.error = null;
this.loading = false;
this.startTimer();
},
formatWait(seconds) {