theme and plugin sync
This commit is contained in:
BIN
autocart_assets/.DS_Store
vendored
Normal file
BIN
autocart_assets/.DS_Store
vendored
Normal file
Binary file not shown.
12
autocart_assets/README.md
Normal file
12
autocart_assets/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
Content Filter
|
||||
=========
|
||||
|
||||
A slide-in filter panel powered by CSS and jQuery.
|
||||
|
||||
[Article on CodyHouse](http://codyhouse.co/gem/content-filter/)
|
||||
|
||||
[Demo](http://codyhouse.co/demo/content-filter/index.html)
|
||||
|
||||
Filter plugin: [MixItUp](https://github.com/patrickkunka/mixitup) (free to use in non-commercial projects)
|
||||
|
||||
[Terms](http://codyhouse.co/terms/)
|
97
autocart_assets/css/filter-panel.css
Normal file
97
autocart_assets/css/filter-panel.css
Normal file
@@ -0,0 +1,97 @@
|
||||
:root {
|
||||
--mdc-theme-primary: #961d20;
|
||||
--mdc-theme-secondary: #961d20;
|
||||
--mdc-checkbox-checked-color: #961d20;
|
||||
/* --mdc-ripple-press-opacity: #961d20; */
|
||||
}
|
||||
|
||||
.range-label{
|
||||
|
||||
background-color: var(--gray-800);
|
||||
padding: 0.3rem 1rem;
|
||||
color: white;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
|
||||
top: 35%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
.ripple {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ripple:before {
|
||||
pointer-events: none;
|
||||
background-image: radial-gradient(circle, #000 10%, transparent 10.01%);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50%;
|
||||
transform: scale(10, 10);
|
||||
transition: transform 0.5s, opacity 1s;
|
||||
}
|
||||
|
||||
.ripple:hover:before {
|
||||
transform: scale(0, 0);
|
||||
opacity: 0.2;
|
||||
}
|
||||
.selected {
|
||||
color: #961d20; /* Change this to your desired color */
|
||||
}
|
||||
|
||||
.mdc-slider .mdc-slider__thumb-knob {
|
||||
|
||||
border: 2px solid #fff!important;
|
||||
-webkit-box-shadow: 0 1px 4px rgba(0,0,0,.4)!important;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.4)!important;
|
||||
/* background: var(--color-primary-dark)!important; */
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
li.autocart-filters:hover {
|
||||
color: var(--mdc-theme-primary) !important;
|
||||
}
|
||||
|
||||
|
||||
/* .autocart-filters *:hover {
|
||||
color: var(--mdc-theme-primary)
|
||||
} */
|
||||
.autocart-filters label{
|
||||
font-family: 'Circular Std', sans-serif;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Circular Std';
|
||||
src: url('fonts/CircularStd-Medium.woff2') format('woff2'),
|
||||
url('fonts/CircularStd-Medium.woff') format('woff');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
.mdc-chip{
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,.4)!important;
|
||||
border: 2px solid var(--mdc-theme-primary) !important;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
|
||||
#tabs-viewer{
|
||||
/* height: fit-content; */
|
||||
position: sticky;
|
||||
max-height: 75vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.overflow-x-scroll {
|
||||
scroll-snap-type: x mandatory;
|
||||
}
|
||||
|
||||
.flex-shrink-0 {
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
|
||||
.sticky-container{
|
||||
position: sticky;
|
||||
}
|
BIN
autocart_assets/css/fonts/CircularStd-Black.woff
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Black.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/CircularStd-Black.woff2
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Black.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/CircularStd-Bold.woff
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Bold.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/CircularStd-Bold.woff2
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Bold.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/CircularStd-Book.woff
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Book.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/CircularStd-Book.woff2
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Book.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/CircularStd-Medium.woff
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Medium.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/CircularStd-Medium.woff2
Normal file
BIN
autocart_assets/css/fonts/CircularStd-Medium.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/MaterialIcons-Regular.woff
Normal file
BIN
autocart_assets/css/fonts/MaterialIcons-Regular.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/MaterialIcons-Regular.woff2
Normal file
BIN
autocart_assets/css/fonts/MaterialIcons-Regular.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/RightGrotesk-CompactBlack.woff
Normal file
BIN
autocart_assets/css/fonts/RightGrotesk-CompactBlack.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/RightGrotesk-CompactBlack.woff2
Normal file
BIN
autocart_assets/css/fonts/RightGrotesk-CompactBlack.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/RightGrotesk-Medium.woff
Normal file
BIN
autocart_assets/css/fonts/RightGrotesk-Medium.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/RightGrotesk-Medium.woff2
Normal file
BIN
autocart_assets/css/fonts/RightGrotesk-Medium.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/WooCommerce.eot
Normal file
BIN
autocart_assets/css/fonts/WooCommerce.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/WooCommerce.ttf
Normal file
BIN
autocart_assets/css/fonts/WooCommerce.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/WooCommerce.woff
Normal file
BIN
autocart_assets/css/fonts/WooCommerce.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/eicons.eot
Normal file
BIN
autocart_assets/css/fonts/eicons.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/eicons.ttf
Normal file
BIN
autocart_assets/css/fonts/eicons.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/eicons.woff
Normal file
BIN
autocart_assets/css/fonts/eicons.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/eicons.woff2
Normal file
BIN
autocart_assets/css/fonts/eicons.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-brands-400.eot
Normal file
BIN
autocart_assets/css/fonts/fa-brands-400.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-brands-400.ttf
Normal file
BIN
autocart_assets/css/fonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-brands-400.woff
Normal file
BIN
autocart_assets/css/fonts/fa-brands-400.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-brands-400.woff2
Normal file
BIN
autocart_assets/css/fonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-regular-400.eot
Normal file
BIN
autocart_assets/css/fonts/fa-regular-400.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-regular-400.ttf
Normal file
BIN
autocart_assets/css/fonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-regular-400.woff
Normal file
BIN
autocart_assets/css/fonts/fa-regular-400.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-regular-400.woff2
Normal file
BIN
autocart_assets/css/fonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-solid-900.eot
Normal file
BIN
autocart_assets/css/fonts/fa-solid-900.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-solid-900.ttf
Normal file
BIN
autocart_assets/css/fonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-solid-900.woff
Normal file
BIN
autocart_assets/css/fonts/fa-solid-900.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fa-solid-900.woff2
Normal file
BIN
autocart_assets/css/fonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont.eot
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont.ttf
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont.woff
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont.woff2
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.eot
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.ttf
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.woff
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.woff2
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont_1.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont_2.eot
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont_2.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/fontawesome-webfont_3.eot
Normal file
BIN
autocart_assets/css/fonts/fontawesome-webfont_3.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/material-design-iconic-font.ttf
Normal file
BIN
autocart_assets/css/fonts/material-design-iconic-font.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/material-design-iconic-font.woff
Normal file
BIN
autocart_assets/css/fonts/material-design-iconic-font.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/material-design-iconic-font.woff2
Normal file
BIN
autocart_assets/css/fonts/material-design-iconic-font.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/revicons.eot
Normal file
BIN
autocart_assets/css/fonts/revicons.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/revicons.ttf
Normal file
BIN
autocart_assets/css/fonts/revicons.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/revicons.woff
Normal file
BIN
autocart_assets/css/fonts/revicons.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/simple-line-icons.eot
Normal file
BIN
autocart_assets/css/fonts/simple-line-icons.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/simple-line-icons.ttf
Normal file
BIN
autocart_assets/css/fonts/simple-line-icons.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/simple-line-icons.woff
Normal file
BIN
autocart_assets/css/fonts/simple-line-icons.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/simple-line-icons.woff2
Normal file
BIN
autocart_assets/css/fonts/simple-line-icons.woff2
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/slick.eot
Normal file
BIN
autocart_assets/css/fonts/slick.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/slick.ttf
Normal file
BIN
autocart_assets/css/fonts/slick.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/slick.woff
Normal file
BIN
autocart_assets/css/fonts/slick.woff
Normal file
Binary file not shown.
13
autocart_assets/css/fonts/slick_1.eot
Normal file
13
autocart_assets/css/fonts/slick_1.eot
Normal file
@@ -0,0 +1,13 @@
|
||||
<html><head><title>404 Not Found</title></head>
|
||||
<body>
|
||||
<center><h1>404 Not Found</h1></center>
|
||||
<hr><center>nginx</center>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html><!-- a padding to disable MSIE and Chrome friendly error page --><!-- a padding to disable MSIE and Chrome friendly error page --><!-- a padding to disable MSIE and Chrome friendly error page --><!-- a padding to disable MSIE and Chrome friendly error page --><!-- a padding to disable MSIE and Chrome friendly error page --><!-- a padding to disable MSIE and Chrome friendly error page -->
|
BIN
autocart_assets/css/fonts/star.eot
Normal file
BIN
autocart_assets/css/fonts/star.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/star.ttf
Normal file
BIN
autocart_assets/css/fonts/star.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/star.woff
Normal file
BIN
autocart_assets/css/fonts/star.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/tb-icon.eot
Normal file
BIN
autocart_assets/css/fonts/tb-icon.eot
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/tb-icon.ttf
Normal file
BIN
autocart_assets/css/fonts/tb-icon.ttf
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/tb-icon.woff
Normal file
BIN
autocart_assets/css/fonts/tb-icon.woff
Normal file
Binary file not shown.
BIN
autocart_assets/css/fonts/tb-icon.woff2
Normal file
BIN
autocart_assets/css/fonts/tb-icon.woff2
Normal file
Binary file not shown.
12
autocart_assets/img/cd-icon-arrow.svg
Normal file
12
autocart_assets/img/cd-icon-arrow.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#BBBBBB;}
|
||||
</style>
|
||||
<g>
|
||||
<polygon class="st0" points="0.9,5.5 3.1,3.4 8,8.3 12.9,3.4 15.1,5.5 8,12.6 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 623 B |
10
autocart_assets/img/cd-icon-check.svg
Normal file
10
autocart_assets/img/cd-icon-check.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<polyline class="st0" points="4,7 7,10 12,5 "/>
|
||||
</svg>
|
After Width: | Height: | Size: 628 B |
15
autocart_assets/img/cd-icon-filter.svg
Normal file
15
autocart_assets/img/cd-icon-filter.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#8F83B8;}
|
||||
</style>
|
||||
<g>
|
||||
<rect x="10" y="3" class="st0" width="6" height="2"/>
|
||||
<polygon class="st0" points="3,7 9,7 9,1 3,1 3,3 0,3 0,5 3,5 "/>
|
||||
<rect y="11" class="st0" width="6" height="2"/>
|
||||
<polygon class="st0" points="13,9 7,9 7,15 13,15 13,13 16,13 16,11 13,11 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 791 B |
133
autocart_assets/index.html
Normal file
133
autocart_assets/index.html
Normal file
@@ -0,0 +1,133 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700' rel='stylesheet' type='text/css'>
|
||||
|
||||
<link rel="stylesheet" href="css/reset.css"> <!-- CSS reset -->
|
||||
<link rel="stylesheet" href="css/style.css"> <!-- Resource style -->
|
||||
<script src="js/modernizr.js"></script> <!-- Modernizr -->
|
||||
|
||||
<title>Content Filters | CodyHouse</title>
|
||||
</head>
|
||||
<body>
|
||||
<header class="cd-header">
|
||||
<h1>Content Filters</h1>
|
||||
</header>
|
||||
|
||||
<main class="cd-main-content">
|
||||
<div class="cd-tab-filter-wrapper">
|
||||
<div class="cd-tab-filter">
|
||||
<ul class="cd-filters">
|
||||
<li class="placeholder">
|
||||
<a data-type="all" href="#0">All</a> <!-- selected option on mobile -->
|
||||
</li>
|
||||
<li class="filter"><a class="selected" href="#0" data-type="all">All</a></li>
|
||||
<li class="filter" data-filter=".color-1"><a href="#0" data-type="color-1">Color 1</a></li>
|
||||
<li class="filter" data-filter=".color-2"><a href="#0" data-type="color-2">Color 2</a></li>
|
||||
</ul> <!-- cd-filters -->
|
||||
</div> <!-- cd-tab-filter -->
|
||||
</div> <!-- cd-tab-filter-wrapper -->
|
||||
|
||||
<section class="cd-gallery">
|
||||
<ul>
|
||||
<li class="mix color-1 check1 radio2 option3"><img src="img/img-1.jpg" alt="Image 1"></li>
|
||||
<li class="mix color-2 check2 radio2 option2"><img src="img/img-2.jpg" alt="Image 2"></li>
|
||||
<li class="mix color-1 check3 radio3 option1"><img src="img/img-3.jpg" alt="Image 3"></li>
|
||||
<li class="mix color-1 check3 radio2 option4"><img src="img/img-4.jpg" alt="Image 4"></li>
|
||||
<li class="mix color-1 check1 radio3 option2"><img src="img/img-5.jpg" alt="Image 5"></li>
|
||||
<li class="mix color-2 check2 radio3 option3"><img src="img/img-6.jpg" alt="Image 6"></li>
|
||||
<li class="mix color-2 check2 radio2 option1"><img src="img/img-7.jpg" alt="Image 7"></li>
|
||||
<li class="mix color-1 check1 radio3 option4"><img src="img/img-8.jpg" alt="Image 8"></li>
|
||||
<li class="mix color-2 check1 radio2 option3"><img src="img/img-9.jpg" alt="Image 9"></li>
|
||||
<li class="mix color-1 check3 radio2 option4"><img src="img/img-10.jpg" alt="Image 10"></li>
|
||||
<li class="mix color-1 check3 radio3 option2"><img src="img/img-11.jpg" alt="Image 11"></li>
|
||||
<li class="mix color-2 check1 radio3 option1"><img src="img/img-12.jpg" alt="Image 12"></li>
|
||||
<li class="gap"></li>
|
||||
<li class="gap"></li>
|
||||
<li class="gap"></li>
|
||||
</ul>
|
||||
<div class="cd-fail-message">No results found</div>
|
||||
</section> <!-- cd-gallery -->
|
||||
|
||||
<div class="cd-filter">
|
||||
<form>
|
||||
<div class="cd-filter-block">
|
||||
<h4>Search</h4>
|
||||
|
||||
<div class="cd-filter-content">
|
||||
<input type="search" placeholder="Try color-1...">
|
||||
</div> <!-- cd-filter-content -->
|
||||
</div> <!-- cd-filter-block -->
|
||||
|
||||
<div class="cd-filter-block">
|
||||
<h4>Check boxes</h4>
|
||||
|
||||
<ul class="cd-filter-content cd-filters list">
|
||||
<li>
|
||||
<input class="filter" data-filter=".check1" type="checkbox" id="checkbox1">
|
||||
<label class="checkbox-label" for="checkbox1">Option 1</label>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input class="filter" data-filter=".check2" type="checkbox" id="checkbox2">
|
||||
<label class="checkbox-label" for="checkbox2">Option 2</label>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input class="filter" data-filter=".check3" type="checkbox" id="checkbox3">
|
||||
<label class="checkbox-label" for="checkbox3">Option 3</label>
|
||||
</li>
|
||||
</ul> <!-- cd-filter-content -->
|
||||
</div> <!-- cd-filter-block -->
|
||||
|
||||
<div class="cd-filter-block">
|
||||
<h4>Select</h4>
|
||||
|
||||
<div class="cd-filter-content">
|
||||
<div class="cd-select cd-filters">
|
||||
<select class="filter" name="selectThis" id="selectThis">
|
||||
<option value="">Choose an option</option>
|
||||
<option value=".option1">Option 1</option>
|
||||
<option value=".option2">Option 2</option>
|
||||
<option value=".option3">Option 3</option>
|
||||
<option value=".option4">Option 4</option>
|
||||
</select>
|
||||
</div> <!-- cd-select -->
|
||||
</div> <!-- cd-filter-content -->
|
||||
</div> <!-- cd-filter-block -->
|
||||
|
||||
<div class="cd-filter-block">
|
||||
<h4>Radio buttons</h4>
|
||||
|
||||
<ul class="cd-filter-content cd-filters list">
|
||||
<li>
|
||||
<input class="filter" data-filter="" type="radio" name="radioButton" id="radio1" checked>
|
||||
<label class="radio-label" for="radio1">All</label>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input class="filter" data-filter=".radio2" type="radio" name="radioButton" id="radio2">
|
||||
<label class="radio-label" for="radio2">Choice 2</label>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input class="filter" data-filter=".radio3" type="radio" name="radioButton" id="radio3">
|
||||
<label class="radio-label" for="radio3">Choice 3</label>
|
||||
</li>
|
||||
</ul> <!-- cd-filter-content -->
|
||||
</div> <!-- cd-filter-block -->
|
||||
</form>
|
||||
|
||||
<a href="#0" class="cd-close">Close</a>
|
||||
</div> <!-- cd-filter -->
|
||||
|
||||
<a href="#0" class="cd-filter-trigger">Filters</a>
|
||||
</main> <!-- cd-main-content -->
|
||||
<script src="js/jquery-2.1.1.js"></script>
|
||||
<script src="js/jquery.mixitup.min.js"></script>
|
||||
<script src="js/main.js"></script> <!-- Resource jQuery -->
|
||||
</body>
|
||||
</html>
|
62
autocart_assets/js/AppliedFilters.js
Normal file
62
autocart_assets/js/AppliedFilters.js
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
class AppliedFilters {
|
||||
constructor(dataStore, filterManager) {
|
||||
this.dataStore = dataStore;
|
||||
this.filterManager = filterManager;
|
||||
this.container = document.querySelector('#applied-filters-block');
|
||||
this.render();
|
||||
this.dataStore.subscribe(() => this.render());
|
||||
}
|
||||
|
||||
removeFilter(type, value) {
|
||||
this.dataStore.removeFilter(type, value);
|
||||
this.filterManager.removeFilter(type, value);
|
||||
}
|
||||
|
||||
render() {
|
||||
const appliedFilters = this.dataStore.getAppliedFilters();
|
||||
const chipSetContainer = document.createElement('div');
|
||||
chipSetContainer.classList.add('mdc-chip-set');
|
||||
|
||||
appliedFilters.forEach((filter) => {
|
||||
filter.values.forEach((fvalue) => {
|
||||
const id = `${filter.type}-${fvalue}`.replace(/[^a-zA-Z0-9-_]/g, '_');
|
||||
const chip = document.createElement('div');
|
||||
chip.innerHTML = `
|
||||
<div id="chip-${id}" class="mdc-chip">
|
||||
<div class="mdc-chip__ripple"></div>
|
||||
<span role="gridcell">
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text">
|
||||
<b>${filter.type.replace(/_/g, " ").replace(/\b\w/g, l => l.toUpperCase())}</b>: ${fvalue}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span role="gridcell">
|
||||
<svg class="mdc-chip__icon mdc-chip__icon--trailing filter-tags" id="close-${id}"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true" filter-tags>
|
||||
<path stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12">
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>`;
|
||||
chip.querySelector(`#close-${id}`).addEventListener('click', () => this.removeFilter(filter.type, fvalue));
|
||||
chipSetContainer.appendChild(chip);
|
||||
});
|
||||
});
|
||||
|
||||
// Clear existing content before appending new elements
|
||||
this.container.innerHTML = '';
|
||||
this.container.appendChild(chipSetContainer);
|
||||
|
||||
// Initialize MDC chips
|
||||
const chipSet = new mdc.chips.MDCChipSet(chipSetContainer);
|
||||
}
|
||||
|
||||
}
|
188
autocart_assets/js/AppointmentForm.js
Normal file
188
autocart_assets/js/AppointmentForm.js
Normal file
@@ -0,0 +1,188 @@
|
||||
class AppointmentForm {
|
||||
constructor(vehicle) {
|
||||
this.vehicle = vehicle;
|
||||
this.initForm();
|
||||
this.modalDiv = null;
|
||||
this.name = 'appointmentForm';
|
||||
}
|
||||
|
||||
initForm() {
|
||||
this.modalDiv = document.createElement('div');
|
||||
const inputIds = [
|
||||
{
|
||||
id: 'agreeToOffersCheckbox',
|
||||
name: 'agreeToOffersCheckbox',
|
||||
text: 'I agree to receive periodical offers, newsletter, safety and recall updates from the dealership. Consent can be withdrawn at any time.',
|
||||
},
|
||||
{
|
||||
id: 'privacyPolicyCheckbox',
|
||||
name: 'privacyPolicyCheckbox',
|
||||
text: 'By submitting this information, you are accepting that it may be collected, used and disclosed as described in our privacy policy.',
|
||||
},
|
||||
];
|
||||
let checkboxes = ``;
|
||||
inputIds.forEach((input) => {
|
||||
checkboxes += `
|
||||
<div class=" mdc-form-field flex flex-row flex-nowrap w-full my-2 lg:my-0 ${input.name} hover:text-red-500">
|
||||
<div class="mdc-checkbox mdc-theme-primary">
|
||||
<input type="checkbox" class="mdc-checkbox__native-control ${input.name}" id="${input.id}" />
|
||||
<div class="mdc-checkbox__background rounded-md">
|
||||
<svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
|
||||
<path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
|
||||
</svg>
|
||||
<div class="mdc-checkbox__mixedmark"></div>
|
||||
</div>
|
||||
<div class="mdc-checkbox__ripple p-2"></div>
|
||||
</div>
|
||||
<label for="${input.id}" class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400"> ${input.text}</label>
|
||||
</div>`;
|
||||
});
|
||||
this.modalDiv.innerHTML = `
|
||||
<form class="p-4 md:p-5">
|
||||
<div class="grid gap-4 grid-cols-2">
|
||||
<div class="col-span-2">
|
||||
<label for="name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Name</label>
|
||||
<input type="text" name="name" id="name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" placeholder="Jane Doe" required="">
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Email</label>
|
||||
<input type="email" name="email" id="email" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" placeholder="jane@gmail.com" required="">
|
||||
</div>
|
||||
<div class="col-span-2 ">
|
||||
<label for="phone" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Phone</label>
|
||||
<input type="tel" name="phone" id="phone" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" placeholder="(123) 456-7890" required="">
|
||||
</div>
|
||||
<div class="col-span-2 ">
|
||||
<label for="preferred-contact" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Preferred Contact Method</label>
|
||||
<select id="preferred-contact" name="preferred-contact" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500">
|
||||
<option selected="">---</option>
|
||||
<option value="Call">Call</option>
|
||||
<option value="Email">Email</option>
|
||||
<option value="Text">Text</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label for="message" name="message" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Product Description</label>
|
||||
<textarea id="message" rows="4" class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="I am interested in..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
${checkboxes}
|
||||
|
||||
<button class="w-full bg-gray-500 hover:bg-gray-800 text-white p-2 rounded-md transition duration-300" id="send-button">Send</button>
|
||||
|
||||
</form>
|
||||
`;
|
||||
|
||||
this.checkboxes = this.modalDiv.querySelectorAll('.mdc-checkbox');
|
||||
if (this.checkboxes) {
|
||||
this.checkboxes.forEach((checkbox, index) => {
|
||||
// Initialize the MDCCheckbox with the actual checkbox element
|
||||
const checkboxElement = checkbox.querySelector(
|
||||
`.mdc-checkbox__native-control.${inputIds[index].name}`
|
||||
);
|
||||
const checkboxInstance = new mdc.checkbox.MDCCheckbox(checkbox);
|
||||
|
||||
// If the checkbox is found, initialize the form field as well
|
||||
if (checkboxElement) {
|
||||
const formFieldInstance = new mdc.formField.MDCFormField(
|
||||
this.modalDiv.querySelector(
|
||||
`.mdc-form-field.${inputIds[index].name}`
|
||||
)
|
||||
);
|
||||
formFieldInstance.input = checkboxElement;
|
||||
} else {
|
||||
console.error('Checkbox element not found:', inputIds[index].id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('Checkboxes not found');
|
||||
}
|
||||
|
||||
this.initializeCheckboxes(this.modalDiv, inputIds);
|
||||
|
||||
this.initializeFormEventListeners(this.modalDiv);
|
||||
|
||||
return this.modalDiv;
|
||||
}
|
||||
|
||||
initializeFormEventListeners(modalDiv) {
|
||||
const formInputs = [
|
||||
'name',
|
||||
'email',
|
||||
'phone',
|
||||
'preferred-contact',
|
||||
'message',
|
||||
'agreeToOffersCheckbox',
|
||||
'privacyPolicyCheckbox',
|
||||
];
|
||||
|
||||
formInputs.forEach((id) => {
|
||||
const inputElement = modalDiv.querySelector(`#${id}`);
|
||||
if (inputElement) {
|
||||
inputElement.addEventListener('input', () => {
|
||||
this.saveFormValues(); // Save form values whenever an input changes
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initializeFormInputs() {
|
||||
const storedValues =
|
||||
JSON.parse(localStorage.getItem(`autocart_${this.name}`)) || {};
|
||||
for (const key in storedValues) {
|
||||
const inputElement = document.getElementById(key);
|
||||
if (inputElement) {
|
||||
inputElement.value = storedValues[key];
|
||||
inputElement.dispatchEvent(new Event('input')); // Dispatch input event
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializeCheckboxes(modalDiv, inputIds) {
|
||||
this.checkboxes = modalDiv.querySelectorAll('.mdc-checkbox');
|
||||
if (this.checkboxes) {
|
||||
this.checkboxes.forEach((checkbox, index) => {
|
||||
// Initialize the MDCCheckbox with the actual checkbox element
|
||||
const checkboxElement = checkbox.querySelector(
|
||||
`.mdc-checkbox__native-control.${inputIds[index].name}`
|
||||
);
|
||||
const checkboxInstance = new mdc.checkbox.MDCCheckbox(checkbox);
|
||||
|
||||
// If the checkbox is found, initialize the form field as well
|
||||
if (checkboxElement) {
|
||||
const formFieldInstance = new mdc.formField.MDCFormField(
|
||||
modalDiv.querySelector(`.mdc-form-field.${inputIds[index].name}`)
|
||||
);
|
||||
formFieldInstance.input = checkboxElement;
|
||||
} else {
|
||||
console.error('Checkbox element not found:', inputIds[index].id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('Checkboxes not found');
|
||||
}
|
||||
}
|
||||
|
||||
saveFormValues() {
|
||||
const formInputs = [
|
||||
'name',
|
||||
'email',
|
||||
'phone',
|
||||
'preferred-contact',
|
||||
'message',
|
||||
'agreeToOffersCheckbox',
|
||||
'privacyPolicyCheckbox',
|
||||
];
|
||||
const formValues = {};
|
||||
|
||||
formInputs.forEach((input) => {
|
||||
const inputElement = document.getElementById(input);
|
||||
if (inputElement) {
|
||||
formValues[input] = inputElement.value;
|
||||
}
|
||||
});
|
||||
|
||||
localStorage.setItem(`autocart_${this.name}`, JSON.stringify(formValues));
|
||||
}
|
||||
}
|
||||
|
102
autocart_assets/js/BodyTypeFilter.js
Normal file
102
autocart_assets/js/BodyTypeFilter.js
Normal file
@@ -0,0 +1,102 @@
|
||||
class BodyTypeFilter {
|
||||
constructor(bodyType, dataStore) {
|
||||
this.value = bodyType.text;
|
||||
this.count = bodyType.count;
|
||||
this.dataStore = dataStore;
|
||||
this.checkbox = null;
|
||||
this.checked = bodyType.checked;
|
||||
this.initCheckbox();
|
||||
|
||||
|
||||
}
|
||||
|
||||
initCheckbox() {
|
||||
const makeListBlock = document.createElement('div');
|
||||
const dynamicId = `autocart_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
const dynamicClass = `autocart_term_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
|
||||
makeListBlock.innerHTML = `
|
||||
|
||||
|
||||
|
||||
<li class="w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<div class="flex items-center mdc-form-field flex-wrap justify-between items-start flex-row w-full ${dynamicClass} hover:text-red-500">
|
||||
<label for="${dynamicId}" class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">${this.value}</label>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
(${this.count})
|
||||
</span>
|
||||
</div>
|
||||
<div class="mdc-checkbox mdc-theme-primary">
|
||||
<input type="checkbox" class="mdc-checkbox__native-control ${dynamicClass}" id="${dynamicId}" />
|
||||
<div class="mdc-checkbox__background rounded-md">
|
||||
<svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
|
||||
<path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
|
||||
</svg>
|
||||
<div class="mdc-checkbox__mixedmark"></div>
|
||||
</div>
|
||||
<div class="mdc-checkbox__ripple p-2"></div>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
|
||||
this.checkbox = makeListBlock.querySelector('.mdc-checkbox');
|
||||
|
||||
if (this.checkbox) {
|
||||
// Initialize the MDCCheckbox with the actual checkbox element
|
||||
const checkboxElement = this.checkbox.querySelector(`.mdc-checkbox__native-control.${dynamicClass}`);
|
||||
this.checkbox = new mdc.checkbox.MDCCheckbox(this.checkbox);
|
||||
this.formField = new mdc.formField.MDCFormField(makeListBlock.querySelector(`.mdc-form-field.${dynamicClass}`));
|
||||
this.formField.input = checkboxElement;
|
||||
this.checkbox.listen('change', () => this.handleCheckboxChange());
|
||||
} else {
|
||||
console.error('Checkbox element not found:', dynamicId);
|
||||
}
|
||||
this.markCheckboxIfValueFiltered()
|
||||
|
||||
return makeListBlock;
|
||||
}
|
||||
|
||||
markCheckboxIfValueFiltered() {
|
||||
let ifAlreadyFiltered = this.dataStore.getAppliedFilters().find(filter=>filter.type == "body_type");
|
||||
if(ifAlreadyFiltered){
|
||||
if(ifAlreadyFiltered.values.includes(this.value)){
|
||||
this.checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxChange() {
|
||||
shouldUpdateFilters = false;
|
||||
const isChecked = this.checkbox.checked;
|
||||
if (isChecked) {
|
||||
this.dataStore.addFilter('body_type', this.value);
|
||||
} else {
|
||||
this.dataStore.removeFilter('body_type', this.value);
|
||||
}
|
||||
|
||||
this.checked = isChecked;
|
||||
|
||||
createTransmissionList(this.dataStore.filterVehicles());
|
||||
createExteriorList(this.dataStore.filterVehicles());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
setParentFilter(value){
|
||||
this.isParentSelected = value;
|
||||
this.generateCardHTML();
|
||||
}
|
||||
|
||||
uncheckCheckbox() {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.checked = false;
|
||||
this.handleCheckboxChange();
|
||||
// this.checkbox.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
86
autocart_assets/js/DataStore.js
Normal file
86
autocart_assets/js/DataStore.js
Normal file
@@ -0,0 +1,86 @@
|
||||
class DataStore {
|
||||
constructor(vehicles) {
|
||||
this.vehicles = vehicles;
|
||||
this.subscribers = [];
|
||||
this.filters = {};
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
subscribe(subscriber) {
|
||||
this.subscribers.push(subscriber);
|
||||
}
|
||||
|
||||
unsubscribe(subscriber) {
|
||||
this.subscribers = this.subscribers.filter((s) => s !== subscriber);
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
this.subscribers = [];
|
||||
}
|
||||
|
||||
notifySubscribers(filteredVehicles = this.vehicles) {
|
||||
const activeFilters = Object.keys(this.filters);
|
||||
if (activeFilters.length > 0) {
|
||||
filteredVehicles = this.filterVehicles(filteredVehicles);
|
||||
}
|
||||
|
||||
this.subscribers.forEach((subscriber) => {
|
||||
subscriber(filteredVehicles);
|
||||
});
|
||||
}
|
||||
|
||||
addFilter(type, value) {
|
||||
this.filters[type] = this.filters[type] || [];
|
||||
this.filters[type].push(value);
|
||||
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
removeFilter(type, value) {
|
||||
if (this.filters[type]) {
|
||||
this.filters[type] = this.filters[type].filter((filter) => filter !== value);
|
||||
if (this.filters[type].length === 0) {
|
||||
delete this.filters[type];
|
||||
}
|
||||
|
||||
this.notifySubscribers();
|
||||
}
|
||||
}
|
||||
|
||||
filterVehicles(vehicles = this.vehicles) {
|
||||
return vehicles.filter((vehicle) => {
|
||||
for (const [type, values] of Object.entries(this.filters)) {
|
||||
if(Array.isArray(vehicle[type])){
|
||||
if(vehicle[type].some(value => values.includes(value))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!values.includes(vehicle[type])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
getAppliedFilters() {
|
||||
return Object.entries(this.filters).map(([type, values]) => ({ type, values }));
|
||||
|
||||
}
|
||||
|
||||
sortBy(property, order) {
|
||||
this.vehicles.sort((a, b) => {
|
||||
const aValue = a[property];
|
||||
const bValue = b[property];
|
||||
|
||||
if (order === 'asc') {
|
||||
return aValue > bValue ? 1 : -1;
|
||||
} else {
|
||||
return bValue > aValue ? 1 : -1;
|
||||
}
|
||||
});
|
||||
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
}
|
50
autocart_assets/js/DisplayMode.js
Normal file
50
autocart_assets/js/DisplayMode.js
Normal file
@@ -0,0 +1,50 @@
|
||||
class DisplayMode {
|
||||
constructor(containerId) {
|
||||
this.containerId = containerId || '.display-products .row';
|
||||
this.container = document.querySelector(this.containerId);
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const gridButton = document.getElementById('grid-view');
|
||||
const listButton = document.getElementById('list-view');
|
||||
|
||||
gridButton.addEventListener('click', () => this.toggleView('grid'));
|
||||
listButton.addEventListener('click', () => this.toggleView('list'));
|
||||
}
|
||||
|
||||
toggleView(viewMode) {
|
||||
const rowElement = document.querySelector('.vehicles-container');
|
||||
|
||||
// Remove 'selected' class from both buttons
|
||||
document.getElementById('grid-view').classList.remove('selected');
|
||||
document.getElementById('list-view').classList.remove('selected');
|
||||
|
||||
// Add the appropriate classes based on the clicked button
|
||||
if (viewMode === 'grid') {
|
||||
rowElement.classList.remove('flex', 'flex-col', 'items-stretch');
|
||||
rowElement.classList.add('grid', 'md:grid-cols-2', 'lg:grid-cols-3', 'transition-all', 'duration-500');
|
||||
document.getElementById('grid-view').classList.add('selected');
|
||||
} else if (viewMode === 'list') {
|
||||
rowElement.classList.remove('grid', 'md:grid-cols-2', 'lg:grid-cols-3');
|
||||
rowElement.classList.add('flex', 'flex-col', 'items-stretch', 'transition-all', 'duration-500');
|
||||
document.getElementById('list-view').classList.add('selected');
|
||||
}
|
||||
|
||||
// Update individual cards based on viewMode
|
||||
const cards = rowElement.querySelectorAll('.vehicle-card');
|
||||
cards.forEach(card => {
|
||||
const vehicleImage = card.querySelector('.vehicle-image');
|
||||
vehicleImage.classList.remove('w-1/4', 'w-full');
|
||||
if (viewMode === 'list') {
|
||||
card.classList.add('flex', 'flex-row' ,'justify-start', 'items-stretch', 'gap-3')
|
||||
// flex flex-row justify-start gap-3
|
||||
vehicleImage.classList.add('w-1/4');
|
||||
} else {
|
||||
card.classList.remove('flex', 'flex-row' ,'justify-start', 'items-stretch', 'gap-3')
|
||||
|
||||
vehicleImage.classList.add('w-full');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
104
autocart_assets/js/ExteriorFilter.js
Normal file
104
autocart_assets/js/ExteriorFilter.js
Normal file
@@ -0,0 +1,104 @@
|
||||
|
||||
|
||||
class ExteriorFilter {
|
||||
constructor(exterior, dataStore) {
|
||||
this.value = exterior.text;
|
||||
this.count = exterior.count;
|
||||
this.dataStore = dataStore;
|
||||
this.checkbox = null;
|
||||
this.checked = exterior.checked;
|
||||
this.initCheckbox();
|
||||
|
||||
|
||||
}
|
||||
|
||||
initCheckbox() {
|
||||
const makeListBlock = document.createElement('div');
|
||||
const dynamicId = `autocart_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
const dynamicClass = `autocart_term_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
|
||||
makeListBlock.innerHTML = `
|
||||
|
||||
|
||||
|
||||
<li class="w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<div class="flex items-center mdc-form-field flex-wrap justify-between items-start flex-row w-full ${dynamicClass} hover:text-red-500">
|
||||
<label for="${dynamicId}" class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">${this.value}</label>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
(${this.count})
|
||||
</span>
|
||||
</div>
|
||||
<div class="mdc-checkbox mdc-theme-primary">
|
||||
<input type="checkbox" class="mdc-checkbox__native-control ${dynamicClass}" id="${dynamicId}" />
|
||||
<div class="mdc-checkbox__background rounded-md">
|
||||
<svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
|
||||
<path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
|
||||
</svg>
|
||||
<div class="mdc-checkbox__mixedmark"></div>
|
||||
</div>
|
||||
<div class="mdc-checkbox__ripple p-2"></div>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
|
||||
this.checkbox = makeListBlock.querySelector('.mdc-checkbox');
|
||||
|
||||
if (this.checkbox) {
|
||||
// Initialize the MDCCheckbox with the actual checkbox element
|
||||
const checkboxElement = this.checkbox.querySelector(`.mdc-checkbox__native-control.${dynamicClass}`);
|
||||
this.checkbox = new mdc.checkbox.MDCCheckbox(this.checkbox);
|
||||
this.formField = new mdc.formField.MDCFormField(makeListBlock.querySelector(`.mdc-form-field.${dynamicClass}`));
|
||||
this.formField.input = checkboxElement;
|
||||
this.checkbox.listen('change', () => this.handleCheckboxChange());
|
||||
} else {
|
||||
console.error('Checkbox element not found:', dynamicId);
|
||||
}
|
||||
|
||||
this.markCheckboxIfValueFiltered()
|
||||
|
||||
|
||||
return makeListBlock;
|
||||
}
|
||||
|
||||
markCheckboxIfValueFiltered() {
|
||||
let ifAlreadyFiltered = this.dataStore.getAppliedFilters().find(filter=>filter.type == "exterior_color");
|
||||
if(ifAlreadyFiltered){
|
||||
if(ifAlreadyFiltered.values.includes(this.value)){
|
||||
this.checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxChange() {
|
||||
shouldUpdateFilters = false;
|
||||
const isChecked = this.checkbox.checked;
|
||||
if (isChecked) {
|
||||
this.dataStore.addFilter('exterior_color', this.value);
|
||||
} else {
|
||||
this.dataStore.removeFilter('exterior_color', this.value);
|
||||
}
|
||||
|
||||
this.checked = isChecked;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
setParentFilter(value){
|
||||
this.isParentSelected = value;
|
||||
this.generateCardHTML();
|
||||
}
|
||||
|
||||
uncheckCheckbox() {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.checked = false;
|
||||
// this.checkbox.dispatchEvent(new Event('change'));
|
||||
this.handleCheckboxChange();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
35
autocart_assets/js/Filter.js
Normal file
35
autocart_assets/js/Filter.js
Normal file
@@ -0,0 +1,35 @@
|
||||
class Filter {
|
||||
constructor(type, value, dataStore, children = []) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
this.dataStore = dataStore;
|
||||
this.children = children;
|
||||
this.checkbox = null;
|
||||
this.initCheckbox();
|
||||
}
|
||||
|
||||
initCheckbox() {
|
||||
// Initialize the checkbox and handle change events
|
||||
this.checkbox.addEventListener('change', () => this.handleCheckboxChange());
|
||||
}
|
||||
|
||||
handleCheckboxChange() {
|
||||
if (this.checkbox.checked) {
|
||||
this.dataStore.addFilter(this);
|
||||
} else {
|
||||
this.dataStore.removeFilter(this);
|
||||
}
|
||||
|
||||
// Update UI or perform other actions based on the change
|
||||
}
|
||||
|
||||
generateCardHTML() {
|
||||
// Default implementation for generating HTML
|
||||
return `
|
||||
<li class="filter-item">
|
||||
<span>${this.type}: ${this.value}</span>
|
||||
<!-- Other HTML elements for the filter -->
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
}
|
152
autocart_assets/js/FilterManager.js
Normal file
152
autocart_assets/js/FilterManager.js
Normal file
@@ -0,0 +1,152 @@
|
||||
class FilterManager {
|
||||
constructor(dataStore) {
|
||||
this.dataStore = dataStore;
|
||||
this.makeFilters = [];
|
||||
this.modelFilters = [];
|
||||
this.trimFilters = [];
|
||||
this.bodyTypeFilters = [];
|
||||
this.transmissionFilters = [];
|
||||
this.exteriorFilters = [];
|
||||
this.subscribers = [];
|
||||
|
||||
}
|
||||
|
||||
createMakeFilter(make) {
|
||||
const makeFilter = new MakeFilter(make, this.dataStore);
|
||||
this.makeFilters.push(makeFilter);
|
||||
this.notifySubscribers();
|
||||
return makeFilter;
|
||||
}
|
||||
|
||||
createModelFilter(model) {
|
||||
const modelFilter = new ModelFilter(model, this.dataStore);
|
||||
this.modelFilters.push(modelFilter);
|
||||
this.notifySubscribers();
|
||||
return modelFilter;
|
||||
}
|
||||
|
||||
createTrimFilter(trim) {
|
||||
const trimFilter = new TrimFilter(trim, this.dataStore);
|
||||
this.trimFilters.push(trimFilter);
|
||||
this.notifySubscribers();
|
||||
return trimFilter;
|
||||
}
|
||||
|
||||
createTransmissionFilter(transmission) {
|
||||
const transmissionFilter = new TransmissionFilter(transmission, this.dataStore);
|
||||
this.transmissionFilters.push(transmissionFilter);
|
||||
this.notifySubscribers();
|
||||
return transmissionFilter;
|
||||
}
|
||||
|
||||
|
||||
createExteriorFilter(exterior) {
|
||||
const exteriorFilter = new ExteriorFilter(exterior, this.dataStore);
|
||||
this.exteriorFilters.push(exteriorFilter);
|
||||
this.notifySubscribers();
|
||||
return exteriorFilter;
|
||||
}
|
||||
|
||||
createBodyTypeFilter(bodyType) {
|
||||
const bodyTypeFilter = new BodyTypeFilter(bodyType, this.dataStore);
|
||||
this.bodyTypeFilters.push(bodyTypeFilter);
|
||||
this.notifySubscribers();
|
||||
return bodyTypeFilter;
|
||||
}
|
||||
|
||||
removeFilter(filterType, filterValue) {
|
||||
this.dataStore.removeFilter(filterType, filterValue);
|
||||
|
||||
if (filterType === 'make') {
|
||||
const makeFilter = this.makeFilters.find(filter => filter.value === filterValue);
|
||||
if (makeFilter) {
|
||||
makeFilter.uncheckCheckbox();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (filterType === 'model') {
|
||||
const modelFilter = this.modelFilters.find(filter => filter.value === filterValue);
|
||||
if (modelFilter) {
|
||||
modelFilter.uncheckCheckbox();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (filterType === 'trim') {
|
||||
// because it came from response as an array
|
||||
this.trimFilters.filter(filter => filter.value === filterValue).forEach(trimFilter=>{
|
||||
if (trimFilter) {
|
||||
trimFilter.uncheckCheckbox();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (filterType === 'body_type') {
|
||||
const bodyType = this.bodyTypeFilters.find(filter => filter.value === filterValue);
|
||||
if (bodyType) {
|
||||
bodyType.uncheckCheckbox();
|
||||
}
|
||||
}
|
||||
|
||||
if (filterType === 'transmission_type') {
|
||||
const transmission = this.transmissionFilters.find(filter => filter.value === filterValue);
|
||||
if (transmission) {
|
||||
transmission.uncheckCheckbox();
|
||||
}
|
||||
}
|
||||
|
||||
if (filterType === 'exterior_color') {
|
||||
const exterior = this.exteriorFilters.find(filter => filter.value === filterValue);
|
||||
if (exterior) {
|
||||
exterior.uncheckCheckbox();
|
||||
}
|
||||
}
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
resetList(type) {
|
||||
|
||||
if (type === 'make') {
|
||||
this.makeFilters = [];
|
||||
}
|
||||
|
||||
if (type === 'model') {
|
||||
this.modelFilters = [];
|
||||
|
||||
}
|
||||
|
||||
if (type === 'trim') {
|
||||
this.trimFilters = [];
|
||||
}
|
||||
|
||||
if (type === 'body_type') {
|
||||
this.bodyTypeFilters = [];
|
||||
}
|
||||
|
||||
if (type === 'transmission_type') {
|
||||
this.transmissionFilters = [];
|
||||
}
|
||||
|
||||
if (type === 'exterior_color') {
|
||||
this.exteriorFilters = [];
|
||||
}
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
notifySubscribers() {
|
||||
this.subscribers.forEach(subscriber => subscriber({
|
||||
makeFilters: this.makeFilters,
|
||||
modelFilters: this.modelFilters,
|
||||
trimFilters: this.trimFilters
|
||||
}));
|
||||
}
|
||||
|
||||
subscribe(subscriber) {
|
||||
this.subscribers.push(subscriber);
|
||||
}
|
||||
|
||||
unsubscribe(subscriber) {
|
||||
this.subscribers = this.subscribers.filter(sub => sub !== subscriber);
|
||||
}
|
||||
}
|
498
autocart_assets/js/FinanceForm.js
Normal file
498
autocart_assets/js/FinanceForm.js
Normal file
@@ -0,0 +1,498 @@
|
||||
class FinanceForm {
|
||||
constructor(vehicle, notifyParentCallback) {
|
||||
this.notifyParentCallback = notifyParentCallback;
|
||||
this.modalDiv = null;
|
||||
this.name = 'financeForm';
|
||||
|
||||
this.frequencyMap = {
|
||||
12: 'Monthly',
|
||||
26: 'Bi-Weekly',
|
||||
52: 'Weekly',
|
||||
};
|
||||
|
||||
this.vehicle = vehicle;
|
||||
|
||||
// Default values
|
||||
this.loanTerm = 12;
|
||||
this.intRate = 7.99;
|
||||
this.downPayment = 0;
|
||||
this.tradeValue = 0;
|
||||
this.paymentFrequency = 26;
|
||||
|
||||
// Check if values exist in localStorage, and if so, override default values
|
||||
const storedValues =
|
||||
JSON.parse(localStorage.getItem(`autocart_${this.name}`)) || {};
|
||||
if (storedValues) {
|
||||
this.loanTerm = parseInt(storedValues.loanTerm) || this.loanTerm;
|
||||
this.intRate = parseFloat(storedValues.customRateValue) || this.intRate;
|
||||
this.downPayment = parseInt(storedValues.downPayment) || this.downPayment;
|
||||
this.tradeValue = parseInt(storedValues.tradeValue) || this.tradeValue;
|
||||
this.paymentFrequency =
|
||||
parseInt(storedValues.paymentFrequency) || this.paymentFrequency;
|
||||
}
|
||||
|
||||
// Calculate other values based on the provided vehicle and overridden inputs
|
||||
let amount = parseFloat(this.vehicle.advertise_price) || 0;
|
||||
let months = parseFloat(this.loanTerm) || 0;
|
||||
let down = parseFloat(this.downPayment) || 0;
|
||||
let trade = parseFloat(this.tradeValue) || 0;
|
||||
let totalDown = down + trade;
|
||||
let annInterest = parseFloat(this.intRate) || 0;
|
||||
let monInt = annInterest / 1200;
|
||||
let financeTotal = amount - totalDown;
|
||||
let numberOfPayments = months / (12 / this.paymentFrequency);
|
||||
|
||||
// Fix calculation for total cost of credit
|
||||
let interest = financeTotal * monInt * numberOfPayments;
|
||||
this.totalCostOfCredit = interest.toFixed(2);
|
||||
|
||||
this.totalObligation = (
|
||||
financeTotal + parseFloat(this.totalCostOfCredit)
|
||||
).toFixed(2);
|
||||
|
||||
this.payment =
|
||||
(
|
||||
(monInt + monInt / (Math.pow(1 + monInt, months) - 1)) *
|
||||
(amount - (totalDown || 0))
|
||||
).toFixed(2) /
|
||||
(this.paymentFrequency / 12);
|
||||
|
||||
// Notify parent callback with the updated values
|
||||
this.notifyParentCallback(this);
|
||||
|
||||
// Initialize the form
|
||||
this.initForm();
|
||||
// this.initializeFormInputs();
|
||||
}
|
||||
|
||||
initForm() {
|
||||
this.modalDiv = document.createElement('div');
|
||||
|
||||
// const modalDiv = document.createElement('div');
|
||||
this.modalDiv.innerHTML = `
|
||||
<div class="bg-white rounded-md grid lg:grid-cols-6 gap-4">
|
||||
<div class="lg:col-span-4">
|
||||
|
||||
<!-- Tab Navigation -->
|
||||
<ul id="tabList" class="grid lg:grid-cols-2 text-sm font-medium text-center text-gray-500 dark:text-gray-400 mb-6 w-full list-none">
|
||||
<li class="flex-1">
|
||||
<a href="#" class="tab-link inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 w-full active bg-gray-600 text-white" data-tab="tab1">Personalize Your Options</a>
|
||||
</li>
|
||||
<li class="flex-1">
|
||||
<a href="#" class="tab-link inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-800 dark:hover:text-white w-full" data-tab="tab2">Apply For Financing</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div id="tab-viewer" class="gap-6">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-content" data-content="tab1">
|
||||
<h4 class="text-xl mb-2">Let’s Structure a Deal That Works For You</h4>
|
||||
<p class="mb-4">
|
||||
We understand that purchasing a vehicle is a big decision, so we want you to be paying a price that you’re comfortable with.
|
||||
<strong>Let’s structure a deal that works for you below:</strong>
|
||||
</p>
|
||||
|
||||
<span class="inline-block p-4 bg-gray-200 border rounded-t-lg">
|
||||
<h5 class="text-lg font-bold">Finance Option</h5>
|
||||
<p class="mb-2">
|
||||
<strong class="paymentSpan">${this.payment}</strong> / <span class="paymentFrequencySpan">${this.frequencyMap[this.paymentFrequency]}</span>
|
||||
</p>
|
||||
<p class="text-sm"><span class="rateSpan">${this.intRate}</span>% APR for <span class="loanTermSpan">${this.loanTerm}</span> Months</p>
|
||||
</span>
|
||||
|
||||
<form class="p-4 md:p-5 bg-gray-200 rounded-r-lg rounded-b-lg">
|
||||
<div class="grid gap-4 mb-4 lg:grid-cols-2">
|
||||
<div>
|
||||
<label for="downPayment" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Down Payment</label>
|
||||
<div class="flex">
|
||||
<span class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border rounded-e-0 border-gray-300 rounded-s-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600 rounded-l-lg">
|
||||
<i class="fas fa-dollar-sign"></i>
|
||||
</span>
|
||||
<input type="number" value="0" id="downPayment" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-r-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="tradeValue" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Vehicle Trade-in</label>
|
||||
<div class="flex">
|
||||
<span class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border rounded-e-0 border-gray-300 rounded-s-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600 rounded-l-lg">
|
||||
<i class="fas fa-dollar-sign"></i>
|
||||
</span>
|
||||
<input type="number" id="tradeValue" value="0" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-r-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="loanTerm" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Loan Term</label>
|
||||
<select name="loanTerm" id="loanTerm" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" required="">
|
||||
<option value="12" selected>12 Month Term</option>
|
||||
<option value="24">24 Month Term</option>
|
||||
<option value="36">36 Month Term</option>
|
||||
<option value="48">48 Month Term</option>
|
||||
<option value="60">60 Month Term</option>
|
||||
<option value="72">72 Month Term</option>
|
||||
<option value="84">84 Month Term</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="paymentFrequency" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Payment Frequency</label>
|
||||
<select id="paymentFrequency" name="paymentFrequency" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500">
|
||||
<option value="12">Monthly</option>
|
||||
<option value="26" selected>Bi-Weekly</option>
|
||||
<option value="52">Weekly</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="">
|
||||
<label for="rate" name="rate" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Program Rates (APR)</label>
|
||||
<select id="rate" name="rate" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 xl:w-96">
|
||||
<option value="default-rate">Financing [Dealer Rate] (7.99%)</option>
|
||||
<option value="custom-rate">Set Custom Rate</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="text-gray-500 ml-2 text-3xl p-5" id="default-rate">
|
||||
7.99%
|
||||
</div>
|
||||
<div class="hidden" id="custom-rate">
|
||||
<label for="customRateValue" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Custom Rate</label>
|
||||
<div class="flex">
|
||||
<span class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border rounded-e-0 border-gray-300 rounded-s-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600 rounded-l-lg">
|
||||
<i class="fas fa-percentage"></i>
|
||||
</span>
|
||||
<input type="number" id="customRateValue" value="5.99" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-r-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-content hidden" data-content="tab2">
|
||||
<h4 class="text-xl mb-2">Let’s Structure a Deal That Works For You</h4>
|
||||
|
||||
<p class="mb-4">
|
||||
We understand that purchasing a vehicle is a big decision, so we want you to be paying a price that you’re comfortable with.
|
||||
<strong>Let’s structure a deal that works for you below:</strong>
|
||||
</p>
|
||||
<div id="appointmentFormContainer">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:col-span-2 p-6 bg-gray-200 rounded-md">
|
||||
<div class="col ">
|
||||
<div class="mb-4">
|
||||
<h4 class="text-lg font-bold">Vehicle Price</h4>
|
||||
<ul class="transmission-container list-none text-gray-900 w-48 text-sm font-medium rounded-lg w-full">
|
||||
<li class="w-full flex items-center flex-wrap justify-between items-start flex-row w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">Vehicle Price</span>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
${this.vehicle.advertise_price.toLocaleString('en-CA', { style: 'currency', currency: 'CAD', minimumFractionDigits: 0, maximumFractionDigits: 0 })}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr class="my-4 border-t border-gray-300">
|
||||
|
||||
<div class="mb-4">
|
||||
<ul class="transmission-container list-none text-gray-900 w-48 text-sm font-medium rounded-lg w-full">
|
||||
<li class="w-full flex items-center flex-wrap justify-between items-start flex-row w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">Finance Total</span>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
${this.vehicle.advertise_price.toLocaleString('en-CA', { style: 'currency', currency: 'CAD', minimumFractionDigits: 0, maximumFractionDigits: 0 })}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<h4 class="text-lg font-bold">Finance Payment</h4>
|
||||
<ul class="transmission-container list-none text-gray-900 w-48 text-sm font-medium rounded-lg w-full">
|
||||
<li class="w-full flex items-center flex-wrap justify-between items-start flex-row w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">Loan Term</span>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base ">
|
||||
<span class="loanTermSpan">${this.loanTerm} </span> Months
|
||||
</span>
|
||||
</li>
|
||||
<li class="w-full flex items-center flex-wrap justify-between items-start flex-row w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">Program Rate</span>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
<span class="rateSpan">7.99</span>%
|
||||
</span>
|
||||
</li>
|
||||
<li class="w-full flex items-center flex-wrap justify-between items-start flex-row w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">Frequency</span>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base paymentFrequencySpan">
|
||||
${this.frequencyMap[this.paymentFrequency]}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<h4 class="text-lg font-bold">Total</h4>
|
||||
<ul class="transmission-container list-none text-gray-900 w-48 text-sm font-medium rounded-lg w-full">
|
||||
<li class="w-full flex items-center flex-wrap justify-between items-start flex-row w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">Total Cost of Credit</span>
|
||||
<span class="autocart_count text-gray-500 ml-2 tran∂sition duration-400 font-normal text-base totalCostOfCreditSpan">
|
||||
${this.totalCostOfCredit.toLocaleString('en-CA', { style: 'currency', currency: 'CAD', minimumFractionDigits: 0, maximumFractionDigits: 0 })}
|
||||
</span>
|
||||
</li>
|
||||
<li class="w-full flex items-center flex-wrap justify-between items-start flex-row w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">Total Obligation</span>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base totalObligationSpan">
|
||||
${this.totalObligation.toLocaleString('en-CA', { style: 'currency', currency: 'CAD', minimumFractionDigits: 0, maximumFractionDigits: 0 })}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mb-4 items-center flex flex-col">
|
||||
<div class="py-2 inline-block transition duration-300">
|
||||
<p>
|
||||
<span class="paymentSpan">${(Number(this.payment) || 0).toLocaleString('en-CA', { style: 'currency', currency: 'CAD' })}</span>
|
||||
/ <span class="paymentFrequencySpan">
|
||||
${this.frequencyMap[this.paymentFrequency]}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<button class="w-full bg-gray-500 hover:bg-gray-800 text-white p-2 rounded-md transition duration-300" id="next-step">next step</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const formContainer = this.modalDiv.querySelector(
|
||||
'#appointmentFormContainer'
|
||||
);
|
||||
formContainer.appendChild(appointmentForm.initForm());
|
||||
|
||||
this.initializeTabNavigation(this.modalDiv);
|
||||
this.initializeFormEventListeners(this.modalDiv);
|
||||
return this.modalDiv;
|
||||
}
|
||||
|
||||
// initializeFormInputs() {
|
||||
// const storedValues = JSON.parse(localStorage.getItem(`autocart_${this.name}`)) || {};
|
||||
// // const mergedValues = { ...this.defaultValues, ...storedValues };
|
||||
// console.log(storedValues);
|
||||
// for (const key in storedValues) {
|
||||
// console.log(key);
|
||||
|
||||
// const inputElement = document.getElementById(key);
|
||||
// if (inputElement) {
|
||||
// inputElement.value = storedValues[key];
|
||||
// }
|
||||
// }
|
||||
// this.updateView()
|
||||
// }
|
||||
|
||||
initializeFormInputs() {
|
||||
const storedValues =
|
||||
JSON.parse(localStorage.getItem(`autocart_${this.name}`)) || {};
|
||||
for (const key in storedValues) {
|
||||
const inputElement = document.getElementById(key);
|
||||
if (inputElement) {
|
||||
inputElement.value = storedValues[key];
|
||||
inputElement.dispatchEvent(new Event('input')); // Dispatch input event
|
||||
}
|
||||
}
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
calculatePayments() {
|
||||
var vehiclePrice = this.vehicle.advertise_price;
|
||||
this.loanTerm = document.getElementById('loanTerm').value;
|
||||
this.paymentFrequency = document.getElementById('paymentFrequency').value;
|
||||
this.intRate =
|
||||
document.getElementById('rate').value == 'default-rate'
|
||||
? 7.99
|
||||
: document.getElementById('customRateValue').value;
|
||||
this.downPayment = document.getElementById('downPayment').value;
|
||||
this.tradeValue = document.getElementById('tradeValue').value;
|
||||
var amount = parseFloat(vehiclePrice) || 0,
|
||||
months = parseFloat(this.loanTerm) || 0,
|
||||
down = parseFloat(this.downPayment) || 0,
|
||||
trade = parseFloat(this.tradeValue) || 0,
|
||||
totalDown = down + trade,
|
||||
annInterest = parseFloat(this.intRate) || 0,
|
||||
monInt = annInterest / 1200;
|
||||
var financeTotal = amount - totalDown;
|
||||
var numberOfPayments = months / (12 / this.paymentFrequency);
|
||||
|
||||
var monthlyPayment =
|
||||
(
|
||||
(monInt + monInt / (Math.pow(1 + monInt, months) - 1)) *
|
||||
(amount - (totalDown || 0))
|
||||
).toFixed(2) /
|
||||
(this.paymentFrequency / 12);
|
||||
this.totalCostOfCredit = (
|
||||
monthlyPayment * numberOfPayments -
|
||||
financeTotal
|
||||
).toFixed(2);
|
||||
this.totalObligation = (
|
||||
parseFloat(this.totalCostOfCredit) + parseFloat(financeTotal)
|
||||
).toFixed(2);
|
||||
this.payment =
|
||||
(
|
||||
(monInt + monInt / (Math.pow(1 + monInt, months) - 1)) *
|
||||
(amount - (totalDown || 0))
|
||||
).toFixed(2) /
|
||||
(this.paymentFrequency / 12);
|
||||
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
updateView() {
|
||||
['default-rate', 'custom-rate'].forEach((el) => {
|
||||
document.getElementById(el).classList.add('hidden');
|
||||
});
|
||||
document
|
||||
.getElementById(document.getElementById('rate').value)
|
||||
.classList.remove('hidden');
|
||||
|
||||
if (document.querySelectorAll('.loanTermSpan').length) {
|
||||
document.querySelectorAll('.loanTermSpan').forEach((el) => {
|
||||
el.innerHTML = `${this.loanTerm}`;
|
||||
});
|
||||
}
|
||||
|
||||
if (document.querySelectorAll('.rateSpan').length) {
|
||||
document.querySelectorAll('.rateSpan').forEach((el) => {
|
||||
el.innerHTML = `${this.intRate}`;
|
||||
});
|
||||
}
|
||||
|
||||
if (document.querySelectorAll('.paymentSpan').length) {
|
||||
document.querySelectorAll('.paymentSpan').forEach((el) => {
|
||||
el.innerHTML = (Number(this.payment) || 0).toLocaleString('en-CA', {
|
||||
style: 'currency',
|
||||
currency: 'CAD',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (document.querySelectorAll('.paymentFrequencySpan').length) {
|
||||
document.querySelectorAll('.paymentFrequencySpan').forEach((el) => {
|
||||
el.innerHTML = this.frequencyMap[this.paymentFrequency];
|
||||
});
|
||||
}
|
||||
|
||||
if (document.querySelectorAll('.totalCostOfCreditSpan').length) {
|
||||
document.querySelectorAll('.totalCostOfCreditSpan').forEach((el) => {
|
||||
el.innerHTML = (Number(this.totalCostOfCredit) || 0).toLocaleString(
|
||||
'en-CA',
|
||||
{ style: 'currency', currency: 'CAD' }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (document.querySelectorAll('.totalObligationSpan').length) {
|
||||
document.querySelectorAll('.totalObligationSpan').forEach((el) => {
|
||||
el.innerHTML = (Number(this.totalObligation) || 0).toLocaleString(
|
||||
'en-CA',
|
||||
{ style: 'currency', currency: 'CAD' }
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
goToNextTab() {
|
||||
let tab1 = document.querySelector('[data-tab="tab1"]');
|
||||
let tab2 = document.querySelector('[data-tab="tab2"]');
|
||||
let backButton = document.getElementById('next-step');
|
||||
|
||||
if (tab1 && tab2 && backButton) {
|
||||
if (tab1.classList.contains('active')) {
|
||||
// Switch to tab2 and update button text to "Back"
|
||||
tab2.click();
|
||||
backButton.innerText = 'Back';
|
||||
} else if (tab2.classList.contains('active')) {
|
||||
// Switch to tab1 and update button text to "Next step"
|
||||
tab1.click();
|
||||
backButton.innerText = 'Next step';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializeTabNavigation(modalDiv) {
|
||||
const tabLinks = modalDiv.querySelectorAll('.tab-link');
|
||||
const tabContents = modalDiv.querySelectorAll('.tab-content');
|
||||
let nextStepButton = modalDiv.querySelector('#next-step');
|
||||
if (nextStepButton) {
|
||||
nextStepButton.addEventListener('click', this.goToNextTab.bind(this));
|
||||
}
|
||||
tabLinks.forEach(function (tabLink) {
|
||||
tabLink.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
tabLinks.forEach(function (link) {
|
||||
link.classList.remove('active', 'bg-gray-600', 'text-white');
|
||||
});
|
||||
tabContents.forEach(function (content) {
|
||||
content.classList.add('hidden');
|
||||
});
|
||||
|
||||
const targetTab = this.getAttribute('data-tab');
|
||||
this.classList.add('active', 'bg-gray-600', 'text-white');
|
||||
|
||||
let backButtonTexts = { tab1: 'Next Step', tab2: 'Back To Options' };
|
||||
nextStepButton.innerText = backButtonTexts[targetTab];
|
||||
modalDiv
|
||||
.querySelector(`.tab-content[data-content="${targetTab}"]`)
|
||||
.classList.remove('hidden');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initializeFormEventListeners(modalDiv) {
|
||||
const formInputs = [
|
||||
'loanTerm',
|
||||
'intRate',
|
||||
'downPayment',
|
||||
'tradeValue',
|
||||
'paymentFrequency',
|
||||
'rate',
|
||||
'customRateValue',
|
||||
];
|
||||
|
||||
formInputs.forEach((id) => {
|
||||
const inputElement = modalDiv.querySelector(`#${id}`);
|
||||
if (inputElement) {
|
||||
inputElement.addEventListener('input', () => {
|
||||
this.calculatePayments();
|
||||
this.saveFormValues(); // Save form values whenever an input changes
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveFormValues() {
|
||||
const formInputs = [
|
||||
'loanTerm',
|
||||
'intRate',
|
||||
'downPayment',
|
||||
'tradeValue',
|
||||
'paymentFrequency',
|
||||
'rate',
|
||||
'customRateValue',
|
||||
];
|
||||
const formValues = {};
|
||||
|
||||
formInputs.forEach((input) => {
|
||||
const inputElement = document.getElementById(input);
|
||||
if (inputElement) {
|
||||
formValues[input] = inputElement.value;
|
||||
}
|
||||
});
|
||||
|
||||
if (formValues['rate'] == 'default-rate') {
|
||||
formValues['customRateValue'] = 7.99;
|
||||
}
|
||||
|
||||
localStorage.setItem(`autocart_${this.name}`, JSON.stringify(formValues));
|
||||
}
|
||||
}
|
||||
|
140
autocart_assets/js/MakeFilter.js
Normal file
140
autocart_assets/js/MakeFilter.js
Normal file
@@ -0,0 +1,140 @@
|
||||
class MakeFilter {
|
||||
constructor(make, dataStore) {
|
||||
this.value = make.text;
|
||||
this.count = make.count;
|
||||
this.dataStore = dataStore;
|
||||
this.checkbox = null;
|
||||
this.models = make.models.map(model=> filterManager.createModelFilter({ text: model.model, count: model.count, trims: model.trims, parent: this.value, checked: false }, this.dataStore));
|
||||
this.checked = make.checked;
|
||||
this.formField = null;
|
||||
// this.initCheckbox();
|
||||
this.subscribers = [];
|
||||
this.models.forEach(m => {
|
||||
m.subscribe(m=> initTrimContainer(m))
|
||||
});
|
||||
}
|
||||
|
||||
initCheckbox() {
|
||||
const makeListBlock = document.createElement('div');
|
||||
const dynamicId = `autocart_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
const dynamicClass = `autocart_term_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
makeListBlock.innerHTML = `
|
||||
<li class="w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<div class="flex items-center mdc-form-field flex-wrap justify-between items-start flex-row w-full ${dynamicClass} hover:text-red-500">
|
||||
<label for="${dynamicId}" class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">${this.value}</label>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
(${this.count})
|
||||
</span>
|
||||
</div>
|
||||
<div class="mdc-checkbox mdc-theme-primary">
|
||||
<input type="checkbox" class="mdc-checkbox__native-control ${dynamicClass}" id="${dynamicId}" />
|
||||
<div class="mdc-checkbox__background rounded-md">
|
||||
<svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
|
||||
<path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
|
||||
</svg>
|
||||
<div class="mdc-checkbox__mixedmark"></div>
|
||||
</div>
|
||||
<div class="mdc-checkbox__ripple p-2"></div>
|
||||
</div>
|
||||
</li>`;
|
||||
|
||||
this.checkbox = makeListBlock.querySelector('.mdc-checkbox');
|
||||
if (this.checkbox) {
|
||||
const checkboxElement = this.checkbox.querySelector(`.mdc-checkbox__native-control.${dynamicClass}`);
|
||||
this.checkbox = new mdc.checkbox.MDCCheckbox(this.checkbox);
|
||||
this.formField = new mdc.formField.MDCFormField(makeListBlock.querySelector(`.mdc-form-field.${dynamicClass}`));
|
||||
this.formField.input = checkboxElement;
|
||||
this.checkbox.listen('change', () => this.handleCheckboxChange());
|
||||
} else {
|
||||
console.error('Checkbox element not found:', dynamicId);
|
||||
}
|
||||
|
||||
this.markCheckboxIfValueFiltered()
|
||||
|
||||
return makeListBlock;
|
||||
}
|
||||
|
||||
markCheckboxIfValueFiltered() {
|
||||
if (this.checked) {
|
||||
this.checkbox.checked = true;
|
||||
} else {
|
||||
let ifAlreadyFiltered = this.dataStore.getAppliedFilters().find(filter => filter.type == "make");
|
||||
if (ifAlreadyFiltered) {
|
||||
if (ifAlreadyFiltered.values.includes(this.value)) {
|
||||
this.checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
// let ifAlreadyFiltered = this.dataStore.getAppliedFilters().find(filter=>filter.type == "make");
|
||||
// if(ifAlreadyFiltered){
|
||||
// if(ifAlreadyFiltered.values.includes(this.value)){
|
||||
// this.checkbox.checked = true;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
handleCheckboxChange() {
|
||||
shouldUpdateFilters = false;
|
||||
const isChecked = this.checkbox.checked;
|
||||
if (isChecked) {
|
||||
this.dataStore.addFilter('make', this.value);
|
||||
// this.updateModelFilters(this.value);
|
||||
} else {
|
||||
this.dataStore.removeFilter('make', this.value);
|
||||
}
|
||||
|
||||
this.checked = isChecked;
|
||||
this.models.forEach(model=>model.uncheckCheckbox())
|
||||
createBodyTypeList(this.dataStore.filterVehicles());
|
||||
createTransmissionList(this.dataStore.filterVehicles());
|
||||
createExteriorList(this.dataStore.filterVehicles());
|
||||
this.notifySubscribers();
|
||||
}
|
||||
|
||||
generateCardHTML() {
|
||||
if(this.checked){
|
||||
// this.checkbox.checked = true;
|
||||
setTimeout(() => {
|
||||
shouldUpdateFilters = false;
|
||||
|
||||
this.dataStore.addFilter('make', this.value);
|
||||
this.notifySubscribers();
|
||||
}, 100);
|
||||
|
||||
}
|
||||
return this.initCheckbox();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
updateModelFilters(value) {
|
||||
this.models.forEach(model => {
|
||||
model.setParentFilter(value)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
uncheckCheckbox() {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.checked = false;
|
||||
this.handleCheckboxChange();
|
||||
// this.checkbox.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
notifySubscribers() {
|
||||
this.subscribers.forEach(subscriber => subscriber(this));
|
||||
}
|
||||
|
||||
subscribe(subscriber) {
|
||||
this.subscribers.push(subscriber);
|
||||
}
|
||||
|
||||
unsubscribe(subscriber) {
|
||||
this.subscribers = this.subscribers.filter(sub => sub !== subscriber);
|
||||
}
|
||||
}
|
98
autocart_assets/js/Modal.js
Normal file
98
autocart_assets/js/Modal.js
Normal file
@@ -0,0 +1,98 @@
|
||||
class Modal {
|
||||
constructor(vehicle) {
|
||||
this.vehicle = vehicle;
|
||||
|
||||
this.modalDiv;
|
||||
this.initModal();
|
||||
}
|
||||
toggleModal(html) {
|
||||
if (this.modalDiv) {
|
||||
const modalBody = this.modalDiv.querySelector('#ac-modal-body');
|
||||
modalBody.innerHTML = '';
|
||||
if (html) {
|
||||
modalBody.appendChild(html.initForm());
|
||||
}
|
||||
}
|
||||
const body = document.querySelector('body');
|
||||
const modal = document.querySelector('.modal');
|
||||
modal.classList.toggle('opacity-0');
|
||||
modal.classList.toggle('pointer-events-none');
|
||||
body.classList.toggle('modal-active');
|
||||
}
|
||||
initModal() {
|
||||
// Create a new div element
|
||||
const modalDiv = document.createElement('div');
|
||||
modalDiv.classList.add(
|
||||
'modal',
|
||||
'z-50',
|
||||
'opacity-0',
|
||||
'pointer-events-none',
|
||||
'fixed',
|
||||
'w-full',
|
||||
'h-full',
|
||||
'top-0',
|
||||
'left-0',
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center'
|
||||
);
|
||||
modalDiv.innerHTML = `
|
||||
<div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50"></div>
|
||||
<div class="modal-container bg-white w-11/12 md:w-3/4 h-3/4 lg:h-11/12 mx-auto rounded shadow-lg z-50 overflow-y-scroll relative no-scrollbar">
|
||||
<div class="modal-close absolute top-0 right-0 cursor-pointer flex flex-col items-center mt-4 mr-4 text-white text-sm z-50">
|
||||
<svg class="fill-current text-white" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"></path>
|
||||
</svg>
|
||||
<span class="text-sm">(Esc)</span>
|
||||
</div>
|
||||
|
||||
<div class="modal-content py-4 text-left px-6">
|
||||
<!--Title-->
|
||||
<div class="flex justify-between items-center pb-3">
|
||||
<p class="text-2xl font-bold">Private Vehicle Appointment Booking</p>
|
||||
<div class="modal-close cursor-pointer z-50">
|
||||
<svg class="fill-current text-black" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||
<path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BODY -->
|
||||
<div id="ac-modal-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const overlay = modalDiv.querySelector('.modal-overlay');
|
||||
|
||||
overlay.addEventListener('click', function () {
|
||||
const activeModal = modalDiv.querySelector('.modal');
|
||||
activeModal &&
|
||||
activeModal.classList.contains('opacity-0') &&
|
||||
this.toggleModal();
|
||||
});
|
||||
|
||||
var closemodal = modalDiv.querySelectorAll('.modal-close');
|
||||
for (var i = 0; i < closemodal.length; i++) {
|
||||
closemodal[i].addEventListener('click', this.toggleModal);
|
||||
}
|
||||
|
||||
document.onkeydown = (evt) => {
|
||||
evt = evt || window.event;
|
||||
var isEscape = false;
|
||||
if ('key' in evt) {
|
||||
isEscape = evt.key === 'Escape' || evt.key === 'Esc';
|
||||
} else {
|
||||
isEscape = evt.keyCode === 27;
|
||||
}
|
||||
if (isEscape && document.body.classList.contains('modal-active')) {
|
||||
this.toggleModal();
|
||||
}
|
||||
};
|
||||
this.modalDiv = modalDiv;
|
||||
return modalDiv;
|
||||
}
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
console.log('modal is here');
|
||||
});
|
||||
|
118
autocart_assets/js/ModelFilter.js
Normal file
118
autocart_assets/js/ModelFilter.js
Normal file
@@ -0,0 +1,118 @@
|
||||
class ModelFilter {
|
||||
constructor(model, dataStore) {
|
||||
this.value = model.text;
|
||||
this.count = model.count;
|
||||
this.parent = model.parent;
|
||||
this.dataStore = dataStore;
|
||||
this.checkbox = null;
|
||||
this.checked = model.checked;
|
||||
this.formField = null;
|
||||
this.trims = model.trims.map(trim=> filterManager.createTrimFilter({ text: trim.text, count: trim.count, parent: this.value, checked: false }, this.dataStore));
|
||||
this.subscribers = [];
|
||||
this.initCheckbox();
|
||||
}
|
||||
|
||||
initCheckbox() {
|
||||
const makeListBlock = document.createElement('div');
|
||||
const dynamicId = `autocart_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
const dynamicClass = `autocart_term_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
|
||||
makeListBlock.innerHTML = `
|
||||
<li class="w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<div class="flex items-center mdc-form-field flex-wrap justify-between items-start flex-row w-full ${dynamicClass} hover:text-red-500">
|
||||
<label for="${dynamicId}" class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">${this.value}</label>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
(${this.count})
|
||||
</span>
|
||||
</div>
|
||||
<div class="mdc-checkbox mdc-theme-primary">
|
||||
<input type="checkbox" class="mdc-checkbox__native-control ${dynamicClass}" id="${dynamicId}" />
|
||||
<div class="mdc-checkbox__background rounded-md">
|
||||
<svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
|
||||
<path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
|
||||
</svg>
|
||||
<div class="mdc-checkbox__mixedmark"></div>
|
||||
</div>
|
||||
<div class="mdc-checkbox__ripple p-2"></div>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
|
||||
this.checkbox = makeListBlock.querySelector('.mdc-checkbox');
|
||||
|
||||
if (this.checkbox) {
|
||||
// Initialize the MDCCheckbox with the actual checkbox element
|
||||
const checkboxElement = this.checkbox.querySelector(`.mdc-checkbox__native-control.${dynamicClass}`);
|
||||
this.checkbox = new mdc.checkbox.MDCCheckbox(this.checkbox);
|
||||
this.formField = new mdc.formField.MDCFormField(makeListBlock.querySelector(`.mdc-form-field.${dynamicClass}`));
|
||||
this.formField.input = checkboxElement;
|
||||
this.checkbox.listen('change', () => this.handleCheckboxChange());
|
||||
} else {
|
||||
console.error('Checkbox element not found:', dynamicId);
|
||||
}
|
||||
|
||||
this.markCheckboxIfValueFiltered()
|
||||
|
||||
|
||||
return makeListBlock;
|
||||
}
|
||||
|
||||
markCheckboxIfValueFiltered() {
|
||||
let ifAlreadyFiltered = this.dataStore.getAppliedFilters().find(filter=>filter.type == "model");
|
||||
if(ifAlreadyFiltered){
|
||||
if(ifAlreadyFiltered.values.includes(this.value)){
|
||||
this.checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxChange() {
|
||||
shouldUpdateFilters = false;
|
||||
const isChecked = this.checkbox.checked;
|
||||
if (isChecked) {
|
||||
|
||||
this.dataStore.addFilter('model', this.value);
|
||||
|
||||
|
||||
} else {
|
||||
this.dataStore.removeFilter('model', this.value);
|
||||
// this.trims = [];
|
||||
}
|
||||
|
||||
this.checked = isChecked;
|
||||
|
||||
|
||||
createBodyTypeList(this.dataStore.filterVehicles());
|
||||
createTransmissionList(this.dataStore.filterVehicles());
|
||||
createExteriorList(this.dataStore.filterVehicles());
|
||||
|
||||
|
||||
this.trims.forEach(trim=>trim.uncheckCheckbox())
|
||||
this.notifySubscribers();
|
||||
|
||||
}
|
||||
|
||||
setParentFilter(value){
|
||||
this.isParentSelected = value;
|
||||
this.generateCardHTML();
|
||||
}
|
||||
|
||||
uncheckCheckbox() {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.checked = false;
|
||||
this.handleCheckboxChange();
|
||||
}
|
||||
}
|
||||
|
||||
notifySubscribers() {
|
||||
this.subscribers.forEach(subscriber => subscriber(this));
|
||||
}
|
||||
|
||||
subscribe(subscriber) {
|
||||
this.subscribers.push(subscriber);
|
||||
}
|
||||
|
||||
unsubscribe(subscriber) {
|
||||
this.subscribers = this.subscribers.filter(sub => sub !== subscriber);
|
||||
}
|
||||
}
|
59
autocart_assets/js/SortBy.js
Normal file
59
autocart_assets/js/SortBy.js
Normal file
@@ -0,0 +1,59 @@
|
||||
class SortBy {
|
||||
constructor(dataStore) {
|
||||
this.dataStore = dataStore;
|
||||
this.sortByElement = null;
|
||||
this.sortOptions = [
|
||||
{ field: 'advertise_price', order: 'asc', label: 'Price: Low to High' },
|
||||
{ field: 'advertise_price', order: 'desc', label: 'Price: High to Low' },
|
||||
{ field: 'year', order: 'asc', label: 'Year: Low to High' },
|
||||
{ field: 'year', order: 'desc', label: 'Year: High to Low' },
|
||||
{ field: 'make', order: 'asc', label: 'Make Name: A to Z' },
|
||||
{ field: 'make', order: 'desc', label: 'Make Name: Z to A' },
|
||||
{ field: 'model', order: 'asc', label: 'Model Name: A to Z' },
|
||||
{ field: 'model', order: 'desc', label: 'Model Name: Z to A' }
|
||||
];
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Get the ul element
|
||||
const sortOptionsList = document.getElementById('sort-options-list');
|
||||
|
||||
// Dynamically create list items
|
||||
this.sortOptions.forEach(option => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.className = 'mdc-list-item';
|
||||
listItem.setAttribute('aria-selected', 'false');
|
||||
listItem.setAttribute('data-value', JSON.stringify([option.field, option.order])); // Use field and order properties
|
||||
|
||||
const rippleSpan = document.createElement('span');
|
||||
rippleSpan.className = 'mdc-list-item__ripple';
|
||||
|
||||
const textSpan = document.createElement('span');
|
||||
textSpan.className = 'mdc-list-item__text';
|
||||
textSpan.textContent = option.label; // Use label property
|
||||
|
||||
listItem.appendChild(rippleSpan);
|
||||
listItem.appendChild(textSpan);
|
||||
|
||||
sortOptionsList.appendChild(listItem);
|
||||
});
|
||||
|
||||
|
||||
this.sortByElement = new mdc.select.MDCSelect(document.querySelector('#order-by'));
|
||||
|
||||
this.sortByElement.listen('MDCSelect:change', this.handleSortChange.bind(this));
|
||||
}
|
||||
|
||||
handleSortChange(event) {
|
||||
|
||||
const selectedOption = this.sortByElement.value;
|
||||
if (selectedOption) {
|
||||
const [sortBy, order] = JSON.parse(selectedOption);
|
||||
this.dataStore.sortBy(sortBy, order);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
104
autocart_assets/js/TransmissionFilter.js
Normal file
104
autocart_assets/js/TransmissionFilter.js
Normal file
@@ -0,0 +1,104 @@
|
||||
class TransmissionFilter {
|
||||
constructor(transmission, dataStore) {
|
||||
this.value = transmission.text;
|
||||
this.count = transmission.count;
|
||||
this.parent = transmission.parent;
|
||||
this.dataStore = dataStore;
|
||||
this.checkbox = null;
|
||||
this.checked = transmission.checked;
|
||||
this.initCheckbox();
|
||||
|
||||
|
||||
}
|
||||
|
||||
initCheckbox() {
|
||||
const makeListBlock = document.createElement('div');
|
||||
const dynamicId = `autocart_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
const dynamicClass = `autocart_term_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
|
||||
makeListBlock.innerHTML = `
|
||||
|
||||
|
||||
<li class="w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<div class="flex items-center mdc-form-field flex-wrap justify-between items-start flex-row w-full ${dynamicClass} hover:text-red-500">
|
||||
<label for="${dynamicId}" class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">${this.value}</label>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
(${this.count})
|
||||
</span>
|
||||
</div>
|
||||
<div class="mdc-checkbox mdc-theme-primary">
|
||||
<input type="checkbox" class="mdc-checkbox__native-control ${dynamicClass}" id="${dynamicId}" />
|
||||
<div class="mdc-checkbox__background rounded-md">
|
||||
<svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
|
||||
<path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
|
||||
</svg>
|
||||
<div class="mdc-checkbox__mixedmark"></div>
|
||||
</div>
|
||||
<div class="mdc-checkbox__ripple p-2"></div>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
|
||||
this.checkbox = makeListBlock.querySelector('.mdc-checkbox');
|
||||
|
||||
if (this.checkbox) {
|
||||
// Initialize the MDCCheckbox with the actual checkbox element
|
||||
const checkboxElement = this.checkbox.querySelector(`.mdc-checkbox__native-control.${dynamicClass}`);
|
||||
this.checkbox = new mdc.checkbox.MDCCheckbox(this.checkbox);
|
||||
this.formField = new mdc.formField.MDCFormField(makeListBlock.querySelector(`.mdc-form-field.${dynamicClass}`));
|
||||
this.formField.input = checkboxElement;
|
||||
this.checkbox.listen('change', () => this.handleCheckboxChange());
|
||||
} else {
|
||||
console.error('Checkbox element not found:', dynamicId);
|
||||
}
|
||||
|
||||
this.markCheckboxIfValueFiltered()
|
||||
// console.log(dataStore.getAppliedFilters());
|
||||
|
||||
return makeListBlock;
|
||||
}
|
||||
|
||||
markCheckboxIfValueFiltered() {
|
||||
let ifAlreadyFiltered = this.dataStore.getAppliedFilters().find(filter=>filter.type == "transmission_type");
|
||||
if(ifAlreadyFiltered){
|
||||
if(ifAlreadyFiltered.values.includes(this.value)){
|
||||
this.checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxChange() {
|
||||
shouldUpdateFilters = false;
|
||||
const isChecked = this.checkbox.checked;
|
||||
if (isChecked) {
|
||||
this.dataStore.addFilter('transmission_type', this.value);
|
||||
} else {
|
||||
this.dataStore.removeFilter('transmission_type', this.value);
|
||||
}
|
||||
|
||||
this.checked = isChecked;
|
||||
createExteriorList(this.dataStore.filterVehicles());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
setParentFilter(value){
|
||||
this.isParentSelected = value;
|
||||
this.generateCardHTML();
|
||||
}
|
||||
|
||||
uncheckCheckbox() {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.checked = false;
|
||||
this.handleCheckboxChange();
|
||||
|
||||
// this.checkbox.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
102
autocart_assets/js/TrimFilter.js
Normal file
102
autocart_assets/js/TrimFilter.js
Normal file
@@ -0,0 +1,102 @@
|
||||
class TrimFilter {
|
||||
constructor(trim, dataStore) {
|
||||
this.value = trim.text;
|
||||
this.count = trim.count;
|
||||
this.parent = trim.parent;
|
||||
this.dataStore = dataStore;
|
||||
this.checkbox = null;
|
||||
this.checked = trim.checked;
|
||||
this.initCheckbox();
|
||||
}
|
||||
|
||||
initCheckbox() {
|
||||
const makeListBlock = document.createElement('div');
|
||||
const dynamicId = `autocart_${this.value.toLowerCase().replace(/\s/g, '-').replace(/\./g, '')}`;
|
||||
const dynamicClass = `autocart_term_${this.value.toLowerCase().replace(/\s/g, '-').replace(/[^a-zA-Z0-9-]/g, '')}`;
|
||||
// const dynamicClass = `autocart_term_${sanitizeClassName(this.value)}`;
|
||||
|
||||
|
||||
|
||||
makeListBlock.innerHTML = `
|
||||
<li class="w-full rounded-t-lg flex items-center justify-between transition duration-400 autocart-filters">
|
||||
<div class="flex items-center mdc-form-field flex-wrap justify-between items-start flex-row w-full ${dynamicClass} hover:text-red-500">
|
||||
<label for="${dynamicId}" class="text-sm font-medium text-gray-900 dark:text-gray-300 transition duration-400 font-normal text-base">${this.value}</label>
|
||||
<span class="autocart_count text-gray-500 ml-2 transition duration-400 font-normal text-base">
|
||||
(${this.count})
|
||||
</span>
|
||||
</div>
|
||||
<div class="mdc-checkbox mdc-theme-primary">
|
||||
<input type="checkbox" class="mdc-checkbox__native-control ${dynamicClass}" id="${dynamicId}" />
|
||||
<div class="mdc-checkbox__background rounded-md">
|
||||
<svg class="mdc-checkbox__checkmark" viewBox="0 0 24 24">
|
||||
<path class="mdc-checkbox__checkmark-path" fill="none" d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
|
||||
</svg>
|
||||
<div class="mdc-checkbox__mixedmark"></div>
|
||||
</div>
|
||||
<div class="mdc-checkbox__ripple p-2"></div>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
|
||||
this.checkbox = makeListBlock.querySelector('.mdc-checkbox');
|
||||
|
||||
if (this.checkbox) {
|
||||
// Initialize the MDCCheckbox with the actual checkbox element
|
||||
const checkboxElement = this.checkbox.querySelector(`.mdc-checkbox__native-control.${dynamicClass}`);
|
||||
this.checkbox = new mdc.checkbox.MDCCheckbox(this.checkbox);
|
||||
this.formField = new mdc.formField.MDCFormField(makeListBlock.querySelector(`.mdc-form-field.${dynamicClass}`));
|
||||
this.formField.input = checkboxElement;
|
||||
this.checkbox.listen('change', () => this.handleCheckboxChange());
|
||||
} else {
|
||||
console.error('Checkbox element not found:', dynamicId);
|
||||
}
|
||||
|
||||
|
||||
this.markCheckboxIfValueFiltered()
|
||||
|
||||
|
||||
return makeListBlock;
|
||||
}
|
||||
|
||||
markCheckboxIfValueFiltered() {
|
||||
let ifAlreadyFiltered = this.dataStore.getAppliedFilters().find(filter=>filter.type == "trim");
|
||||
if(ifAlreadyFiltered){
|
||||
if(ifAlreadyFiltered.values.includes(this.value)){
|
||||
this.checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxChange() {
|
||||
shouldUpdateFilters = false;
|
||||
const isChecked = this.checkbox.checked;
|
||||
if (isChecked) {
|
||||
this.dataStore.addFilter('trim', this.value);
|
||||
} else {
|
||||
this.dataStore.removeFilter('trim', this.value);
|
||||
}
|
||||
|
||||
this.checked = isChecked;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
setParentFilter(value){
|
||||
this.isParentSelected = value;
|
||||
this.generateCardHTML();
|
||||
}
|
||||
|
||||
uncheckCheckbox() {
|
||||
if (this.checkbox) {
|
||||
this.checkbox.checked = false;
|
||||
this.handleCheckboxChange();
|
||||
|
||||
// this.checkbox.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
62
autocart_assets/js/VehicleThumbnail.js
Normal file
62
autocart_assets/js/VehicleThumbnail.js
Normal file
@@ -0,0 +1,62 @@
|
||||
class VehicleThumbnail {
|
||||
constructor(vehicle) {
|
||||
this.id = vehicle.id_vehicle;
|
||||
this.name = vehicle.make + ' ' + vehicle.model;
|
||||
this.year = vehicle.year;
|
||||
this.make = vehicle.make;
|
||||
this.model = vehicle.model;
|
||||
this.trim = vehicle.trim;
|
||||
this.landingImage = vehicle.landingImage;
|
||||
this.advertisePrice = vehicle.advertise_price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
this.stockNumber = vehicle.stock_number;
|
||||
this.mileage = vehicle.mileage;
|
||||
}
|
||||
|
||||
generateCardElement() {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = this.generateCardHTML().trim();
|
||||
|
||||
// Get the vehicle-card element
|
||||
const cardElement = wrapper.querySelector('.vehicle-card');
|
||||
const ripple = new mdc.ripple.MDCRipple(cardElement);
|
||||
ripple.unbounded = false;
|
||||
cardElement.addEventListener('click', () => {
|
||||
console.log(dataStore)
|
||||
var queryString = encodeURIComponent(JSON.stringify(dataStore.filters));
|
||||
|
||||
const url = `vehicle-details/?vehicle_id=${encodeURIComponent(this.id)}&filters=${queryString}`;
|
||||
window.location.href = url;
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return cardElement;
|
||||
}
|
||||
|
||||
|
||||
|
||||
generateCardHTML() {
|
||||
return `
|
||||
<div class="bg-white shadow-md rounded-md transition-all duration-500 vehicle-card cursor-pointer mdc-ripple-surface hover:shadow-lg" data-vehicle-id="${this.id}">
|
||||
<img src="${this.landingImage}" alt="Vehicle Image" class="w-full h-auto object-cover transition-all duration-500 vehicle-image rounded-tl-md rounded-tr-md">
|
||||
<div class="flex flex-col justify-around transition-all duration-500 p-4">
|
||||
<h3 class="text-lg font-semibold mb-2 break-words ">${this.year} ${this.name} ${this.trim.join(' - ')}</h3>
|
||||
<div class="text-gray-600">Year: ${this.year}</div>
|
||||
<div class="text-gray-600">Mileage: ${this.mileage} km</div>
|
||||
<div class="text-red-800 font-bold mt-2">Price: $${this.advertisePrice}</div>
|
||||
<div class="text-gray-600">Stock #: ${this.stockNumber} km</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the ripple component for all instances of VehicleThumbnail
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const vehicleThumbnails = document.querySelectorAll('.mdc-ripple-surface');
|
||||
vehicleThumbnails.forEach(thumbnail => {
|
||||
const ripple = new mdc.ripple.MDCRipple(thumbnail);
|
||||
ripple.unbounded = true;
|
||||
});
|
||||
});
|
281
autocart_assets/js/index.js
Normal file
281
autocart_assets/js/index.js
Normal file
@@ -0,0 +1,281 @@
|
||||
let drawer;
|
||||
let keyword;
|
||||
let price;
|
||||
let year;
|
||||
let kilometres;
|
||||
let dataStore;
|
||||
let filterManager;
|
||||
let appliedFilters;
|
||||
let shouldUpdateFilters = true;
|
||||
let modelFilter = [];
|
||||
let makeFilters;
|
||||
function sendReq() {
|
||||
var formData = {
|
||||
'action': 'handle_autocart_form',
|
||||
'minPrice': price.getValueStart(),
|
||||
'maxPrice': price.getValue(),
|
||||
'minKilometres': kilometres.getValueStart(),
|
||||
'maxKilometres': kilometres.getValue(),
|
||||
'minYear': year.getValueStart(),
|
||||
'maxYear': year.getValue(),
|
||||
'autocart_nonce': jQuery('#autocart_nonce').val(),
|
||||
};
|
||||
const vehiclesContainer = document.querySelector('.vehicles-container');
|
||||
vehiclesContainer.textContent = 'Loading...';
|
||||
|
||||
const filtersContainer = document.querySelector('.make-container');
|
||||
filtersContainer.textContent = 'Loading...';
|
||||
jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: ajax_object.ajax_url,
|
||||
data: formData,
|
||||
success: function (response) {
|
||||
const vehicles = response.data.vehicles;
|
||||
if (dataStore) {
|
||||
dataStore.unsubscribeAll();
|
||||
}
|
||||
|
||||
|
||||
// Initialize new data store
|
||||
dataStore.vehicles = vehicles;
|
||||
filterManager = new FilterManager(dataStore);
|
||||
appliedFilters = new AppliedFilters(dataStore, filterManager);
|
||||
|
||||
// filterManager = new FilterManager(dataStore);
|
||||
// appliedFilters = new AppliedFilters(dataStore, filterManager);
|
||||
|
||||
shouldUpdateFilters = true;
|
||||
dataStore.subscribe(filteredVehicles => {
|
||||
console.log('dataStore has been changed')
|
||||
handleSuccess(filteredVehicles);
|
||||
});
|
||||
|
||||
dataStore.notifySubscribers(vehicles);
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.log(xhr.responseText);
|
||||
console.log('Status: ' + status);
|
||||
console.log('Error: ' + error);
|
||||
jQuery("#inventory-search__search-button").text("Error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleSuccess(vehicles, updateFilters = shouldUpdateFilters) {
|
||||
if(updateFilters){
|
||||
createMakeList(vehicles);
|
||||
createBodyTypeList(vehicles);
|
||||
createTransmissionList(vehicles);
|
||||
createExteriorList(vehicles);
|
||||
}
|
||||
const vehiclesContainer = document.querySelector('.vehicles-container');
|
||||
vehiclesContainer.innerHTML = '';
|
||||
|
||||
vehicles.forEach(vehicleData => {
|
||||
const vehicle = new VehicleThumbnail(vehicleData);
|
||||
// vehiclesContainer.appendChild(vehicle.generateCardHTML());
|
||||
vehiclesContainer.appendChild(vehicle.generateCardElement());
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function createMakeList(vehicles){
|
||||
|
||||
const makeContainer = document.querySelector('.make-container');
|
||||
makeContainer.innerHTML = '';
|
||||
|
||||
const makeCounts = vehicles.reduce((acc, vehicle) => {
|
||||
const { make } = vehicle;
|
||||
acc[make] = (acc[make] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
let makeValue = document.querySelector('#autocart-make-field').value;
|
||||
// if(makeValue){
|
||||
// console.log('makeValue',makeValue);
|
||||
// // dataStore.addFilter('make', makeValue);
|
||||
// }
|
||||
makeFilters = Object.entries(makeCounts).map(([make, count]) => {
|
||||
|
||||
return filterManager.createMakeFilter({
|
||||
text: make,
|
||||
checked: makeValue.toLowerCase() === make.toLowerCase(),
|
||||
count,
|
||||
models: fetchAvailableModels(vehicles, make)
|
||||
}, dataStore);
|
||||
});
|
||||
makeFilters.forEach(make => {
|
||||
|
||||
makeContainer.appendChild(make.generateCardHTML());
|
||||
make.subscribe(m=> initModelContainer(m))
|
||||
});
|
||||
}
|
||||
|
||||
function createBodyTypeList(vehicles){
|
||||
const bodyTypeContainer = document.querySelector('.body-type-container');
|
||||
bodyTypeContainer.innerHTML = '';
|
||||
|
||||
const bodyType = vehicles.reduce((acc, vehicle) => {
|
||||
const { body_type } = vehicle;
|
||||
acc[body_type] = (acc[body_type] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
filterManager.resetList('body_type');
|
||||
|
||||
|
||||
bodyTypeFilters = Object.entries(bodyType).map(([body_type, count]) => filterManager.createBodyTypeFilter({ text: body_type, count}, dataStore));
|
||||
bodyTypeFilters.forEach(body_type => {
|
||||
bodyTypeContainer.appendChild(body_type.initCheckbox());
|
||||
// body_type.subscribe(m=> iniBodyTypeContainer(m))
|
||||
});
|
||||
}
|
||||
|
||||
function createTransmissionList(vehicles){
|
||||
const transmissionContainer = document.querySelector('.transmission-container');
|
||||
|
||||
transmissionContainer.innerHTML = '';
|
||||
|
||||
const transmission = vehicles.reduce((acc, vehicle) => {
|
||||
const { transmission_type } = vehicle;
|
||||
acc[transmission_type] = (acc[transmission_type] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
filterManager.resetList('transmission_type');
|
||||
transmissionFilters = Object.entries(transmission).map(([transmission_type, count]) => filterManager.createTransmissionFilter({ text: transmission_type, count}, dataStore));
|
||||
transmissionFilters.forEach(transmission => {
|
||||
transmissionContainer.appendChild(transmission.initCheckbox());
|
||||
});
|
||||
}
|
||||
|
||||
function createExteriorList(vehicles){
|
||||
console.log(vehicles);
|
||||
const exteriorContainer = document.querySelector('.exterior-color');
|
||||
exteriorContainer.innerHTML = '';
|
||||
const exterior = vehicles.reduce((acc, vehicle) => {
|
||||
const { exterior_color } = vehicle;
|
||||
acc[exterior_color] = (acc[exterior_color] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
filterManager.resetList('exterior_color');
|
||||
|
||||
exteriorFilters = Object.entries(exterior).map(([exterior_color, count]) => filterManager.createExteriorFilter({ text: exterior_color, count}, dataStore));
|
||||
exteriorFilters.forEach(exterior => {
|
||||
exteriorContainer.appendChild(exterior.initCheckbox());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function initModelContainer(make){
|
||||
let list = [...new Set(filterManager.makeFilters.filter(m=>m.checked == true).map(m=>m.models).flat())];
|
||||
const modelContainer = document.querySelector('.model-container');
|
||||
if (list.length === 0) {
|
||||
modelContainer.innerHTML = '<p>Please Select a Make</p>';
|
||||
} else if (list && list.length > 0) {
|
||||
modelContainer.innerHTML = '';
|
||||
list.forEach(model => {
|
||||
modelContainer.appendChild(model.initCheckbox());
|
||||
});
|
||||
} else {
|
||||
modelContainer.innerHTML = '<p>No models available</p>';
|
||||
}
|
||||
}
|
||||
|
||||
function initTrimContainer(model){
|
||||
let list = [...new Set(filterManager.modelFilters.filter(m=>m.checked == true).map(m=>m.trims).flat())];
|
||||
// const uniqueArray = [...new Set(array)];
|
||||
const trimContainer = document.querySelector('.trim-container');
|
||||
|
||||
if (list.length === 0) {
|
||||
trimContainer.innerHTML = '<p>Please Select a Model</p>';
|
||||
} else if (list && list.length > 0) {
|
||||
trimContainer.innerHTML = '';
|
||||
list.forEach(trim => {
|
||||
trimContainer.appendChild(trim.initCheckbox());
|
||||
});
|
||||
} else {
|
||||
trimContainer.innerHTML = '<p>No trims available</p>';
|
||||
}
|
||||
}
|
||||
|
||||
function fetchAvailableModels(vehicles, make) {
|
||||
const filteredModels = vehicles
|
||||
.filter(vehicle => vehicle.make === make)
|
||||
.map(vehicle => vehicle.model);
|
||||
// Count occurrences of each model
|
||||
const modelCounts = filteredModels.reduce((acc, model) => {
|
||||
acc[model] = (acc[model] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Convert to an array of objects
|
||||
const modelsArray = Object.entries(modelCounts).map(([model, count]) => ({ model, count }));
|
||||
modelsArray.map(m=>m['trims'] = fetchAvailableTrim(vehicles, m.model))
|
||||
return modelsArray;
|
||||
}
|
||||
|
||||
function fetchAvailableTrim(vehicles, modelName){
|
||||
const filteredVehicles = vehicles.filter(vehicle => vehicle.model === modelName);
|
||||
const trimCount = filteredVehicles.reduce((result, vehicle) => {
|
||||
vehicle.trim.forEach(trim => {
|
||||
const existingTrim = result.find(item => item.text === trim);
|
||||
if (existingTrim) {
|
||||
existingTrim.count += 1;
|
||||
} else {
|
||||
result.push({ text: trim, count: 1 });
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
return trimCount;
|
||||
}
|
||||
|
||||
function updateModel(models){
|
||||
console.log('updateModel', models)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function debounce(func, delay) {
|
||||
let timeout;
|
||||
return function () {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
}
|
||||
|
||||
function updatePrice() {
|
||||
const min = price.getValueStart();
|
||||
const max = price.getValue();
|
||||
const formattedMin = '$' + min.toLocaleString();
|
||||
const formattedMax = '$' + max.toLocaleString();
|
||||
document.getElementById('priceRange').textContent = `${formattedMin} to ${formattedMax}`;
|
||||
debouncedSendReq();
|
||||
}
|
||||
|
||||
function updateYear() {
|
||||
const min = year.getValueStart();
|
||||
const max = year.getValue();
|
||||
document.getElementById('yearRange').textContent = `${min} to ${max}`;
|
||||
debouncedSendReq();
|
||||
}
|
||||
|
||||
function updateMileage() {
|
||||
const min = kilometres.getValueStart();
|
||||
const max = kilometres.getValue();
|
||||
document.getElementById('KilometresRange').textContent = `${min} to ${max}`;
|
||||
debouncedSendReq();
|
||||
}
|
||||
|
||||
// Create debounced versions of sendReq
|
||||
const debouncedSendReq = debounce(sendReq, 300);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
300
autocart_assets/templates/autocart-results.php
Normal file
300
autocart_assets/templates/autocart-results.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
/*
|
||||
Template Name: Autocart Results
|
||||
*/
|
||||
get_header();
|
||||
// Get filter values from URL parameters
|
||||
|
||||
$minPrice = isset($_GET['minPrice']) ? sanitize_text_field($_GET['minPrice']): null;
|
||||
$maxPrice = isset($_GET['maxPrice']) ? sanitize_text_field($_GET['maxPrice']):null;
|
||||
|
||||
$minKilometres = isset($_GET['minKilometres']) ? sanitize_text_field($_GET['minKilometres']): null;
|
||||
$maxKilometres = isset($_GET['maxKilometres']) ? sanitize_text_field($_GET['maxKilometres']):null;
|
||||
|
||||
$make = isset($_GET['make']) ? sanitize_text_field($_GET['make']):null;
|
||||
$minYear = isset($_GET['minYear']) ? (int)sanitize_text_field($_GET['minYear']):null;
|
||||
$maxYear = isset($_GET['maxYear']) ? (int)sanitize_text_field($_GET['maxYear']):null;
|
||||
// echo '<pre>';var_dump($minYear);echo '<br>'; var_dump($maxYear);exit;
|
||||
|
||||
|
||||
$startYear = 2012;
|
||||
$endYear = (int)date('Y');
|
||||
|
||||
|
||||
|
||||
|
||||
// Exit the script
|
||||
// exit;
|
||||
// Generate nonce
|
||||
$ajax_nonce = wp_create_nonce("update-filters-ajax-nonce");
|
||||
|
||||
$response = get_transient("autocart_response");
|
||||
|
||||
?>
|
||||
<!-- Main Content -->
|
||||
<div class="flex-grow flex max-w-7xl mx-auto">
|
||||
<!-- Left Drawer for Filters -->
|
||||
<aside class="w-1/4 p-4 shadow-lg bg-white hidden md:block rounded">
|
||||
<div class="mdc-select mdc-select--outlined .col-xl-3 rounded p-2 w-full" id="order-by">
|
||||
<div class="mdc-select__anchor">
|
||||
<span class="mdc-notched-outline">
|
||||
<span class="mdc-notched-outline__leading"></span>
|
||||
<span class="mdc-notched-outline__notch" style="border-left: none; border-right: none;">
|
||||
<span id="outlined-select-label" class="mdc-floating-label">Sort By</span>
|
||||
</span>
|
||||
<span class="mdc-notched-outline__trailing"></span>
|
||||
</span>
|
||||
<span class="mdc-select__selected-text-container">
|
||||
<span class="mdc-select__selected-text"></span>
|
||||
</span>
|
||||
<span class="mdc-select__dropdown-icon">
|
||||
<svg class="mdc-select__dropdown-icon-graphic" viewBox="7 10 10 5" focusable="false">
|
||||
<polygon class="mdc-select__dropdown-icon-inactive" stroke="none" fill-rule="evenodd" points="7 10 12 15 17 10"></polygon>
|
||||
<polygon class="mdc-select__dropdown-icon-active" stroke="none" fill-rule="evenodd" points="7 15 12 10 17 15"></polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mdc-select__menu mdc-menu mdc-menu-surface mdc-menu-surface--fullwidth">
|
||||
<ul class="mdc-list" role="listbox" aria-label="Sort By" id="sort-options-list">
|
||||
<!-- Options will be dynamically added here using JavaScript -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<!-- Panel 1 -->
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Price </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div class="flex justify-center items-center">
|
||||
<span id="priceRange" class="slider-readings"> <?php echo '$' . number_format($minPrice) . ' to $' . number_format($maxPrice) ?> </span></div>
|
||||
<div class="mdc-slider mdc-slider--range" id="autocart-price-range-field">
|
||||
<input class="mdc-slider__input" type="range" min="10000" max="80000" value="
|
||||
<?php echo isset($minPrice)? $minPrice: 10000 ?>" name="rangeStart" aria-label="Continuous range slider demo">
|
||||
<input class="mdc-slider__input" type="range" min="10000" max="80000" value="<?php echo isset($maxPrice)? $maxPrice: 80000 ?>" name="rangeEnd" aria-label="Continuous range slider demo">
|
||||
<div class="mdc-slider__track">
|
||||
<div class="mdc-slider__track--inactive"></div>
|
||||
<div class="mdc-slider__track--active">
|
||||
<div class="mdc-slider__track--active_fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Panel 2 -->
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Year </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div class="flex justify-center items-center">
|
||||
<span id="yearRange" class="slider-readings"> <?php echo $minYear .' to '.$maxYear; ?> </span>
|
||||
</div>
|
||||
<div class="mdc-slider mdc-slider--range" id="autocart-year-range-field">
|
||||
<input class="mdc-slider__input" type="range" min="<?php echo $startYear ?>" max="<?php echo $endYear ?>" value="<?php echo isset($minYear) ? $minYear : $startYear ?>" name="rangeStart" aria-label="Continuous range slider demo">
|
||||
<input class="mdc-slider__input" type="range" min="<?php echo $startYear ?>" max="<?php echo $endYear ?>" value="<?php echo isset($maxYear) ? $maxYear : $endYear ?>" name="rangeEnd" aria-label="Continuous range slider demo">
|
||||
<div class="mdc-slider__track">
|
||||
<div class="mdc-slider__track--inactive"></div>
|
||||
<div class="mdc-slider__track--active">
|
||||
<div class="mdc-slider__track--active_fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Panel 3 -->
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Kilometres </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div class="flex justify-center items-center">
|
||||
<span id="KilometresRange" class="slider-readings">19000 to 222000 </span>
|
||||
</div>
|
||||
<div class="mdc-slider mdc-slider--range" id="autocart-kilometres-range-field">
|
||||
<input class="mdc-slider__input" type="range" min="19000" max="222000" value="19000" name="rangeStart" aria-label="Continuous range slider demo">
|
||||
<input class="mdc-slider__input" type="range" min="19000" max="222000" value="222000" name="rangeEnd" aria-label="Continuous range slider demo">
|
||||
<div class="mdc-slider__track">
|
||||
<div class="mdc-slider__track--inactive"></div>
|
||||
<div class="mdc-slider__track--active">
|
||||
<div class="mdc-slider__track--active_fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Panel 4 -->
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Make </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="make-container list-none text-gray-900 w-48 bg-white text-sm font-medium rounded-lg w-full">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Model </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="model-container list-none text-gray-900 w-48 bg-white text-sm font-medium rounded-lg w-full">
|
||||
<p>Please Select a Make.</p>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Trim </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="trim-container list-none text-gray-900 bg-white text-sm font-medium rounded-lg w-full">
|
||||
<p>Please Select a Model.</p>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Body Style </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="body-type-container list-none text-gray-900 bg-white text-sm font-medium rounded-lg w-full">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Transmission </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="transmission-container list-none text-gray-900 bg-white text-sm font-medium rounded-lg w-full">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded p-2">
|
||||
<div class="flex justify-between items-center p-4">
|
||||
<div class="w-full text-left focus:outline-none transition"> Exterior Color </div>
|
||||
<svg class="w-4 h-4 hover:bg-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="exterior-color list-none text-gray-900 bg-white text-sm font-medium rounded-lg w-full">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add more filters as needed -->
|
||||
</aside>
|
||||
<!-- Main Content -->
|
||||
<div class="w-3/4 ml-1/4 p-4 pb-0 flex-grow">
|
||||
<!-- Top App Bar with Menu Button -->
|
||||
<header class="shadow-lg bg-white p-4 flex justify-between items-center rounded">
|
||||
<div class="mt-2" id="applied-filters-block"></div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<i id="grid-view" class="fas fa-th text-gray-500 cursor-pointer transition duration-300 transform hover:scale-110 hover:shadow-md ripple selected"></i>
|
||||
<i id="list-view" class="fas fa-list text-gray-500 cursor-pointer transition duration-300 transform hover:scale-110 hover:shadow-md ripple"></i>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Vehicle List -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-4 vehicles-container">
|
||||
<!-- Vehicle Cards go here -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
wp_nonce_field('autocart_nonce', 'autocart_nonce'); ?> <?php get_footer();
|
||||
|
||||
if ($make) {
|
||||
echo "<input type='hidden' id='autocart-make-field' value='$make'>";
|
||||
} ?>
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
price = new mdc.slider.MDCSlider(document.querySelector('#autocart-price-range-field'));
|
||||
year = new mdc.slider.MDCSlider(document.querySelector('#autocart-year-range-field'));
|
||||
kilometres = new mdc.slider.MDCSlider(document.querySelector('#autocart-kilometres-range-field'));
|
||||
price.listen('MDCSlider:input', updatePrice);
|
||||
year.listen('MDCSlider:input', updateYear);
|
||||
kilometres.listen('MDCSlider:input', updateMileage);
|
||||
new DisplayMode();
|
||||
dataStore = new DataStore([]);
|
||||
|
||||
new SortBy(dataStore);
|
||||
sendReq();
|
||||
});
|
||||
|
||||
</script>
|
455
autocart_assets/templates/search-form.php
Normal file
455
autocart_assets/templates/search-form.php
Normal file
@@ -0,0 +1,455 @@
|
||||
<?php
|
||||
$priceRanges = [
|
||||
[
|
||||
'text' => 'All Prices',
|
||||
'value' => [10000, 80000],
|
||||
],
|
||||
[
|
||||
'text' => 'Under $10,000',
|
||||
'value' => [0, 10000],
|
||||
],
|
||||
[
|
||||
'text' => '$10,000 - $19,999',
|
||||
'value' => [10000, 19000],
|
||||
],
|
||||
[
|
||||
'text' => '$20,000 - $29,999',
|
||||
'value' => [20000, 29000],
|
||||
],
|
||||
[
|
||||
'text' => '$30,000 - $39,999',
|
||||
'value' => [30000, 39000],
|
||||
],
|
||||
[
|
||||
'text' => '$40,000 - $49,999',
|
||||
'value' => [40000, 49000],
|
||||
],
|
||||
[
|
||||
'text' => '$50,000 - $59,999',
|
||||
'value' => [50000, 59000],
|
||||
],
|
||||
[
|
||||
'text' => '$60,000 - $69,999',
|
||||
'value' => [60000, 69000],
|
||||
],
|
||||
[
|
||||
'text' => '$70,000 - $79,999',
|
||||
'value' => [70000, 80000],
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
$startYear = 2012;
|
||||
$endYear = (int)date('Y');
|
||||
$years = range($startYear, $endYear);
|
||||
array_unshift($years, 'All Years');
|
||||
// Transform the array into an array of objects
|
||||
$years = array_map(function ($year) use ($startYear, $endYear) {
|
||||
if ($year == 'All Years') {
|
||||
return ['text' => $year, 'value' => [$startYear, $endYear]];
|
||||
} else {
|
||||
return ['text' => $year, 'value' => [$year, $year]];
|
||||
}
|
||||
}, $years);
|
||||
$carMakes = [
|
||||
"All Makes", "Acura", "Audi", "BMW", "Ford", "GMC",
|
||||
"Honda", "Hyundai", "Jaguar", "Jeep", "Kia",
|
||||
"Lexus", "Mazda", "Mercedes-Benz", "Tesla", "Toyota", "Volkswagen"
|
||||
];
|
||||
|
||||
?>
|
||||
|
||||
<form id="vehicle-search-form" name="vehicle-search-form" method="get" action="<?php echo esc_url(home_url('/index.php/autocart-results')); ?>" class="w-full bg-white px-6 lg:px-8 py-10 rounded-md">
|
||||
<h2 class="text-xl lg:text-3xl font-bold mb-2 uppercase">Find your vehicle</h2>
|
||||
<div class="sm:grid sm:grid-cols-2 gap-4">
|
||||
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="vehicle_search_make">Make</label>
|
||||
<select class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" id="vehicle_search_make" name="vehicle_search_make">
|
||||
<?php foreach ($carMakes as $index => $make) : ?>
|
||||
<option value="<?= strtolower(str_replace(' ', '', $make)); ?>" <?= $index === 0 ? 'selected' : ''; ?>>
|
||||
<?= $make; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="vehicle_search_model">Model</label>
|
||||
<select class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" id="vehicle_search_model" name="vehicle_search_model">
|
||||
<option value="" disabled="" selected="">Select Model</option>
|
||||
<!-- <option value="Camry">Camry</option>
|
||||
<option value="Accord">Accord</option>
|
||||
<option value="F-150">F-150</option>
|
||||
<option value="Silverado">Silverado</option>
|
||||
<option value="Altima">Altima</option> -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="vehicle_search_year">Year</label>
|
||||
<select class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" id="vehicle_search_year" name="vehicle_search_year">
|
||||
<?php foreach ($years as $index => $year) : ?>
|
||||
<option value="<?= htmlspecialchars(json_encode($year['value'])); ?>" <?= $index === 0 ? 'selected' : ''; ?>>
|
||||
<?= $year['text']; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="vehicle_search_body_type">Body Type</label>
|
||||
<select class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" id="vehicle_search_body_type" name="vehicle_search_body_type">
|
||||
<option value="" disabled="" selected="">Select Body Type</option>
|
||||
<!-- <option value="Sedan">Sedan</option>
|
||||
<option value="SUV">SUV</option>
|
||||
<option value="Truck">Truck</option>
|
||||
<option value="Hatchback">Hatchback</option>
|
||||
<option value="Coupe">Coupe</option> -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="vehicle_search_transmission">Transmission</label>
|
||||
<select class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" id="vehicle_search_transmission" name="vehicle_search_transmission">
|
||||
<option value="" disabled="" selected="">Select Transmission</option>
|
||||
<!-- <option value="Automatic">Automatic</option>
|
||||
<option value="Manual">Manual</option>
|
||||
<option value="CVT">CVT</option> -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="vehicle_search_trim">Trim</label>
|
||||
<select class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" id="vehicle_search_trim" name="vehicle_search_trim">
|
||||
<option value="" disabled="" selected="">Select Trim</option>
|
||||
<!-- <option value="LE">LE</option>
|
||||
<option value="EX">EX</option>
|
||||
<option value="XLT">XLT</option>
|
||||
<option value="LT">LT</option>
|
||||
<option value="SV">SV</option> -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col col-span-2">
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="autocart-kilometres-range-field">Kilometers</label>
|
||||
<!-- <input class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" type="hidden" id="autocart-kilometres-range-field" name="autocart-kilometres-range-field" min="16000" max="200000"> -->
|
||||
</div>
|
||||
<div class="range-container">
|
||||
<div class="mdc-slider mdc-slider--range" id="autocart-kilometres-range-field">
|
||||
<input class="mdc-slider__input" type="range" min="19000" max="222000" value="19000" name="rangeStart" aria-label="Continuous range slider demo">
|
||||
<input class="mdc-slider__input" type="range" min="19000" max="222000" value="222000" name="rangeEnd" aria-label="Continuous range slider demo">
|
||||
<div class="mdc-slider__track">
|
||||
<div class="mdc-slider__track--inactive"></div>
|
||||
<div class="mdc-slider__track--active">
|
||||
<div class="mdc-slider__track--active_fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="block mb-2 text-sm font-medium text-white-700 range-label" for="autocart-kilometres-range-field">19000 to 222000</label>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-700" for="autocart-price-range-field">Price</label>
|
||||
<!-- <input class="w-full px-3 py-2 border rounded-md text-gray-700 focus:outline-none focus:border-red-500" type="hidden" id="vehicle_search_price" name="vehicle_search_price" min="5000" max="70000"> -->
|
||||
</div>
|
||||
<div class="range-container">
|
||||
|
||||
<?php
|
||||
$minPrice = $priceRanges[0]['value'][0];
|
||||
$maxPrice = $priceRanges[count($priceRanges) - 1]['value'][1]; ?>
|
||||
<div class="mdc-slider mdc-slider--range" id="autocart-price-range-field">
|
||||
<input class="mdc-slider__input" type="range" min="<?php echo $minPrice; ?>" max="<?php echo $maxPrice; ?>" value="<?php echo isset($minPrice) ? $minPrice : 10000; ?>" name="rangeStart" aria-label="Continuous range slider demo">
|
||||
<input class="mdc-slider__input" type="range" min="<?php echo $minPrice; ?>" max="<?php echo $maxPrice; ?>" value="<?php echo isset($maxPrice) ? $maxPrice : 80000; ?>" name="rangeEnd" aria-label="Continuous range slider demo">
|
||||
<div class="mdc-slider__track">
|
||||
<div class="mdc-slider__track--inactive"></div>
|
||||
<div class="mdc-slider__track--active">
|
||||
<div class="mdc-slider__track--active_fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
<div class="mdc-slider__thumb">
|
||||
<div class="mdc-slider__thumb-knob"></div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="block mb-2 text-sm font-medium text-white-700 range-label" for="autocart-price-range-field"><?php echo '$' . number_format($minPrice) . ' to $' . number_format($maxPrice) ?></label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="maxPrice" id="autocart-max-price-field-input" />
|
||||
<input type="hidden" name="minPrice" id="autocart-min-price-field-input" />
|
||||
<input type="hidden" name="maxYear" id="autocart-max-year-field-input" />
|
||||
<input type="hidden" name="minYear" id="autocart-min-year-field-input" />
|
||||
<input type="hidden" name="make" id="autocart-make-field-input" />
|
||||
|
||||
<?php wp_nonce_field('autocart_nonce', 'autocart_nonce'); ?>
|
||||
|
||||
<div class="lg:col-span-2">
|
||||
<button type="submit" aria-label="Find Your Vehicle Search" class="w-full bg-red-500 hover:bg-red-800 transition-colors duration-300 text-white font-medium py-2 px-4 rounded" id="inventory-search-button">
|
||||
<i class="bi-search mr-2"></i>Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
let priceWidget;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
priceWidget = new mdc.slider.MDCSlider(document.querySelector('#autocart-price-range-field'));
|
||||
priceWidget.listen('MDCSlider:input', updatePiceWidget);
|
||||
|
||||
|
||||
kilometresWidget = new mdc.slider.MDCSlider(document.querySelector('#autocart-kilometres-range-field'));
|
||||
kilometresWidget.listen('MDCSlider:input', updateMileageWidget);
|
||||
|
||||
const year = document.querySelector('#vehicle_search_year');
|
||||
const make = document.querySelector('#vehicle_search_make');
|
||||
const model = document.querySelector('#vehicle_search_model');
|
||||
|
||||
// Initial AJAX request on document ready
|
||||
handleFieldChange([{
|
||||
property: 'body_type',
|
||||
name: 'Body Type',
|
||||
selector: '#vehicle_search_body_type'
|
||||
},
|
||||
{
|
||||
property: 'transmission_type',
|
||||
name: 'Transmission',
|
||||
selector: '#vehicle_search_transmission'
|
||||
},
|
||||
{
|
||||
property: 'make',
|
||||
name: 'Make',
|
||||
selector: '#vehicle_search_make'
|
||||
},
|
||||
{
|
||||
property: 'model',
|
||||
name: 'Model',
|
||||
selector: '#vehicle_search_model'
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
year.addEventListener('change', function() {
|
||||
debouncedHandleFieldChange([{
|
||||
property: 'body_type',
|
||||
name: 'Body Type',
|
||||
selector: '#vehicle_search_body_type'
|
||||
},
|
||||
{
|
||||
property: 'transmission_type',
|
||||
name: 'Transmission',
|
||||
selector: '#vehicle_search_transmission'
|
||||
},
|
||||
{
|
||||
property: 'make',
|
||||
name: 'Make',
|
||||
selector: '#vehicle_search_make'
|
||||
},
|
||||
{
|
||||
property: 'model',
|
||||
name: 'Model',
|
||||
selector: '#vehicle_search_model'
|
||||
}
|
||||
])
|
||||
});
|
||||
make.addEventListener('change', function() {
|
||||
debouncedHandleFieldChange([{
|
||||
property: 'model',
|
||||
name: 'Model',
|
||||
selector: '#vehicle_search_model'
|
||||
},
|
||||
{
|
||||
property: 'body_type',
|
||||
name: 'Body Type',
|
||||
selector: '#vehicle_search_body_type'
|
||||
},
|
||||
{
|
||||
property: 'transmission_type',
|
||||
name: 'Transmission',
|
||||
selector: '#vehicle_search_transmission'
|
||||
},
|
||||
])
|
||||
});
|
||||
model.addEventListener('change', function() {
|
||||
debouncedHandleFieldChange([{
|
||||
property: 'trim',
|
||||
name: 'Tim',
|
||||
selector: '#vehicle_search_trim'
|
||||
}])
|
||||
});
|
||||
|
||||
const btns = document.getElementsByClassName("ripple-effect");
|
||||
|
||||
for (const btn of btns) {
|
||||
btn.addEventListener("click", rippleEffect);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function updateMileageWidget() {
|
||||
document.querySelector('label.range-label[for="autocart-kilometres-range-field"]').textContent = `${kilometresWidget.getValueStart()} to ${kilometresWidget.getValue()}`;
|
||||
debouncedHandleFieldChange();
|
||||
}
|
||||
|
||||
function updatePiceWidget() {
|
||||
document.querySelector('label.range-label[for="autocart-price-range-field"]').textContent = `${priceWidget.getValueStart()} to ${priceWidget.getValue()}`;
|
||||
debouncedHandleFieldChange();
|
||||
}
|
||||
|
||||
|
||||
function debounce(func, delay) {
|
||||
let timeout;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const debouncedHandleFieldChange = debounce(
|
||||
function(inputs) {
|
||||
handleFieldChange(inputs);
|
||||
}, 300);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function handleFieldChange(inputs = []) {
|
||||
// const price = document.querySelector('#autocart-price-field');
|
||||
const year = document.querySelector('#vehicle_search_year');
|
||||
const make = document.querySelector('#vehicle_search_make');
|
||||
|
||||
// let priceRange = JSON.parse(priceWidget.value);
|
||||
// let minPrice = priceRange[0];
|
||||
// let maxPrice = priceRange[1];
|
||||
|
||||
let yearRange = JSON.parse(year.value);
|
||||
console.log('yearRange', yearRange)
|
||||
let minYear = yearRange[0];
|
||||
let maxYear = yearRange[1];
|
||||
|
||||
document.getElementById('autocart-max-price-field-input').value = priceWidget.getValue();;
|
||||
document.getElementById('autocart-min-price-field-input').value = priceWidget.getValueStart();
|
||||
document.getElementById('autocart-max-year-field-input').value = maxYear;
|
||||
document.getElementById('autocart-min-year-field-input').value = minYear;
|
||||
document.getElementById('autocart-make-field-input').value = make.value;
|
||||
|
||||
// var formData = {
|
||||
// 'action': 'handle_autocart_form',
|
||||
// 'minPrice': priceWidget.getValueStart(),
|
||||
// 'maxPrice': priceWidget.getValue(),
|
||||
// 'make': make.value,
|
||||
// 'minYear': minYear,
|
||||
// 'maxYear': maxYear,
|
||||
// 'autocart_nonce': jQuery('#autocart_nonce').val(),
|
||||
// };
|
||||
|
||||
var formData = {
|
||||
'action': 'handle_autocart_form',
|
||||
'minPrice': priceWidget.getValueStart(),
|
||||
'maxPrice': priceWidget.getValue(),
|
||||
'minKilometres': kilometresWidget.getValueStart(),
|
||||
'maxKilometres': kilometresWidget.getValue(),
|
||||
'minYear': minYear,
|
||||
'maxYear': maxYear,
|
||||
'make': make.value,
|
||||
'autocart_nonce': jQuery('#autocart_nonce').val(),
|
||||
};
|
||||
jQuery("#inventory-search-button").text("Loading...");
|
||||
jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: ajax_object.ajax_url,
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
const vehicles = response.data.vehicles;
|
||||
if (inputs) {
|
||||
inputs.forEach(element => {
|
||||
createOptions(vehicles, element);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
jQuery("#inventory-search-button").text("Search (" + vehicles.length + ")");
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log(xhr.responseText);
|
||||
console.log('Status: ' + status);
|
||||
console.log('Error: ' + error);
|
||||
jQuery("#inventory-search-button").text("Error");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function rippleEffect(event) {
|
||||
const btn = event.currentTarget;
|
||||
btn.classList.add("overflow-hidden", "shadow", "relative");
|
||||
|
||||
const circle = document.createElement("span");
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const diameter = Math.max(btn.clientWidth, btn.clientHeight);
|
||||
const radius = diameter / 2;
|
||||
|
||||
const offsetX = event.clientX - rect.left;
|
||||
const offsetY = event.clientY - rect.top;
|
||||
|
||||
circle.style.width = circle.style.height = `${diameter}px`;
|
||||
circle.style.left = `${offsetX - radius}px`;
|
||||
circle.style.top = `${offsetY - radius}px`;
|
||||
circle.classList.add("ripple");
|
||||
|
||||
const ripple = btn.getElementsByClassName("ripple")[0];
|
||||
|
||||
if (ripple) {
|
||||
ripple.remove();
|
||||
}
|
||||
btn.appendChild(circle);
|
||||
}
|
||||
|
||||
function createOptions(vehicles, input) {
|
||||
if (vehicles.length) {
|
||||
const element = document.querySelector(input.selector);
|
||||
|
||||
// Remove existing options only
|
||||
const existingOptions = element.querySelectorAll('option');
|
||||
existingOptions.forEach(option => option.remove());
|
||||
|
||||
const counts = vehicles.reduce((acc, vehicle) => {
|
||||
const value = vehicle[input.property];
|
||||
acc[value] = (acc[value] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Sort the property values alphabetically
|
||||
const sortedValues = Object.keys(counts).sort();
|
||||
|
||||
// Add "All" at the beginning
|
||||
sortedValues.unshift(`All ${input.name}s`);
|
||||
|
||||
// Create and append options
|
||||
sortedValues.forEach(value => {
|
||||
var option = document.createElement("option");
|
||||
option.text = value;
|
||||
option.value = value;
|
||||
option.style.textTransform = "capitalize";
|
||||
element.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
689
autocart_assets/templates/single-vehicle.php
Normal file
689
autocart_assets/templates/single-vehicle.php
Normal file
@@ -0,0 +1,689 @@
|
||||
<?php
|
||||
/*
|
||||
Template Name: Vehicle Template
|
||||
*/
|
||||
get_header();
|
||||
|
||||
?>
|
||||
<style>
|
||||
.modal {
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
body.modal-active {
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible !important;
|
||||
}
|
||||
|
||||
span.ripple {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
transform: scale(0);
|
||||
animation: ripple 600ms linear;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
to {
|
||||
transform: scale(4);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
// Retrieve the autocart response data
|
||||
$vehicle_id = isset($_GET['vehicle_id']) ? urldecode($_GET['vehicle_id']) : '';
|
||||
|
||||
if ($vehicle_id) {
|
||||
// Retrieve the external token from WordPress options
|
||||
$external_token = get_option('autocart_server_token');
|
||||
$pluginToken = get_option('autocart_plugin_token'); // Retrieve the token from where you stored it
|
||||
|
||||
|
||||
|
||||
if ($external_token) {
|
||||
// Make an external request to get vehicle details
|
||||
$vehicle_details_url = REMOTE_SERVER_URL . '/api/v1/wp/vehicle/' . $vehicle_id;
|
||||
$vehicle_details_response = wp_remote_get($vehicle_details_url, array(
|
||||
'headers' => array(
|
||||
'Authorization' => $external_token,
|
||||
'Plugin' => $pluginToken
|
||||
),
|
||||
));
|
||||
|
||||
// Make another request to get vehicle images
|
||||
$vehicle_images_url = REMOTE_SERVER_URL . '/api/v1/companies/1/vehicles/' . $vehicle_id . '/images';
|
||||
$vehicle_images_response = wp_remote_get($vehicle_images_url, array(
|
||||
'headers' => array(
|
||||
'Authorization' => $external_token,
|
||||
'Plugin' => $pluginToken
|
||||
|
||||
),
|
||||
));
|
||||
|
||||
$vehicle_features = REMOTE_SERVER_URL . '/api/v1/companies/1/vehicles/' . $vehicle_id . '/vehiclefeatures';
|
||||
$vehicle_features_response = wp_remote_get($vehicle_features, array(
|
||||
'headers' => array(
|
||||
'Authorization' => $external_token,
|
||||
'Plugin' => $pluginToken
|
||||
|
||||
),
|
||||
));
|
||||
|
||||
if (
|
||||
!is_wp_error($vehicle_details_response) && !is_wp_error($vehicle_images_response)
|
||||
&& wp_remote_retrieve_response_code($vehicle_details_response) === 200
|
||||
&& wp_remote_retrieve_response_code($vehicle_images_response) === 200
|
||||
&& wp_remote_retrieve_response_code($vehicle_features_response) === 200
|
||||
) {
|
||||
|
||||
// Decode and print the vehicle details response
|
||||
$vehicle_details = json_decode(wp_remote_retrieve_body($vehicle_details_response), true);
|
||||
$vehicle_features = json_decode(wp_remote_retrieve_body($vehicle_features_response), true);
|
||||
|
||||
// Decode and print the vehicle images response
|
||||
$vehicle_images = json_decode(wp_remote_retrieve_body($vehicle_images_response), true);
|
||||
|
||||
|
||||
// var_dump($searchData); exit;
|
||||
$filters = array(
|
||||
'body_type' => $vehicle_details['body_type']
|
||||
);
|
||||
|
||||
$similar_vehicles_response = wp_remote_post(REMOTE_SERVER_URL . '/api/v1/wp', array(
|
||||
'body' => json_encode($filters), // The filters array will be sent in the body of the request
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/json', // Make sure to set the Content-Type header appropriately
|
||||
'Authorization' => $external_token,
|
||||
'Plugin' => $pluginToken
|
||||
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!is_wp_error($similar_vehicles_response) && wp_remote_retrieve_response_code($similar_vehicles_response) === 200) {
|
||||
$similar_vehicles = json_decode(wp_remote_retrieve_body($similar_vehicles_response), true);
|
||||
$filtered_vehicles = array_filter($similar_vehicles, function ($vehicle) use ($vehicle_details) {
|
||||
return $vehicle['id_vehicle'] !== $vehicle_details['id_vehicle'];
|
||||
});
|
||||
|
||||
// echo '<pre>';
|
||||
// print_r($filtered_vehicles);
|
||||
// echo '</pre>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Initialize an empty array to store grouped features
|
||||
$groupedFeatures = array();
|
||||
|
||||
// Loop through the features and group them by category
|
||||
foreach ($vehicle_features as $feature) {
|
||||
$category = $feature['category'];
|
||||
|
||||
// Check if the category key exists in the grouped array
|
||||
if (!array_key_exists($category, $groupedFeatures)) {
|
||||
// If not, create an empty array for the category
|
||||
$groupedFeatures[$category] = array();
|
||||
}
|
||||
|
||||
// Add the feature to the corresponding category
|
||||
$groupedFeatures[$category][] = $feature;
|
||||
}
|
||||
|
||||
// Now $groupedFeatures contains features grouped by category
|
||||
// print_r($groupedFeatures);
|
||||
|
||||
// echo '<pre>';
|
||||
// print_r($searchData);
|
||||
// echo '</pre>';
|
||||
|
||||
} else {
|
||||
echo '<div class="bg-gray-100 dark:bg-gray-800 py-8">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex flex-col md:flex-row"><p>Error retrieving vehicle details or images.</p></div></div</div>';
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
echo '<div class="bg-gray-100 dark:bg-gray-800 py-8">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex flex-col md:flex-row"><p>Error: External token not found.</p>/div></div</div>';
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
echo '<div class="bg-gray-100 dark:bg-gray-800 py-8">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex flex-col md:flex-row"><p>No vehicle found.</p></div></div</div>';
|
||||
exit;
|
||||
} ?>
|
||||
|
||||
<?php
|
||||
// Assuming you have the images in the $vehicle_images array
|
||||
$image_title = !empty($vehicle_images[0]['title']) ? $vehicle_images[0]['title'] : '';
|
||||
$image_description = !empty($vehicle_images[0]['description']) ? $vehicle_images[0]['description'] : '';
|
||||
?>
|
||||
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<div class="grid grid-cols-2">
|
||||
|
||||
<div class="relative group cursor-pointer">
|
||||
<img src="<?php echo $vehicle_images[0]['url']; ?>" alt="<?php echo $image_title; ?>" class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black opacity-0 group-hover:opacity-75 transition duration-500 ease-in-out"></div>
|
||||
<div class="absolute inset-0 bottom-0 p-4 text-white group-hover:opacity-100 transition duration-500 ease-in-out" onclick="showFeaturedImage(0);openFeaturedImage()">
|
||||
<h3><?php echo $image_title; ?></h3>
|
||||
<p><?php echo $image_description; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative grid grid-cols-2">
|
||||
<?php
|
||||
|
||||
$end = (!empty($vehicle_images) && count($vehicle_images) >= 5) ? 5 : count($vehicle_images);
|
||||
// Start loop from index 1 to skip the first image
|
||||
for ($i = 1; $i < $end; $i++) {
|
||||
$image = $vehicle_images[$i];
|
||||
// Extract image URL, title, and description
|
||||
$image_url = $image['url'];
|
||||
|
||||
?>
|
||||
<div class="relative group cursor-pointer">
|
||||
<img src="<?php echo $image_url; ?>" alt="<?php echo $image_title; ?>" class="w-full h-full object-cover">
|
||||
<div class="absolute inset-0 bg-black opacity-0 group-hover:opacity-75 transition duration-500 ease-in-out"></div>
|
||||
<div class="absolute inset-0 bottom-0 p-4 text-white group-hover:opacity-100 transition duration-500 ease-in-out" onclick="showFeaturedImage(<?php echo $i; ?>);openFeaturedImage()">
|
||||
<h3><?php echo $image_title; ?></h3>
|
||||
<p><?php echo $image_description; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="bg-gray-100 dark:bg-gray-800 py-4">
|
||||
<!-- Single Car Information -->
|
||||
<div class="lg:grid grid-cols-3 relative">
|
||||
|
||||
<!-- Vehicle Details -->
|
||||
<div class="mx-auto col-span-2">
|
||||
|
||||
<!-- Single Car -->
|
||||
<div class="m-4">
|
||||
<h2 class="text-2xl font-bold text-gray-800 dark:text-white mb-2">
|
||||
<!-- title -->
|
||||
<?php echo $vehicle_details['year'] . ' ' . $vehicle_details['make'] . ' ' . $vehicle_details['model'] . ' ' . $vehicle_details['trim'] . ' - ' . $vehicle_details['exterior_color'] . ' ' . $vehicle_details['interior_color']; ?>
|
||||
</h2>
|
||||
<p> Stock #: <?php echo $vehicle_details['stock_number'] ?></p>
|
||||
<hr class="my-4 border-gray-300">
|
||||
<span class="font-bold text-gray-700 dark:text-gray-300">About this vehicle</span>
|
||||
<div class="flex items-center mt-4">
|
||||
<div class="text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-tachometer-alt w-3 h-3 me-3"></i>
|
||||
<span class="label">Kilometres</span>
|
||||
<span class="value"><?php echo number_format($vehicle_details['mileage']) ?></span>
|
||||
</button>
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-car-side w-3 h-3 me-3"></i>
|
||||
<span class="label">Body Style</span>
|
||||
<span class="value"><?php echo $vehicle_details['body_type'] ?></span>
|
||||
</button>
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-cogs w-3 h-3 me-3"></i>
|
||||
<span class="label">Engine</span>
|
||||
<span class="value"><?php echo $vehicle_details['engine'] ?></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-palette w-3 h-3 me-3"></i>
|
||||
<span class="label">Exterior Colour</span>
|
||||
<span class="value"><?php echo $vehicle_details['exterior_color'] ?></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 122.88" width="20" height="20">
|
||||
<path d="M61.44,0A61.46,61.46,0,1,1,18,18,61.23,61.23,0,0,1,61.44,0Zm4.07,82.09a6.67,6.67,0,1,1-8.14,0V68.62H42.31V82.09a6.67,6.67,0,1,1-8.14,0V46.17a6.67,6.67,0,1,1,8.14,0V60.48H57.37V46.17a6.67,6.67,0,1,1,8.14,0V60.48H80.57V46.17a6.67,6.67,0,1,1,8.14,0V64a4.41,4.41,0,0,1,0,.52,4.07,4.07,0,0,1-4.07,4.07H65.51V82.09Zm33-57.76a52.46,52.46,0,1,0,15.38,37.11A52.29,52.29,0,0,0,98.55,24.33Z" />
|
||||
</svg>
|
||||
<span class="label">Transmission</span>
|
||||
<span class="value"><?php echo $vehicle_details['transmission_type'] ?></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-users w-3 h-3 me-3"></i>
|
||||
<span class="label"># of Passengers</span>
|
||||
<span class="value"><?php echo $vehicle_details['optional_seating'] ?></span>
|
||||
</button>
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 63.67" width="20" height="10">
|
||||
<path d="M7.69,25.88c-8.3-4.22-7.35-8.92,1-8.43L10.55,21,14.4,9c1.51-4.71,4-9,9-9H66.89c4.24,0,6.94,3.09,8.37,7A9.88,9.88,0,0,1,79,6.69h24.8c3.38,0,6.2.12,9.29,2.32a12.64,12.64,0,0,1,2.66,2.57,32.64,32.64,0,0,1,3.39,6.15l3.2,6.66a2.53,2.53,0,0,1,.25,2.64l-2.52,8.19c-.2.45-.6.64-1.29.5a10.51,10.51,0,0,0-11.6,9c-.06.53,0,1.22-.44,1.54a1.48,1.48,0,0,1-1.09.15H87.15c-.1,2.72-.3,5.83-.52,9.46v5a2.81,2.81,0,0,1-2.81,2.8h-12a2.81,2.81,0,0,1-2.81-2.8V58.62H18.18v2.25a2.82,2.82,0,0,1-2.81,2.8H3.4a2.81,2.81,0,0,1-2.8-2.8V54.4a3,3,0,0,1,0-.43c-.91-11.62-2.19-22.1,7.06-28.09ZM86.78,37.44H85.31a.84.84,0,0,0-.85,1,1.07,1.07,0,0,0,1,1h19.89a.83.83,0,0,0,.84-1h0a1.07,1.07,0,0,0-1-1Zm-2.32,1ZM79.27,11h27.56a7.4,7.4,0,0,1,4.78,3.28l5.5,11.26.11,1.07H83.3L79.27,11Zm32.41,19.19h5.37a1.07,1.07,0,0,1,1,1h0a.83.83,0,0,1-.84.95h-5.37a1.07,1.07,0,0,1-1-.95h0a.83.83,0,0,1,.84-1ZM23,39.66,12.34,38.32C9.83,38,9.16,39.1,10,41.26l1.15,2.79a4.06,4.06,0,0,0,1.44,1.61,4.82,4.82,0,0,0,2.38.65l9.48.08c2.29,0,3.28-.92,2.56-3A5.11,5.11,0,0,0,23,39.66Zm41.27,0,10.63-1.34c2.51-.28,3.19.78,2.33,2.94l-1.15,2.79a4.06,4.06,0,0,1-1.44,1.61,4.82,4.82,0,0,1-2.38.65l-9.48.08c-2.29,0-3.28-.92-2.56-3a5.13,5.13,0,0,1,4-3.71ZM14.54,23.59h60l-2.9-12c-.79-3.66-3.08-6.84-6.84-6.84H25c-3.76,0-5.69,3.26-6.85,6.84l-3.61,12v0Z" style="fill: #000;"></path>
|
||||
</svg>
|
||||
<span class="label">Doors</span>
|
||||
<span class="value"><?php echo $vehicle_details['doors'] ?></span>
|
||||
</button>
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-gas-pump w-3 h-3 me-3"></i>
|
||||
<span class="label">Fuel Type</span>
|
||||
<span class="value"><?php echo $vehicle_details['fuel_type'] ?></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-clipboard-check w-3 h-3 me-3"></i>
|
||||
<span class="label">Condition</span>
|
||||
<span class="value">Used</span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="relative inline-flex items-center w-full px-4 py-2 text-sm font-medium border-b border-gray-200 hover:bg-gray-100 hover:text-black-700 dark:border-gray-600 dark:hover:bg-gray-600 dark:hover:text-white text-black flex-row gap-2">
|
||||
<i class="fas fa-palette w-3 h-3 me-3"></i>
|
||||
<span class="label">Interior Colour</span>
|
||||
<span class="value"><?php echo $vehicle_details['interior_color'] ?></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="m-4">
|
||||
<!-- <span class="font-bold text-gray-700 dark:text-gray-300">Standard equipment</span> -->
|
||||
<div id="accordion-equipment" data-accordion="collapse" data-active-classes="bg-white dark:bg-gray-900 text-gray-900 dark:text-white mt-4 transition-all duration-300" data-inactive-classes="text-gray-500 dark:text-gray-400">
|
||||
<h2 id="accordion-features-heading-1" class="py-2 my-0">
|
||||
<button type="button" class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border-b border-gray-200 gap-3 focus:bg-gray-700 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white bg-white round-lg focus:text-white" data-accordion-target="#accordion-features-body-1" aria-expanded="true" aria-controls="accordion-features-body-1">
|
||||
<span>Description</span>
|
||||
<svg data-accordion-icon class="w-3 h-3 rotate-0 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5" />
|
||||
</svg>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="accordion-features-body-1" class="text-gray-900 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white transition-all duration-300" aria-labelledby="accordion-features-heading-1">
|
||||
<div class="bg-white px-4 py-2 border-b border-gray-200 dark:border-gray-700 rounded-lg ">
|
||||
<p class="mt-4 text-gray-600 dark:text-gray-300 text-sm mb-4">
|
||||
<?php echo implode("<br><br>", explode(' ', $vehicle_details['comments'])); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Options -->
|
||||
<div class="m-4">
|
||||
<p class="mb-4 font-bold text-gray-700 dark:text-gray-300">Options</p>
|
||||
<div id="accordion-flush" data-accordion="collapse" data-active-classes="bg-white dark:bg-gray-900 text-gray-900 dark:text-white mt-4 transition-all duration-300" data-inactive-classes="text-gray-500 dark:text-gray-400">
|
||||
<div class="md:grid grid-cols-3">
|
||||
<ul class="col-span-1 flex-column space-y-4 text-sm font-medium text-gray-500 dark:text-gray-400 md:me-4 mb-4 md:mb-0">
|
||||
<?php foreach ($groupedFeatures as $category => $features) : ?>
|
||||
<?php
|
||||
// Remove special characters and spaces from category for use in HTML IDs
|
||||
$categorySlug = strtolower(preg_replace('/[^a-z0-9]/', '', $category));
|
||||
?>
|
||||
<button class="inline-flex items-center text-left px-4 py-3 rounded-lg hover:text-gray-900 bg-gray-50 hover:bg-gray-300 w-full dark:hover:bg-gray-700 dark:hover:text-white text-gray-900 border border-gray-200 dark:bg-gray-700 dark:border-gray-600 dark:text-white transition-all duration-300 ripple-effect" data-tab-target="#tab-<?= $categorySlug ?>-body-1" data-te-ripple-init>
|
||||
<?= $category ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<div class="sticky-container col-span-2 p-6 bg-gray-50 text-medium text-gray-900 border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white transition-all duration-300 top-32 lg:top-56" id="tabs-viewer">
|
||||
|
||||
</div>
|
||||
|
||||
<?php foreach ($groupedFeatures as $category => $features) : ?>
|
||||
<?php
|
||||
// Remove special characters and spaces from category for use in HTML IDs
|
||||
$categorySlug = strtolower(preg_replace('/[^a-z0-9]/', '', $category));
|
||||
?>
|
||||
|
||||
<div class="p-6 bg-gray-50 text-medium text-gray-500 dark:text-gray-400 dark:bg-gray-800 rounded-lg w-full tab-content hidden" id="tab-<?= $categorySlug ?>-body-1">
|
||||
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2"><?= $category ?></h3>
|
||||
<?php foreach ($features as $feature) : ?>
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400"><?= $feature['name'] . (isset($feature['value']) && !empty($feature['value']) ? ': <span>' . $feature['value'] . '</span>' : '') ?> </p>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="p-4 text-sm text-gray-800 rounded-lg bg-gray-50 dark:bg-gray-800 dark:text-gray-300 mt-4" role="alert">
|
||||
<span class="font-medium">*Standard Equipment</span> is the default equipment supplied for the Make and Model of this vehicle, but may not represent the final vehicle with additional / altered equipment options.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Vehicle Financing and Booking Widget -->
|
||||
<div class="px-4 col-span-1 relative">
|
||||
<div class="sticky-container grid gap-3 top-56">
|
||||
<button role="button" class="prev px-2 py-2 rounded-full bg-neutral-100 text-neutral-900 group absolute top-1/4 left-4 transform -translate-y-1/2 z-10" aria-label="prev" onclick="navigateSlider(-1)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 group-active:-translate-x-2 transition-all duration-200 ease-linear">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
<div id="featuredImageContainer" class="ripple-effect" style="width: 100%;" onclick="openFeaturedImage()">
|
||||
<img id="featuredImage" class="cursor-pointer h-auto max-w-full rounded-lg " src="#" alt="">
|
||||
</div>
|
||||
|
||||
<div id="thumbnailContainer" class="no-scrollbar slides overflow-scroll smooth-scroll relative w-full whitespace-nowrap touch-pan-x before:shrink-0 after:shrink-0 before:w-[36vw] after:w-[36vw] snap-mandatory flex snap-x gap-2 border border-transparent rounded-lg dark:bg-gray-700 dark:border-transparent dark:text-white w-full">
|
||||
<?php
|
||||
// Display thumbnail images
|
||||
foreach ($vehicle_images as $index => $image) {
|
||||
if (isset($image['url'])) { ?>
|
||||
<div class='slide flex-shrink-0 w-[70vw] h-[calc(70vw*1.5)] sm:w-[40vw] sm:h-[calc(40vw*1.5)] md:w-[25vw] md:h-[calc(25vw*1.5)] overflow-clip relative snap-center rounded-3xl w-16 h-16'>
|
||||
<a href="<?php echo esc_url($image['url']) ?>" class="glightbox" data-glightbox="gallery">
|
||||
<img src="<?php echo esc_url($image['url']) ?>" alt="Image <?php echo $index; ?>" class='block w-full h-full object-cover object-center absolute right-0 animate-parallax h-full w-full object-cover rounded-lg thumbnail' onclick="showFeaturedImage(<?php echo $index; ?>)" />
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<button role="button" class="next px-2 py-2 rounded-full bg-neutral-100 text-neutral-900 group absolute top-1/4 right-4 transform -translate-y-1/2 z-10" aria-label="next" onclick="navigateSlider(1)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 group-active:translate-x-2 transition-all duration-200 ease-linear">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="flex items-center flex-wrap">
|
||||
<div class="flex justify-between items-end w-full gap-4 relative whitespace-nowrap snap-x">
|
||||
<div class="flex justify-between items-end w-full gap-4">
|
||||
<div class="flex-1 text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white w-full">
|
||||
<div class="p-4">
|
||||
<span class="text-xl font-semibold">
|
||||
<span class="text-gray-600 dark:text-gray-300">
|
||||
<?php echo $formattedPrice = '$ ' . number_format($vehicle_details['advertise_price']); ?>
|
||||
</span>
|
||||
</span>
|
||||
<p class="text-xs text-gray-400">+ taxes and fees</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white w-full">
|
||||
<div class="p-4">
|
||||
<span class="text-xl font-semibold">
|
||||
<span class="text-gray-600 dark:text-gray-300 paymentSpan">
|
||||
<?php echo $formattedPrice = '$ ' . number_format(416); ?>
|
||||
</span>
|
||||
<i class="fas fa-calculator cursor-pointer" onclick="openFinanceModal()"></i>
|
||||
</span>
|
||||
<p class="text-xs text-gray-400"><span class="rateSpan">7.99</span> for <span class="loanTermSpan">12</span> Months </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center flex-wrap">
|
||||
<div class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border-b border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white bg-white round-lg focus:text-white flex-col gap-3">
|
||||
<span class="text-red-600 self-start">Estimate Your Payments <i class="fas fa-greater-than text-xs"></i></span>
|
||||
<!-- Primary Button -->
|
||||
<button class="w-full bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline-gray focus:border-gray-700 ripple-effect" onclick="openAppointmentModal()">
|
||||
Book a Private Appointment
|
||||
</button>
|
||||
|
||||
<!-- Secondary Button -->
|
||||
<button class="w-full mt-2 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline-gray focus:border-gray-400 ripple-effect">
|
||||
Apply for Financing
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Similar Vehicles -->
|
||||
<div class="flex flex-col md:flex-row max-w-7xl overflow-scroll no-scrollbar">
|
||||
<div class="mx-auto px-4 mt-10">
|
||||
<h2 class="text-2xl font-bold mb-4 px-4">Similar Vehicles</h2>
|
||||
<?php if (!empty($filtered_vehicles)) : ?>
|
||||
<div class="relative overflow-hidden rounded-lg">
|
||||
<!-- Left scroll button -->
|
||||
<button class="absolute left-5 z-10 p-2 bg-neutral-100 rounded-full hover:bg-gray-200 top-1/3" onclick="scrollCarouselLeft()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 group-active:-translate-x-2 transition-all duration-200 ease-linear">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<!-- Right scroll button -->
|
||||
<button class="absolute right-5 z-10 p-2 bg-neutral-100 rounded-full hover:bg-gray-200 top-1/3" onclick="scrollCarouselRight()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 group-active:translate-x-2 transition-all duration-200 ease-linear">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<!-- Vehicle cards -->
|
||||
<div class="flex whitespace-nowrap overflow-x-scroll scroll-smooth px-4 pb-4 flex-stretch space-x-4">
|
||||
<?php foreach ($filtered_vehicles as $vehicle) : ?>
|
||||
<div class="flex-shrink-0 w-80">
|
||||
<div class="bg-white rounded-lg shadow-md hover:shadow-lg transition duration-200 flex flex-col justify-between h-full">
|
||||
<img src="<?php echo $vehicle['landingImage']; ?>" alt="Vehicle image" class="w-full object-cover rounded-tl-md rounded-tr-md mb-4">
|
||||
<div class="p-4 ">
|
||||
<div class="mb-2">
|
||||
<h3 class="text-lg font-medium text-gray-700 truncate"><?php echo $vehicle['year'] . ' ' . $vehicle['make'] . ' ' . $vehicle['model'] . ' ' . implode(', ', $vehicle['trim']); ?></h3>
|
||||
<p class="text-sm text-gray-500"><?php echo number_format($vehicle['mileage']) . ' miles - ' . $vehicle['exterior_color'] . ' - ' . $vehicle['transmission_type']; ?></p>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-base font-medium text-gray-700">$<?php echo number_format($vehicle['advertise_price']); ?></span>
|
||||
<a href="vehicle-details/?vehicle_id=<?php echo $vehicle['id_vehicle']; ?>" class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-gray-700 rounded-lg hover:bg-gray-800 hover:text-white">View Details</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<p>No similar vehicles found.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
?>
|
||||
|
||||
<script>
|
||||
// modal
|
||||
const handleFinanceChanges = (financeForm) => {
|
||||
if (document.querySelectorAll('.paymentSpan').length) {
|
||||
document.querySelectorAll('.paymentSpan').forEach(el => {
|
||||
el.textContent = (Number(financeForm.payment) || 0).toLocaleString('en-CA', {
|
||||
style: 'currency',
|
||||
currency: 'CAD'
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// document.querySelectorAll('.paymentFrequencySpan').forEach(el=>{
|
||||
// // el.textContent = ${financeForm.frequencyMap[financeForm.paymentFrequency]}
|
||||
// })
|
||||
|
||||
if (document.querySelectorAll('.loanTermSpan').length) {
|
||||
document.querySelectorAll('.loanTermSpan').forEach(el => {
|
||||
el.innerHTML = `${financeForm.loanTerm}`;
|
||||
})
|
||||
}
|
||||
|
||||
if (document.querySelectorAll('.rateSpan').length) {
|
||||
document.querySelectorAll('.rateSpan').forEach(el => {
|
||||
el.innerHTML = `${financeForm.intRate}`;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
const vehicleDetails = <?php echo json_encode($vehicle_details); ?>;
|
||||
const appointmentForm = new AppointmentForm();
|
||||
const financeForm = new FinanceForm(vehicleDetails, handleFinanceChanges);
|
||||
const modal = new Modal(<?php echo $vehicle_id ?>);
|
||||
document.body.appendChild(modal.initModal());
|
||||
|
||||
|
||||
|
||||
function openAppointmentModal() {
|
||||
modal.toggleModal(appointmentForm);
|
||||
appointmentForm.initializeFormInputs();
|
||||
}
|
||||
|
||||
function openFinanceModal() {
|
||||
modal.toggleModal(financeForm);
|
||||
financeForm.initializeFormInputs();
|
||||
}
|
||||
|
||||
// carousel
|
||||
let lightbox;
|
||||
let selectedSlide = 0;
|
||||
|
||||
function openFeaturedImage() {
|
||||
lightbox = GLightbox({
|
||||
selector: 'glightbox',
|
||||
touchNavigation: true,
|
||||
loop: true,
|
||||
startAt: selectedSlide, // Set the starting slide
|
||||
onClose: function() {
|
||||
const slideElements = document.querySelectorAll('#thumbnailContainer .thumbnail');
|
||||
if (slideElements.length >= lightbox.index) {
|
||||
const selectedSlideElement = slideElements[lightbox.index];
|
||||
showFeaturedImage(lightbox.index)
|
||||
}
|
||||
}
|
||||
});
|
||||
lightbox.open();
|
||||
}
|
||||
|
||||
function showFeaturedImage(idx) {
|
||||
event.preventDefault();
|
||||
if (typeof lightbox !== 'undefined') {
|
||||
lightbox.destroy();
|
||||
}
|
||||
setFeatureImage(idx);
|
||||
}
|
||||
|
||||
function setFeatureImage(idx) {
|
||||
const slideElements = document.querySelectorAll('#thumbnailContainer .thumbnail');
|
||||
if (slideElements.length >= idx) {
|
||||
const selectedSlideElement = slideElements[idx];
|
||||
document.getElementById('featuredImage').src = selectedSlideElement.src;
|
||||
selectedSlide = idx;
|
||||
}
|
||||
}
|
||||
|
||||
function navigateSlider(direction) {
|
||||
selectedSlide += direction;
|
||||
const slideElements = document.querySelectorAll('#thumbnailContainer .thumbnail');
|
||||
if (selectedSlide < 0) {
|
||||
selectedSlide = slideElements.length - 1;
|
||||
} else if (selectedSlide >= slideElements.length) {
|
||||
selectedSlide = 0;
|
||||
}
|
||||
setFeatureImage(selectedSlide);
|
||||
}
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
|
||||
var queryString = window.location.search;
|
||||
var urlParams = new URLSearchParams(queryString);
|
||||
var filters = JSON.parse(decodeURIComponent(urlParams.get('filters')));
|
||||
console.log(filters);
|
||||
setFeatureImage(0);
|
||||
const accordionHeaders = document.querySelectorAll('[data-accordion-target]');
|
||||
accordionHeaders.forEach(header => {
|
||||
header.addEventListener('click', function() {
|
||||
const target = document.querySelector(this.getAttribute('data-accordion-target'));
|
||||
target.classList.toggle('max-h-0');
|
||||
target.classList.toggle('overflow-hidden');
|
||||
const expanded = target.classList.contains('max-h-0') ? 'false' : 'true';
|
||||
this.setAttribute('aria-expanded', expanded);
|
||||
const icon = this.querySelector('[data-accordion-icon]');
|
||||
icon.style.transform = expanded === 'true' ? 'rotate(0deg)' : 'rotate(180deg)';
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// ripple effect
|
||||
|
||||
const btns = document.getElementsByClassName("ripple-effect");
|
||||
|
||||
for (const btn of btns) {
|
||||
btn.addEventListener("click", rippleEffect);
|
||||
}
|
||||
|
||||
const tabs = document.querySelectorAll('[data-tab-target]');
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const target = document.querySelector(tab.getAttribute('data-tab-target'));
|
||||
|
||||
document.querySelector('#tabs-viewer').innerHTML = target.innerHTML;
|
||||
|
||||
tabs.forEach(t => {
|
||||
t.classList.remove('text-white', 'bg-gray-700', 'active');
|
||||
t.classList.add('bg-gray-50');
|
||||
});
|
||||
|
||||
tab.classList.add('text-white', 'bg-gray-700', 'active');
|
||||
tab.classList.remove('bg-gray-50');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
function rippleEffect(event) {
|
||||
const btn = event.currentTarget;
|
||||
// Add classes to the selected element
|
||||
btn.classList.add("overflow-hidden", "shadow", "relative");
|
||||
|
||||
const circle = document.createElement("span");
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const diameter = Math.max(btn.clientWidth, btn.clientHeight);
|
||||
const radius = diameter / 2;
|
||||
|
||||
// Calculate the position relative to the button element
|
||||
const offsetX = event.clientX - rect.left;
|
||||
const offsetY = event.clientY - rect.top;
|
||||
|
||||
circle.style.width = circle.style.height = `${diameter}px`;
|
||||
circle.style.left = `${offsetX - radius}px`;
|
||||
circle.style.top = `${offsetY - radius}px`;
|
||||
circle.classList.add("ripple");
|
||||
|
||||
const ripple = btn.getElementsByClassName("ripple")[0];
|
||||
|
||||
if (ripple) {
|
||||
ripple.remove();
|
||||
}
|
||||
|
||||
btn.appendChild(circle);
|
||||
}
|
||||
|
||||
|
||||
function scrollCarouselLeft() {
|
||||
|
||||
const carousel = document.querySelector('.overflow-x-scroll');
|
||||
carousel.scrollTo({
|
||||
left: carousel.scrollLeft - carousel.offsetWidth,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
// const carousel = document.querySelector('.overflow-x-scroll');
|
||||
// carousel.scrollLeft -= carousel.offsetWidth;
|
||||
}
|
||||
|
||||
function scrollCarouselRight() {
|
||||
const carousel = document.querySelector('.overflow-x-scroll');
|
||||
const maxScrollRight = carousel.scrollWidth - carousel.offsetWidth; // Calculate max scrollable distance
|
||||
|
||||
// Prevent scrolling beyond the end
|
||||
if (carousel.scrollLeft >= maxScrollRight) {
|
||||
return;
|
||||
}
|
||||
|
||||
carousel.scrollTo({
|
||||
left: carousel.scrollLeft + carousel.offsetWidth,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
</script>
|
Reference in New Issue
Block a user