haikal/templates/inventory/car_form.html
2025-07-24 11:50:58 +03:00

773 lines
39 KiB
HTML

{% extends "base.html" %}
{% load i18n static custom_filters %}
{% block title %}
{% trans 'Add New Car' %} {% endblock %}
{% block content %}
<style>
#video {
width: 100%;
max-width: 480px;
height: auto;
margin: 0 auto;
}
.disabled{
opacity: 0.5;
pointer-events: none;
}
</style>
<!-- JavaScript Section -->
<script src="{% static 'vendors/zxing/index.min.js' %}"></script>
<script src="{% static 'vendors/tesseract/tesseract.min.js' %}"></script>
{% if not vendor_exists %}
<div class="alert alert-outline-warning d-flex align-items-center"
role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>
<p class="mb-0 flex-1">
{{ _("Please Add A Vendor, Before Adding A Car .") }} <a href="{% url 'vendor_create' request.dealer.slug %}"
class="ms-3 text-body-primary fs-9">{{ _("Add Vendor") }}</a>
</p>
<button class="btn-close"
type="button"
data-bs-dismiss="alert"
aria-label="Close"></button>
</div>
{% endif %}
<!---->
<div class="row justify-content-center mt-5 mb-3
{% if not vendor_exists %}disabled{% endif %}" hx-boost="false">
<div class="col-lg-8 col-md-10">
<div class="card shadow-sm border-0 rounded-3">
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
<h3 class="mb-0 fs-4 text-center text-white">
<h3 class="mb-3">
{% trans 'Add Car' %}
<li class="fas fa-car-on text-primary ms-2"></li>
</h3>
</h3>
</div>
<div class="card-body bg-light-subtle">
<form method="post" id="carForm" class="form needs-validation" novalidate>
{% csrf_token %}
{% include 'partials/form_errors.html' %}
<div class="d-flex flex-column">
<div class="d-flex flex-column flex-sm-grow-1 p-0">
<div class="row g-4">
<!-- VIN Section -->
<div class="col-lg-12 ">
<div class="card bg-body mb-3 shadow-sm">
<div class="card-body">
<!-- Improved VIN input with integrated buttons -->
<div class="mb-3">
<label for="{{ form.vin.id_for_label }}" class="form-label fw-bold">{% trans 'VIN' %}</label>
<div class="input-group">
<input type="text"
class="form-control text-center"
id="{{ form.vin.id_for_label }}"
name="{{ form.vin.html_name }}"
placeholder="{% trans 'VIN' %}"
required />
<button type="button"
class="btn btn-phoenix-warning"
id="scan-vin-btn"
data-bs-toggle="modal"
data-bs-target="#scannerModal"
title="{% trans 'Scan VIN' %}">
<span class="fas fa-camera fs-9"></span>
</button>
<button type="button"
class="btn btn-phoenix-success"
id="decodeVinBtn"
title="{% trans 'Decode VIN' %}">
<span class="fas fa-search fs-9"></span>
</button>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<div class="form-floating">
<input type="number"
class="form-control form-control-sm text-center"
id="{{ form.year.id_for_label }}"
name="{{ form.year.html_name }}" />
<label for="{{ form.year.id_for_label }}">{% trans 'Year' %}</label>
<span class="text-success fw-bold mt-1 d-block" id="year-check"></span>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6" id="make-row">
<div class="form-floating">
{{ form.id_car_make|add_class:"form-select form-select-sm text-center" }}
<label for="{{ form.id_car_make.id_for_label }}">{% trans 'make'|capfirst %}</label>
<span class="text-success fw-bold mt-1 d-block" id="make-check"></span>
</div>
</div>
<div class="col-md-6" id="model-row">
<div class="form-floating">
<select class="form-select form-select-sm text-center"
id="{{ form.id_car_model.id_for_label }}"
name="{{ form.id_car_model.html_name }}">
<option value="">{% trans 'Select' %}</option>
</select>
<label for="{{ form.id_car_model.id_for_label }}">{% trans 'model'|capfirst %}</label>
<span class="text-success fw-bold mt-1 d-block" id="model-check"></span>
</div>
<div class="badge badge-phoenix fs-11 badge-phoenix-success m-1"
id="generation-div"></div>
</div>
<div id="serie-row" class="col-md-6">
<div class="form-floating">
<select class="form-select form-select-sm text-center"
id="{{ form.id_car_serie.id_for_label }}"
name="{{ form.id_car_serie.html_name }}">
<option value="">{% trans 'Select' %}</option>
</select>
<label for="{{ form.id_car_serie.id_for_label }}">{% trans 'Series' %}</label>
</div>
</div>
<div class="col-md-6" id="trim-row">
<div class="form-floating">
<select class="form-select form-select-sm text-center"
id="{{ form.id_car_trim.id_for_label }}"
name="{{ form.id_car_trim.html_name }}">
<option value="">{% trans 'Select' %}</option>
</select>
<label for="{{ form.id_car_trim.id_for_label }}">{% trans 'trim'|capfirst %}</label>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6 d-flex align-items-center justify-content-center gap-2 mt-2">
<button type="button"
class="btn btn-phoenix-success"
id="specification-btn"
data-bs-toggle="modal"
data-bs-target="#specificationsModal"
disabled>
{% trans 'specifications'|capfirst %}
</button>
<button type="button"
class="btn btn-phoenix-warning"
id="options-btn"
data-bs-toggle="modal"
data-bs-target="#equipmentOptionsModal"
disabled>{% trans 'options'|capfirst %}</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-lg-12">
<div class="row">
<!--Vendor Field-->
<div class="col-lg-4 col-xl-4">
<div class="card bg-body my-1 shadow-sm">
<div class="card-body">
<div class="form-floating">
{{ form.vendor|add_class:"form-select text-center" }}
<label for="{{ form.vendor.id_for_label }}" class="form-label">{% trans 'Vendor' %}</label>
</div>
</div>
</div>
</div>
<!-- Stock Type Card -->
<div class="col-lg-4 col-xl-4">
<div class="card bg-body my-1 shadow-sm">
<div class="card-body">
<div class="form-floating">
{{ form.stock_type|add_class:"form-select text-center" }}
<label for="{{ form.stock_type.id_for_label }}">{% trans 'Stock Type'|capfirst %}</label>
</div>
</div>
</div>
</div>
<!-- Mileage Card -->
<div class="col-lg-4 col-xl-4">
<div class="card bg-body my-1 shadow-sm">
<div class="card-body">
<div class="form-floating">
{{ form.mileage|add_class:"form-control text-center" }}
<label for="{{ form.mileage.id_for_label }}">{% trans 'Mileage'|capfirst %}</label>
</div>
</div>
</div>
</div>
<!-- Receiving Date Field -->
<div class="col-lg-4 col-xl-4">
<div class="card bg-body my-3 shadow-sm">
<div class="card-body">
<div class="form-floating">
{{ form.receiving_date|add_class:"form-control text-center" }}
<label for="{{ form.receiving_date.id_for_label }}" class="form-label">{% trans 'Receiving Date' %}:</label>
</div>
</div>
</div>
</div>
<!-- Remarks Card -->
<div class="col-lg-4 col-xl-8">
<div class="card bg-body my-3 shadow-sm">
<div class="card-body">
<div class="form-floating">
{{ form.remarks|add_class:"form-control text-center" }}
<label for="{{ form.remarks.id_for_label }}">{% trans 'Remarks'|capfirst %}:</label>
</div>
</div>
</div>
</div>
</div>
<hr class="my-2">
<!--Save Buttons-->
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
<button type="submit"
hx-on-before-request="this.setAttribute('disabled','true')"
hx-on-after-request="this.removeAttribute('disabled')"
name="add_another"
value="true"
class="btn btn-lg btn-phoenix-success md-me-2">
{% trans "Save and Add Another" %}
</button>
<button type="submit"
hx-on-before-request="this.setAttribute('disabled','true')"
hx-on-after-request="this.removeAttribute('disabled')"
name="go_to_stats"
value="true"
class="btn btn-lg btn-phoenix-primary">
{% trans "Save and Go to Inventory" %}
</button>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<!-- Modal sections remain largely unchanged -->
<!--Specification Modal-->
<div class="modal fade"
id="specificationsModal"
tabindex="-1"
aria-labelledby="specificationsModalLabel"
aria-modal="true"
role="dialog">
<div class="modal-dialog modal-md">
<div class="modal-content overflow-hidden">
<div class="modal-header position-relative">
<h5 class="modal-title" id="specificationsModalLabel">
<span class="ms-3 my-5">{% trans 'specifications'|capfirst %}</span>
</h5>
<button class="btn btn-circle project-modal-btn position-absolute end-0 top-0 mt-3 me-3 bg-body-emphasis"
data-bs-dismiss="modal">
<span class="fa-solid fa-xmark text-body dark__text-gray-100"></span>
</button>
</div>
<div class="modal-body">
<span class="m-1 fw-light">{{ _("Details") }}</span>
<div id="specificationsContent"></div>
</div>
<div class="modal-footer">
<button class="btn btn-phoenix-primary" type="button" data-bs-dismiss="modal">{% trans 'Close' %}</button>
</div>
</div>
</div>
</div>
<!--equipments and options Modal-->
<div class="modal fade"
id="equipmentOptionsModal"
tabindex="-1"
aria-labelledby="equipmentOptionsModalLabel"
aria-modal="true"
role="dialog">
<div class="modal-dialog modal-md">
<div class="modal-content overflow-hidden">
<div class="modal-header position-relative">
<h5 class="modal-title" id="equipmentOptionsModalLabel">{% trans 'Options'|capfirst %}</h5>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="{% trans 'Close' %}"></button>
</div>
<div class="modal-body">
<div class="col-lg-4 col-xl-3">
<div class="card h-100">
<div class="card-body" id="equipment-container">
<label class="form-label" for="equipment_id">{% trans 'equipment'|capfirst %}:</label>
<select class="form-select text-center"
id="equipment_id"
name="equipment_name_id">
<option value="">{% trans 'Select' %}</option>
</select>
</div>
</div>
</div>
<div id="optionsContent"></div>
</div>
<div class="modal-footer">
<button class="btn btn-phoenix-primary" type="button" data-bs-dismiss="modal">{% trans 'Close' %}</button>
</div>
</div>
</div>
</div>
<!--Scanner Modal-->
<div class="modal fade"
id="scannerModal"
tabindex="-1"
aria-labelledby="scannerModalLabel">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-top-3">
<div class="modal-header rounded-top-3 shadow">
<h5 class="modal-title" id="scannerModalLabel">{{ _("scanner") }}</h5>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="{{ _("Close") }}"></button>
</div>
<div class="modal-body text-center">
<video id="video"
autoplay
playsinline
class="img-fluid border rounded shadow-sm">
</video>
<p id="result" class="mt-2 fw-bold">{{ _("VIN will appear here.") }}</p>
<button id="ocr-fallback-btn" class="btn btn-phoenix-primary mt-3">{{ _("Use OCR Fallback") }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!---->
{% endblock content %}
{% 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;
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;
}
// 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;
}
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) {
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);
if (vinData.make_id && elements.makeSelect) {
elements.makeSelect.value = vinData.make_id;
document.getElementById("make-check").innerHTML = "&#10003;";
await loadModels(vinData.make_id);
}
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) {
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();
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() {
if (currentStream) {
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) => {
const option = document.createElement("option");
option.value = trim.id_car_trim;
option.textContent = document.documentElement.lang === "en" ? trim.name : trim.name;
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
}
});
const data = await response.json();
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 %}