Add queue kiosk and display devices for Care Queue management.
Deploy Ladill Care / deploy (push) Successful in 39s
Deploy Ladill Care / deploy (push) Successful in 39s
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>
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import Alpine from 'alpinejs';
|
||||
import { registerLadillClipboard } from './ladill-clipboard';
|
||||
import { registerCareDisplay } from './care-display';
|
||||
import { registerCareKiosk } from './care-kiosk';
|
||||
import collapse from '@alpinejs/collapse';
|
||||
|
||||
Alpine.plugin(collapse);
|
||||
registerLadillClipboard(Alpine);
|
||||
registerCareDisplay(Alpine);
|
||||
registerCareKiosk(Alpine);
|
||||
|
||||
// In-app notification bell + dropdown.
|
||||
Alpine.data('notificationDropdown', (config = {}) => ({
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
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`;
|
||||
},
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user