Compare commits

..

No commits in common. "14680585b9e85683551462a4ba7b9abadd8c827a" and "0b1bb109340db82e15891b6ebdd4c3bbe419aaa7" have entirely different histories.

8 changed files with 331 additions and 457 deletions

View File

@ -4451,14 +4451,20 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
entity = dealer.entity entity = dealer.entity
status = self.request.GET.get("status") status = self.request.GET.get("status")
queryset = entity.get_estimates() queryset = entity.get_estimates()
type(queryset)
if status: if status:
queryset = queryset.filter(status=status) queryset = queryset.filter(status=status)
search_query = self.request.GET.get('q', None) for f in queryset.first()._meta.get_fields():
print(f)
search_query = self.request.GET.get('q', '').strip()
print(search_query)
if search_query: if search_query:
print("inside")
queryset = queryset.filter( queryset = queryset.filter(
Q(estimate_number__icontains=search_query) Q(estimate_number__icontains=search_query)
).distinct() ).distinct()
return queryset return queryset
@ -5823,7 +5829,6 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
permission_required = ["inventory.view_lead"] permission_required = ["inventory.view_lead"]
def get_queryset(self): def get_queryset(self):
# print(self.request.is_dealer)
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
query = self.request.GET.get("q") query = self.request.GET.get("q")
qs = models.Lead.objects.filter(dealer=dealer).exclude(status="converted") qs = models.Lead.objects.filter(dealer=dealer).exclude(status="converted")

View File

@ -80,7 +80,7 @@
</head> </head>
<body> <body>
<main class="main" id="top"> <main class="main" id="top">
<div id="main_content" class="px-3"> <div 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

@ -66,7 +66,7 @@
rel="stylesheet" rel="stylesheet"
id="user-style-default"> id="user-style-default">
{% endif %} {% endif %}
<script src="{% static 'js/main.js' %}"></script> <script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/jquery.min.js' %}"></script> <script src="{% static 'js/jquery.min.js' %}"></script>
{% comment %} <script src="{% static 'js/echarts.js' %}"></script> {% endcomment %} {% comment %} <script src="{% static 'js/echarts.js' %}"></script> {% endcomment %}
@ -154,38 +154,7 @@
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,8 +494,7 @@
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') }}"> data-note-title="{{ _("Update") }}<i class='fas fa-pen-square text-primary ms-2'></i>">
<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

@ -10,7 +10,7 @@
<li class="fas fa-bullhorn text-primary ms-2"></li> <li class="fas fa-bullhorn text-primary ms-2"></li>
</h2> </h2>
<!-- Action Tracking Modal --> <!-- Action Tracking Modal -->
{% comment %} {% include "crm/leads/partials/update_action.html" %} {% endcomment %} {% include "crm/leads/partials/update_action.html" %}
<div class="row g-3 justify-content-between mb-4"> <div class="row g-3 justify-content-between mb-4">
<div class="col-auto"> <div class="col-auto">
<div class="d-md-flex justify-content-between"> <div class="d-md-flex justify-content-between">

View File

