update on the htmx lead

This commit is contained in:
ismail 2025-07-24 11:50:58 +03:00
parent f8527795ae
commit 14680585b9
5 changed files with 459 additions and 328 deletions

View File

@ -80,7 +80,7 @@
</head> </head>
<body> <body>
<main class="main" id="top"> <main class="main" id="top">
<div class="px-3"> <div id="main_content" class="px-3">
<div class="row min-vh-100 flex-center p-5"> <div class="row min-vh-100 flex-center p-5">
<div class="col-12 col-xl-10 col-xxl-8"> <div class="col-12 col-xl-10 col-xxl-8">
<div class="row justify-content-center align-items-center g-5"> <div class="row justify-content-center align-items-center g-5">

View File

@ -83,9 +83,9 @@
{% block period_navigation %} {% block period_navigation %}
{% endblock period_navigation %} {% endblock period_navigation %}
<div id="main_content" class="fade-me-in" hx-boost="true" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML transition:false" hx-select-oob="#toast-container" hx-history-elt> <div id="main_content" class="fade-me-in" hx-boost="true" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML transition:false" hx-select-oob="#toast-container" hx-history-elt>
{% comment %} <div id="spinner" class="htmx-indicator spinner-bg"> <div id="spinner" class="htmx-indicator spinner-bg">
<img src="{% static 'spinner.svg' %}" width="100" height="100" alt=""> <img src="{% static 'spinner.svg' %}" width="100" height="100" alt="">
</div> {% endcomment %} </div>
{% block customCSS %}{% endblock %} {% block customCSS %}{% endblock %}
{% block content %}{% endblock content %} {% block content %}{% endblock content %}
{% block customJS %}{% endblock %} {% block customJS %}{% endblock %}
@ -154,7 +154,38 @@
document.getElementById('global-indicator') document.getElementById('global-indicator')
]; ];
});*/ });*/
let Toast = Swal.mixin({
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 3000,
timerProgressBar: true,
didOpen: (toast) => {
toast.onmouseenter = Swal.stopTimer;
toast.onmouseleave = Swal.resumeTimer;
}
});
function notify(tag, msg) {
Toast.fire({
icon: tag,
titleText: msg
});
}
document.addEventListener('htmx:afterRequest', function(evt) {
if(evt.detail.xhr.status == 403){
/* Notify the user of a 404 Not Found response */
notify("error", "You do not have permission to view this page");
}
if(evt.detail.xhr.status == 404){
/* Notify the user of a 404 Not Found response */
return alert("Error: Could Not Find Resource");
}
if (evt.detail.successful != true) {
console.log(evt)
/* Notify of an unexpected error, & print error to console */
notify("error", "Unexpected Error");
}
});
</script> </script>
{% comment %} {% block customJS %}{% endblock %} {% endcomment %} {% comment %} {% block customJS %}{% endblock %} {% endcomment %}

View File

@ -494,7 +494,8 @@
data-url="{% url 'update_note' request.dealer.slug note.pk %}" data-url="{% url 'update_note' request.dealer.slug note.pk %}"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#noteModal" data-bs-target="#noteModal"
data-note-title="{{ _("Update") }}<i class='fas fa-pen-square text-primary ms-2'></i>"> data-note-title="{{ _('Update') }}">
<i class='fas fa-pen-square text-primary ms-2'></i>
{{ _("Update") }} {{ _("Update") }}
</a> </a>
<button class="btn btn-phoenix-danger btn-sm delete-btn" <button class="btn btn-phoenix-danger btn-sm delete-btn"

View File

@ -20,7 +20,7 @@
<li class="collapsed-nav-item-title d-none">{% trans "Inventory"|capfirst %}</li> <li class="collapsed-nav-item-title d-none">{% trans "Inventory"|capfirst %}</li>
{% if perms.inventory.add_car %} {% if perms.inventory.add_car %}
<li class="nav-item"> <li class="nav-item">
<a hx-boost="false" id="btn-add-car" class="nav-link btn-add-car" href="{% url 'car_add' request.dealer.slug %}"> <a hx-boost="true" id="btn-add-car" class="nav-link btn-add-car" href="{% url 'car_add' request.dealer.slug %}">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-plus-circle"></span></span><span class="nav-link-text">{% trans "add car"|capfirst %}</span> <span class="nav-link-icon"><span class="fas fa-plus-circle"></span></span><span class="nav-link-text">{% trans "add car"|capfirst %}</span>
</div> </div>

