Add self-service kiosk for queue ticket issuance.
Deploy Ladill Queue / deploy (push) Successful in 36s
Deploy Ladill Queue / deploy (push) Successful in 36s
Customers can pick a service, optionally enter details, and receive a ticket on a branded touchscreen flow with admin-configurable queues and auto-reset. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -46,18 +46,43 @@ export function registerKioskFlow(Alpine) {
|
||||
ticket: null,
|
||||
error: null,
|
||||
loading: false,
|
||||
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',
|
||||
|
||||
init() {
|
||||
if (this.queues.length === 1) {
|
||||
this.queueId = this.queues[0].id;
|
||||
}
|
||||
},
|
||||
|
||||
selectQueue(id) {
|
||||
this.queueId = id;
|
||||
this.error = null;
|
||||
|
||||
if (! this.collectName && ! this.collectPhone) {
|
||||
this.issue();
|
||||
return;
|
||||
}
|
||||
|
||||
this.step = 'details';
|
||||
},
|
||||
|
||||
async issue() {
|
||||
if (! this.queueId) return;
|
||||
if (! this.queueId || this.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const res = await fetch(this.issueUrl, {
|
||||
method: 'POST',
|
||||
@@ -73,9 +98,14 @@ export function registerKioskFlow(Alpine) {
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (! res.ok) throw new Error(data.message || 'Could not issue ticket');
|
||||
|
||||
if (! res.ok) {
|
||||
throw new Error(data.message || 'Could not issue ticket');
|
||||
}
|
||||
|
||||
this.ticket = data.data;
|
||||
this.step = 'ticket';
|
||||
this.scheduleReset();
|
||||
} catch (e) {
|
||||
this.error = e.message || 'Something went wrong';
|
||||
} finally {
|
||||
@@ -83,13 +113,51 @@ 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 = null;
|
||||
this.queueId = this.queues.length === 1 ? this.queues[0].id : null;
|
||||
this.customerName = '';
|
||||
this.customerPhone = '';
|
||||
this.ticket = null;
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
formatWait(seconds) {
|
||||
if (! seconds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const minutes = Math.ceil(seconds / 60);
|
||||
|
||||
return minutes <= 1 ? 'About 1 minute' : `About ${minutes} minutes`;
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user