F7 List Item Navigation

User
Framework 7 on click on list open new page with in place of modal
Assistant
To open a new **Framework7 page** (not a modal) **when clicking on a list item**, you can use the built-in `f7` router by defining `path` and `url` or `componentUrl` in your `routes` array, then using an “ tag with the appropriate `href`.

### βœ… Example Setup

#### 1. **Define a route in `f7-init.js`**

var f7App = new Framework7({
  el: '#app',
  name: 'MyApp',
  theme: 'auto',
  routes: [
    {
      path: '/event-details/',
      url: 'pages/event-details.html', // This is your HTML page
    },
  ],
});

#### 2. **List Item Click to Open Page**
In your page HTML (e.g., `home.html`):

<div class="list">
  <ul>
    <li>
      <a href="/event-details/" class="item-link item-content">
        <div class="item-inner">
          <div class="item-title">View Event</div>
        </div>
      </a>
    </li>
  </ul>
</div>

> βœ… This will open `event-details.html` as a **new page**, not as a modal.

### 🧠 Notes:
– Make sure your “ is present and wraps your pages.
– All pages should be inside `.views` and `.view-main` to work with F7 router transitions.
– Don’t use jQuery `.on('click')` unless you prevent default and manually navigate using `f7App.views.main.router.navigate()`.

### Optional: JavaScript navigation
If you want to open the page via a jQuery click handler:

$(document).on('click', '.open-event-page', function () {
  f7App.views.main.router.navigate('/event-details/');
});

HTML:

<li><a href="#" class="item-link item-content open-event-page">
  <div class="item-inner">
    <div class="item-title">View Event</div>
  </div>
</a></li>

Let me know if you want to pass dynamic data or parameters to the new page.

User
List is

test
Sunday at 29/06/25 –
05:00 PM

0/500

like this I want also display data in detail page

Assistant
Thanks, Neeraj. Based on your layout and Framework7 + jQuery integration (as per previous context), here's how you can make clicking your `.event-item` open a new Framework7 **page** with **event detail**, and pass the data (`event-id`, `event-date-id`, title, etc.) dynamically.

## βœ… Step-by-Step Implementation

### πŸ”Ή 1. Define a route for the event detail page

In your `routes` config:

routes: [
  {
    path: '/event/:id/:dateId/',
    componentUrl: 'pages/event-details.html',
  },
],

### πŸ”Ή 2. HTML for the list item (as-is, add class and data)

Add a class like `.open-event` to your `.event-item`:

<div class="space-y-4 mb-4 event-item list-group-item active open-event"
     data-event-id="381"
     data-event-date-id="2319"
     data-title="test"
     data-time="Sunday at 29/06/25 - 05:00 PM"
     data-capacity="0/500">
  <div class="rounded-lg border border-border/40 card bg-background hover:shadow-lg transition-shadow cursor-pointer">
    <div class="p-6">
      <div class="flex justify-between items-center">
        <div>
          <h3 class="text-xl font-semibold">test</h3>
          <p class="text-gray-600">Sunday at 29/06/25 - 05:00 PM</p>
        </div>
        <div class="text-right">
          <div class="inline-flex items-center rounded-full border font-semibold transition-all px-2.5 py-0.5 text-xs">
            0/500
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

### πŸ”Ή 3. JavaScript to handle the click and navigate

$(document).on('click', '.open-event', function () {
  const eventId = $(this).data('event-id');
  const dateId = $(this).data('event-date-id');
  const title = $(this).data('title');
  const time = $(this).data('time');
  const capacity = $(this).data('capacity');

  // Pass data via route props
  f7.views.main.router.navigate(`/event/${eventId}/${dateId}/`, {
    props: {
      title,
      time,
      capacity,
    },
  });
});

### πŸ”Ή 4. Event Details Page (event-details.html)

<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-inner sliding">
        <div class="title">Event Details</div>
      </div>
    </div>
    <div class="page-content">
      <div class="block">
        <h2 class="text-2xl font-bold">{{title}}</h2>
        <p class="text-gray-600">{{time}}</p>
        <p class="mt-2 text-sm">Capacity: {{capacity}}</p>
      </div>
    </div>
  </div>
</template>

<script>
  export default (props, { $f7 }) => {
    return {
      data() {
        return {
          title: props.title,
          time: props.time,
          capacity: props.capacity,
        };
      },
    };
  };
</script>

## 🧠 Summary

