Files
isaaccladandCursor a3839da869
Deploy Ladill Care / deploy (push) Successful in 39s
Add queue kiosk and display devices for Care Queue management.
Register walk-up kiosks and waiting-room display devices under Devices, with public kiosk ticket issue and linked TV boards surfaced in sidebar and Queue shortcuts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-20 10:59:02 +00:00

163 lines
4.5 KiB
JavaScript

export function registerCareKiosk(Alpine) {
Alpine.data('careKioskFlow', (config = {}) => ({
step: 'welcome',
queueId: null,
customerName: '',
customerPhone: '',
ticket: null,
error: null,
loading: false,
idleTimer: null,
countdownInterval: null,
resetCountdown: 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 ?? 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;
}
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;
}
this.step = 'details';
},
async issue() {
if (! this.queueId || this.loading) {
return;
}
this.loading = true;
this.error = null;
this.resetIdleTimer();
try {
const res = await fetch(this.issueUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
'X-CSRF-TOKEN': this.csrf,
},
body: JSON.stringify({
queue_id: this.queueId,
customer_name: this.customerName || null,
customer_phone: this.customerPhone || null,
}),
});
const data = await res.json();
if (! res.ok) {
throw new Error(data.message || 'Could not issue ticket');
}
this.ticket = data.data;
this.step = 'ticket';
this.resetIdleTimer();
} catch (e) {
this.error = e.message || 'Something went wrong';
} finally {
this.loading = false;
}
},
reset() {
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) {
if (! seconds) {
return null;
}
const minutes = Math.ceil(seconds / 60);
return minutes <= 1 ? 'About 1 minute' : `About ${minutes} minutes`;
},
}));
}