App Permission and Event Load

User
document.addEventListener('deviceready', function () {
if (cordova.platformId === 'android') {
var permissions = cordova.plugins.permissions;

permissions.checkPermission(permissions.CAMERA, function (status) {
if (!status.hasPermission) {
// Request permission
permissions.requestPermission(
permissions.CAMERA,
function (status) {
if (status.hasPermission) {
navigator.splashscreen.hide(); // Hide splash after permission
initializeApp(); // Proceed with your app
} else {
navigator.app.exitApp(); // Exit if denied
}
},
function () {
navigator.app.exitApp(); // Exit on request error
}
);
} else {
navigator.splashscreen.hide();
initializeApp(); // Permission already granted
}
});
} else {
navigator.splashscreen.hide(); // For browser/iOS fallback
initializeApp();
}
}, false);
function initializeApp() {

window.TOKEN = 'fossettscircustoken';
window.eventId = -1;
window.eventDateId = -1;
window.ApiUrl = 'https://staging.fossettscircus.com/boxoffice/index.php/api/';

var scanConfig = {
preferFrontCamera : false, // iOS and Android
showFlipCameraButton : true, // iOS and Android
showTorchButton : true, // iOS and Android
torchOn: false, // Android, launch with the torch switched on (if available)
saveHistory: false, // Android, save scan history (default false)
prompt : "Place a code inside the scan area", // Android
resultDisplayDuration: 500, // Android, display scanned text for X ms. 0 suppresses it entirely, default 1500
formats : "QR_CODE", // default: all but PDF_417 and RSS_EXPANDED
//orientation : "landscape", // Android only (portrait|landscape), default unset so it rotates with the device
disableAnimations : true, // iOS
disableSuccessBeep: false // iOS and Android
};
$(document).ready(function()
{

$(document).on('page:init','.page[data-name="event-details"]',function (e) {
const page = e.detail;

const title = page.route.query.title || 'No Title';
const location = page.route.query.location || '';
const time = page.route.query.time || '';
const code = page.route.query.code || '';
eventDateId = page.route.query.eventDateId || -1;
eventId = page.route.query.eventId || -1;
console.log(title, location, time, code);
page.$el.find('.event-title').text(title);
page.$el.find('.event-location').text(location);
page.$el.find('.event-time').text(time);
page.$el.find('input[name="code"]').val(code);
var sendCode = function(code,type)
{

if(type=='qr')
f7App.dialog.preloader('Scanning code…');
else
f7App.dialog.preloader('Sending code…');
$('.scan').prop('disabled', true);
$('.manually').prop('disabled', true);
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('Code '+ code +' sent to boxoffice, plase wait…');

$.ajax({
url: `${ApiUrl}scan`,
method: 'POST',
data: {
token: TOKEN,
event_date_id: eventDateId,
order_code: code
},
success: function(result,status,xhr)
{
var apiResult = JSON.parse(result);

if ( apiResult.result )
{
$('#scan-result').removeClass('bg-dark').addClass('bg-success').html(apiResult.message+''+apiResult.seating+''+apiResult.tickets);
f7App.dialog.alert(apiResult.message, 'Scan Failed');
// reset input to set next code
$('[name="code"]').val(eventId +'.'+ eventDateId +'.');
}
else
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html(apiResult.message);
f7App.dialog.alert(apiResult.message, 'Scan Failed');
}
},
error: function(xhr,status,error)
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('Code '+ result.text +' could not be sent to boxoffice, please try again');

f7App.dialog.alert(`${result.text} could not be sent to boxoffice, please try again`, 'Scan Failed');

},
complete: function()
{
$('.scan').prop('disabled', false);
$('.manually').prop('disabled', false);
f7App.dialog.close();
}
});
}

var scanError = function (error)
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html("Scanning failed: " + error);

//f7App.popup.open('#modal-scan-result');
};

var scanSuccess = function(result)
{
if (! result.cancelled )
{
if (eventDateId > -1)
{
sendCode(result.text,'qr');
}
else
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('You must select an event date to scan tickets');
}
}
else
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('Scan was cancelled');
}

//f7App.popup.open('#modal-scan-result');
};
$(document).on('click', '.scan', function()
{
cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});

$(document).on('click', '.manually', function()
{
sendCode($('[name="code"]').val(),'manual');
});
});

// Page: Customer
$(document).on('page:init', '.page[data-name="home"]', function () {

loadEvents();
$(document).on('click', '.reload-events', function()
{
loadEvents();

});

$(document).on('click', '.customer-screen .events .list-group-item', function()
{

// $('.customer-screen .events .list-group-item').removeClass('active');
// $(this).addClass('active');

// $('#scan-info').html($(this).html());

eventId = $(this).attr('data-event-id');
eventDateId = $(this).attr('data-event-date-id');

$('[name="code"]').val(eventId +'.'+ eventDateId +'.');

// first scan on event date click
//cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
// f7App.popup.open('#modal-scan-result');

});

});