– Your `.event-item` contains the data in `data-*` attributes.
– On click, you grab the data and route to `/event/:id/:dateId/`.
– Framework7 passes the props to the detail page.
– Detail page shows the dynamic content.

Let me know if you want to also fetch full event data from API using the ID.

User
I want to give href
Assistant
Perfect β€” if you prefer using an `href` on the element instead of handling the click in JavaScript, Framework7 supports dynamic routing using URL parameters, and you can pass some data via query string or params.

## βœ… Step-by-Step with `href`

### πŸ”Ή 1. Update your `routes` in `f7-init.js`

routes: [
  {
    path: '/event/:id/:dateId/',
    componentUrl: 'pages/event-details.html',
  },
],

### πŸ”Ή 2. Update your List Item HTML with `href`

You can wrap your `.event-item` with an “ tag like this:

<a href="/event/381/2319/?title=test&time=Sunday%20at%2029/06/25%20-%2005:00%20PM&capacity=0/500"
   class="event-link">
  <div class="space-y-4 mb-4 list-group-item active">
    <div class="rounded-lg border border-border/40 card bg-background hover:shadow-lg transition-shadow cursor-pointer">
      <div class="p-6">
        <div class="flex justify-between items-center">
          <div>
            <h3 class="text-xl font-semibold">test</h3>
            <p class="text-gray-600">Sunday at 29/06/25 - 05:00 PM</p>
          </div>
          <div class="text-right">
            <div class="inline-flex items-center rounded-full border font-semibold transition-all px-2.5 py-0.5 text-xs">
              0/500
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</a>

> βœ… This will navigate like a normal link using Framework7's router. Make sure it’s inside a `.view`.

### πŸ”Ή 3. `event-details.html` to Read URL and Query Params

<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-inner sliding">
        <div class="title">Event Details</div>
      </div>
    </div>
    <div class="page-content">
      <div class="block">
        <h2 class="text-2xl font-bold">{{title}}</h2>
        <p class="text-gray-600">{{time}}</p>
        <p class="mt-2 text-sm">Capacity: {{capacity}}</p>
      </div>
    </div>
  </div>
</template>

<script>
  export default (props, { $f7route }) => {
    return {
      data() {
        return {
          title: $f7route.query.title,
          time: $f7route.query.time,
          capacity: $f7route.query.capacity,
        };
      },
    };
  };
</script>

### 🧠 Tip for Dynamic Data

If you want to fetch full event info based only on `event-id`, then just pass that in `href="/event/381/2319/"`, and in the `event-details.html`, fetch details using Ajax.

Would you like that version too β€” fetching event details from API based on the ID only?

User
VM935:1 Uncaught SyntaxError: Failed to execute 'appendChild' on 'Node': Unexpected token 'export'
at n.append (dom7.esm.js:831:17)
at Ta (parse-component.js:79:13)
at Object.parse (component.js:29:16)
at component-loader.js:140:51
append @ dom7.esm.js:831
Ta @ parse-component.js:79
parse @ component.js:29
(anonymous) @ component-loader.js:140
Promise.then
componentLoader @ component-loader.js:139
pageComponentLoader @ component-loader.js:186
Ot @ navigate.js:739
(anonymous) @ navigate.js:871
h @ navigate.js:868
(anonymous) @ navigate.js:987
n @ process-route-queue.js:62
Tt @ process-route-queue.js:84
Lt.navigate @ navigate.js:970
(anonymous) @ index.js:235
dispatch @ jquery-3.4.1.min.js:2
v.handle @ jquery-3.4.1.min.js:2Understand this error
parse-component.js:81 Uncaught (in promise) TypeError: t[n] is not a function
at Ta (parse-component.js:81:28)
at Object.parse (component.js:29:16)
at component-loader.js:140:51
Assistant
You're seeing this error:

Uncaught SyntaxError: Failed to execute 'appendChild' on 'Node': Unexpected token 'export'

Because **Framework7 is trying to parse your `event-details.html` as a component**, but it contains **raw ES module `export` syntax**, which is **not valid in `.html` files** unless you’re using Webpack or a build system (like Framework7 with Vue/React CLI builds).

## βœ… Quick Fix: Use `page` HTML without `export default`

If you're using `componentUrl: 'event-details.html'`, your `event-details.html` **must NOT** contain “ with `export default`.

Instead, use Framework7's **Template7-style** or **plain HTML page structure**.

### βœ… Replace your `event-details.html` with this:

