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,7 +339,137 @@
</div> </div>
</div> </div>
<!----> <!---->
{% endblock content %}
{% block customJS %}
<script> <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) { function getCookie(name) {
let cookieValue = null; let cookieValue = null;
if (document.cookie && document.cookie !== "") { if (document.cookie && document.cookie !== "") {
@ -355,57 +485,16 @@
return cookieValue; return cookieValue;
} }
document.addEventListener("DOMContentLoaded", function () { // VIN Decoding functions
const csrfToken = getCookie("csrftoken");
const vinInput = document.getElementById("{{ form.vin.id_for_label }}");
const decodeVinBtn = document.getElementById("decodeVinBtn");
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")
const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}";
const closeButton = document.querySelector(".btn-close");
const scanVinBtn = document.getElementById("scan-vin-btn");
const videoElement = document.getElementById("video");
const resultDisplay = document.getElementById("result");
const fallbackButton = document.getElementById("ocr-fallback-btn");
let codeReader;
codeReader = new ZXing.BrowserMultiFormatReader();
let currentStream = null;
function closeModal() {
stopScanner();
try {
const scannerModal = document.getElementById("scannerModal");
if (scannerModal) {
document.activeElement.blur();
scannerModal.setAttribute("inert", "true");
const modalInstance = bootstrap.Modal.getInstance(scannerModal);
if (modalInstance) modalInstance.hide();
if (scanVinBtn) scanVinBtn.focus();
}
} catch (err) {
console.error("Error closing scanner modal:", err);
}
}
async function decodeVin() { async function decodeVin() {
const vinInput = document.getElementById("{{ form.vin.id_for_label }}");
const vinNumber = vinInput.value.trim(); const vinNumber = vinInput.value.trim();
if (vinNumber.length !== 17) { if (vinNumber.length !== 17) {
Swal.fire("error", "{% trans 'Please enter a valid VIN.' %}"); Swal.fire("error", "{% trans 'Please enter a valid VIN.' %}");
/*alert("{% trans 'Please enter a valid VIN.' %}");*/
return; return;
} }
showLoading(); showLoading();
try { try {
const response = await fetch(`${ajaxUrl}?action=decode_vin&vin_no=${vinNumber}`, { const response = await fetch(`${ajaxUrl}?action=decode_vin&vin_no=${vinNumber}`, {
@ -415,6 +504,7 @@
}, },
}); });
const data = await response.json(); const data = await response.json();
if (data.success) { if (data.success) {
hideLoading(); hideLoading();
await updateFields(data.data); await updateFields(data.data);
@ -430,31 +520,38 @@
} }
async function updateFields(vinData) { 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")
};
console.log(vinData); console.log(vinData);
if (vinData.make_id) { if (vinData.make_id && elements.makeSelect) {
makeSelect.value = vinData.make_id; elements.makeSelect.value = vinData.make_id;
document.getElementById("make-check").innerHTML = "&#10003;"; document.getElementById("make-check").innerHTML = "&#10003;";
await loadModels(vinData.make_id); await loadModels(vinData.make_id);
} }
if (vinData.model_id) { if (vinData.model_id && elements.modelSelect) {
modelSelect.value = vinData.model_id; elements.modelSelect.value = vinData.model_id;
document.getElementById("model-check").innerHTML = "&#10003;"; document.getElementById("model-check").innerHTML = "&#10003;";
await loadSeries(vinData.model_id, vinData.year); await loadSeries(vinData.model_id, vinData.year);
} }
if (vinData.year) { if (vinData.year && elements.yearSelect) {
yearSelect.value = vinData.year; elements.yearSelect.value = vinData.year;
document.getElementById("year-check").innerHTML = "&#10003;"; document.getElementById("year-check").innerHTML = "&#10003;";
} }
} }
// Start the scanner // Scanner functions
async function startScanner() { async function startScanner(videoElement) {
if (!codeReader) return;
codeReader codeReader
.decodeFromVideoDevice(null, videoElement, async (result, err) => { .decodeFromVideoDevice(null, videoElement, async (result, err) => {
let res = await result;
if (result) { if (result) {
vinInput.value = result.text; document.getElementById("{{ form.vin.id_for_label }}").value = result.text;
closeModal(); closeModal();
await decodeVin(); await decodeVin();
} }
@ -462,12 +559,14 @@
.catch(console.error); .catch(console.error);
} }
function captureAndOCR() { function captureAndOCR(videoElement, vinInput) {
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
const context = canvas.getContext("2d"); const context = canvas.getContext("2d");
canvas.width = videoElement.videoWidth; canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight; canvas.height = videoElement.videoHeight;
context.drawImage(videoElement, 0, 0, canvas.width, canvas.height); context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
if (typeof Tesseract !== 'undefined') {
Tesseract.recognize(canvas.toDataURL("image/png"), "eng") Tesseract.recognize(canvas.toDataURL("image/png"), "eng")
.then(({ data: { text } }) => { .then(({ data: { text } }) => {
const vin = text.match(/[A-HJ-NPR-Z0-9]{17}/); const vin = text.match(/[A-HJ-NPR-Z0-9]{17}/);
@ -477,20 +576,40 @@
}) })
.catch((err) => console.error("OCR Error:", err)); .catch((err) => console.error("OCR Error:", err));
} }
}
function closeModal(scanVinBtn) {
stopScanner();
try {
const scannerModal = document.getElementById("scannerModal");
if (scannerModal) {
document.activeElement.blur();
scannerModal.setAttribute("inert", "true");
const modalInstance = bootstrap.Modal.getInstance(scannerModal);
if (modalInstance) modalInstance.hide();
if (scanVinBtn) scanVinBtn.focus();
}
} catch (err) {
console.error("Error closing scanner modal:", err);
}
}
function stopScanner() { 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();
} }
// Data loading functions
function resetDropdown(dropdown, placeholder) { function resetDropdown(dropdown, placeholder) {
dropdown.innerHTML = `<option value="">${placeholder}</option>`; if (dropdown) dropdown.innerHTML = `<option value="">${placeholder}</option>`;
} }
async function loadModels(makeId) { async function loadModels(makeId, modelSelect, elements) {
if (!modelSelect) return;
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: {
@ -507,10 +626,12 @@
}); });
} }
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) { async function loadTrims(serie_id, model_id, elements) {
resetDropdown(trimSelect, '{% trans "Select" %}'); if (!elements?.trimSelect) return;
specificationsContent.innerHTML = "";
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}`, { 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;
if (elements.showSpecificationButton) {
elements.showSpecificationButton.disabled = !elements.trimSelect.value;
}
} }
async function loadEquipment(trimId){ async function loadEquipment(trimId, elements) {
resetDropdown(equipmentSelect, '{% trans "Select" %}'); if (!elements?.equipmentSelect) return;
optionsContent.innerHTML = "";
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,50 +743,11 @@
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", () => {
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() { function showLoading() {
Swal.fire({ Swal.fire({
title: "{% trans 'Please Wait' %}", title: "{% trans 'Please Wait' %}",
@ -671,4 +770,4 @@
}); });
} }
</script> </script>
{% endblock %} {% endblock customJS %}