theme and plugin sync

This commit is contained in:
2024-03-20 17:38:18 -04:00
commit 03e44e02f0
224 changed files with 56542 additions and 0 deletions

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

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

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

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

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

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

View 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>
`;
}
}

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

View 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">Lets 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 youre comfortable with.
<strong>Lets 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">Lets 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 youre comfortable with.
<strong>Lets 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));
}
}

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

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

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

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

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

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

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