<div class="page" data-name="event-details">
  <div class="navbar">
    <div class="navbar-inner sliding">
      <div class="title">Event Details</div>
    </div>
  </div>
  <div class="page-content">
    <div class="block">
      <h2 class="event-title text-2xl font-bold"></h2>
      <p class="event-time text-gray-600"></p>
      <p class="event-capacity mt-2 text-sm"></p>
    </div>
  </div>
</div>

<script>
  // Page init hook
  document.addEventListener('page:init', function (e) {
    const page = e.detail;

    // Get query params
    const title = page.route.query.title || 'No Title';
    const time = page.route.query.time || '';
    const capacity = page.route.query.capacity || '';

    page.$el.find('.event-title').text(title);
    page.$el.find('.event-time').text(time);
    page.$el.find('.event-capacity').text('Capacity: ' + capacity);
  });
</script>

## βœ… Summary

| Problem | Solution |
|——–|———-|
| You used `export default` in `.html` | That only works in `.js`/`.vue` files in Vue CLI/Webpack builds |
| You're using `.html` with `componentUrl` | Use plain Framework7 page format with `page:init` script |
| You want to pass data | Use query params in `href`, and read them in `page.route.query` |

Let me know if you want to use `.vue` single-file components instead β€” that needs a build setup with Framework7 CLI.

User
arrow_left_circle_fill

FOSSETT'S CIRCUS
Customer system

Scan Customer Ticket

  Scan QR Code

{{title}}-{{location}}
{{time}}

No scans yet

Set Code Manually

<script>
// Page init hook
        document.addEventListener('page:init', function (e) {
          const page = e.detail;
      
          // Get query params
          const title = page.route.query.title || 'No Title';
          const time = page.route.query.time || '';
          const location = page.route.query.location || '';
      
          page.$el.find('.event-title').text(title);
          page.$el.find('.event-time').text(time);
          page.$el.find('.event-capacity').text('Capacity: ' + capacity);
        });
</script>

adjust in above

Assistant
Thanks for sharing your full HTML structure. You're using `{{title}}`, `{{location}}`, and `{{time}}` placeholders in the HTML, which won't work automatically in a plain HTML Framework7 page β€” those are for Template7 rendering.

Since you're already handling data dynamically using `page:init`, here's how to properly **adjust your page to display the passed event details** (title, location, time, etc.).

## βœ… Step-by-Step Fix for Your Page

### πŸ”Ή 1. Replace the `{{title}}`, `{{location}}`, `{{time}}` section with identifiable “s:

Replace this:

<div>
  {{title}}-{{location}}<br>
  {{time}}
</div>

With:

<div class="block text-center text-lg font-medium mt-4">
  <span class="event-title"></span> - <span class="event-location"></span><br>
  <span class="event-time"></span>
</div>

### πŸ”Ή 2. Update your “ accordingly:

Here’s the corrected script, including all variables:

<script>
  document.addEventListener('page:init', function (e) {
    const page = e.detail;

    // Get query parameters
    const title = page.route.query.title || 'No Title';
    const time = page.route.query.time || '';
    const location = page.route.query.location || '';
    const code = page.route.query.code || '';

    // Update DOM elements
    page.$el.find('.event-title').text(title);
    page.$el.find('.event-location').text(location);
    page.$el.find('.event-time').text(time);

    // Optional: Fill the input with pre-filled code (if passed)
    page.$el.find('input[name="code"]').val(code);
  });
</script>

## βœ… Example Href to Navigate Here

You should link to this page using something like:

<a href="/event/381/2319/?title=Test%20Event&location=Main%20Tent&time=29/06/25%20-%2005:00%20PM&code=ABC123">
  <div class="event-item">...</div>
</a>

You can dynamically build this `href` using JS/jQuery if needed.

## βœ… Summary

– ❌ `{{title}}` syntax won't work in `.html` unless you use a Template7 `.tpl` file or a rendering engine.
– βœ… Use `.text()` in `page:init` with class-based spans like `.event-title`.
– βœ… Use query strings in the `href` to pass data dynamically.

Let me know if you'd rather use a `.js` component or fetch the event data from API using just the ID instead.

User
use href to open event detail page
Assistant
To **open the event detail page using `href`** and pass data like `title`, `location`, `time`, and `code`, follow this structured approach using Framework7 routing.

## βœ… Step-by-Step Setup

### βœ… 1. Define route in your Framework7 app:

routes: [
  {
    path: '/event/:id/:dateId/',
    url: 'pages/event-details.html',
  },
],

### βœ… 2. Update your list item with `href` and query parameters:

