Align kiosk visitor check-out UI with staff sign-in.
Deploy Ladill Frontdesk / deploy (push) Successful in 48s

Use the same two-step card, mode cards, and badge scan flow before confirming check-out.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-29 09:30:34 +00:00
co-authored by Cursor
parent 96be429d96
commit 4e449bc1eb
2 changed files with 223 additions and 69 deletions
+80 -22
View File
@@ -31,6 +31,7 @@ export function registerKioskFlow(Alpine) {
checkInUrl: config.checkInUrl || '',
checkOutUrl: config.checkOutUrl || '',
checkoutIdentifyMode: 'code',
checkoutFormStep: 1,
checkoutForm: { badge_code: '', qr_token: '' },
checkoutQrScanner: null,
checkoutQrScanning: false,
@@ -191,6 +192,17 @@ export function registerKioskFlow(Alpine) {
async goBack() {
if (this.step === 'checkout_identify') {
await this.stopCheckoutQrScanner(true);
if (this.checkoutFormStep === 2) {
this.checkoutFormStep = 1;
this.errorMessage = '';
if (this.checkoutIdentifyMode === 'badge' && ! this.checkoutQrScanned) {
await this.$nextTick();
await this.startCheckoutQrScanner();
}
this.resetTimer();
return;
}
}
if (this.step === 'staff_identify') {
if (this.staffFormStep === 2) {
@@ -247,6 +259,7 @@ export function registerKioskFlow(Alpine) {
this.result = null;
this.checkoutResult = null;
this.checkoutIdentifyMode = 'code';
this.checkoutFormStep = 1;
this.checkoutForm = { badge_code: '', qr_token: '' };
this.checkoutQrScanned = false;
this.checkoutQrError = '';
@@ -286,18 +299,79 @@ export function registerKioskFlow(Alpine) {
this.step = 'checkout_identify';
this.errorMessage = '';
this.checkoutIdentifyMode = 'code';
this.checkoutFormStep = 1;
this.checkoutForm = { badge_code: '', qr_token: '' };
this.checkoutQrScanned = false;
this.checkoutQrError = '';
this.resetTimer();
},
checkoutIdentifyStepLabel() {
return this.checkoutFormStep === 1 ? 'Identify your visit' : 'Confirm check-out';
},
validateCheckoutIdentifyStep1() {
if (this.checkoutIdentifyMode === 'code') {
if (! String(this.checkoutForm.badge_code || '').trim()) {
this.showError('Please enter your badge code.');
return false;
}
} else if (! String(this.checkoutForm.qr_token || '').trim()) {
this.showError('Scan your badge to continue.');
return false;
}
this.errorMessage = '';
return true;
},
async nextCheckoutIdentifyStep() {
if (! this.validateCheckoutIdentifyStep1()) {
return;
}
await this.stopCheckoutQrScanner(false);
this.checkoutFormStep = 2;
this.errorMessage = '';
this.resetTimer();
},
async prevCheckoutIdentifyStep() {
if (this.checkoutFormStep === 2) {
this.checkoutFormStep = 1;
this.errorMessage = '';
if (this.checkoutIdentifyMode === 'badge' && ! this.checkoutQrScanned) {
await this.$nextTick();
await this.startCheckoutQrScanner();
}
this.resetTimer();
return;
}
await this.goBack();
},
async checkoutIdentify() {
if (this.checkoutFormStep === 1) {
await this.nextCheckoutIdentifyStep();
return;
}
await this.submitCheckOut();
},
async setCheckoutIdentifyMode(mode) {
if (this.checkoutIdentifyMode === mode) {
return;
}
await this.stopCheckoutQrScanner(true);
this.checkoutIdentifyMode = mode;
this.checkoutFormStep = 1;
this.checkoutForm = { badge_code: '', qr_token: '' };
this.checkoutQrError = '';
if (mode === 'badge') {
@@ -323,7 +397,7 @@ export function registerKioskFlow(Alpine) {
},
async startCheckoutQrScanner() {
if (this.checkoutQrScanning || this.checkoutQrScanned || this.checkoutIdentifyMode !== 'badge' || this.step !== 'checkout_identify') {
if (this.checkoutQrScanning || this.checkoutQrScanned || this.checkoutIdentifyMode !== 'badge' || this.step !== 'checkout_identify' || this.checkoutFormStep !== 1) {
return;
}
@@ -349,7 +423,7 @@ export function registerKioskFlow(Alpine) {
);
this.checkoutQrScanning = true;
} catch (e) {
this.checkoutQrError = 'Camera unavailable. Enter your badge code instead.';
this.checkoutQrError = 'Camera unavailable. Use an external scanner or switch to badge code.';
console.error('Checkout QR scanner failed', e);
}
},
@@ -363,12 +437,14 @@ export function registerKioskFlow(Alpine) {
this.checkoutQrScanned = true;
this.errorMessage = '';
await this.stopCheckoutQrScanner(false);
await this.submitCheckOut();
this.checkoutFormStep = 2;
this.resetTimer();
},
async rescanCheckoutBadge() {
this.checkoutQrScanned = false;
this.checkoutForm.qr_token = '';
this.checkoutFormStep = 1;
this.checkoutQrError = '';
await this.$nextTick();
await this.startCheckoutQrScanner();
@@ -395,24 +471,6 @@ export function registerKioskFlow(Alpine) {
}
},
validateCheckoutIdentify() {
if (this.checkoutIdentifyMode === 'code') {
if (! String(this.checkoutForm.badge_code || '').trim()) {
this.showError('Please enter your badge code.');
return false;
}
} else if (! String(this.checkoutForm.qr_token || '').trim()) {
this.showError('Scan your badge QR code to continue.');
return false;
}
this.errorMessage = '';
return true;
},
checkoutCredentials() {
const body = {};
if (this.checkoutIdentifyMode === 'badge' && this.checkoutForm.qr_token.trim()) {
@@ -425,7 +483,7 @@ export function registerKioskFlow(Alpine) {
},
async submitCheckOut() {
if (! this.validateCheckoutIdentify()) {
if (! this.validateCheckoutIdentifyStep1()) {
return;
}