View File

@ -339,8 +339,138 @@
</div> </div>
</div> </div>
<!----> <!---->
<script> {% endblock content %}
function getCookie(name) { {% block customJS %}
<script>
// Global variables
let codeReader;
let currentStream = null;
const csrfToken = getCookie("csrftoken");
const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}";
// Initialize when page loads and after HTMX swaps
document.addEventListener('DOMContentLoaded', initPage);
document.addEventListener('htmx:afterSwap', initPage);
function initPage() {
// Get DOM elements
const elements = {
vinInput: document.getElementById("{{ form.vin.id_for_label }}"),
decodeVinBtn: document.getElementById("decodeVinBtn"),
makeSelect: document.getElementById("{{ form.id_car_make.id_for_label }}"),
modelSelect: document.getElementById("{{ form.id_car_model.id_for_label }}"),
yearSelect: document.getElementById("{{ form.year.id_for_label }}"),
serieSelect: document.getElementById("{{ form.id_car_serie.id_for_label }}"),
trimSelect: document.getElementById("{{ form.id_car_trim.id_for_label }}"),
equipmentSelect: document.getElementById("equipment_id"),
showSpecificationButton: document.getElementById("specification-btn"),
showEquipmentButton: document.getElementById("options-btn"),
specificationsContent: document.getElementById("specificationsContent"),
optionsContent: document.getElementById("optionsContent"),
generationContainer: document.getElementById("generation-div"),
closeButton: document.querySelector(".btn-close"),
scanVinBtn: document.getElementById("scan-vin-btn"),
videoElement: document.getElementById("video"),
resultDisplay: document.getElementById("result"),
fallbackButton: document.getElementById("ocr-fallback-btn")
};
// Initialize scanner if available
if (typeof ZXing !== 'undefined' && !codeReader) {
codeReader = new ZXing.BrowserMultiFormatReader();
}
// Add event listeners
setupEventListeners(elements);
}
function setupEventListeners(elements) {
// Remove existing listeners first to prevent duplicates
removeEventListeners(elements);
// Add new listeners
if (elements.decodeVinBtn) {
elements.decodeVinBtn.addEventListener("click", decodeVin);
}
if (elements.scanVinBtn) {
elements.scanVinBtn.addEventListener("click", () => {
elements.resultDisplay.textContent = "";
startScanner(elements.videoElement);
});
}
if (elements.fallbackButton) {
elements.fallbackButton.addEventListener("click", () => {
captureAndOCR(elements.videoElement, elements.vinInput);
});
}
if (elements.serieSelect) {
elements.serieSelect.addEventListener("change", () => {
const serie_id = elements.serieSelect.value;
const model_id = elements.modelSelect.value;
if (serie_id && model_id) loadTrims(serie_id, model_id, elements);
});
}
if (elements.trimSelect) {
elements.trimSelect.addEventListener("change", () => {
const trimId = elements.trimSelect.value;
elements.showSpecificationButton.disabled = !trimId;
elements.showEquipmentButton.disabled = !trimId;
if (trimId) loadSpecifications(trimId, elements);
loadEquipment(trimId, elements);
});
}
if (elements.equipmentSelect) {
elements.equipmentSelect.addEventListener("change", () => {
const equipmentId = elements.equipmentSelect.value;
if (equipmentId) loadOptions(equipmentId, elements);
});
}
if (elements.closeButton) {
elements.closeButton.addEventListener("click", () => closeModal(elements.scanVinBtn));
}
if (elements.makeSelect) {
elements.makeSelect.addEventListener("change", (e) => {
loadModels(e.target.value, elements.modelSelect.value, elements);
});
}
if (elements.modelSelect) {
elements.modelSelect.addEventListener("change", (e) => {
loadSeries(e.target.value, elements.yearSelect.value, elements);
});
}
}
function removeEventListeners(elements) {
// Remove all event listeners to prevent duplicates
const events = [
{ element: elements.decodeVinBtn, event: "click", func: decodeVin },
{ element: elements.scanVinBtn, event: "click" },
{ element: elements.fallbackButton, event: "click" },
{ element: elements.serieSelect, event: "change" },
{ element: elements.trimSelect, event: "change" },
{ element: elements.equipmentSelect, event: "change" },
{ element: elements.closeButton, event: "click" },
{ element: elements.makeSelect, event: "change" },
{ element: elements.modelSelect, event: "change" }
];
events.forEach(item => {
if (item.element) {
item.element.removeEventListener(item.event, item.func || null);
}
});
}
// Cookie helper function
function getCookie(name) {
let cookieValue = null; let cookieValue = null;
if (document.cookie && document.cookie !== "") { if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";"); const cookies = document.cookie.split(";");
@ -353,37 +483,102 @@
} }
} }
return cookieValue; return cookieValue;
}
// VIN Decoding functions
async function decodeVin() {
const vinInput = document.getElementById("{{ form.vin.id_for_label }}");
const vinNumber = vinInput.value.trim();
if (vinNumber.length !== 17) {
Swal.fire("error", "{% trans 'Please enter a valid VIN.' %}");
return;
} }
document.addEventListener("DOMContentLoaded", function () { showLoading();
const csrfToken = getCookie("csrftoken"); try {
const response = await fetch(`${ajaxUrl}?action=decode_vin&vin_no=${vinNumber}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
const vinInput = document.getElementById("{{ form.vin.id_for_label }}"); if (data.success) {
const decodeVinBtn = document.getElementById("decodeVinBtn"); hideLoading();
const makeSelect = document.getElementById("{{ form.id_car_make.id_for_label }}"); await updateFields(data.data);
const modelSelect = document.getElementById("{{ form.id_car_model.id_for_label }}"); } else {
const yearSelect = document.getElementById("{{ form.year.id_for_label }}"); hideLoading();
const serieSelect = document.getElementById("{{ form.id_car_serie.id_for_label }}"); Swal.fire("{% trans 'error' %}", data.error);
const trimSelect = document.getElementById("{{ form.id_car_trim.id_for_label }}"); }
const equipmentSelect = document.getElementById("equipment_id") } catch (error) {
const showSpecificationButton = document.getElementById("specification-btn"); console.error("Error decoding VIN:", error);
const showEquipmentButton = document.getElementById("options-btn") hideLoading();
const specificationsContent = document.getElementById("specificationsContent"); Swal.fire("error", "{% trans 'An error occurred while decoding the VIN.' %}");
const optionsContent = document.getElementById("optionsContent") }
const generationContainer = document.getElementById("generation-div") }
const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}"; async function updateFields(vinData) {
const elements = {
makeSelect: document.getElementById("{{ form.id_car_make.id_for_label }}"),
modelSelect: document.getElementById("{{ form.id_car_model.id_for_label }}"),
yearSelect: document.getElementById("{{ form.year.id_for_label }}"),
generationContainer: document.getElementById("generation-div")
};
const closeButton = document.querySelector(".btn-close"); console.log(vinData);
const scanVinBtn = document.getElementById("scan-vin-btn"); if (vinData.make_id && elements.makeSelect) {
const videoElement = document.getElementById("video"); elements.makeSelect.value = vinData.make_id;
const resultDisplay = document.getElementById("result"); document.getElementById("make-check").innerHTML = "&#10003;";
const fallbackButton = document.getElementById("ocr-fallback-btn"); await loadModels(vinData.make_id);
let codeReader; }
codeReader = new ZXing.BrowserMultiFormatReader(); if (vinData.model_id && elements.modelSelect) {
let currentStream = null; elements.modelSelect.value = vinData.model_id;
document.getElementById("model-check").innerHTML = "&#10003;";
await loadSeries(vinData.model_id, vinData.year);
}
if (vinData.year && elements.yearSelect) {
elements.yearSelect.value = vinData.year;
document.getElementById("year-check").innerHTML = "&#10003;";
}
}
function closeModal() { // Scanner functions
async function startScanner(videoElement) {
if (!codeReader) return;
codeReader
.decodeFromVideoDevice(null, videoElement, async (result, err) => {
if (result) {
document.getElementById("{{ form.vin.id_for_label }}").value = result.text;
closeModal();
await decodeVin();
}
})
.catch(console.error);
}
function captureAndOCR(videoElement, vinInput) {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
if (typeof Tesseract !== 'undefined') {
Tesseract.recognize(canvas.toDataURL("image/png"), "eng")
.then(({ data: { text } }) => {
const vin = text.match(/[A-HJ-NPR-Z0-9]{17}/);
if (vin) vinInput.value = vin[0];
closeModal();
decodeVin();
})
.catch((err) => console.error("OCR Error:", err));
}
}
function closeModal(scanVinBtn) {
stopScanner(); stopScanner();
try { try {
const scannerModal = document.getElementById("scannerModal"); const scannerModal = document.getElementById("scannerModal");
@ -397,100 +592,24 @@
} catch (err) { } catch (err) {
console.error("Error closing scanner modal:", err); console.error("Error closing scanner modal:", err);
} }
} }
async function decodeVin() { function stopScanner() {
const vinNumber = vinInput.value.trim();
if (vinNumber.length !== 17) {
Swal.fire("error", "{% trans 'Please enter a valid VIN.' %}");
/*alert("{% trans 'Please enter a valid VIN.' %}");*/
return;
}
showLoading();
try {
const response = await fetch(`${ajaxUrl}?action=decode_vin&vin_no=${vinNumber}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
if (data.success) {
hideLoading();
await updateFields(data.data);
} else {
hideLoading();
Swal.fire("{% trans 'error' %}", data.error);
}
} catch (error) {
console.error("Error decoding VIN:", error);
hideLoading();
Swal.fire("error", "{% trans 'An error occurred while decoding the VIN.' %}");
}
}
async function updateFields(vinData) {
console.log(vinData);
if (vinData.make_id) {
makeSelect.value = vinData.make_id;
document.getElementById("make-check").innerHTML = "&#10003;";
await loadModels(vinData.make_id);
}
if (vinData.model_id) {
modelSelect.value = vinData.model_id;
document.getElementById("model-check").innerHTML = "&#10003;";
await loadSeries(vinData.model_id, vinData.year);
}
if (vinData.year) {
yearSelect.value = vinData.year;
document.getElementById("year-check").innerHTML = "&#10003;";
}
}
// Start the scanner
async function startScanner() {
codeReader
.decodeFromVideoDevice(null, videoElement, async (result, err) => {
let res = await result;
if (result) {
vinInput.value = result.text;
closeModal();
await decodeVin();
}
})
.catch(console.error);
}
function captureAndOCR() {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
Tesseract.recognize(canvas.toDataURL("image/png"), "eng")
.then(({ data: { text } }) => {
const vin = text.match(/[A-HJ-NPR-Z0-9]{17}/);
if (vin) vinInput.value = vin[0];
closeModal();
decodeVin();
})
.catch((err) => console.error("OCR Error:", err));
}
function stopScanner() {
if (currentStream) { if (currentStream) {
currentStream.getTracks().forEach((track) => track.stop()); currentStream.getTracks().forEach((track) => track.stop());
currentStream = null; currentStream = null;
} }
codeReader.reset(); if (codeReader) codeReader.reset();
} }
function resetDropdown(dropdown, placeholder) { // Data loading functions
dropdown.innerHTML = `<option value="">${placeholder}</option>`; function resetDropdown(dropdown, placeholder) {
} if (dropdown) dropdown.innerHTML = `<option value="">${placeholder}</option>`;
}
async function loadModels(makeId, modelSelect, elements) {
if (!modelSelect) return;
async function loadModels(makeId) {
resetDropdown(modelSelect, '{% trans "Select" %}'); resetDropdown(modelSelect, '{% trans "Select" %}');
const response = await fetch(`${ajaxUrl}?action=get_models&make_id=${makeId}`, { const response = await fetch(`${ajaxUrl}?action=get_models&make_id=${makeId}`, {
headers: { headers: {
@ -505,12 +624,14 @@
option.textContent = document.documentElement.lang === "en" ? model.name : model.arabic_name; option.textContent = document.documentElement.lang === "en" ? model.name : model.arabic_name;
modelSelect.appendChild(option); modelSelect.appendChild(option);
}); });
} }
async function loadSeries(modelId, year) { async function loadSeries(modelId, year, elements) {
resetDropdown(serieSelect, '{% trans "Select" %}'); if (!elements?.serieSelect) return;
resetDropdown(trimSelect, '{% trans "Select" %}');
specificationsContent.innerHTML = ""; resetDropdown(elements.serieSelect, '{% trans "Select" %}');
resetDropdown(elements.trimSelect, '{% trans "Select" %}');
if (elements.specificationsContent) elements.specificationsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_series&model_id=${modelId}&year=${year}`, { const response = await fetch(`${ajaxUrl}?action=get_series&model_id=${modelId}&year=${year}`, {
headers: { headers: {
@ -519,20 +640,23 @@
}, },
}); });
const data = await response.json(); const data = await response.json();
console.log(data) console.log(data);
data.forEach((serie) => {
data.forEach((serie) => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = serie.id_car_serie; option.value = serie.id_car_serie;
option.textContent = document.documentElement.lang === "en" ? serie.name : serie.name; option.textContent = document.documentElement.lang === "en" ? serie.name : serie.name;
generationContainer.innerHTML = serie.generation_name if (elements.generationContainer) elements.generationContainer.innerHTML = serie.generation_name;
serieSelect.appendChild(option); elements.serieSelect.appendChild(option);
}); });
} }
async function loadTrims(serie_id, model_id, elements) {
if (!elements?.trimSelect) return;
resetDropdown(elements.trimSelect, '{% trans "Select" %}');
if (elements.specificationsContent) elements.specificationsContent.innerHTML = "";
async function loadTrims(serie_id, model_id) {
resetDropdown(trimSelect, '{% trans "Select" %}');
specificationsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_trims&serie_id=${serie_id}&model_id=${model_id}`, { const response = await fetch(`${ajaxUrl}?action=get_trims&serie_id=${serie_id}&model_id=${model_id}`, {
headers: { headers: {
"X-Requested-With": "XMLHttpRequest", "X-Requested-With": "XMLHttpRequest",
@ -540,18 +664,25 @@
} }
}); });
const data = await response.json(); const data = await response.json();
data.forEach((trim) => { data.forEach((trim) => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = trim.id_car_trim; option.value = trim.id_car_trim;
option.textContent = document.documentElement.lang === "en" ? trim.name : trim.name; option.textContent = document.documentElement.lang === "en" ? trim.name : trim.name;
trimSelect.appendChild(option); elements.trimSelect.appendChild(option);
}); });
showSpecificationButton.disabled = !this.value;
}
async function loadEquipment(trimId){ if (elements.showSpecificationButton) {
resetDropdown(equipmentSelect, '{% trans "Select" %}'); elements.showSpecificationButton.disabled = !elements.trimSelect.value;
optionsContent.innerHTML = ""; }
}
async function loadEquipment(trimId, elements) {
if (!elements?.equipmentSelect) return;
resetDropdown(elements.equipmentSelect, '{% trans "Select" %}');
if (elements.optionsContent) elements.optionsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_equipments&trim_id=${trimId}`, { const response = await fetch(`${ajaxUrl}?action=get_equipments&trim_id=${trimId}`, {
headers: { headers: {
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',
@ -559,16 +690,19 @@
} }
}); });
const data = await response.json(); const data = await response.json();
data.forEach((equipment) => { data.forEach((equipment) => {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = equipment.id_car_equipment; option.value = equipment.id_car_equipment;
option.textContent = equipment.name; option.textContent = equipment.name;
equipmentSelect.appendChild(option); elements.equipmentSelect.appendChild(option);
}); });
} }
async function loadSpecifications(trimId) { async function loadSpecifications(trimId, elements) {
specificationsContent.innerHTML = ""; if (!elements?.specificationsContent) return;
elements.specificationsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_specifications&trim_id=${trimId}`, { const response = await fetch(`${ajaxUrl}?action=get_specifications&trim_id=${trimId}`, {
headers: { headers: {
"X-Requested-With": "XMLHttpRequest", "X-Requested-With": "XMLHttpRequest",
@ -576,6 +710,7 @@
}, },
}); });
const data = await response.json(); const data = await response.json();
data.forEach((spec) => { data.forEach((spec) => {
const parentDiv = document.createElement("div"); const parentDiv = document.createElement("div");
parentDiv.innerHTML = `<strong>${spec.parent_name}</strong>`; parentDiv.innerHTML = `<strong>${spec.parent_name}</strong>`;
@ -584,12 +719,14 @@
specDiv.innerHTML = `• ${s.s_name}: ${s.s_value} ${s.s_unit}`; specDiv.innerHTML = `• ${s.s_name}: ${s.s_value} ${s.s_unit}`;
parentDiv.appendChild(specDiv); parentDiv.appendChild(specDiv);
}); });
specificationsContent.appendChild(parentDiv); elements.specificationsContent.appendChild(parentDiv);
}); });
} }
async function loadOptions(equipmentId) { async function loadOptions(equipmentId, elements) {
optionsContent.innerHTML = ""; if (!elements?.optionsContent) return;
elements.optionsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_options&equipment_id=${equipmentId}`, { const response = await fetch(`${ajaxUrl}?action=get_options&equipment_id=${equipmentId}`, {
headers: { headers: {
"X-Requested-With": "XMLHttpRequest", "X-Requested-With": "XMLHttpRequest",
@ -597,6 +734,7 @@
}, },
}); });
const data = await response.json(); const data = await response.json();
data.forEach((parent) => { data.forEach((parent) => {
const parentDiv = document.createElement("div"); const parentDiv = document.createElement("div");
parentDiv.innerHTML = `<strong>${parent.parent_name}</strong>`; parentDiv.innerHTML = `<strong>${parent.parent_name}</strong>`;
@ -605,51 +743,12 @@
optDiv.innerHTML = `• ${option.option_name}`; optDiv.innerHTML = `• ${option.option_name}`;
parentDiv.appendChild(optDiv); parentDiv.appendChild(optDiv);
}); });
optionsContent.appendChild(parentDiv); elements.optionsContent.appendChild(parentDiv);
}); });
} }
// UI Helper functions
scanVinBtn.addEventListener("click", () => { function showLoading() {
resultDisplay.textContent = "";
startScanner();
});
fallbackButton.addEventListener("click", () => {
captureAndOCR();
})
serieSelect.addEventListener("change", () => {
const serie_id = serieSelect.value;
const model_id = modelSelect.value;
if (serie_id && model_id) loadTrims(serie_id, model_id);
})
trimSelect.addEventListener("change", () => {
const trimId = trimSelect.value
showSpecificationButton.disabled = !trimId
showEquipmentButton.disabled = !trimId
if (trimId) loadSpecifications(trimId)
loadEquipment(trimId)
})
equipmentSelect.addEventListener("change", () => {
const equipmentId = equipmentSelect.value
if (equipmentId) loadOptions(equipmentId)
})
closeButton.addEventListener("click", closeModal);
makeSelect.addEventListener("change", (e) => {
loadModels(e.target.value, modelSelect.value);
})
modelSelect.addEventListener("change", (e) => {
loadSeries(e.target.value, yearSelect.value);
})
decodeVinBtn.addEventListener("click", decodeVin);
})
function showLoading() {
Swal.fire({ Swal.fire({
title: "{% trans 'Please Wait' %}", title: "{% trans 'Please Wait' %}",
text: "{% trans 'Loading' %}...", text: "{% trans 'Loading' %}...",
@ -658,17 +757,17 @@
Swal.showLoading(); Swal.showLoading();
}, },
}); });
} }
function hideLoading() { function hideLoading() {
Swal.close(); Swal.close();
} }
function notify(tag, msg) { function notify(tag, msg) {
Swal.fire({ Swal.fire({
icon: tag, icon: tag,
titleText: msg, titleText: msg,
}); });
} }
</script> </script>
{% endblock %} {% endblock customJS %}