Here’s how you can build your clickable list item using an “:

<a
  href="/event/381/2319/?title=Test%20Event&location=Main%20Tent&time=29/06/25%20-%2005:00%20PM&code=ABC123"
  class="event-link"
>
  <div class="space-y-4 mb-4 list-group-item active">
    <div class="rounded-lg border border-border/40 card bg-background hover:shadow-lg transition-shadow cursor-pointer">
      <div class="p-6">
        <div class="flex justify-between items-center">
          <div>
            <h3 class="text-xl font-semibold">Test Event</h3>
            <p class="text-gray-600">29/06/25 - 05:00 PM</p>
          </div>
          <div class="text-right">
            <div class="inline-flex items-center rounded-full border font-semibold px-2.5 py-0.5 text-xs">
              0/500
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</a>

Make sure this link is inside the `.view-main` wrapper so Framework7 can intercept and navigate properly.

### βœ… 3. Update `event-details.html` to handle those query params:

Ensure this part exists **in `pages/event-details.html`**:

<div class="block text-center text-lg font-medium mt-4">
  <span class="event-title"></span> - <span class="event-location"></span><br>
  <span class="event-time"></span>
</div>

And include this “ block:

<script>
  document.addEventListener('page:init', 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 || '';

    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);
  });
</script>

## βœ… Final Notes

– The `href` route format:
`/event/{eventId}/{eventDateId}/?title=…&location=…&time=…&code=…`
– Framework7 auto-handles this navigation as long as it's inside `.view-main`.

Would you like help dynamically building these `href`s using JavaScript/jQuery from data attributes?

User
parse-component.js:94 Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'id')
at Ta (parse-component.js:94:13)
at Object.parse (component.js:29:16)
at component-loader.js:140:51
Assistant
This error:

Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'id')

is coming from **`parse-component.js`**, which is part of Framework7's internal component parsing logic. It typically means:

> ⚠️ You're trying to load a `componentUrl` (which expects a Vue/Svelte/React component file), but you passed it an **HTML file** that is not a proper Framework7 component.

## βœ… Solution: Use `url` instead of `componentUrl`

Since you're working with a plain `.html` file (`event-details.html`), **you must use `url`**, not `componentUrl`.

### βœ… Correct route definition:

routes: [
  {
    path: '/event/:id/:dateId/',
    url: 'pages/event-details.html', // βœ… not componentUrl
  },
],

### ❌ Don't do this:

routes: [
  {
    path: '/event/:id/:dateId/',
    componentUrl: 'pages/event-details.html', // ❌ wrong for plain HTML
  },
],

## βœ… Recap of the working setup

1. **Link:**

<a href="/event/381/2319/?title=Test&location=MainTent&time=29/06/25%20-%2005:00%20PM&code=ABC123">Open Event</a>

2. **Route:**

{
     path: '/event/:id/:dateId/',
     url: 'pages/event-details.html',
   }

3. **HTML file (`event-details.html`)** has a `.page` and a `page:init` listener to access query params.

## βœ… Final Fix Checklist

– [x] Your route uses `url`, not `componentUrl`
– [x] The linked file is a plain `.html` with `…`
– [x] No `export default` inside `.html`
– [x] Data passed via query string and read via `page.route.query`

Let me know if you want to switch to `.js` or `.vue` component-based routing β€” that requires a different setup.

User
arrow_left_circle_fill

FOSSETT'S CIRCUS
Customer system

Scan Customer Ticket

  Scan QR Code

No scans yet

Set Code Manually

<script>
document.addEventListener('page:init', 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);
          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);
        });
</script>

not working

Assistant
Thanks for the detailed HTML. Your setup looks good overall, but if `page.route.query` values like `title`, `location`, etc., are coming back `undefined` or not rendering, it’s likely due to one of the following:

## βœ… Common Reasons and Fixes

### πŸ”Ή 1. βœ… Your `href` may be missing proper URL encoding or structure

Make sure your `href` includes query parameters **after the route**, like this:

<a href="/event/381/2319/?title=Test%20Event&location=Main%20Tent&time=29%2F06%2F25%20-%2005%3A00%20PM&code=ABC123">
  Open Event
</a>

You can encode it manually or use `encodeURIComponent()` in JS.

### πŸ”Ή 2. βœ… You must use `url` not `componentUrl` in your routes

Ensure your route is defined like this:

routes: [
  {
    path: '/event/:id/:dateId/',
    url: 'pages/event-details.html', // βœ… for static HTML
  },
],

