/index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <!-- <meta http-equiv="Content-Security-Policy" content="default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: content:"> -->
  <meta name="viewport"
    content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover">

  <meta name="theme-color" content="#fff">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">
  <title>DriveLife</title>

  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <link rel="apple-touch-icon" href="assets/icons/apple-touch-icon.png">
  <link rel="icon" href="assets/icons/favicon.png">

  <link rel="stylesheet" href="framework7/framework7-bundle.min.css">
  <link rel="stylesheet" href="css/icons.css">
  <link rel="stylesheet" href="css/app.css">
  <link rel="stylesheet" href="css/custom.css?v=1.111">
  <link rel="stylesheet" href="css/custom-kesh.css">

  <link rel="preload" as="image" href="assets/img/icon-add-post.svg">
  <link rel="preload" as="image" href="assets/img/icon-qr-code.svg">
  <link rel="preload" as="image" href="assets/img/icon-vehicle-add.svg">

  <script defer
    src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDqDMSFVfl-tOgqaj4ZqA5I3HnobrIK6jg&loading=async&libraries=places&v=weekly">
    </script>

  <script src="//cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>

<body>
  <div id="app">
    <div class="views tabs safe-areas app-landing-page">
      <div class="init-loader">
        <div class="preloader preloader-central">
          <span class="preloader-inner"><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span>
          </span>
        </div>
      </div>

      <div class="init-loader light">
        <div class="preloader preloader-central">
          <span class="preloader-inner"><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span>
          </span>
        </div>
      </div>

      <div class="toolbar toolbar-bottom tabbar-icons footer">
        <div class="toolbar-inner">
          <a href="#view-social" class="footer-links tab-link tab-link-active start-link">
            <i class="icon f7-icons">house</i>
            <span class="tabbar-label">Home</span>
          </a>
          <a href="#view-discover" class="footer-links tab-link">
            <i class="icon f7-icons">search</i>
            <span class="tabbar-label">Discover</span>
          </a>
          <a class="tab-link" id="open-action-sheet">
            <i class="icon f7-icons">plus_app</i>
            <span class="tabbar-label">Add</span>
          </a>
          <a href="#view-store" class="footer-links tab-link">
            <i class="icon f7-icons">cart</i>
            <span class="tabbar-label">Store</span>
          </a>
          <a href="#view-profile" class="footer-links tab-link view-profile-link">
            <i class="icon f7-icons">person_circle</i>
            <span class="tabbar-label">Profile</span>
          </a>
        </div>
      </div>

      <!-- Left Menu panel -->
      <div class="panel panel-left panel-push">
        <div class="block">
          <img src="assets/img/ce-logo-dark.png" />
          <p>Welcome to the DriveLife App by CarEvents.com</p>
          <p>This is currently an early access app.</p>
          </p>Over the coming months, we will be adding all your favourite features from the CarEvents.com website,
          including
          adding new events, venues, car clubs and more.</p>
          <p>To report any bugs or request any features, please email: <a
              href="mailto:app@carevents.com">app@carevents.com</a></p>
        </div>
        <a class="logout-button"><i class="icon f7-icons">arrow_left_square</i> Logout</a>
      </div>

      <div id="view-home" class="view view-init view-main tab tab-active">
        <div class="page" data-name="home">
          <div class="page-content">
            <div class="ptr-preloader">
              <div class="preloader"></div>
              <div class="ptr-arrow"></div>
            </div>
          </div>
        </div>
      </div>

      <div id="view-social" class="view view-init tab" data-name="social" data-url="/social/">
      </div>

      <div id="view-discover" class="view view-init tab" data-name="discover" data-url="/discover/">
      </div>

      <div id="view-store" class="view view-init tab" data-name="store" data-url="/store/">
      </div>

      <div id="view-profile" class="view view-init tab" data-name="profile" data-url="/profile/">
      </div>

      <div id="view-profile-edit" class="view view-init tab" data-name="profile-edit" data-url="/profile-edit/">
      </div>

      <!-- <div id="view-auth" class="view view-init tab" data-name="auth" data-url="/auth/">
      </div> -->

      <div id="view-notifications" class="view view-init tab" data-name="notifications" data-url="/notifications/">
      </div>

      <div id="view-profile-garage-edit" class="view view-init tab" data-name="profile-garage-edit"
        data-url="/profile-garage-edit/">
      </div>

      <div id="profile-view" class="view view-init tab" data-name="profile-view" data-url="/profile-view/1">
      </div>

      <div id="post-view" class="view view-init tab" data-name="post-view" data-url="/post-view/-1">
      </div>

      <div id="profile-garage-edit" class="view view-init tab" data-name="profile-garage-edit"
        data-url="/profile-garage-edit/">
      </div>
      <div id="profile-garage-vehicle-view" class="view view-init tab" data-name="profile-garage-vehicle-view"
        data-url="/profile-garage-vehicle-view/-1">
      </div>

      <div id="discover-view-venue" class="view view-init tab" data-name="discover-view-venue"
        data-url="/discover-view-venue/-1">
      </div>

      <div id="discover-view-event" class="view view-init tab" data-name="discover-view-event"
        data-url="/discover-view-event/-1">
      </div>

      <div id="search" class="view view-init tab" data-name="search" data-url="/search/">
      </div>

      <!-- <div id="view-profile-edit-images" class="view view-init tab" data-name="profile-edit-images"
        data-url="/profile-edit-images/">
      </div>

      <div id="view-profile-edit-mydetails" class="view view-init tab" data-name="profile-edit-mydetails"
        data-url="/profile-edit-mydetails/">
      </div>

      <div id="view-profile-edit-socials" class="view view-init tab" data-name="profile-edit-socials"
        data-url="/profile-edit-socials/">
      </div>

      <div id="view-profile-edit-username" class="view view-init tab" data-name="profile-edit-username"
        data-url="/profile-edit-username/">
      </div>

      <div id="view-profile-garage-vehicle-add" class="view view-init tab" data-name="profile-garage-vehicle-add"
        data-url="/profile-garage-vehicle-add/">
      </div> -->
    </div>

    <!-- Comments Slider -->
    <div class="popup comments-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title" style="left: 145.5px;">Comments</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>

          <div class="page-content">
            <div class="comments-list" id="comments-list">
              <div class="preloader"></div>
            </div>

            <form id="comment-form">
              <!-- replying to -->
              <span class="replying-to hidden"></span>
              <div class="toolbar messagebar">
                <div class="toolbar-inner">
                  <div class="messagebar-area">
                    <textarea class="resizable" name="comment" placeholder="Message"></textarea>
                  </div>
                  <button type="submit" class="link icon-only demo-send-message-link"><i
                      class="icon f7-icons">arrow_up_circle_fill</i></button>
                  <!-- <a class="link icon-only demo-send-message-link"><i class="icon f7-icons">arrow_up_circle_fill</i></a> -->
                </div>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>

    <div class="popup share-popup three-quarter-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title">Share This</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>
          <div class="page-content">

            <div class="block">
              <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                <ul>
                  <li>
                    <a href="#" id="copy-link" target="_blank"><i class="icon f7-icons">link</i>
                      Copy
                      Link
                    </a>
                  </li>
                  <li>
                    <a href="#" id="share-post-email" target="_blank"><i class="icon f7-icons">envelope</i>
                      Share via Email
                    </a>
                  </li>
                </ul>
              </div>
            </div>

          </div>
        </div>
      </div>
    </div>

    <!-- Edit Post Slider -->
    <div class="popup edit-post-popup three-quarter-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title">Post Options</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>
          <div class="page-content">

            <div class="block">
              <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                <ul>
                  <li>
                    <a href="#" target="_blank" id="edit-post">
                      <i class="icon f7-icons">pencil</i>
                      Edit Post
                    </a>
                  </li>

                  <li><a href="#" target="_blank" id="delete-post"><i class="icon f7-icons">delete_right</i> Delete
                      Post</a></li>
                </ul>
              </div>
            </div>

          </div>
        </div>
      </div>
    </div>
    <!-- Edit Post Slider -->

    <!-- Add Link Slider -->
    <div class="popup add-link-popup three-quarter-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title">Add Link</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>
          <div class="page-content">
            <div class="block">
              <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                <form>
                  <ul>
                    <li class="item-content item-input">
                      <div class="item-inner">
                        <div class="item-title item-label">Link Title</div>
                        <div class="item-input-wrap">
                          <input type="text" name="custom_link_title" placeholder="E.g. My Website" />
                          <span class="input-clear-button"></span>
                        </div>
                      </div>
                    </li>
                    <li class="item-content item-input">
                      <div class="item-inner">
                        <div class="item-title item-label">Link URL</div>
                        <div class="item-input-wrap">
                          <input type="text" name="custom_link_url" placeholder="E.g. https://www.mylink.com" />
                          <span class="input-clear-button"></span>
                        </div>
                      </div>
                    </li>
                  </ul>

                  <div class="button-add-link">
                    <div class="button button-large button-fill margin-bottom" id="add-link-btn">
                      Save</div>
                  </div>
                </form>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Framework7 library -->
  <script src="framework7/framework7-bundle.min.js"></script>
  <script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script>

  <!-- App routes -->
  <script src="js/routes.js" type="module"></script>
  <!-- App store -->
  <script src="js/store.js" type="module"></script>


  <!-- App scripts -->
  <script src="js/app.js" type="module"></script>
  <script src="js/qr-scanner.js" type="module"></script>

  <!-- Page modules -->
  <script src="js/view-user-profile.js" type="module"></script>
  <script src="js/homepage.js" type="module"></script>
  <script src="js/profile.js" type="module"></script>
  <script src="js/profile-edit.js" type="module"></script>
  <script src="js/view-post.js" type="module"></script>
  <script src="js/notifications.js" type="module"></script>
  <script src="js/discoverpage.js" type="module"></script>
  <script src="js/search.js" type="module"></script>
  <script src="js/qr.js" type="module"></script>
  <script src="js/event-view.js" type="module"></script>
  <script src="js/venue-view.js" type="module"></script>
  <script src="js/edit-post.js" type="module"></script>
</body>

</html>

/pages/404.html
<div class="page">
  <div class="navbar">
    <div class="navbar-bg"></div>
    <div class="navbar-inner sliding">
      <div class="left">
        <a href="#" class="link back">
          <i class="icon icon-back"></i>
          <span class="if-not-md">Back</span>
        </a>
      </div>
      <div class="title">Not found</div>
    </div>
  </div>
  <div class="page-content">
    <div class="block block-strong inset">
      <p>Sorry</p>
      <p>Requested content not found.</p>
    </div>
  </div>
</div>

/pages/discover-view-event.html
<template>
  <div class="page" data-name="discover-view-event">
    <!-- Top Navbar -->
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="link icon-only">
            <i class="icon f7-icons">qrcode</i>
          </a>
          <a href="#" class="link icon-only">
            <i class="icon f7-icons">bell</i>
          </a>
        </div>
      </div>
    </div>

    <!-- Page content-->
    <div class="page-content">
      <div class="loading-fullscreen">
        <div class="preloader preloader-central">
          <span class="preloader-inner"><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span>
          </span>
        </div>
      </div>

      <div class="discover-view-event view-event">
        <div class="event-detail-img-box">
          <div class="swiper-container">
            <div class="swiper-wrapper">
              <div class="swiper-slide">
                <div class="swiper-image" style="background-color: gray;"></div>
              </div>
              <div class="swiper-slide">
                <div class="swiper-image" style="background-color: gray;"></div>
              </div>
            </div>
          </div>
        </div>

        <div class="container">
          <div class="event-detail">
            <div class="event-detail-title"></div>
            <div class="event-time-address-wrap mt-3 mb-2">
              <div class="event-time-address">
                <i class="f7-icons">calendar</i>
                <span>
                  <div class="skeleton-block" style="width: 100px"></div>
                </span>
              </div>
              <div class="event-time-address">
                <i class="f7-icons">clock</i>
                <span>
                  <div class="skeleton-block" style="width: 100px"></div>
                </span>
              </div>
              <div class="event-time-address">
                <i class="f7-icons">placemark_fill</i>
                <span>
                  <div class="skeleton-block" style="width: 300px"></div>
                </span>
              </div>
            </div>
            <div class="event-list-btn mt-1">
              <div class="btn w-100 bg-dark"><i class="f7-icons">tickets</i> Buy tickets</div>
            </div>
            <div class="event-list-btn d-flex">
              <div class="btn btn-primary w-100" id="favourite_event" style="display: none;"><i
                  class="f7-icons">heart_fill</i> Favourite</div>
              <div class="btn btn-primary w-100 popup-open" data-popup=".share-listing-popup">
                <i class="f7-icons">paperplane_fill</i> Share
              </div>
            </div>
          </div>



          <div class="listing-tabs">
            <a href="#tab-about" class="tab-link tab-link-active">About</a>
            <a href="#tab-entry-details" class="tab-link">Entry & Tickets</a>
          </div>

          <swiper-container class="tabs">
            <swiper-slide id="tab-about" class="tab tab-active">
              <div class="swiper-inner-container">
                <div class="event-des-wrap">
                  <p>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  </p>
                </div>
              </div>
            </swiper-slide>
            <swiper-slide id="tab-entry-details" class="tab">
              <div class="swiper-inner-container">
                <div class="event-des-wrap entry-details">
                  <p>
                  <div class="skeleton-block" style="width: 200px"></div>
                  </p>
                </div>
              </div>
            </swiper-slide>
          </swiper-container>
        </div>
      </div>
    </div>
    <!-- Page content-->

    <!-- Share Slider -->
    <div class="popup share-listing-popup three-quarter-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title">Share This</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>
          <div class="page-content">

            <div class="block">
              <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                <ul>
                  <li><a href="#" target="_blank" id="copy-event-link"><i class="icon f7-icons">link</i> Copy Link</a>
                  </li>
                  <li><a href="#" target="_blank" id="share-email-event-link"><i class="icon f7-icons">envelope</i>
                      Share via Email</a></li>
                </ul>
              </div>
            </div>

          </div>
        </div>
      </div>
    </div>
    <!-- * Share Slider -->

  </div>
</template>

/pages/discover-view-venue.html
<template>
  <div class="page" data-name="discover-view-venue">


    <!-- Top Navbar -->
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="link icon-only">
            <i class="icon f7-icons">qrcode</i>
          </a>
          <a href="#" class="link icon-only">
            <i class="icon f7-icons">bell</i>
          </a>
        </div>
      </div>

    </div>


    <!-- Page content-->
    <div class="page-content">

      <div class="loading-fullscreen">
        <div class="preloader preloader-central">
          <span class="preloader-inner"><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span>
          </span>
        </div>
      </div>

      <div class="discover-view-event">
        <div class="event-detail-img-box">
          <div class="swiper-container">
            <div class="swiper-wrapper">
              <div class="swiper-slide">
                <div class="swiper-image" style="background-color: gray;"></div>
              </div>
              <div class="swiper-slide">
                <div class="swiper-image" style="background-color: gray;"></div>
              </div>
            </div>
          </div>
        </div>

        <div class="container">
          <div class="event-detail">
            <div class="event-detail-title">
              <div class="skeleton-block" style="width: 200px"></div>

            </div>
            <div class="event-time-address-wrap mt-3 mb-2">
              <div class="event-time-address">
                <i class="f7-icons">placemark_fill</i>
                <span>
                  <div class="skeleton-block" style="width: 200px"></div>
                </span>
              </div>
            </div>
            <div class="event-list-btn mt-1">
              <div class="btn w-100 bg-dark venue-follow-btn">Follow</div>
            </div>
            <div class="event-list-btn d-flex">
              <div class="btn btn-primary w-100 popup-open" data-popup=".share-listing-popup"><i
                  class="f7-icons">paperplane_fill</i> Share</div>
            </div>
          </div>

          <div class="listing-tabs">
            <a href="#tab-events" class="tab-link  tab-link-active">Upcoming Events</a>
            <a href="#tab-about" class="tab-link">About</a>
          </div>

          <swiper-container class="tabs">
            <swiper-slide id="tab-events" class="tab tab-active">
              <div class="swiper-inner-container">
                <div class="grid grid-cols-2 event-listing mt-2">
                  <a href="#" class="card event-item">
                    <div class="event-image position-relative">
                      <div class="image-rectangle" style="background-image: url('assets/img/start01.jpg');"></div>
                      <div class="event-dates">
                        <div class="event-date-item">
                          <p>Feb</p>
                          <h5>18</h5>
                        </div>
                        <div class="event-date-item">
                          <p>Feb</p>
                          <h5>21</h5>
                        </div>
                      </div>
                    </div>
                    <div class="card-content">
                      <h3 class="event-title">Power Maxed MotoFest Coventry</h3>
                      <p class="event-info">Starts Thu, 18 Feb 2023</p>
                      <div class="event-info">NEC Birmingham</div>
                    </div>
                  </a>
                </div>
              </div>
            </swiper-slide>
            <swiper-slide id="tab-about" class="tab">
              <div class="swiper-inner-container">
                <div class="event-des-wrap">
                  <p>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  <div class="skeleton-block" style="width: 95%;padding: .5rem; margin-bottom: 5px;"></div>
                  </p>
                </div>
              </div>
            </swiper-slide>
          </swiper-container>
        </div>
      </div>
    </div>
    <!-- Page content-->


    <!-- Share Slider -->
    <div class="popup share-listing-popup three-quarter-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title">Share This</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>
          <div class="page-content">

            <div class="block">
              <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                <ul>
                  <li><a href="#" target="_blank" id="copy-venue-link"><i class="icon f7-icons">link</i> Copy Link</a>
                  </li>
                  <li><a href="#" target="_blank" id="share-email-venue-link"><i class="icon f7-icons">envelope</i>
                      Share via Email</a></li>
                </ul>
              </div>
            </div>

          </div>
        </div>
      </div>
    </div>
    <!-- * Share Slider -->

  </div>
</template>

/pages/discover.html
<template>
    <div class="page" data-name="discover">


        <!-- Top Navbar -->
        <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
                <div class="left">
                    <a href="#" class="link icon-only panel-open" data-panel="left">
                        <i class="icon f7-icons">bars</i>
                    </a>
                </div>
                <div class="middle">
                    <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
                </div>
                <div class="right">
                    <a href="#" class="link icon-only open-qr-modal">
                        <i class="icon f7-icons">qrcode</i>
                    </a>
                    <a href="/notifications/" class="link icon-only">
                        <div class="notification-count"></div>
                        <i class="icon f7-icons">bell</i>
                    </a>
                </div>
            </div>
        </div>

        <!-- Page content-->
        <div class="page-content discover-page infinite-scroll-content ptr-content ptr-watch-scrollable"
            data-ptr-distance="130" data-ptr-mousewheel="true">
            <div class="ptr-preloader">
                <div class="preloader"></div>
                <div class="ptr-arrow"></div>
            </div>

            <div class="discovery-wrap">
                <form class="searchbar">
                    <div class="searchbar-inner">
                        <div class="searchbar-input-wrap">
                            <input type="search" placeholder="Search" class="discover-search" />
                            <i class="searchbar-icon"></i>
                            <!-- <span class="input-clear-button"></span> -->
                        </div>
                        <span class="searchbar-disable-button if-not-aurora">Cancel</span>
                    </div>
                </form>

                <div class="tabbar-nav pt-1">
                    <ul>
                        <li><a href="#featured" class="tab-link tab-link-active" data-id="featured">Featured</a></li>
                        <li><a href="#events" class="tab-link" data-id="events">Events</a></li>
                        <li><a href="#venues" class="tab-link" data-id="venues">Venues</a></li>
                        <li><a href="#users" class="tab-link" data-id="users">Users</a></li>
                        <li><a href="#vehicles" class="tab-link" data-id="vehicles">Vehicles</a></li>
                    </ul>
                </div>

                <div class="container mt-15 mb-15">
                    <div class="tabs">
                        <div class="tab tab-active" id="featured">
                            <div class="featured-section">
                                <swiper-container slides-per-view="1" loop="true">
                                    <swiper-slide>
                                        <div class="featured-item">
                                            <div class="featured-img"
                                                style="background-image:url('https://www.carevents.com/uk/wp-content/uploads/sites/3/2023/12/3d7fe3f3-a748-4e98-b366-bc93bb51e3ed-1024x576.jpg')">
                                            </div>
                                            <div class="featured-content">
                                                <h2>Kwik Fit British Touring Car Championship – Brands Hatch</h2>
                                                <p>5th-6th Oct 24</p>
                                                <a href="/discover-view-event/91937" class="btn  btn-primary ">See
                                                    More</a>
                                            </div>
                                        </div>
                                    </swiper-slide>
                                </swiper-container>
                            </div>

                            <div class="trending-section">
                                <h2 class="section-title mt-3 mb-2">Trending Events</h2>
                                <swiper-container class="event-listing" slides-per-view="2" loop="true"
                                    id="trending-events">
                                    <div class="infinite-scroll-preloader">
                                        <div class="preloader"></div>
                                    </div>
                                </swiper-container>
                            </div>

                            <div class="trending-section">
                                <h2 class="section-title mt-3 mb-2">Trending Venues</h2>
                                <swiper-container class="event-listing" slides-per-view="2" loop="true"
                                    id="trending-venues">
                                    <div class="infinite-scroll-preloader">
                                        <div class="preloader"></div>
                                    </div>
                                </swiper-container>
                            </div>
                        </div>

                        <div class="tab" id="events">
                            <div class="filter-bar mb-2">
                                <div class="filter-item popup-open" data-popup=".filter-bydate-popup">
                                    <span>Date</span>
                                    <i class="icon f7-icons">chevron_down</i>
                                </div>
                                <div class="filter-item popup-open" data-popup=".filter-bycategory-popup">
                                    <span>Category </span>
                                    <i class="icon f7-icons">chevron_down</i>
                                </div>
                                <div class="filter-item popup-open" data-popup=".filter-bylocation-popup">
                                    <span>Location </span>
                                    <i class="icon f7-icons">chevron_down</i>
                                </div>
                            </div>

                            <div class="trending-section">
                                <div class="grid grid-cols-2 event-listing" id="filtered-events-tab">
                                    <!-- content populates here -->
                                </div>
                            </div>

                            <br />

                            <div class="infinite-scroll-preloader events-tab">
                                <div class="preloader"></div>
                            </div>
                        </div>

                        <div class="tab" id="venues">
                            <div class="filter-bar mb-2">
                                <div class="filter-item popup-open" data-popup=".filter-bylocation-popup">
                                    <span>Location </span>
                                    <i class="icon f7-icons">chevron_down</i>
                                </div>
                            </div>
                            <div class="trending-section">
                                <div class="grid grid-cols-2 event-listing" id="filtered-venues-tab">

                                </div>
                            </div>

                            <br />
                            <div class="infinite-scroll-preloader venues-tab">
                                <div class="preloader"></div>
                            </div>
                        </div>

                        <div class="tab" id="users">
                            <div class="list list-outline-ios list-strong-ios list-dividers-ios mt-2">
                                <ul id="users-tab">

                                </ul>
                                <br />
                                <div class="infinite-scroll-preloader users-tab">
                                    <div class="preloader"></div>
                                </div>
                            </div>
                        </div>

                        <div class="tab" id="vehicles">
                            <div class="list list-outline-ios list-strong-ios list-dividers-ios mt-2">
                                <ul id="vehicles-tab">

                                </ul>
                                <br />
                                <div class="infinite-scroll-preloader vehicles-tab">
                                    <div class="preloader"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Filter - Date - Slider -->
            <div class="popup filter-bydate-popup three-quarter-popup">
                <div class="view view-init tab" data-name="filter-bydate" data-url="/discover/">
                    <div class="page">
                        <div class="navbar">
                            <div class="navbar-inner">
                                <div class="title">Filter by Date</div>
                                <div class="right">
                                    <!-- Link to close popup -->
                                    <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
                                </div>
                            </div>
                        </div>
                        <div class="page-content">

                            <div class="block-title">Quick Options</div>
                            <div class="list" id="date-filters">
                                <ul>
                                    <li>
                                        <label class="item-checkbox item-content">
                                            <input type="checkbox" name="anytime" value="anytime" checked />
                                            <i class="icon icon-checkbox"></i>
                                            <div class="item-inner">
                                                <div class="item-title">Anytime</div>
                                            </div>
                                        </label>
                                    </li>
                                    <li>
                                        <label class="item-checkbox item-content">
                                            <input type="checkbox" name="today" value="today" />
                                            <i class="icon icon-checkbox"></i>
                                            <div class="item-inner">
                                                <div class="item-title">Today</div>
                                            </div>
                                        </label>
                                    </li>
                                    <li>
                                        <label class="item-checkbox item-content">
                                            <input type="checkbox" name="tomorrow" value="tomorrow" />
                                            <i class="icon icon-checkbox"></i>
                                            <div class="item-inner">
                                                <div class="item-title">Tomorrow</div>
                                            </div>
                                        </label>
                                    </li>
                                    <li>
                                        <label class="item-checkbox item-content">
                                            <input type="checkbox" name="this_weekend" value="this-weekend" />
                                            <i class="icon icon-checkbox"></i>
                                            <div class="item-inner">
                                                <div class="item-title">This Weekend</div>
                                            </div>
                                        </label>
                                    </li>
                                </ul>
                            </div>

                            <div class="block-title">Select Date Range</div>
                            <div class="list">
                                <ul>
                                    <li>
                                        <div class="item-content item-input">
                                            <div class="item-inner">
                                                <div class="item-title item-label">Owned From</div>
                                                <div class="item-input-wrap">
                                                    <input type="text" placeholder="Select date" readonly="readonly"
                                                        id="date_from" name="date_from" />
                                                </div>
                                            </div>
                                        </div>
                                    </li>
                                    <li>
                                        <div class="item-content item-input">
                                            <div class="item-inner">
                                                <div class="item-title item-label">Owned To</div>
                                                <div class="item-input-wrap">
                                                    <input type="text" placeholder="Select date" readonly="readonly"
                                                        id="date_to" name="date_to" />
                                                </div>
                                            </div>
                                        </div>
                                    </li>
                                </ul>
                            </div>

                            <div class="popup-lower-button">
                                <button class="button button-large button-fill margin-bottom apply-filters">Apply
                                    Filters</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!-- * Filter - Date - Slider -->

            <!-- Filter - Category - Slider -->
            <div class="popup filter-bycategory-popup three-quarter-popup">
                <div class="view view-init tab" data-name="filter-bycategory" data-url="/discover/">
                    <div class="page">
                        <div class="navbar">
                            <div class="navbar-inner">
                                <div class="title">Filter by Category</div>
                                <div class="right">
                                    <!-- Link to close popup -->
                                    <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
                                </div>
                            </div>
                        </div>
                        <div class="page-content">
                            <form class="list mt-2" id="category-filters">
                                <ul>
                                    <li>
                                        <label class="item-checkbox item-content">
                                            <input type="checkbox" name="all" value="0" checked />
                                            <i class="icon icon-checkbox"></i>
                                            <div class="item-inner">
                                                <div class="item-title">All</div>
                                            </div>
                                        </label>
                                    </li>
                                </ul>
                            </form>
                            <div class="popup-lower-button">
                                <button class="button button-large button-fill margin-bottom apply-filters">Apply
                                    Filters</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!-- * Filter - Category - Slider -->

            <!-- Filter - Location - Slider -->
            <div class="popup filter-bylocation-popup three-quarter-popup">
                <div class="view view-init tab" data-name="filter-bylocation" data-url="/discover/">
                    <div class="page">
                        <div class="navbar">
                            <div class="navbar-inner">
                                <div class="title">Filter by Location</div>
                                <div class="right">
                                    <!-- Link to close popup -->
                                    <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
                                </div>
                            </div>
                        </div>
                        <div class="page-content">
                            <div class="block location-field">
                                <p>Location</p>
                                <input type="text" placeholder="Enter Location" id="autocomplete" />
                                <input type="hidden" id="lat" />
                                <input type="hidden" id="lng" />
                            </div>

                            <div class="list mt2" id="location-filters">
                                <ul>
                                    <li>
                                        <label class="item-radio item-radio-icon-start item-content">
                                            <input type="radio" name="location" value="national" checked />
                                            <i class="icon icon-radio"></i>
                                            <div class="item-inner">
                                                <div class="item-title">National</div>
                                            </div>
                                        </label>
                                    </li>
                                    <li>
                                        <label class="item-radio item-radio-icon-start item-content">
                                            <input type="radio" name="location" value="near-me" />
                                            <i class="icon icon-radio"></i>
                                            <div class="item-inner">
                                                <div class="item-title">Nearby</div>
                                            </div>
                                        </label>
                                    </li>
                                    <li>
                                        <label class="item-radio item-radio-icon-start item-content">
                                            <input type="radio" name="location" value="25-miles" />
                                            <i class="icon icon-radio"></i>
                                            <div class="item-inner">
                                                <div class="item-title">25 Miles</div>
                                            </div>
                                        </label>
                                    </li>
                                    <li>
                                        <label class="item-radio item-radio-icon-start item-content">
                                            <input type="radio" name="location" value="50-miles" />
                                            <i class="icon icon-radio"></i>
                                            <div class="item-inner">
                                                <div class="item-title">50 Miles</div>
                                            </div>
                                        </label>
                                    </li>
                                    <li>
                                        <label class="item-radio item-radio-icon-start item-content">
                                            <input type="radio" name="location" value="100-miles" />
                                            <i class="icon icon-radio"></i>
                                            <div class="item-inner">
                                                <div class="item-title">100 Miles</div>
                                            </div>
                                        </label>
                                    </li>


                                </ul>
                            </div>

                            <div class="popup-lower-button">
                                <button class="button button-large button-fill margin-bottom apply-filters">Apply
                                    Filters</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!-- * Filter - Location - Slider -->
        </div>
    </div>
</template>

/pages/forgot-password.html
<template>
  <div class="page" data-name="forgot-password">
    <div class="navbar">
      <div class="navbar-inner">
        <div class="left">
                <a class="link back">
                  <i class="icon icon-back"></i>
                </a>
              </div>
              <div class="right">
              </div>
      </div>
    </div>
    <div class="page-content"> 

      <div class="login-form">
            <div class="section">
                <h1>Reset Password</h1>
                <h4>Enter your email below</h4>
            </div>

                <form>

                  <div class="list list-strong-ios list-dividers-ios inset-ios">
                  <ul>
                    <li class="item-content item-input">
                      <div class="item-inner">
                        <div class="item-title item-label">Email</div>
                        <div class="item-input-wrap">
                          <input type="email" placeholder="Enter email address" required />
                          <span class="input-clear-button"></span>
                        </div>
                      </div>
                    </li>
                  </ul>
                </div>

                <div class="login-buttons">
                  <a href="#" class="button button-large button-fill margin-bottom">Send reset email</a>
                </div>

                </form>

        </div>
    
    </div>
  </div>
</template>

/pages/home.html
<template>
    <div class="page no-swipeback" data-name="social">
        <div class="navbar navbar-large">
            <div class="navbar-inner">
                <div class="left">
                    <a href="#" class="link icon-only panel-open" data-panel="left">
                        <i class="icon f7-icons">bars</i>
                    </a>
                </div>
                <div class="middle">
                    <div class="header-logo"><img src="../assets/img/logo-dark.png" /></div>
                </div>
                <div class="right">
                    <a href="#" class="link icon-only open-qr-modal">
                        <i class="icon f7-icons">qrcode</i>
                    </a>
                    <a href="/notifications/" class="link icon-only">
                        <div class="notification-count"></div>
                        <i class="icon f7-icons">bell</i>
                    </a>
                </div>

            </div>

        </div>

        <div class="social-tabs toolbar toolbar-bottom tabbar">
            <div class="toolbar-inner">
                <a href="#tab-latest" class="tab-link tab-link-active" data-type="latest">Latest</a>
                <a href="#tab-following" class="tab-link" data-type="following">Following</a>
            </div>
        </div>

        <div class="page-content ptr-content ptr-watch-scrollable home-page social-content infinite-scroll-content"
            data-ptr-distance="130" data-ptr-mousewheel="true">
            <div class="ptr-preloader">
                <div class="preloader"></div>
                <div class="ptr-arrow"></div>
            </div>

            <div class="tabs">
                <div class="tab tab-active" id="tab-latest">
                    <div class="data virtual-list list-virtual-latest">

                    </div>
                    <div class="infinite-scroll-preloader home-posts">
                        <div class="preloader"></div>
                    </div>
                </div>

                <div class="tab" id="tab-following">
                    <div class="data virtual-list list-virtual-following"></div>

                    <div class="infinite-scroll-preloader home-following-posts">
                        <div class="preloader"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

/pages/login.html
<template>
  <div class="page no-toolbar no-swipeback" data-name="signin">
    <div class="navbar">
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <!-- <i class="icon icon-back"></i> -->
          </a>
        </div>
        <div class="right">
          <a href="/signup-step1/" class="link">
            Sign Up
          </a>
        </div>
      </div>
    </div>
    <div class="page-content login-screen-content">
      <div class="login-form">
        <div class="section">
          <h1>Login</h1>
          <h4>Log into your DriveLife Account</h4>
        </div>

        <form>
          <div class="list list-strong-ios list-dividers-ios inset-ios">
            <ul>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Email</div>
                  <div class="item-input-wrap">
                    <input type="email" name="username" placeholder="Enter email address" />
                    <span class="input-clear-button"></span>
                  </div>
                </div>
              </li>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Password</div>
                  <div class="item-input-wrap">
                    <input type="password" name="password" placeholder="Enter password" />
                    <span class="input-clear-button"></span>
                  </div>
                </div>
              </li>
            </ul>
          </div>
          <div class="block">
            <p class="text-center"><a href="https://www.carevents.com/reset-password/" id="forgot-password">Forgot
                Password?</a></p>
          </div>

          <div class="login-buttons">
            <button type="submit" class="button button-large button-fill margin-bottom">
              Next

            </button>
          </div>
        </form>
      </div>
    </div>
  </div>
</template>

/pages/notifications.html
<template>
    <div class="page no-toolbar" data-name="notifications">
        <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
                <div class="left">
                    <a class="link back">
                        <i class="icon icon-back"></i>
                    </a>
                </div>
                <div class="middle">
                    <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
                </div>

            </div>
        </div>

        <!-- Page content-->
        <div class="page-content notification-page ptr-content ptr-watch-scrollable" data-infinite-distance="50"
            data-ptr-distance="120">
            <div class="ptr-preloader">
                <div class="preloader"></div>
                <div class="ptr-arrow"></div>
            </div>

            <div class="notification-wrap">
                <div class="">
                    <div class="app-notification-title" data-title="Recent" data-id="recent"></div>
                    <div class="notification-list" id="recent">

                    </div>
                </div>

                <div class="">
                    <div class="app-notification-title" data-title="Last Week" data-id="last-week"></div>
                    <div class="notification-list" id="this-week">
                    </div>
                </div>

                <div class="">
                    <div class="app-notification-title" data-title="Last 30 days" data-id="last-30"></div>
                    <div class="notification-list" id="last-30-days">
                    </div>
                </div>

                <div class="load-more-notifications btn btn-primary hidden" data-page="1" data-total-pages="NaN">Load
                    more
                </div>
            </div>
        </div>
        <!-- Page content-->
    </div>
</template>

/pages/post-edit.html
<template>
  <div class="page no-toolbar" data-name="post-edit">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="top-right-action" id="update-post">
            Save
          </a>
        </div>

      </div>
    </div>

    <div class="page-content">
      <div class="block-title">Edit Post</div>

      <div class="list list-strong-ios list-dividers-ios inset-ios">
        <ul>
          <li class="item-content item-input">
            <div class="item-inner">
              <div class="item-input-wrap">
                <input type="hidden" id="edit_post_id" />
                <textarea placeholder="Post content" rows="8" id="post_content"></textarea>
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

/pages/post-view.html
<template>
    <div class="page" data-name="post-view">
        <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
                <div class="left">
                    <a class="link back">
                        <i class="icon icon-back"></i>
                    </a>
                </div>
                <div class="middle">
                    <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
                </div>
                <div class="right">
                    <a href="#" class="link icon-only open-qr-modal">
                        <i class="icon f7-icons">qrcode</i>
                    </a>
                    <a href="/notifications/" class="link icon-only">
                        <div class="notification-count"></div>
                        <i class="icon f7-icons">bell</i>
                    </a>
                </div>
            </div>
        </div>

        <div class="page-content">
            <div id="post-view-container">
                <div class="loading-fullscreen post-view">
                    <div class="preloader preloader-central">
                        <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                                class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                                class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                                class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                                class="preloader-inner-line"></span>
                        </span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

/pages/profile-edit-account-settings.html
<template>
    <div class="page no-toolbar" data-name="profile-edit-account-settings">
        <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
                <div class="left">
                    <a class="link back">
                        <i class="icon icon-back"></i>
                    </a>
                </div>
                <div class="middle">
                    <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
                </div>
                <div class="right">
                    <a href="#" class="top-right-action">
                        Save
                    </a>
                </div>

            </div>
        </div>

        <div class="page-content">
            <div class="block-title">Delete your account</div>
            <div class="list list-strong-ios list-dividers-ios inset-ios">
                <p>If you would like to completely remove your account, please click below. Please note, it is not
                    possible to restore an account once it has been deleted.</p>
                <p><button class="button button-fill color-red account-delete">Delete Account</button></p>
            </div>
        </div>
    </div>
</template>

/pages/profile-edit-images.html
<template>
  <div class="page no-toolbar" data-name="profile-edit-images">

    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="top-right-action" id="save-profile-images">
            Save
          </a>
        </div>
      </div>
    </div>

    <div class="page-content profile-edit-images">
      <div class="block-title">Profile Image</div>
      <div class="list list-strong-ios list-dividers-ios inset-ios">
        <div class="custom-file-upload profile" id="fileUpload">
          <input type="file" id="fileuploadInput" name="profile_image" />
          <label for="fileuploadInput">
          </label>
          <span>
            <i class="icon f7-icons">cloud_upload</i>
            <small>Tap to Upload</small>
          </span>
        </div>
      </div>

      <div class="block-title">Cover Image</div>
      <div class="list list-strong-ios list-dividers-ios inset-ios">
        <div class="custom-file-upload cover" id="fileUpload">
          <input type="file" id="fileuploadInput1" name="cover_image" />
          <label for="fileuploadInput1">
          </label>
          <span>
            <i class="icon f7-icons">cloud_upload</i>
            <small>Tap to Upload</small>
          </span>
        </div>
      </div>


    </div>


  </div>
</template>

/pages/profile-edit-mydetails.html
<template>
  <div class="page no-toolbar" data-name="profile-edit-mydetails">

    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="top-right-action" id="save-details">
            Save
          </a>
        </div>
      </div>
    </div>

    <div class="page-content">
      <div class="block-title">Your Details</div>

      <div class="list list-strong-ios list-dividers-ios inset-ios">
        <form>
          <ul>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">First Name *</div>
                <div class="item-input-wrap">
                  <input type="text" name="first_name" placeholder="Enter first name" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Last Name *</div>
                <div class="item-input-wrap">
                  <input type="text" name="last_name" placeholder="Enter last name" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <li class="item-content item-input" id="email-input">
              <div class="item-inner">
                <div class="item-title item-label">Email * <span class="error-message" id="email-verify-span">Email not
                    verified</span></div>
                <div class="item-input-wrap">
                  <input type="text" name="email" placeholder="Enter email" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>

            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Tel No</div>
                <div class="item-input-wrap">
                  <input type="text" name="tel_no" placeholder="Enter tel no" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>

          </ul>
        </form>
      </div>

      <div class="block-title">Password</div>

      <div class="list list-strong-ios list-dividers-ios inset-ios change-password">
        <form>
          <ul>
            <li class="accordion-item">
              <a class="item-link item-content">
                <div class="item-inner">
                  <div class="item-title password-toggle-title">Change Password</div>
                </div>
              </a>
              <div class="accordion-item-content">
                <div>
                  <ul>
                    <li class="item-content item-input">
                      <div class="item-inner">
                        <div class="item-title item-label">Current Password *</div>
                        <div class="item-input-wrap">
                          <input type="password" name="current_password" placeholder="Enter Current Password" />
                          <span class="input-clear-button"></span>
                        </div>
                      </div>
                    </li>
                    <li class="item-content item-input">
                      <div class="item-inner">
                        <div class="item-title item-label">Password *</div>
                        <div class="item-input-wrap">
                          <input type="password" name="password" placeholder="Enter Password" />
                          <span class="input-clear-button"></span>
                        </div>
                      </div>
                    </li>
                    <li class="item-content item-input">
                      <div class="item-inner">
                        <div class="item-title item-label">Confirm Password *</div>
                        <div class="item-input-wrap">
                          <input type="password" name="confirm_password" placeholder="Confirm Password" />
                          <span class="input-clear-button"></span>
                        </div>
                      </div>
                    </li>
                  </ul>
                </div>
                <div class="button-add-link">
                  <div class="button button-large button-fill margin-bottom" id="update_password">Update Password</div>
                </div>
              </div>
            </li>
          </ul>
        </form>
      </div>
    </div>
  </div>
</template>

/pages/profile-edit-socials.html
<template>
  <div class="page no-toolbar" data-name="profile-edit-socials">

    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="top-right-action" id="save-profile-socials">
            Save
          </a>
        </div>

      </div>
    </div>


    <div class="page-content">

      <div class="block-title">Social Links</div>

      <div class="list list-strong-ios list-dividers-ios inset-ios">
        <form>
          <ul>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Instagram</div>
                <div class="item-input-wrap">
                  <input type="text" name="social_instagram" placeholder="Enter Instagram Username" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Facebook</div>
                <div class="item-input-wrap">
                  <input type="text" name="social_facebook" placeholder="Enter Facebook Username" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">TikTok</div>
                <div class="item-input-wrap">
                  <input type="text" name="social_tiktok" placeholder="Enter Tiktok Username" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">YouTube</div>
                <div class="item-input-wrap">
                  <input type="text" name="social_youtube" placeholder="Enter YouTube Username" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Mivia</div>
                <div class="item-input-wrap">
                  <input type="text" name="social_mivia" placeholder="Enter Mivia Username" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Custodian Garage / Car link</div>
                <div class="item-input-wrap">
                  <input type="text" name="social_custodian" placeholder="Enter Custodian Username" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>

          </ul>
        </form>
      </div>

      <div class="block-title">Other Links</div>
      <div class="list list-strong-ios list-dividers-ios inset-ios social-other-links">
        <ul></ul>

        <div class="button-add-link">
          <div class="button button-fill margin-bottom popup-open" data-popup=".add-link-popup">Add Link</div>
        </div>
      </div>
    </div>
  </div>
</template>

/pages/profile-edit-username.html
<template>
  <div class="page no-toolbar" data-name="profile-edit-username">

    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="top-right-action" id="save-username">
            Save
          </a>
        </div>

      </div>
    </div>

    <div class="page-content">
      <div class="block-title">Change Username
        <div class="item-input-info change-username-info">Your username can be changed every 30 days</div>
      </div>

      <div class="list list-strong-ios list-dividers-ios inset-ios">
        <form>
          <ul>
            <li class="item-content item-input">
              <div class="item-inner profile-edit-view">
                <div class="item-title item-label">Username</div>
                <div class="item-input-wrap">
                  <input type="text" name="username" placeholder="Enter desired Username" id="lowercaseInput" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
          </ul>
        </form>
        <br />
        <div class="item-input-info change-username-info" id="username-editable"></div>
      </div>
    </div>
  </div>
</template>

/pages/profile-edit.html
<template>
  <div class="page" data-name="profile-edit">

    <div class="navbar navbar-nologo">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="link icon-only open-qr-modal">
            <i class="icon f7-icons">qrcode</i>
          </a>
          <a href="#" class="link icon-only">
            <i class="icon f7-icons">bell</i>
          </a>
        </div>

      </div>
    </div>


    <div class="page-content">
      <div class="block">
        <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
          <ul>
            <li><a href="/profile-edit-images/">Edit Profile Images</a></li>
            <li><a href="/profile-edit-socials/">Manage Social Links</a></li>
            <li><a href="/profile-edit-mydetails/">My Details</a></li>
            <li><a href="/profile-edit-username/">Username</a></li>
            <li><a href="/profile-edit-account-settings/">Account Settings</a></li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>

/pages/profile-garage-edit.html
<template>
  <div class="page" data-name="profile-garage-edit">

    <div class="navbar navbar-nologo">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="/profile-garage-vehicle-add/" class="top-right-action">
            + Add
          </a>
        </div>

      </div>
    </div>


    <div class="page-content">
      <div class="garage-list profile-landing-page">

        <div class="listview-title garage-sub-title">Current Vehicles</div>
        <ul class="listview image-listview media transparent flush pt-1" id="garage-edit-current-list">

        </ul>

        <div class="listview-title garage-sub-title">Past Vehicles</div>
        <ul class="listview image-listview media transparent flush pt-1" id="garage-edit-past-list">

        </ul>
      </div>
    </div>
  </div>
</template>

/pages/profile-garage-vehicle-add.html
<template>
  <div class="page no-toolbar" data-name="profile-garage-vehicle-add">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="top-right-action" id="submit-add-vehicle-form">
            Save
          </a>
        </div>
      </div>
    </div>

    <div class="page-content">
      <form id="addVehicleForm">
        <div class="block-title">Vehicle Image</div>

        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <div class="custom-file-upload" id="fileUpload">
            <input type="file" id="fileuploadInput" name="vehicle_image" />
            <label for="fileuploadInput">
              <span>
                <i class="icon f7-icons">cloud_upload</i>
                <small>Tap to Upload</small>
              </span>
            </label>
          </div>
        </div>

        <div class="block-title">Vehicle Details</div>
        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <ul>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Vehicle Make *</div>
                <div class="item-input-wrap input-dropdown-wrap">
                  <select class="input-with-value" name="vehicle_make" required>
                    <option disabled selected value="0">Please Select</option>
                    <option value="Acura">Acura</option>
                    <option value="Alfa Romeo">Alfa Romeo</option>
                    <option value="Aston Martin">Aston Martin</option>
                    <option value="Audi">Audi</option>
                    <option value="Alpine ">Alpine</option>
                    <option value="Bentley">Bentley</option>
                    <option value="BMW">BMW</option>
                    <option value="Bugatti">Bugatti</option>
                    <option value="Buick">Buick</option>
                    <option value="Cadillac">Cadillac</option>
                    <option value="Chevrolet">Chevrolet</option>
                    <option value="Chrysler">Chrysler</option>
                    <option value="Citroën">Citroën</option>
                    <option value="Dodge">Dodge</option>
                    <option value="Ferrari">Ferrari</option>
                    <option value="Fiat">Fiat</option>
                    <option value="Ford">Ford</option>
                    <option value="GMC">GMC</option>
                    <option value="Honda">Honda</option>
                    <option value="Hyundai">Hyundai</option>
                    <option value="Infiniti">Infiniti</option>
                    <option value="Jaguar">Jaguar</option>
                    <option value="Jeep">Jeep</option>
                    <option value="Kia">Kia</option>
                    <option value="Lamborghini">Lamborghini</option>
                    <option value="Land Rover">Land Rover</option>
                    <option value="Lexus">Lexus</option>
                    <option value="Lotus">Lotus</option>
                    <option value="Lincoln">Lincoln</option>
                    <option value="Maserati">Maserati</option>
                    <option value="Mazda">Mazda</option>
                    <option value="McLaren">McLaren</option>
                    <option value="Mercedes-Benz">Mercedes-Benz</option>
                    <option value="Mini">Mini</option>
                    <option value="Mitsubishi">Mitsubishi</option>
                    <option value="Nissan">Nissan</option>
                    <option value="Peugeot">Peugeot</option>
                    <option value="Porsche">Porsche</option>
                    <option value="Ram">Ram</option>
                    <option value="Renault">Renault</option>
                    <option value="Rolls-Royce">Rolls-Royce</option>
                    <option value="Saab">Saab</option>
                    <option value="Subaru">Subaru</option>
                    <option value="Suzuki">Suzuki</option>
                    <option value="Tesla">Tesla</option>
                    <option value="TVR">TVR</option>
                    <option value="Toyota">Toyota</option>
                    <option value="Volkswagen">Volkswagen</option>
                    <option value="Volvo">Volvo</option>
                  </select>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Model *</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Your vehicle model" name="vehicle_model" required />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Variant</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Add any vehicle variant info" name="vehicle_variant" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Registration</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Your vehicle reg" name="vehicle_reg" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Colour</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Your vehicle colour" name="vehicle_colour" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Short description -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Short Description</div>
                <div class="item-input-wrap">
                  <textarea name="vehicle_description" placeholder="Add a short description of your vehicle"
                    maxlength="200"></textarea>
                </div>
              </div>
            </li>
          </ul>
        </div>

        <div class="block-title">Ownership Dates</div>

        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <ul>

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Ownership *</div>
                <div class="item-input-wrap input-dropdown-wrap">
                  <select class="input-with-value" name="vehicle_ownership">
                    <option value="current">Current Vehicle</option>
                    <option value="past">Past Vehicle</option>
                  </select>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li>
              <div class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Owned From</div>
                  <div class="item-input-wrap">
                    <input type="text" placeholder="Select date" readonly="readonly" id="owned-from"
                      name="vehicle_owned_from" />
                  </div>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li>
              <div class="item-content item-input" id="owned-to-block" style="display: none;">
                <div class="item-inner">
                  <div class="item-title item-label">Owned To</div>
                  <div class="item-input-wrap">
                    <input type="text" placeholder="Select date" readonly="readonly" id="owned-to"
                      name="vehicle_owned_to" />
                  </div>
                </div>
              </div>
            </li>
            <!-- * Input -->

          </ul>
        </div>



        <div class="block-title">Vehicle Tagging</div>

        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <ul>
            <li class="toggle-inline">
              <div>Allow this vehicle to be discovered & tagged via it's registration</div>
              <div>
                <label class="toggle">
                  <input type="checkbox" name="vehicle_tagging" />
                  <span class="toggle-icon"></span>
                </label>
              </div>
            </li>

          </ul>
        </div>
      </form>
    </div>
  </div>
</template>

/pages/profile-garage-vehicle-edit.html
<template>
  <div class="page no-toolbar" data-name="profile-garage-vehicle-edit">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="top-right-action" id="submit-vehicle-form">
            Save
          </a>
        </div>
      </div>
    </div>

    <div class="page-content">
      <div class="loading-fullscreen">
        <div class="preloader preloader-central">
          <span class="preloader-inner"><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span>
          </span>
        </div>
      </div>
      <form id="vehicleForm">
        <input type="hidden" name="garage_id" value="" />

        <div class="block-title">Vehicle Image</div>

        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <div class="custom-file-upload" id="fileUpload">
            <input type="file" id="fileuploadInput" name="vehicle_image" />
            <label for="fileuploadInput">
            </label>
            <span>
              <i class="icon f7-icons">cloud_upload</i>
              <small>Tap to Upload</small>
            </span>
          </div>
        </div>

        <div class="block-title">Vehicle Details</div>
        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <ul>
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Vehicle Make *</div>
                <div class="item-input-wrap input-dropdown-wrap">
                  <select class="input-with-value" name="vehicle_make" required>
                    <option disabled selected value="0">Please Select</option>
                    <option value="Acura">Acura</option>
                    <option value="Alfa Romeo">Alfa Romeo</option>
                    <option value="Aston Martin">Aston Martin</option>
                    <option value="Audi">Audi</option>
                    <option value="Alpine ">Alpine</option>
                    <option value="Bentley">Bentley</option>
                    <option value="BMW">BMW</option>
                    <option value="Bugatti">Bugatti</option>
                    <option value="Buick">Buick</option>
                    <option value="Cadillac">Cadillac</option>
                    <option value="Chevrolet">Chevrolet</option>
                    <option value="Chrysler">Chrysler</option>
                    <option value="Citroën">Citroën</option>
                    <option value="Dodge">Dodge</option>
                    <option value="Ferrari">Ferrari</option>
                    <option value="Fiat">Fiat</option>
                    <option value="Ford">Ford</option>
                    <option value="GMC">GMC</option>
                    <option value="Honda">Honda</option>
                    <option value="Hyundai">Hyundai</option>
                    <option value="Infiniti">Infiniti</option>
                    <option value="Jaguar">Jaguar</option>
                    <option value="Jeep">Jeep</option>
                    <option value="Kia">Kia</option>
                    <option value="Lamborghini">Lamborghini</option>
                    <option value="Land Rover">Land Rover</option>
                    <option value="Lexus">Lexus</option>
                    <option value="Lotus">Lotus</option>
                    <option value="Lincoln">Lincoln</option>
                    <option value="Maserati">Maserati</option>
                    <option value="Mazda">Mazda</option>
                    <option value="McLaren">McLaren</option>
                    <option value="Mercedes-Benz">Mercedes-Benz</option>
                    <option value="Mini">Mini</option>
                    <option value="Mitsubishi">Mitsubishi</option>
                    <option value="Nissan">Nissan</option>
                    <option value="Peugeot">Peugeot</option>
                    <option value="Porsche">Porsche</option>
                    <option value="Ram">Ram</option>
                    <option value="Renault">Renault</option>
                    <option value="Rolls-Royce">Rolls-Royce</option>
                    <option value="Saab">Saab</option>
                    <option value="Subaru">Subaru</option>
                    <option value="Suzuki">Suzuki</option>
                    <option value="Tesla">Tesla</option>
                    <option value="TVR">TVR</option>
                    <option value="Toyota">Toyota</option>
                    <option value="Volkswagen">Volkswagen</option>
                    <option value="Volvo">Volvo</option>
                  </select>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Model *</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Your vehicle model" name="vehicle_model" required />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Variant</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Add any vehicle variant info" name="vehicle_variant" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Registration</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Your vehicle reg" name="vehicle_reg" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Colour</div>
                <div class="item-input-wrap">
                  <input type="text" placeholder="Your vehicle colour" name="vehicle_colour" />
                  <span class="input-clear-button"></span>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Short description -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Short Description</div>
                <div class="item-input-wrap">
                  <textarea name="vehicle_description" placeholder="Add a short description of your vehicle"
                    maxlength="200"></textarea>
                </div>
              </div>
            </li>
          </ul>
        </div>

        <div class="block-title">Ownership Dates</div>

        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <ul>

            <!-- Input -->
            <li class="item-content item-input">
              <div class="item-inner">
                <div class="item-title item-label">Ownership *</div>
                <div class="item-input-wrap input-dropdown-wrap">
                  <select class="input-with-value" name="vehicle_ownership">
                    <option value="current">Current Vehicle</option>
                    <option value="past">Past Vehicle</option>
                  </select>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li>
              <div class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Owned From</div>
                  <div class="item-input-wrap">
                    <input type="text" placeholder="Select date" readonly="readonly" id="owned-from"
                      name="vehicle_owned_from" />
                  </div>
                </div>
              </div>
            </li>
            <!-- * Input -->

            <!-- Input -->
            <li>
              <div class="item-content item-input" id="owned-to-block">
                <div class="item-inner">
                  <div class="item-title item-label">Owned To</div>
                  <div class="item-input-wrap">
                    <input type="text" placeholder="Select date" readonly="readonly" id="owned-to"
                      name="vehicle_owned_to" />
                  </div>
                </div>
              </div>
            </li>
            <!-- * Input -->

          </ul>
        </div>



        <div class="block-title">Vehicle Tagging</div>

        <div class="list list-strong-ios list-dividers-ios inset-ios">
          <ul>
            <li class="toggle-inline">
              <div>Allow this vehicle to be discovered & tagged via it's registration</div>
              <div>
                <label class="toggle">
                  <input type="checkbox" name="vehicle_tagging" />
                  <span class="toggle-icon"></span>
                </label>
              </div>
            </li>

          </ul>
        </div>



        <div class="block">
          <button class="mt-4 button button-large button-fill color-red" id="delete-vehicle" type="button">Delete
            Vehicle</button>
        </div>
      </form>
    </div>
  </div>
</template>

/pages/profile-garage-vehicle-view.html
<template>
  <div class="page" data-name="profile-garage-vehicle-view">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="link icon-only open-qr-modal">
            <i class="icon f7-icons">qrcode</i>
          </a>
          <a href="/notifications/" class="link icon-only">
            <div class="notification-count"></div>
            <i class="icon f7-icons">bell</i>
          </a>
        </div>

      </div>
    </div>

    <div class="page-content profile-landing-page infinite-scroll-content" data-infinite-distance="50">
      <div class="loading-fullscreen garage">
        <div class="preloader preloader-central">
          <span class="preloader-inner"><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
              class="preloader-inner-line"></span>
          </span>
        </div>
      </div>

      <div class="vehicle-profile-background" style="background-color:gray;">
        <a class="vehicle-profile-image" href="/profile/" style="background-color:gray;"></a>
      </div>

      <div class="profile-garage-intro text-center">
        <h1>
          <div class="skeleton-block" style="width: 100px"></div>
        </h1>
        <p class="garage-owned-information">
        <div class="skeleton-block" style="width: 100px"></div>
        </p>
        <p class="garage-vehicle-description">
        <div class="skeleton-block" style="width: 100px"></div>
        </p>

        <div class="profile-links-edit garage d-flex">
          <!-- <div class="profile-link dark-bg garage-add-post">Add Post</div> -->
        </div>
      </div>

      <div class="profile-lower">
        <div class="profile-tabs garage-view">
          <a href="#tab-1" class="tab-link tab-link-active" id="garage-posts">Posts</a>
          <a href="#tab-2" class="tab-link" id="garage-tags">Tags</a>
        </div>

        <swiper-container class="tabs">
          <swiper-slide id="tab-1" class="tab">
            <div class="swiper-inner-container">
              <div class="profile-grid" id="garage-posts-tab">
              </div>

              <!-- Preloader for infinite scroll -->
              <div class="infinite-scroll-preloader garage-posts-tab">
                <div class="preloader"></div>
              </div>
            </div>
          </swiper-slide>
          <swiper-slide id="tab-2" class="tab">
            <div class="swiper-inner-container">
              <div class="profile-grid" id="garage-tags-tab">
              </div>

              <!-- Preloader for infinite scroll -->
              <div class="infinite-scroll-preloader garage-tags-tab">
                <div class="preloader"></div>
              </div>
            </div>
          </swiper-slide>
        </swiper-container>
      </div>
    </div>
  </div>
</template>

/pages/profile-view.html
<template>
    <div class="page" data-name="profile-view">
        <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
                <div class="left">
                    <a class="link back">
                        <i class="icon icon-back"></i>
                    </a>
                </div>
                <div class="middle">
                    <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
                </div>
                <div class="right">
                    <a href="#" class="link icon-only open-qr-modal">
                        <i class="icon f7-icons open-qr-modal">qrcode</i>
                    </a>
                </div>
            </div>
        </div>

        <div class="page-content profile-landing-page infinite-scroll-content view-page ptr-content ptr-watch-scrollable"
            data-infinite-distance="50" data-ptr-distance="130">

            <div class="ptr-preloader">
                <div class="preloader"></div>
                <div class="ptr-arrow"></div>
            </div>

            <div class="loading-fullscreen">
                <div class="preloader preloader-central">
                    <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                            class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                            class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                            class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                            class="preloader-inner-line"></span>
                    </span>
                </div>
            </div>

            <div class="profile-background" style="background-color:gray;">
            </div>
            <div class="profile-head">
                <div class="profile-image" style="background-color:gray;"></div>
                <h3 class="name profile-username">
                    <div class="skeleton-block" style="width: 100px"></div>
                </h3>
                <h5 class="subtext profile-name">
                    <div class="skeleton-block" style="width: 100px"></div>
                </h5>
                <div class="mt-1">
                    <div class="btn w-70 bg-dark user-follow-btn">
                        <div class="skeleton-block" style="width: 80px"></div>
                    </div>
                </div>
            </div>

            <div class="profile-links">
                <div class="profile-links-external d-flex">
                    <a class="profile-link social-link link external " id="instagram" target="_blank"
                        href="https://www.instagram.com/"><img src="assets/img/icon-instagram.svg" />
                        <span>Instagram</span></a>
                    <a class="profile-link social-link link external " id="facebook" target="_blank"
                        href="https://www.facebook.com"><img src="assets/img/icon-facebook.svg" />
                        <span>Facebook</span></a>
                    <a class="profile-link social-link link external " id="tiktok" target="_blank"
                        href="https://www.tiktok.com"><img src="assets/img/icon-tiktok.svg" /> <span>Tiktok</span></a>
                    <a class="profile-link social-link link external " id="youtube" target="_blank"
                        href="https://www.youtube.com"><img src="assets/img/icon-youtube.svg" />
                        <span>YouTube</span></a>
                    <a class="profile-link social-link popup-open" data-popup=".links-popup"><img
                            src="assets/img/icon-link.svg?v=1.4" />
                        <span>More</span></a>
                </div>
            </div>

            <div class="profile-lower">
                <div class="profile-tabs">
                    <a href="#profileview-posts-tab" class="tab-link tab-link-active" id="my-posts">Posts</a>
                    <a href="#profileview-garage-tab" class="tab-link" id="my-garage">Garage</a>
                    <a href="#profileview-tags-tab" class="tab-link" id="my-tags">Tags</a>
                </div>

                <swiper-container class="tabs">

                    <swiper-slide id="profileview-posts-tab" class="tab  tab-active">
                        <div class="swiper-inner-container">
                            <div class="profile-grid list virtual-list" id="profile-view-grid-posts">
                                <!-- Content generates here -->
                            </div>

                            <!-- Preloader for infinite scroll -->
                            <div class="infinite-scroll-preloader posts-tab view-profile">
                                <div class="preloader"></div>
                            </div>
                        </div>
                    </swiper-slide>

                    <swiper-slide id="profileview-garage-tab" class="tab">
                        <div class="swiper-inner-container">
                            <div class="listview-title garage-sub-title">Current Vehicles</div>

                            <ul
                                class="listview image-listview media transparent flush pt-1 pview-current-vehicles-list">
                                <!-- Content generates here -->
                            </ul>

                            <div class="listview-title garage-sub-title mt-2">Past Vehicles</div>
                            <ul class="listview image-listview media transparent flush pt-1 pview-past-vehicles-list">
                                <!-- Content generates here -->
                            </ul>

                        </div>
                    </swiper-slide>


                    <swiper-slide id="profileview-tags-tab" class="tab">
                        <div class="swiper-inner-container">
                            <div class="profile-grid list virtual-list" id="profile-view-grid-tags">
                                <!-- Content generates here -->
                            </div>

                            <!-- Preloader for infinite scroll -->
                            <div class="infinite-scroll-preloader tags-tab view-profile">
                                <div class="preloader"></div>
                            </div>
                        </div>
                    </swiper-slide>
                </swiper-container>
            </div>
        </div>

        <div class="popup links-popup three-quarter-popup">
            <div class="view">
                <div class="page">
                    <div class="navbar">
                        <div class="navbar-inner">
                            <div class="title">More Links</div>
                            <div class="right">
                                <!-- Link to close popup -->
                                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
                            </div>
                        </div>
                    </div>
                    <div class="page-content">
                        <div class="block profile-external-links">
                            <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                                <ul>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

/pages/profile.html
<template>
  <div class="page" data-name="profile">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a href="#" class="link icon-only panel-open" data-panel="left">
            <i class="icon f7-icons">bars</i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="link icon-only open-qr-modal">
            <i class="icon f7-icons">qrcode</i>
          </a>
          <a href="/notifications/" class="link icon-only">
            <div class="notification-count"></div>
            <i class="icon f7-icons">bell</i>
          </a>
        </div>
      </div>
    </div>

    <div
      class="page-content no-swipeback profile-landing-page infinite-scroll-content my-profile ptr-content ptr-watch-scrollable"
      data-infinite-distance="50" data-ptr-distance="120">
      <div class="ptr-preloader">
        <div class="preloader"></div>
        <div class="ptr-arrow"></div>
      </div>
      <div class="profile-background" style="background-color: gray;"></div>
      <div class="profile-head">
        <div class="profile-image" style="background-color: gray;"></div>
        <h3 class="name profile-username">@</h3>
        <h5 class="subtext profile-name"></h5>
      </div>

      <div class="profile-links">
        <div class="profile-links-edit d-flex">
          <!-- <a class="profile-link popup-open" data-popup=".edit-profile-popup">Edit Profile</a> -->
          <a class="profile-link" href="/profile-edit/">Edit Profile</a>
          <a class="profile-link dark-bg" href="/profile-garage-edit/">Edit Garage</a>
        </div>

        <div class="profile-links-external d-flex">
          <a class="profile-link social-link link external " id="instagram" target="_blank"
            href="https://www.instagram.com/"><img src="assets/img/icon-instagram.svg" /> <span>Instagram</span></a>
          <a class="profile-link social-link link external " id="facebook" target="_blank"
            href="https://www.facebook.com"><img src="assets/img/icon-facebook.svg" /> <span>Facebook</span></a>
          <a class="profile-link social-link link external " id="tiktok" target="_blank"
            href="https://www.tiktok.com"><img src="assets/img/icon-tiktok.svg" /> <span>Tiktok</span></a>
          <a class="profile-link social-link link external " id="youtube" target="_blank"
            href="https://www.youtube.com"><img src="assets/img/icon-youtube.svg" /> <span>YouTube</span></a>
          <a class="profile-link social-link popup-open" data-popup=".links-popup"><img
              src="assets/img/icon-link.svg?v=1.4" />
            <span>More</span></a>
        </div>
      </div>

      <div class="profile-lower">
        <div class="profile-tabs">
          <a href="#profile-posts-tab" class="tab-link tab-link-active" id="my-posts">Posts</a>
          <a href="#profile-garage-tab" class="tab-link" id="my-garage">Garage</a>
          <a href="#profile-tags-tab" class="tab-link" id="my-tags">Tags</a>
        </div>

        <swiper-container class="tabs">
          <swiper-slide id="profile-posts-tab" class="tab tab-active">
            <div class="swiper-inner-container">
              <div class="profile-grid" id="profile-grid-posts">
                <!-- Content generates here -->
              </div>

              <!-- Preloader for infinite scroll -->
              <div class="infinite-scroll-preloader posts-tab">
                <div class="preloader"></div>
              </div>
            </div>
          </swiper-slide>

          <swiper-slide id="profile-garage-tab" class="tab ">
            <div class="swiper-inner-container">
              <div class="listview-title garage-sub-title">Current Vehicles</div>

              <ul class="listview image-listview media transparent flush pt-1 current-vehicles-list">
                <!-- Content generates here -->
              </ul>

              <div class="listview-title garage-sub-title mt-2">Past Vehicles</div>
              <ul class="listview image-listview media transparent flush pt-1 past-vehicles-list">
                <!-- Content generates here -->
              </ul>

            </div>
          </swiper-slide>

          <swiper-slide id="profile-tags-tab" class="tab">
            <div class="swiper-inner-container">
              <div class="profile-grid" id="profile-grid-tags">
                <!-- Content generates here -->
              </div>

              <!-- Preloader for infinite scroll -->
              <div class="infinite-scroll-preloader tags-tab">
                <div class="preloader"></div>
              </div>
            </div>
          </swiper-slide>
        </swiper-container>
      </div>
    </div>

    <div class="popup links-popup three-quarter-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title">More Links</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>
          <div class="page-content">
            <div class="block profile-external-links">
              <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                <ul>
                </ul>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Edit Profile Slider -->
    <div class="popup edit-profile-popup three-quarter-popup">
      <div class="view">
        <div class="page">
          <div class="navbar">
            <div class="navbar-inner">
              <div class="title">Edit Profile</div>
              <div class="right">
                <!-- Link to close popup -->
                <a class="link popup-close"><i class="icon f7-icons">xmark</i></a>
              </div>
            </div>
          </div>
          <div class="page-content">

            <div class="block">
              <div class="list links-list list-outline-ios list-strong-ios list-dividers-ios">
                <ul>
                  <li><a href="/profile-edit-images/">Edit Profile Images</a></li>
                  <li><a href="/profile-edit-socials/">Manage Social Links</a></li>
                  <li><a href="/profile-edit-mydetails/">My Details</a></li>
                  <li><a href="/profile-edit-username/">Username</a></li>
                </ul>
              </div>
            </div>

          </div>
        </div>
      </div>
    </div>
    <!-- * Edit Profile Slider -->
  </div>
</template>

/pages/search.html
<template>
  <div class="page" data-name="search">
    <!-- Top Navbar -->
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <i class="icon icon-back"></i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="link icon-only open-qr-modal">
            <i class="icon f7-icons">qrcode</i>
          </a>
          <a href="#" class="link icon-only">
            <i class="icon f7-icons">bell</i>
          </a>
        </div>
      </div>
    </div>

    <!-- Page content-->
    <div class="page-content infinite-scroll-content">
      <div class="discovery-wrap">
        <div class="searchbar">
          <div class="searchbar-inner">
            <div class="searchbar-input-wrap">
              <input type="search" placeholder="Search" class="discover-search" id="discover-search"
                aria-autocomplete="none" />
              <i class="searchbar-icon"></i>
              <!-- <span class="input-clear-button"></span> -->
            </div>
            <span class="searchbar-disable-button if-not-aurora">Cancel</span>
          </div>
          <ul id="search-history" class="search-history"></ul> <!-- Container for the search history -->
        </div>

        <div class="tabbar-nav pt-1">
          <ul>
            <li><a href="#top-results" class="tab-link tab-link-active" data-type="all">Top</a></li>
            <li><a href="#events-results" class="tab-link" data-type="events">Events</a></li>
            <li><a href="#venues-results" class="tab-link" data-type="venues">Venues</a></li>
            <li><a href="#users-results" class="tab-link" data-type="users">Users</a></li>
            <li><a href="#vehicles-results" class="tab-link" data-type="vehicles">Vehicles</a></li>
          </ul>
        </div>

        <div class="tabs">
          <!-- Tab - Top Results -->
          <div class="tab tab-active" id="top-results">
            <div class="list list-outline-ios list-strong-ios list-dividers-ios mt-2">
              <div class="loading-fullscreen search-view">
                <div class="preloader preloader-central">
                  <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                      class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                      class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                      class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                      class="preloader-inner-line"></span>
                  </span>
                </div>
              </div>
            </div>
          </div>

          <!-- Tab - Events -->
          <div class="tab" id="events-results">
            <div class="list list-outline-ios list-strong-ios list-dividers-ios mt-2">
              <ul>
                <div class="loading-fullscreen search-view">
                  <div class="preloader preloader-central">
                    <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span>
                    </span>
                  </div>
                </div>
              </ul>
            </div>
          </div>


          <!-- Tab - Venues -->
          <div class="tab" id="venues-results">
            <div class="list list-outline-ios list-strong-ios list-dividers-ios mt-2">
              <ul>
                <div class="loading-fullscreen search-view">
                  <div class="preloader preloader-central">
                    <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span>
                    </span>
                  </div>
                </div>
              </ul>
            </div>
          </div>

          <!-- Tab - Users -->
          <div class="tab" id="users-results">
            <div class="list list-outline-ios list-strong-ios list-dividers-ios mt-2">
              <ul>
                <div class="loading-fullscreen search-view">
                  <div class="preloader preloader-central">
                    <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span>
                    </span>
                  </div>
                </div>
              </ul>
            </div>
          </div>

          <!-- Tab - Vehicles -->
          <div class="tab" id="vehicles-results">
            <div class="list list-outline-ios list-strong-ios list-dividers-ios mt-2">
              <ul>
                <div class="loading-fullscreen search-view">
                  <div class="preloader preloader-central">
                    <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                        class="preloader-inner-line"></span>
                    </span>
                  </div>
                </div>
              </ul>
            </div>
          </div>

        </div>
      </div>
    </div>
    <!-- Page content-->
  </div>
</template>

/pages/signup-complete.html
<template>
  <div class="page no-navbar no-toolbar no-swipeback" data-name="signup-complete">
    <div class="page-content env-padding">
      <div class="login-form">
        <div class="block">
          <div class="section"><img src="assets/img/start01.jpg" />
            <h1 class="">Welcome to DriveLife</h1>
            <h4>Early Access</h4>
          </div>
          <p>Brought to you be CarEvents.com, DriveLife brings to you a world of car events, venues, media and more.</p>
          <p>This is currently an early access app and will be updated with tonnes of new features in the coming weeks,
            including video and gallery uploading, our online store and more.</p>
          <p>Thank you for joining us on a journey to empower car enthusiasts!</p>
        </div>
        <div class="login-buttons">
          <button type="submit" class="button button-large button-fill margin-bottom" id="signup-complete">
            Continue
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

/pages/signup-step1.html
<template>
  <div class="page  no-toolbar no-swipeback" data-name="signup-step1">
    <div class="navbar">
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <!-- <i class="icon icon-back"></i> -->
          </a>
        </div>
        <div class="right">
          <a href="/login/" class="link">
            Login
          </a>
        </div>
      </div>
    </div>
    <div class="page-content">
      <div class="login-form">
        <div class="section">
          <h1>Register</h1>
          <h4>Create your DriveLife Account</h4>
        </div>

        <form id="sign-up-step1">
          <div class="list list-strong-ios list-dividers-ios inset-ios">
            <ul>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">First Name</div>
                  <div class="item-input-wrap">
                    <input type="text" name="first_name" placeholder="Enter first name" />
                    <span class="input-clear-button"></span>
                  </div>
                </div>
              </li>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Last Name</div>
                  <div class="item-input-wrap">
                    <input type="text" name="last_name" placeholder="Enter last name" />
                    <span class="input-clear-button"></span>
                  </div>
                </div>
              </li>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Email</div>
                  <div class="item-input-wrap">
                    <input type="email" name="email" placeholder="Enter email address" />
                    <span class="input-clear-button"></span>
                  </div>
                </div>
              </li>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Password</div>
                  <div class="item-input-wrap">
                    <input type="password" name="password" placeholder="Enter password" />
                    <span class="toggle-password"></span>
                  </div>
                </div>
              </li>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-title item-label">Confirm Password</div>
                  <div class="item-input-wrap">
                    <input type="password" name="confirm_password" placeholder="Enter password" />
                    <span class="toggle-password"></span>
                  </div>
                </div>
              </li>
            </ul>
          </div>

          <div class="list list-strong-ios list-dividers-ios inset-ios">
            <ul>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="agree_terms" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">I agree to the <a class="popup-open" data-popup=".terms-popup">Terms &
                      Conditions</a></div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="agree_privacy" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">I agree to the <a class="popup-open" data-popup=".privacy-popup">Privacy
                      Policy</a></div>
                </label>
              </li>
            </ul>
          </div>

          <div class="login-buttons">
            <button type="submit" class="button button-large button-fill margin-bottom">
              Next
            </button>
          </div>
        </form>



        <!-- Popups - Terms -->
        <div class="popup terms-popup three-quarter-popup">
          <div class="view">
            <div class="page">

              <div class="page-content">
                <div class="privacy-statement text-center">
                  <h3>Terms & Conditions</h3>
                  <p><strong>INTRODUCTION</strong></p>
                  <p>1.1 Car Events Ltd. (“CarEvents.com”, “we”, “us”, “our”) is a company registered in England and
                    Wales under company
                    number 12698619, with registered offices at The Motorist Lennerton Lane, Sherburn In Elmet, Leeds,
                    England, LS25
                    6JE.</p>
                  <p>1.2 We operate an online and mobile App service where you can search for and purchase car show and
                    related event
                    tickets. No tickets are sold by us, we simply facilitate the sale process via a direct link to the
                    show organiser’s
                    Stripe and/or Paypal accounts.</p>
                  <p>1.3 This “Purchase Policy” sets out the terms and conditions applicable to purchases of Tickets via
                    any of the
                    CarEvents.com platforms. If you are making a purchase online, this Purchase Policy also incorporates
                    our website
                    Terms of Use.</p>
                  <p>1.4 We promote the sale of Tickets and associated products and services on behalf of event
                    organisers, promoters,
                    venues, car clubs and other persons involved in the organisation of events (“Event Partner(s)”).
                    Please note that we
                    are not responsible for organising or delivering the events themselves.</p>
                  <p>1.5 It is the responsibility of the Event Organisers to set the number and type of Tickets
                    available via the
                    CarEvents.com online organiser dashboard tool on an event by event basis.</p>
                  <p>1.6 We promote the sale of Tickets from numerous online interfaces (desktop, mobile website and
                    app). All of our
                    interfaces access the same ticketing system and ticket inventory, therefore Tickets for popular
                    events may sell-out
                    quickly. Occasionally, additional Tickets for sold-out events may become available prior to events.
                    However, we do
                    not control Ticket inventory or its availability.</p>
                  <p>1.7 We can’t offer any exchanges or refunds if the event is going ahead on the date originally
                    planned. If the date
                    of an event is changed, your tickets will be transferred to the new date. If the date does change,
                    you may request a
                    refund by emailing the event organiser directly (within a minimum of 14 days before the event takes
                    place). Refunds
                    are not guaranteed and are at the event organiser’s discretion.</p>
                  <p>1.8 Nothing in this Purchase Policy affects your statutory rights as a consumer. For further
                    information about your
                    statutory rights, please contact Citizens Advice.</p>
                  <p>1.9 We act solely as the event orgnaiser’s fulfilment partner and are not responsible for any
                    aspects of your
                    purchase from the event partner, save for delivery of tickets to you. If you have any queries or
                    complaints
                    regarding your purchase of tickets from any of our event partners or other third parties, please
                    contact them
                    directly.</p>
                  <p><strong>YOUR ACCOUNT AND REGISTRATION</strong></p>
                  <p>2.1 In order to set up a CarEvents.com account to purchase or sell Tickets you must:</p>
                  <p>(a) be at least 18 years old (or the age of legal capacity in the country of purchase) and able to
                    enter into
                    legally binding contracts; and</p>
                  <p>(b) follow the instructions to set up a password-protected account providing your correct full name
                    and email
                    address (all your details must be kept up to date at all times).</p>
                  <p>2.2 In order to setup a CarEvents.com account to purchase a ticket, you must be at least 16 years
                    old, however
                    please note that the contract (and associated rights and obligations) for the purchase of that
                    Ticket remains
                    between you and the Event Partner.</p>
                  <p>2.3 If you are making purchases on behalf of a company or other legal entity, you represent and
                    warrant that you
                    have the authority to bind that company or other legal entity (and this Purchase Policy and
                    references to “you”
                    refer and apply to that company or other legal entity).</p>
                  <p>2.4 Please refer to our Privacy Notice and Cookies Policy for more details on how we use and
                    protect your personal
                    data. If we are investigating your account, or if we are investigated ourselves, you agree to comply
                    fully with our
                    requests for information about you and your purchases.</p>
                  <p>2.5 You must not create or use multiple accounts with the purpose or intention of circumventing any
                    of the terms of
                    this Purchase Policy or concealing your identity or other personal details.</p>
                  <p>2.6 You must not use our website or app, your account and/or any of our related services (together
                    the “CE
                    Services”) for any unlawful purpose or in any unlawful manner. If we discover or suspect that you
                    have used or are
                    using or attempting to use the CE Services in such a way that a criminal offence has been, is being
                    or might be
                    committed, we are required by law to report your identity and details of such activity to the
                    relevant authorities
                    (and any relevant Event Partner).</p>
                  <p>2.7 We reserve the right to terminate your account and/or cancel any of your orders and/or prohibit
                    you from making
                    future orders or using the CE Services in future if:</p>
                  <p>(a) any abusive or threatening behaviour is carried out by you or on your behalf or via your
                    account;</p>
                  <p>(b) we suspect any fraudulent activity or other illegal activity is carried out by you or on your
                    behalf or via
                    your account;</p>
                  <p>(c) we suspect any unauthorised use of your account or other unauthorised activity is carried out
                    by you or on your
                    behalf or via your account;</p>
                  <p>(d) we are ordered to do so by any legal or regulatory authority; and/or</p>
                  <p>(e) you otherwise breach the terms of this Purchase Policy or any other applicable policies or
                    terms and conditions
                    (including any applicable Event Partner’s terms and conditions).</p>
                  <p>2.8 You may close your account by emailing members@carevents.com. However, please note that such
                    closure shall not
                    take effect until after any events that you have purchased Tickets for have taken place.</p>
                  <p>2.9 Termination of your account and/or cancellations of any purchases under this Purchase Policy
                    shall not affect
                    our or your rights and liabilities which have accrued prior to and including the date of such
                    termination or
                    cancellation.</p>

                  <p><strong>LEGALLY BINDING CONTRACT</strong></p>
                  <p>3.1 In order to make a purchase from us or any of our event partners, you must be at least 18 years
                    old (or the age
                    of legal capacity in the country of purchase) and able to enter into legally binding contracts. If
                    you are
                    purchasing online, you must also have a CarEvents.com account and a valid credit or debit card
                    issued in your name.
                  </p>
                  <p>3.2 Any purchase from us or our event partners forms a legally binding contract that is subject to:
                    (i) this
                    Purchase Policy; (ii) any special terms and conditions stated to be applicable to a ticket and/or
                    event; (iii) other
                    terms and conditions of the Event Partner(s) and/or event; and (iv) any venue terms and conditions
                    (including
                    conditions of entry). You should read this Purchase Policy carefully before you make a purchase.</p>
                  <p>3.3 By purchasing one or more Items from us or our event partners, you acknowledge that you have
                    read, understood
                    and agree to be bound by the terms and conditions of this Purchase Policy. If you do not agree with
                    this Purchase
                    Policy or any other applicable terms and conditions, or if you cannot comply with any of them, then
                    you must not
                    make a purchase.</p>
                  <p>3.4 We reserve the right from time to time to make changes to this Purchase Policy. Where we make
                    any such changes,
                    we shall post the updated version of this Purchase Policy on our website. Therefore, we recommend
                    you check this
                    Purchase Policy regularly to stay informed of its current terms and conditions. All purchases are
                    subject to the
                    applicable version of this Purchase Policy that was published at the time of purchase. If you do not
                    agree with any
                    revised version of this Purchase Policy, or if you cannot comply with it, then you must not make a
                    purchase.</p>

                  <p><strong>PRICES, PAYMENT AND PLACING ORDERS</strong></p>
                  <p>4.1 Accepted methods of payment include Paypal, Visa and MasterCard debit or credit cards (via
                    Stripe)</p>
                  <p>4.2 Your contract for purchase starts once we have confirmed your order and ends immediately after
                    completion of
                    the event for which you have purchased Item(s).</p>
                  <p>4.3 If you do not receive an order confirmation after submitting payment information, or if you
                    experience an error
                    message or service interruption after submitting payment information, it is your responsibility to
                    confirm via your
                    CarEvents.com account or contacting the Event Partner whether or not your order has been placed.
                    Only you may be
                    aware of any problems that may occur during the purchase process. We will not be responsible for any
                    costs or losses
                    you incur if you assume that an order was or was not placed because you failed to receive an order
                    confirmation.</p>
                  <p>4.4 All purchases are subject to credit or debit card verification (if applicable), other security
                    checks. Your
                    order may be cancelled if it has not passed the relevant verification process or if payment is not
                    received in full.
                    In rare circumstances, if your payment is recalled by the associated bank or payment provider, we
                    reserve the right
                    to cancel and refund any order for which an order confirmation has been sent. We accept no
                    responsibility or
                    liability for such cancellations, as these are outside our control.</p>
                  <p>4.5 It is prohibited to obtain or attempt to obtain any Items through unauthorised use of any
                    robot, spider or
                    other automated device or software, or through unauthorised framing or linking to any website, or
                    through any other
                    illegal or unauthorised activity. We reserve the right to cancel any orders that we reasonably
                    suspect to have been
                    made in breach of this Purchase Policy, without any notice to you, and any and all Items obtained as
                    part of such
                    orders will be void.</p>
                  <p>4.6 To prevent fraud and protect us, we reserve the right to carry out checks and/or request that
                    additional
                    information be provided in order to verify purchases. We reserve the right to cancel any orders that
                    we reasonably
                    suspect to have been made fraudulently, without any notice to you, and any and all Items obtained as
                    part of such
                    orders will be void.</p>
                  <p>4.7 Please ensure that you read the full Item description details and are happy with your selection
                    prior to
                    purchase as we may be unable to rectify issues arising as a result of your mistake.</p>

                  <p><strong>DELIVERY</strong></p>
                  <p>6.1 All CarEvents.com tickets are supplied as an e-delivery, you are responsible for either (i)
                    ensuring that you
                    log in to your CarEvents.com account and download your Tickets in good time prior to the event; or
                    (ii) providing a
                    valid email address for e-delivery of Tickets and ensuring that you are able to receive delivery of
                    the Tickets by
                    email (for example by ensuring that your email mailbox does not reject, bounce or otherwise prevent
                    any relevant
                    emails from being delivered, and by checking your email mailbox regularly (including junk or spam
                    folders).</p>

                  <p><strong>TICKET RIGHTS AND OBLIGATIONS</strong></p>
                  <p>8.1 Any Ticket you purchase via CE Services remains the property of the relevant Event Partner and
                    is a personal
                    revocable licence which may be withdrawn, and admission refused, at any time.</p>

                  <p><strong>TICKET RESTRICTIONS</strong></p>
                  <p>9.1 When purchasing Tickets from an Event Partner, you are limited to a specified number of Tickets
                    for each event.
                    This number is included on the first purchase page and is verified with every order. This policy is
                    in effect to
                    discourage and prevent unfair ticket buying practices. Tickets may be restricted to a maximum number
                    per person (or
                    business, as applicable), per credit or debit card and, for some events or tours, a restriction may
                    apply per
                    household as well. We reserve the right to cancel any order(s) for Tickets purchased in excess of
                    the relevant
                    limits without notice.</p>
                  <p>9.2 Tickets may be sold subject to certain restrictions on entry or use, such as a minimum age for
                    entry. Any such
                    restrictions will be displayed or otherwise notified to you before or at the time of booking. It is
                    your
                    responsibility to ensure that you read all notifications and other important information displayed
                    or notified to
                    you as part of the purchase process. We will not be responsible if you or any guests under your
                    booking are refused
                    admission because of a failure to meet or prove that you/they meet any restrictions (e.g. a minimum
                    age
                    requirement).</p>
                  <p>9.3 You are not entitled to purchase any Tickets as a trader acting in the course of business with
                    the intention of
                    reselling your Tickets for profit unless formal written permission is given by us and the relevant
                    Event Partner in
                    advance. If we discover or have reason to suspect that you have purchased and intend to resell, or
                    have sold Tickets
                    in breach of this clause, we reserve the right to cancel your Tickets without notice.</p>
                  <p>9.4 You may not resell or transfer your Tickets if prohibited by law. In addition, Event Partners
                    may restrict or
                    prohibit the resale or transfer of tickets for some events. Any resale or transfer (or attempted
                    resale or transfer)
                    of a ticket in breach of the applicable law or any restrictions or prohibition imposed by an Event
                    Partner is
                    grounds for seizure or cancellation of that Ticket.</p>
                  <p>9.5 Tickets purchased from us may not:</p>
                  <p>(a) be used for advertising, promotions, contests or sweepstakes (or for other such similar
                    commercial gain);
                    and/or</p>
                  <p>(b) be combined with any hospitality, travel or accommodation service and/or any other merchandise,
                    product or
                    service to create a package for sale or other distribution,</p>
                  <p>unless formal written permission is given by us and the relevant Event Partner in advance and
                    provided that even if
                    such permission is granted, use of our or any Event Partner’s trade marks and other intellectual
                    property is subject
                    to the express prior written consent of the owner.</p>

                  <p><strong>EVENT TIMINGS AND ADMISSIONS</strong></p>
                  <p>10.1 Please note that advertised start times of events are subject to change.</p>
                  <p>10.2 Tickets are sold subject to the Event Partner’s right to alter or vary the programme of an
                    event due to events
                    or circumstances beyond its reasonable control without being obliged to refund monies or exchange
                    tickets, unless
                    such change is a material alteration as described in clause 11, in which case the provisions of that
                    clause shall
                    apply.</p>
                  <p>10.3 Generally, every effort to admit latecomers will be made at a suitable break in the event, but
                    admission
                    cannot always be guaranteed.</p>
                  <p>10.4 The event venue may conduct security searches of you and other patrons for safety and security
                    purposes and/or
                    may refuse admission to patrons (including you) breaching or suspected of breaching any terms and
                    conditions of the
                    event or any Event Partner.</p>
                  <p>10.5 Admission to all events is subject to the terms of admission of the relevant venue, and
                    certain items (e.g.
                    laser pens, mobile phones, dogs (except guide dogs) and patrons’ own food and drink) may be
                    prohibited. Please check
                    with the venue directly. The unauthorised use of photographic and/or recording equipment at events
                    may also be
                    prohibited. The use of drones or similar equipment for any reason in, at or near the event venue
                    without written
                    permission from the event partner is strictly prohibited.</p>
                  <p>10.6 Breach of any of venue terms and conditions or any unacceptable behaviour likely to cause
                    damage, nuisance or
                    injury shall entitle the Event Partner to eject you from the venue.</p>
                  <p>10.7 Event Partners reserve the right to refuse admission to the venue, or to remove any person
                    from the venue for
                    reasons of public safety, any unacceptable behaviour likely to cause damage, nuisance or injury, or
                    for any breach
                    of the Event Partners´ terms and conditions.</p>
                  <p>10.8 Unless expressly authorised by the relevant Event Partner, there will be no pass-outs or
                    re-admissions of any
                    kind.</p>
                  <p>10.9 By attending an event, you and other patrons understand and agree to being photographed,
                    filmed and/or
                    recorded in relation to the event and/or for safety and security, including filming by the police.
                    You and other
                    patrons understand and agree that resulting photographs, videos, audio recordings and/or audiovisual
                    recordings may
                    be used in any and all media for any purpose at any time throughout the world (however, you may
                    object to such use
                    by specific request to privacy@CarEvents.com).</p>
                  <p>10.10 Prolonged exposure to loud music or noise may damage your hearing and we advise you and all
                    patrons to wear
                    adequate ear protection at events.</p>
                  <p>10.11 Special effects, which may include sound, audio-visual, pyrotechnic effects or lighting
                    effects may be
                    featured at an event, which may not be suitable for those with photosensitive epilepsy, or similar
                    conditions.</p>

                  <p><strong>EVENT CANCELLATIONS AND ALTERATIONS</strong></p>
                  <p>11.1 If an event is cancelled, rescheduled or materially altered, we will use reasonable endeavours
                    to notify you
                    once we have received the relevant information and authorisation from our Event Partner. However, we
                    cannot
                    guarantee that you will be informed of such cancellation, rescheduling or alteration before the date
                    of the event.
                    It is your responsibility to ascertain whether an event has been cancelled, rescheduled or altered
                    and the date and
                    time of any rescheduled event.</p>
                  <p>11.2 Cancellation: If an event is cancelled altogether, we’ll usually just refund your tickets
                    automatically. We
                    refund the face value for each ticket – you’ll see a credit onto your card after we’ve emailed you
                    about the
                    cancellation with a timescale.</p>
                  <p>11.3 Rescheduling: If an event for which you have purchased Tickets or Packages is rescheduled,
                    Tickets and
                    Packages will usually be valid for the new date (or you will be offered Tickets or Packages of a
                    value corresponding
                    with your original Tickets or Packages for the rescheduled event, subject to availability). If the
                    date of an event
                    is changed, you may request a refund by emailing info@CarEvents.com. You must email at least 5 days
                    before the
                    event.</p>
                  <p>11.4 Material Alteration: If an event for which you have purchased Tickets or Packages is
                    “materially altered” (as
                    defined in clause 11.5 below), Tickets and Packages will usually be valid for the altered event (or
                    you will be
                    offered Tickets or Packages of a value corresponding with your original Tickets or Packages for the
                    altered event,
                    subject to availability). If you notify the event partner within the specified deadline that you do
                    not wish to
                    attend the altered event, you should be able to cancel your order and obtain a refund of the Sale
                    Price of your
                    Tickets or Packages (Service Charges and Order Processing Fees are non-refundable).</p>
                  <p>11.5 For the purposes of this Purchase Policy, a “material alteration” is a change (other than a
                    rescheduling)
                    which, in our and the relevant Event Partner’s reasonable opinion, makes the event materially
                    different to the event
                    that purchasers of Tickets, taken generally, could reasonably expect. In particular, please note
                    that the following
                    are not deemed to be “material alterations”: adverse weather conditions; changes of any advertised
                    event
                    contributor; changes to the line-up of any multi-performer event (such as a festival); curtailment
                    of the event
                    where the majority of an event is performed in full; and delays to the starting of the performance
                    of an event.</p>
                  <p>11.6 To claim a refund under clause 11.2, 11.3 or 11.4, please contact the event partner directly.
                  </p>
                  <p>11.7 Refunds will be made using the same means of payment as you used for the initial purchase.</p>

                  <p><strong>STATUTORY RIGHT TO CANCEL</strong></p>
                  <p>13.1 Tickets and Packages cannot be cancelled, exchanged or refunded after purchase, save in the
                    circumstances set
                    out in clause 11 or 12.</p>
                  <p>13.2 To meet the relevant cancellation deadline, it is sufficient for you to send your
                    communication concerning
                    your exercise of the right to cancel before the cancellation period, as set out by the event partner
                    has expired.
                  </p>
                  <p>13.3 If you cancel a purchase under this clause 13, the event partner will generally reimburse to
                    you all payments
                    received from you in relation to the relevant cancelled event.</p>

                  <p><strong>INTERPRETATION</strong></p>
                  <p>14.1 The terms “including”, “include”, “in particular”, “e.g.” or any similar expression shall be
                    construed as
                    illustrative and shall not limit the sense of the words, description, definition, phrase or term
                    preceding those
                    terms.</p>
                  <p>14.2 The headings used within this Purchase Policy are for reference purposes only and do not
                    affect its
                    interpretation. Clauses references in these terms and conditions are references to the clauses of
                    these terms and
                    conditions of this Purchase Policy.</p>
                  <p>14.3 Capitalised terms in this Purchase Policy shall have the special meaning ascribed to them as
                    set out within
                    this Purchase Policy.</p>

                  <p><strong>WARRANTIES AND INDEMNITIES</strong></p>
                  <p>15.1 You represent and warrant that the information that you submit to us in relation to your
                    account and in your
                    use of the CarEvents.com services is true, accurate and complete and you will not use any false
                    information,
                    including contact information. You further warrant and represent that you are at least 18 years old
                    (or the age of
                    legal capacity in the country of purchase) and can enter into legally binding contracts for the
                    purchase of Tickets.
                  </p>
                  <p>15.2 You represent and warrant that in using our website, you shall comply with all applicable laws
                    and
                    regulations, along with the terms of this Purchase Policy and any other applicable terms and
                    conditions.</p>
                  <p>15.3 You hereby indemnify and hold harmless us and our affiliates along with their respective
                    officers, directors,
                    employees and agents (the “Indemnified Parties”) against any losses, damages, expenses (including
                    reasonable legal
                    fees), liabilities, claims and/or demands suffered by any Indemnified Parties arising out of or in
                    connection with
                    your breach of this Purchase Policy or any other applicable terms and conditions, breach of any
                    applicable laws or
                    regulations, or breach of any third party rights.</p>

                  <p><strong>LIMITATION OF LIABILITY</strong></p>
                  <p>16.1 To the maximum extent permitted by law, we (including our affiliates, parent undertakings,
                    subsidiaries, and
                    their respective officers, directors, employees, agents, legal representatives and sub-contractors)
                    and our relevant
                    Event Partners shall not be liable for any loss, injury or damage to any person (including you) or
                    property
                    howsoever caused (including by us and/or by the Event Partner):</p>
                  <p>(a) in any circumstances where there is no breach of contract or a legal duty of care owed by us or
                    the relevant
                    Event Partner;</p>
                  <p>(b) in circumstances where such loss or damage is not directly as a result of any such breach (save
                    for death or
                    personal injury resulting from our or an Event Partner’s negligence); or</p>
                  <p>(c) to the extent that any increase in any loss or damage results from your negligence or breach by
                    you of any of
                    the terms of this Purchase Policy and/or any other applicable terms and conditions and/or any
                    applicable laws or
                    regulations.</p>
                  <p>16.2 To the maximum extent permitted by law, we (including our affiliates, parent undertakings,
                    subsidiaries, and
                    their respective officers, directors, employees, agents, legal representatives and sub-contractors)
                    and our relevant
                    Event Partners, shall not be liable for any indirect or consequential losses or loss of data,
                    profits, revenue,
                    earnings, goodwill, reputation, enjoyment or opportunity, or for distress, or any exemplary, special
                    or punitive
                    damages, arising directly or indirectly from your use of the CarEvents.com services and/or any
                    purchases made under
                    this Purchase Policy. In particular please note that:</p>
                  <p>(a) personal arrangements and expenditure, including travel, accommodation, hospitality and other
                    costs and
                    expenses incurred by you relating to an event which have been arranged by you are at your own risk,
                    and neither we
                    nor the relevant Event Partners shall be responsible or liable to you for any wasted or
                    unrecoverable costs or
                    expenditure in relation to such personal arrangements, even if caused as a result of the
                    cancellation, rescheduling
                    or alteration of an event for which you have purchased tickets under this Purchase Policy; and</p>
                  <p>(b) neither we nor any relevant Event Partner shall be responsible or liable to you for any loss of
                    enjoyment or
                    amenity, including where an event has been cancelled, rescheduled or altered; and</p>
                  <p>(c) neither we nor any relevant Event Partner shall be responsible or liable to you (and you will
                    not be entitled
                    to any refund) if admission to a venue or event is refused or revoked at any time as a result of
                    your breach of any
                    Event Partner’s terms and conditions.</p>
                  <p>16.3 Unless otherwise stated in this clause 16, our and any relevant Event Partner’s liability to
                    you in connection
                    with an event (including, but not limited to, for any cancellation, rescheduling or alteration of an
                    event) and any
                    Items you have purchased shall be limited to the price paid by you for the Items, including any
                    Service Charges but
                    excluding any Order Processing Fees.</p>
                  <p>16.4 We are not responsible for any internet connection errors experienced while using the
                    CarEvents.com services.
                  </p>
                  <p>16.5 We are not responsible for the actions or failures of any Venue, promoter or other Event
                    Partner. Under no
                    circumstances shall we be liable for death or personal injury suffered by you or your guests arising
                    out of
                    attendance at an event, unless caused by our negligence. Neither shall we be liable for any loss or
                    damage sustained
                    to your property or belongings, or those of any guests under your booking, attending an event.</p>
                  <p>16.6 We will not be liable to you for failure to perform any of our obligations under this Purchase
                    Policy to the
                    extent that the failure is caused by a force majeure event (meaning any cause beyond our reasonable
                    control
                    including without limitation, acts of God, war, insurrection, riot, civil disturbances, acts of
                    terrorism, fire,
                    explosion, flood, theft of essential equipment, malicious damage, strike, lock out, weather, third
                    party injunction,
                    national defence requirements, acts or regulations of national or local governments). This clause
                    does not affect
                    the terms of any clauses specifically providing for a right of refund.</p>
                  <p>16.7 Nothing in this Purchase Policy seeks to exclude or limit our or any Event Partner’s liability
                    for death or
                    personal injury caused by our or any Event Partner’s negligence, fraud or other type of liability
                    which cannot by
                    law be excluded or limited.</p>

                  <p><strong>QUERIES, COMPLAINTS AND DISPUTE RESOLUTION</strong></p>
                  <p>17.1 If we need to contact you, we will use your CarEvents.com account contact details. It is your
                    responsibility
                    to inform us immediately of any changes to your contact details, whether before or after receipt of
                    Items. In
                    particular, please ensure that you provide us with a valid email address as this is our preferred
                    method of
                    contacting you. You should also be aware that your email mailbox settings may treat our emails as
                    junk, so remember
                    to check your junk and/or spam folders.</p>
                  <p>17.2 If you have any queries or complaints regarding your purchase, please contact the event
                    partner directly,
                    quoting any order reference numbers.</p>
                  <p>17.3 Please note that we do not tolerate aggressive or abusive behaviour towards our staff or
                    representatives, or
                    unreasonable demands or persistence being used (including any threat, abuse or harassment towards
                    our staff or
                    representatives in any form or any media). We reserve the right to take such action we deem
                    reasonably necessary in
                    the circumstances to address any such behaviour towards our staff or representatives.</p>

                  <p><strong>GENERAL</strong></p>
                  <p>18.1 If we delay or fail to enforce any of the provisions of this Purchase Policy, it shall not
                    mean that we have
                    waived our right to do so.</p>
                  <p>18.2 We shall be entitled to assign our rights and obligations under this Purchase Policy provided
                    that your rights
                    are not adversely affected.</p>
                  <p>18.3 If any provision of this Purchase Policy is found by a competent court to be invalid or
                    unenforceable, that
                    provision shall be deemed to be omitted from this Purchase Policy and this shall not prevent the
                    other provisions
                    from continuing to remain in full force and operate separately.</p>
                  <p>18.4 If any provision of this Purchase Policy is or becomes illegal, invalid or unenforceable
                    pursuant to the law
                    of any applicable jurisdiction, this shall not affect or impair the legality, validity or
                    enforceability in that
                    jurisdiction of any other provision of this Purchase Policy.</p>
                  <p>18.5 Any of our and our Event Partners’ affiliates, successors, or assigns may enforce these terms
                    in accordance
                    with the provisions of the Contracts (Rights of Third Parties) Act 1999. Except as provided in the
                    previous
                    sentence, this Purchase Policy does not create any right enforceable by any person who is not a
                    party to it but does
                    not affect any right or remedy that a third party has which exists or is available apart from the
                    Contracts (Rights
                    of Third Parties) Act 1999.</p>
                  <p>18.6 Nothing contained within this Purchase Policy and no action taken by you or us pursuant to
                    this Purchase
                    Policy shall create, or be deemed to create, a partnership, joint venture, or establish a
                    relationship of principal
                    and agent.</p>
                  <p>18.7 Any notice provided under this Purchase Policy shall be delivered upon receipt and shall be
                    deemed to have
                    been received at the time of delivery (if delivered by hand, registered post or courier) or at the
                    time of
                    transmission (if delivered by email).</p>
                  <p>18.8 This Purchase Policy shall be governed by and construed in all respects in accordance with
                    English law and
                    both you and we agree to submit to the non-exclusive jurisdiction of the English courts in relation
                    to any dispute
                    arising out of or in connection with this Purchase Policy.</p>
                  <p><em>Publication Date: 16 January 2024.</em></p>
                </div>
              </div>
            </div>
          </div>
        </div>


        <!-- Popups - Privacy -->
        <div class="popup privacy-popup three-quarter-popup">
          <div class="view">
            <div class="page">
              <div class="page-content">
                <div class="privacy-statement text-center">
                  <h2>Privacy Policy</h2>
                  <p>This Privacy Policy describes Our policies and procedures on the collection, use and disclosure
                    of Your information
                    when You use the Service and tells You about Your privacy rights and how the law protects You.</p>
                  <p>We use Your Personal data to provide and improve the Service. By using the Service, You agree to
                    the collection and
                    use of information in accordance with this Privacy Policy.</p>
                  <h2>Interpretation and Definitions</h2>
                  <h2>Interpretation</h2>
                  <p>The words of which the initial letter is capitalized have meanings defined under the following
                    conditions. The
                    following definitions shall have the same meaning regardless of whether they appear in singular or
                    in plural.</p>
                  <h2>Definitions</h2>
                  <p>For the purposes of this Privacy Policy:</p>
                  <ul>
                    <li><strong>Account</strong> means a unique account created for You to access our Service or parts
                      of our Service.
                    </li>
                    <li><strong>Company</strong> (referred to as either “CarEvents.com”, “the Company”, “We”, “Us” or
                      “Our” in this
                      Agreement) refers to Car Events Ltd, The Motorist Lennerton Lane, Sherburn In Elmet, Leeds,
                      England, LS25 6JE.
                    </li>
                    <li><strong>Cookies</strong> are small files that are placed on Your computer, mobile device or
                      any other device by
                      a website, containing the details of Your browsing history on that website among its many uses.
                    </li>
                    <li><strong>Country</strong> refers to: United Kingdom</li>
                    <li><strong>Device</strong> means any device that can access the Service such as a computer, a
                      cellphone or a
                      digital tablet.</li>
                    <li><strong>Personal Data</strong> is any information that relates to an identified or
                      identifiable individual.</li>
                    <li><strong>Service</strong> refers to the Website.</li>
                    <li><strong>Service Provider</strong> means any natural or legal person who processes the data on
                      behalf of the
                      Company. It refers to third-party companies or individuals employed by the Company to facilitate
                      the Service, to
                      provide the Service on behalf of the Company, to perform services related to the Service or to
                      assist the Company
                      in analyzing how the Service is used.</li>
                    <li><strong>Usage Data</strong> refers to data collected automatically, either generated by the
                      use of the Service
                      or from the Service infrastructure itself (for example, the duration of a page visit).</li>
                    <li><strong>Website</strong> refers to CarEvents.com, accessible from <a
                        href="https://www.carevents.com" target="_blank"
                        rel="nofollow noopener">https://www.carevents.com</a></li>
                    <li><strong>You</strong> means the individual accessing or using the Service, or the company, or
                      other legal entity
                      on behalf of which such individual is accessing or using the Service, as applicable.</li>
                  </ul>
                  <h2>Collecting and Using Your Personal Data</h2>
                  <h2>Types of Data Collected</h2>
                  <h3>Personal Data</h3>
                  <p>While using Our Service, We may ask You to provide Us with certain personally identifiable
                    information that can be
                    used to contact or identify You. Personally identifiable information may include, but is not
                    limited to:</p>
                  <ul>
                    <li>Email address</li>
                    <li>First name and last name</li>
                    <li>Address, State, Province, ZIP/Postal code, City</li>
                    <li>Usage Data</li>
                  </ul>
                  <h3>Usage Data</h3>
                  <p>Usage Data is collected automatically when using the Service.</p>
                  <p>Usage Data may include information such as Your Device’s Internet Protocol address (e.g. IP
                    address), browser type,
                    browser version, the pages of our Service that You visit, the time and date of Your visit, the
                    time spent on those
                    pages, unique device identifiers and other diagnostic data.</p>
                  <p>When You access the Service by or through a mobile device, We may collect certain information
                    automatically,
                    including, but not limited to, the type of mobile device You use, Your mobile device unique ID,
                    the IP address of
                    Your mobile device, Your mobile operating system, the type of mobile Internet browser You use,
                    unique device
                    identifiers and other diagnostic data.</p>
                  <p>We may also collect information that Your browser sends whenever You visit our Service or when
                    You access the
                    Service by or through a mobile device.</p>
                  <h3>Tracking Technologies and Cookies</h3>
                  <p>We use Cookies and similar tracking technologies to track the activity on Our Service and store
                    certain
                    information. Tracking technologies used are beacons, tags, and scripts to collect and track
                    information and to
                    improve and analyze Our Service. The technologies We use may include:</p>
                  <ul>
                    <li><strong>Cookies or Browser Cookies.</strong> A cookie is a small file placed on Your Device.
                      You can instruct
                      Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You
                      do not accept
                      Cookies, You may not be able to use some parts of our Service. Unless you have adjusted Your
                      browser setting so
                      that it will refuse Cookies, our Service may use Cookies.</li>
                    <li><strong>Flash Cookies.</strong> Certain features of our Service may use local stored objects
                      (or Flash Cookies)
                      to collect and store information about Your preferences or Your activity on our Service. Flash
                      Cookies are not
                      managed by the same browser settings as those used for Browser Cookies. For more information on
                      how You can delete
                      Flash Cookies, please read “Where can I change the settings for disabling, or deleting local
                      shared objects?”
                      available at <a
                        href="https://helpx.adobe.com/flash-player/kb/disable-local-shared-objects-flash.html#main_Where_can_I_change_the_settings_for_disabling__or_deleting_local_shared_objects_"
                        target="_blank"
                        rel="external nofollow noopener">https://helpx.adobe.com/flash-player/kb/disable-local-shared-objects-flash.html#main_Where_can_I_change_the_settings_for_disabling__or_deleting_local_shared_objects_</a>
                    </li>
                    <li><strong>Web Beacons.</strong> Certain sections of our Service and our emails may contain small
                      electronic files
                      known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that
                      permit the Company,
                      for example, to count users who have visited those pages or opened an email and for other
                      related website
                      statistics (for example, recording the popularity of a certain section and verifying system and
                      server integrity).
                    </li>
                  </ul>
                  <p>Cookies can be “Persistent” or “Session” Cookies. Persistent Cookies remain on Your personal
                    computer or mobile
                    device when You go offline, while Session Cookies are deleted as soon as You close Your web
                    browser. You can learn
                    more about cookies here: <a href="https://www.termsfeed.com/blog/cookies/" target="_blank"
                      rel="noopener">All About
                      Cookies by TermsFeed</a>.</p>
                  <p>We use both Session and Persistent Cookies for the purposes set out below:</p>
                  <ul>
                    <li><strong>Necessary / Essential Cookies</strong>Type: Session CookiesAdministered by: UsPurpose:
                      These Cookies are
                      essential to provide You with services available through the Website and to enable You to use
                      some of its
                      features. They help to authenticate users and prevent fraudulent use of user accounts. Without
                      these Cookies, the
                      services that You have asked for cannot be provided, and We only use these Cookies to provide
                      You with those
                      services.</li>
                    <li><strong>Cookies Policy / Notice Acceptance Cookies</strong>Type: Persistent
                      CookiesAdministered by: UsPurpose:
                      These Cookies identify if users have accepted the use of cookies on the Website.</li>
                    <li><strong>Functionality Cookies</strong>Type: Persistent CookiesAdministered by: UsPurpose:
                      These Cookies allow us
                      to remember choices You make when You use the Website, such as remembering your login details or
                      language
                      preference. The purpose of these Cookies is to provide You with a more personal experience and
                      to avoid You having
                      to re-enter your preferences every time You use the Website.</li>
                  </ul>
                  <p>For more information about the cookies we use and your choices regarding cookies, please visit
                    our Cookies Policy
                    or the Cookies section of our Privacy Policy.</p>
                  <h2>Use of Your Personal Data</h2>
                  <p>The Company may use Personal Data for the following purposes:</p>
                  <ul>
                    <li><strong>To provide and maintain our Service</strong>, including to monitor the usage of our
                      Service.</li>
                    <li><strong>To manage Your Account:</strong> to manage Your registration as a user of the Service.
                      The Personal Data
                      You provide can give You access to different functionalities of the Service that are available
                      to You as a
                      registered user.</li>
                    <li><strong>For the performance of a contract:</strong> the development, compliance and
                      undertaking of the purchase
                      contract for the products, items or services You have purchased or of any other contract with Us
                      through the
                      Service.</li>
                    <li><strong>To contact You:</strong> To contact You by email, telephone calls, SMS, or other
                      equivalent forms of
                      electronic communication, such as a mobile application’s push notifications regarding updates or
                      informative
                      communications related to the functionalities, products or contracted services, including the
                      security updates,
                      when necessary or reasonable for their implementation.</li>
                    <li><strong>To provide You</strong> with news, special offers and general information about other
                      goods, services
                      and events which we offer that are similar to those that you have already purchased or enquired
                      about unless You
                      have opted not to receive such information.</li>
                    <li><strong>To manage Your requests:</strong> To attend and manage Your requests to Us.</li>
                    <li><strong>For business transfers:</strong> We may use Your information to evaluate or conduct a
                      merger,
                      divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or
                      all of Our assets,
                      whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in
                      which Personal Data
                      held by Us about our Service users is among the assets transferred.</li>
                    <li><strong>For other purposes</strong>: We may use Your information for other purposes, such as
                      data analysis,
                      identifying usage trends, determining the effectiveness of our promotional campaigns and to
                      evaluate and improve
                      our Service, products, services, marketing and your experience.</li>
                  </ul>
                  <p>We may share Your personal information in the following situations:</p>
                  <ul>
                    <li><strong>With Service Providers:</strong> We may share Your personal information with Service
                      Providers to
                      monitor and analyze the use of our Service, to contact You.</li>
                    <li><strong>For business transfers:</strong> We may share or transfer Your personal information in
                      connection with,
                      or during negotiations of, any merger, sale of Company assets, financing, or acquisition of all
                      or a portion of
                      Our business to another company.</li>
                    <li><strong>With Affiliates:</strong> We may share Your information with Our affiliates, in which
                      case we will
                      require those affiliates to honor this Privacy Policy. Affiliates include Our parent company and
                      any other
                      subsidiaries, joint venture partners or other companies that We control or that are under common
                      control with Us.
                    </li>
                    <li><strong>With business partners:</strong> We may share Your information with Our business
                      partners to offer You
                      certain products, services or promotions.</li>
                    <li><strong>With other users:</strong> when You share personal information or otherwise interact
                      in the public areas
                      with other users, such information may be viewed by all users and may be publicly distributed
                      outside.</li>
                    <li><strong>With Your consent</strong>: We may disclose Your personal information for any other
                      purpose with Your
                      consent.</li>
                  </ul>
                  <h2>Retention of Your Personal Data</h2>
                  <p>The Company will retain Your Personal Data only for as long as is necessary for the purposes set
                    out in this
                    Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply with
                    our legal
                    obligations (for example, if we are required to retain your data to comply with applicable laws),
                    resolve disputes,
                    and enforce our legal agreements and policies.</p>
                  <p>The Company will also retain Usage Data for internal analysis purposes. Usage Data is generally
                    retained for a
                    shorter period of time, except when this data is used to strengthen the security or to improve the
                    functionality of
                    Our Service, or We are legally obligated to retain this data for longer time periods.</p>
                  <h2>Transfer of Your Personal Data</h2>
                  <p>Your information, including Personal Data, is processed at the Company’s operating offices and in
                    any other places
                    where the parties involved in the processing are located. It means that this information may be
                    transferred to — and
                    maintained on — computers located outside of Your state, province, country or other governmental
                    jurisdiction where
                    the data protection laws may differ than those from Your jurisdiction.</p>
                  <p>Your consent to this Privacy Policy followed by Your submission of such information represents
                    Your agreement to
                    that transfer.</p>
                  <p>The Company will take all steps reasonably necessary to ensure that Your data is treated securely
                    and in accordance
                    with this Privacy Policy and no transfer of Your Personal Data will take place to an organization
                    or a country
                    unless there are adequate controls in place including the security of Your data and other personal
                    information.</p>
                  <h2>Disclosure of Your Personal Data</h2>
                  <h3>Business Transactions</h3>
                  <p>If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be
                    transferred. We will
                    provide notice before Your Personal Data is transferred and becomes subject to a different Privacy
                    Policy.</p>
                  <h3>Law enforcement</h3>
                  <p>Under certain circumstances, the Company may be required to disclose Your Personal Data if
                    required to do so by law
                    or in response to valid requests by public authorities (e.g. a court or a government agency).</p>
                  <h3>Other legal requirements</h3>
                  <p>The Company may disclose Your Personal Data in the good faith belief that such action is
                    necessary to:</p>
                  <ul>
                    <li>Comply with a legal obligation</li>
                    <li>Protect and defend the rights or property of the Company</li>
                    <li>Prevent or investigate possible wrongdoing in connection with the Service</li>
                    <li>Protect the personal safety of Users of the Service or the public</li>
                    <li>Protect against legal liability</li>
                  </ul>
                  <h2>Security of Your Personal Data</h2>
                  <p>The security of Your Personal Data is important to Us, but remember that no method of
                    transmission over the
                    Internet, or method of electronic storage is 100% secure. While We strive to use commercially
                    acceptable means to
                    protect Your Personal Data, We canno

/pages/signup-step2.html
<template>
  <div class="page no-toolbar no-swipeback" data-name="signup-step2">
    <div class="navbar">
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <!-- <i class="icon icon-back"></i> -->
          </a>
        </div>
        <div class="right">
        </div>
      </div>
    </div>
    <div class="page-content">

      <div class="login-form">
        <div class="section">
          <h1>Username</h1>
          <h4>Create your DriveLife username</h4>
        </div>

        <form id="sign-up-step2">
          <div class="list list-strong-ios list-dividers-ios inset-ios">
            <ul>
              <li class="item-content item-input">
                <div class="item-inner">
                  <div class="item-input-wrap item-input-center">
                    <input type="text" name="username" value="" placeholder="Username" />
                  </div>
                </div>
              </li>
            </ul>
          </div>

          <div class="login-buttons">
            <button type="submit" class="button button-large button-fill margin-bottom">
              Next
            </button>
          </div>
        </form>
      </div>
    </div>
  </div>
</template>

/pages/signup-step3.html
<template>
  <div class="page no-toolbar no-swipeback" data-name="signup-step3">
    <div class="navbar">
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <!-- <i class="icon icon-back"></i> -->
          </a>
        </div>
        <div class="right">
        </div>
      </div>
    </div>
    <div class="page-content">

      <div class="login-form">
        <div class="section">
          <h1>About You</h1>
          <h4>What type of content revs your engine?<br /><em>Tick all that apply</em></h4>
        </div>

        <form id="car-selection-form">
          <div class="list list-strong-ios list-dividers-ios inset-ios">
            <ul>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="car_type" value="0" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Sports Cars / Supercars / Exotics</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="car_type" value="1" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">JDM & Modded</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="car_type" value="2" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Classic Cars</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="car_type" value="3" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Electric Cars</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="car_type" value="4" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Hotrods & Dragsters</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="car_type" value="5" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Race Cars</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="car_type" value="6" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Motorbikes</div>
                </label>
              </li>
            </ul>
          </div>

          <div class="login-buttons">
            <button type="submit" class="button button-large button-fill margin-bottom">Next</button>
          </div>
        </form>
      </div>
    </div>
  </div>
</template>

/pages/signup-step4.html
<template>
  <div class="page no-toolbar no-swipeback" data-name="signup-step4">
    <div class="navbar">
      <div class="navbar-inner">
        <div class="left">
          <a class="link back">
            <!-- <i class="icon icon-back"></i> -->
          </a>
        </div>
        <div class="right">
        </div>
      </div>
    </div>
    <div class="page-content">

      <div class="login-form">
        <div class="section">
          <h1>About You</h1>
          <h4>Which best describes you?<br /><em>Tick all that apply</em></h4>
        </div>

        <form id="interest-selection-form">
          <div class="list list-strong-ios list-dividers-ios inset-ios">
            <ul>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="interest" value="0" checked />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">I just love cars</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="interest" value="1" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Photographer / Videographer</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="interest" value="2" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Club Owner / Admin</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="interest" value="3" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Event Organiser</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="interest" value="4" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Automotive Venue</div>
                </label>
              </li>
              <li>
                <label class="item-checkbox item-checkbox-icon-start item-content">
                  <input type="checkbox" name="interest" value="5" />
                  <i class="icon icon-checkbox"></i>
                  <div class="item-title">Automotive Business Owner</div>
                </label>
              </li>
            </ul>
          </div>

          <div class="login-buttons">
            <button type="submit" class="button button-large button-fill margin-bottom">Next</button>
          </div>
        </form>


      </div>

    </div>
  </div>
</template>

/pages/store.html
<template>
  <div class="page" data-name="store">

    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="left">
          <a href="#" class="link icon-only panel-open" data-panel="left">
            <i class="icon f7-icons">bars</i>
          </a>
        </div>
        <div class="middle">
          <div class="header-logo"><img src="assets/img/logo-dark.png" /></div>
        </div>
        <div class="right">
          <a href="#" class="link icon-only open-qr-modal">
            <i class="icon f7-icons">qrcode</i>
          </a>
          <a href="/notifications/" class="link icon-only">
            <div class="notification-count"></div>
            <i class="icon f7-icons">bell</i>
          </a>
        </div>
      </div>
    </div>


    <div class="page-content">


      <div class="store-coming-soon text-center">
        <i class="icon f7-icons">cart</i>
        <h2>Coming Soon</h2>
        <p>We're just putting the finishing touches on our store - Check back soon for a world of automotive merch,
          clothing and more!</p>
      </div>



    </div>
  </div>
</template>

/js/api/auth.js
import {
    API_URL,
    TIMEOUT_MS_HIGHER
} from "./consts.js"
import store from "../store.js"

export const getSessionUser = async () => {
    // get session from somewhere    
    if (store.state.user) {
        return store.state.user
    }

    // check in local storage
    const session = window.localStorage.getItem('token')
    if (session) {
        return session
    }

    return null
}

export const getUserDetails = async (token) => {
    try {
        let url = `${API_URL}/wp-json/app/v1/get-user-profile/`
        let response = await fetch(url, {
            method: "POST",
            mode: 'cors',
            headers: {
                "Content-Type": "application/json",
                "Authorization": `Bearer ${token}`,
            },
        })

        const data = await response.json()

        if (response.status !== 200) throw new Error(data.message)
        return data
    } catch (error) {
        console.error('Error fetching user details:', error)
        return null
    }
}

export const verifyUser = async (credentials) => {
    try {
        // Convert the credentials object to a URL query string
        const queryParams = new URLSearchParams(credentials).toString()

        // Make a GET request with the query parameters
        const response = await fetch(`${API_URL}/wp-json/ticket_scanner/v1/verify_user/?${queryParams}`, {
            method: "GET",
            mode: 'cors',
            headers: {
                "Content-Type": "application/json",
            },
        })

        if (response.ok) {
            return await response.json()
        } else {
            console.error('Failed to verify user:', response.statusText)
            return null
        }
    } catch (error) {
        console.error(error)
        return error
    }
}

export const verifyEmail = async (token) => {
    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/verify-email`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                token
            }),
        })

        const data = await response.json()
        return data
    } catch (error) {
        console.error('Error verifying email:', error)
        return null
    }
}

export const sendEmailVerification = async () => {
    try {
        const user = await getSessionUser()
        if (!user) return

        const response = await fetch(`${API_URL}/wp-json/app/v1/resend-verification-email`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
            }),
        })

        const data = await response.json()
        return data
    } catch (error) {
        console.error('Error sending email verification:', error)
        return null
    }
}


export const handleSignUp = async (user) => {
    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/register-user`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(user),
        })

        const data = await response.json()

        if (response.status !== 201) {
            return {
                success: false,
                message: data.message,
                code: data.code,
            }
        }

        return data
    } catch (error) {
        return error
    }
}

export const updateUsername = async (username, user_id = null) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        if (!user_id) {
            const user = await getSessionUser();
            if (!user) return {
                success: false,
                message: 'User id not provided'
            };
            user_id = user.id
        }

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGHER)

        const response = await fetch(`${API_URL}/wp-json/app/v1/update-username`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user_id,
                username
            }),
            signal
        })

        const data = await response.json()
        return data
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to update your username, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

export const updateContentIds = async (content_ids, user_id) => {
    const response = await fetch(`${API_URL}/wp-json/app/v1/update-selected-content`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id,
            content_ids
        }),
    })

    const data = await response.json()
    return data
}

export const updateAboutUserIds = async (content_ids, user_id) => {
    const response = await fetch(`${API_URL}/wp-json/app/v1/update-about-content`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id,
            content_ids
        }),
    })

    const data = await response.json()
    return data
}

export const updatePassword = async (new_password, old_password) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGHER)

        const response = await fetch(`${API_URL}/wp-json/app/v1/update-password`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                new_password,
                old_password
            }),
            signal
        })

        const data = await response.json()
        return data
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to update your password, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

export const updateUserDetails = async (details, email_changed) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser();
        if (!user) return;

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGHER)

        const response = await fetch(`${API_URL}/wp-json/app/v1/update-user-details`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                ...details,
                email_changed
            }),
            signal
        });

        const data = await response.json();
        return data;
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to update your details, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
};

export const getUserById = async (id) => {
    try {
        let url = `${API_URL}/wp-json/app/v1/get-user-profile-next`
        let response = await fetch(url, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: id
            }),
        })

        const data = await response.json()

        if (response.status !== 200) throw new Error(data.message)
        return data
    } catch (error) {
        console.error('Error fetching user details:', error)
        return null
    }
}

export const getUserNotifications = async (load_old_notifications = false) => {
    try {
        const user = await getSessionUser()
        if (!user) {
            throw new Error('Session user not found')
        }

        const response = await fetch(`${API_URL}/wp-json/app/v1/get-notifications`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                load_old_notifications
            }),
        })

        const data = await response.json()
        return data
    } catch (error) {
        console.error('Error fetching user notifications:', error)
        return null
    }
}

export const getNotificationCount = async () => {
    const user = await getSessionUser()
    if (!user || !user.id) return

    const response = await fetch(`${API_URL}/wp-json/app/v1/get-new-notifications-count`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user.id
        }),
    });

    const data = await response.json();
    return data;
}

export const markMultipleNotificationsAsRead = async (notificationIds) => {
    const user = await getSessionUser();
    if (!user) return null;

    const response = await fetch(`${API_URL}/wp-json/app/v1/bulk-notifications-read`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user.id
        }),
    });

    const data = await response.json();
    return data;
};

export const deleteUserAccount = async (password) => {
    const user = await getSessionUser()
    if (!user || !user.id) return

    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/delete_account`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                password
            }),
        })

        const data = await response.json()
        return data
    } catch (error) {
        return {
            success: false,
            message: 'Oops, unable to delete your account. Please try again later.'
        }
    }
}

/js/api/consts.js
export const API_URL = 'https://www.carevents.com/uk'
// export const API_URL = 'https://wordpress-889362-4267074.cloudwaysapps.com/uk'
export const TIMEOUT_MS_LOW = 15 * 1000 // 15 seconds
export const TIMEOUT_MS_HIGH = 30 * 1000 // 30 seconds
export const TIMEOUT_MS_HIGHER = 60 * 1000 // 60 seconds

export const sendRNMessage = ({
    page,
    type,
    user_id,
    association_id,
    association_type
}) => {
    if (typeof window.ReactNativeWebView === 'undefined') {
        console.warn(`This is not a react native webview, failed to send message: ${type} - ${user_id}`)
    }

    try {
        if (window !== undefined && window.ReactNativeWebView) {
            window.ReactNativeWebView.postMessage(JSON.stringify({
                type,
                page,
                user_id,
                association_id,
                association_type
            }))
        } else {
            console.warn(`Failed to send message: ${type} - ${user_id}`)
        }
    } catch (e) {
        console.error(e)
    }
}

/js/api/discover.js
import {
    getSessionUser
} from "./auth.js";
import {
    API_URL
} from "./consts.js";

/**
 * Fetches the discover data
 * 
 * @param {string} search
 * @param {'users' | 'events' | 'venues' | 'all'} type 
 * @param {number} page default 1
 * 
 */
export const getDiscoverData = async (search, type, page = 1, signal) => {
    const user = await getSessionUser();

    const response = await fetch(`${API_URL}/wp-json/app/v1/discover-search`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            search,
            user_id: user?.id,
            page,
            type,
            per_page: 10
        }),
        signal
    });

    const data = await response.json();
    return data;
};

export const fetchEvent = async (eventId) => {
    let user;
    try {
        user = await getSessionUser();
    } catch (e) {
        console.error("Error fetching user no session");
    }

    const response = await fetch(`${API_URL}/wp-json/app/v1/get-event`, {
        cache: "no-cache",
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            event_id: eventId,
            user_id: user?.id
        }),
    });

    const data = await response.json();
    if (response.status !== 200) {
        throw new Error(data.message);
    }

    return data;
};

export const fetchTrendingEvents = async (page, paginate = false, filters = null) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser();

        if (!user) {
            throw new Error('Session user not found');
        }

        const response = await fetch(`${API_URL}/wp-json/app/v1/get-events-trending`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                page,
                per_page: 10,
                paginate,
                filters
            }),
            signal
        });

        const data = await response.json();

        if (!data) {
            throw new Error('Failed to fetch trending events');
        }

        return data;
    } catch (error) {
        console.error(error);
        return null;
    }
};

export const fetchTrendingVenues = async (page, paginate = false, filters = '{}') => {
    try {
        const user = await getSessionUser();

        if (!user) {
            throw new Error('Session user not found');
        }

        const response = await fetch(`${API_URL}/wp-json/app/v1/get-venues-trending`, {
            method: "POST",
            cache: "force-cache",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                page,
                per_page: 10,
                paginate,
                filters
            }),
        });

        const data = await response.json();
        return data;
    } catch (error) {
        console.error(error);
        return null;
    }
};

export const fetchEventCats = async () => {
    const response = await fetch(`${API_URL}/wp-json/app/v1/get-event-categories`, {
        method: "GET",
        cache: "force-cache",
    });

    const data = await response.json();
    return data;
};

export const maybeFavoriteEvent = async (eventId) => {
    const user = await getSessionUser();

    if (!user || !user.id) {
        return null;
    }

    const response = await fetch(`${API_URL}/wp-json/app/v1/favourite-event`, {
        cache: "no-cache",
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            event_id: eventId,
            user_id: user.id
        }),
    });

    const data = await response.json();
    return data;
};

export const maybeFollowVenue = async (venueId) => {
    const user = await getSessionUser();

    if (!user || !user.id) {
        return null;
    }

    const response = await fetch(`${API_URL}/wp-json/app/v1/follow-venue`, {
        cache: "no-cache",
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            venue_id: venueId,
            user_id: user.id
        }),
    });

    const data = await response.json();
    return data;
}

export const fetchVenue = async (venueId) => {
    const user = await getSessionUser();

    const response = await fetch(`${API_URL}/wp-json/app/v1/get-venue`, {
        cache: "no-cache",
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            venue_id: venueId,
            user_id: user?.id
        }),
    });

    const data = await response.json();
    if (response.status !== 200) {
        throw new Error(data.message);
    }

    return data;
}

export const fetchTrendingUsers = async (page, is_vehicle = false) => {
    try {
        const user = await getSessionUser();

        if (!user) {
            throw new Error('Session user not found');
        }

        const response = await fetch(`${API_URL}/wp-json/app/v1/popular-users-cars`, {
            method: "POST",
            cache: "force-cache",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                page,
                per_page: 10,
                is_vehicle
            }),
        });

        const data = await response.json();
        return data;
    } catch (error) {
        console.error(error);
        return null;
    }
};

/js/api/garage.js
import {
    getSessionUser
} from './auth.js'
import {
    API_URL,
    TIMEOUT_MS_HIGH,
    TIMEOUT_MS_HIGHER,
} from './consts.js'

export const getUserGarage = async (profileId) => {
    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/get-user-garage`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: profileId
            }),
        })

        const data = await response.json()
        if (response.status !== 200) {
            return []
        }

        return data
    } catch (error) {
        return []
    }
}

export const getGargeById = async (garageId) => {
    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/get-garage`, {
            method: "POST",
            cache: "force-cache",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                garage_id: garageId
            }),
        })

        const data = await response.json()

        if (response.status !== 200) {
            throw new Error('Failed to fetch users posts')
        }

        return data
    } catch (error) {
        console.error(error)
        return null
    }
}

export const getPostsForGarage = async (garageId, page = 1, tagged = false) => {
    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/get-garage-posts`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                garage_id: garageId,
                page,
                limit: 10,
                tagged
            }),
        })

        const data = await response.json()
        if (response.status !== 200) {
            return null
        }

        return data
    } catch (error) {
        return null
    }
}

export const addVehicleToGarage = async (data) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGH)

        const response = await fetch(`${API_URL}/wp-json/app/v1/add-vehicle-to-garage`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                ...data,
                user_id: user.id,
            }),
            signal
        })

        const res = await response.json()
        return res
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to add your vehicle, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

export const updateVehicleInGarage = async (data, garageId) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGH)

        const response = await fetch(`${API_URL}/wp-json/app/v1/update-garage`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                ...data,
                user_id: user.id,
                garage_id: garageId,
            }),
            signal
        })

        const res = await response.json()
        return res
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to update your vehicle, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

export const deleteVehicleFromGarage = async (garageId) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGH)

        const response = await fetch(`${API_URL}/wp-json/app/v1/delete-garage`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                garage_id: garageId,
            }),
            signal
        })

        const res = await response.json()
        return res
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to delete your vehicle, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

/js/api/posts.js
import {
    API_URL,
    TIMEOUT_MS_LOW
} from './consts.js'
import {
    getSessionUser
} from './auth.js'

export async function fetchPosts(page, following = false) {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        // setTimeout(() => {
        //     controller.abort()
        // }, 5)

        const response = await fetch(`${API_URL}/wp-json/app/v1/get-posts?page=${page}&limit=10`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                following_only: following,
            }),
            signal // Abort signal
        })

        const data = await response.json()
        return data
    } catch (error) {
        console.log(error);

        return {}
    }
}

export async function fetchComments(postId) {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_LOW)

        const response = await fetch(`${API_URL}/wp-json/app/v1/get-post-comments`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                post_id: postId
            }),
            signal
        })

        const data = await response.json()
        return data
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to fetch comments, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

export const maybeLikePost = async (postId) => {
    const user = await getSessionUser()
    if (!user) return

    const response = await fetch(`${API_URL}/wp-json/app/v1/toggle-like-post`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user.id,
            post_id: postId
        }),
    })
    const data = await response.json()
    return data
}

export const addComment = async (postId, comment, comment_id = null) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_LOW)

        const response = await fetch(`${API_URL}/wp-json/app/v1/add-post-comment`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                post_id: postId,
                comment,
                parent_id: comment_id
            }),
            signal
        })

        const data = await response.json()
        return data
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to add comment, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

export const deleteComment = async (commentId) => {
    const user = await getSessionUser()
    if (!user) return

    const response = await fetch(`${API_URL}/wp-json/app/v1/delete-post-comment`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user.id,
            comment_id: commentId
        }),
    })

    const data = await response.json()
    return data
}

export const maybeLikeComment = async (commentId, ownerId) => {
    const user = await getSessionUser()
    if (!user) return

    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/toggle-like-comment`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                comment_id: commentId,
                owner_id: ownerId
            }),
        })
        const data = await response.json()

        if (!response.ok || response.status !== 200) {
            throw new Error(data.message)
        }

        return data
    } catch (e) {
        console.error("Error liking comment")
        throw new Error(e.message)
    }
}

export const getPostsForUser = async (profileId, page = 1, tagged = false, limit = 10) => {
    const response = await fetch(`${API_URL}/wp-json/app/v1/get-user-posts`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: profileId,
            page,
            limit,
            tagged
        }),
    })

    const data = await response.json()
    if (response.status !== 200) {
        return {
            data: [],
            total_pages: 0,
            page: 1,
            limit
        }
    }

    return data
}

export const getPostById = async (post_id) => {
    const user = await getSessionUser()
    if (!user) return

    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/get-post`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                post_id
            }),
        })
        const data = await response.json()
        return data
    } catch (error) {
        return null
    }
}

export const deletePost = async (post_id) => {
    const user = await getSessionUser()
    if (!user) return

    try {
        const response = await fetch(`${API_URL}/wp-json/app/v1/delete-post`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                post_id
            }),
        })

        if (response.status !== 200) {
            throw new Error("Error deleting post")
        }

        return true
    } catch (error) {
        return false
    }
}

/**
 * @param {Object} data {
     post_id: string | number;
     new_tags: PostTag[];
     removed_tags: number[];
     caption ? : string;
     location ? : string;
 }
 */
export const updatePost = async (data) => {
    try {
        const user = await getSessionUser();
        if (!user || !user.id) return null;

        const response = await fetch(`${API_URL}/wp-json/app/v1/edit-post`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                ...data
            }),
        });

        const res = await response.json();
        return res;
    } catch (e) {
        console.error("Error updating post", e.message);
        return null;
    }
};

/js/api/profile.js
import {
    API_URL,
    TIMEOUT_MS_HIGHER
} from "./consts.js"
import {
    getSessionUser
} from "./auth.js";

export const addUserProfileLinks = async ({
    link,
    type,
}) => {
    const user = await getSessionUser();
    if (!user) return null;

    const response = await fetch(`${API_URL}/wp-json/app/v1/add-profile-links`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user.id,
            link,
            type
        }),
    });

    const data = await response.json();
    return data;
};

export const updateSocialLinks = async (links) => {
    const user = await getSessionUser();
    if (!user) return;

    const response = await fetch(`${API_URL}/wp-json/app/v1/update-social-links`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user.id,
            links
        }),
    });

    const data = await response.json();
    return data;
};

export const removeProfileLink = async (linkId) => {
    const user = await getSessionUser();
    if (!user) return;

    const response = await fetch(`${API_URL}/wp-json/app/v1/remove-profile-link`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user.id,
            link_id: linkId
        }),
    });

    const data = await response.json();
    console.log(response);

    if (response.status !== 200 || data.error) {
        return false
    }

    return true;
};

export const updateUserDetails = async (details, email_changed) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser();
        if (!user) return;

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGHER)

        const response = await fetch(`${API_URL}/wp-json/app/v1/update-user-details`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                ...details,
                email_changed
            }),
            signal
        });

        const data = await response.json();
        return data;
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to update your details, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
};

export const updateProfileImage = async (image) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser();
        if (!user) return;

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGHER)


        const response = await fetch(`${API_URL}/wp-json/app/v1/update-profile-image`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                image
            }),
            signal
        });

        const data = await response.json();
        return data;
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to update your profile image, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
};

export const updateCoverImage = async (image) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser();
        if (!user) return;

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_HIGHER)

        const response = await fetch(`${API_URL}/wp-json/app/v1/update-cover-image`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                user_id: user.id,
                image
            }),
            signal
        });

        const data = await response.json();
        return data;
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: "Failed to update your cover image, your connection timed out",
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
};

export const maybeFollowUser = async (profileId) => {
    const user = await getSessionUser();
    if (!user) return;

    const response = await fetch(`${API_URL}/wp-json/app/v1/follow-user`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            following_id: profileId,
            follower_id: user.id
        }),
    });

    const data = await response.json();
    return data;
};

export const removeTagFromPost = async (tagId) => {
    const user = await getSessionUser();
    if (!user) return;

    const response = await fetch(`${API_URL}/wp-json/app/v1/remove-post-tag`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            tag_id: tagId,
            user_id: user.id
        }),
    });

    const data = await response.json();
    return data;
}

export const approvePostTag = async (tagId) => {
    const user = await getSessionUser();
    if (!user) return;

    const response = await fetch(`${API_URL}/wp-json/app/v1/approve-post-tag`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            tag_id: tagId,
            user_id: user.id
        }),
    });

    const data = await response.json();
    return data;
}

/js/api/scanner.js
import {
    getSessionUser
} from "./auth.js"
import {
    API_URL
} from "./consts.js"

export const verifyScan = async (decodedText) => {
    const user = await getSessionUser()

    const response = await fetch(`${API_URL}/wp-json/app/v1/verify-qr-code`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            user_id: user?.id,
            qr_code: decodedText
        }),
    })

    const data = await response.json()
    return data
}

const linkProfile = async (decodedText) => {
    const user = await getSessionUser()

    const response = await fetch(`${API_URL}/wp-json/app/v1/link-qr-code-entity`, {
        cache: "no-cache",
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            entity_id: user?.id,
            qr_code: decodedText,
            entity_type: "profile"
        }),
    })

    const data = await response.json()
    return data
}

const unlinkProfile = async (decodedText) => {
    const user = await getSessionUser()

    const response = await fetch(`${API_URL}/wp-json/app/v1/unlink-qr-code-entity`, {
        cache: "no-cache",
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            qr_code: decodedText,
            entity_id: user?.id,
            entity_type: "profile"
        }),
    })

    const data = await response.json()
    return data
}

export const getIDFromQrCode = async (decodedText) => {
    const response = await fetch(`${API_URL}/wp-json/app/v1/get-linked-entity`, {
        cache: "no-cache",
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            qr_code: decodedText
        }),
    })

    const data = await response.json()

    if ((data && data.error) || response.status === 404) {
        throw new Error(data.error)
    }

    return data
}

export const handleLink = async (result) => {
    if (!result) {
        return
    }

    try {
        const response = await linkProfile(result?.qr_code)
        return response
    } catch (e) {
        console.error("Error linking profile", e)
        return {
            status: 'error',
            text: 'Error linking profile'
        }
    }
}

export const handleUnlink = async (result) => {
    if (!result) {
        return
    }

    try {
        const response = await unlinkProfile(result?.qr_code)

        if (response.status === 'error') {
            return {
                type: 'error',
                text: response.message
            }
        } else {
            return {
                type: 'success',
                text: response.message
            }
        }

    } catch (e) {
        console.error("Error unlinking profile", e)
        return {
            type: 'error',
            text: 'Error unlinking profile'
        }
    }
}

/js/app.js
//------------------------------------------ CORE ------------------------------------------//
import {
  getSessionUser,
  handleSignUp,
  updateAboutUserIds,
  updateContentIds,
  updateUsername,
  verifyEmail,
  verifyUser
} from './api/auth.js'
import store from './store.js'
import routes from './routes.js'

import {
  displayProfile
} from './profile.js'
import {
  getIDFromQrCode
} from './api/scanner.js'
import {
  openModal,
  openQRModal
} from './qr.js'
import {
  sendRNMessage
} from './api/consts.js'


var $ = Dom7
var userStore = store.getters.user
var notificationCountStore = store.getters.getNotifCount
var networkErrors = store.getters.checkPoorNetworkError

var toolbarEl = $('.footer')[0]

var app = new Framework7({
  initOnDeviceReady: true,
  view: {
    pushState: false,
    stackPages: true,
    xhrCache: true,
    preloadPreviousPage: true,
    // browserHistory: true,
  },
  notification: {
    title: 'DriveLife',
    closeTimeout: 10000,
    closeOnClick: true,
    icon: '<img src="assets/icons/favicon.png"/>',
  },
  toast: {
    closeTimeout: 3000,
    closeButton: true,
  },
  name: 'DriveLife',
  theme: 'ios',
  smartSelect: {
    closeOnSelect: true,
  },
  cache: true,
  el: '#app', // App root element
  on: {
    init: async function () {
      const verifyToken = getQueryParameter('verifyToken')
      if (verifyToken) {
        await verifyUserEmail(verifyToken)
        return;
      }

      await handleSSOSignIn() // SSO with CarEvents
      await store.dispatch('checkAuth')

      const isAuthenticated = store.getters.isAuthenticated.value

      if (!isAuthenticated) {
        this.views.main.router.navigate('/auth/')
      } else {
        $('.init-loader').hide()
        $('.start-link').click();
      }

      await handleQRCode()

      const deeplink = getQueryParameter('deeplink')
      if (deeplink) {
        // get the page from the deeplink and navigate to it
        // ex; http://localhost:3000/post-view/308
        // get the /post-view/308 and navigate to it
        let path = deeplink.split('/').slice(3).join('/')

        if (!path.startsWith('/')) {
          path = `/${path}`
        }

        this.views.main.router.navigate(path)
        // remove the query parameter from the URL
        window.history.pushState({}, document.title, window.location.pathname)
      }
    },
    pageInit: function (page) {
      if (page.name === 'profile') {
        userStore.onUpdated((data) => {
          if (data && data.id) {
            const isEmailVerified = data.email_verified ?? false;

            if (!isEmailVerified) {
              const profileHead = $('.page[data-name="profile"] .profile-head')

              if (profileHead.length) {
                // Add email verification message before the element
                $(`
                  <div class="email-verification-message">
                    <p>Your email is not verified. Please verify your email address to access all features.</p>
                  </div>
                `).insertBefore(profileHead);

                profileHead.addClass('email-not-verified');
              }
            }
          }

          if (data && data.id && !data.external_refresh) {
            displayProfile(data, 'profile')
            store.dispatch('getMyGarage')
          }

          if (data && data.id && !data.refreshed) {
            store.dispatch('getMyPosts', {
              page: 1,
              clear: true
            })
            store.dispatch('getMyTags', {
              page: 1,
              clear: true
            })
          }
        })
      }

      if (page.name === 'discover') {
        userStore.onUpdated((data) => {
          if (data && data.id && !data.refreshed) {
            store.dispatch('getTrendingEvents')
            store.dispatch('getTrendingVenues')
            store.dispatch('filterTrendingUsers')
            store.dispatch('filterTrendingVehicles')
            store.dispatch('fetchEventCategories')
          }
        })
      }

      if (page.name === 'signup-step2') {
        const registerData = store.getters.getRegisterData.value

        const userNameEl = document.getElementsByName('username')[0]
        userNameEl.value = registerData.username
      }
    },
  },
  store: store,
  routes: routes,
})

async function handleSSOSignIn() {
  $('.init-loader').show()

  // SSO with CarEvents
  const ceToken = getQueryParameter('token')
  if (ceToken) {
    // remove the query parameter from the URL
    window.history.pushState({}, document.title, window.location.pathname)
    window.localStorage.setItem('token', ceToken)
  }

  // check if deeplink url has ?token= query parameter
  // if it does, save the token in the local storage
  const deeplink = getQueryParameter('deeplink')
  if (deeplink) {
    const token = deeplink.split('?token=')[1]
    if (token) {
      window.localStorage.setItem('token', token)
      // remove the query parameter from the URL
      window.history.pushState({}, document.title, window.location.pathname)
    }
  }
}

async function handleQRCode() {
  const deeplink = getQueryParameter('deeplink')
  if (deeplink) {
    // check if deeplink has ?qr= query parameter
    // if it does, get the value of the qr parameter and redirect to the profile
    // ex; http://localhost:3000/?qr=123456
    const maybeQr = deeplink.split('?qr=')[1]
    const deepqrCode = maybeQr ? maybeQr : null

    if (deepqrCode) {
      maybeRedirectToProfile(deepqrCode)
      return;
    }

    // check if url looks like https://mydrivelife.com/qr/8700279E
    // if it does, get the qr code and redirect to the profile
    const isDriveLifeUrl = deeplink.includes('mydrivelife.com/qr/')
    if (isDriveLifeUrl) {
      const qrCode = deeplink.split('/').slice(-1)[0]
      maybeRedirectToProfile(qrCode)
      return;
    }
  }

  const qrCode = getQueryParameter('qr')
  if (qrCode) {
    maybeRedirectToProfile(qrCode)
  }
}

async function verifyUserEmail(token) {
  // remove the query parameter from the URL
  // window.history.pushState({}, document.title, window.location.pathname)

  try {
    // Clear the app landing page content
    $('.app-landing-page').html('')

    // Add content to show email verification in progress
    $('.app-landing-page').html(`
      <div class="verification-content">
        <div class="block">
          <img src="assets/img/ce-logo-dark.png" />
        <div class="verification-header">
          <h1>Email Verification</h1>
        </div>
        <div class="verification-body">
          <div class="verification-loader">
            <div class="preloader color-white"></div> 
          </div>
          <div class="verification-message">
            <p>Verifying your email address...</p>
          </div>
        </div>
        </div>
      </div>
    `)

    const response = await verifyEmail(token)

    // Check if there's an error in the response
    if (!response || response.status === 'error') {
      // Display an error message
      $('.verification-body').html(`
        <div class="verification-message">
          <p class="verification-error">An error occurred: ${response.message || 'Please try again.'}</p>
          <p class="verification-error">If you continue to experience issues, please contact support.</p>
          <div class="button button-fill button-large" id="goto-app">Go Back</div>
        </div>
      `)
      return
    }

    if (response.status === 'success') {
      // Show a success message in the DOM
      $('.verification-body').html(`
        <div class="verification-message">
          <p class="verification-success">Your email has been successfully verified! You can now proceed.</p>
          <div class="button button-fill button-large" id="goto-app">Continue</div>
        </div>
      `)
      return
    }
  } catch (error) {
    // Display a generic error message in case of exceptions
    $('.verification-body').html(`
      <div class="verification-message">
        <p class="verification-error">An unexpected error occurred. Please try again.</p>
        <p class="verification-error">If you continue to experience issues, please contact support.</p>
        <div class="button button-fill button-large" id="goto-app">Go Back</div>
      </div>
    `)
  }
}

$(document).on('click', '#goto-app', function (e) {
  // remove the query parameter from the URL
  window.history.pushState({}, document.title, window.location.pathname)
  // reload the page
  window.location.reload()
})

$(document).on('click', '.start-link', function (e) {
  toolbarEl.style.display = 'block'
})

$(document).on('mousedown', '.toolbar-bottom a', async function (e) {
  var targetHref = $(this).attr('href');
  var validTabs = ['#view-social', '#view-discover', '#view-store', '#view-profile'];

  if ($(this).hasClass('tab-link-active') && validTabs.includes(targetHref)) {
    var view = app.views.current;
    if (view.history.length > 1) {
      view.router.back(view.history[0], {
        force: true
      });
    }
  }
  if (!view || !view.history) {
    return
  }

  if (targetHref == '#view-social' && view.history.length <= 1) {
    $('.page-current .page-content').scrollTop(0, 200);

    const ptrContent = app.ptr.get('.ptr-content.home-page')
    ptrContent.refresh()
  }
});

export function showToast(message, type = 'Message', position = 'bottom') {
  app.toast.create({
    text: message,
    position: position,
    closeTimeout: 3000,
  }).open()
}

async function maybeRedirectToProfile(qrCode) {
  var view = app.views.current

  try {
    $('.init-loader').show()
    const response = await getIDFromQrCode(qrCode)

    if (response && response.status === 'error') {
      throw new Error(response.message || 'Oops, Unable to find the profile linked to this QR code.')
    }

    const user = store.getters.user.value
    const id = response?.data?.linked_to;

    if (id) {
      if (user && user.id == id) {
        $('.view-profile-link').click()
      } else {
        view.router.navigate(`/profile-view/${id}`)
      }
    } else {
      openModal()
      setTimeout(() => {
        store.dispatch('setScannedData', {
          status: 'success',
          qr_code: qrCode,
          message: 'QR Code is not linked to any profile',
          available: true
        })
      }, 1000)
    }

    // remove the query parameter from the URL
    window.history.pushState({}, document.title, window.location.pathname)
    $('.init-loader').hide()
  } catch (error) {
    console.log(error);
    window.history.pushState({}, document.title, window.location.pathname)
    app.dialog.alert(error.message || 'Oops, Unable to find the profile linked to this QR code.')
    $('.init-loader').hide()
  }
}

// Function to parse query parameters from the URL
function getQueryParameter(name, url) {
  const urlParams = new URLSearchParams(url || window.location.search)
  return urlParams.get(name)
}

function isAndroid() {
  const toMatch = [
    /Android/i,
    // /webOS/i,
    // /iPhone/i,
    // /iPad/i,
    // /iPod/i,
    /BlackBerry/i,
    /Windows Phone/i,
  ];

  return toMatch.some((toMatchItem) => {
    return navigator.userAgent.match(toMatchItem);
  });
}

export function onBackKeyDown() {
  // check if the device is an android device
  if (!isAndroid()) {
    return
  }

  var view = app.views.current

  var leftp = app.panel.left && app.panel.left.opened
  var rightp = app.panel.right && app.panel.right.opened

  window.ReactNativeWebView.postMessage(JSON.stringify({
    his: view.history,
    url: app.views.main.router.url,
    leftp,
    rightp
  }))

  if (leftp || rightp) {
    app.panel.close()
    return false
  } else if ($('.modal-in').length > 0) {
    app.dialog.close()
    app.popup.close()
    return false
  } else if (view.history[0] == '/social/') {
    window.ReactNativeWebView.postMessage('exit_app')
    return true
  } else {
    if (view.history.length < 2) {
      $('.tab-link[href="#view-home"]').click()
      return
    }

    view.router.back()
    return false
  }
}

function onPostUpload() {
  store.dispatch('getMyPosts', {
    page: 1,
    clear: true
  })

  store.dispatch('getPosts', {
    page: 1,
    reset: true
  })
}

window.onPostUpload = onPostUpload
window.onAppBackKey = onBackKeyDown

userStore.onUpdated((data) => {
  if (data && data.id && !data.external_refresh && !data.refreshed) {
    store.dispatch('getPosts', {
      page: 1,
      reset: true
    })
    store.dispatch('getFollowingPosts', {
      page: 1,
      reset: true
    })
  }
})

notificationCountStore.onUpdated((data) => {
  document.querySelectorAll('.notification-count').forEach((el) => {
    el.innerHTML = data
    el.style.display = data > 0 ? 'flex' : 'none'
  })
})

networkErrors.onUpdated((data) => {
  if (data) {
    app.dialog.alert('Poor network connection. Please check your internet connection and try again.')
  }
})

// Action Sheet with Grid Layout
var actionSheet = app.actions.create({
  grid: true,
  buttons: [
    [{
      text: '<div class="actions-grid-item">Add Post</div>',
      icon: '<img src="assets/img/icon-add-post.svg" width="48" style="max-width: 100%"/>',
      onClick: async function () {
        const user = await getSessionUser()
        if (user) {
          sendRNMessage({
            type: "createPost",
            user_id: user.id,
            page: 'create-post',
          })
        }
      }
    },
    {
      text: '<div class="actions-grid-item">Scan QR Code</div>',
      icon: '<img src="assets/img/icon-qr-code.svg" width="48" style="max-width: 100%;"/>',
      onClick: function () {
        openQRModal()
      }
    },
    {
      text: '<div class="actions-grid-item">My Vehicles</div>',
      icon: '<img src="assets/img/icon-vehicle-add.svg" width="48" style="max-width: 100%;"/>',
      onClick: function () {
        var view = app.views.current
        view.router.navigate('/profile-garage-edit/');
      }
    }
    ],
  ]
});

// Init slider
new Swiper('.swiper-container', {
  pagination: {
    el: '.swiper-pagination',
    clickable: true,
  },
})

app.popup.create({
  el: '.share-popup',
  swipeToClose: 'to-bottom'
})

app.popup.create({
  el: '.edit-post-popup',
  swipeToClose: 'to-bottom'
})

$(document).on('click', '#open-action-sheet', function () {
  actionSheet.open()
})

// Handle login form submission
$(document).on('submit', '.login-screen-content form', async function (e) {
  e.preventDefault()

  var username = $(this).find('input[name="username"]').val()
  var password = $(this).find('input[name="password"]').val()

  if (!username) {
    showToast('Username is required')
    return
  }

  if (!password) {
    showToast('Password is required')
    return
  }


  try {
    app.preloader.show()
    const response = await verifyUser({
      email: username,
      password
    })

    app.preloader.hide()

    if (!response || response.error) {
      app.dialog.alert(response.error || 'Login failed, please try again')
      return
    }

    if (response.success) {
      showToast('Login successful')
      await store.dispatch('login', {
        token: response.token
      })

      app.views.main.router.navigate('/')
      $('.start-link').click();

      toolbarEl.style.display = 'block'
      return
    }
  } catch (error) {
    app.dialog.alert('Login failed, please try again')
  }
})

$(document).on('click', '.toggle-password', function () {
  var input = $(this).prev('input')
  if (input.attr('type') === 'password') {
    input.attr('type', 'text')
    $(this).html('<i class="fa fa-eye-slash"></i>')
  } else {
    input.attr('type', 'password')
    $(this).html('<i class="fa fa-eye"></i>')
  }
})

// Register forms
// Step 1
$(document).on('submit', 'form#sign-up-step1', async function (e) {
  e.preventDefault()

  var firstName = $(this).find('input[name="first_name"]').val().trim()
  var lastName = $(this).find('input[name="last_name"]').val().trim()
  var email = $(this).find('input[name="email"]').val().trim()
  var password = $(this).find('input[name="password"]').val().trim()
  var confirmPassword = $(this).find('input[name="confirm_password"]').val().trim()
  var agreeTerms = $(this).find('input[name="agree_terms"]').is(':checked')
  var agreePrivacy = $(this).find('input[name="agree_privacy"]').is(':checked')

  if (!firstName) {
    showToast('First name is required')
    return
  }

  if (!lastName) {
    showToast('Last name is required')
    return
  }

  if (!email) {
    showToast('Email is required')
    return
  }

  var emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  if (!emailPattern.test(email)) {
    showToast('Please enter a valid email address')
    return
  }

  if (!password) {
    showToast('Password is required')
    return
  }

  // Check if password has at least 8 characters
  if (password.length < 8) {
    showToast('Password must be at least 8 characters long.')
    return
  }

  // Check if password contains at least one lowercase letter
  if (!/[a-z]/.test(password)) {
    showToast('Password must contain at least one lowercase letter.')
    return
  }

  // Check if password contains at least one uppercase letter
  if (!/[A-Z]/.test(password)) {
    showToast('Password must contain at least one uppercase letter.')
    return
  }

  // Check if password contains at least one number
  if (!/\d/.test(password)) {
    showToast('Password must contain at least one number.')
    return
  }

  if (password.length < 8) {
    showToast('Password must be at least 8 characters long')
    return
  }

  if (!confirmPassword) {
    showToast('Please confirm your password')
    return
  }

  if (password !== confirmPassword) {
    showToast('Passwords do not match')
    return
  }

  if (!agreeTerms) {
    showToast('You must agree to the Terms & Conditions')
    return
  }

  if (!agreePrivacy) {
    showToast('You must agree to the Privacy Policy')
    return
  }

  // add a loader to the login button
  var loginButton = $(this).find('button[type="submit"]')[0]
  loginButton.innerHTML = '<div class="preloader color-white"></div>'

  try {
    app.preloader.show()

    const response = await handleSignUp({
      full_name: `${firstName} ${lastName}`,
      email,
      password
    })

    app.preloader.hide()

    if (!response || !response.success) {
      app.dialog.alert(response.message || 'An error occurred, please try again')
      loginButton.innerHTML = 'Next'
      return
    }

    store.dispatch('setRegisterData', {
      email,
      password,
      user_id: response.user_id,
      username: response.username
    })
    app.views.main.router.navigate('/signup-step2/')
  } catch (error) {
    console.log(error)
    app.dialog.alert(error.message || 'An error occurred, please try again')
    loginButton.innerHTML = 'Next'
    return
  }
})

// Step 2
$(document).on('submit', 'form#sign-up-step2', async function (e) {
  e.preventDefault()

  var username = $(this).find('input[name="username"]').val().trim()

  if (!username) {
    showToast('Username is required')
    return
  }

  // username can only have letters, numbers, and underscores
  var usernamePattern = /^[a-zA-Z0-9_]+$/
  if (!usernamePattern.test(username)) {
    showToast('Username can only contain letters, numbers, and underscores')
    return
  }

  // username must be at least 3 characters long
  if (username.length < 3) {
    showToast('Username must be at least 3 characters long')
    return
  }

  let registerData = store.getters.getRegisterData.value
  try {
    if (registerData.username !== username) {
      app.preloader.show()

      const response = await updateUsername(username, registerData.user_id)

      app.preloader.hide()

      if (!response || !response.success) {
        app.notification.create({
          titleRightText: 'now',
          subtitle: 'Oops, something went wrong',
          text: error.message || 'Failed to update username',
        }).open()
        // app.dialog.alert(response.message || 'An error occurred, please try again')
        return
      }

      store.dispatch('setRegisterData', {
        ...registerData,
        username
      })
    }

    app.views.main.router.navigate('/signup-step3/')
  } catch (error) {
    console.log(error);
    app.notification.create({
      titleRightText: 'now',
      subtitle: 'Oops, something went wrong',
      text: error.message || 'Failed to update username',
    }).open()
    return
  }
})

// Step 3
$(document).on('submit', '#car-selection-form', async function (e) {
  e.preventDefault()

  // Get all checked checkboxes' values
  var selectedCarTypes = []
  $(this).find('input[name="car_type"]:checked').each(function () {
    selectedCarTypes.push($(this).val())
  })

  // Check if at least one checkbox is selected
  if (selectedCarTypes.length === 0) {
    showToast('Please select at least one car type')
    return
  }

  // For demonstration, log the selected values to the console
  let registerData = store.getters.getRegisterData.value

  try {
    app.preloader.show()
    const response = await updateContentIds(selectedCarTypes, registerData.user_id)

    if (!response || !response.success) {
      app.dialog.alert(response.message || 'Oops, Unable to save your selection.')
    }

    app.preloader.hide()
    app.views.main.router.navigate('/signup-step4/')
  } catch (error) {
    console.log(error)
    app.dialog.alert('An error occurred, please try again')
    return
  }

  // Redirect to the next step (this can be customized as needed)
  app.views.main.router.navigate('/signup-step4/')
})

// Step 4
$(document).on('submit', '#interest-selection-form', async function (e) {
  e.preventDefault()

  // Get all checked checkboxes' values
  var selectedInterests = []
  $(this).find('input[name="interest"]:checked').each(function () {
    selectedInterests.push($(this).val())
  })

  // Check if at least one checkbox is selected
  if (selectedInterests.length === 0) {
    showToast('Please select at least one interest')
    return
  }

  let registerData = store.getters.getRegisterData.value

  try {
    app.preloader.show()
    const response = await updateAboutUserIds(selectedInterests, registerData.user_id)

    if (!response || !response.success) {
      app.dialog.alert(response.message || 'Oops, Unable to save your selection.')
    }

    app.preloader.hide()

    app.views.main.router.navigate('/signup-complete/')
  } catch (error) {
    console.log(error)
    app.dialog.alert('An error occurred, please try again')
    return
  }
})

// Signup complete
$(document).on('click', '#signup-complete', async function (e) {
  const registerData = store.getters.getRegisterData.value

  if (!registerData || !registerData.user_id || !registerData.email || !registerData.password) {
    app.dialog.alert('An error occurred, please try again')
    return
  }

  try {
    app.preloader.show()

    const response = await verifyUser({
      email: registerData.email,
      password: registerData.password
    })

    app.preloader.hide()

    if (!response || response.error) {
      app.dialog.alert(response.error || 'Login failed, please try again')
      app.views.main.router.navigate('/auth/')
      loginButton.innerHTML = 'Next'
      return
    }

    if (response.success) {
      await store.dispatch('login', {
        token: response.token
      })
      app.views.main.router.navigate('/')
      $('.start-link').click();

      toolbarEl.style.display = 'block'
      return
    }
  } catch (error) {
    app.dialog.alert('Login failed, please try again')
  }
})

$(document).on('page:afterin', '.page[data-name="auth"]', function (e) {
  toolbarEl.style.display = 'none'

  setTimeout(() => {
    $('.init-loader').hide()
  }, 300)
});

$(document).on('page:afterin', '.page[data-name="signup-step1"]', function (e) {
  app.popup.create({
    el: '.privacy-popup',
    swipeToClose: 'to-bottom'
  })
});

// logout-button
$(document).on('click', '.logout-button', async function (e) {
  app.dialog.close()
  app.popup.close()
  app.panel.close()

  await store.dispatch('logout')

  // reload page
  window.location.reload()
  // app.views.current.router.navigate('/auth/')
})

$(document).on('click', '.view-profile', function (e) {
  $('.view-profile-link').click()
})

$(document).on('click', '#forgot-password', function (e) {
  // open the url in a new tab
  window.open($(this).attr('href'), '_blank')
})

// SSO with CarEvents
$(document).on('click', '#sso-ce-button', function (e) {

  // URL encode the redirect URI (the app URL)
  const appRedirectUri = encodeURIComponent('https://app.mydrivelife.com/'); // Replace with your app's redirect URL

  // Build the CarEvents login URL with the state and app_redirect
  const loginUrl = `https://www.carevents.com/uk/login?app_redirect=${appRedirectUri}`;

  // Store the state in localStorage or sessionStorage for validation later
  window.open(loginUrl, '_blank')
})

export default app

/js/discoverpage.js
import app from "./app.js";
import store from "./store.js";
var $ = Dom7;

var trendingEventsStore = store.getters.getTrendingEvents;
var trendingVenuesStore = store.getters.getTrendingVenues;
var eventCategories = store.getters.getEventCategories;
var filteredEventsStore = store.getters.getFilteredEvents;
var filteredVenuesStore = store.getters.getFilteredVenues;
var trendingUsersStore = store.getters.getTrendingUsers;
var trendingVehiclesStore = store.getters.getTrendingVehicles;

var isFetchingPosts = false
var currentEventsPage = 1
var currentVenuesPage = 1
var currentUsersPage = 1
var refreshed = false

var totalEventPages = 1
var totalVenuesPages = 1
var totalUsersPages = 1
var totalVehiclePages = 1

var autocomplete;
var filters = {};

//AUTOCOMPLETE FUNCTIONS
function initAutocomplete() {
    autocomplete = new google.maps.places.Autocomplete(
        document.getElementById("autocomplete"), {
        types: ["establishment", "geocode"],
        componentRestrictions: {
            country: 'GB'
        }
    }
    );
    autocomplete.setFields(["geometry", "address_component"]);
    autocomplete.addListener("place_changed", fillInAddress);
}

function fillInAddress() {
    const place = autocomplete.getPlace();

    console.log(place);

    document.getElementById('lat').value = place.geometry.location.lat();
    document.getElementById('lng').value = place.geometry.location.lng();
}

function populateEventCard(data = [], isSwiper = true) {
    const swiperContainer = document.querySelector('#trending-events');
    if (isSwiper) swiperContainer.innerHTML = '';

    const eventsTabContainer = document.querySelector('#filtered-events-tab');

    if (refreshed) {
        eventsTabContainer.innerHTML = '';
    }

    data.forEach(event => {
        const startDate = new Date(event.start_date);
        const endDate = new Date(event.end_date);
        // const startDate = new Date(event.dates[0].start_date);
        // const endDate = new Date(event.dates[0].end_date);

        let endDateString = '';

        if (startDate.getDate() !== endDate.getDate()) {
            endDateString = `
            <div class="event-date-item">
                <p>${endDate.toLocaleString('default', { month: 'short' })}</p>
                <h5>${endDate.getDate()}</h5>
            </div>
            `
        }

        const card = `
        <a href="/discover-view-event/${event.id}">
            <div class="card event-item">
                <div class="event-image position-relative">
                    <div class="image-rectangle" style="background-image: url('${event.thumbnail}');"></div>
                    <div class="event-dates">
                        <div class="event-date-item">
                            <p>${startDate.toLocaleString('default', { month: 'short' })}</p>
                            <h5>${startDate.getDate()}</h5>
                        </div>
                        ${endDateString}
                    </div>
                </div>
                <div class="card-content">
                    <h3 class="event-title">${event.title}</h3>
                    <p class="event-info">Starts ${startDate.toLocaleString('default', { weekday: 'short' })}, ${startDate.getDate()} ${startDate.toLocaleString('default', { month: 'short' })} ${startDate.getFullYear()}</p>
                    <div class="event-info">
                        ${event.location}
                    </div>
                </div>
            </div>
        </a>
        `;

        if (isSwiper) {
            const swiperSlide = document.createElement('swiper-slide');
            swiperSlide.innerHTML = card;
            swiperContainer.appendChild(swiperSlide);
        }

        eventsTabContainer.innerHTML += card;
    });
}

function populateVenueCard(data = [], isSwiper = true) {
    const swiperContainer = document.querySelector('#trending-venues');

    if (isSwiper) swiperContainer.innerHTML = '';

    const eventsTabContainer = document.querySelector('#filtered-venues-tab');

    data.forEach(event => {
        const swiperSlide = document.createElement('swiper-slide');

        const card = `
        <a href="/discover-view-venue/${event.ID}">
            <div class="card event-item">
                <div class="event-image position-relative">
                    <div class="image-rectangle" style="background-image: url('${event.cover_image}');"></div>
                </div>
                <div class="card-content">
                    <h3 class="event-title">${event.title}</h3>
                    <div class="event-info">
                        ${event.venue_location}
                    </div>
                    <div class="event-info">
                        Apprx. ${event.distance} miles away
                    </div>
                </div>
            </div>
        </a>
        `;

        if (isSwiper) {
            swiperSlide.innerHTML = card;
            swiperContainer.appendChild(swiperSlide);
        }

        eventsTabContainer.innerHTML += card;
    });


}

function populateUsersCard(data = []) {
    const tabContainer = document.querySelector('#users-tab');

    data.forEach(user => {
        let linkTo = user.type === 'user' ? `/profile-view/${user.id}` : `/profile-garage-vehicle-view/${user.id}`;
        let title;

        if (user.type === 'user') {
            title = user.name;
        }

        if (user.type === 'vehicle') {
            const userName = user.owner.username;
            const vehicleName = user.title;

            title = `${vehicleName} <br/> Owner ${userName}`;
        }


        const card = `
        <li>
            <a class="item-link search-result item-content" href="${linkTo}">
                <div class="item-media">
                    <div class="image-square image-rounded"
                        style="background-image:url('${user.thumbnail || 'assets/img/profile-placeholder.jpg'}')">
                    </div>
                </div>
                <div class="item-inner">
                    <div class="item-title">${title}</div>
                </div>
            </a>
        </li>
        `;
        tabContainer.innerHTML += card;
    });
}

function populateVehiclesCard(data = []) {
    const tabContainer = document.querySelector('#vehicles-tab');

    data.forEach(user => {
        let linkTo = `/profile-garage-vehicle-view/${user.id}`;
        let title;

        const userName = user.owner.username;
        const vehicleName = user.title;

        title = `${vehicleName} <br/> Owner @${userName}`;

        const card = `
        <li>
            <a class="item-link search-result item-content" href="${linkTo}">
                <div class="item-media">
                    <div class="image-square image-rounded"
                        style="background-image:url('${user.thumbnail}')">
                    </div>
                </div>
                <div class="item-inner">
                    <div class="item-title">${title}</div>
                </div>
            </a>
        </li>
        `;
        tabContainer.innerHTML += card;
    });
}

function addCategoryOptions(categories) {
    const categoryFilters = document.querySelector('#category-filters ul');

    categories.forEach(category => {
        const listItem = document.createElement('li');

        listItem.innerHTML = `
            <label class="item-checkbox item-content">
                <input type="checkbox" name="${category.slug}" value="${category.id}" />
                <i class="icon icon-checkbox"></i>
                <div class="item-inner">
                    <div class="item-title">${category.name}</div>
                </div>
            </label>
        `;

        categoryFilters.appendChild(listItem);
    });
}

// Event listener for the submit button
$(document).on('click', '.apply-filters', function (e) {
    const dateFilters = document.querySelector('#date-filters ul');
    const locationFilters = document.querySelector('#location-filters ul');
    const categoryFilters = document.querySelector('#category-filters ul');

    e.preventDefault(); // Prevent form submission if you're handling it via JavaScript
    const selectedCats = [...categoryFilters.querySelectorAll('input[type="checkbox"]:checked')].map(cb => cb.value);
    const selectedLocation = [...locationFilters.querySelectorAll('input[type="radio"]:checked')].map(cb => cb.value);
    const dateFilter = [...dateFilters.querySelectorAll('input[type="checkbox"]:checked')].map(cb => cb.value);

    const latitude = document.getElementById('lat').value;
    const longitude = document.getElementById('lng').value;

    let location = null;

    if (latitude && longitude) {
        location = {
            latitude,
            longitude
        }
    }

    filters = {
        'event_location': selectedLocation,
        'custom_location': location,
        'event_date': dateFilter,
        //  'event_start': customDateRange?.start,
        //  'event_end': customDateRange?.end,
        'event_category': !selectedCats?.includes(0) ? selectedCats : undefined,
    };

    // get active tab
    const activeTab = document.querySelector('.tabbar-nav .tab-link-active').getAttribute('data-id');

    if (activeTab === 'events') {
        store.dispatch('filterEvents', {
            page: 1,
            filters
        })

        currentEventsPage = 1
        isFetchingPosts = false

        const eventsTabContainer = document.querySelector('#filtered-events-tab');
        eventsTabContainer.innerHTML = '';
    }

    if (activeTab === 'venues') {
        store.dispatch('filterVenues', {
            page: 1,
            filters
        })

        currentVenuesPage = 1
        isFetchingPosts = false

        const venuesTabContainer = document.querySelector('#filtered-venues-tab');
        venuesTabContainer.innerHTML = '';
    }

    // close popup
    app.popup.close()
});

$(document).on('change', '#category-filters ul', function (e) {
    const categoryFilters = document.querySelector('#category-filters ul');
    var allCheckbox = categoryFilters.querySelector('input[name="all"]');

    const targetCheckbox = e.target;

    if (targetCheckbox.name !== "all") {
        // If any other checkbox is selected, uncheck "All"
        if (targetCheckbox.checked) {
            allCheckbox.checked = false;
        } else {
            // If all other checkboxes are unchecked, check "All"
            const allUnchecked = [...categoryFilters.querySelectorAll('input[type="checkbox"]')]
                .filter(cb => cb.name !== "all")
                .every(cb => !cb.checked);

            if (allUnchecked) {
                allCheckbox.checked = true;
            }
        }
    } else {
        // If "All" is selected, uncheck all other checkboxes
        if (targetCheckbox.checked) {
            [...categoryFilters.querySelectorAll('input[type="checkbox"]')]
                .filter(cb => cb.name !== "all")
                .forEach(cb => cb.checked = false);
        }
    }
});

eventCategories.onUpdated((data) => {
    addCategoryOptions(data);
})

trendingVenuesStore.onUpdated((data) => {
    totalVenuesPages = data.total_pages

    populateVenueCard(data.data);
});

trendingEventsStore.onUpdated((data) => {
    totalEventPages = data.total_pages

    populateEventCard(data.data);
});

trendingUsersStore.onUpdated((data) => {
    const tabContainer = document.querySelector('#users-tab');
    if (!data || data.data.length === 0) {
        tabContainer.innerHTML = `
            <div class="no-events">
                <h3>No trending users found for you</h3>
            </div>
        `;
        return
    }

    totalUsersPages = data.total_pages

    if ((totalUsersPages == data.page) || (totalUsersPages == 0) || (data.new_data.length < 10)) {
        $('.infinite-scroll-preloader.users-tab').hide()
    } else {
        $('.infinite-scroll-preloader.users-tab').show()
    }

    populateUsersCard(data.new_data);
});

trendingVehiclesStore.onUpdated((data) => {
    const tabContainer = document.querySelector('#vehicles-tab');
    if (!data || data.data.length === 0) {
        tabContainer.innerHTML = `
            <div class="no-events">
                <h3>No trending vehicles found for you</h3>
            </div>
        `;
        return
    }

    totalVehiclePages = data.total_pages

    if ((totalVehiclePages == data.page) || (totalVehiclePages == 0) || (data.new_data.length < 10)) {
        $('.infinite-scroll-preloader.vehicles-tab').hide()
    } else {
        $('.infinite-scroll-preloader.vehicles-tab').show()
    }

    populateVehiclesCard(data.new_data);
});

// Filtered views
filteredEventsStore.onUpdated((data) => {
    const eventsTabContainer = document.querySelector('#filtered-events-tab');
    if (!data || data.data.length === 0) {
        eventsTabContainer.innerHTML = `
            <div class="no-events">
                <h3>No events found</h3>
            </div>
        `;
        return
    }

    totalEventPages = data.total_pages

    if ((totalEventPages == data.page) || (totalEventPages == 0) || (data.new_data.length < 10)) {
        $('.infinite-scroll-preloader.events-tab').hide()
    } else {
        $('.infinite-scroll-preloader.events-tab').show()
    }

    populateEventCard(data.new_data, false);
});

filteredVenuesStore.onUpdated((data) => {
    const tabContainer = document.querySelector('#filtered-venues-tab');

    if (!data || data.data.length === 0) {
        tabContainer.innerHTML = `
            <div class="no-venues">
                <h3>No venues found</h3>
            </div>
        `;

        totalVenuesPages = 0
        return
    }

    if ((totalVenuesPages == data.page) || (totalVenuesPages == 0) || (data.new_data.length == 0)) {
        $('.infinite-scroll-preloader.venues-tab').hide()
        totalVenuesPages = 0
    } else {
        $('.infinite-scroll-preloader.venues-tab').show()
        totalVenuesPages = data.total_pages
        populateVenueCard(data.new_data, false);
    }
});

$(document).on('page:init', '.page[data-name="discover"]', function (e) {
    //Date Filters
    app.calendar.create({
        inputEl: '#date-from',
        openIn: 'customModal',
        header: true,
        footer: true,
    });

    app.calendar.create({
        inputEl: '#date-to',
        openIn: 'customModal',
        header: true,
        footer: true,
    });

    //Filter Date Popup
    app.popup.create({
        el: '.filter-bydate-popup',
        swipeToClose: 'to-bottom'
    });

    //Filter Category Popup
    app.popup.create({
        el: '.filter-bycategory-popup',
        swipeToClose: 'to-bottom'
    });

    //Filter Location Popup
    app.popup.create({
        el: '.filter-bylocation-popup',
        swipeToClose: 'to-bottom'
    });

    //SEARCH BAR
    $('.discover-search').on('mousedown', function (event) {
        event.preventDefault();
        app.views.discover.router.navigate('/search/');
    });

    const dateFilters = document.querySelector('#date-filters ul');
    const locationFilters = document.querySelector('#location-filters ul');

    // Event listener for checkbox selection
    dateFilters.addEventListener('change', function (e) {
        const targetCheckbox = e.target;

        // Uncheck all checkboxes except the one that was clicked
        if (targetCheckbox.type === "checkbox") {
            [...dateFilters.querySelectorAll('input[type="checkbox"]')].forEach(checkbox => {
                if (checkbox !== targetCheckbox) {
                    checkbox.checked = false;
                }
            });
        }
    });

    locationFilters.addEventListener('change', function (e) {
        const targetCheckbox = e.target;

        // Uncheck all checkboxes except the one that was clicked
        if (targetCheckbox.type === "checkbox") {
            [...locationFilters.querySelectorAll('input[type="checkbox"]')].forEach(checkbox => {
                if (checkbox !== targetCheckbox) {
                    checkbox.checked = false;
                }
            });
        }
    });

    initAutocomplete();
});

$(document).on('infinite', '.discover-page.infinite-scroll-content', async function (e) {
    if (isFetchingPosts) return

    // get active tab
    const activeTabId = $('.tabbar-nav .tab-link-active').attr('data-id');

    if (!activeTabId) return

    isFetchingPosts = true

    if (activeTabId == 'events') {
        currentEventsPage++

        if (currentEventsPage <= totalEventPages) {
            await store.dispatch('filterEvents', {
                page: currentEventsPage,
                filters
            })
            isFetchingPosts = false
        }
    } else if (activeTabId == 'venues') {
        currentVenuesPage++

        if (currentVenuesPage <= totalVenuesPages) {
            await store.dispatch('filterVenues', {
                page: currentVenuesPage,
                filters
            })
            isFetchingPosts = false
        }
    }
    isFetchingPosts = false
})

$(document).on('page:init', '.page[data-name="discover-view-event"]', function (e) {
    // Init slider
    new Swiper('.swiper-container', {
        pagination: {
            el: '.swiper-pagination',
            clickable: true,
        },
    })

    app.popup.create({
        el: '.share-listing-popup',
        swipeToClose: 'to-bottom'
    });
});

$(document).on('ptr:refresh', '.discover-page.ptr-content', async function (e) {
    refreshed = true
    currentEventsPage = 1
    currentVenuesPage = 1

    try {
        await store.dispatch('getTrendingEvents')
        await store.dispatch('getTrendingVenues')
        await store.dispatch('filterTrendingUsers')
        await store.dispatch('fetchEventCategories')
    } catch (error) {
        console.log(error);
    }

    refreshed = false
    app.ptr.get('.discover-page.ptr-content').done()
})

$(document).on('click', '#featured-event-link', function (e) {
    e.preventDefault();
    window.open(e.target.href, '_blank');
});

/js/edit-post.js
import {
    updatePost
} from "./api/posts.js";
import app, {
    showToast
} from "./app.js";
import store from "./store.js";

var $ = Dom7;

$(document).on('page:beforein', '.page[data-name="post-edit"]', async function (e) {
    var posts = store.getters.myPosts.value
    var postId = e.detail.route.params.id

    if (!postId || postId == -1) {
        return;
    }

    console.log(posts);
    const post = posts.data.find(p => p.id == postId)

    $('#edit_post_id').val(postId);
    $('#post_content').val(post.caption);

});

$(document).on('click', '#update-post', async function (e) {
    var view = app.views.current

    const description = $('#post_content').val();
    const postId = $('#edit_post_id').val();

    const data = {
        post_id: postId,
        caption: description
    }

    try {
        app.preloader.show()

        const response = await updatePost(data)

        app.preloader.hide()

        if (!response || response.error) {
            throw new Error(response.error);
        }

        showToast('Post updated successfully')

        // fine elem with data-post-id="52" and update the .media-post-description .post-caption text
        var postElem = $(`[data-post-id="${postId}"]`).find('.media-post-description')

        const maxDescriptionLength = 200; // Set your character limit here
        const isLongDescription = description.length > maxDescriptionLength;
        const shortDescription = isLongDescription ? description.slice(0, maxDescriptionLength) : description;

        // for each postElem, loop through and update the .post-caption and .full-description hidden input
        postElem.each(function () {
            var postCaption = $(this).find('.post-caption');
            var fullDescription = $(this).find('.full-description');

            postCaption.text(shortDescription);
            fullDescription.val(description);
        });

        store.dispatch('updatePost', {
            post_id: postId,
            caption: description
        })

        view.router.back()
    } catch (error) {
        app.notification.create({
            titleRightText: 'now',
            subtitle: 'Oops, something went wrong',
            text: error.message || 'Failed to update post',
        }).open()
        app.preloader.hide()
    }
})

/js/event-view.js
import {
    fetchEvent,
    maybeFavoriteEvent
} from "./api/discover.js";
import app from "./app.js";

var $ = Dom7;

//DISCOVER - VIEW EVENT
$(document).on('page:afterin', '.page[data-name="discover-view-event"]', async function (e) {

    var eventId = e.detail.route.params.id

    if (!eventId || eventId === '-1') {
        return;
    }


    $('.loading-fullscreen').show()

    const eventData = await fetchEvent(eventId)
    $('.loading-fullscreen').hide()

    const mainContainer = $('.discover-view-event.view-event');

    if (!eventData) {
        mainContainer.html('<div class="text-align-center">No event found</div>');
        return;
    }

    // Populating the Event Title
    mainContainer.find('.event-detail-title').text(eventData.title);

    // Populating the Event Date
    // const dateText = `Sun, Aug 25th 2024`; // You can format this dynamically if needed
    const startDate = new Date(eventData.dates[0].start_date);
    const endDate = new Date(eventData.dates[0].end_date);

    // format as "Sun, Aug 25th 2024" or "Sun, Aug 25th 2024 - Mon, Aug 26th 2024"
    if (startDate.toDateString() === endDate.toDateString()) {
        var dateText = startDate.toLocaleDateString('en-US', {
            weekday: 'short',
            month: 'short',
            day: 'numeric',
            year: 'numeric'
        });
    }

    if (startDate.toDateString() !== endDate.toDateString()) {
        var dateText = startDate.toLocaleDateString('en-US', {
            weekday: 'short',
            month: 'short',
            day: 'numeric',
            year: 'numeric'
        }) + ' - ' + endDate.toLocaleDateString('en-US', {
            weekday: 'short',
            month: 'short',
            day: 'numeric',
            year: 'numeric'
        });
    }

    mainContainer.find('.event-time-address:nth-child(1) span').text(dateText);

    // Populating the Event Time
    const timeText = `${eventData.dates[0].start_time} - ${eventData.dates[0].end_time}`;
    mainContainer.find('.event-time-address:nth-child(2) span').text(timeText);

    // Populating the Event Location
    mainContainer.find('.event-time-address:nth-child(3) span').text(eventData.location);

    // Populating the Event Description in "About" Tab
    mainContainer.find('#tab-about .event-des-wrap').html(`<p>${eventData.description}</p>`);
    mainContainer.find('#tab-entry-details .event-des-wrap').html(`<p>${eventData.entry_details}</p>`);

    if (eventData.has_tickets) {
        // Populating the Ticket Button URL
        mainContainer.find('.event-list-btn .btn.bg-dark').on('click', function () {
            window.location.href = eventData.ticket_url;
        });
    } else {
        // Hiding the Ticket Button
        mainContainer.find('.event-list-btn').hide();
    }

    // Populating the Swiper Images
    const swiperWrapper = mainContainer.find('.swiper-wrapper');
    swiperWrapper.empty(); // Clear existing placeholders

    const gallery = eventData.gallery || [];
    gallery.push({
        url: eventData.cover_photo.url
    });

    gallery.forEach(image => {
        const slide = `
        <div class="swiper-slide">
            <div class="swiper-image" style="background-image: url('${image.url}');"></div>
        </div>
    `;
        swiperWrapper.append(slide);
    });

    mainContainer.find('.event-des-wrap a').on('click', function (e) {
        console.log('clicked');
        e.preventDefault();
        window.open($(this).attr('href'), '_blank');
    });

    $('#copy-event-link').attr('data-event-id', eventId)
    $('#share-email-event-link').attr('data-event-id', eventId)

    // set the event id to the favourite button
    const faveBtn = $('#favourite_event')
    faveBtn.attr('data-event-id', eventId)

    if (eventData.is_liked) {
        faveBtn.addClass('favourite')
        faveBtn.innerHTML = `<i class="f7-icons">heart_fill</i> Favourite`
    } else {
        faveBtn.removeClass('favourite')
        faveBtn.innerHTML = `<i class="f7-icons">heart</i> Favourite`
    }
});

$(document).on('click', '#favourite_event', async function () {
    // get the event id from the button
    const eventId = $(this).attr('data-event-id');
    const isFavourite = $(this).hasClass('favourite');

    // optmisitically update the UI
    if (isFavourite) {
        $(this).removeClass('favourite')
        $(this).innerHTML = `<i class="f7-icons">heart</i> Favourite`
    } else {
        $(this).addClass('favourite')
        $(this).innerHTML = `<i class="f7-icons">heart_fill</i> Favourite`
    }

    // call the API to favourite the event
    await maybeFavoriteEvent(eventId);
})

$(document).on('click', '#copy-event-link', function () {
    const eventId = $(this).attr('data-event-id');
    const eventLink = `${window.location.origin}/discover-view-event/${eventId}`;

    navigator.clipboard.writeText(eventLink);


    app.toast.create({
        text: 'Link copied to clipboard',
        closeTimeout: 2000
    }).open()
});

$(document).on('click', '#share-email-event-link', function () {
    const eventId = $(this).attr('data-event-id');
    const eventLink = `${window.location.origin}/discover-view-event/${eventId}`;

    window.open(`mailto:?subject=Event Link&body=${eventLink}`, '_blank');
});

/js/homepage.js
import app, {
  showToast
} from "./app.js"
import store from "./store.js"

import {
  formatPostDate
} from './utils.js'
import {
  fetchComments,
  maybeLikePost,
  maybeLikeComment,
  addComment,
  deletePost,
  deleteComment
} from './api/posts.js'
import {
  getSessionUser
} from "./api/auth.js"

var $ = Dom7
var currentPostsPage = 1
var currentFollowingPostsPage = 1

var postsStore = store.getters.posts
var followingPostsStore = store.getters.followingPosts

var totalPostPages = 0
var totalFPostPages = 0

// Infinite Scroll Event
var isFetchingPosts = false
var activeTab = 'latest'
var refreshed = false

//screen width
var containerWidth = window.innerWidth


// Function to pause all videos
function pauseAllVideos() {
  var videos = document.querySelectorAll('video.video-js');
  videos.forEach(function (video) {
    video.pause();
  });
}

export function loadVideos() {
  var videos = document.querySelectorAll('video.video-js');

  // Function to resume videos if needed (optional)
  function playVisibleVideos() {
    videos.forEach(function (video) {
      var observer = new IntersectionObserver(function (entries) {
        entries.forEach(function (entry) {
          if (entry.isIntersecting) {
            video.play();
          }
        });
      }, { threshold: 0.5 });
      observer.observe(video);
    });
  }

  // Listen for visibility change
  document.addEventListener('visibilitychange', function () {
    if (document.hidden) {
      // Pause all videos when the user changes tabs or minimizes
      pauseAllVideos();
    } else {
      // Optionally resume videos if visible
      playVisibleVideos();
    }
  });

  // Loop through each video element
  videos.forEach(function (video) {
    var videoSrc = video.getAttribute('data-src');

    if (Hls.isSupported()) {
      var hls = new Hls();
      hls.loadSource(videoSrc);
      hls.attachMedia(video);
      hls.on(Hls.Events.MANIFEST_PARSED, function () {
      });
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = videoSrc;
      video.addEventListener('loadedmetadata', function () {
      });
    }

    // Set up IntersectionObserver to pause/play videos based on visibility
    var observer = new IntersectionObserver(function (entries) {
      entries.forEach(function (entry) {
        if (entry.isIntersecting) {
          video.play();
        } else {
          video.pause();
        }
      });
    }, { threshold: 0.5 });

    observer.observe(video);

    video.addEventListener('play', function () {
      video.removeAttribute('controls'); // Hide controls
    });

    video.addEventListener('click', function () {
      video.setAttribute('controls', 'controls');
    });
  });
}

postsStore.onUpdated(async (data) => {
  totalPostPages = data.total_pages

  if ((data.page == data.total_pages) || (data.new_data.length == 0)) {
    $('.infinite-scroll-preloader.home-posts').hide()

    if (data.data.length == 0) {
      $('#tab-latest .data').html('<p class="text-center">No posts</p>')
      return;
    }
  }

  if (data.reset) {
    refreshed = data.reset
  }

  displayPosts(data.new_data, false)
})

followingPostsStore.onUpdated((data) => {
  totalFPostPages = data.total_pages

  if ((data.page == data.total_pages) || (data.new_data.length == 0)) {
    $('.infinite-scroll-preloader.home-following-posts').hide()
    if (data.data.length == 0) {
      $('#tab-following .data').html('<p class="text-center">No posts</p>')
      return;
    }
  }

  if (data.reset) {
    refreshed = data.reset
  }

  displayPosts(data.new_data, true)
})

$(document).on('infinite', '.infinite-scroll-content.home-page', async function () {
  if (isFetchingPosts) return

  const totalPages = activeTab === 'following' ? totalFPostPages : totalPostPages
  const storeName = activeTab === 'following' ? 'getFollowingPosts' : 'getPosts'

  if (activeTab === 'following') {
    currentFollowingPostsPage++
  } else {
    currentPostsPage++
  }

  const currentPage = activeTab === 'following' ? currentFollowingPostsPage : currentPostsPage
  if (currentPage >= totalPages) {
    return
  }

  isFetchingPosts = true

  await store.dispatch(storeName, {
    page: currentPage
  })
  isFetchingPosts = false
})

$(document).on('page:beforein', '.page[data-name="social"]', function (e) {
  const ptrContent = app.ptr.get('.ptr-content.home-page')
  ptrContent.on('refresh', async function () {
    refreshed = true
    const storeName = activeTab === 'following' ? 'getFollowingPosts' : 'getPosts'

    if (isFetchingPosts) return

    isFetchingPosts = true

    if (activeTab === 'following') {
      currentFollowingPostsPage = 1
    } else {
      currentPostsPage = 1
    }

    await store.dispatch(storeName, {
      page: 1,
      clear: true
    })

    isFetchingPosts = false
    app.ptr.done()
  })

  app.toolbar.show('.toolbar.toolbar-bottom', true)
})

// before page out
$(document).on('page:beforeout', '.page[data-name="social"]', function () {
  pauseAllVideos()
})

/* Based on this http://jsfiddle.net/brettwp/J4djY/*/
export function detectDoubleTapClosure(callback) {
  let lastTap = 0
  let timeout

  return function detectDoubleTap(event) {

    const curTime = new Date().getTime()
    const tapLen = curTime - lastTap
    if (tapLen < 500 && tapLen > 0) {
      event.preventDefault()

      // pass the event target to the callback
      callback(event.target)
    } else {
      timeout = setTimeout(() => {
        clearTimeout(timeout)
      }, 500)
    }

    lastTap = curTime
  }
}

// event listener for tab change
$(document).on('click', '.social-tabs .tab-link', async function (e) {
  const type = this.getAttribute('data-type')
  activeTab = type
})

function preloadImage(url) {
  const MAX_PRELOADS = 10;

  // Get all preloaded images
  const preloadedImages = document.querySelectorAll('.post-media-preloader');

  // If there are more than 10 preloaded images, remove the first one
  if (preloadedImages.length >= MAX_PRELOADS) {
    preloadedImages[0].remove();
  }

  // Preload the image
  document.head.insertAdjacentHTML('beforeend', `
    <link rel="preload" href="${url}" as="image" class="post-media-preloader" />
  `);
}

async function displayPosts(posts, following = false) {
  const postsContainer = $(following ? '#tab-following .data' : '#tab-latest .data');

  if (refreshed) {
    postsContainer.html('')
    refreshed = false
  }

  const user = await getSessionUser()

  posts.forEach(post => {
    let post_actions = `
      <div class="media-post-actions">
        <div class="media-post-like" data-post-id="${post.id}">
          <i class="icon f7-icons ${post.is_liked ? 'text-red' : ''}" data-post-id="${post.id}">${post.is_liked ? 'heart_fill' : 'heart'}</i>
        </div>
        <div class="media-post-comment popup-open" data-popup=".comments-popup" data-post-id="${post.id}">
          <i class="icon f7-icons">chat_bubble</i>
        </div>
        <div class="media-post-share popup-open" data-popup=".share-popup">
          <i class="icon f7-icons">paperplane</i>
        </div>
    `;

    if (post.user_id == user.id) {
      post_actions += `
        <div class="media-post-edit popup-open" data-popup=".edit-post-popup" data-post-id="${post.id}">
          <i class="icon f7-icons">gear_alt</i>
        </div>
      `;
    }

    post_actions += `</div>`;

    const date = formatPostDate(post.post_date);
    const maxDescriptionLength = 200; // Set your character limit here
    const isLongDescription = post.caption.length > maxDescriptionLength;
    const shortDescription = isLongDescription ? post.caption.slice(0, maxDescriptionLength) : post.caption;

    let imageHeight = 400;

    if (post.media.length > 0) {
      const intrinsicWidth = post.media[0].media_width;
      const intrinsicHeight = post.media[0].media_height;
      const media_type = post.media[0].media_type;

      // Calculate intrinsic aspect ratio
      const intrinsicRatio = intrinsicWidth / intrinsicHeight;

      // Calculate the rendered height based on the container width
      const renderedHeight = containerWidth / intrinsicRatio;

      // Use either the rendered height or the fallback height
      if (renderedHeight > 0) {
        if (renderedHeight > 500) {
          imageHeight = 500
        } else {
          imageHeight = renderedHeight
        }


        if (media_type === 'video') {
          imageHeight = renderedHeight
        }
      }
    }

    let profile_link;

    if (post.user_id == user.id) {
      profile_link = `
      <a href="#" class="view-profile media-post-header">
        <div class="media-post-avatar" style="background-image: url('${post.user_profile_image || 'assets/img/profile-placeholder.jpg'}');"></div>
        <div class="media-post-user">${post.username}</div>
        <div class="media-post-date">${date}</div>
      </a>`
    } else {
      profile_link = `
      <a href="/profile-view/${post.user_id}" class="media-post-header">
        <div class="media-post-avatar" style="background-image: url('${post.user_profile_image || 'assets/img/profile-placeholder.jpg'}');"></div>
        <div class="media-post-user">${post.username}</div>
        <div class="media-post-date">${date}</div>
      </a>`
    }

    const postItem = `
      <div class="media-post" data-post-id="${post.id}" data-is-liked="${post.is_liked}">
        <div class="media-post-content">
          ${profile_link}
          <div class="media-post-content">
          <swiper-container pagination class="demo-swiper-multiple" space-between="50">
                ${post.media.map((mediaItem, index) => {
      // Preload the first image in the post
      if (index === 0 && mediaItem.media_type !== 'video') {
        preloadImage(mediaItem.media_url)
      }

      // // create a url encoded string for the media url
      // const videoThumbnail = mediaItem.media_type === 'video' ?
      //   encodeURIComponent(`${mediaItem.media_url}/thumbnails/thumbnail.jpg`) : '';

      return `
          <swiper-slide class="swiper-slide post-media ${mediaItem.media_type === 'video' ? 'video' : ''}" style="height: ${imageHeight}px; ">
                    ${mediaItem.media_type === 'video' ?
          `<video 
              style="height: ${imageHeight}px;" 
              class="video-js" 
              data-src="${mediaItem.media_url}/manifest/video.m3u8" 
              preload="auto" 
              playsinline 
              loop 
              controls 
              autoplay 
              poster="${mediaItem.media_url}/thumbnails/thumbnail.jpg"  <!-- Add the thumbnail as the poster image -->
            ></video>`
          : `<img src="${mediaItem.media_url}" 
                  alt="${mediaItem.caption || post.username + 's post'}"
                  style="text-align: center;"
                  onerror = "this.style.display='none';"
              />`}
            </swiper-slide>`}).join('')}
          </swiper-container>
          </div>
          ${post_actions}
          <div class="media-post-likecount" data-like-count="${post.likes_count}">${post.likes_count} likes</div>
          <div class="media-post-description">
            <strong>${post.username}</strong> <br/> <span class="post-caption">${shortDescription}</span>
            <span class="full-description hidden">${post.caption}</span>
            ${isLongDescription ? `<span class="media-post-readmore">... more</span>` : ''}
          </div>
          ${post.comments_count > 0 ? `<div class="media-post-commentcount popup-open" data-popup=".comments-popup" data-post-id="${post.id}">View ${post.comments_count} comments</div>` : ''}
        </div>
      </div>
    `;

    postsContainer.append(postItem);
  });

  loadVideos()
}

export function togglePostLike(postId, single = false) {
  // Find all post elements with the specified postId
  let container = single ? `.media-post.single[data-post-id="${postId}"]` : `.media-post[data-post-id="${postId}"]`
  const postElements = document.querySelectorAll(container)

  // Iterate through all matching post elements and update them
  postElements.forEach(postElement => {
    const likeIcon = postElement.querySelector('.media-post-like i')
    const isLiked = postElement.getAttribute('data-is-liked') === 'true'
    const likeCountElem = postElement.querySelector('.media-post-likecount')
    let likeCount = parseInt(likeCountElem.getAttribute('data-like-count'))

    // Toggle the like state
    if (isLiked) {
      likeIcon.classList.remove('text-red')
      likeIcon.innerText = 'heart'
      likeCount--
      postElement.setAttribute('data-is-liked', 'false')
    } else {
      likeIcon.classList.add('text-red')
      likeIcon.innerText = 'heart_fill'
      likeCount++
      postElement.setAttribute('data-is-liked', 'true')
    }

    // Update like count
    likeCountElem.innerText = `${likeCount} likes`
    likeCountElem.setAttribute('data-like-count', likeCount)

    if (single) {
      var pathStore = store.getters.getPathData

      if (pathStore && pathStore.value[`/post/${postId}`]) {
        var post = pathStore.value[`/post/${postId}`]
        post.is_liked = !isLiked
        post.likes_count = likeCount

        store.dispatch('setPathData', {
          path: `/post/${postId}`,
          data: post,
        })
      }
    }
  })


  // Optionally, make an API call to update the like status on the server
  maybeLikePost(postId)
}

function displayComments(comments, postId) {
  const user = store.getters.user.value

  const commentsContainer = document.getElementById('comments-list')
  // reset the comments container
  commentsContainer.innerHTML = ''
  const commentForm = document.getElementById('comment-form')
  commentForm.setAttribute('data-post-id', postId)

  if (!comments.length) {
    commentsContainer.innerHTML = '<div class="no-comments">No comments found</div>'
    return
  }

  comments.forEach(comment => {
    const replyItems = comment.replies.length > 0 ? `
        <div class="comment-replies">
          <span class="comment-replies-toggle" data-replies-count="${comment.replies.length}">
            Show ${comment.replies.length} ${comment.replies.length > 1 ? 'replies' : 'reply'}
          </span>
          <div class="comment-replies-container">
            ${comment.replies.map(reply => {

      // Determine the delete button visibility
      const deleteButton = reply.user_id == user.id ?
        `<div class="comment-delete" data-comment-id="${reply.id}">
                <i class="icon f7-icons text-red">trash</i>
                </div>` :
        '';

      return `
                <div class="comment" data-comment-id="${reply.id}" data-is-liked="${reply.liked}" data-owner-id="${reply.user_id}"
                  data-owner-name="${reply.user_login}">

                  <a href="#" data-url="${reply.user_id == user.id ? '#' : `/profile-view/${reply.user_id}`}" class="${reply.user_id == user.id ? 'view-profile' : ''} comment-profile-img" style="background-image:url('${reply.profile_image || 'assets/img/profile-placeholder.jpg'}');">
                  </a>

                  <div class="comment-content-container">
                    <div class="comment-username">
                      <a href="#" data-url="${reply.user_id == user.id ? '#' : `/profile-view/${reply.user_id}`}" class="${reply.user_id == user.id ? 'view-profile' : 'a'}">
                        ${reply.user_login}
                      </a>
                      <span class="date">${formatPostDate(reply.comment_date)}</span>
                    </div>
                    
                    <div class="comment-content">${reply.comment}</div>
                    <div class="comment-actions">
                      <div class="comment-like">
                        <i class="icon f7-icons ${reply.liked ? 'text-red' : ''}">
                          ${reply.liked ? 'heart_fill' : 'heart'}
                        </i> 
                        <span class="comment-likes-count" data-likes-count="${reply.likes_count}">
                          ${reply.likes_count}
                        </span>
                      </div>
                      <div class="comment-reply">
                        <i class="icon f7-icons">chat_bubble</i> <span>Reply</span>
                      </div>
                      ${deleteButton}
                    </div>
                  </div>
                  <div class="clearfix"></div>
                </div>`;
    }).join('')}
          </div>
        </div>` : '';

    let commenter_link = `/profile-view/${comment.user_id}`;

    if (comment.user_id == user.id) {
      commenter_link = '/profile/';
    }

    const deleteButton = comment.user_id == user.id ?
      `<div class="comment-delete" data-comment-id="${comment.id}"><i class="icon f7-icons text-red">trash</i></div>` : '';

    const commentItem = `
      <div class="comment" 
        data-comment-id="${comment.id}" 
        data-is-liked="${comment.liked}" 
        data-owner-id="${comment.user_id}"
        data-owner-name="${comment.user_login}">

         <a href="#" data-url="${comment.user_id == user.id ? '#' : `/profile-view/${comment.user_id}`}" class="${comment.user_id == user.id ? 'view-profile' : ''} comment-profile-img" 
         style="background-image:url('${comment.profile_image || 'assets/img/profile-placeholder.jpg'}');">
         </a>
        <div class="comment-content-container">
          <div class="comment-username">
            <a href="#" data-url="${comment.user_id == user.id ? '#' : `/profile-view/${comment.user_id}`}" class="${comment.user_id == user.id ? 'view-profile' : 'a'}">
                ${comment.user_login}
            </a>
            <span class="date">${formatPostDate(comment.comment_date)}</span>
          </div>
          <div class="comment-content">${comment.comment}</div>
          <div class="comment-actions">
            <div class="comment-like">
              <i class="icon f7-icons ${comment.liked && 'text-red'}">${comment.liked ? 'heart_fill' : 'heart'}</i> 
              <span class="comment-likes-count" data-likes-count="${comment.likes_count}">
                ${comment.likes_count}
              </span>
            </div>
            <div class="comment-reply">
              <i class="icon f7-icons">chat_bubble</i> <span>Reply</span>
            </div>
            ${deleteButton}
          </div>
          ${replyItems}
        </div>
        <div class="clearfix"></div>
      </div>
    `
    commentsContainer.insertAdjacentHTML('beforeend', commentItem)
  })

  // Add click event listener for liking a comment
  const likeButtons = document.querySelectorAll('.comment-like')
  likeButtons.forEach(button => {
    button.addEventListener('click', (event) => {
      const commentId = event.currentTarget.closest('.comment').getAttribute('data-comment-id')
      const ownerId = event.currentTarget.closest('.comment').getAttribute('data-owner-id')
      toggleCommentLike(commentId, ownerId)
    })
  })
}

function toggleCommentLike(commentId, ownerId) {
  // Find the comment element and its like icon
  const commentElement = document.querySelector(`.comment[data-comment-id="${commentId}"]`)
  const likeIcon = commentElement.querySelector('.comment-like i')
  const isLiked = commentElement.getAttribute('data-is-liked') === 'true'
  const likeCountElem = commentElement.querySelector('.comment-likes-count')
  let likeCount = parseInt(likeCountElem.getAttribute('data-likes-count'))

  // Toggle the like state
  if (isLiked) {
    likeIcon.classList.remove('text-red')
    likeIcon.innerText = 'heart'
    likeCount--
    commentElement.setAttribute('data-is-liked', 'false')
  } else {
    likeIcon.classList.add('text-red')
    likeIcon.innerText = 'heart_fill'
    likeCount++
    commentElement.setAttribute('data-is-liked', 'true')
  }

  // Update like count
  likeCountElem.innerText = likeCount
  likeCountElem.setAttribute('data-likes-count', likeCount)

  maybeLikeComment(commentId, ownerId)
}

$(document).on('click', '.media-post-readmore', function () {
  const postDescription = this.previousElementSibling.previousElementSibling; // The short description
  const fullDescription = this.previousElementSibling; // The full description

  if (fullDescription.classList.contains('hidden')) {
    postDescription.classList.add('hidden');
    fullDescription.classList.remove('hidden');
    this.textContent = '... less';
  } else {
    postDescription.classList.remove('hidden');
    fullDescription.classList.add('hidden');
    this.textContent = '... more';
  }
});

$(document).on('click', '.media-post-like i', (e) => {
  const postId = e.target.getAttribute('data-post-id')

  const parent = e.target.closest('.media-post')
  const isSingle = parent.classList.contains('single') ? true : false

  togglePostLike(postId, isSingle)
})

// set the post id as a data attribute from the edit post popup
$(document).on('click', '.media-post-edit', function () {
  const postId = $(this).closest('.media-post').attr('data-post-id')
  const isSingleView = $(this).closest('.media-post').hasClass('single')
  $('.edit-post-popup').attr('data-post-id', postId)
  $('.edit-post-popup').attr('data-is-single', isSingleView)
})

$(document).on('click', '#delete-post', function () {
  var view = app.views.current

  // set the post id as a data attribute from the edit post popup
  const postId = $('.edit-post-popup').attr('data-post-id')
  const isSingleView = $('.edit-post-popup').attr('data-is-single')

  app.dialog.confirm('Are you sure you want to delete this post?', 'Delete Post', async () => {
    app.preloader.show()
    const response = await deletePost(postId)
    app.preloader.hide()

    if (response) {
      store.dispatch('getMyPosts', {
        page: 1,
        clear: true
      })
      store.dispatch('getMyTags', {
        page: 1,
        clear: true
      })

      console.log(isSingleView);

      if (isSingleView == 'true') {
        // $('.view-profile-link').click()
        view.router.back()
        // view.router.navigate('/profile/')
      }

      showToast('Post deleted successfully')
      // remove the post from the DOM
      $(`.media-post[data-post-id="${postId}"]`).remove()
      app.popup.close('.edit-post-popup')
    } else {
      showToast('Failed to delete post')
      app.preloader.hide()
    }
  })
})

$(document).on('click', '#edit-post', function () {
  var view = app.views.current

  // set the post id as a data attribute from the edit post popup
  const postId = $('.edit-post-popup').attr('data-post-id')
  view.router.navigate(`/post-edit/${postId}`, {
    force: true
  })

  app.popup.close('.edit-post-popup')
})

$(document).on('touchstart', '.media-post-content .post-media', detectDoubleTapClosure((e) => {
  const parent = e.closest('.media-post')
  const postId = parent.getAttribute('data-post-id')
  const isLiked = parent.getAttribute('data-is-liked') === 'true'

  if (isLiked) {
    return
  }

  togglePostLike(postId)
}), {
  passive: false
})

// media-post-video click
$(document).on('click', '.media-post-video', function () {
  if (this.paused) {
    this.play()
  } else {
    this.pause()
  }
})

// on .popup-open click
$(document).on('click', '.media-post-comment, .media-post-commentcount', async function () {
  const postId = this.getAttribute('data-post-id')

  if (!postId) {
    return
  }

  document.getElementById('comments-list').innerHTML = '<div class="preloader"></div>'
  document.getElementById('comment-form').reset()

  // update the post id in the comment form
  document.getElementById('comment-form').setAttribute('data-post-id', '')
  document.getElementById('comment-form').removeAttribute('data-comment-id')

  document.getElementById('comment-form').querySelector('.replying-to').innerHTML = ''
  document.getElementById('comment-form').querySelector('.replying-to').classList.add('hidden')

  try {
    const comments = await fetchComments(postId)
    displayComments(comments, postId)
  } catch (error) {
    app.notification.create({
      titleRightText: 'now',
      subtitle: 'Oops, something went wrong',
      text: error.message || 'Failed to fetch comments',
    }).open()
  }
})

$(document).on('click', '.media-post-share', function () {
  // set the post id as a data attribute 
  const postId = $(this).closest('.media-post').attr('data-post-id')
  $('.share-popup').attr('data-post-id', postId)
  $('#copy-link').attr('data-clipboard-text', `${window.location.origin}/post-view/${postId}`)
})

$(document).on('click', '#share-post-email', function () {
  const postId = $(this).closest('.popup').attr('data-post-id')
  const postLink = `${window.location.origin}/post-view/${postId}`

  // open the email composer
  window.open(`mailto:?subject=Check out this post&body=${postLink}`)
})

// data-clipboard-text click
$(document).on('click', '#copy-link', function () {
  const copyText = $(this).attr('data-clipboard-text')
  navigator.clipboard.writeText(copyText)

  app.toast.create({
    text: 'Link copied to clipboard',
    closeTimeout: 2000
  }).open()
})

// on .comment-replies-toggle click
$(document).on('click', '.comment-replies-toggle', function () {
  const commentRepliesContainer = this.nextElementSibling
  commentRepliesContainer.classList.toggle('show')
  const repliesCount = this.getAttribute('data-replies-count')

  this.innerText = this.innerText === `Show ${repliesCount} replies` ? `Hide ${repliesCount} replies` : `Show ${repliesCount} replies`
})

// on comment form submit
$('#comment-form').on('submit', async function (e) {
  e.preventDefault()

  const postId = this.getAttribute('data-post-id')
  const commentId = this.getAttribute('data-comment-id')
  const comment = this.comment.value

  if (!comment) {
    // app.dialog.alert('Please enter a comment')
    return
  }

  app.preloader.show()

  try {
    const response = await addComment(postId, comment, commentId)

    app.preloader.hide()

    if (response) {
      this.reset()
      this.removeAttribute('data-comment-id')
      this.querySelector('.replying-to').innerHTML = ''
      this.querySelector('.replying-to').classList.add('hidden')
      const comments = await fetchComments(postId)
      displayComments(comments, postId)
    } else {
      app.notification.create({
        text: 'Failed to add comment',
        titleRightText: 'now',
        subtitle: 'Oops, something went wrong',
      }).open()
    }
  } catch (error) {
    app.notification.create({
      titleRightText: 'now',
      subtitle: 'Oops, something went wrong',
      text: error.message || 'Failed to add comment',
    }).open()
    app.preloader.hide()
  }
})

//.comment-reply click
$(document).on('click', '.comment-reply', function () {
  // get the comment id, and comment owner id
  const commentId = this.closest('.comment').getAttribute('data-comment-id')
  const ownerId = this.closest('.comment').getAttribute('data-owner-id')
  const ownerName = this.closest('.comment').getAttribute('data-owner-name')

  // add something above the comment form to show the user they are replying to a comment
  // add the comment id to the form
  document.getElementById('comment-form').setAttribute('data-comment-id', commentId)
  document.getElementById('comment-form').comment.focus()

  // add the owner name to the form
  //  <span class="replying-to">Replying to <strong>m88xrk</strong></span>
  const replyingTo = document.getElementById('comment-form').querySelector('.replying-to')
  replyingTo.innerHTML = `Replying to <strong>${ownerName}</strong>`
  replyingTo.classList.remove('hidden')
  document.getElementById('comment-form').prepend(replyingTo)
})

$(document).on('click', '.comment-delete', async function () {
  app.dialog.confirm('Are you sure you want to delete this comment? This will remove all replies to this comment', 'Delete Comment', async () => {
    try {
      const commentId = this.getAttribute('data-comment-id')
      const response = await deleteComment(commentId)

      if (response && response.success) {
        // remove the comment from the DOM
        $(`.comment[data-comment-id="${commentId}"]`).remove()
        showToast('Comment deleted successfully')
      }
    } catch (error) {
      app.dialog.alert('Failed to delete comment')
    }
  })
})

$(document).on('click', '.comment a', function (e) {
  var view = app.views.current
  // hide the comments popup
  app.popup.close()

  // get the href attribute
  const href = this.getAttribute('data-url')

  if (!href || href === '#') {
    return
  }

  // prevent the default action
  e.preventDefault()

  view.router.navigate(href, {
    force: true
  })
})

/js/notifications.js
import {
    getSessionUser
} from "./api/auth.js"
import {
    approvePostTag,
    maybeFollowUser,
    removeTagFromPost
} from "./api/profile.js"
import app, { showToast } from "./app.js"
import store from "./store.js"

var $ = Dom7
var notificationsStore = store.getters.getNotifications
var refreshed = false
var userStore = store.getters.user
let notificationInterval = null

$(document).on('page:afterin', '.page[data-name="notifications"]', async function (e) {
    const data = notificationsStore.value

    if (!data || !data.success) {
        return
    }

    // a list if all unread notification ids
    const unreadNotificationIds = [
        ...data.data.recent,
        ...data.data.last_week,
        ...data.data.last_30_days,
    ].filter((item) => item.is_read === "0").map((item) => item._id);

    if (unreadNotificationIds.length > 0) {
        store.dispatch('markNotificationsAsRead', unreadNotificationIds)
    }
})

userStore.onUpdated((data) => {
    if (data && data.id) {
        store.dispatch('notificationCount')
        store.dispatch('fetchNotifications', {
            load_more: false
        })

        // fetch notifications every 1 min
        // create an interval to fetch notifications every 1 min
        if (!notificationInterval) {
            notificationInterval = setInterval(() => {
                refreshed = true
                store.dispatch('notificationCount')
                store.dispatch('fetchNotifications', {
                    load_more: false
                })
            }, 60000)
        } else {
            clearInterval(notificationInterval)
            notificationInterval = setInterval(() => {
                refreshed = true
                store.dispatch('notificationCount')
                store.dispatch('fetchNotifications', {
                    load_more: false
                })
            }, 60000)
        }
    }

    if (!data || !data.id) {
        clearInterval(notificationInterval)
    }
})

notificationsStore.onUpdated(async (data) => {
    if (!data || !data.success) {
        $('.notification-wrap').html('<p class="text-center">No notifications</p>')
        return
    }

    const notifications = data.data

    const recentContainer = document.getElementById('recent');
    const thisWeekContainer = document.getElementById('this-week');
    const last30DaysContainer = document.getElementById('last-30-days');

    if (refreshed) {
        recentContainer.innerHTML = '';
        thisWeekContainer.innerHTML = '';
        last30DaysContainer.innerHTML = '';
        refreshed = false
    }


    var user = await getSessionUser()

    document.querySelectorAll('.app-notification-title').forEach(elem => {
        if (elem.getAttribute('data-id') === 'last-30') {
            if (notifications.last_30_days.length > 0) {
                elem.innerHTML = elem.getAttribute('data-title');
            } else {
                elem.innerHTML = '';
            }
            return
        }
        elem.innerHTML = elem.getAttribute('data-title');
    })

    if (!notifications.recent.length && !notifications.is_paginated) {
        recentContainer.innerHTML = '<p class="text-center">No recent notifications</p>';
    }

    if (!notifications.last_week.length && !notifications.has_more_notifications) {
        thisWeekContainer.innerHTML = '<p class="text-center">No notifications from this week</p>';
    }

    notifications.last_30_days.forEach(notification => {
        const notificationItem = createNotificationItem(notification, user);
        last30DaysContainer.appendChild(notificationItem);
    });

    notifications.recent.forEach(notification => {
        const notificationItem = createNotificationItem(notification, user);
        recentContainer.appendChild(notificationItem);
    });

    notifications.last_week.forEach(notification => {
        const notificationItem = createNotificationItem(notification, user);
        thisWeekContainer.appendChild(notificationItem);
    });

    // add a load more button at the end
    if ((notifications.recent.length >= 0 || notifications.last_week.length >= 0) && (notifications.has_more_notifications)) {
        $('.load-more-notifications').removeClass('hidden');
    } else {
        $('.load-more-notifications').addClass('hidden');
    }
})

$(document).on('click', '.load-more-notifications', async function (e) {
    await store.dispatch('fetchNotifications', {
        load_more: true
    })
})

function timeAgo(dateString) {
    const now = new Date();
    const past = new Date(dateString);
    const diffInHours = Math.floor((now - past) / (1000 * 60 * 60));
    return diffInHours > 24 ? `${Math.floor(diffInHours / 24)}d ago` : `${diffInHours}h ago`;
}

function createNotificationItem(notification, user) {
    const isFollow = notification.type === 'follow' ? true : false;
    const container = document.createElement(isFollow ? 'div' : 'a');

    if (!isFollow) {
        container.href = `/post-view/${notification.entity.entity_id}`;
    }

    let isReadClass = notification.is_read == "0" ? "unread-notif" : "";

    container.className = `notification-item ${isReadClass}`;
    container.dataset.notificationId = notification._id;

    // Profile image and notification content container
    const leftContainer = document.createElement('div');
    leftContainer.className = 'notification-left';

    const imageDiv = document.createElement('a');
    imageDiv.className = 'image-square image-rounded';
    imageDiv.style.backgroundImage = `url('${notification.entity.initiator_data.profile_image || 'assets/img/profile-placeholder.jpg'}')`;
    imageDiv.href = `/profile-view/${notification.entity.user_id}`;

    const infoDiv = document.createElement('div');
    infoDiv.className = 'notification-info';

    let content = '';

    // Conditional content rendering based on the type
    if (notification.type === 'like') {
        content = `
            <div class="notification-text">
                <a href="/profile-view/${notification.entity.user_id}"><strong>${notification.entity.initiator_data.display_name}</strong></a> liked your ${notification.entity.entity_type}
                ${notification.entity.entity_data.comment ? `<span class="inline font-semibold text-black">: "${notification.entity.entity_data.comment}"</span>` : ''}
                <span class=""></span>
            </div>
        `;
    } else if (notification.type === 'comment') {
        const eclipseComment = notification.entity.entity_data.comment.length > 50 ? notification.entity.entity_data.comment.substring(0, 50) + '...' : notification.entity.entity_data.comment;

        container.href = `/post-view/${notification.entity.entity_data.post_id}?commentId=${notification.entity.entity_id}`;

        content = `
            <div class="notification-text">
                <a href="/profile-view/${notification.entity.user_id}"><strong>${notification.entity.initiator_data.display_name}</strong></a> commented on your post: 
                <span class="font-semibold text-black">"${eclipseComment}"</span>
                <span class="${isReadClass}"></span>
            </div>
        `;
    } else if (notification.type === 'follow') {
        content = `
            <div class="notification-text">
                <a href="/profile-view/${notification.entity.user_id}"><strong>${notification.entity.initiator_data.display_name}</strong></a> followed you
                <span class="${isReadClass}"></span>
            </div>
        `;
    } else if (notification.type === 'mention') {
        content = `
            <div class="notification-text">
                <a href="/profile-view/${notification.entity.user_id}"><strong>${notification.entity.initiator_data.display_name}</strong></a> mentioned you in a ${notification.entity.entity_type}
                ${notification.entity.entity_data.comment ? `<span class="block font-semibold text-black">"${notification.entity.entity_data.comment}"</span>` : ''}
                <span class="${isReadClass}"></span>
            </div>
        `;
    } else if (notification.type === 'post') {
        // <a href="/profile-garage-vehicle-view/${notification.entity.entity_data?.garage?.id}">
        //     <strong>${notification.entity.entity_data?.garage?.make || ''} ${notification.entity.entity_data?.garage?.model || ''}</strong>
        // </a>
        content = `
            <div class="notification-text">
                <a href="/profile-view/${notification.entity.user_id}"><strong>${notification.entity.initiator_data.display_name}</strong></a> has tagged ${notification.entity.entity_type === 'car' ? "your car" : "you"} in a post
                <span class="${isReadClass}"></span>
            </div>
            ${(notification.entity.entity_type === 'car' && !notification.entity.entity_data.tag_approved) ? `<div class="notification-text tag-actions">
                <div class="btn btn-primary btn-sm approve-tag" data-tag-id="${notification.entity.entity_data.tag_id}">Approve</div>
                <div class="btn btn-secondary btn-sm decline-tag" data-tag-id="${notification.entity.entity_data.tag_id}">Decline</div>
                </div>` : ''}
        `;
    } else if (notification.type === 'tag') {
        content = `
            <div class="notification-text">
                <a href="/profile-view/${notification.entity.user_id}"><strong>${notification.entity.initiator_data.display_name}</strong></a> ${notification.entity.entity_type === 'car' ? "tagged your car in a post" : "tagged you in a post"}
                <span class="${isReadClass}"></span>
            </div>
            ${(!notification.entity.entity_data.tag_approved && notification.entity.entity_data.tag_id) ? `<div class="notification-text tag-actions">
                <div class="btn btn-primary btn-sm approve-tag" data-tag-id="${notification.entity.entity_data.tag_id}">Approve</div>
                <div class="btn btn-secondary btn-sm decline-tag" data-tag-id="${notification.entity.entity_data.tag_id}">Decline</div>
                </div>` : ''}
        `;

        container.href = `#`;
    }

    // Add time ago
    const timeSpan = document.createElement('span');
    timeSpan.className = 'notification-time';
    timeSpan.textContent = timeAgo(notification.date);

    infoDiv.innerHTML = `${content} ${timeSpan.outerHTML}`;

    // Adding profile image and content to the left container
    leftContainer.appendChild(imageDiv);
    leftContainer.appendChild(infoDiv);

    // Append left container to the main container
    container.appendChild(leftContainer);

    if (notification.type === 'follow') {
        const isFollowing = user.following.includes(notification.entity.user_id);
        if (!isFollowing) {
            let followBtn = `<div class="btn btn-primary btn-sm toggle-follow" data-is-following="${isFollowing}" data-user-id="${notification.entity.user_id}">
                Follow
            </div>`;
            container.innerHTML += followBtn;
        }
    } else {
        const rightContainer = document.createElement('a');
        rightContainer.className = 'notification-left';
        let path;

        if (notification.entity.entity_type === 'car') {
            path = 'post-view';
            rightContainer.href = `/${path}/${notification.entity.entity_data.post_id}`;

        } else if (notification.entity.entity_type === 'post' || notification.entity.entity_type === 'tag') {
            path = 'post-view';
            rightContainer.href = `/${path}/${notification.entity.entity_id}`;

        } else if (notification.entity.entity_type === 'comment') {
            path = 'post-view';
            rightContainer.href = `/${path}/${notification.entity.entity_data.post_id}?commentId=${notification.entity.entity_id}`;
        } else {
            path = 'profile-view';
            rightContainer.href = `/${path}/${notification.entity.user_id}`;
        }

        if (notification.type === 'tag') {
            path = 'post-view';
            rightContainer.href = `/${path}/${notification.entity.entity_id}`;
        }


        const imageDiv = document.createElement('div');
        imageDiv.className = 'image-square image-rounded';
        imageDiv.style.backgroundImage = `url('${notification.entity.entity_data.media}')`;
        imageDiv.style.backgroundSize = 'cover';
        imageDiv.style.backgroundPosition = 'center';
        imageDiv.style.backgroundColor = '#f1f1f1';

        rightContainer.appendChild(imageDiv);
        container.appendChild(rightContainer)
    }

    return container;
}

$(document).on('click', '.toggle-follow', async function (e) {
    const userId = e.target.dataset.userId;
    const isFollowing = e.target.dataset.isFollowing === 'true';

    if (!isFollowing) {
        // hide the button
        e.target.style.display = 'none';
    }

    // update the button text
    e.target.textContent = isFollowing ? 'Follow' : 'Unfollow';
    e.target.dataset.isFollowing = !isFollowing;

    await maybeFollowUser(userId);
    store.dispatch('updateUserDetails')
});

$(document).on('ptr:refresh', '.notification-page.ptr-content', async function (e) {
    refreshed = true

    try {
        await store.dispatch('notificationCount')
        await store.dispatch('fetchNotifications', {
            load_more: false
        })
    } catch (error) {
        console.log(error);
    }

    app.ptr.get('.notification-page.ptr-content').done()
})

$(document).on('click', '.approve-tag', async function (e) {
    e.preventDefault()

    const tagId = e.target.dataset.tagId

    app.dialog.confirm('Are you sure you want to approve this tag?', 'Approve Tag', async function () {
        try {
            app.preloader.show()

            const response = await approvePostTag(tagId)

            app.preloader.hide()

            if (response.success) {
                // remove the buttons
                e.target.parentElement.innerHTML = ''

                showToast('Tag has been approved')
            } else {
                showToast(response.message || 'Failed to approve tag')
            }
        } catch (error) {
            app.preloader.hide()
            showToast('Failed to approve tag')
        }
    })
})

$(document).on('click', '.decline-tag', async function (e) {
    e.preventDefault()

    const tagId = e.target.dataset.tagId

    app.dialog.confirm('Are you sure you want to decline this tag?', 'Decline Tag', async function () {
        try {
            app.preloader.show()

            const response = await removeTagFromPost(tagId)

            app.preloader.hide()

            if (response.success) {
                // refetch notifications
                store.dispatch('fetchNotifications', {
                    load_more: false
                })
                showToast('Tag has been declined')
            } else {
                showToast(response.message || 'Failed to decline tag')
            }
        } catch (error) {
            app.preloader.hide()
            showToast('Failed to decline tag')
        }
    })
})

/js/profile-edit.js
import {
    deleteUserAccount,
    getSessionUser,
    updatePassword,
    updateUserDetails,
    updateUsername
} from "./api/auth.js"
import {
    addUserProfileLinks,
    removeProfileLink,
    updateCoverImage,
    updateProfileImage,
    updateSocialLinks
} from "./api/profile.js"
import app, {
    showToast
} from "./app.js"
import store from "./store.js"

var $ = Dom7

// --------------- Edit Profile Page ---------------
$(document).on('page:init', '.page[data-name="profile-edit-mydetails"]', async function (e) {
    var view = app.views.current

    const user = await getSessionUser()
    const isEmailVerified = user.email_verified ?? false;

    if (!user) {
        view.router.navigate('/login')
        return
    }

    // Example of how to fill in the form fields with the provided data
    document.querySelector('input[name="email"]').value = user.email || '';

    // if email is not verified, show the verify email button
    if (isEmailVerified) {
        $('#email-verify-span').remove()
    }

    document.querySelector('input[name="first_name"]').value = user.first_name || '';
    document.querySelector('input[name="last_name"]').value = user.last_name || '';
    document.querySelector('input[name="tel_no"]').value = user.billing_info?.phone || '';
})


$(document).on('click', '#save-details', async function () {
    var view = app.views.current

    const user = await getSessionUser()


    // Get the values from the input fields
    const firstName = $('input[name="first_name"]').val().trim();
    const lastName = $('input[name="last_name"]').val().trim();
    const email = $('input[name="email"]').val().trim();
    const telNo = $('input[name="tel_no"]').val().trim();

    // Validate the input fields
    if (firstName === '') {
        showToast('Please enter your first name', 'Error');
        return;
    }

    if (lastName === '') {
        showToast('Please enter your last name', 'Error');
        return;
    }

    if (email === '') {
        showToast('Please enter your email', 'Error');
        return;
    }

    // Simple email validation
    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailPattern.test(email)) {
        showToast('Please enter a valid email address', 'Error');
        return;
    }

    // // phone validation
    // if (telNo === '') {
    //     showToast('Please enter a valid phone number', 'Error');
    //     return
    // }

    // Prepare the data for the API request
    const requestData = {
        first_name: firstName,
        last_name: lastName,
        email: email,
        phone: telNo,
    };

    // check if the request data is the same as userdata
    let dirtied = false;

    for (const key in requestData) {
        if (key == 'phone') {
            if (requestData[key] !== user.billing_info.phone) {
                dirtied = true
                break;
            }
            continue
        }

        if (requestData[key] !== user[key]) {
            dirtied = true
            break;
        }
    }

    if (!dirtied) {
        return
    }

    try {
        app.preloader.show()

        const response = await updateUserDetails(requestData, email !== user.email)

        app.preloader.hide()


        if (response && response.success) {
            // showToast('Details updated successfully', 'Success');
            showToast('Details updated successfully')
            view.router.navigate('/profile/')
            store.dispatch('updateUserDetails')
            return;
        }

        throw new Error(response.message);
    } catch (error) {
        app.preloader.hide()
        app.notification.create({
            titleRightText: 'now',
            subtitle: 'Oops, something went wrong',
            text: error.message || 'Failed to update details',
        }).open()
    }
});

$(document).on('click', '#update_password', async function () {
    var view = app.views.current

    // Get the values from the password input fields
    const password = $('input[name="password"]').val().trim();
    const current_password = $('input[name="current_password"]').val().trim();
    const confirmPassword = $('input[name="confirm_password"]').val().trim();

    if (current_password === '') {
        showToast('Please enter your current password', 'Error');
        return
    }

    if (password.length < 8) {
        showToast('Password must be at least 8 characters long.')
        return
    }

    // Check if password contains at least one lowercase letter
    if (!/[a-z]/.test(password)) {
        showToast('Password must contain at least one lowercase letter.')
        return
    }

    // Check if password contains at least one uppercase letter
    if (!/[A-Z]/.test(password)) {
        showToast('Password must contain at least one uppercase letter.')
        return
    }

    // Check if password contains at least one number
    if (!/\d/.test(password)) {
        showToast('Password must contain at least one number.')
        return
    }

    if (confirmPassword === '') {
        showToast('Please confirm your password', 'Error');
        return;
    }

    if (password !== confirmPassword) {
        showToast('Passwords do not match', 'Error');
        return;
    }

    try {
        app.preloader.show()

        const response = await updatePassword(password, current_password)

        app.preloader.hide()

        if (response && response.success) {
            showToast('Password updated successfully')
            view.router.navigate('/profile/')
            return;
        }

        throw new Error(response.message);
    } catch (error) {
        app.preloader.hide()
        app.notification.create({
            titleRightText: 'now',
            subtitle: 'Oops, something went wrong',
            text: error.message || 'Failed to update password',
        }).open()
    }
})
// --------------- End Edit Profile Page ---------------


// --------------- Edit Username Page ---------------
$(document).on('page:beforein', '.page[data-name="profile-edit-username"]', async function (e) {
    var view = app.views.current

    const user = await getSessionUser()

    if (!user) {
        view.router.navigate('/login')
        return
    }


    // Example of how to fill in the form fields with the provided data
    $('.profile-edit-view input[name="username"]').val(user.username || '')

    if (user.can_update_username) {
        $('#username-editable').remove()
    } else {
        document.querySelector('#username-editable').innerText = `You can change your username in ${user.next_update_username} days`
    }
})

$(document).on('click', '#save-username', async function () {
    var view = app.views.current

    const user = await getSessionUser()

    if (!user.can_update_username) {
        return
    }

    const username = $('.profile-edit-view input[name="username"]').val()

    if (username === '') {
        showToast('Please enter a username', 'Error')
        return
    }

    // username can only have letters, numbers, and underscores
    var usernamePattern = /^[a-zA-Z0-9_]+$/
    if (!usernamePattern.test(username)) {
        showToast('Username can only contain letters, numbers, and underscores')
        return
    }

    // username must be at least 3 characters long
    if (username.length < 3) {
        showToast('Username must be at least 3 characters long')
        return
    }

    if (username === user.username) {
        return
    }

    try {
        app.preloader.show()

        const response = await updateUsername(username)

        app.preloader.hide()

        if (response && response.success) {
            showToast('Username updated successfully', 'Success')
            view.router.navigate('/profile/')

            store.dispatch('updateUserDetails')
        } else {
            throw new Error(response.message)
        }
    } catch (error) {
        app.preloader.hide()
        app.notification.create({
            titleRightText: 'now',
            subtitle: 'Oops, something went wrong',
            text: error.message || 'Failed to update username',
        }).open()
    }


})
// --------------- End Edit Username Page ---------------


// --------------- Edit Profile images Page ---------------
$(document).on('page:init', '.page[data-name="profile-edit-images"]', async function (e) {
    var view = app.views.current

    const user = await getSessionUser()

    if (!user) {
        view.router.navigate('/login')
        return
    }

    // If a cover photo exists, use it as the background image of the upload label
    if (user.cover_image) {
        $('input[name="cover_image"]').closest('.custom-file-upload').find('label').css('background-image', `url('${user.cover_image}')`)
        $('input[name="cover_image"]').closest('.custom-file-upload').find('label').css('background-size', 'cover')
    }

    if (user.profile_image) {
        $('input[name="profile_image"]').closest('.custom-file-upload').find('label').css('background-image', `url('${user.profile_image}')`)
        $('input[name="profile_image"]').closest('.custom-file-upload').find('label').css('background-size', 'contain')
        $('input[name="profile_image"]').closest('.custom-file-upload').find('label').css('background-position', 'center')
        $('input[name="profile_image"]').closest('.custom-file-upload').find('label').css('background-repeat', 'no-repeat')
    }
})

$(document).on('click', '#save-profile-images', async function () {
    var view = app.views.current

    const cover_image = $('input[name="cover_image"]').prop('files')[0]
    const profile_image = $('input[name="profile_image"]').prop('files')[0]


    let coverBase64 = null
    let profileBase64 = null

    if (cover_image) {
        // Wrap the FileReader in a Promise to wait for it to complete
        coverBase64 = await new Promise((resolve, reject) => {
            const reader = new FileReader()
            reader.readAsDataURL(cover_image)

            reader.onload = () => resolve(reader.result)
            reader.onerror = () => reject(new Error('Failed to read image as base64'))
        })
    }

    if (profile_image) {
        // Wrap the FileReader in a Promise to wait for it to complete
        profileBase64 = await new Promise((resolve, reject) => {
            const reader = new FileReader()
            reader.readAsDataURL(profile_image)

            reader.onload = () => resolve(reader.result)
            reader.onerror = () => reject(new Error('Failed to read image as base64'))
        })
    }

    if (!coverBase64 && !profileBase64) {
        return
    }

    try {
        app.preloader.show()

        let promises = []

        if (profileBase64) {
            promises.push(updateProfileImage(profileBase64))
        }

        if (coverBase64) {
            promises.push(updateCoverImage(coverBase64))
        }

        const responses = await Promise.all(promises)
        app.preloader.hide()

        if (responses.every(response => response && response.success)) {
            showToast('Images updated successfully', 'Success')
            view.router.navigate('/profile/')

            store.dispatch('updateUserDetails')
            return
        }

        if (responses.some(response => response && !response.success)) {
            throw new Error('Failed to update images')
        }
    } catch (error) {
        app.preloader.hide()

        app.notification.create({
            titleRightText: 'now',
            subtitle: 'Oops, something went wrong',
            text: error.message || 'Failed to update images',
        }).open()
    }
})

$(document).on('change', 'input[name="cover_image"]', function (e) {
    const file = e.target.files[0];
    const reader = new FileReader();

    reader.onload = function (event) {
        console.log($('.custom-file-upload.cover'));

        $('.custom-file-upload.cover')
            .find('label')
            .css('background-image', `url('${event.target.result}')`)
            .css('background-size', 'cover');
    };

    if (file) {
        reader.readAsDataURL(file);
    }
});

$(document).on('change', 'input[name="profile_image"]', function (e) {
    const file = e.target.files[0];
    const reader = new FileReader();

    reader.onload = function (event) {
        console.log($('.custom-file-upload.profile'));

        $('.custom-file-upload.profile')
            .find('label')
            .css('background-image', `url('${event.target.result}')`)
            .css('background-size', 'cover');
    };

    if (file) {
        reader.readAsDataURL(file);
    }
});
// --------------- End Profile images Page ---------------


// --------------- Edit Socials Page ---------------
$(document).on('page:init', '.page[data-name="profile-edit-socials"]', async function (e) {
    var view = app.views.current

    const user = await getSessionUser()

    if (!user) {
        view.router.navigate('/login')
        return
    }

    const externalLinks = user.profile_links || {};

    app.popup.create({
        el: '.add-link-popup',
        swipeToClose: 'to-bottom'
    });

    // Populate form fields
    document.querySelector('input[name="social_instagram"]').value = externalLinks.instagram || '';
    document.querySelector('input[name="social_facebook"]').value = externalLinks.facebook || '';
    document.querySelector('input[name="social_tiktok"]').value = externalLinks.tiktok || '';
    document.querySelector('input[name="social_youtube"]').value = externalLinks.youtube || '';
    document.querySelector('input[name="social_mivia"]').value = externalLinks.mivia || '';
    document.querySelector('input[name="social_custodian"]').value = externalLinks.custodian || '';

    // .social-other-links ul
    const externalLinksContainer = $('.social-other-links ul')[0];

    externalLinks.external_links?.forEach(linkObj => {
        const listItem = document.createElement('li');


        listItem.innerHTML = `
        <a class="item-link item-content" href="${linkObj.link.url}" data-link-id="${linkObj.id}">
            <div class="item-inner">
                <div class="item-title">
                    ${linkObj.link.label}
                </div>
                <div class="item-after delete-external-link"><i class="icon f7-icons">xmark_circle</i></div>
            </div>
        </a>
        `;
        externalLinksContainer.appendChild(listItem);
    });
})

// Add event listener for the Save button
$(document).on('click', '#add-link-btn', async function () {
    const linkTitle = $('input[name="custom_link_title"]').val();
    const linkUrl = $('input[name="custom_link_url"]').val();

    // Validate the inputs
    if (linkTitle === '') {
        console.log('Please enter a link title.', $('input[name="custom_link_title"]'));
        showToast('Please enter a link title.', 'Error');
        return;
    }

    if (linkUrl === '') {
        showToast('Please enter a link URL.', 'Error');
        return;
    }

    // Simple URL validation (basic check)
    // const urlPattern = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([/\w \.-]*)*\/?$/;
    // if (!urlPattern.test(linkUrl)) {
    //     showToast('Please enter a valid URL.', 'Error');
    //     return;
    // }


    const urlPattern = /^(https?:\/\/|www\.)[\da-z\.-]+\.[a-z]{2,6}\/?$/;
    if (!urlPattern.test(linkUrl)) {
        showToast('Please enter a valid URL.', 'Error');
        return;
    }

    // Mock API request (POST request)
    const requestData = {
        label: linkTitle,
        url: linkUrl
    };

    app.popup.close()

    app.preloader.show()

    const response = await addUserProfileLinks({
        type: 'external_links',
        link: requestData
    })

    if (response && response.success) {
        showToast('Link added successfully', 'Success');

        // Add the new link to the list
        const listItem = document.createElement('li');
        listItem.innerHTML = `
            <a class="item-link item-content" href="${linkUrl}" data-link-id="${response.id}">
                <div class="item-inner">
                    <div class="item-title">
                        ${linkTitle}
                    </div>
                    <div class="item-after"><i class="icon f7-icons delete-external-link">xmark_circle</i></div>
                </div>
            </a>
        `;

        const externalLinksContainer = $('.social-other-links ul')[0];
        externalLinksContainer.appendChild(listItem);
        store.dispatch('updateUserDetails')
    }

    app.preloader.hide()
});

// Save social links
$(document).on('click', '#save-profile-socials', async function () {
    var view = app.views.current

    const user = await getSessionUser()

    let instagram = $('input[name="social_instagram"]').val();
    let facebook = $('input[name="social_facebook"]').val();
    let tiktok = $('input[name="social_tiktok"]').val();
    let youtube = $('input[name="social_youtube"]').val();
    let mivia = $('input[name="social_mivia"]').val();
    let custodian = $('input[name="social_custodian"]').val();

    const links = {
        instagram,
        facebook,
        tiktok,
        youtube,
        mivia,
        custodian
    };

    // Define the allowed username pattern (alphanumeric, _, -, .)
    const usernamePattern = /^[a-zA-Z0-9._-]+$/;

    // Define a function to strip '@' from the start of a username
    function cleanUsername(username) {
        return username.startsWith('@') ? username.substring(1) : username;
    }

    // Clean and validate Instagram username
    if (instagram) {
        instagram = cleanUsername(instagram);
        links.instagram = instagram;

        if (!usernamePattern.test(instagram)) {
            showToast('Please enter a valid Instagram username (letters, numbers, underscores, periods, hyphens only)', 'Error');
            return;
        }
    }

    // Clean and validate TikTok username
    if (tiktok) {
        tiktok = cleanUsername(tiktok);
        links.tiktok = tiktok;

        if (!usernamePattern.test(tiktok)) {
            showToast('Please enter a valid TikTok username (letters, numbers, underscores, periods, hyphens only)', 'Error');
            return;
        }
    }

    // Clean and validate YouTube username
    if (youtube) {
        youtube = cleanUsername(youtube);
        links.youtube = youtube;

        if (!usernamePattern.test(youtube)) {
            showToast('Please enter a valid YouTube username (letters, numbers, underscores, periods, hyphens only)', 'Error');
            return;
        }
    }

    // Clean and validate Mivia username
    if (mivia) {
        mivia = cleanUsername(mivia);
        links.mivia = mivia;

        if (!usernamePattern.test(mivia)) {
            showToast('Please enter a valid Mivia username (letters, numbers, underscores, periods, hyphens only)', 'Error');
            return;
        }
    }

    // Allow URL with "https://" for Custodian and validate it
    const urlPattern = /^https:\/\/[\da-z\.-]+\.[a-z]{2,6}\/?$/;
    // Allow any URL that starts with "https://"
    if (custodian && !custodian.startsWith('https://')) {
        showToast('Please enter a valid Custodian URL that starts with https://', 'Error');
        return;
    }

    // Clean and validate Facebook username
    if (facebook) {
        facebook = cleanUsername(facebook);
        links.facebook = facebook;

        if (!usernamePattern.test(facebook)) {
            showToast('Please enter a valid Facebook username (letters, numbers, underscores, periods, hyphens only)', 'Error');
            return;
        }
    }


    // check if the request data is the same as userdata
    let dirtied = false;

    for (const key in links) {
        if (links[key] !== user.profile_links[key]) {
            dirtied = true
            break;
        }
    }

    if (!dirtied) {
        return
    }

    app.preloader.show()

    const response = await updateSocialLinks(links);

    app.preloader.hide()

    if (response && response.success) {
        showToast('Social links updated successfully', 'Success');
        view.router.navigate('/profile/')
        store.dispatch('updateUserDetails')
    } else {
        showToast('Failed to update social links', 'Error');
    }
})

// Delete link
$(document).on('click', '.delete-external-link', async function (e) {
    const linkId = e.target.closest('.item-link').dataset.linkId;

    // confirm dialog
    app.dialog.confirm('Are you sure you want to delete this link?', 'Delete Link', async function () {
        app.preloader.show()

        const response = await removeProfileLink(linkId);

        app.preloader.hide()

        if (response) {
            showToast('Link deleted successfully', 'Success')
            // remove the link from the list
            e.target.closest('.item-link').remove();
            store.dispatch('updateUserDetails')

        } else {
            showToast('Failed to delete link', 'Error')
        }
    })
})
// --------------- End Edit Socials Page ---------------

$(document).on('input', '#lowercaseInput', function (event) {
    this.value = this.value.toLowerCase();
});

// --------------- Delete Profile Page ---------------
// $(document).on('page:init', '.page[data-name="profile-edit-account-settings"]', function (e) {
// });

$(document).on('click', '.account-delete', function (e) {
    app.dialog.confirm('Are you sure? It is not possible to restore accounts once deleted.', 'Confirm', function (name) {
        app.dialog.passwordConfirm('Please enter your password.', 'Delete Account', async function (password) {
            await handleDeleteAccount(password);
        });
    });
});

async function handleDeleteAccount(password) {

    try {
        app.preloader.show();

        const response = await deleteUserAccount(password);

        app.preloader.hide();
        if (!response) {
            app.dialog.alert('Failed to delete account', 'Error');
            return;
        }

        if (response && response.success == false) {
            app.dialog.alert(response.message, 'Error');
            return;
        }

        if (response && response.success) {
            app.dialog.alert('Account deleted successfully', 'Success');

            setTimeout(() => {
                store.dispatch('logout');
            }, 1000);
        }
    } catch (error) {
        app.preloader.hide();
        app.dialog.alert(error.message || 'Failed to delete account', 'Error');
    }
}

// Custom dialog to ask for password
app.dialog.passwordConfirm = function (text, title, callback) {
    app.dialog.create({
        title: title,
        text: text,
        content: '<div class="dialog-input-field item-input"><div class="item-input-wrap"><input type="password" name="password" placeholder="Enter your password" class="dialog-input"></div></div>',
        buttons: [{
            text: 'Cancel',
            onClick: function (dialog, e) {
                dialog.close();
            }
        },
        {
            text: 'Delete',
            bold: true,
            onClick: function (dialog, e) {
                var password = dialog.$el.find('.dialog-input').val(); // Get the password entered
                if (!password) {
                    showToast('Please enter your password', 'Error');
                    return;
                }
                callback(password); // Pass the password to the callback
                dialog.close();
            }
        }
        ]
    }).open();
};

/js/profile.js
import store from "./store.js"
import app, {
  showToast
} from "./app.js"
import {
  addVehicleToGarage,
  deleteVehicleFromGarage,
  getGargeById,
  updateVehicleInGarage
} from "./api/garage.js"
import {
  getSessionUser
} from "./api/auth.js"
import {
  sendRNMessage
} from "./api/consts.js"
var $ = Dom7

var garageStore = store.getters.myGarage
var myPostsStore = store.getters.myPosts
var myTagsStore = store.getters.myTags
var pathStore = store.getters.getPathData
var userStore = store.getters.user
var refreshed = false;

var isFetchingPosts = false
var totalPostPages = 1
var totalFPostPages = 1

var currentPostPage = 1
var currentFPostPage = 1

// Garage posts
var totalGaragePostPages = 1
var currentGaragePostPage = 1

// Garage tags
var totalGarageTagPages = 1
var currentGarageTagPage = 1

export function displayProfile(user, container = 'profile') {
  if (!user) {
    console.error('User object not provided');
    return;
  }

  // Select the container element
  const containerElem = document.querySelector(`.page[data-name="${container}"]`);
  if (!containerElem) {
    console.error(`Container element with data-name="${container}" not found.`);
    return;
  }

  // Profile Head
  const usernameElem = containerElem.querySelector('.profile-head .profile-username');
  const nameElem = containerElem.querySelector('.profile-head .profile-name');
  if (usernameElem) usernameElem.textContent = `@${user.username}`;
  if (nameElem) nameElem.textContent = `${user.first_name} ${user.last_name}`;

  // Profile Image
  const profileImageElem = containerElem.querySelector('.profile-head .profile-image');
  if (profileImageElem) {
    profileImageElem.style.backgroundImage = `url('${user.profile_image || 'assets/img/profile-placeholder.jpg'}')`;
  }

  // Cover Image
  if (user.cover_image) {
    const profileBackgroundElem = containerElem.querySelector('.profile-background');
    if (profileBackgroundElem) {
      profileBackgroundElem.style.backgroundImage = `url('${user.cover_image}')`;
    }
  }

  // Profile Links
  const profileLinks = user.profile_links || {};

  const setLinkHref = (selector, url) => {
    const linkElem = containerElem.querySelector(selector);
    if (linkElem) {
      linkElem.setAttribute('href', url);
      linkElem.onclick = (e) => {
        e.preventDefault();
        window.open(url, '_blank');
      }

      // Enable the link
      linkElem.style.opacity = 1;
    }
  };

  if (profileLinks.instagram) {
    setLinkHref('#instagram', `https://www.instagram.com/${profileLinks.instagram}`);
  } else {
    // set opacity to 0.5
    const instagramElem = containerElem.querySelector('#instagram');
    if (instagramElem) {
      instagramElem.style.opacity = 0.2;
      // disable the link
      instagramElem.onclick = (e) => e.preventDefault();
    }
  }

  if (profileLinks.facebook) {
    setLinkHref('#facebook', `https://www.facebook.com/${profileLinks.facebook}`);
  } else {
    // set opacity to 0.5
    const facebookElem = containerElem.querySelector('#facebook');
    if (facebookElem) {
      facebookElem.style.opacity = 0.2;
      // disable the link
      facebookElem.onclick = (e) => e.preventDefault();
    }
  }

  if (profileLinks.tiktok) {
    setLinkHref('#tiktok', `https://www.tiktok.com/@${profileLinks.tiktok}`);
  } else {
    // set opacity to 0.5
    const tiktokElem = containerElem.querySelector('#tiktok');
    if (tiktokElem) {
      tiktokElem.style.opacity = 0.2;
      // disable the link
      tiktokElem.onclick = (e) => e.preventDefault();
    }
  }

  if (profileLinks.youtube) {
    setLinkHref('#youtube', `https://www.youtube.com/@${profileLinks.youtube}`);
  } else {
    // set opacity to 0.5
    const youtubeElem = containerElem.querySelector('#youtube');
    if (youtubeElem) {
      youtubeElem.style.opacity = 0.2;
      // disable the link
      youtubeElem.onclick = (e) => e.preventDefault();
    }
  }

  // Display External Links
  const externalLinks = profileLinks.external_links || []; // Assuming this is an array
  const linksList = containerElem.querySelector('.profile-external-links ul');
  if (linksList) {
    linksList.innerHTML = ''; // Clear any existing links

    if (externalLinks.length > 0) {
      externalLinks.forEach(linkObj => {
        const listItem = document.createElement('li');
        const link = document.createElement('a');
        link.href = linkObj.link.url;

        link.target = '_blank';
        link.textContent = linkObj.link.label;
        listItem.appendChild(link);
        linksList.appendChild(listItem);
      });
    } else {
      // Optionally handle the case where there are no external links
      const noLinksItem = document.createElement('li');
      noLinksItem.textContent = 'No external links available';
      linksList.appendChild(noLinksItem);
    }
  }
}

$(document).on('click', '.profile-external-links ul li a', function (e) {
  e.preventDefault()
  const url = new URL(e.target.href)
  window.open(url, '_blank')
})

export function displayGarage(garage) {
  if (!garage) return

  const garageContainer = document.getElementById('profile-garage') // Make sure you have a container with this ID
  garageContainer.innerHTML = createGarageContent(garage)
}

export function createGarageContent(garages, currentList, pastList) {
  // Elements for current and past vehicles
  const currentVehiclesList = document.querySelector(currentList)
  const pastVehiclesList = document.querySelector(pastList)

  if (!currentVehiclesList || !pastVehiclesList) {
    console.log('Invalid elements provided for current and past vehicles');
    return
  }

  currentVehiclesList.innerHTML = '' // Clear the list before adding new vehicles
  pastVehiclesList.innerHTML = '' // Clear the list before adding new vehicles

  if (garages.error) {
    currentVehiclesList.innerHTML = '<li>No current vehicles</li>'
    pastVehiclesList.innerHTML = '<li>No past vehicles</li>'
    return
  }

  // Function to generate vehicle HTML
  function generateVehicleHTML(vehicle) {
    return `
    <li>
        <a href="/profile-garage-vehicle-view/${vehicle.id}" class="item">
            <div class="imageWrapper">
                <div class="image-square image-rounded"
                    style="background-image:url('${vehicle.cover_photo || 'assets/img/placeholder1.jpg'}');">
                    </div>
            </div>
            <div class="in">
                <div>
                    ${vehicle.make} ${vehicle.model}
                </div>
            </div>
        </a>
    </li>
    `
  }

  // Sort vehicles into current and past vehicles
  garages.forEach(vehicle => {
    if (vehicle.owned_until === "" || vehicle.owned_until.toLowerCase() === "present") {
      currentVehiclesList.innerHTML += generateVehicleHTML(vehicle)
    } else {
      pastVehiclesList.innerHTML += generateVehicleHTML(vehicle)
    }
  })

  if (currentVehiclesList.innerHTML === '') {
    currentVehiclesList.innerHTML = '<li>No current vehicles</li>'
  }

  if (pastVehiclesList.innerHTML === '') {
    pastVehiclesList.innerHTML = '<li>No past vehicles</li>'
  }
}

function generatePostGridItem(post) {
  if (!post.media || post.media.length === 0) return ''

  const media = post.media[0] // Get the first media item
  const isVideo = media.media_type === "video" || media.media_url.includes('.mp4')

  if (isVideo) {
    return `
      <a href="/post-view/${post.id}" class="grid-item" data-src="${media.media_url}/thumbnails/thumbnail.jpg">
        <img 
          src="${media.media_url}/thumbnails/thumbnail.jpg"
          loading="lazy"
          role="presentation"
          sizes="(max-width: 320px) 280px, 320px"
          decoding="async"
          fetchPriority="high"
          style="object-fit: cover; max-width: 130px;"
        />
      </a>`
  } else {
    return `
      <a href="/post-view/${post.id}" class="grid-item image-square" data-src="${media.media_url}">
        <img 
          src="${media.media_url}"
          loading="lazy"
          role="presentation"
          sizes="(max-width: 320px) 280px, 320px"
          decoding="async"
          fetchPriority="high"
          style="object-fit: cover; max-width: 130px;"
        />
      </a>`
  }
}

// Calculate the number of posts and decide if we need to add empty items
export function fillGridWithPosts(posts, profileGridID, reset = false) {
  // Select the container where the posts will be displayed
  const profileGrid = document.getElementById(profileGridID)

  if (reset) {
    profileGrid.innerHTML = '' // Clear the grid before adding new posts
  }

  posts.forEach(post => {
    profileGrid.innerHTML += generatePostGridItem(post)
  })
}

userStore.onUpdated((user) => {
  displayProfile(user)
})

garageStore.onUpdated((garage) => {
  // clear path data
  store.dispatch('clearPathData')
  createGarageContent(garage, '.current-vehicles-list', '.past-vehicles-list')
})

myPostsStore.onUpdated((data) => {
  if (data && data.new_data) {
    const posts = data.new_data
    totalPostPages = data.total_pages

    if ((data.page === data.total_pages) || (data.total_pages == 0)) {
      // hide preloader
      $('.infinite-scroll-preloader.posts-tab').hide()
    }

    if (data.data.length === 0) {
      const profileGrid = document.getElementById('profile-grid-posts')
      profileGrid.innerHTML = '<p></p><p>No posts</p>'
      return;
    }

    // Call the function to fill the grid
    fillGridWithPosts(posts, 'profile-grid-posts', data.cleared || false)
  }
})

myTagsStore.onUpdated((data) => {
  if (data && data.new_data) {
    const posts = data.new_data
    totalFPostPages = data.total_pages

    if ((data.page === data.total_pages) || (data.total_pages == 0)) {
      // hide preloader
      $('.infinite-scroll-preloader.tags-tab').hide()
    }

    if (data.data.length === 0) {
      const profileGrid = document.getElementById('profile-grid-tags')
      profileGrid.innerHTML = '<p></p><p>No tagged posts</p>'
      return;
    }

    // Call the function to fill the grid
    fillGridWithPosts(posts, 'profile-grid-tags', data.cleared || false)
  }
})

$(document).on('page:init', '.page[data-name="profile-garage-vehicle-add"]', function (e) {
  app.calendar.create({
    inputEl: '#owned-from',
    openIn: 'customModal',
    header: true,
    footer: true,
    dateFormat: 'dd/mm/yyyy',
    maxDate: new Date()
  })

  app.calendar.create({
    inputEl: '#owned-to',
    openIn: 'customModal',
    header: true,
    footer: true,
    dateFormat: 'dd/mm/yyyy',
    // minDate: new Date()
  })
})

$(document).on('page:init', '.page[data-name="profile-garage-vehicle-edit"]', function (e) {
  app.calendar.create({
    inputEl: '#owned-from',
    openIn: 'customModal',
    header: true,
    footer: true,
    dateFormat: 'dd/mm/yyyy',
    maxDate: new Date()
  })

  app.calendar.create({
    inputEl: '#owned-to',
    openIn: 'customModal',
    header: true,
    footer: true,
    dateFormat: 'dd/mm/yyyy',
    // minDate: new Date()
  })
})

$(document).on('infinite', '.profile-landing-page.infinite-scroll-content', async function (e) {
  refreshed = false

  if (isFetchingPosts) return

  const activeTab = document.querySelector('.profile-tabs .tab-link-active')
  const activeTabId = activeTab.id

  if (!activeTabId || activeTabId === 'my-garage') return

  const getterFunc = activeTabId === 'my-posts' ? 'getMyPosts' : 'getMyTags'

  isFetchingPosts = true

  if (activeTabId === 'my-posts') {
    currentPostPage++

    if (currentPostPage <= totalPostPages) {
      await store.dispatch(getterFunc, {
        page: currentPostPage,
        clear: false
      })
      isFetchingPosts = false
    }
  } else {
    currentFPostPage++

    if (currentFPostPage <= totalFPostPages) {
      await store.dispatch(getterFunc, {
        page: currentPostPage,
        clear: false
      })
      isFetchingPosts = false
    }
  }
})

$(document).on('ptr:refresh', '.profile-landing-page.ptr-content.my-profile', async function (e) {
  refreshed = true

  try {
    store.dispatch('clearPathData')

    await store.dispatch('updateUserDetails')
    await store.dispatch('getMyGarage')
    await store.dispatch('getMyPosts', {
      page: 1,
      clear: true
    })
    await store.dispatch('getMyTags', {
      page: 1,
      clear: true
    })
  } catch (error) {
    console.log(error);
  }
  app.ptr.get('.profile-landing-page.ptr-content.my-profile').done()
})

$(document).on('page:beforein', '.page[data-name="profile"]', async function (e) {
  const user = await getSessionUser()

  if (user && user.id) {
    const isEmailVerified = user.email_verified ?? false;

    if (!isEmailVerified) {
      const profileHead = $('.page[data-name="profile"] .profile-head')

      if (profileHead.length) {
        // Add email verification message before the element
        $(`
          <div class="email-verification-message">
            <p>Your email is not verified. Please verify your email address to access all features.</p>
          </div>
        `).insertBefore(profileHead);

        profileHead.addClass('email-not-verified');
      } else {
        console.log('Profile head element not found.');
      }
    }
  }

  app.popup.create({
    el: '.links-popup',
    swipeToClose: 'to-bottom'
  })
})

// ------- Garage Views -------
$(document).on('page:init', '.page[data-name="profile-garage-vehicle-view"]', async function (e) {
  var garageId = e.detail.route.params.id

  if (!garageId) {
    app.dialog.alert('Garage not found')
    app.views.main.router.back()
    return
  }

  if (garageId == -1) {
    return;
  }

  let cachedData = null
  try {
    if (pathStore && pathStore.value[`/garage/${garageId}`]) {
      cachedData = pathStore.value[`/garage/${garageId}`]
    }
  } catch (error) {
    console.error('Error fetching cached data:', error)
  }

  if (cachedData) {
    $('.loading-fullscreen.garage').hide()
    store.dispatch('setGarageViewPosts', garageId, 1)
    store.dispatch('setGarageViewTags', garageId, 1)
    updateProfilePage(cachedData)
    return
  }

  $('.loading-fullscreen.garage').show()

  const garage = await getGargeById(garageId)
  if (!garage) {
    $('.loading-fullscreen').hide()

    app.dialog.alert('Garage not found')
    app.views.main.router.back()
    return
  }

  $('.loading-fullscreen.garage').hide()

  // Assuming `path` is a dynamic path like '/garage/2'
  store.dispatch('setPathData', {
    path: `/garage/${garageId}`,
    data: garage,
  })

  // Call the function to update the page
  updateProfilePage(garage)
  store.dispatch('setGarageViewPosts', garageId, 1)
  store.dispatch('setGarageViewTags', garageId, 1)
})

store.getters.getGarageViewPosts.onUpdated((data) => {
  if (data && data.data) {
    const posts = data.data

    totalGaragePostPages = data.total_pages

    // if there is only one page of posts or no posts
    if ((data.page == data.total_pages) || (data.total_pages == 0)) {
      // hide preloader
      $('.infinite-scroll-preloader.garage-posts-tab').hide()
    }

    if (data.data.length === 0) {
      const profileGrid = document.getElementById('garage-posts-tab')
      profileGrid.innerHTML = '<p>No posts yet</p>'
      return;
    }


    // Call the function to fill the grid
    fillGridWithPosts(posts, 'garage-posts-tab')
  }
})

store.getters.getGarageViewTags.onUpdated((data) => {
  if (data && data.data) {
    const posts = data.data
    totalGarageTagPages = data.total_pages

    // if there is only one page of posts or no posts
    if ((data.page == data.total_pages) || (data.total_pages == 0)) {
      // hide preloader
      $('.infinite-scroll-preloader.garage-tags-tab').hide()
    }

    if (data.data.length === 0) {
      const profileGrid = document.getElementById('garage-tags-tab')
      profileGrid.innerHTML = '<p>No tagged posts yet</p>'
      return;
    }

    // Call the function to fill the grid
    fillGridWithPosts(posts, 'garage-tags-tab')
  }
})

// Function to update the HTML with the data
async function updateProfilePage(data) {
  const user = await getSessionUser()

  // Update the cover photo
  const coverPhotoElement = document.querySelector('.vehicle-profile-background')
  if (coverPhotoElement) {
    coverPhotoElement.style.backgroundImage = `url('${data.cover_photo}')`
  }

  // Update the profile image
  const profileImageElement = document.querySelector('.vehicle-profile-image')
  if (profileImageElement) {
    profileImageElement.style.backgroundImage = `url('${data.owner.profile_image || 'assets/img/profile-placeholder.jpg'}')`

    let profile_link = `/profile-view/${data.owner_id}`

    if (user.id == data.owner_id) {
      profile_link = '/profile/'
      // add class view-profile
      profileImageElement.classList.add('view-profile')
    }

    profileImageElement.setAttribute('href', profile_link)
  }
  // Update the vehicle make and model
  const vehicleTitleElement = document.querySelector('.profile-garage-intro h1')
  if (vehicleTitleElement) {
    vehicleTitleElement.textContent = `${data.make} ${data.model}`
  }


  const profileLinks = $('.profile-links-edit.garage')
  if (profileLinks) {
    const editLink = `<a class="profile-link" href="/profile-garage-vehicle-edit/${data.id}">Edit Vehicle</a>`
    const user = await getSessionUser()

    if (data.owner_id == user.id) {
      profileLinks.prepend(editLink)
    }

    // $('.garage-add-post').attr('data-garage-id', data.id)

    $(document).on('click', '.garage-add-post', async function (e) {
      return;
      const garageId = $(this).attr('data-garage-id')

      if (!garageId) {
        app.dialog.alert('Garage not found')
        return
      }

      const user = await getSessionUser()
      if (user) {
        sendRNMessage({
          type: "createPost",
          user_id: user.id,
          page: 'profile-garage-post',
          association_id: garageId,
          association_type: 'garage',
        })
      }
    })
  }

  // Update the ownership information
  const ownershipInfoElement = document.querySelector('.garage-owned-information')
  if (ownershipInfoElement) {
    const ownedUntilText = data.owned_until ? ` - ${data.owned_until}` : ' - Present'
    ownershipInfoElement.textContent = `Owned from ${data.owned_since}${ownedUntilText}`
  }

  // Update the vehicle description
  const vehicleDescriptionElement = document.querySelector('.garage-vehicle-description')
  if (vehicleDescriptionElement) {
    vehicleDescriptionElement.textContent = data.short_description
  }
}

$(document).on('page:init', '.page[data-name="profile-garage-edit"]', async function (e) {
  const garage = garageStore.value
  createGarageContent(garage, '#garage-edit-current-list', '#garage-edit-past-list')
})

$(document).on('page:init', '.page[data-name="profile-garage-vehicle-edit"]', async function (e) {
  var garageId = e.detail.route.params.id
  var view = app.views.current

  if (!garageId) {
    app.dialog.alert('Garage not found')
    view.router.back(view.history[0], {
      force: true
    })
    return
  }

  let data = null
  try {
    if (pathStore && pathStore.value[`/garage/${garageId}`]) {
      data = pathStore.value[`/garage/${garageId}`]
    }
  } catch (error) {
    console.error('Error fetching cached data:', error)
  }

  if (!data) {
    $('.loading-fullscreen').show()
    const garage = await getGargeById(garageId)

    if (!garage) {
      app.dialog.alert('Garage not found')
      view.router.back(view.history[0], {
        force: true
      })
      return
    }

    data = garage
    // Assuming `path` is a dynamic path like '/garage/2'
    store.dispatch('setPathData', {
      path: `/garage/${garageId}`,
      data: data,
    })
  }

  $('.loading-fullscreen').hide()

  // check if user is the owner of the garage
  const user = await getSessionUser()
  if (data.owner_id != user.id) {
    app.dialog.alert('You are not authorized to edit this vehicle')
    view.router.back(view.history[0], {
      force: true
    })
    return
  }

  document.querySelector('input[name="garage_id"]').value = garageId

  // Populate form fields with garage data
  document.querySelector('select[name="vehicle_make"]').value = data.make
  document.querySelector('input[name="vehicle_model"]').value = data.model
  document.querySelector('input[name="vehicle_variant"]').value = data.variant
  document.querySelector('input[name="vehicle_reg"]').value = data.registration
  document.querySelector('input[name="vehicle_colour"]').value = data.colour
  document.querySelector('input[name="vehicle_owned_from"]').value = data.owned_since
  document.querySelector('input[name="vehicle_owned_to"]').value = data.owned_until || ''
  document.querySelector('input[name="vehicle_tagging"]').checked = data.allow_tagging === "1"
  document.querySelector('textarea[name="vehicle_description"]').value = data.short_description || ''

  // If a cover photo exists, use it as the background image of the upload label
  if (data.cover_photo) {
    document.querySelector('.custom-file-upload label').style.backgroundImage = `url('${data.cover_photo}')`
    document.querySelector('.custom-file-upload label').style.backgroundSize = 'cover'
  }

  // Set vehicle ownership and toggle the "Owned To" date picker
  const ownershipSelect = document.querySelector('select[name="vehicle_ownership"]')

  const toggleOwnedToDatePicker = () => {
    const ownedToInput = document.querySelector('input[name="vehicle_owned_to"]')
    const ownedToBContainer = document.querySelector('#owned-to-block')
    if (ownershipSelect.value === "current") { // Current Vehicle
      ownedToBContainer.style.display = 'none'
      ownedToInput.value = ''
    } else {
      ownedToBContainer.style.display = 'block'
    }
  }

  // Initially set the visibility based on the garage data
  const isPrimary = data.primary_car === "1" ? true : false;
  const hasOwndedTo = data.owned_until && data.owned_until.length > 1 ? true : false;
  ownershipSelect.value = hasOwndedTo ? "past" : "current"
  toggleOwnedToDatePicker()

  // Attach event listener to toggle visibility when ownership type changes
  ownershipSelect.addEventListener('change', toggleOwnedToDatePicker)

  // input vehicle_image
  $(document).on('change', 'input#fileuploadInput', function (e) {
    const file = e.target.files[0]
    const reader = new FileReader()

    reader.onload = function (e) {
      document.querySelector('.custom-file-upload label').style.backgroundImage = `url('${e.target.result}')`
      document.querySelector('.custom-file-upload label').style.backgroundSize = 'cover'
    }

    reader.readAsDataURL(file)
  })

  // #delete-vehicle on click
  $(document).on('click', '#delete-vehicle', async function (e) {
    app.dialog.confirm('Are you sure you want to delete this vehicle?', async function () {
      try {
        app.preloader.show()

        const response = await deleteVehicleFromGarage(garageId)

        if (!response || !response.success) {
          throw new Error('Failed to delete vehicle')
        }

        app.preloader.hide()

        showToast('Vehicle deleted successfully')

        await store.dispatch('getMyGarage')
        view.router.back('/profile-garage-edit/', {
          force: true
        })

      } catch (error) {
        console.log(error);
        app.preloader.hide()

        app.notification.create({
          titleRightText: 'now',
          subtitle: 'Oops, something went wrong',
          text: error.message || 'Failed to delete vehicle',
        }).open()
      }
    })
  })
})

function parseDate(dateString) {
  const parts = dateString.split('/');
  return new Date(parts[2], parts[1] - 1, parts[0]); // YYYY, MM, DD
}

// submit-vehicle-form
$(document).on('click', '#submit-vehicle-form', async function (e) {
  var view = app.views.current

  // form data
  const form = $('form#vehicleForm')

  // values
  const garageId = form.find('input[name="garage_id"]').val()

  const make = form.find('select[name="vehicle_make"]').val()
  const model = form.find('input[name="vehicle_model"]').val()
  const variant = form.find('input[name="vehicle_variant"]').val()
  const reg = form.find('input[name="vehicle_reg"]').val()
  const colour = form.find('input[name="vehicle_colour"]').val()
  const description = form.find('textarea[name="vehicle_description"]').val()

  const owned_from = form.find('input[name="vehicle_owned_from"]').val()
  const owned_to = form.find('input[name="vehicle_owned_to"]').val()

  const primary_car = form.find('select[name="vehicle_ownership"]').val()
  const allow_tagging = form.find('input[name="vehicle_tagging"]').is(':checked') ? 1 : 0

  const cover_image = form.find('input[name="vehicle_image"]').prop('files')[0]

  if (!make || make === "0") {
    showToast('Please select a vehicle make')
    return
  }

  if (!model) {
    showToast('Please enter a vehicle model')
    return
  }

  // if (!owned_from) {
  //   showToast('Please enter the date you owned the vehicle from')
  //   return
  // }

  // // if primary_car is past, owned_to is required
  // if (primary_car === "past" && !owned_to) {
  //   showToast('Please enter the date you owned the vehicle to')
  //   return
  // }

  if (owned_to && owned_from) {
    const ownedFromDate = parseDate(owned_from.trim());
    const ownedToDate = parseDate(owned_to.trim());

    if (isNaN(ownedFromDate) || isNaN(ownedToDate)) {
      showToast('One or both of the dates are invalid.');
      return;
    }

    if (ownedToDate < ownedFromDate) {
      showToast('Owned to date cannot be less than owned from date');
      return;
    }
  }

  let base64 = null

  if (cover_image) {
    // Wrap the FileReader in a Promise to wait for it to complete
    base64 = await new Promise((resolve, reject) => {
      const reader = new FileReader()
      reader.readAsDataURL(cover_image)

      reader.onload = () => resolve(reader.result)
      reader.onerror = () => reject(new Error('Failed to read image as base64'))
    })
  }

  try {
    app.preloader.show()

    const response = await updateVehicleInGarage({
      make,
      model,
      variant,
      registration: reg,
      colour,
      ownedFrom: owned_from,
      ownedTo: owned_to,
      primary_car,
      allow_tagging,
      cover_photo: base64,
      vehicle_period: primary_car,
      description
    },
      garageId
    )

    if (!response || !response.success) {
      throw new Error('Failed to update vehicle')
    }

    app.preloader.hide()
    showToast('Vehicle updated successfully')

    // refresh garage
    await store.dispatch('getMyGarage')

    view.router.back(view.history[0], {
      force: true
    })
  } catch (error) {
    app.preloader.hide()
    app.notification.create({
      titleRightText: 'now',
      subtitle: 'Oops, something went wrong',
      text: error.message || 'Failed to update vehicle',
    }).open()
  }
})

$(document).on('page:init', '.page[data-name="profile-garage-vehicle-add"]', async function (e) {
  const toggleOwnedToDatePicker = (e) => {
    const ownedToInput = document.querySelector('input[name="vehicle_owned_to"]')
    const ownedToBContainer = document.querySelector('#owned-to-block')

    const value = e.target.value

    if (value === "current") { // Current Vehicle
      ownedToBContainer.style.display = 'none'
      ownedToInput.value = ''
    } else {
      ownedToBContainer.style.display = 'block'
    }
  }

  $(document).on('change', 'select[name="vehicle_ownership"]', toggleOwnedToDatePicker)

  // input vehicle_image
  $(document).on('change', 'input[name="vehicle_image"]', function (e) {
    const file = e.target.files[0]
    const reader = new FileReader()

    reader.onload = function (e) {
      document.querySelector('.custom-file-upload label').style.backgroundImage = `url('${e.target.result}')`
      document.querySelector('.custom-file-upload label').style.backgroundSize = 'cover'
    }

    reader.readAsDataURL(file)
  })
})

$(document).on('click', '#submit-add-vehicle-form', async function (e) {
  var view = app.views.current

  const form = $('form#addVehicleForm')

  // values
  const make = form.find('select[name="vehicle_make"]').val()
  const model = form.find('input[name="vehicle_model"]').val()
  const variant = form.find('input[name="vehicle_variant"]').val()
  const reg = form.find('input[name="vehicle_reg"]').val()
  const colour = form.find('input[name="vehicle_colour"]').val()
  const description = form.find('textarea[name="vehicle_description"]').val()

  const owned_from = form.find('input[name="vehicle_owned_from"]').val()
  const owned_to = form.find('input[name="vehicle_owned_to"]').val()

  const primary_car = form.find('select[name="vehicle_ownership"]').val()
  const allow_tagging = form.find('input[name="vehicle_tagging"]').is(':checked') ? 1 : 0

  const cover_image = form.find('input[name="vehicle_image"]').prop('files')[0]

  if (!make || make === "0") {
    showToast('Please select a vehicle make')
    return
  }

  if (!model) {
    showToast('Please enter a vehicle model')
    return
  }

  // if (!reg) {
  //   app.dialog.alert('Please enter a vehicle registration number')
  //   return
  // }

  // if (!owned_from) {
  //   app.dialog.alert('Please enter the date you owned the vehicle from')
  //   return
  // }

  if (owned_to && owned_from) {
    const ownedFromDate = parseDate(owned_from.trim());
    const ownedToDate = parseDate(owned_to.trim());

    if (isNaN(ownedFromDate) || isNaN(ownedToDate)) {
      showToast('One or both of the dates are invalid.');
      return;
    }

    if (ownedToDate < ownedFromDate) {
      showToast('Owned to date cannot be less than owned from date');
      return;
    }
  }

  // if primary_car is past, owned_to is required
  if (primary_car === "past" && !owned_to) {
    showToast('Please enter the date you owned the vehicle to');

    return
  }

  if (!cover_image) {
    showToast('Please upload a cover image')
    return
  }

  let base64 = null

  if (cover_image) {
    // Wrap the FileReader in a Promise to wait for it to complete
    base64 = await new Promise((resolve, reject) => {
      const reader = new FileReader()
      reader.readAsDataURL(cover_image)

      reader.onload = () => resolve(reader.result)
      reader.onerror = () => reject(new Error('Failed to read image as base64'))
    })
  }

  try {
    app.preloader.show()

    const response = await addVehicleToGarage({
      make,
      model,
      variant,
      registration: reg,
      colour,
      ownedFrom: owned_from,
      ownedTo: owned_to,
      primary_car,
      allow_tagging,
      cover_photo: base64,
      vehicle_period: primary_car,
      description
    })

    if (!response || !response.success) {
      throw new Error('Failed to update vehicle')
    }

    app.preloader.hide()

    store.dispatch('getMyGarage')
    app.dialog.alert('Vehicle added successfully')

    // redirect to garage
    view.router.back(`/profile-garage-vehicle-view/${response.id}`, {
      force: true
    })
  } catch (error) {
    app.preloader.hide()

    app.notification.create({
      titleRightText: 'now',
      subtitle: 'Oops, something went wrong',
      text: error.message || 'Failed to add vehicle',
    }).open()
  }
})

/js/qr-scanner.js
import {
    verifyScan
} from "./api/scanner.js"
import store from "./store.js"

export const onScanSuccess = async (decodedText) => {
    // https://mydrivelife.com/qr/clpmhHGEXyUD
    // get the qr code and verify it

    if (store.getters.isScanningQrCode.value) {
        return
    }

    store.dispatch('setScanningQrCode', true)

    try {
        const url = new URL(decodedText)

        if (url.hostname === 'mydrivelife.com') {
            const qrCode = url.pathname.split('/').pop()
            if (qrCode) {
                const response = await verifyScan(qrCode)
                store.dispatch('setScannedData', response)
                store.dispatch('setScanningQrCode', false)
            }
        } else {
            store.dispatch('setScannedData', {
                status: 'error',
                message: 'Oops, looks like you scanned an invalid QR code',
                available: false
            })
            store.dispatch('setScanningQrCode', false)
        }
    } catch (error) {
        console.log('error', error)
    }
}

export function onScanFailure(error) {
    // handle scan failure, usually better to ignore and keep scanning.
    // for example:
    // console.warn(`Code scan error = ${error}`)
}

/js/qr.js
import {
    onScanFailure,
    onScanSuccess
} from './qr-scanner.js'
import {
    handleLink,
    handleUnlink
} from './api/scanner.js'
import app from "./app.js"
import store from "./store.js"

var $ = Dom7;

var html5QrCode;
let defaultConfig = {
    qrbox: {
        width: 250,
        height: 250
    },
    fps: 60,
    showTorchButtonIfSupported: true,
    showZoomSliderIfSupported: true,
    // aspectRatio: 1.7777778
}

const renderResult = (result) => {
    const user = store.getters.user.value

    if (!result || result.status === 'error') {
        return `<h2 class="text-center">${result?.message || 'Oops, looks like you scanned an invalid QR code'}</h2>`
    }

    if (result.available) {
        return (
            `<h2 class="text-center">Congrats! This QR code is up for grabs</h2>
        <button id="link-profile">
          Link Profile
        </button>`
        )
    }

    if (!result.available) {
        var view = app.views.current

        if (result.data && result.data.linked_to != user?.id) {
            view.router.navigate(`/profile-view/${result.data.linked_to}`)
            return;
        }

        return (
            `
        <h2 class="text-center">Sorry, this QR code is already linked</h2>
        ${result.data && result.data.linked_to == user?.id ? (
                `<button id="unlink-profile"
          onClick={handleUnlink}
        >
          Unlink Profile
        </button>`
            ) : '  '}
      `
        )
    }
}

// Function to create and open the modal with default content
export function openModal() {
    const myModal = app.dialog.create({
        title: 'Scan QR Code',
        content: `
      <div id="custom-modal-content">
        <div id="reader" width="600px"></div>
      </div>
    `,
        buttons: [{
            text: 'Close',
            onClick: function () {
                try {
                    if (html5QrCode) {
                        html5QrCode.stop()
                    }

                    store.dispatch('setScannedData', null)
                } catch (error) {
                    console.error('Error stopping qr code', error)
                }
            }
        }]
    })

    // Open the modal
    myModal.open()
}

// on link profile
$(document).on('click', '#link-profile', async function () {
    const result = store.getters.scannedData.value

    if (result) {
        const response = await handleLink(result)
        if (response.status === 'error') {
            store.dispatch('setScannedData', {
                status: 'error',
                message: response.text,
                available: false
            })
        }

        app.dialog.close()
        app.dialog.alert(response.message)

        // reset the scanned data
        store.dispatch('setScannedData', null)
    }
})

// unlink profile
$(document).on('click', '#unlink-profile', async function () {
    const result = store.getters.scannedData.value
    // close the modal
    app.dialog.close()

    if (result) {
        const response = await handleUnlink(result)
        if (response.type === 'success') {
            app.dialog.alert(response.text)
        }

        // reset the scanned data
        store.dispatch('setScannedData', null)
    }
})

export function openQRModal() {
    openModal()

    html5QrCode = new Html5Qrcode("reader")

    html5QrCode?.start({
        facingMode: "environment"
    },
        defaultConfig,
        onScanSuccess,
        onScanFailure
    )
}

$(document).on('click', '.open-qr-modal', function () {
    openQRModal()
})

store.getters.scannedData.onUpdated((data) => {
    if (html5QrCode) {
        html5QrCode.stop()
    }

    if (data) {
        const html = renderResult(data);

        if (!html) {
            // close the modal
            app.dialog.close()
            return
        }

        document.getElementById('custom-modal-content').innerHTML = html
    }
})

/js/routes.js
// var v = Date.now()
var v = '1.0.1'

var routes = [{
    path: '/',
    url: './index.html',
    // name: 'home',
  },
  {
    path: '/social/',
    componentUrl: './pages/home.html?' + v,
    keepAlive: true,
  },
  {
    path: '/notifications/',
    componentUrl: './pages/notifications.html?' + v,
    keepAlive: true,
  },
  {
    path: '/auth/',
    url: './pages/auth.html',
    // options: {
    //   animate: false,
    // },
  },
  {
    path: '/signin/',
    url: './pages/login.html',
  },
  {
    path: '/signup-step1/',
    componentUrl: './pages/signup-step1.html?' + v,
  },
  {
    path: '/signup-step2/',
    componentUrl: './pages/signup-step2.html?' + v,
  },
  {
    path: '/signup-step3/',
    componentUrl: './pages/signup-step3.html?' + v,
  },
  {
    path: '/signup-step4/',
    componentUrl: './pages/signup-step4.html?' + v,
  },
  {
    path: '/signup-complete/',
    componentUrl: './pages/signup-complete.html?' + v,
  },
  {
    path: '/login/',
    componentUrl: './pages/login.html?' + v,
  },
  {
    path: '/forgot-password/',
    componentUrl: './pages/forgot-password.html?' + v,
  },
  {
    path: '/discover/',
    componentUrl: './pages/discover.html?' + v,
    keepAlive: true,
  },
  {
    path: '/discover-view-event/:id',
    componentUrl: './pages/discover-view-event.html?' + v,
  }, {
    path: '/discover-view-venue/:id',
    componentUrl: './pages/discover-view-venue.html?' + v,
  },
  {
    path: '/store/',
    componentUrl: './pages/store.html?' + v,
  },
  {
    path: '/profile/',
    componentUrl: './pages/profile.html?' + v,
    keepAlive: true,
  },
  {
    path: '/profile-view/:id',
    componentUrl: './pages/profile-view.html?' + v,

  },
  {
    path: '/profile-garage-vehicle-view/:id',
    componentUrl: './pages/profile-garage-vehicle-view.html?' + v,
  },
  {
    path: '/post-view/:id',
    componentUrl: './pages/post-view.html?' + v,
  },
  {
    path: '/profile-edit/',
    componentUrl: './pages/profile-edit.html?' + v,
  },
  {
    path: '/search/',
    componentUrl: './pages/search.html?' + v,
  },
  {
    path: '/profile-garage-edit/',
    componentUrl: './pages/profile-garage-edit.html?' + v,
  },
  {
    path: '/profile-garage-vehicle-add/',
    componentUrl: './pages/profile-garage-vehicle-add.html?' + v,
  },
  {
    path: '/profile-garage-vehicle-edit/:id',
    componentUrl: './pages/profile-garage-vehicle-edit.html?' + v,
  },
  {
    path: '/profile-edit-images/',
    componentUrl: './pages/profile-edit-images.html?' + v,
  },
  {
    path: '/profile-edit-socials/',
    componentUrl: './pages/profile-edit-socials.html?' + v,
  },
  {
    path: '/profile-edit-mydetails/',
    componentUrl: './pages/profile-edit-mydetails.html?' + v,
  },
  {
    path: '/profile-edit-username/',
    componentUrl: './pages/profile-edit-username.html?' + v,
  },
  {
    path: '/post-edit/:id',
    componentUrl: './pages/post-edit.html?' + v,
  },
  {
    path: '/profile-edit-account-settings/',
    componentUrl: './pages/profile-edit-account-settings.html?' + v,
  },
  // Default route (404 page). MUST BE THE LAST
  {
    path: '(.*)',
    componentUrl: './pages/404.html?' + v,
  },
]

export default routes

/js/search.js
import {
    getDiscoverData
} from "./api/discover.js";
import store from "./store.js";

var $ = Dom7;

let activeTab = 'all';
let lastSearchText = '';
let controller; // To store the current AbortController

var searchResultsStore = store.getters.getSearchResults;

$(document).on('page:afterin', '.page[data-name="search"]', async function (e) {
    $('.loading-fullscreen.search-view').hide()

    // Delay the focus slightly to ensure it's triggered on mobile
    setTimeout(function () {
        $('#discover-search').focus();

        // Scroll to the input to make sure it's in view (this sometimes triggers the keyboard)
        $('#discover-search')[0].scrollIntoView({ behavior: 'smooth', block: 'center' });

        // Re-trigger focus after scrolling
        $('#discover-search').focus();
    }, 300); // Adjust the delay if needed
})

// event listener for tab change
$(document).on('click', '.discovery-wrap .tab-link', async function (e) {
    const type = this.getAttribute('data-type')
    activeTab = type
})

function debounce(func, delay) {
    let timer;
    return function (...args) {
        clearTimeout(timer);
        timer = setTimeout(() => func.apply(this, args), delay);
    };
}

function addLoaderToTabs() {
    const eventsContainer = document.querySelector('#events-results .list ul');
    const usersContainer = document.querySelector('#users-results .list ul');
    const vehiclesContainer = document.querySelector('#vehicles-results .list ul');
    const venuesContainer = document.querySelector('#venues-results .list ul');
    const topContainer = document.querySelector('#top-results .list');

    const loader = `
    <div class="loading-fullscreen search-view">
        <div class="preloader preloader-central">
            <span class="preloader-inner"><span class="preloader-inner-line"></span><span
                class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                class="preloader-inner-line"></span><span class="preloader-inner-line"></span><span
                class="preloader-inner-line"></span>
            </span>
        </div>
    </div>`;

    eventsContainer.innerHTML = loader;
    usersContainer.innerHTML = loader;
    venuesContainer.innerHTML = loader;
    topContainer.innerHTML = loader;
    vehiclesContainer.innerHTML = loader;
    $('.loading-fullscreen.search-view').show()
}

// Function to save searches to localStorage
function saveSearchToHistory(search) {
    const maxHistoryLength = 5; // Limit the number of stored searches
    let searchHistory = JSON.parse(localStorage.getItem('searchHistory')) || [];

    // Check if search is already in the history
    if (!searchHistory.includes(search)) {
        // Add the new search to the beginning of the array
        searchHistory.unshift(search);

        // Keep only the last maxHistoryLength searches
        if (searchHistory.length > maxHistoryLength) {
            searchHistory = searchHistory.slice(0, maxHistoryLength);
        }

        // Save the updated history to localStorage
        localStorage.setItem('searchHistory', JSON.stringify(searchHistory));
    }
}

// Function to display search history
function displaySearchHistory() {
    const searchHistory = JSON.parse(localStorage.getItem('searchHistory')) || [];

    const historyContainer = $('#search-history');
    historyContainer.empty(); // Clear previous history

    if (searchHistory.length > 0) {
        // Populate the history container with recent searches
        searchHistory.forEach(search => {
            historyContainer.append(`
                <li class="search-history-item" data-index="${searchHistory.indexOf(search)}">
                <div>
                    <i class="icon f7-icons">timer</i>
                    <span>${search}</span>
                </div>
                <i class="icon f7-icons delete-history">xmark</i>
                </li>
                `);
        });

        // Show the history container
        historyContainer.show();
    } else {
        // Hide the history container if there's no history
        historyContainer.hide();
    }
}

// Handle clicking on the delete history icon
$(document).on('click', '.delete-history', function (e) {
    e.stopPropagation(); // Prevent the search from being triggered

    let searchHistory = JSON.parse(localStorage.getItem('searchHistory')) || [];
    const index = $(this).closest('li').data('index');

    // Remove the search from the history
    searchHistory.splice(index, 1);
    localStorage.setItem('searchHistory', JSON.stringify(searchHistory));

    // Update the search history display after deletion
    displaySearchHistory();
});

// Hide search history when clicking outside
$(document).on('click', function (e) {
    if (!$(e.target).closest('#discover-search').length && !$(e.target).closest('#search-history').length) {
        $('#search-history').hide();
    }
});

// Handle clicking on a history item to perform the search
$(document).on('click', '#search-history li', function () {
    // Get the search text from the history item span
    const search = $(this).find('span').text();

    // clear the search input
    $('#discover-search').val('');

    $('#discover-search').val(search).trigger('input');
    $('#search-history').hide(); // Hide the history after selecting
});

// Optionally, display history on input focus
// $(document).on('click', '#discover-search', displaySearchHistory);

const debouncedSearch = debounce(async function () {
    const search = $(this).val().trim();

    if (search.length < 3 || search === lastSearchText) {
        return;
    }

    lastSearchText = search;

    // Abort the previous request if it's still ongoing
    if (controller) {
        controller.abort();
    }

    // Create a new AbortController for the current request
    controller = new AbortController();
    const signal = controller.signal;

    addLoaderToTabs();


    // Save the valid search to history
    saveSearchToHistory(search);

    try {
        const searchResults = await getDiscoverData(search, 'all', 1, signal);
        store.dispatch('setSearchResults', searchResults);
    } catch (error) {
        if (error.name === 'AbortError') {
            console.log('Fetch aborted');
        } else {
            console.error('Error fetching search results:', error);
        }
    }
}, 300); // Adjust the delay (in milliseconds) as needed

$(document).on('change input paste', '#discover-search', debouncedSearch);

// Function to render a list of items in the DOM
function renderList(container, items, renderItem) {
    container.innerHTML = '';

    const noResultsMessage = `
    <li class="item-content w-full">
        <strong class="text-center w-full">No results found</strong>
    </li>`;

    if (!items || items.length === 0) {
        container.innerHTML = noResultsMessage;
        return;
    }

    // Render each item
    items.forEach(item => {
        const li = document.createElement('li');
        li.innerHTML = renderItem(item);
        container.appendChild(li);
    });
}

// Function to render the search results
function renderSearchResults(searchResults) {
    const eventsContainer = document.querySelector('#events-results .list ul');
    const usersContainer = document.querySelector('#users-results .list ul');
    const vehiclesContainer = document.querySelector('#vehicles-results .list ul');
    const venuesContainer = document.querySelector('#venues-results .list ul');
    const topContainer = document.querySelector('#top-results .list');

    // Ensure containers are cleared before rendering
    eventsContainer.innerHTML = '';
    usersContainer.innerHTML = '';
    venuesContainer.innerHTML = '';
    topContainer.innerHTML = '';

    // Render events
    if (searchResults.events) {
        renderList(eventsContainer, searchResults.events.data, event => `
            <a class="item-link search-result item-content" href="/discover-view-event/${event.id}">
                <div class="item-media">
                    <div class="image-square image-rounded" style="background-image:url('${event.thumbnail}')"></div>
                </div>
                <div class="item-inner">
                    <div class="item-title">${event.name}</div>
                </div>
            </a>
        `);
    }

    // Render users
    if (searchResults.users) {
        renderList(usersContainer, searchResults.users.data, (user) => {
            const userLink = user.type == 'user' ? '/profile-view/' + user.id : '/profile-garage-vehicle-view/' + user.id;
            let contentText;

            if (user.type == 'user') {
                contentText = `${user.name} (@${user.username})`;
            }

            if (user.type == 'vehicle') {
                contentText = `${user.name}'s <b>${user.meta.make} ${user.meta.model}</b>`;
            }

            return `
                <a class="item-link search-result item-content" href="${userLink}">
                    <div class="item-media">
                        <div class="image-square image-rounded" style="background-image:url('${user.thumbnail || 'assets/img/profile-placeholder.jpg'}')"></div>
                    </div>
                    <div class="item-inner">
                        <div class="item-title">${contentText}</div>
                    </div>
                </a>`
        });
    }

    if (searchResults.vehicles) {
        renderList(vehiclesContainer, searchResults.vehicles.data, (user) => {
            const userLink = user.type == 'post' ? '/post-view/' + user.id : '/profile-garage-vehicle-view/' + user.id;
            let contentText;

            if (user.type == 'post') {
                contentText = `${user.username} tagged ${user.name}`;
            }

            if (user.type == 'vehicle') {
                contentText = `${user.name}'s <b>${user.meta.make} ${user.meta.model}</b>`;
            }

            return `
                <a class="item-link search-result item-content" href="${userLink}">
                    <div class="item-media">
                        <div class="image-square image-rounded" style="background-image:url('${user.thumbnail || 'assets/img/profile-placeholder.jpg'}')"></div>
                    </div>
                    <div class="item-inner">
                        <div class="item-title">${contentText}</div>
                    </div>
                </a>`
        });
    }

    // Render venues
    if (searchResults.venues) {
        renderList(venuesContainer, searchResults.venues.data, venue => `
            <a class="item-link search-result item-content" href="/discover-view-venue/${venue.id}">
                <div class="item-media">
                    <div class="image-square image-rounded" style="background-image:url('${venue.thumbnail}')"></div>
                </div>
                <div class="item-inner">
                    <div class="item-title">${venue.name} - ${venue.venue_location}</div>
                </div>
            </a>
        `);
    }

    // Render top results
    let hasTopResults = false;

    if (searchResults.top_results) {
        if (searchResults.top_results.events && searchResults.top_results.events.length > 0) {
            hasTopResults = true;
            const eventSubList = document.createElement('ul');
            const heading = document.createElement('h2');

            heading.innerHTML = 'Trending Events';
            heading.classList.add('section-title', 'mt-3', 'mb-2');
            topContainer.appendChild(heading);
            topContainer.appendChild(eventSubList);

            renderList(eventSubList, searchResults.top_results.events, event => `
                <a class="item-link search-result item-content" href="/discover-view-event/${event.id}">
                    <div class="item-media">
                        <div class="image-square image-rounded" style="background-image:url('${event.thumbnail}')"></div>
                    </div>
                    <div class="item-inner">
                        <div class="item-title">${event.name}</div>
                    </div>
                </a>
            `);
        }

        if (searchResults.top_results.users && searchResults.top_results.users.length > 0) {
            hasTopResults = true;

            const userSubList = document.createElement('ul');
            const heading = document.createElement('h2');

            heading.innerHTML = 'Trending Users';
            heading.classList.add('section-title', 'mt-3', 'mb-2');
            topContainer.appendChild(heading);
            topContainer.appendChild(userSubList);

            renderList(userSubList, searchResults.top_results.users, (user) => {
                const userLink = user.type == 'user' ? '/profile-view/' + user.id : '/profile-garage-vehicle-view/' + user.id;
                let contentText;

                if (user.type == 'user') {
                    contentText = `${user.name} (@${user.username})`;
                }

                if (user.type == 'vehicle') {
                    contentText = `${user.name}'s <b>${user.meta.make} ${user.meta.model}</b>`;
                }

                return `
                <a class="item-link search-result item-content" href="${userLink}">
                    <div class="item-media">
                        <div class="image-square image-rounded" style="background-image:url('${user.thumbnail || 'assets/img/profile-placeholder.jpg'}')"></div>
                    </div>
                    <div class="item-inner">
                        <div class="item-title">${contentText}</div>
                    </div>
                </a>`
            });
        }

        if (searchResults.top_results.venues && searchResults.top_results.venues.length > 0) {
            hasTopResults = true;

            const venueSubList = document.createElement('ul');
            const heading = document.createElement('h2');

            heading.innerHTML = 'Trending Venues';
            heading.classList.add('section-title', 'mt-3', 'mb-2');

            topContainer.appendChild(heading);
            topContainer.appendChild(venueSubList);

            renderList(venueSubList, searchResults.top_results.venues, venue => `
                <a class="item-link search-result item-content" href="/discover-view-venue/${venue.id}">
                    <div class="item-media">
                        <div class="image-square image-rounded" style="background-image:url('${venue.thumbnail}')"></div>
                    </div>
                    <div class="item-inner">
                        <div class="item-title">${venue.name} - ${venue.venue_location}</div>
                    </div>
                </a>
            `);
        }
    }

    if (!hasTopResults) {
        topContainer.innerHTML = `
        <li class="item-content w-full">
            <strong class="text-center w-full">No results found</strong>
        </li>`;
    }
}

searchResultsStore.onUpdated((results) => {
    renderSearchResults(results);
});

/js/store.js
import {
  getNotificationCount,
  getSessionUser,
  getUserDetails,
  getUserNotifications,
  markMultipleNotificationsAsRead
} from './api/auth.js'
import {
  sendRNMessage
} from './api/consts.js'
import {
  fetchTrendingEvents,
  fetchTrendingUsers,
  fetchTrendingVenues,
  fetchEventCats
} from './api/discover.js'
import {
  getPostsForGarage,
  getUserGarage
} from './api/garage.js'
import {
  fetchPosts,
  getPostsForUser
} from './api/posts.js'

var createStore = Framework7.createStore

const DEFAULT_SEARCH_RESULTS = {
  events: {
    data: [],
    total_pages: 0,
    page: 1,
    limit: 10,
  },
  users: {
    data: [],
    total_pages: 0,
    page: 1,
    limit: 10,
  },
  venues: {
    data: [],
    total_pages: 0,
    page: 1,
    limit: 10,
  },
  top_results: {
    events: [],
    users: [],
    venues: [],
  },
  success: false,
}

const DEFAULT_PAGINATED_DATA = {
  data: [],
  new_data: [],
  total_pages: 0,
  page: 1,
  limit: 10,
}

const store = createStore({
  state: {
    user: null,
    posts: {
      new_data: [],
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    following_posts: {
      new_data: [],
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    registerData: {
      user_id: '',
      email: '',
      password: '',
      username: '',
    },
    myGarage: [],
    myPosts: {
      new_data: [],
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    myTags: {
      new_data: [],
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    garageViewPosts: {
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    garageViewTags: {
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    scannedData: null,
    scanningQrCode: false,
    paths: {}, // Object to store unique paths and their data
    userPaths: {}, // Object to store unique user paths and their data
    userPathsUpdated: false,
    notifications: [],
    // discover page
    trendingEvents: [],
    trendingVenues: [],
    eventCategories: [],
    filteredEvents: {
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    filteredVenues: {
      data: [],
      total_pages: 0,
      page: 1,
      limit: 10,
    },
    discoverSearchData: DEFAULT_SEARCH_RESULTS,
    trendingUsers: DEFAULT_PAGINATED_DATA,
    // Search results
    searchResults: DEFAULT_SEARCH_RESULTS,
    notificationCount: 0,
    poorNetworkError: false,
    trendingVehicles: DEFAULT_PAGINATED_DATA,
  },
  getters: {
    getTrendingVehicles({
      state
    }) {
      return state.trendingVehicles
    },
    checkPoorNetworkError({
      state
    }) {
      return state.poorNetworkError
    },
    getNotifCount({
      state
    }) {
      return state.notificationCount
    },
    getTrendingUsers({
      state
    }) {
      return state.trendingUsers
    },
    getSearchResults({
      state
    }) {
      return state.searchResults
    },
    getFilteredEvents({
      state
    }) {
      return state.filteredEvents
    },
    getFilteredVenues({
      state
    }) {
      return state.filteredVenues
    },
    getEventCategories({
      state
    }) {
      return state.eventCategories
    },
    getTrendingEvents({
      state
    }) {
      return state.trendingEvents
    },
    getTrendingVenues({
      state
    }) {
      return state.trendingVenues
    },
    getPathData({
      state
    }) {
      return state.paths
    },
    getUserPathUpdated({
      state
    }) {
      return state.userPathsUpdated
    },
    getUserPathData({
      state
    }) {
      return state.userPaths
    },
    getGarageViewPosts({
      state
    }) {
      return state.garageViewPosts
    },
    getGarageViewTags({
      state
    }) {
      return state.garageViewTags
    },
    user({
      state
    }) {
      return state.user
    },
    getRegisterData({
      state
    }) {
      return state.registerData
    },
    isAuthenticated({
      state
    }) {
      return !!state.user
    },
    posts({
      state
    }) {
      return state.posts
    },
    followingPosts({
      state
    }) {
      return state.following_posts
    },
    myGarage({
      state
    }) {
      return state.myGarage
    },
    myPosts({
      state
    }) {
      return state.myPosts
    },
    myTags({
      state
    }) {
      return state.myTags
    },
    scannedData({
      state
    }) {
      return state.scannedData
    },
    isScanningQrCode({
      state
    }) {
      return state.scanningQrCode
    },
    getNotifications({
      state
    }) {
      return state.notifications
    },
  },
  actions: {
    updatePost({
      state
    }, {
      post_id,
      caption
    }) {
      // loop through the posts and find the post with the post_id
      const posts = state.posts.data
      const post = posts.find(p => p.id == post_id)

      const myPosts = state.myPosts.data
      const myPost = myPosts.find(p => p.id == post_id)

      // update the post with the new data
      if (post) {
        // update the post with the new data
        post.caption = caption
        // update the state with the new posts
        state.posts = {
          ...state.posts,
          data: posts,
        }
      }

      if (myPost) {
        myPost.caption = caption

        state.myPosts = {
          ...state.myPosts,
          data: myPosts,
        }
      }
    },
    markNotificationsAsRead({
      state
    }, notification_ids) {
      markMultipleNotificationsAsRead(notification_ids)
      state.notificationCount = 0
    },
    async notificationCount({
      state
    }) {
      const response = await getNotificationCount()
      state.notificationCount = response.count
    },
    setSearchResults({
      state
    }, payload) {
      state.searchResults = payload
    },
    async filterEvents({
      state
    }, {
      filters,
      page = 1
    }) {
      try {
        const events = await fetchTrendingEvents(page, true, filters)

        const data = {
          new_data: events.data,
          data: [
            ...state.filteredEvents.data,
            ...events.data,
          ],
          total_pages: events.total_pages,
          page: page,
          limit: events.limit,
        }

        state.filteredEvents = data
      } catch (error) {
        console.error('Failed to filter events', error)
        state.filteredEvents = {
          new_data: [],
          data: [],
          total_pages: 0,
          page: 1,
          limit: 10,
        }
      }
    },
    async filterTrendingUsers({
      state
    }, page = 1) {
      try {
        const response = await fetchTrendingUsers(page)

        const data = {
          new_data: response.data,
          data: [
            ...state.trendingUsers.data,
            ...response.data,
          ],
          total_pages: response.total_pages,
          page: page,
          limit: response.limit,
        }

        state.trendingUsers = data
      } catch (error) {
        console.log('Failed to fetch trending users', error);
        state.trendingUsers = {
          new_data: [],
          data: [],
          total_pages: 0,
          page: 1,
          limit: 10,
        }
      }
    },
    async filterTrendingVehicles({
      state
    }, page = 1) {
      try {
        const response = await fetchTrendingUsers(page, true)

        const data = {
          new_data: response.data,
          data: [
            ...state.trendingVehicles.data,
            ...response.data,
          ],
          total_pages: response.total_pages,
          page: page,
          limit: response.limit,
        }

        state.trendingVehicles = data
      } catch (error) {
        console.log('Failed to fetch trending vehicles', error);
        state.trendingVehicles = {
          new_data: [],
          data: [],
          total_pages: 0,
          page: 1,
          limit: 10,
        }
      }
    },
    async filterTrendingUsers({
      state
    }, page = 1) {
      try {
        const response = await fetchTrendingUsers(page)

        const data = {
          new_data: response.data,
          data: [
            ...state.trendingUsers.data,
            ...response.data,
          ],
          total_pages: response.total_pages,
          page: page,
          limit: response.limit,
        }

        state.trendingUsers = data
      } catch (error) {
        console.log('Failed to fetch trending users', error);
        state.trendingUsers = {
          new_data: [],
          data: [],
          total_pages: 0,
          page: 1,
          limit: 10,
        }
      }
    },
    async filterVenues({
      state
    }, {
      filters,
      page = 1
    }) {
      try {
        const events = await fetchTrendingVenues(page, true, filters)

        let existingVenues = state.filteredVenues.data.length > 0 ? state.filteredVenues.data : state.trendingVenues.data

        const data = {
          new_data: events.data,
          data: [
            ...existingVenues,
            ...events.data,
          ],
          total_pages: events.total_pages,
          page: page,
          limit: events.limit,
        }

        state.filteredVenues = data
      } catch (error) {
        console.error('Failed to filter venues', error)
        state.filteredVenues = {
          new_data: [],
          data: [],
          total_pages: 0,
          page: 1,
          limit: 10,
        }
      }
    },
    async fetchEventCategories({
      state
    }) {
      const categories = await fetchEventCats()

      state.eventCategories = categories
    },
    async getTrendingEvents({
      state
    }) {
      const events = await fetchTrendingEvents(1, false)
      state.trendingEvents = events
    },
    async getTrendingVenues({
      state
    }) {
      const venues = await fetchTrendingVenues(1, false)
      state.trendingVenues = venues
    },
    async fetchNotifications({
      state
    }, {
      load_more = false
    }) {
      const notifications = await getUserNotifications(load_more)
      state.notifications = notifications
    },
    async getUserPosts({
      state
    }, {
      user_id,
      page = 1,
      clear = false
    }) {
      const posts = await getPostsForUser(user_id, page)

      let prevUserPosts = {
        data: []
      }

      if (!clear) {
        if (state.userPaths[`user-${user_id}-posts`]) {
          prevUserPosts = state.userPaths[`user-${user_id}-posts`]
        }
      }

      const data = {
        new_data: posts.data,
        data: [
          ...prevUserPosts.data,
          ...posts.data,
        ],
        total_pages: posts.total_pages,
        page: page,
        limit: posts.limit,
        cleared: clear,
      }

      state.userPaths[`user-${user_id}-posts`] = data
      state.userPathsUpdated = true
    },
    async getUserTags({
      state
    }, {
      user_id,
      page = 1,
      clear = false
    }) {
      const posts = await getPostsForUser(user_id, page, true)

      let prevUserPosts = {
        data: []
      }
      if (!clear) {
        if (state.userPaths[`user-${user_id}-tags`]) {
          prevUserPosts = state.userPaths[`user-${user_id}-tags`]
        }
      }

      const data = {
        new_data: posts.data,
        data: [
          ...prevUserPosts.data,
          ...posts.data,
        ],
        total_pages: posts.total_pages,
        page: page,
        limit: posts.limit,
        cleared: clear,
      }

      state.userPaths[`user-${user_id}-tags`] = data
      state.userPathsUpdated = true
    },
    clearPathData({
      state
    }) {
      state.paths = {}
    },
    setPathData({
      state
    }, {
      path,
      data
    }) {
      // Ensure the path key exists
      if (!state.paths[path]) {
        state.paths[path] = {}
      }

      // Update the data for the given path
      state.paths[path] = {
        ...state.paths[path],
        ...data,
      }
    },
    removePathData({
      state
    }, path) {
      if (state.paths[path]) {
        delete state.paths[path]
      }
    },
    async login({
      state
    }, {
      token
    }) {
      try {
        const userDetails = await getUserDetails(token)

        if (!userDetails || !userDetails.success) {
          window.localStorage.removeItem('token')
          throw new Error('User not found')
        }

        window.localStorage.setItem('token', token)
        state.user = userDetails.user

        setTimeout(() => {
          sendRNMessage({
            type: "authData",
            user_id: userDetails.user.id,
            page: 'auth',
          })
        }, 1000)
      } catch (error) {
        console.error('Login failed', error)
      }
    },
    logout({
      state
    }) {
      state.user = null
      window.localStorage.removeItem('token')
      window.location.reload()

      window.ReactNativeWebView.postMessage(JSON.stringify({
        type: "signOut",
        user_id: null,
        page: 'auth',
      }))
    },
    async updateUserDetails({
      state
    }, external = false) {
      const token = window.localStorage.getItem('token')

      if (!token) {
        return this.logout()
      }

      try {
        const userDetails = await getUserDetails(token)

        if (!userDetails || !userDetails.success) {
          window.localStorage.removeItem('token')
          throw new Error('User not found')
        }

        window.localStorage.setItem('token', token)
        state.user = {
          ...userDetails.user,
          refreshed: true,
          external_refresh: external,
        }
      } catch (error) {
        console.error('Login failed', error)
      }
    },
    async checkAuth(context) {
      const token = await getSessionUser()

      if (token) {
        await context.dispatch('login', {
          token: token
        })
      } else {
        window.localStorage.removeItem('token')
      }
    },
    async getPosts({
      state
    }, {
      page = 1,
      reset = false
    }) {
      try {
        console.log('Fetching posts', page, reset);

        const posts = await fetchPosts(page)

        if (reset) {
          state.posts = {
            new_data: posts.data,
            data: posts.data,
            total_pages: posts.total_pages,
            page: page,
            limit: posts.limit,
            reset: true,
          }
          return
        }

        const data = {
          new_data: posts.data,
          data: [
            ...state.posts.data,
            ...posts.data,
          ],
          total_pages: posts.total_pages,
          page: page,
          limit: posts.limit,
        }

        state.posts = data
      } catch (error) {
        console.error('Failed to fetch posts', error)

        if (error.name === 'RequestTimeout') {
          state.poorNetworkError = true
        }

        state.posts = {
          new_data: [],
          data: [],
          total_pages: 0,
          page: 1,
          limit: 10,
        }
      }
    },
    async setGarageViewPosts({
      state
    }, garage_id, page = 1) {
      const posts = await getPostsForGarage(garage_id, page)

      let prevData = state.garageViewPosts.data

      if (page === 1) {
        prevData = []
      }

      const data = {
        data: [
          ...prevData,
          ...posts.data,
        ],
        total_pages: posts.total_pages,
        page: page,
        limit: posts.limit,
      }

      state.garageViewPosts = data
    },
    async setGarageViewTags({
      state
    }, garage_id, page = 1) {
      const posts = await getPostsForGarage(garage_id, page, true)

      let prevData = state.garageViewTags.data

      if (page === 1) {
        prevData = []
      }

      const data = {
        data: [
          ...prevData,
          ...posts.data,
        ],
        total_pages: posts.total_pages,
        page: page,
        limit: posts.limit,
      }

      state.garageViewTags = data
    },
    async getFollowingPosts({
      state
    }, {
      page = 1,
      reset = false
    }) {
      try {
        const posts = await fetchPosts(page, true)

        if (reset) {
          state.following_posts = {
            new_data: posts.data,
            data: posts.data,
            total_pages: posts.total_pages,
            page: page,
            limit: posts.limit,
            reset: true,
          }
          return
        }

        const data = {
          new_data: posts.data,
          data: [
            ...state.following_posts.data,
            ...posts.data,
          ],
          total_pages: posts.total_pages,
          page: page,
          limit: posts.limit,
        }

        state.following_posts = data
      } catch (error) {
        console.error('Failed to fetch following posts', error)

        if (error.name === 'RequestTimeout') {
          state.poorNetworkError = true
        }

        state.following_posts = {
          new_data: [],
          data: [],
          total_pages: 0,
          page: 1,
          limit: 10,
        }
      }
    },
    async setRegisterData({
      state
    }, {
      email,
      password,
      username,
      user_id
    }) {
      state.registerData = {
        email: email,
        password: password,
        username: username,
        user_id: user_id,
      }
    },
    async getMyGarage({
      state
    }) {
      const garage = await getUserGarage(state.user.id)
      state.myGarage = garage
    },
    async getMyPosts({
      state
    }, {
      page = 1,
      clear = false
    }) {
      const posts = await getPostsForUser(state.user.id, page)

      if (clear) {
        state.myPosts = {
          new_data: posts.data,
          data: posts.data,
          total_pages: posts.total_pages || 0,
          page: page,
          limit: posts.limit || 10,
          cleared: true,
        }
        return
      }

      const data = {
        new_data: posts.data,
        data: [
          ...state.myPosts.data,
          ...posts.data,
        ],
        total_pages: posts.total_pages,
        page: page,
        limit: posts.limit,
      }

      state.myPosts = data
    },
    async getMyTags({
      state
    }, {
      page = 1,
      clear = false
    }) {
      const posts = await getPostsForUser(state.user.id, page, true)

      if (clear) {
        state.myTags = {
          new_data: posts.data,
          data: posts.data,
          total_pages: posts.total_pages || 0,
          page: page,
          limit: posts.limit || 10,
          cleared: true,
        }
        return
      }

      const data = {
        new_data: posts.data,
        data: [
          ...state.myTags.data,
          ...posts.data,
        ],
        total_pages: posts.total_pages,
        page: page,
        limit: posts.limit,
      }

      state.myTags = data
    },
    setScannedData({
      state
    }, data) {
      state.scannedData = data
    },
    setScanningQrCode({
      state
    }, value) {
      state.scanningQrCode = value
    },
  },
})

export default store

/js/utils.js
export function formatPostDate(date) {
    const postDate = new Date(date)
    // show date as Just now, 1 minute ago, 1 hour ago, 1 day ago, 1 week ago, 1 month ago, 1 year ago
    const currentDate = new Date()
    const diff = currentDate - postDate
    const seconds = Math.floor(diff / 1000)
    const minutes = Math.floor(seconds / 60)
    const hours = Math.floor(minutes / 60)
    const days = Math.floor(hours / 24)
    const weeks = Math.floor(days / 7)
    const months = Math.floor(days / 30)

    if (months > 0) {
        return months === 1 ? '1 month ago' : `${months} months ago`
    }

    if (weeks > 0) {
        return weeks === 1 ? '1 week ago' : `${weeks} weeks ago`
    }

    if (days > 0) {
        return days === 1 ? '1 day ago' : `${days} days ago`
    }

    if (hours > 0) {
        return hours === 1 ? '1 hour ago' : `${hours} hours ago`
    }

    if (minutes > 0) {
        return minutes === 1 ? '1 minute ago' : `${minutes} minutes ago`
    }

    return 'Just now'
}

export const timedFetch = async (url, fetchObj, error = 'Your connection seems unstable.', timeout = TIMEOUT_MS_LOW) => {
    const controller = new AbortController()
    const signal = controller.signal

    try {
        const user = await getSessionUser()
        if (!user) return

        setTimeout(() => {
            controller.abort()
        }, TIMEOUT_MS_LOW)

        const response = await fetch(url,
            fetchObj,
            signal
        )

        const data = await response.json()
        return data
    } catch (error) {
        if (error.name === 'AbortError') {
            throw {
                message: error,
                name: "TimeOutError"
            };
        } else {
            throw error; // Rethrow any other errors
        }
    }
}

/js/venue-view.js
import {
    fetchVenue,
    maybeFollowVenue
} from "./api/discover.js";
import app from "./app.js";

var $ = Dom7;

//DISCOVER - VIEW EVENT
$(document).on('page:init', '.page[data-name="discover-view-venue"]', async function (e) {
    var venueId = e.detail.route.params.id

    if (!venueId || venueId === '-1') {
        return;
    }

    $('.loading-fullscreen').show()
    const venueData = await fetchVenue(venueId);

    $('.loading-fullscreen').hide()

    $('#copy-venue-link').attr('data-venue-id', venueId);
    $('#share-email-venue-link').attr('data-venue-id', venueId);

    const mainContainer = $('.discover-view-event');

    if (!venueData) {
        mainContainer.html('<div class="text-align-center">No event found</div>');
        return;
    }

    // Populating the Event Title
    mainContainer.find('.event-detail-title').text(venueData.title);

    // Populating the Event Location
    // create a map link with the address
    const mapLink = `https://www.google.com/maps/search/?api=1&query=${venueData.location}`
    // create an anchor tag with the map link
    const mapLinkTag = `<a href="${mapLink}" target="_blank" class="event-location-map">${venueData.location}</a>`
    mainContainer.find('.event-time-address span').html(mapLinkTag);

    // Populating the Cover Image
    mainContainer.find('.event-detail-img-box .swiper-slide .swiper-image')
        .css('background-image', `url('${venueData.cover_photo.url}')`)
        .attr('alt', venueData.cover_photo.alt);

    // Populating the "About" Tab Content
    mainContainer.find('#tab-about .event-des-wrap').html(`<p>${venueData.description}</p>`);

    // Populating the "Follow" button state
    const followButton = mainContainer.find('.event-list-btn .btn.bg-dark');
    if (venueData.is_following) {
        followButton.text('Following');
    } else {
        followButton.text('Follow');
    }

    // Populating the "Upcoming Events" Tab (if there are any events)
    const eventsContainer = mainContainer.find('#tab-events .grid.event-listing');
    eventsContainer.empty(); // Clear any placeholder content

    if (venueData.events.length > 0) {
        venueData.events.forEach(event => {
            const startDate = new Date(event.start_date);
            const endDate = new Date(event.end_date);

            const startMonth = startDate.toLocaleString('default', {
                month: 'short'
            });
            const startDay = startDate.getDate();

            let endDateString = '';

            if (startDate.getDate() !== endDate.getDate()) {
                endDateString = `
            <div class="event-date-item">
                <p>${endDate.toLocaleString('default', { month: 'short' })}</p>
                <h5>${endDate.getDate()}</h5>
            </div>
            `
            }

            const eventItem = `
            <a href="/discover-view-event/${event.id}" class="card event-item">
                <div class="event-image position-relative">
                    <div class="image-rectangle" style="background-image: url('${event.thumbnail}');"></div>
                    <div class="event-dates">
                        <div class="event-date-item">
                            <p>${startMonth}</p>
                            <h5>${startDay}</h5>
                        </div>
                        ${endDateString}
                    </div>
                </div>
                <div class="card-content">
                    <h3 class="event-title">${event.title}</h3>
                    <p class="event-info">Starts ${event.start_date}</p>
                    <div class="event-info">${event.location}</div>
                </div>
            </a>
        `;
            eventsContainer.append(eventItem);
        });
    } else {
        eventsContainer.html('<div class="text-align-center">No upcoming events</div>');
    }

    // follow button
    const is_following = venueData.is_following

    if (is_following) {
        mainContainer.find('.venue-follow-btn').text('Following')
    }

    mainContainer.find('.venue-follow-btn').on('click', async function () {
        const followButton = $(this);
        const isFollowing = followButton.text() === 'Following';

        // change the button text
        followButton.text(isFollowing ? 'Follow' : 'Following');
        const response = await maybeFollowVenue(venueId)
    });
})

// event-location-map
$(document).on('click', '.event-time-address span a', function (e) {
    e.preventDefault();
    const mapLink = $(this).attr('href');
    window.open(mapLink, '_blank');
});

$(document).on('click', '#copy-venue-link', function () {
    const venueId = $(this).attr('data-venue-id');
    const eventLink = `${window.location.origin}/discover-view-venue/${venueId}`;

    navigator.clipboard.writeText(eventLink);

    app.toast.create({
        text: 'Link copied to clipboard',
        closeTimeout: 2000
    }).open()
});

// #share-email-venue-link click event
$(document).on('click', '#share-email-venue-link', function () {
    const venueId = $(this).attr('data-venue-id');
    const eventLink = `${window.location.origin}/discover-view-venue/${venueId}`;

    window.open(`mailto:?subject=Check out this venue&body=${eventLink}`);
});

/js/view-post.js
import app from "./app.js"
import {
  getPostById
} from "./api/posts.js"
import {
  formatPostDate
} from "./utils.js"
import store from "./store.js"
import { detectDoubleTapClosure, loadVideos, togglePostLike } from "./homepage.js"

var $ = Dom7
var containerWidth = window.innerWidth

export function displayPost(post) {
  const postsContainer = document.getElementById('post-view-container')
  postsContainer.innerHTML = '' // Clear any existing posts

  const user = store.getters.user.value

  let post_actions = `
  <div class="media-post-actions">
    <div class="media-post-like" data-post-id="${post.id}">
      <i class="icon f7-icons ${post.is_liked ? 'text-red' : ''}" data-post-id="${post.id}">${post.is_liked ? 'heart_fill' : 'heart'}</i>
    </div>
    <div class="media-post-comment popup-open" data-popup=".comments-popup" data-post-id="${post.id}">
      <i class="icon f7-icons">chat_bubble</i>
    </div>
    <div class="media-post-share popup-open" data-popup=".share-popup">
      <i class="icon f7-icons">paperplane</i>
    </div>
`;

  if (post.user_id == user.id) {
    post_actions += `
    <div class="media-post-edit popup-open" data-popup=".edit-post-popup" data-post-id="${post.id}">
      <i class="icon f7-icons">gear_alt</i>
    </div>
  `;
  }

  post_actions += `</div>`;

  const date = formatPostDate(post.post_date);
  const maxDescriptionLength = 200; // Set your character limit here
  const isLongDescription = post.caption.length > maxDescriptionLength;
  const shortDescription = isLongDescription ? post.caption.slice(0, maxDescriptionLength) : post.caption;

  let imageHeight = 400;

  if (post.media.length > 0) {
    const intrinsicWidth = post.media[0].media_width;
    const intrinsicHeight = post.media[0].media_height;
    const media_type = post.media[0].media_type;

    // Calculate intrinsic aspect ratio
    const intrinsicRatio = intrinsicWidth / intrinsicHeight;

    // Calculate the rendered height based on the container width
    const renderedHeight = containerWidth / intrinsicRatio;

    // Use either the rendered height or the fallback height
    if (renderedHeight > 0) {
      if (renderedHeight > 500) {
        imageHeight = 500
      } else {
        imageHeight = renderedHeight
      }


      if (media_type === 'video') {
        imageHeight = renderedHeight
      }
    }
  }


  let profile_link;

  if (post.user_id == user.id) {
    profile_link = `
  <a href="#" class="view-profile media-post-header">
    <div class="media-post-avatar" style="background-image: url('${post.user_profile_image || 'assets/img/profile-placeholder.jpg'}');"></div>
    <div class="media-post-user">${post.username}</div>
    <div class="media-post-date">${date}</div>
  </a>`
  } else {
    profile_link = `
  <a href="/profile-view/${post.user_id}" class="media-post-header">
    <div class="media-post-avatar" style="background-image: url('${post.user_profile_image || 'assets/img/profile-placeholder.jpg'}');"></div>
    <div class="media-post-user">${post.username}</div>
    <div class="media-post-date">${date}</div>
  </a>`
  }

  const postItem = `
  <div class="media-post single" data-post-id="${post.id}" data-is-liked="${post.is_liked}">
    <div class="media-single-post-content">
      ${profile_link}
      <div class="media-single-post-content">
      <swiper-container pagination class="demo-swiper-multiple" space-between="50">
            ${post.media.map((mediaItem, index) => {

    return `<swiper-slide class="swiper-slide post-media ${mediaItem.media_type === 'video' ? 'video' : ''}" style="height: ${imageHeight}px; ">
                ${mediaItem.media_type === 'video' ?
        `<video 
              style="height: ${imageHeight}px;" 
              class="video-js" 
              data-src="${mediaItem.media_url}/manifest/video.m3u8" 
              preload="auto" 
              playsinline 
              loop 
              controls 
              autoplay 
              poster="${mediaItem.media_url}/thumbnails/thumbnail.jpg"  <!-- Add the thumbnail as the poster image -->
            ></video>`
        : `
                <img 
                    src="${mediaItem.media_url}" 
                    alt="${mediaItem.caption || post.username + 's post'}"
                    style="text-align: center;"
                    onerror = "this.style.display='none';"
                  />`}
              </swiper-slide>
            `}).join('')}
      </swiper-container>
      </div>
      ${post_actions}
      <div class="media-post-likecount" data-like-count="${post.likes_count}">${post.likes_count} likes</div>
      <div class="media-post-description">
        <strong>${post.username}</strong> <br/> <span class="post-caption">${shortDescription}</span>
        <span class="full-description hidden">${post.caption}</span>
        ${isLongDescription ? `<span class="media-post-readmore">... more</span>` : ''}
      </div>
      ${post.comments_count > 0 ? `<div class="media-post-commentcount popup-open" data-popup=".comments-popup" data-post-id="${post.id}">View ${post.comments_count} comments</div>` : ''}
    </div>
  </div>
`;

  postsContainer.insertAdjacentHTML('beforeend', postItem)
  loadVideos()
}

$(document).on('touchstart', '.media-single-post-content .post-media', detectDoubleTapClosure((e) => {
  const parent = e.closest('.media-post')
  const postId = parent.getAttribute('data-post-id')
  const isLiked = parent.getAttribute('data-is-liked') === 'true'

  if (isLiked) {
    return
  }

  togglePostLike(postId, true)

  var pathStore = store.getters.getPathData

  if (pathStore && pathStore.value[`/post/${postId}`]) {
    var post = pathStore.value[`/post/${postId}`]
    post.is_liked = true
    post.likes_count += 1

    store.dispatch('setPathData', {
      path: `/post/${postId}`,
      data: post,
    })
  }
}), {
  passive: false
})

$(document).on('page:beforein', '.page[data-name="post-view"]', async function (e) {
  var pathStore = store.getters.getPathData

  var postId = e.detail.route.params.id
  var query = e.detail.route.query

  let commentId;

  if (query && query.commentId) {
    commentId = query.commentId
  }

  if (!postId || postId === '-1') {
    return
  }

  let cachedData = null

  try {
    if (pathStore && pathStore.value[`/post/${postId}`]) {
      cachedData = pathStore.value[`/post/${postId}`]
    }
  } catch (error) {
    console.error('Error fetching cached data:', error)
  }

  if (!cachedData) {
    $('.loading-fullscreen.post-view').show()

    const post = await getPostById(postId)
    if (!post) {
      app.dialog.alert('Post not found', 'Error')
      return
    }

    store.dispatch('setPathData', {
      path: `/post/${postId}`,
      data: post,
    })

    cachedData = post
  } else {
    $('.loading-fullscreen.post-view').hide()
  }

  displayPost(cachedData)

  if (commentId) {
    $('.media-post-comment').click()
  }


  setTimeout(() => {
    // find .comment data-comment-id="${comment.id}" and animate it to glow#
    if (commentId) {
      const comment = $(`.comment[data-comment-id="${commentId}"]`)
      console.log('Comment:', comment);

      if (comment.length > 0) {
        comment.addClass('target-highlight')
        // Scroll to the comment
        document.querySelector(`.comment[data-comment-id="${commentId}"]`).scrollIntoView({
          behavior: 'smooth', // Optional, adds smooth scrolling
          block: 'start', // Aligns the element to the top of the view
          inline: 'nearest' // Aligns the element horizontally in the viewport
        });

      }

      setTimeout(() => {
        comment.removeClass('target-highlight')
      }, 3000)
    }
  }, 2000)
})

/js/view-user-profile.js
import {
    getSessionUser,
    getUserById
} from "./api/auth.js"
import {
    getUserGarage
} from "./api/garage.js"
import {
    maybeFollowUser
} from "./api/profile.js"
import app from "./app.js"
import {
    createGarageContent,
    displayProfile,
    fillGridWithPosts
} from "./profile.js"
import store from "./store.js"

var $ = Dom7

var totalPostPages = 1
var totalFPostPages = 1
var currentPostPage = 1
var currentFPostPage = 1

var isFetchingPosts = false
var refreshed = false;

var userId = null

$(document).on('page:init', '.page[data-name="profile-view"]', async function (e) {
    userId = e.detail.route.params.id

    currentPostPage = 1
    currentFPostPage = 1
    isFetchingPosts = false
    refreshed = false

    store.dispatch('getUserPosts', {
        user_id: userId,
        clear: true
    })

    store.dispatch('getUserTags', {
        user_id: userId,
        clear: true
    })
})

$(document).on('page:beforein', '.page[data-name="profile-view"]', async function (e) {
    $('.loading-fullscreen').show()

    var pathStore = store.getters.getPathData
    userId = e.detail.route.params.id

    const sessionUser = await getSessionUser()

    if (!sessionUser || !sessionUser.id) {
        return;
    }

    // Follow button
    const followButton = $('.user-follow-btn')
    const sessionFollowings = sessionUser.following;

    if (sessionFollowings.includes(`${userId}`)) {
        followButton.text('Following')
    } else {
        followButton.text('Follow')
    }

    followButton.attr('data-user-id', userId)

    let cachedData = null
    try {
        if (pathStore && pathStore.value[`/user/${userId}`]) {
            cachedData = pathStore.value[`/user/${userId}`]
        }
    } catch (error) {
        console.error('Error fetching cached data:', error)
    }

    await renderProfileData(cachedData, userId)
})

$(document).on('click', '.user-follow-btn', async function () {
    const followButton = $(this)
    const isFollowing = followButton.text() === 'Following'

    // change the button text
    followButton.text(isFollowing ? 'Follow' : 'Following')
    const response = await maybeFollowUser(followButton.attr('data-user-id'))

    if (response && response.success) {
        store.dispatch('updateUserDetails', {
            external: true
        })
    }
})

async function renderProfileData(cachedData, userId) {
    // if (!refreshed && !cachedData) {
    // }

    refreshed = false

    if (!cachedData) {
        const data = await getUserById(userId)
        console.log('User data:', data);

        if (!data || data.error) {
            $('.loading-fullscreen').hide()
            app.dialog.alert('User not found', 'Error')

            view.router.back(view.history[0], {
                force: true
            })
            return
        }

        const garage = await getUserGarage(userId)

        if (garage) {
            createGarageContent(garage, '.pview-current-vehicles-list', '.pview-past-vehicles-list')
        }

        // Assuming `path` is a dynamic path like '/garage/2'
        store.dispatch('setPathData', {
            path: `/user/${userId}`,
            data: {
                user: data.user,
                garage: garage,
            },
        })

        displayProfile(data.user, 'profile-view')
    } else {
        displayProfile(cachedData.user, 'profile-view')

        if (cachedData.garage) {
            createGarageContent(cachedData.garage, '.pview-current-vehicles-list', '.pview-past-vehicles-list')
        }
    }

    $('.loading-fullscreen').hide()
}

function populateUsersPosts(data) {
    if (data) {
        const postsKey = `user-${userId}-posts`
        const tagsKey = `user-${userId}-tags`

        // Handle posts
        if (data[postsKey]) {
            totalPostPages = data[postsKey].total_pages || 0

            let reset = data[postsKey].cleared || false

            // Only update the DOM if there are new posts
            if (data[postsKey].new_data && data[postsKey].new_data.length > 0) {
                fillGridWithPosts(data[postsKey].new_data, 'profile-view-grid-posts', reset)
                // Clear new_data after processing to avoid re-rendering

                data[postsKey].new_data = []
            }

            if ((data[postsKey].page === totalPostPages) || (totalPostPages == 0)) {
                // hide preloader
                $('.infinite-scroll-preloader.posts-tab.view-profile').hide()
            }

            if (data[postsKey].data.length === 0) {
                const profileGrid = document.getElementById('profile-view-grid-posts')
                profileGrid.innerHTML = '<p></p><p>No posts</p>'
                return;
            }

        }

        // Handle tags
        if (data[tagsKey]) {
            totalFPostPages = data[tagsKey].total_pages || 0

            let reset = data[tagsKey].cleared || false

            // Only update the DOM if there are new tags
            if (data[tagsKey].new_data && data[tagsKey].new_data.length > 0) {
                fillGridWithPosts(data[tagsKey].new_data, 'profile-view-grid-tags', reset)
                // Clear new_data after processing to avoid re-rendering
                data[tagsKey].new_data = []
            }

            if ((data[tagsKey].page === totalFPostPages) || (totalFPostPages == 0)) {
                // hide preloader
                $('.infinite-scroll-preloader.tags-tab.view-profile').hide()
            }

            if (data[tagsKey].data.length === 0) {
                const profileGrid = document.getElementById('profile-view-grid-tags')
                profileGrid.innerHTML = '<p></p><p>No tagged posts</p>'
                return;
            }

        }
    }
}

store.getters.getUserPathUpdated.onUpdated(() => {
    const data = store.getters.getUserPathData.value
    populateUsersPosts(data)
})

$(document).on('infinite', '.profile-landing-page.infinite-scroll-content.view-page', async function (e) {
    if (isFetchingPosts) return

    const activeTab = document.querySelector('.profile-tabs .tab-link-active')
    const activeTabId = activeTab.id

    if (!activeTabId || activeTabId === 'my-garage') return

    const getterFunc = activeTabId === 'my-posts' ? 'getUserPosts' : 'getUserTags'

    isFetchingPosts = true

    if (activeTabId === 'my-posts') {
        currentPostPage++
        if (currentPostPage <= totalPostPages) {
            await store.dispatch(getterFunc, {
                user_id: userId,
                page: currentPostPage
            })
            isFetchingPosts = false
        }
    } else {
        currentFPostPage++

        if (currentFPostPage <= totalFPostPages) {
            await store.dispatch(getterFunc, {
                user_id: userId,
                page: currentFPostPage
            })
            isFetchingPosts = false
        }
    }
})

$(document).on('ptr:refresh', '.profile-landing-page.view-page.ptr-content', async function (e) {
    try {
        currentPostPage = 1
        currentFPostPage = 1

        store.dispatch('removePathData', `/user/${userId}`)

        await renderProfileData(null, userId)

        store.dispatch('getUserPosts', {
            user_id: userId,
            clear: true
        })

        store.dispatch('getUserTags', {
            user_id: userId,
            clear: true
        })

        refreshed = true
    } catch (error) {
        console.log('Error refreshing profile page:', error);
    }

    app.ptr.get('.profile-landing-page.view-page.ptr-content').done()
})