// $('ons-button').on('click', function(e) {
// ons.notification.alert('Button is tapped!');
// })

});

}
function loadEvents()
{

f7App.dialog.preloader('Loading events…');
$.ajax({
url: `${ApiUrl}today`,
method: 'POST',
data: {
token: TOKEN
},
success: function(result,status,xhr)
{
console.log(result);
var events = JSON.parse(result);
if (events.result) {

$('div.events').html('');

const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
$.each(events.data, function(i, val)
{
var eventDatetime = new Date(val.event_datetime);
var dateOptions = { year: '2-digit', month: '2-digit', day: '2-digit' };
var timeOptions = { hour12: true, hour: '2-digit', minute: '2-digit' };
var dayname=weekday[eventDatetime.getDay()];
var code=val.event_id +'.'+ val.event_date_id +'.';

$('div.events').append(`

${val.venue_title}-${val.venue_full}
${dayname} at ${eventDatetime.toLocaleDateString('en-GB', dateOptions)} –
${eventDatetime.toLocaleTimeString('en-US', timeOptions)}

0/500

`);

});
}else{
$('div.events').html(`${events.message}`);
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert('Something went wrong. Please try again.', 'Error');
},
complete: function()
{
f7App.dialog.close();
}
});
}
we are using frameqork 7
// Initialize Framework7 app
document.addEventListener('deviceready', function () {
window.f7App = new Framework7({
el: '#app',
name: 'FossettsCircusApp',
theme: 'auto',
routes: [
{
path: '/',
url: 'pages/home.html',
},
{
path: '/event/:id/:dateId/',
url: 'pages/event-details.html',
},

],
});

// Create main view
window.f7App.views.create('.view-main');

},false);
and created an android app but some time event not loads

Assistant
Thanks for sharing the full code. If your event list intermittently **doesn't load in the Android app**, the issue is likely one of the following:

## ✅ **Potential Causes and Fixes**

### 1. **`ApiUrl` not set correctly at runtime**
Sometimes, `initializeApp()` may run **before** `deviceready`, or the `window.ApiUrl` isn’t ready when `loadEvents()` is called.

**Fix: Make sure `initializeApp()` is only called after `deviceready` and `navigator.splashscreen.hide()` completes.** You're already doing that correctly, but just ensure **no code anywhere else calls `loadEvents()` directly** outside `page:init`.

### 2. **Framework7 route/page timing issues**
Your `page:init` for the home page loads the events. But if the Framework7 app hasn't finished initializing, it might not bind the event correctly.

**Fix: Ensure `f7App` is initialized before page events fire. Combine it like this:**

document.addEventListener('deviceready', function () {
  window.f7App = new Framework7({
    el: '#app',
    name: 'FossettsCircusApp',
    theme: 'auto',
    routes: [
      {
        path: '/',
        url: 'pages/home.html',
      },
      {
        path: '/event/:id/:dateId/',
        url: 'pages/event-details.html',
      },
    ],
    on: {
      init: function () {
        // Safe to call initializeApp here
        initializeApp();
      }
    }
  });

  window.f7App.views.create('.view-main');
});

### 3. **Timing: `page:init` might miss first time**
If the `page:init` event for `home` fires **before your jQuery `$(document).ready`**, the event handler might never bind.

**Fix:** Move your `.on('page:init', …)` bindings **outside** `$(document).ready()`.

// This should be outside $(document).ready()
$(document).on('page:init', '.page[data-name="home"]', function () {
  loadEvents();
  $(document).on('click', '.reload-events', function () {
    loadEvents();
  });

  $(document).on('click', '.customer-screen .events .list-group-item', function () {
    eventId     = $(this).attr('data-event-id');
    eventDateId = $(this).attr('data-event-date-id');
    $('[name="code"]').val(eventId + '.' + eventDateId + '.');
  });
});

### 4. **Network errors (no internet or delay)**
If the Android device has slow/no connectivity, your AJAX call will fail.

**Fix:**
– Add a retry mechanism.
– Log `xhr.status` in your `error` callback.
– Also show user-friendly error messages.

error: function(xhr, status, error) {
  console.error('AJAX Error', status, error);
  f7App.dialog.alert('Network error. Please check your connection and try again.', 'Error');
}

### 5. **Events missing in backend response**
Sometimes your backend may return `result: false` or an empty list even though events exist.

**Fix: Temporarily log full JSON for debugging:**

console.log("Raw response:", result);

Also ensure the backend returns `Content-Type: application/json` correctly.

### 6. **Android WebView caching/SSL issues**
On older Android devices, WebView might fail to load `https` APIs properly.

**Fix:**
– Ensure API URL uses HTTPS (you are).
– In `config.xml`, add:

<access origin="*" />
  <allow-navigation href="*" />
  <allow-intent href="http://*/*" />
  <allow-intent href="https://*/*" />

– Add “ in `AndroidManifest.xml`.

### ✅ Suggested Debug Enhancements

Add an explicit loading error alert like this:

success: function(result, status, xhr) {
  try {
    var events = JSON.parse(result);
    if (!events.result) throw new Error(events.message);
    // continue rendering
  } catch (err) {
    console.error('Parse or API error:', err);
    $('div.events').html(`<div class="text-red-500 p-4">${err.message || 'Could not load events'}</div>`);
  }
}

## ✅ Summary of Fixes

| Issue | Fix |
|———————————-|———————————————————————-|
| `page:init` not always firing | Move `$(document).on('page:init',…)` **outside** `$(document).ready` |
| `f7App` not ready | Use `on: { init: … }` when initializing Framework7 |
| Network/API issue | Add better error handling/logging and retry |
| Race conditions or misorder | Ensure `initializeApp()` runs only after `deviceready` & `f7App` |
| Android WebView issues | Check `config.xml` and WebView settings |

Let me know if you’d like a modified/cleaned-up version of your code with all these fixes applied.

User
document.addEventListener('deviceready', function () {
if (cordova.platformId === 'android') {
var permissions = cordova.plugins.permissions;

permissions.checkPermission(permissions.CAMERA, function (status) {
if (!status.hasPermission) {
// Request permission
permissions.requestPermission(
permissions.CAMERA,
function (status) {
if (status.hasPermission) {
navigator.splashscreen.hide(); // Hide splash after permission
initializeApp(); // Proceed with your app
} else {
navigator.app.exitApp(); // Exit if denied
}
},
function () {
navigator.app.exitApp(); // Exit on request error
}
);
} else {
navigator.splashscreen.hide();
initializeApp(); // Permission already granted
}
});
} else {
navigator.splashscreen.hide(); // For browser/iOS fallback
initializeApp();
}
}, false);
function initializeApp() {

window.TOKEN = 'fossettscircustoken';
window.eventId = -1;
window.eventDateId = -1;
window.ApiUrl = 'https://staging.fossettscircus.com/boxoffice/index.php/api/';

var scanConfig = {
preferFrontCamera : false, // iOS and Android
showFlipCameraButton : true, // iOS and Android
showTorchButton : true, // iOS and Android
torchOn: false, // Android, launch with the torch switched on (if available)
saveHistory: false, // Android, save scan history (default false)
prompt : "Place a code inside the scan area", // Android
resultDisplayDuration: 500, // Android, display scanned text for X ms. 0 suppresses it entirely, default 1500
formats : "QR_CODE", // default: all but PDF_417 and RSS_EXPANDED
//orientation : "landscape", // Android only (portrait|landscape), default unset so it rotates with the device
disableAnimations : true, // iOS
disableSuccessBeep: false // iOS and Android
};
$(document).ready(function()
{

$(document).on('page:init','.page[data-name="event-details"]',function (e) {
const page = e.detail;

const title = page.route.query.title || 'No Title';
const location = page.route.query.location || '';
const time = page.route.query.time || '';
const code = page.route.query.code || '';
console.log(title, location, time, code);
eventDateId = page.route.query.eventDateId || -1;
eventId = page.route.query.eventId || -1;
page.$el.find('.event-title').text(title);
page.$el.find('.event-location').text(location);
page.$el.find('.event-time').text(time);
page.$el.find('input[name="code"]').val(code);
var sendCode = function(code,type)
{

if(type=='qr')
f7App.dialog.preloader('Scanning code…');
else
f7App.dialog.preloader('Sending code…');
$('.scan').prop('disabled', true);

$.ajax({
url: `${ApiUrl}staff_scan`,
method: 'POST',
data: {
token: TOKEN,
event_date_id: eventDateId,
order_code: code
},
success: function(result,status,xhr)
{
var apiResult = JSON.parse(result);

if ( apiResult.result )
{
f7App.dialog.alert(apiResult.message, 'Scan Success');

}
else
{
f7App.dialog.alert(apiResult.message, 'Scan Failed');
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert(`${result.text} could not be sent to boxoffice, please try again`, 'Scan Failed');

},
complete: function()
{
$('.scan').prop('disabled', false);
//$('.manually').prop('disabled', false);
f7App.dialog.close();
}
});
}
var scanSuccess = function(result)
{
if (! result.cancelled )
{
if (eventDateId > -1)
{
sendCode(result.text,'qr');
}
else
{
f7App.dialog.alert('You must select an event date to scan tickets', 'Scan Failed');
}
}
else
{
f7App.dialog.alert('Scan was cancelled', 'Scan Failed');
}

//f7App.popup.open('#modal-scan-result');
};
var scanError = function (error)
{
f7App.dialog.alert("Scanning failed: " + error, 'Scan Failed');

//f7App.popup.open('#modal-scan-result');
};

$(document).on('click', '.scan', function()
{
cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});

});

$(document).on('page:init', '.page[data-name="home"]', function () {

});

$(document).on('page:init', '.page[data-name="staff-pay-terminal"]', function () {

$(document).on('input', '#amountInput', function () {
const amount = parseFloat($(this).val());
const btn = $('#chargeBtn');

if (amount > 0) {
btn.removeClass('disabled:opacity-50').addClass('color-green');
btn.text('Charge €' + amount.toFixed(2));
} else {
btn.addClass('disabled:opacity-50').removeClass('color-green');
btn.text('Charge €0.00');
}
});
})

$(document).on('page:init', '.page[data-name="staff-tickets"]', function () {

loadEvents('yes');
$(document).on('click', '.reload-events-staff', function()
{
loadEvents('yes');

});

});
$(document).on('page:init', '.page[data-name="staff-check-orders"]', function () {

});
$(document).on('page:init', '.page[data-name="staff-dashboard"]', function () {

});

});

}
function loadEvents(staff)
{
f7App.dialog.preloader('Loading events…');
$.ajax({
url: `${ApiUrl}today`,
method: 'POST',
data: {
token: TOKEN
},
success: function(result,status,xhr)
{
console.log(result);
var events = JSON.parse(result);
if (events.result) {

$('.customer-screen div.events').html('');
$('.staff-screen div.events').html('');

const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
$.each(events.data, function(i, val)
{
var eventDatetime = new Date(val.event_datetime);
var dateOptions = { year: '2-digit', month: '2-digit', day: '2-digit' };
var timeOptions = { hour12: true, hour: '2-digit', minute: '2-digit' };
var dayname=weekday[eventDatetime.getDay()];
var code=val.event_id +'.'+ val.event_date_id +'.';

$('.staff-screen div.events').append(`

${val.venue_full}
${dayname} at ${eventDatetime.toLocaleDateString('en-GB', dateOptions)} –
${eventDatetime.toLocaleTimeString('en-US', timeOptions)}

0/500

`);

});
}else{
$('.events').html(`${events.message}`);
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert('Something went wrong. Please try again.', 'Error');
},
complete: function()
{
f7App.dialog.close();
}
});
}

above code working fine but When we scan two ticket with in 2 min of time then its show scanning failed ,scanning already progress

Assistant
The issue you're encountering — **“Scanning failed, scanning already in progress”** when scanning two tickets quickly — happens because you're not waiting for the **first scan operation to fully complete** before allowing the second one.

This is likely due to the fact that `.scan()` is being triggered while another is still pending or hasn't fully cleaned up (like dialogs not closed, or buttons not re-enabled).

## ✅ Recommended Fix: Add a scan lock to prevent double triggers

You can solve this cleanly by introducing a **`scanInProgress` lock** that prevents a new scan until the previous one has fully completed.

### ✅ **Solution – Add `scanInProgress` flag**

### 🛠 Add this to your global scope inside `initializeApp()`:

window.scanInProgress = false;

### 🔒 Update your `.scan` click handler like this:

$(document).on('click', '.scan', function () {
    if (scanInProgress) {
        f7App.dialog.alert('Please wait, scanning is still in progress.', 'Hold On');
        return;
    }

    scanInProgress = true; // lock scanning
    cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});

### ✅ Modify `scanSuccess()` and `scanError()` to **reset the lock**

var scanSuccess = function(result) {
    if (!result.cancelled) {
        if (eventDateId > -1) {
            sendCode(result.text, 'qr');
        } else {
            f7App.dialog.alert('You must select an event date to scan tickets', 'Scan Failed');
        }
    } else {
        f7App.dialog.alert('Scan was cancelled', 'Scan Info');
    }

    scanInProgress = false; // release lock
};

var scanError = function(error) {
    f7App.dialog.alert("Scanning failed: " + error, 'Scan Failed');
    scanInProgress = false; // release lock
};

### ✅ Also ensure `sendCode()` releases the lock if it was QR-triggered:

Modify the `complete` callback in your AJAX call inside `sendCode()`:

complete: function() {
    $('.scan').prop('disabled', false);
    f7App.dialog.close();
    
    if (type === 'qr') {
        scanInProgress = false; // only unlock for QR scans
    }
}

### ✅ Optional: Add Visual Feedback During Scan

If you'd like, show a toast or disable the button with a spinner when `scanInProgress = true`.

## ✅ Final Result

– Prevents accidental rapid scanning.
– Ensures only one scan operation is allowed at a time.
– Auto unlocks on success, failure, or cancel.
– Improves user feedback.

Let me know if you’d like the full cleaned-up version of your `initializeApp()` and related handlers with this fix applied.

User
document.addEventListener('deviceready', function () {
if (cordova.platformId === 'android') {
var permissions = cordova.plugins.permissions;

permissions.checkPermission(permissions.CAMERA, function (status) {
if (!status.hasPermission) {
// Request permission
permissions.requestPermission(
permissions.CAMERA,
function (status) {
if (status.hasPermission) {
navigator.splashscreen.hide(); // Hide splash after permission
initializeApp(); // Proceed with your app
} else {
navigator.app.exitApp(); // Exit if denied
}
},
function () {
navigator.app.exitApp(); // Exit on request error
}
);
} else {
navigator.splashscreen.hide();
initializeApp(); // Permission already granted
}
});
} else {
navigator.splashscreen.hide(); // For browser/iOS fallback
initializeApp();
}
}, false);
function initializeApp() {

window.TOKEN = 'fossettscircustoken';
window.eventId = -1;
window.eventDateId = -1;
window.ApiUrl = 'https://staging.fossettscircus.com/boxoffice/index.php/api/';

var scanConfig = {
preferFrontCamera : false, // iOS and Android
showFlipCameraButton : true, // iOS and Android
showTorchButton : true, // iOS and Android
torchOn: false, // Android, launch with the torch switched on (if available)
saveHistory: false, // Android, save scan history (default false)
prompt : "Place a code inside the scan area", // Android
resultDisplayDuration: 500, // Android, display scanned text for X ms. 0 suppresses it entirely, default 1500
formats : "QR_CODE", // default: all but PDF_417 and RSS_EXPANDED
//orientation : "landscape", // Android only (portrait|landscape), default unset so it rotates with the device
disableAnimations : true, // iOS
disableSuccessBeep: false // iOS and Android
};
$(document).ready(function()
{

$(document).on('page:init','.page[data-name="event-details"]',function (e) {
const page = e.detail;

const title = page.route.query.title || 'No Title';
const location = page.route.query.location || '';
const time = page.route.query.time || '';
const code = page.route.query.code || '';
console.log(title, location, time, code);
eventDateId = page.route.query.eventDateId || -1;
eventId = page.route.query.eventId || -1;
page.$el.find('.event-title').text(title);
page.$el.find('.event-location').text(location);
page.$el.find('.event-time').text(time);
page.$el.find('input[name="code"]').val(code);
var sendCode = function(code,type)
{

if(type=='qr')
f7App.dialog.preloader('Scanning code…');
else
f7App.dialog.preloader('Sending code…');
$('.scan').prop('disabled', true);

$.ajax({
url: `${ApiUrl}staff_scan`,
method: 'POST',
data: {
token: TOKEN,
event_date_id: eventDateId,
order_code: code
},
success: function(result,status,xhr)
{
var apiResult = JSON.parse(result);

if ( apiResult.result )
{
f7App.dialog.alert(apiResult.message, 'Scan Success');

}
else
{
f7App.dialog.alert(apiResult.message, 'Scan Failed');
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert(`${result.text} could not be sent to boxoffice, please try again`, 'Scan Failed');

},
complete: function()
{
$('.scan').prop('disabled', false);
//$('.manually').prop('disabled', false);
f7App.dialog.close();
}
});
}
var scanSuccess = function(result)
{
if (! result.cancelled )
{
if (eventDateId > -1)
{
sendCode(result.text,'qr');
}
else
{
f7App.dialog.alert('You must select an event date to scan tickets', 'Scan Failed');
}
}
else
{
f7App.dialog.alert('Scan was cancelled', 'Scan Failed');
}

//f7App.popup.open('#modal-scan-result');
};
var scanError = function (error)
{
f7App.dialog.alert("Scanning failed: " + error, 'Scan Failed');

//f7App.popup.open('#modal-scan-result');
};

$(document).on('click', '.scan', function()
{
cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});

});

$(document).on('page:init', '.page[data-name="home"]', function () {

});

$(document).on('page:init', '.page[data-name="staff-pay-terminal"]', function () {

$(document).on('input', '#amountInput', function () {
const amount = parseFloat($(this).val());
const btn = $('#chargeBtn');

if (amount > 0) {
btn.removeClass('disabled:opacity-50').addClass('color-green');
btn.text('Charge €' + amount.toFixed(2));
} else {
btn.addClass('disabled:opacity-50').removeClass('color-green');
btn.text('Charge €0.00');
}
});
})

$(document).on('page:init', '.page[data-name="staff-tickets"]', function () {

loadEvents('yes');
$(document).on('click', '.reload-events-staff', function()
{
loadEvents('yes');

});

});
$(document).on('page:init', '.page[data-name="staff-check-orders"]', function () {

});
$(document).on('page:init', '.page[data-name="staff-dashboard"]', function () {

});

});

}
function loadEvents(staff)
{
f7App.dialog.preloader('Loading events…');
$.ajax({
url: `${ApiUrl}today`,
method: 'POST',
data: {
token: TOKEN
},
success: function(result,status,xhr)
{
console.log(result);
var events = JSON.parse(result);
if (events.result) {

$('.customer-screen div.events').html('');
$('.staff-screen div.events').html('');

const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
$.each(events.data, function(i, val)
{
var eventDatetime = new Date(val.event_datetime);
var dateOptions = { year: '2-digit', month: '2-digit', day: '2-digit' };
var timeOptions = { hour12: true, hour: '2-digit', minute: '2-digit' };
var dayname=weekday[eventDatetime.getDay()];
var code=val.event_id +'.'+ val.event_date_id +'.';

$('.staff-screen div.events').append(`

${val.venue_full}
${dayname} at ${eventDatetime.toLocaleDateString('en-GB', dateOptions)} –
${eventDatetime.toLocaleTimeString('en-US', timeOptions)}

0/500

`);

});
}else{
$('.events').html(`${events.message}`);
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert('Something went wrong. Please try again.', 'Error');
},
complete: function()
{
f7App.dialog.close();
}
});
}
Adjust in above code also some time framework 7 alert show multiple time

Assistant
Thanks for the complete code. Let’s address both of your concerns effectively:

## ✅ Issue 1: **Scanning twice within 2 minutes triggers “Scanning already in progress”**

This happens because your scan function doesn't block repeated calls until the AJAX call completes.

## ✅ Issue 2: **`f7App.dialog.alert` shows multiple times (alert overlaps)**

This happens because Framework7 allows multiple alerts if called in succession without waiting for the previous one to close.

## ✅ **Fixed and Improved Code Adjustments**

### ✅ Step 1: Add a scan lock globally

Place this **above `$(document).ready()` inside `initializeApp()`**:

let scanInProgress = false;
let alertVisible = false;

### ✅ Step 2: Safe Framework7 Alert Wrapper

Add this helper function inside `initializeApp()`:

function showAlert(message, title = 'Alert') {
    if (alertVisible) return;
    alertVisible = true;
    f7App.dialog.alert(message, title, () => {
        alertVisible = false; // reset after user closes
    });
}

Use `showAlert()` **instead of `f7App.dialog.alert()`** everywhere.

### ✅ Step 3: Updated `.scan` click handler

Inside your `.page[data-name="event-details"]` block, replace this:

$(document).on('click', '.scan', function() {
    cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});

With this:

$(document).on('click', '.scan', function () {
    if (scanInProgress) {
        showAlert('Scan is already in progress. Please wait.', 'Hold On');
        return;
    }

    scanInProgress = true;
    cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});

### ✅ Step 4: Update `scanSuccess`, `scanError`, and `sendCode`

#### ✅ `scanSuccess`:

var scanSuccess = function(result) {
    if (!result.cancelled) {
        if (eventDateId > -1) {
            sendCode(result.text, 'qr');
        } else {
            showAlert('You must select an event date to scan tickets', 'Scan Failed');
            scanInProgress = false;
        }
    } else {
        showAlert('Scan was cancelled', 'Info');
        scanInProgress = false;
    }
};

#### ✅ `scanError`:

var scanError = function(error) {
    showAlert('Scanning failed: ' + error, 'Scan Failed');
    scanInProgress = false;
};

#### ✅ Update `complete` callback in `sendCode()`:

complete: function () {
    $('.scan').prop('disabled', false);
    f7App.dialog.close();
    scanInProgress = false;
}

### ✅ Step 5: Replace all `f7App.dialog.alert(…)` with `showAlert(…)`

For example:

// Instead of this
f7App.dialog.alert(apiResult.message, 'Scan Success');

// Use this
showAlert(apiResult.message, 'Scan Success');

## ✅ Summary of Benefits

| Issue | Fix |
|——|—–|
| Scan triggered too fast | `scanInProgress` flag prevents re-entry |
| Multiple alerts stack | `alertVisible` lock ensures only one alert at a time |
| Cleaner UX | Alerts don't overlap, and scans behave reliably |

Let me know if you'd like the **full rewritten version** of `initializeApp()` with all these changes applied.

User
give me full code
Assistant
Here's your full, optimized `initializeApp()` function with:

– ✅ `scanInProgress` lock to prevent double scans
– ✅ `alertVisible` flag to avoid multiple overlapping alerts
– ✅ All `f7App.dialog.alert` calls replaced with `showAlert()` for safety
– ✅ Cleaned up structure for clarity

### ✅ Full `initializeApp()` Code (Updated)

function initializeApp() {
    window.TOKEN = 'fossettscircustoken';
    window.eventId = -1;
    window.eventDateId = -1;
    window.ApiUrl = 'https://staging.fossettscircus.com/boxoffice/index.php/api/';
    let scanInProgress = false;
    let alertVisible = false;

    const scanConfig = {
        preferFrontCamera: false,
        showFlipCameraButton: true,
        showTorchButton: true,
        torchOn: false,
        saveHistory: false,
        prompt: "Place a code inside the scan area",
        resultDisplayDuration: 500,
        formats: "QR_CODE",
        disableAnimations: true,
        disableSuccessBeep: false
    };

    function showAlert(message, title = 'Alert') {
        if (alertVisible) return;
        alertVisible = true;
        f7App.dialog.alert(message, title, () => {
            alertVisible = false;
        });
    }

    $(document).ready(function () {

        // Event Details Page
        $(document).on('page:init', '.page[data-name="event-details"]', function (e) {
            const page = e.detail;
            const title = page.route.query.title || 'No Title';
            const location = page.route.query.location || '';
            const time = page.route.query.time || '';
            const code = page.route.query.code || '';
            eventDateId = page.route.query.eventDateId || -1;
            eventId = page.route.query.eventId || -1;

            page.$el.find('.event-title').text(title);
            page.$el.find('.event-location').text(location);
            page.$el.find('.event-time').text(time);
            page.$el.find('input[name="code"]').val(code);

            function sendCode(code, type) {
                if (type === 'qr') {
                    f7App.dialog.preloader('Scanning code...');
                } else {
                    f7App.dialog.preloader('Sending code...');
                }

                $('.scan').prop('disabled', true);

                $.ajax({
                    url: `${ApiUrl}staff_scan`,
                    method: 'POST',
                    data: {
                        token: TOKEN,
                        event_date_id: eventDateId,
                        order_code: code
                    },
                    success: function (result, status, xhr) {
                        const apiResult = JSON.parse(result);
                        if (apiResult.result) {
                            showAlert(apiResult.message, 'Scan Success');
                        } else {
                            showAlert(apiResult.message, 'Scan Failed');
                        }
                    },
                    error: function (xhr, status, error) {
                        showAlert('Something went wrong. Please try again.', 'Scan Failed');
                    },
                    complete: function () {
                        $('.scan').prop('disabled', false);
                        f7App.dialog.close();
                        scanInProgress = false;
                    }
                });
            }

            function scanSuccess(result) {
                if (!result.cancelled) {
                    if (eventDateId > -1) {
                        sendCode(result.text, 'qr');
                    } else {
                        showAlert('You must select an event date to scan tickets', 'Scan Failed');
                        scanInProgress = false;
                    }
                } else {
                    showAlert('Scan was cancelled', 'Info');
                    scanInProgress = false;
                }
            }

            function scanError(error) {
                showAlert('Scanning failed: ' + error, 'Scan Failed');
                scanInProgress = false;
            }

            $(document).on('click', '.scan', function () {
                if (scanInProgress) {
                    showAlert('Scan is already in progress. Please wait.', 'Hold On');
                    return;
                }
                scanInProgress = true;
                cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
            });
        });

        // Staff Pay Terminal Page
        $(document).on('page:init', '.page[data-name="staff-pay-terminal"]', function () {
            $(document).on('input', '#amountInput', function () {
                const amount = parseFloat($(this).val());
                const btn = $('#chargeBtn');

                if (amount > 0) {
                    btn.removeClass('disabled:opacity-50').addClass('color-green');
                    btn.text('Charge €' + amount.toFixed(2));
                } else {
                    btn.addClass('disabled:opacity-50').removeClass('color-green');
                    btn.text('Charge €0.00');
                }
            });
        });

        // Staff Tickets Page
        $(document).on('page:init', '.page[data-name="staff-tickets"]', function () {
            loadEvents('yes');
            $(document).on('click', '.reload-events-staff', function () {
                loadEvents('yes');
            });
        });

        // Other page inits (for future use)
        $(document).on('page:init', '.page[data-name="home"]', function () {});
        $(document).on('page:init', '.page[data-name="staff-check-orders"]', function () {});
        $(document).on('page:init', '.page[data-name="staff-dashboard"]', function () {});
    });
}

### ✅ No changes needed in `loadEvents()` unless you want the same alert lock.

Let me know if you'd like me to also apply the `showAlert` improvement in the `loadEvents()` error/success UI, or want debounce for `.reload-events-staff`.

User
document.addEventListener('deviceready', function () {
if (cordova.platformId === 'android') {
var permissions = cordova.plugins.permissions;

permissions.checkPermission(permissions.CAMERA, function (status) {
if (!status.hasPermission) {
// Request permission
permissions.requestPermission(
permissions.CAMERA,
function (status) {
if (status.hasPermission) {
navigator.splashscreen.hide(); // Hide splash after permission
initializeApp(); // Proceed with your app
} else {
navigator.app.exitApp(); // Exit if denied
}
},
function () {
navigator.app.exitApp(); // Exit on request error
}
);
} else {
navigator.splashscreen.hide();
initializeApp(); // Permission already granted
}
});
} else {
navigator.splashscreen.hide(); // For browser/iOS fallback
initializeApp();
}
}, false);
Do above also need change

Assistant
Yes, a **small improvement** can be made to your `deviceready` handler as well — not because it's broken, but to improve **code readability**, **error handling**, and **guaranteed single `initializeApp()` call**.

## ✅ Your current code is functionally correct.
But here's what we can improve:

### 🔍 Issues to consider:

1. **Nested callbacks** (callback hell) – we can flatten it slightly.
2. `navigator.splashscreen.hide()` is called in 3 different places – this could be centralized.
3. `initializeApp()` is scattered inside those conditionals – centralizing it improves maintainability.
4. In rare cases, plugins may not be ready instantly — we can add a fallback using `setTimeout`.

## ✅ Improved version of `deviceready` handler

document.addEventListener('deviceready', function () {
    function startApp() {
        navigator.splashscreen.hide();
        initializeApp();
    }

    if (cordova.platformId === 'android' && cordova.plugins && cordova.plugins.permissions) {
        const permissions = cordova.plugins.permissions;

        permissions.checkPermission(permissions.CAMERA, function (status) {
            if (status.hasPermission) {
                startApp();
            } else {
                permissions.requestPermission(
                    permissions.CAMERA,
                    function (status) {
                        if (status.hasPermission) {
                            startApp();
                        } else {
                            navigator.app.exitApp(); // Exit if permission denied
                        }
                    },
                    function () {
                        navigator.app.exitApp(); // Exit on error
                    }
                );
            }
        });
    } else {
        // iOS, browser, or fallback
        startApp();
    }
}, false);

## ✅ What's better in this version?

| Improvement | Benefit |
|————————–|———|
| `startApp()` function | Centralized `splashscreen.hide()` and `initializeApp()` logic |
| Flat structure | Easier to read, debug, and modify |
| Extra check for `cordova.plugins` | Avoids runtime errors if permissions plugin hasn't loaded yet |
| Ready for fallback | Easier to add retries or `setTimeout` if needed later |

Let me know if you'd like me to integrate this into the complete `initializeApp()` block you’re using, or refactor the whole app boot sequence in one file.

User
// Initialize Framework7 app
document.addEventListener('deviceready', function () {
window.f7App = new Framework7({
el: '#app',
name: 'FossettsCircusApp',
theme: 'auto',
routes: [
{
path: '/',
url: 'pages/home.html',
},
{
path: '/staff-tickets/',
url: 'pages/staff-tickets.html',
},
{
path: '/staff-pay-terminal/',
url: 'pages/staff-pay-terminal.html',
},
{
path: '/staff-check-orders/',
url: 'pages/staff-check-orders.html',
},
{
path: '/event/:id/:dateId/',
url: 'pages/event-details.html',
},

],
});

// Create main view
window.f7App.views.create('.view-main');

},false);

Assistant
Your current Framework7 app initialization is technically correct. However, given that you're already initializing Framework7 inside a `deviceready` event and you're calling `initializeApp()` separately, **you should merge these safely** to avoid duplication or race conditions.

### ✅ Problem:

You're now using **two separate `document.addEventListener('deviceready', …` blocks**:

1. One for permissions + `initializeApp()`
2. One for Framework7 initialization

### ❗️This can cause **race conditions**:
– If the F7 app is not ready before `initializeApp()` runs (especially if `f7App.dialog.alert()` is used), it might throw an error like `f7App is undefined`.

### ✅ Unified, clean version:

Below is a **merged version** that ensures:

– Framework7 is initialized **before** `initializeApp()` runs
– Camera permissions are handled properly
– Only a **single `deviceready` listener**

### ✅ Full Combined Code:

document.addEventListener('deviceready', function () {
  // Initialize Framework7 first
  window.f7App = new Framework7({
    el: '#app',
    name: 'FossettsCircusApp',
    theme: 'auto',
    routes: [
      { path: '/', url: 'pages/home.html' },
      { path: '/staff-tickets/', url: 'pages/staff-tickets.html' },
      { path: '/staff-pay-terminal/', url: 'pages/staff-pay-terminal.html' },
      { path: '/staff-check-orders/', url: 'pages/staff-check-orders.html' },
      { path: '/event/:id/:dateId/', url: 'pages/event-details.html' }
    ]
  });

  // Create the main view
  window.f7App.views.create('.view-main');

  // Helper to hide splash and start the app
  function startApp() {
    navigator.splashscreen.hide();
    initializeApp(); // Your custom logic goes here
  }

  // Handle Android camera permission
  if (cordova.platformId === 'android' && cordova.plugins && cordova.plugins.permissions) {
    const permissions = cordova.plugins.permissions;

    permissions.checkPermission(permissions.CAMERA, function (status) {
      if (status.hasPermission) {
        startApp();
      } else {
        permissions.requestPermission(
          permissions.CAMERA,
          function (status) {
            if (status.hasPermission) {
              startApp();
            } else {
              navigator.app.exitApp();
            }
          },
          function () {
            navigator.app.exitApp();
          }
        );
      }
    });
  } else {
    // For iOS / browser fallback
    startApp();
  }
});

### ✅ Benefits:

– Only **one `deviceready` block**
– Framework7 is always initialized **before `initializeApp()`**
– No redundant logic or duplicated splash hide calls
– Safe for Android + iOS + browser fallback

Let me know if you'd like me to inject this directly into your full `index.js` or main app file structure.

User
document.addEventListener('deviceready', function () {
if (cordova.platformId === 'android') {
var permissions = cordova.plugins.permissions;

permissions.checkPermission(permissions.CAMERA, function (status) {
if (!status.hasPermission) {
// Request permission
permissions.requestPermission(
permissions.CAMERA,
function (status) {
if (status.hasPermission) {
navigator.splashscreen.hide(); // Hide splash after permission
initializeApp(); // Proceed with your app
} else {
navigator.app.exitApp(); // Exit if denied
}
},
function () {
navigator.app.exitApp(); // Exit on request error
}
);
} else {
navigator.splashscreen.hide();
initializeApp(); // Permission already granted
}
});
} else {
navigator.splashscreen.hide(); // For browser/iOS fallback
initializeApp();
}
}, false);
function initializeApp() {

window.TOKEN = 'fossettscircustoken';
window.eventId = -1;
window.eventDateId = -1;
window.ApiUrl = 'https://staging.fossettscircus.com/boxoffice/index.php/api/';

var scanConfig = {
preferFrontCamera : false, // iOS and Android
showFlipCameraButton : true, // iOS and Android
showTorchButton : true, // iOS and Android
torchOn: false, // Android, launch with the torch switched on (if available)
saveHistory: false, // Android, save scan history (default false)
prompt : "Place a code inside the scan area", // Android
resultDisplayDuration: 500, // Android, display scanned text for X ms. 0 suppresses it entirely, default 1500
formats : "QR_CODE", // default: all but PDF_417 and RSS_EXPANDED
//orientation : "landscape", // Android only (portrait|landscape), default unset so it rotates with the device
disableAnimations : true, // iOS
disableSuccessBeep: false // iOS and Android
};

$(document).on('page:init','.page[data-name="event-details"]',function (e) {
const page = e.detail;

const title = page.route.query.title || 'No Title';
const location = page.route.query.location || '';
const time = page.route.query.time || '';
const code = page.route.query.code || '';
eventDateId = page.route.query.eventDateId || -1;
eventId = page.route.query.eventId || -1;
console.log(title, location, time, code);
page.$el.find('.event-title').text(title);
page.$el.find('.event-location').text(location);
page.$el.find('.event-time').text(time);
page.$el.find('input[name="code"]').val(code);
var sendCode = function(code,type)
{

if(type=='qr')
f7App.dialog.preloader('Scanning code…');
else
f7App.dialog.preloader('Sending code…');
$('.scan').prop('disabled', true);
$('.manually').prop('disabled', true);
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('Code '+ code +' sent to boxoffice, plase wait…');

$.ajax({
url: `${ApiUrl}scan`,
method: 'POST',
data: {
token: TOKEN,
event_date_id: eventDateId,
order_code: code
},
success: function(result,status,xhr)
{
var apiResult = JSON.parse(result);

if ( apiResult.result )
{
$('#scan-result').removeClass('bg-dark').addClass('bg-success').html(apiResult.message+''+apiResult.seating+''+apiResult.tickets);
f7App.dialog.alert(apiResult.message, 'Scan Failed');
// reset input to set next code
$('[name="code"]').val(eventId +'.'+ eventDateId +'.');
}
else
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html(apiResult.message);
f7App.dialog.alert(apiResult.message, 'Scan Failed');
}
},
error: function(xhr,status,error)
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('Code '+ result.text +' could not be sent to boxoffice, please try again');

f7App.dialog.alert(`${result.text} could not be sent to boxoffice, please try again`, 'Scan Failed');

},
complete: function()
{
$('.scan').prop('disabled', false);
$('.manually').prop('disabled', false);
f7App.dialog.close();
}
});
}

var scanError = function (error)
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html("Scanning failed: " + error);

//f7App.popup.open('#modal-scan-result');
};

var scanSuccess = function(result)
{
if (! result.cancelled )
{
if (eventDateId > -1)
{
sendCode(result.text,'qr');
}
else
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('You must select an event date to scan tickets');
}
}
else
{
$('#scan-result').removeClass('bg-success').addClass('bg-dark').html('Scan was cancelled');
}

//f7App.popup.open('#modal-scan-result');
};
$(document).on('click', '.scan', function()
{
cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
});

$(document).on('click', '.manually', function()
{
sendCode($('[name="code"]').val(),'manual');
});
});

// Page: Customer
$(document).on('page:init', '.page[data-name="home"]', function () {

loadEvents();
$(document).on('click', '.reload-events', function()
{
loadEvents();

});

});

}
function loadEvents()
{

f7App.dialog.preloader('Loading events…');
$.ajax({
url: `${ApiUrl}today`,
method: 'POST',
data: {
token: TOKEN
},
success: function(result,status,xhr)
{
console.log(result);
var events = JSON.parse(result);
if (events.result) {

$('div.events').html('');

const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
$.each(events.data, function(i, val)
{
var eventDatetime = new Date(val.event_datetime);
var dateOptions = { year: '2-digit', month: '2-digit', day: '2-digit' };
var timeOptions = { hour12: true, hour: '2-digit', minute: '2-digit' };
var dayname=weekday[eventDatetime.getDay()];
var code=val.event_id +'.'+ val.event_date_id +'.';

$('div.events').append(`

${val.venue_title}-${val.venue_full}
${dayname} at ${eventDatetime.toLocaleDateString('en-GB', dateOptions)} –
${eventDatetime.toLocaleTimeString('en-US', timeOptions)}

0/500

`);

});
}else{
$('div.events').html(`${events.message}`);
}
},
error: function(xhr,status,error)
{
f7App.dialog.alert('Something went wrong. Please try again.', 'Error');
},
complete: function()
{
f7App.dialog.close();
}
});
}
Also make for above

f7inti
// Initialize Framework7 app
document.addEventListener('deviceready', function () {
window.f7App = new Framework7({
el: '#app',
name: 'FossettsCircusApp',
theme: 'auto',
routes: [
{
path: '/',
url: 'pages/home.html',
},
{
path: '/event/:id/:dateId/',
url: 'pages/event-details.html',
},

],
});

// Create main view
window.f7App.views.create('.view-main');

},false);

Assistant
User is building a Cordova app with Framework7 that includes scanning functionality, event detail pages, and permissions handling for Android camera access. The app uses jQuery for event handling and dynamic content injection.
Assistant
Here’s the **fully optimized and unified version** of your Cordova + Framework7 app setup:

### ✅ Key Improvements

– Combined Framework7 and `initializeApp()` inside a **single `deviceready` block**
– Added **`scanInProgress`** and **`alertVisible`** to prevent repeated scans and overlapping alerts
– Centralized **alert logic** with `showAlert()`
– Cleaned up **event bindings** and comments for maintainability

### ✅ Final, Clean Version

document.addEventListener('deviceready', function () {
  // Initialize Framework7
  window.f7App = new Framework7({
    el: '#app',
    name: 'FossettsCircusApp',
    theme: 'auto',
    routes: [
      { path: '/', url: 'pages/home.html' },
      { path: '/event/:id/:dateId/', url: 'pages/event-details.html' }
    ]
  });

  // Create main view
  window.f7App.views.create('.view-main');

  // Start the app after permissions
  function startApp() {
    navigator.splashscreen.hide();
    initializeApp();
  }

  // Handle camera permissions on Android
  if (cordova.platformId === 'android' && cordova.plugins && cordova.plugins.permissions) {
    const permissions = cordova.plugins.permissions;
    permissions.checkPermission(permissions.CAMERA, function (status) {
      if (status.hasPermission) {
        startApp();
      } else {
        permissions.requestPermission(
          permissions.CAMERA,
          function (status) {
            status.hasPermission ? startApp() : navigator.app.exitApp();
          },
          function () {
            navigator.app.exitApp();
          }
        );
      }
    });
  } else {
    startApp(); // For iOS or browser
  }
}, false);

### ✅ `initializeApp()` Function

function initializeApp() {
  window.TOKEN = 'fossettscircustoken';
  window.eventId = -1;
  window.eventDateId = -1;
  window.ApiUrl = 'https://staging.fossettscircus.com/boxoffice/index.php/api/';
  let scanInProgress = false;
  let alertVisible = false;

  const scanConfig = {
    preferFrontCamera: false,
    showFlipCameraButton: true,
    showTorchButton: true,
    torchOn: false,
    saveHistory: false,
    prompt: "Place a code inside the scan area",
    resultDisplayDuration: 500,
    formats: "QR_CODE",
    disableAnimations: true,
    disableSuccessBeep: false
  };

  function showAlert(message, title = 'Alert') {
    if (alertVisible) return;
    alertVisible = true;
    f7App.dialog.alert(message, title, () => {
      alertVisible = false;
    });
  }

  // Event Details Page
  $(document).on('page:init', '.page[data-name="event-details"]', function (e) {
    const page = e.detail;
    const title = page.route.query.title || 'No Title';
    const location = page.route.query.location || '';
    const time = page.route.query.time || '';
    const code = page.route.query.code || '';
    eventDateId = page.route.query.eventDateId || -1;
    eventId = page.route.query.eventId || -1;

    page.$el.find('.event-title').text(title);
    page.$el.find('.event-location').text(location);
    page.$el.find('.event-time').text(time);
    page.$el.find('input[name="code"]').val(code);

    function sendCode(code, type) {
      f7App.dialog.preloader(type === 'qr' ? 'Scanning code...' : 'Sending code...');
      $('.scan').prop('disabled', true);
      $('.manually').prop('disabled', true);

      $('#scan-result').removeClass('bg-success').addClass('bg-dark')
        .html(`Code <u class="bg-primary text-white p-1">${code}</u> sent to boxoffice, please wait...`);

      $.ajax({
        url: `${ApiUrl}scan`,
        method: 'POST',
        data: {
          token: TOKEN,
          event_date_id: eventDateId,
          order_code: code
        },
        success: function (result) {
          const apiResult = JSON.parse(result);
          if (apiResult.result) {
            $('#scan-result').removeClass('bg-dark').addClass('bg-success')
              .html(`${apiResult.message}<br>${apiResult.seating}<br>${apiResult.tickets}`);
            showAlert(apiResult.message, 'Scan Success');
            $('[name="code"]').val(`${eventId}.${eventDateId}.`);
          } else {
            $('#scan-result').removeClass('bg-success').addClass('bg-dark')
              .html(apiResult.message);
            showAlert(apiResult.message, 'Scan Failed');
          }
        },
        error: function () {
          $('#scan-result').removeClass('bg-success').addClass('bg-dark')
            .html(`Code <u class="bg-primary text-white p-1">${code}</u> could not be sent to boxoffice, please try again`);
          showAlert('Scan failed. Please try again.', 'Scan Failed');
        },
        complete: function () {
          $('.scan').prop('disabled', false);
          $('.manually').prop('disabled', false);
          f7App.dialog.close();
          scanInProgress = false;
        }
      });
    }

    function scanSuccess(result) {
      if (!result.cancelled) {
        if (eventDateId > -1) {
          sendCode(result.text, 'qr');
        } else {
          $('#scan-result').removeClass('bg-success').addClass('bg-dark')
            .html('You must select an event date to scan tickets');
        }
      } else {
        $('#scan-result').removeClass('bg-success').addClass('bg-dark')
          .html('Scan was cancelled');
      }
      scanInProgress = false;
    }

    function scanError(error) {
      $('#scan-result').removeClass('bg-success').addClass('bg-dark')
        .html(`Scanning failed: ${error}`);
      showAlert(`Scanning failed: ${error}`, 'Scan Error');
      scanInProgress = false;
    }

    $(document).on('click', '.scan', function () {
      if (scanInProgress) {
        showAlert('Scan is already in progress. Please wait.', 'Please Wait');
        return;
      }
      scanInProgress = true;
      cordova.plugins.barcodeScanner.scan(scanSuccess, scanError, scanConfig);
    });

    $(document).on('click', '.manually', function () {
      sendCode($('[name="code"]').val(), 'manual');
    });
  });

  // Home Page
  $(document).on('page:init', '.page[data-name="home"]', function () {
    loadEvents();
    $(document).on('click', '.reload-events', function () {
      loadEvents();
    });
  });
}

### ✅ `loadEvents()` (unchanged but compatible)

Your existing `loadEvents()` function works fine with this setup. If you'd like to use `showAlert()` in it too for consistency, let me know.

Let me know if you'd also like this split into modular files (like `f7-init.js`, `events.js`, etc.) for better maintainability.

Leave a Comment

Your email address will not be published. Required fields are marked *