Using `componentUrl` here would cause the `parse-component.js` error you're seeing.

### πŸ”Ή 3. βœ… Page must be wrapped inside `.view-main` and `.views`

Make sure your layout in `index.html` or wherever you define the views looks like:

<div class="views">
  <div class="view view-main">
    <!-- initial page here -->
  </div>
</div>

Framework7’s router only intercepts links inside `.view`.

### πŸ”Ή 4. βœ… Confirm your `event-details.html` is being loaded

Add a quick check:

document.addEventListener('page:init', function (e) {
  const page = e.detail;
  if (page.name === 'event-details') {
    console.log('Event details page loaded');

    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 || '';

    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);
  }
});

If this log never shows up, the page isn't loaded or the route isn’t matching.

### πŸ”Ή 5. βœ… Framework7 may not parse query strings unless `href` is 100% correct

Even a small typo in the query string or route path can break parsing.

**Working example:**

<a href="/event/123/456/?title=Test%20Event&location=Big%20Top&time=29%2F06%2F25%20-%2005%3A00%20PM&code=XYZ789">
  View Event
</a>

## βœ… Diagnostic Tip

Open your browser dev tools β†’ Console β†’ and see:

– Does the page route to `event-details.html`?
– Does `console.log(title, location, time, code);` show correct values?

If not, please paste the exact “ you're using, and I’ll validate the URL encoding and structure.

Let me know and I’ll help you solve it in minutes.

User
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.preloader(`${apiResult.message}`);
// 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.preloader(`${apiResult.message}`);
}
},
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.preloader(`${result.text} could not be sent to boxoffice, please try again`);

},
complete: function()
{
$('.scan').prop('disabled', false);
$('.manually').prop('disabled', false);
f7App.dialog.close();
}
});
}
f7dialog not closed even its applied in complete function

Assistant
You're correctly calling `f7App.dialog.close();` inside the `complete` callback, but it's **not closing the preloader**, likely due to **multiple conflicting `f7App.dialog.preloader()` calls**, especially in the `success` and `error` blocks.

Let’s go through it step-by-step and solve it.

## πŸ” What's Going Wrong

### πŸ”΄ Problem:
You're calling `f7App.dialog.preloader(…)` **again inside `success` and `error`**, which opens another preloader. But then you call `f7App.dialog.close()` only once in `complete`, so:

– **The first preloader is replaced** by the second one.
– The second one never gets closed properly.

## βœ… Solution

### βœ… Use `f7App.dialog.preloader()` **only once**, and use `f7App.dialog.close()` in `complete`.

### βœ… Replace `f7App.dialog.preloader(…)` inside `success` and `error` with `f7App.dialog.alert(…)` or `toast`.

## βœ… Fixed Version of Your 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 <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, status, xhr) {
            var apiResult = JSON.parse(result);

            if (apiResult.result) {
                $('#scan-result')
                    .removeClass('bg-dark')
                    .addClass('bg-success')
                    .html(apiResult.message + '<br>' + apiResult.seating + '<br>' + apiResult.tickets);

                // βœ… Use alert or toast instead of preloader again
                f7App.dialog.alert(apiResult.message, 'Scan Success');
                $('[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) {
            const message = 'Could not send code to boxoffice, please try again';
            $('#scan-result')
                .removeClass('bg-success')
                .addClass('bg-dark')
                .html('Code <u class="bg-primary text-white p-1">' + code + '</u> ' + message);

            f7App.dialog.alert(message, 'Scan Error');
        },
        complete: function() {
            $('.scan').prop('disabled', false);
            $('.manually').prop('disabled', false);
            f7App.dialog.close(); // βœ… Only one preloader to close
        }
    });
};

## βœ… Optional Improvement: Use Toast instead of Alert

If you don’t want popups, replace `.alert()` with:

f7App.toast.create({
  text: apiResult.message,
  closeTimeout: 3000,
}).open();

Let me know if you want this wrapped in a reusable toast/alert handler!

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',function () {
$(document).on('click', '.logout-staff', function()
{

localStorage.removeItem('staff_token');
localStorage.removeItem('is_staff_loggedin');
f7App.views.main.router.navigate('/');
});

});

$(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);
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);
});

