theme and plugin sync
This commit is contained in:
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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user