@ -13,7 +13,7 @@
aria-label="Close"></button> aria-label="Close"></button>
</div> </div>
<form id="actionTrackingForm" <form id="actionTrackingForm"
action="{% url 'update_lead_actions' request.dealer.slug %}" action="{% url 'update_lead_actions' lead.dealer.slug %}"
hx-select-oob="#currentStage:outerHTML,#leadStatus:outerHTML,#toast-container:outerHTML" hx-select-oob="#currentStage:outerHTML,#leadStatus:outerHTML,#toast-container:outerHTML"
hx-swap="none" hx-swap="none"
hx-on::after-request="{ hx-on::after-request="{

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="true" id="btn-add-car" class="nav-link btn-add-car" href="{% url 'car_add' request.dealer.slug %}"> <a 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

@ -2,7 +2,7 @@
{% load i18n static custom_filters %} {% load i18n static custom_filters %}
{% block title %} {% block title %}
{% trans 'Add New Car' %} {% endblock %} {% trans 'Add New Car' %} {% endblock %}
{% block content %} {% block content %}
<style> <style>
#video { #video {
width: 100%; width: 100%;
@ -339,435 +339,336 @@
</div> </div>
</div> </div>
<!----> <!---->
{% endblock content %} <script>
{% block customJS %} function getCookie(name) {
<script> let cookieValue = null;
// Global variables if (document.cookie && document.cookie !== "") {
let codeReader; const cookies = document.cookie.split(";");
let currentStream = null; for (let cookie of cookies) {
const csrfToken = getCookie("csrftoken"); cookie = cookie.trim();
const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}"; if (cookie.substring(0, name.length + 1) === name + "=") {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
// Initialize when page loads and after HTMX swaps break;
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;
if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";");
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.substring(0, name.length + 1) === name + "=") {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
} }
return cookieValue;
} }
}
return cookieValue;
}
// VIN Decoding functions document.addEventListener("DOMContentLoaded", function () {
async function decodeVin() { const csrfToken = getCookie("csrftoken");
const vinInput = document.getElementById("{{ form.vin.id_for_label }}");
const vinNumber = vinInput.value.trim();
if (vinNumber.length !== 17) { const vinInput = document.getElementById("{{ form.vin.id_for_label }}");
Swal.fire("error", "{% trans 'Please enter a valid VIN.' %}"); const decodeVinBtn = document.getElementById("decodeVinBtn");
return; const makeSelect = document.getElementById("{{ form.id_car_make.id_for_label }}");
} const modelSelect = document.getElementById("{{ form.id_car_model.id_for_label }}");
const yearSelect = document.getElementById("{{ form.year.id_for_label }}");
const serieSelect = document.getElementById("{{ form.id_car_serie.id_for_label }}");
const trimSelect = document.getElementById("{{ form.id_car_trim.id_for_label }}");
const equipmentSelect = document.getElementById("equipment_id")
const showSpecificationButton = document.getElementById("specification-btn");
const showEquipmentButton = document.getElementById("options-btn")
const specificationsContent = document.getElementById("specificationsContent");
const optionsContent = document.getElementById("optionsContent")
const generationContainer = document.getElementById("generation-div")
showLoading(); const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}";
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) { const closeButton = document.querySelector(".btn-close");
hideLoading(); const scanVinBtn = document.getElementById("scan-vin-btn");
await updateFields(data.data); const videoElement = document.getElementById("video");
} else { const resultDisplay = document.getElementById("result");
hideLoading(); const fallbackButton = document.getElementById("ocr-fallback-btn");
Swal.fire("{% trans 'error' %}", data.error); let codeReader;
} codeReader = new ZXing.BrowserMultiFormatReader();
} catch (error) { let currentStream = null;
console.error("Error decoding VIN:", error);
hideLoading();
Swal.fire("error", "{% trans 'An error occurred while decoding the VIN.' %}");
}
}
async function updateFields(vinData) { function closeModal() {
const elements = { stopScanner();
makeSelect: document.getElementById("{{ form.id_car_make.id_for_label }}"), try {
modelSelect: document.getElementById("{{ form.id_car_model.id_for_label }}"), const scannerModal = document.getElementById("scannerModal");
yearSelect: document.getElementById("{{ form.year.id_for_label }}"), if (scannerModal) {
generationContainer: document.getElementById("generation-div") document.activeElement.blur();
}; scannerModal.setAttribute("inert", "true");
const modalInstance = bootstrap.Modal.getInstance(scannerModal);
console.log(vinData); if (modalInstance) modalInstance.hide();
if (vinData.make_id && elements.makeSelect) { if (scanVinBtn) scanVinBtn.focus();
elements.makeSelect.value = vinData.make_id; }
document.getElementById("make-check").innerHTML = "&#10003;"; } catch (err) {
await loadModels(vinData.make_id); console.error("Error closing scanner modal:", err);
} }
if (vinData.model_id && elements.modelSelect) {
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;";
}
}
// 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) { async function decodeVin() {
const canvas = document.createElement("canvas"); const vinNumber = vinInput.value.trim();
const context = canvas.getContext("2d"); if (vinNumber.length !== 17) {
canvas.width = videoElement.videoWidth; Swal.fire("error", "{% trans 'Please enter a valid VIN.' %}");
canvas.height = videoElement.videoHeight; /*alert("{% trans 'Please enter a valid VIN.' %}");*/
context.drawImage(videoElement, 0, 0, canvas.width, canvas.height); 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.' %}");
}
}
if (typeof Tesseract !== 'undefined') { async function updateFields(vinData) {
Tesseract.recognize(canvas.toDataURL("image/png"), "eng") console.log(vinData);
.then(({ data: { text } }) => { if (vinData.make_id) {
const vin = text.match(/[A-HJ-NPR-Z0-9]{17}/); makeSelect.value = vinData.make_id;
if (vin) vinInput.value = vin[0]; document.getElementById("make-check").innerHTML = "&#10003;";
closeModal(); await loadModels(vinData.make_id);
decodeVin(); }
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) {
currentStream.getTracks().forEach((track) => track.stop());
currentStream = null;
}
codeReader.reset();
}
function resetDropdown(dropdown, placeholder) {
dropdown.innerHTML = `<option value="">${placeholder}</option>`;
}
async function loadModels(makeId) {
resetDropdown(modelSelect, '{% trans "Select" %}');
const response = await fetch(`${ajaxUrl}?action=get_models&make_id=${makeId}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
data.forEach((model) => {
const option = document.createElement("option");
option.value = model.id_car_model;
option.textContent = document.documentElement.lang === "en" ? model.name : model.arabic_name;
modelSelect.appendChild(option);
});
}
async function loadSeries(modelId, year) {
resetDropdown(serieSelect, '{% trans "Select" %}');
resetDropdown(trimSelect, '{% trans "Select" %}');
specificationsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_series&model_id=${modelId}&year=${year}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
console.log(data)
data.forEach((serie) => {
const option = document.createElement("option");
option.value = serie.id_car_serie;
option.textContent = document.documentElement.lang === "en" ? serie.name : serie.name;
generationContainer.innerHTML = serie.generation_name
serieSelect.appendChild(option);
});
}
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}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
}
});
const data = await response.json();
data.forEach((trim) => {
const option = document.createElement("option");
option.value = trim.id_car_trim;
option.textContent = document.documentElement.lang === "en" ? trim.name : trim.name;
trimSelect.appendChild(option);
});
showSpecificationButton.disabled = !this.value;
}
async function loadEquipment(trimId){
resetDropdown(equipmentSelect, '{% trans "Select" %}');
optionsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_equipments&trim_id=${trimId}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': csrfToken
}
});
const data = await response.json();
data.forEach((equipment) => {
const option = document.createElement('option');
option.value = equipment.id_car_equipment;
option.textContent = equipment.name;
equipmentSelect.appendChild(option);
});
}
async function loadSpecifications(trimId) {
specificationsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_specifications&trim_id=${trimId}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
data.forEach((spec) => {
const parentDiv = document.createElement("div");
parentDiv.innerHTML = `<strong>${spec.parent_name}</strong>`;
spec.specifications.forEach((s) => {
const specDiv = document.createElement("div");
specDiv.innerHTML = `• ${s.s_name}: ${s.s_value} ${s.s_unit}`;
parentDiv.appendChild(specDiv);
});
specificationsContent.appendChild(parentDiv);
});
}
async function loadOptions(equipmentId) {
optionsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_options&equipment_id=${equipmentId}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
data.forEach((parent) => {
const parentDiv = document.createElement("div");
parentDiv.innerHTML = `<strong>${parent.parent_name}</strong>`;
parent.options.forEach((option) => {
const optDiv = document.createElement("div");
optDiv.innerHTML = `• ${option.option_name}`;
parentDiv.appendChild(optDiv);
});
optionsContent.appendChild(parentDiv);
});
}
scanVinBtn.addEventListener("click", () => {
resultDisplay.textContent = "";
startScanner();
});
fallbackButton.addEventListener("click", () => {
captureAndOCR();
}) })
.catch((err) => console.error("OCR Error:", err));
}
}
function closeModal(scanVinBtn) { serieSelect.addEventListener("change", () => {
stopScanner(); const serie_id = serieSelect.value;
try { const model_id = modelSelect.value;
const scannerModal = document.getElementById("scannerModal"); if (serie_id && model_id) loadTrims(serie_id, model_id);
if (scannerModal) { })
document.activeElement.blur();
scannerModal.setAttribute("inert", "true"); trimSelect.addEventListener("change", () => {
const modalInstance = bootstrap.Modal.getInstance(scannerModal); const trimId = trimSelect.value
if (modalInstance) modalInstance.hide(); showSpecificationButton.disabled = !trimId
if (scanVinBtn) scanVinBtn.focus(); 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({
title: "{% trans 'Please Wait' %}",
text: "{% trans 'Loading' %}...",
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
},
});
} }
} catch (err) {
console.error("Error closing scanner modal:", err);
}
}
function stopScanner() { function hideLoading() {
if (currentStream) { Swal.close();
currentStream.getTracks().forEach((track) => track.stop());
currentStream = null;
}
if (codeReader) codeReader.reset();
}
// Data loading functions
function resetDropdown(dropdown, placeholder) {
if (dropdown) dropdown.innerHTML = `<option value="">${placeholder}</option>`;
}
async function loadModels(makeId, modelSelect, elements) {
if (!modelSelect) return;
resetDropdown(modelSelect, '{% trans "Select" %}');
const response = await fetch(`${ajaxUrl}?action=get_models&make_id=${makeId}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
data.forEach((model) => {
const option = document.createElement("option");
option.value = model.id_car_model;
option.textContent = document.documentElement.lang === "en" ? model.name : model.arabic_name;
modelSelect.appendChild(option);
});
}
async function loadSeries(modelId, year, elements) {
if (!elements?.serieSelect) return;
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}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
console.log(data);
data.forEach((serie) => {
const option = document.createElement("option");
option.value = serie.id_car_serie;
option.textContent = document.documentElement.lang === "en" ? serie.name : serie.name;
if (elements.generationContainer) elements.generationContainer.innerHTML = serie.generation_name;
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 = "";
const response = await fetch(`${ajaxUrl}?action=get_trims&serie_id=${serie_id}&model_id=${model_id}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
} }
});
const data = await response.json();
data.forEach((trim) => { function notify(tag, msg) {
const option = document.createElement("option"); Swal.fire({
option.value = trim.id_car_trim; icon: tag,
option.textContent = document.documentElement.lang === "en" ? trim.name : trim.name; titleText: msg,
elements.trimSelect.appendChild(option); });
});
if (elements.showSpecificationButton) {
elements.showSpecificationButton.disabled = !elements.trimSelect.value;
}
}
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}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': csrfToken
} }
}); </script>
const data = await response.json(); {% endblock %}
data.forEach((equipment) => {
const option = document.createElement('option');
option.value = equipment.id_car_equipment;
option.textContent = equipment.name;
elements.equipmentSelect.appendChild(option);
});
}
async function loadSpecifications(trimId, elements) {
if (!elements?.specificationsContent) return;
elements.specificationsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_specifications&trim_id=${trimId}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
data.forEach((spec) => {
const parentDiv = document.createElement("div");
parentDiv.innerHTML = `<strong>${spec.parent_name}</strong>`;
spec.specifications.forEach((s) => {
const specDiv = document.createElement("div");
specDiv.innerHTML = `• ${s.s_name}: ${s.s_value} ${s.s_unit}`;
parentDiv.appendChild(specDiv);
});
elements.specificationsContent.appendChild(parentDiv);
});
}
async function loadOptions(equipmentId, elements) {
if (!elements?.optionsContent) return;
elements.optionsContent.innerHTML = "";
const response = await fetch(`${ajaxUrl}?action=get_options&equipment_id=${equipmentId}`, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": csrfToken,
},
});
const data = await response.json();
data.forEach((parent) => {
const parentDiv = document.createElement("div");
parentDiv.innerHTML = `<strong>${parent.parent_name}</strong>`;
parent.options.forEach((option) => {
const optDiv = document.createElement("div");
optDiv.innerHTML = `• ${option.option_name}`;
parentDiv.appendChild(optDiv);
});
elements.optionsContent.appendChild(parentDiv);
});
}
// UI Helper functions
function showLoading() {
Swal.fire({
title: "{% trans 'Please Wait' %}",
text: "{% trans 'Loading' %}...",
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
},
});
}
function hideLoading() {
Swal.close();
}
function notify(tag, msg) {
Swal.fire({
icon: tag,
titleText: msg,
});
}
</script>
{% endblock customJS %}