$(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 () {

});
$(document).on('page:init', '.page[data-name="staff-login"]', function () {
if(localStorage.getItem('is_staff_loggedin'))
{
f7App.views.main.router.navigate('/staff/');
}
$('#staff-login-form').on('submit', function (e) {
e.preventDefault();
var email = $('#staff-email').val();
var password = $('#staff-password').val();
f7App.dialog.preloader('Logging in…');
$.ajax({
url: `${ApiUrl}staff_login`,
method: 'POST',
data: {
token: TOKEN,
email: email,
password: password
},
success: function (result, status, xhr) {
var apiResult = JSON.parse(result);
window.localStorage.setItem('staff_token', apiResult.token);
window.localStorage.setItem('is_staff_loggedin', true);
if (apiResult.result) {
f7App.views.main.router.navigate('/staff/');
} else {
$('#login-result').html(apiResult.message);
}
},
error: function (xhr, status, error) {
f7App.dialog.alert('Something went wrong. Please try again.', 'Error');
},
complete: function () {
// βœ… Hide preloader after AJAX completes
f7App.dialog.close();
}
});
});
});

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

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');
};
loadEvents('no');
$(document).on('click', '.reload-events', function()
{
loadEvents('no');

});

// $(document).on('click', '.open-event-detail', function () {
// const eventId = $(this).data('event-id');
// const eventDateId = $(this).data('event-date-id');
// const title = $(this).data('title');
// const time = $(this).data('time');
// const location = $(this).data('location');
// const code=eventId +'.'+ eventDateId +'.';

// // Pass data via route props
// f7App.views.main.router.navigate(`/event/${eventId}/${eventDateId}/`, {
// props: {
// title,
// time,
// location,
// eventId,
// eventDateId,
// code
// },
// });
// });

// load events click
// $('.reload-events').trigger('click');

$(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');

});

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

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

});

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

});

}
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 ul.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 +'.';
if(staff=='no')
{
$('.customer-screen div.events').append(`

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

0/500

`);
}else{
$('.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();
}
});
}

when click on .manually button then f7 preloader showing multiple times and after scan failed alert it needs to click ok to 3 times to close alert in android apk

Assistant
You're encountering **multiple preloader dialogs showing up** and **repeated alerts needing multiple taps to close** after clicking the `.manually` button or scanning a code. This behavior typically happens when:

### ❌ **The same function is being triggered multiple times or listeners are not scoped correctly**

## βœ… Solution Steps

### βœ… 1. **Prevent multiple bindings**

You're binding `sendCode` and click handlers **inside `page:init`**, but also globally (like `deviceready`). Each time the page is initialized or navigated, it **re-binds the same `.click` handlers again**, causing functions to fire **multiple times**.

### πŸ”§ Fix: **Use `.off('click').on('click')` to avoid multiple bindings**

Update this part:

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

Change it to this:

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

Do the same for `.scan` and other dynamic buttons:

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

### βœ… 2. **Ensure `sendCode()` is not declared inside `page:init`**

If you're declaring `sendCode()` inside `page:init`, it gets **redefined on every page load**, which is bad.

🟒 Move `sendCode()` outside (to global scope) or wrap it safely in a `window.sendCode = function() { … }` and avoid redefining it every time.

### βœ… 3. **Avoid multiple `.dialog.preloader()` or `.dialog.alert()` calls**

Right now, you have:

f7App.dialog.alert(apiResult.message, 'Scan Failed');
f7App.dialog.close();

You should never call `.close()` after `.alert()` β€” alerts are modal, and **Framework7 stacks them if triggered quickly**.

🟒 Fix this by removing unnecessary `f7App.dialog.alert()` after a preloader:

**Replace this block:**

f7App.dialog.alert(apiResult.message, 'Scan Failed');
f7App.dialog.close();

**With just:**

f7App.dialog.close();
f7App.dialog.alert(apiResult.message, 'Scan Failed');

πŸ” And ensure `.alert()` is **only called once**, not inside loops or after the dialog is already open.

## βœ… Final Clean Example for `.manually` button

$(document).off('click', '.manually').on('click', '.manually', function () {
    if (!window.eventDateId || eventDateId < 0) {
        f7App.dialog.alert('Please select an event first.', 'Error');
        return;
    }
    const code = $('[name="code"]').val();
    sendCode(code, 'manual');
});

## βœ… Summary

| Problem | Solution |
|——–|———-|
| Multiple dialogs or alerts | Use `.off().on()` for safe event binding |
| Multiple alerts stacking | Only call `f7App.dialog.alert()` once, never chain it after `preloader` |
| Redundant function definitions | Define `sendCode()` only once in global scope |

Let me know if you want me to cleanly refactor your whole `customer` page logic for clarity and performance.

Leave a Comment

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