Deploy Ladill Queue / deploy (push) Successful in 56s
Phases 1–6: tickets, counters, displays, appointments, workflows, rules, analytics, reports, feedback, admin, device API, and Gitea deploy workflow for queue.ladill.com. Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
3.1 KiB
JavaScript
99 lines
3.1 KiB
JavaScript
export function registerKioskFlow(Alpine) {
|
|
Alpine.data('kioskFlow', (config = {}) => ({
|
|
step: 'select',
|
|
queueId: null,
|
|
customerName: '',
|
|
customerPhone: '',
|
|
ticket: null,
|
|
error: null,
|
|
loading: false,
|
|
issueUrl: config.issueUrl,
|
|
csrf: config.csrf,
|
|
|
|
selectQueue(id) {
|
|
this.queueId = id;
|
|
this.step = 'details';
|
|
},
|
|
|
|
async issue() {
|
|
if (! this.queueId) return;
|
|
this.loading = true;
|
|
this.error = null;
|
|
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';
|
|
} catch (e) {
|
|
this.error = e.message || 'Something went wrong';
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
reset() {
|
|
this.step = 'select';
|
|
this.queueId = null;
|
|
this.customerName = '';
|
|
this.customerPhone = '';
|
|
this.ticket = null;
|
|
this.error = null;
|
|
},
|
|
}));
|
|
|
|
Alpine.data('displayBoard', (dataUrl) => ({
|
|
payload: null,
|
|
async poll() {
|
|
try {
|
|
const res = await fetch(dataUrl, { headers: { Accept: 'application/json' } });
|
|
this.payload = await res.json();
|
|
if (this.payload?.announcements?.length && window.speechSynthesis) {
|
|
const msg = this.payload.announcements[0]?.message;
|
|
if (msg) {
|
|
const utter = new SpeechSynthesisUtterance(msg);
|
|
window.speechSynthesis.speak(utter);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('Display poll failed', e);
|
|
}
|
|
},
|
|
init() {
|
|
this.poll();
|
|
setInterval(() => this.poll(), 5000);
|
|
},
|
|
}));
|
|
|
|
Alpine.data('mobileTracker', (pollUrl) => ({
|
|
data: null,
|
|
ahead: 0,
|
|
async poll() {
|
|
try {
|
|
const res = await fetch(pollUrl, { headers: { Accept: 'application/json' } });
|
|
const json = await res.json();
|
|
this.data = json.data;
|
|
this.ahead = json.customers_ahead ?? 0;
|
|
} catch (e) {
|
|
console.error('Tracker poll failed', e);
|
|
}
|
|
},
|
|
init() {
|
|
this.poll();
|
|
setInterval(() => this.poll(), 10000);
|
|
},
|
|
}));
|
|
}
|