Fix Django template syntax and set col-map margin-top to 50px

This commit is contained in:
Karl Determe 2026-08-25 12:11:24 +02:00
parent d82dcde16d
commit 682c2bdfcf

View file

@ -3,175 +3,85 @@
{% load i18n %} {% load i18n %}
{% block head %} {% block head %}
<link href="{% static 'common/mapbox-gl-draw.css' %}" rel="stylesheet" /> <link href="{% static 'common/mapbox-gl-draw.css' %}" rel="stylesheet"/>
<link rel="stylesheet" href="{% static 'assets/assets_index.css' %}"> <link rel="stylesheet" href="{% static 'assets/assets_index.css' %}">
<link rel="stylesheet" href="{% static 'assets/info-panel.css' %}"> <link rel="stylesheet" href="{% static 'assets/info-panel.css' %}">
<style> <style>
#assets-search-input:focus { #assets-search-input:focus { outline: none; box-shadow: none; border-color: #dee2e6; }
outline: none; #unified-empty-state { display: none; color: #6c757d; }
box-shadow: none; /* ─── Filter modal ──────────────────────────────────── */
border-color: #dee2e6; #filterModal .modal-dialog { max-width: 95%; }
} #filterModal .modal-content { display: flex; flex-direction: column; max-height: 90vh; }
#filterModal .modal-body { flex: 1; overflow-y: auto; }
#unified-empty-state { #filterModal .modal-footer { flex-shrink: 0; }
display: none; .filter-section { margin-bottom: 1rem; }
color: #6c757d; .filter-section-header {
} display: flex; justify-content: space-between; align-items: center;
margin-bottom: 0.75rem; gap: 1rem;
/* ─── Filter modal ──────────────────────────────────── */ }
#filterModal .modal-dialog { .filter-section-header > label { font-weight: 600; color: #333; margin: 0; flex-shrink: 0; }
max-width: 95%; .filter-section-buttons { display: flex; gap: 0.5rem; margin-left: auto; flex-shrink: 0; }
} .filter-section-buttons button { padding: 0.25rem 0.5rem; font-size: 0.75rem; white-space: nowrap; }
.filter-options {
#filterModal .modal-content { display: flex; flex-wrap: wrap; gap: 0.5rem; min-height: 2.5rem;
display: flex; padding: 0.5rem; background-color: #f8f9fa;
flex-direction: column; border-radius: 0.375rem; border: 1px solid #dee2e6;
max-height: 90vh; }
} .filter-option { display: flex; align-items: center; }
.filter-option input[type="checkbox"],
#filterModal .modal-body { .filter-option input[type="radio"] {
flex: 1; position: absolute; opacity: 0; width: 0; height: 0; cursor: pointer;
overflow-y: auto; }
} .filter-option label {
display: inline-flex; align-items: center; justify-content: center;
#filterModal .modal-footer { margin: 0; font-weight: 500; cursor: pointer;
flex-shrink: 0; background-color: #e7f1ff; color: #0c5cc9;
} border-radius: 1rem; padding: 0.35rem 0.75rem; font-size: 0.8rem;
border: 1px solid #b8d4f1; transition: all 0.15s ease;
.filter-section { white-space: nowrap;
margin-bottom: 1rem; }
} .filter-option input[type="checkbox"]:checked + label,
.filter-option input[type="radio"]:checked + label {
.filter-section-header { background-color: #0c5cc9; color: #fff; border-color: #0c5cc9;
display: flex; }
justify-content: space-between; .filter-option label:hover { background-color: #cfe2f3; }
align-items: center; .filter-option input:checked + label:hover { background-color: #0a4fb0; }
margin-bottom: 0.75rem; .filter-option input:disabled + label { opacity: 0.5; cursor: not-allowed; }
gap: 1rem; #map .maplibregl-popup-close-button {
} font-size: 1.35rem;
line-height: 1;
.filter-section-header>label { padding: 0.2rem 0.45rem;
font-weight: 600; }
color: #333; .unified-add-panel {
margin: 0; border: 1px solid #dee2e6;
flex-shrink: 0; border-radius: 0.75rem;
} padding: 1rem;
background: #f8f9fa;
.filter-section-buttons { }
display: flex; .unified-add-panel h6 {
gap: 0.5rem; font-size: 0.95rem;
margin-left: auto; margin-bottom: 0.75rem;
flex-shrink: 0; }
} .unified-add-panel .form-select,
.unified-add-panel .form-control {
.filter-section-buttons button { border-radius: 0.65rem;
padding: 0.25rem 0.5rem; }
font-size: 0.75rem; #map .maplibregl-ctrl-top-right,
white-space: nowrap; #map .mapboxgl-ctrl-top-right {
} top: 10px !important;
}
.filter-options { </style>
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
min-height: 2.5rem;
padding: 0.5rem;
background-color: #f8f9fa;
border-radius: 0.375rem;
border: 1px solid #dee2e6;
}
.filter-option {
display: flex;
align-items: center;
}
.filter-option input[type="checkbox"],
.filter-option input[type="radio"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
cursor: pointer;
}
.filter-option label {
display: inline-flex;
align-items: center;
justify-content: center;
margin: 0;
font-weight: 500;
cursor: pointer;
background-color: #e7f1ff;
color: #0c5cc9;
border-radius: 1rem;
padding: 0.35rem 0.75rem;
font-size: 0.8rem;
border: 1px solid #b8d4f1;
transition: all 0.15s ease;
white-space: nowrap;
}
.filter-option input[type="checkbox"]:checked+label,
.filter-option input[type="radio"]:checked+label {
background-color: #0c5cc9;
color: #fff;
border-color: #0c5cc9;
}
.filter-option label:hover {
background-color: #cfe2f3;
}
.filter-option input:checked+label:hover {
background-color: #0a4fb0;
}
.filter-option input:disabled+label {
opacity: 0.5;
cursor: not-allowed;
}
#map .maplibregl-popup-close-button {
font-size: 1.35rem;
line-height: 1;
padding: 0.2rem 0.45rem;
}
.unified-add-panel {
border: 1px solid #dee2e6;
border-radius: 0.75rem;
padding: 1rem;
background: #f8f9fa;
}
.unified-add-panel h6 {
font-size: 0.95rem;
margin-bottom: 0.75rem;
}
.unified-add-panel .form-select,
.unified-add-panel .form-control {
border-radius: 0.65rem;
}
#map .maplibregl-ctrl-top-right,
#map .mapboxgl-ctrl-top-right {
top: 10px !important;
}
</style>
{% endblock head %} {% endblock head %}
{% block content %} {% block content %}
{# ─── Filter modal ──────────────────────────────────────────────────────────── #} {# ─── Modal de filtre ──────────────────────────────────────────────────────── #}
<div class="modal fade" id="filterModal" tabindex="-1" aria-labelledby="filterModalLabel" aria-hidden="true"> <div class="modal fade" id="filterModal" tabindex="-1" aria-labelledby="filterModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="filterModalLabel">{% translate "Filtrer les assets" %}</h5> <h5 class="modal-title" id="filterModalLabel">{% translate "Filtrer les assets" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
aria-label="{% translate 'Fermer' %}"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -180,21 +90,19 @@
<div class="filter-section-header"> <div class="filter-section-header">
<label>{% translate "Thématique" %}</label> <label>{% translate "Thématique" %}</label>
<div class="filter-section-buttons"> <div class="filter-section-buttons">
<button type="button" class="btn btn-sm btn-outline-secondary" id="thematic-select-all">{% <button type="button" class="btn btn-sm btn-outline-secondary" id="thematic-select-all">{% translate "Tous" %}</button>
translate "Tous" %}</button> <button type="button" class="btn btn-sm btn-outline-secondary" id="thematic-deselect-all">{% translate "Aucun" %}</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="thematic-deselect-all">{%
translate "Aucun" %}</button>
</div> </div>
</div> </div>
<div class="filter-options"> <div class="filter-options">
{% for thematic in accessible_thematics %} {% for thematic in accessible_thematics %}
<div class="filter-option"> <div class="filter-option">
<input type="checkbox" class="thematic-checkbox" id="thematic-cb-{{ thematic.code }}" <input type="checkbox" class="thematic-checkbox"
value="{{ thematic.code }}" {% if thematic.code in selected_thematics %}checked{% endif id="thematic-cb-{{ thematic.code }}"
%}> value="{{ thematic.code }}"
{% if thematic.code in selected_thematics %}checked{% endif %}>
<label for="thematic-cb-{{ thematic.code }}"> <label for="thematic-cb-{{ thematic.code }}">
{% if thematic.icon %}<i class="bi {{ thematic.icon }} me-1"></i>{% endif %}{{ {% if thematic.icon %}<i class="bi {{ thematic.icon }} me-1"></i>{% endif %}{{ thematic.name }}
thematic.name }}
</label> </label>
</div> </div>
{% endfor %} {% endfor %}
@ -206,15 +114,12 @@
<div class="filter-section-header"> <div class="filter-section-header">
<label>{% translate "Catégorie" %}</label> <label>{% translate "Catégorie" %}</label>
<div class="filter-section-buttons"> <div class="filter-section-buttons">
<button type="button" class="btn btn-sm btn-outline-secondary" id="category-select-all">{% <button type="button" class="btn btn-sm btn-outline-secondary" id="category-select-all">{% translate "Toutes" %}</button>
translate "Toutes" %}</button> <button type="button" class="btn btn-sm btn-outline-secondary" id="category-deselect-all">{% translate "Aucune" %}</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="category-deselect-all">{%
translate "Aucune" %}</button>
</div> </div>
</div> </div>
<div id="category-filter-options" class="filter-options"> <div id="category-filter-options" class="filter-options">
<span class="text-muted small">{% translate "Sélectionnez d'abord une ou plusieurs thématiques." <span class="text-muted small">{% translate "Sélectionnez d'abord une ou plusieurs thématiques." %}</span>
%}</span>
</div> </div>
</div> </div>
@ -223,17 +128,13 @@
<div class="filter-section-header"> <div class="filter-section-header">
<label>{% translate "Modèle" %}</label> <label>{% translate "Modèle" %}</label>
<div class="filter-section-buttons"> <div class="filter-section-buttons">
<button type="button" class="btn btn-sm btn-outline-secondary" id="model-select-all">{% <button type="button" class="btn btn-sm btn-outline-secondary" id="model-select-all">{% translate "Tous" %}</button>
translate "Tous" %}</button> <button type="button" class="btn btn-sm btn-outline-secondary" id="model-deselect-all">{% translate "Aucun" %}</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="model-deselect-all">{% <button type="button" class="btn btn-sm btn-outline-secondary d-none" id="model-filter-toggle"></button>
translate "Aucun" %}</button>
<button type="button" class="btn btn-sm btn-outline-secondary d-none"
id="model-filter-toggle"></button>
</div> </div>
</div> </div>
<div id="model-filter-options" class="filter-options"> <div id="model-filter-options" class="filter-options">
<span class="text-muted small">{% translate "Sélectionnez d'abord une ou plusieurs catégories." <span class="text-muted small">{% translate "Sélectionnez d'abord une ou plusieurs catégories." %}</span>
%}</span>
</div> </div>
</div> </div>
@ -243,8 +144,7 @@
<label>{% translate "Filtres thématiques" %}</label> <label>{% translate "Filtres thématiques" %}</label>
</div> </div>
<div id="conditional-filter-options" class="filter-options"> <div id="conditional-filter-options" class="filter-options">
<span class="text-muted small">{% translate "Sélectionnez d'abord une ou plusieurs thématiques." <span class="text-muted small">{% translate "Sélectionnez d'abord une ou plusieurs thématiques." %}</span>
%}</span>
</div> </div>
</div> </div>
@ -255,21 +155,21 @@
</div> </div>
<div class="filter-options"> <div class="filter-options">
<div class="filter-option"> <div class="filter-option">
<input type="radio" name="unified-type-filter" id="type-all" value="all" {% if <input type="radio" name="unified-type-filter" id="type-all" value="all"
preventive_element_type !='assets' and preventive_element_type !='locations' %}checked{% {% if preventive_element_type != 'assets' and preventive_element_type != 'locations' %}checked{% endif %}
endif %} {% if preventive_element_type %}disabled{% endif %}> {% if preventive_element_type %}disabled{% endif %}>
<label for="type-all">{% translate "Assets et localisations" %}</label> <label for="type-all">{% translate "Assets et localisations" %}</label>
</div> </div>
<div class="filter-option"> <div class="filter-option">
<input type="radio" name="unified-type-filter" id="type-locations" value="locations" {% if <input type="radio" name="unified-type-filter" id="type-locations" value="locations"
preventive_element_type=='locations' %}checked{% endif %} {% if preventive_element_type {% if preventive_element_type == 'locations' %}checked{% endif %}
%}disabled{% endif %}> {% if preventive_element_type %}disabled{% endif %}>
<label for="type-locations">{% translate "Localisations uniquement" %}</label> <label for="type-locations">{% translate "Localisations uniquement" %}</label>
</div> </div>
<div class="filter-option"> <div class="filter-option">
<input type="radio" name="unified-type-filter" id="type-assets" value="assets" {% if <input type="radio" name="unified-type-filter" id="type-assets" value="assets"
preventive_element_type=='assets' %}checked{% endif %} {% if preventive_element_type {% if preventive_element_type == 'assets' %}checked{% endif %}
%}disabled{% endif %}> {% if preventive_element_type %}disabled{% endif %}>
<label for="type-assets">{% translate "Assets uniquement" %}</label> <label for="type-assets">{% translate "Assets uniquement" %}</label>
</div> </div>
</div> </div>
@ -297,8 +197,7 @@
<div class="filter-options"> <div class="filter-options">
{% for status_value, status_label in asset_status_choices %} {% for status_value, status_label in asset_status_choices %}
<div class="filter-option"> <div class="filter-option">
<input type="checkbox" name="filter-status" id="filter-status-{{ status_value }}" <input type="checkbox" name="filter-status" id="filter-status-{{ status_value }}" value="{{ status_value }}">
value="{{ status_value }}">
<label for="filter-status-{{ status_value }}">{{ status_label }}</label> <label for="filter-status-{{ status_value }}">{{ status_label }}</label>
</div> </div>
{% endfor %} {% endfor %}
@ -313,14 +212,10 @@
<label>{% translate "Statut de validation" %}</label> <label>{% translate "Statut de validation" %}</label>
</div> </div>
<div class="filter-options"> <div class="filter-options">
{% for validation_status_value, validation_status_label in asset_validation_status_choices {% for validation_status_value, validation_status_label in asset_validation_status_choices %}
%}
<div class="filter-option"> <div class="filter-option">
<input type="checkbox" name="filter-validation-status" <input type="checkbox" name="filter-validation-status" id="filter-validation-status-{{ validation_status_value }}" value="{{ validation_status_value }}">
id="filter-validation-status-{{ validation_status_value }}" <label for="filter-validation-status-{{ validation_status_value }}">{{ validation_status_label }}</label>
value="{{ validation_status_value }}">
<label for="filter-validation-status-{{ validation_status_value }}">{{
validation_status_label }}</label>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -329,10 +224,8 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" id="reset-filters" class="btn btn-secondary">{% translate "Réinitialiser" <button type="button" id="reset-filters" class="btn btn-secondary">{% translate "Réinitialiser" %}</button>
%}</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" %}</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler"
%}</button>
<button type="button" id="apply-filters" class="btn btn-primary">{% translate "Filtrer" %}</button> <button type="button" id="apply-filters" class="btn btn-primary">{% translate "Filtrer" %}</button>
</div> </div>
</div> </div>
@ -340,14 +233,12 @@
</div> </div>
{# ─── Add chooser modal ────────────────────────────────────────────────────── #} {# ─── Add chooser modal ────────────────────────────────────────────────────── #}
<div class="modal fade" id="unifiedAddChooserModal" tabindex="-1" aria-labelledby="unifiedAddChooserModalLabel" <div class="modal fade" id="unifiedAddChooserModal" tabindex="-1" aria-labelledby="unifiedAddChooserModalLabel" aria-hidden="true">
aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered"> <div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="unifiedAddChooserModalLabel">{% translate "Créer un élément" %}</h5> <h5 class="modal-title" id="unifiedAddChooserModalLabel">{% translate "Créer un élément" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
aria-label="{% translate 'Fermer' %}"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="unified-add-chooser-alert" class="alert alert-info small mb-3"> <div id="unified-add-chooser-alert" class="alert alert-info small mb-3">
@ -356,10 +247,8 @@
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6"> <div class="col-md-6">
<div class="unified-add-panel h-100"> <div class="unified-add-panel h-100">
<h6 class="d-flex align-items-center gap-2"><i class="bi bi-layers"></i>{% translate "Asset" <h6 class="d-flex align-items-center gap-2"><i class="bi bi-layers"></i>{% translate "Asset" %}</h6>
%}</h6> <label for="unified-add-asset-thematic" class="form-label small text-muted mb-1">{% translate "Thématique" %}</label>
<label for="unified-add-asset-thematic" class="form-label small text-muted mb-1">{%
translate "Thématique" %}</label>
<select id="unified-add-asset-thematic" class="form-select mb-3"></select> <select id="unified-add-asset-thematic" class="form-select mb-3"></select>
<button type="button" id="unified-add-asset-open" class="btn btn-primary w-100"> <button type="button" id="unified-add-asset-open" class="btn btn-primary w-100">
<i class="bi bi-plus-circle me-1"></i>{% translate "Créer un asset" %} <i class="bi bi-plus-circle me-1"></i>{% translate "Créer un asset" %}
@ -368,10 +257,8 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="unified-add-panel h-100"> <div class="unified-add-panel h-100">
<h6 class="d-flex align-items-center gap-2"><i class="bi bi-geo-alt"></i>{% translate <h6 class="d-flex align-items-center gap-2"><i class="bi bi-geo-alt"></i>{% translate "Localisation" %}</h6>
"Localisation" %}</h6> <label for="unified-add-location-thematic" class="form-label small text-muted mb-1">{% translate "Thématique" %}</label>
<label for="unified-add-location-thematic" class="form-label small text-muted mb-1">{%
translate "Thématique" %}</label>
<select id="unified-add-location-thematic" class="form-select mb-3"></select> <select id="unified-add-location-thematic" class="form-select mb-3"></select>
<button type="button" id="unified-add-location-open" class="btn btn-outline-primary w-100"> <button type="button" id="unified-add-location-open" class="btn btn-outline-primary w-100">
<i class="bi bi-plus-circle me-1"></i>{% translate "Créer une localisation" %} <i class="bi bi-plus-circle me-1"></i>{% translate "Créer une localisation" %}
@ -388,46 +275,39 @@
</div> </div>
{# ─── Asset create modal ──────────────────────────────────────────────────── #} {# ─── Asset create modal ──────────────────────────────────────────────────── #}
<div class="modal fade" id="unifiedAddAssetModal" tabindex="-1" aria-labelledby="unifiedAddAssetModalLabel" <div class="modal fade" id="unifiedAddAssetModal" tabindex="-1" aria-labelledby="unifiedAddAssetModalLabel" aria-hidden="true">
aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable"> <div class="modal-dialog modal-lg modal-dialog-scrollable">
<form id="unifiedAddAssetForm" class="modal-content"> <form id="unifiedAddAssetForm" class="modal-content">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="thematic_code" id="unified-add-asset-thematic-code"> <input type="hidden" name="thematic_code" id="unified-add-asset-thematic-code">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="unifiedAddAssetModalLabel">{% translate "Créer un asset" %}</h5> <h5 class="modal-title" id="unifiedAddAssetModalLabel">{% translate "Créer un asset" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
aria-label="{% translate 'Fermer' %}"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="unifiedAddAssetAlert" class="alert d-none" role="alert"></div> <div id="unifiedAddAssetAlert" class="alert d-none" role="alert"></div>
<div class="mb-3"> <div class="mb-3">
<label for="unifiedAddAssetCategory" class="form-label">{% translate "Catégorie" %} <span <label for="unifiedAddAssetCategory" class="form-label">{% translate "Catégorie" %} <span class="text-danger">*</span></label>
class="text-danger">*</span></label>
<select class="form-select" id="unifiedAddAssetCategory" name="category_id" required> <select class="form-select" id="unifiedAddAssetCategory" name="category_id" required>
<option value="">{% translate "Sélectionner une catégorie..." %}</option> <option value="">{% translate "Sélectionner une catégorie..." %}</option>
</select> </select>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="unifiedAddAssetModel" class="form-label">{% translate "Modèle" %} <span <label for="unifiedAddAssetModel" class="form-label">{% translate "Modèle" %} <span class="text-danger">*</span></label>
class="text-danger">*</span></label>
<select class="form-select" id="unifiedAddAssetModel" name="model_id" required disabled> <select class="form-select" id="unifiedAddAssetModel" name="model_id" required disabled>
<option value="">{% translate "Sélectionner d'abord une catégorie..." %}</option> <option value="">{% translate "Sélectionner d'abord une catégorie..." %}</option>
</select> </select>
</div> </div>
<div class="mb-3 d-none" id="unifiedAddAssetLocationGroup"> <div class="mb-3 d-none" id="unifiedAddAssetLocationGroup">
<label for="unifiedAddAssetLocation" class="form-label">{% translate "Localisation" %} <span <label for="unifiedAddAssetLocation" class="form-label">{% translate "Localisation" %} <span class="text-danger">*</span></label>
class="text-danger">*</span></label>
<select class="form-select" id="unifiedAddAssetLocation" name="location_id" disabled> <select class="form-select" id="unifiedAddAssetLocation" name="location_id" disabled>
<option value="">{% translate "Chargement..." %}</option> <option value="">{% translate "Chargement..." %}</option>
</select> </select>
<div class="form-text text-muted" id="unifiedAddAssetLocationHelp"></div> <div class="form-text text-muted" id="unifiedAddAssetLocationHelp"></div>
</div> </div>
<div class="mb-3" id="unifiedAddAssetCodeGroup"> <div class="mb-3" id="unifiedAddAssetCodeGroup">
<label for="unifiedAddAssetCode" class="form-label">{% translate "Code" %} <span <label for="unifiedAddAssetCode" class="form-label">{% translate "Code" %} <span class="text-danger">*</span></label>
class="text-danger">*</span></label> <input type="text" class="form-control" id="unifiedAddAssetCode" name="code" maxlength="50" required>
<input type="text" class="form-control" id="unifiedAddAssetCode" name="code" maxlength="50"
required>
<div class="form-text text-muted d-none" id="unifiedAddAssetCodeHelp"></div> <div class="form-text text-muted d-none" id="unifiedAddAssetCodeHelp"></div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
@ -439,13 +319,11 @@
<input type="text" class="form-control" id="unifiedAddAssetNameNl" name="name_nl" maxlength="200"> <input type="text" class="form-control" id="unifiedAddAssetNameNl" name="name_nl" maxlength="200">
</div> </div>
<div class="alert alert-info small mb-0"> <div class="alert alert-info small mb-0">
<i class="bi bi-info-circle me-1"></i>{% translate "L'asset sera créé avec le statut 'À approuver'." <i class="bi bi-info-circle me-1"></i>{% translate "L'asset sera créé avec le statut 'À approuver'." %}
%}
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" %}</button>
%}</button>
<button type="submit" class="btn btn-primary" id="unifiedAddAssetSubmit"> <button type="submit" class="btn btn-primary" id="unifiedAddAssetSubmit">
<i class="bi bi-plus-circle me-1"></i>{% translate "Créer l'asset" %} <i class="bi bi-plus-circle me-1"></i>{% translate "Créer l'asset" %}
</button> </button>
@ -455,17 +333,14 @@
</div> </div>
{# ─── Location create modal ───────────────────────────────────────────────── #} {# ─── Location create modal ───────────────────────────────────────────────── #}
<div class="modal fade" id="unifiedAddLocationModal" tabindex="-1" aria-labelledby="unifiedAddLocationModalLabel" <div class="modal fade" id="unifiedAddLocationModal" tabindex="-1" aria-labelledby="unifiedAddLocationModalLabel" aria-hidden="true">
aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable"> <div class="modal-dialog modal-lg modal-dialog-scrollable">
<form id="unifiedAddLocationForm" class="modal-content"> <form id="unifiedAddLocationForm" class="modal-content">
{% csrf_token %} {% csrf_token %}
<input type="hidden" id="unified-add-location-thematic-code" name="thematic_code">
<input type="hidden" id="unified-add-location-type" name="location_type"> <input type="hidden" id="unified-add-location-type" name="location_type">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="unifiedAddLocationModalLabel">{% translate "Créer une localisation" %}</h5> <h5 class="modal-title" id="unifiedAddLocationModalLabel">{% translate "Créer une localisation" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
aria-label="{% translate 'Fermer' %}"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="unifiedAddLocationAlert" class="alert d-none" role="alert"></div> <div id="unifiedAddLocationAlert" class="alert d-none" role="alert"></div>
@ -477,8 +352,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" %}</button>
%}</button>
<button type="submit" class="btn btn-success" id="unifiedAddLocationSubmit"> <button type="submit" class="btn btn-success" id="unifiedAddLocationSubmit">
<i class="bi bi-plus-circle me-1"></i>{% translate "Créer la localisation" %} <i class="bi bi-plus-circle me-1"></i>{% translate "Créer la localisation" %}
</button> </button>
@ -508,17 +382,12 @@
{# Dropdown Tableaux spécifiques (générique selon les thématiques accessibles) #} {# Dropdown Tableaux spécifiques (générique selon les thématiques accessibles) #}
{% if specific_tables %} {% if specific_tables %}
<div class="dropdown"> <div class="dropdown">
<button <button class="btn btn-light btn-sm dropdown-toggle rounded-pill px-3 border shadow-sm d-inline-flex align-items-center"
class="btn btn-light btn-sm dropdown-toggle rounded-pill px-3 border shadow-sm d-inline-flex align-items-center" type="button" id="specificTablesDropdown" data-bs-toggle="dropdown" aria-expanded="false">
type="button" id="specificTablesDropdown" data-bs-toggle="dropdown" aria-expanded="false">
{% translate "Répertoires" %} {% translate "Répertoires" %}
</button> </button>
<ul class="dropdown-menu dropdown-menu-end shadow border-0 mt-1" aria-labelledby="specificTablesDropdown" <ul class="dropdown-menu dropdown-menu-end shadow border-0 mt-1" aria-labelledby="specificTablesDropdown" style="min-width: 270px;">
style="min-width: 270px;"> <li><h6 class="dropdown-header text-uppercase small text-muted">{% translate "Répertoires spécifiques" %}</h6></li>
<li>
<h6 class="dropdown-header text-uppercase small text-muted">{% translate "Répertoires spécifiques"
%}</h6>
</li>
{% for table in specific_tables %} {% for table in specific_tables %}
<li> <li>
<a class="dropdown-item d-flex align-items-center gap-3 py-2" href="{{ table.url }}"> <a class="dropdown-item d-flex align-items-center gap-3 py-2" href="{{ table.url }}">
@ -537,16 +406,13 @@
{% endif %} {% endif %}
<div class="btn-group" role="group" aria-label="{% translate 'Sélection de la vue' %}"> <div class="btn-group" role="group" aria-label="{% translate 'Sélection de la vue' %}">
<button id="layout-btn-table" class="btn btn-light" type="button" <button id="layout-btn-table" class="btn btn-light" type="button" title="{% translate 'Vue tableau uniquement' %}">
title="{% translate 'Vue tableau uniquement' %}">
<i class="bi bi-table"></i> <i class="bi bi-table"></i>
</button> </button>
<button id="layout-btn-both" class="btn btn-light text-primary" type="button" <button id="layout-btn-both" class="btn btn-light text-primary" type="button" title="{% translate 'Vue partagée' %}">
title="{% translate 'Vue partagée' %}">
<i class="bi bi-layout-split"></i> <i class="bi bi-layout-split"></i>
</button> </button>
<button id="layout-btn-map" class="btn btn-light" type="button" <button id="layout-btn-map" class="btn btn-light" type="button" title="{% translate 'Vue carte uniquement' %}">
title="{% translate 'Vue carte uniquement' %}">
<i class="bi bi-globe-europe-africa"></i> <i class="bi bi-globe-europe-africa"></i>
</button> </button>
</div> </div>
@ -554,38 +420,35 @@
{# Search widget - perfectly centered horizontally #} {# Search widget - perfectly centered horizontally #}
<div id="assets-search-widget" <div id="assets-search-widget"
style="position:absolute; top:4px; left:50%; transform:translateX(-50%); z-index:10; pointer-events:none; max-width:calc(100% - 360px);"> style="position:absolute; top:4px; left:50%; transform:translateX(-50%); z-index:10; pointer-events:none; max-width:calc(100% - 360px);">
<div class="input-group" style="pointer-events:auto; flex-wrap:nowrap;"> <div class="input-group" style="pointer-events:auto; flex-wrap:nowrap;">
<div style="position:relative; display:flex; width:360px; max-width:100%; min-width:60px;"> <div style="position:relative; display:flex; width:360px; max-width:100%; min-width:60px;">
<input type="text" id="assets-search-input" class="form-control form-control-sm" <input type="text" id="assets-search-input" class="form-control form-control-sm"
style="width:100%; border-radius:20px 0 0 20px; border-color:#dee2e6; padding-right:1.6rem;" style="width:100%; border-radius:20px 0 0 20px; border-color:#dee2e6; padding-right:1.6rem;"
placeholder="{% translate 'Code, nom, type…' %}"> placeholder="{% translate 'Code, nom, type…' %}">
<button type="button" id="assets-search-clear" style="position:absolute; right:4px; top:50%; transform:translateY(-50%); <button type="button" id="assets-search-clear"
style="position:absolute; right:4px; top:50%; transform:translateY(-50%);
background:none; border:none; padding:0 2px; line-height:1; background:none; border:none; padding:0 2px; line-height:1;
color:#adb5bd; cursor:pointer; visibility:hidden;" title="{% translate 'Effacer' %}"> color:#adb5bd; cursor:pointer; visibility:hidden;"
title="{% translate 'Effacer' %}">
<i class="bi bi-x-lg" style="font-size:0.7rem;"></i> <i class="bi bi-x-lg" style="font-size:0.7rem;"></i>
</button> </button>
</div> </div>
<span class="input-group-text bg-white px-1" <span class="input-group-text bg-white px-1" style="border-left:0; border-right:0; border-color:#dee2e6; flex-shrink:0;">
style="border-left:0; border-right:0; border-color:#dee2e6; flex-shrink:0;">
<div class="btn-group btn-group-sm" role="group"> <div class="btn-group btn-group-sm" role="group">
<input type="radio" class="btn-check" name="assets-search-mode" id="assets-search-mode-and" <input type="radio" class="btn-check" name="assets-search-mode" id="assets-search-mode-and" value="and" checked>
value="and" checked>
<label class="btn btn-outline-secondary py-0 px-2" for="assets-search-mode-and" <label class="btn btn-outline-secondary py-0 px-2" for="assets-search-mode-and"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-placement="top"
title="{% translate 'Tous les mots recherchés doivent être présents' %}">{% translate "ET" title="{% translate 'Tous les mots recherchés doivent être présents' %}">{% translate "ET" %}</label>
%}</label> <input type="radio" class="btn-check" name="assets-search-mode" id="assets-search-mode-or" value="or">
<input type="radio" class="btn-check" name="assets-search-mode" id="assets-search-mode-or"
value="or">
<label class="btn btn-outline-secondary py-0 px-2" for="assets-search-mode-or" <label class="btn btn-outline-secondary py-0 px-2" for="assets-search-mode-or"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-toggle="tooltip" data-bs-placement="top"
title="{% translate 'Au moins un des mots recherchés doit être présent' %}">{% translate "OU" title="{% translate 'Au moins un des mots recherchés doit être présent' %}">{% translate "OU" %}</label>
%}</label>
</div> </div>
</span> </span>
<button class="btn btn-sm px-3" type="button" id="assets-search-btn" <button class="btn btn-sm px-3" type="button" id="assets-search-btn"
style="border-radius:0 20px 20px 0; border:1px solid #dee2e6; border-left:0; background:white; color:#495057; flex-shrink:0;" style="border-radius:0 20px 20px 0; border:1px solid #dee2e6; border-left:0; background:white; color:#495057; flex-shrink:0;"
title="{% translate 'Rechercher' %}"> title="{% translate 'Rechercher' %}">
<i class="bi bi-search"></i> <i class="bi bi-search"></i>
</button> </button>
</div> </div>
@ -605,16 +468,16 @@
</li> </li>
<div class="my-1 ms-2"> <div class="my-1 ms-2">
<button class="btn btn-light" type="button" id="btn-add-asset" <button class="btn btn-light" type="button" id="btn-add-asset"
title="{% translate 'Ajouter un asset' %}"> title="{% translate 'Ajouter un asset' %}">
<i class="bi bi-plus-circle"></i> <i class="bi bi-plus-circle"></i>
</button> </button>
</div> </div>
<div class="my-1 ms-1"> <div class="my-1 ms-1">
<button class="btn btn-light position-relative" type="button" data-bs-toggle="modal" <button class="btn btn-light position-relative" type="button"
data-bs-target="#filterModal" title="{% translate 'Filtres' %}"> data-bs-toggle="modal" data-bs-target="#filterModal"
title="{% translate 'Filtres' %}">
<i class="bi bi-funnel"></i> {% translate "Filtres" %} <i class="bi bi-funnel"></i> {% translate "Filtres" %}
<span id="filter-badge" <span id="filter-badge" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-primary d-none">
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-primary d-none">
<span id="filter-count">0</span> <span id="filter-count">0</span>
</span> </span>
</button> </button>
@ -623,49 +486,41 @@
{# Tab content #} {# Tab content #}
<div class="tab-content d-flex flex-column" style="flex: 1; min-height: 0;"> <div class="tab-content d-flex flex-column" style="flex: 1; min-height: 0;">
<div class="tab-pane fade show active d-flex flex-column" <div class="tab-pane fade show active d-flex flex-column" style="flex: 1; min-height: 0; padding-top: 0.5rem;">
style="flex: 1; min-height: 0; padding-top: 0.5rem;">
{# Selection toolbar #} {# Selection toolbar #}
<div class="d-flex gap-2 flex-wrap align-items-center mb-1"> <div class="d-flex gap-2 flex-wrap align-items-center mb-1">
<button type="button" class="btn btn-sm btn-outline-secondary" id="unified-select-all">{% <button type="button" class="btn btn-sm btn-outline-secondary" id="unified-select-all">{% translate "Tout sélectionner" %}</button>
translate "Tout sélectionner" %}</button> <button type="button" class="btn btn-sm btn-outline-secondary" id="unified-deselect-all">{% translate "Tout désélectionner" %}</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="unified-deselect-all">{%
translate "Tout désélectionner" %}</button>
{% if preventive_draft_id %} {% if preventive_draft_id %}
<button type="submit" class="btn btn-success btn-sm" form="preventive-select-form">{% translate <button type="submit" class="btn btn-success btn-sm" form="preventive-select-form">{% translate "Valider la sélection" %}</button>
"Valider la sélection" %}</button>
{% endif %} {% endif %}
<div id="unified-selection-status" class="text-muted small mb-0">0 {% translate "sélectionné(s)" <div id="unified-selection-status" class="text-muted small mb-0">0 {% translate "sélectionné(s)" %}</div>
%}</div>
{% if not preventive_draft_id %} {% if not preventive_draft_id %}
<div class="dropdown ms-auto" id="unified-bulk-action-dropdown"> <div class="dropdown ms-auto" id="unified-bulk-action-dropdown">
<button class="btn btn-light btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" <button class="btn btn-light btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
aria-expanded="false">
{% translate "Action" %} {% translate "Action" %}
</button> </button>
<ul class="dropdown-menu dropdown-menu-end"> <ul class="dropdown-menu dropdown-menu-end">
<li> <li>
<a class="dropdown-item" href="#" id="unified-action-export" data-bs-toggle="modal" <a class="dropdown-item" href="#" id="unified-action-export"
data-bs-target="#unifiedExportModal"> data-bs-toggle="modal" data-bs-target="#unifiedExportModal">
<i class="bi bi-download me-2"></i>{% translate "Exporter" %} <i class="bi bi-download me-2"></i>{% translate "Exporter" %}
</a> </a>
</li> </li>
{% if has_bulk_edit_status_permission %} {% if has_bulk_edit_status_permission %}
<li> <li>
<a class="dropdown-item" href="#" id="unified-action-bulk-status" <a class="dropdown-item" href="#" id="unified-action-bulk-status"
data-bs-toggle="modal" data-bs-target="#bulkStatusModal"> data-bs-toggle="modal" data-bs-target="#bulkStatusModal">
<i class="bi bi-pencil-square me-2"></i>{% translate "Modifier le statut en <i class="bi bi-pencil-square me-2"></i>{% translate "Modifier le statut en masse" %}
masse" %}
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if has_bulk_edit_validation_status_permission %} {% if has_bulk_edit_validation_status_permission %}
<li> <li>
<a class="dropdown-item" href="#" id="unified-action-bulk-validation-status" <a class="dropdown-item" href="#" id="unified-action-bulk-validation-status"
data-bs-toggle="modal" data-bs-target="#bulkValidationStatusModal"> data-bs-toggle="modal" data-bs-target="#bulkValidationStatusModal">
<i class="bi bi-shield-check me-2"></i>{% translate "Modifier le statut de <i class="bi bi-shield-check me-2"></i>{% translate "Modifier le statut de validation en masse" %}
validation en masse" %}
</a> </a>
</li> </li>
{% endif %} {% endif %}
@ -678,8 +533,7 @@
<div style="position: relative;"> <div style="position: relative;">
{# Spinner overlay — shown on top of existing table content during loading #} {# Spinner overlay — shown on top of existing table content during loading #}
<div id="unified-spinner" <div id="unified-spinner" style="display:none; position:absolute; inset:0; z-index:10; background:rgba(255,255,255,0.75); justify-content:center; align-items:flex-start; padding-top:2rem;">
style="display:none; position:absolute; inset:0; z-index:10; background:rgba(255,255,255,0.75); justify-content:center; align-items:flex-start; padding-top:2rem;">
<div class="spinner-border text-primary" role="status"> <div class="spinner-border text-primary" role="status">
<span class="visually-hidden">{% translate "Loading..." %}</span> <span class="visually-hidden">{% translate "Loading..." %}</span>
</div> </div>
@ -688,14 +542,17 @@
{# Empty state #} {# Empty state #}
<div id="unified-empty-state" class="text-center py-5"> <div id="unified-empty-state" class="text-center py-5">
<i class="bi bi-layers fs-1 text-muted"></i> <i class="bi bi-layers fs-1 text-muted"></i>
<p class="mt-2">{% translate "Sélectionnez une ou plusieurs thématiques dans les filtres, <p class="mt-2">{% translate "Sélectionnez une ou plusieurs thématiques dans les filtres, saisissez une recherche ou zoomez sur la carte." %}</p>
saisissez une recherche ou zoomez sur la carte." %}</p>
</div> </div>
{# Table #} {# Table #}
<table id="unified-table" class="table table-hover table-sm" data-pagination="true" <table id="unified-table"
data-page-size="100" data-click-to-select="true" data-show-columns="true" class="table table-hover table-sm"
data-search="false"> data-pagination="true"
data-page-size="100"
data-click-to-select="true"
data-show-columns="true"
data-search="false">
</table> </table>
</div> </div>
@ -718,11 +575,8 @@
</div> </div>
<div id="map" style="position: relative; height: calc(100vh - 200px);"> <div id="map" style="position: relative; height: calc(100vh - 200px);">
<div id="assets-hint" <div id="assets-hint" class="small fst-italic position-absolute start-50 translate-middle-x bg-white px-3 py-1 rounded-pill shadow-sm border" style="top: 10px; z-index: 5; visibility: hidden; color: #0d6efd; white-space: nowrap; pointer-events: none;">
class="small fst-italic position-absolute start-50 translate-middle-x bg-white px-3 py-1 rounded-pill shadow-sm border" <i class="bi bi-info-circle me-1"></i>{% translate "Choisissez une catégorie ou zoomez sur la carte pour voir les assets." %}
style="top: 10px; z-index: 5; visibility: hidden; color: #0d6efd; white-space: nowrap; pointer-events: none;">
<i class="bi bi-info-circle me-1"></i>{% translate "Choisissez une catégorie ou zoomez sur la carte
pour voir les assets." %}
</div> </div>
<div id="totalsOverlay" class="totals-overlay"> <div id="totalsOverlay" class="totals-overlay">
<div class="totals-toolbar"> <div class="totals-toolbar">
@ -735,50 +589,41 @@
</div> </div>
</div> </div>
<div> <div>
<button id="totals_button" class="btn btn-light mt-2 mb-1" type="button">{% translate "Totaux" <button id="totals_button" class="btn btn-light mt-2 mb-1" type="button">{% translate "Totaux" %}</button>
%}</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% if not preventive_draft_id %} {% if not preventive_draft_id %}
<div class="modal fade" id="unifiedExportModal" tabindex="-1" aria-labelledby="unifiedExportModalLabel" <div class="modal fade" id="unifiedExportModal" tabindex="-1" aria-labelledby="unifiedExportModalLabel" aria-hidden="true">
aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="unifiedExportModalLabel">{% translate "Exporter les assets sélectionnés" %} <h5 class="modal-title" id="unifiedExportModalLabel">{% translate "Exporter les assets sélectionnés" %}</h5>
</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="{% translate 'Fermer' %}"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p class="text-muted small">{% translate "Choisissez le format d'export :" %}</p> <p class="text-muted small">{% translate "Choisissez le format d'export :" %}</p>
<div class="form-check"> <div class="form-check mb-2">
<input class="form-check-input" type="radio" name="unified-export-format" <input class="form-check-input" type="radio" name="unified-export-format" id="unified-export-format-csv" value="csv" checked>
id="unified-export-format-csv" value="csv" checked>
<label class="form-check-label" for="unified-export-format-csv">CSV</label> <label class="form-check-label" for="unified-export-format-csv">CSV</label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="unified-export-format" <input class="form-check-input" type="radio" name="unified-export-format" id="unified-export-format-xlsx" value="xlsx">
id="unified-export-format-xlsx" value="xlsx">
<label class="form-check-label" for="unified-export-format-xlsx">Excel (XLSX)</label> <label class="form-check-label" for="unified-export-format-xlsx">Excel (XLSX)</label>
</div> </div>
<div id="unified-export-error" class="alert alert-danger d-none mt-3" role="alert"></div> <div id="unified-export-error" class="alert alert-danger d-none mt-3" role="alert"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" %}</button>
%}</button> <button type="button" class="btn btn-primary" id="unified-confirm-export-btn">{% translate "Exporter" %}</button>
<button type="button" class="btn btn-primary" id="unified-confirm-export-btn">{% translate "Exporter"
%}</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endif %}
{# ─── Modal bulk status ────────────────────────────────────────────────── #} {# ─── Modal bulk status ──────────────────────────────────────────────────── #}
{% if has_bulk_edit_status_permission %} {% if has_bulk_edit_status_permission %}
<div class="modal fade" id="bulkStatusModal" tabindex="-1" aria-labelledby="bulkStatusModalLabel" aria-hidden="true"> <div class="modal fade" id="bulkStatusModal" tabindex="-1" aria-labelledby="bulkStatusModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
@ -791,6 +636,7 @@
<div class="mb-3"> <div class="mb-3">
<label for="bulk-status-select" class="form-label">{% translate "Nouveau statut" %}</label> <label for="bulk-status-select" class="form-label">{% translate "Nouveau statut" %}</label>
<select class="form-select" id="bulk-status-select"> <select class="form-select" id="bulk-status-select">
<option value="">-</option>
{% for value, label in asset_status_choices %} {% for value, label in asset_status_choices %}
<option value="{{ value }}">{{ label }}</option> <option value="{{ value }}">{{ label }}</option>
{% endfor %} {% endfor %}
@ -799,10 +645,8 @@
<div id="bulk-status-error" class="alert alert-danger d-none" role="alert"></div> <div id="bulk-status-error" class="alert alert-danger d-none" role="alert"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" %}</button>
%}</button> <button type="button" class="btn btn-primary" id="bulk-status-confirm-btn">{% translate "Enregistrer" %}</button>
<button type="button" class="btn btn-primary" id="bulk-status-confirm-btn">{% translate "Enregistrer"
%}</button>
</div> </div>
</div> </div>
</div> </div>
@ -811,19 +655,16 @@
{# ─── Modal bulk validation status ──────────────────────────────────────── #} {# ─── Modal bulk validation status ──────────────────────────────────────── #}
{% if has_bulk_edit_validation_status_permission %} {% if has_bulk_edit_validation_status_permission %}
<div class="modal fade" id="bulkValidationStatusModal" tabindex="-1" aria-labelledby="bulkValidationStatusModalLabel" <div class="modal fade" id="bulkValidationStatusModal" tabindex="-1" aria-labelledby="bulkValidationStatusModalLabel" aria-hidden="true">
aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="bulkValidationStatusModalLabel">{% translate "Modifier le statut de <h5 class="modal-title" id="bulkValidationStatusModalLabel">{% translate "Modifier le statut de validation en masse" %}</h5>
validation en masse" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="mb-3"> <div class="mb-3">
<label for="bulk-validation-status-select" class="form-label">{% translate "Nouveau statut de <label for="bulk-validation-status-select" class="form-label">{% translate "Nouveau statut de validation" %}</label>
validation" %}</label>
<select class="form-select" id="bulk-validation-status-select"> <select class="form-select" id="bulk-validation-status-select">
<option value="">-</option> <option value="">-</option>
{% for value, label in asset_validation_status_choices %} {% for value, label in asset_validation_status_choices %}
@ -834,10 +675,8 @@
<div id="bulk-validation-status-error" class="alert alert-danger d-none" role="alert"></div> <div id="bulk-validation-status-error" class="alert alert-danger d-none" role="alert"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% translate "Annuler" %}</button>
%}</button> <button type="button" class="btn btn-primary" id="bulk-validation-status-confirm-btn">{% translate "Enregistrer" %}</button>
<button type="button" class="btn btn-primary" id="bulk-validation-status-confirm-btn">{% translate
"Enregistrer" %}</button>
</div> </div>
</div> </div>
</div> </div>
@ -859,58 +698,58 @@
{% endblock content %} {% endblock content %}
{% block script %} {% block script %}
<script src="{% static 'common/mapbox-gl-draw.js' %}"></script> <script src="{% static 'common/mapbox-gl-draw.js' %}"></script>
<script src="{% static 'common/layer-switcher-control.js' %}"></script> <script src="{% static 'common/layer-switcher-control.js' %}"></script>
<script> <script>
const preventive_draft_id = "{{ preventive_draft_id|default:'' }}"; const preventive_draft_id = "{{ preventive_draft_id|default:'' }}";
var selected_locations_set = new Set({{ selected_locations_ids| safe }}); var selected_locations_set = new Set({{ selected_locations_ids|safe }});
const initial_selected_assets = {{ selected_assets_json| safe }}; const initial_selected_assets = {{ selected_assets_json|safe }};
var selected_assets_set = new Set(); var selected_assets_set = new Set();
const preventive_element_type = "{{ preventive_element_type|default:'' }}"; const preventive_element_type = "{{ preventive_element_type|default:'' }}";
const INITIAL_ASSET_LAYOUT = '{{ request.user.config.assets_list_layout|default:"both" }}'; const INITIAL_ASSET_LAYOUT = '{{ request.user.config.assets_list_layout|default:"both" }}';
const THEMATIC_CONFIG = {{ thematic_config_json| safe }}; const THEMATIC_CONFIG = {{ thematic_config_json|safe }};
const CONDITIONAL_FILTER_CONFIG = {{ conditional_filter_config_json| safe }}; const CONDITIONAL_FILTER_CONFIG = {{ conditional_filter_config_json|safe }};
const THEMATIC_CATEGORIES = {{ thematic_categories_json| safe }}; const THEMATIC_CATEGORIES = {{ thematic_categories_json|safe }};
const ADD_MODAL_CONFIG = {{ add_modal_config_json| safe }}; const ADD_MODAL_CONFIG = {{ add_modal_config_json|safe }};
const SELECTED_THEMATICS = {{ selected_thematics| safe }}; const SELECTED_THEMATICS = {{ selected_thematics|safe }};
const HAS_URL_THEMATICS = {% if has_url_thematics %}true{% else %} false{% endif %}; const HAS_URL_THEMATICS = {% if has_url_thematics %}true{% else %}false{% endif %};
const SELECTED_CATEGORIES = {{ selected_categories_json| safe }}; const SELECTED_CATEGORIES = {{ selected_categories_json|safe }};
const PREVENTIVE_DRAFT_ID = "{{ preventive_draft_id|default:'' }}"; const PREVENTIVE_DRAFT_ID = "{{ preventive_draft_id|default:'' }}";
const LOCKED_THEMATIC_CODE = "{{ locked_thematic_code|default:'' }}"; const LOCKED_THEMATIC_CODE = "{{ locked_thematic_code|default:'' }}";
const PREVENTIVE_ELEMENT_TYPE = "{{ preventive_element_type|default:'' }}"; const PREVENTIVE_ELEMENT_TYPE = "{{ preventive_element_type|default:'' }}";
const SELECTED_ASSETS = {{ selected_assets_json| safe }}; const SELECTED_ASSETS = {{ selected_assets_json|safe }};
const SELECTED_LOCATION_IDS = {{ selected_locations_ids| safe }}; const SELECTED_LOCATION_IDS = {{ selected_locations_ids|safe }};
const LINK_ASSETS_URL = "{{ link_assets_url|default:'' }}"; const LINK_ASSETS_URL = "{{ link_assets_url|default:'' }}";
const AUTOCOMPLETE_STREET_URL = "{% url 'common:autocomplete_street' %}"; const AUTOCOMPLETE_STREET_URL = "{% url 'common:autocomplete_street' %}";
const ASSET_MODELS_BY_CATEGORY_URL_TEMPLATE = "{% url 'assets:get_asset_models' 'THEMATIC_CODE' 0 %}"; const ASSET_MODELS_BY_CATEGORY_URL_TEMPLATE = "{% url 'assets:get_asset_models' 'THEMATIC_CODE' 0 %}";
const ASSETS_INDEX_PREFS = {{ assets_index_prefs_json| safe }}; const ASSETS_INDEX_PREFS = {{ assets_index_prefs_json|safe }};
const DEFAULT_THEMATIC_CODE = "{{ default_thematic_code|default:'' }}"; const DEFAULT_THEMATIC_CODE = "{{ default_thematic_code|default:'' }}";
const SAVE_ASSETS_PREFS_URL = "{% url 'common:save_assets_index_preferences' %}"; const SAVE_ASSETS_PREFS_URL = "{% url 'common:save_assets_index_preferences' %}";
const exportAssetsUrl = "{% url 'assets:export_assets' %}"; const exportAssetsUrl = "{% url 'assets:export_assets' %}";
const bulkEditAssetsUrl = "{% url 'assets:bulk_edit_assets' %}"; const bulkEditAssetsUrl = "{% url 'assets:bulk_edit_assets' %}";
// Static icon URLs for unified_map_layers.js (cannot use the static tag in plain JS) // Static icon URLs for unified_map_layers.js (cannot use the static tag in plain JS)
const UNIFIED_ICON_URLS = { const UNIFIED_ICON_URLS = {
trafficlights: "{% static 'assets/traffic_light_icon.svg' %}", trafficlights: "{% static 'assets/traffic_light_icon.svg' %}",
trafficlightradar: "{% static 'assets/radar_preventif_icon.svg' %}?v=20260710", trafficlightradar: "{% static 'assets/radar_preventif_icon.svg' %}?v=20260710",
trafficlightradarpole: "{% static 'assets/simple_pole_icon.svg' %}", trafficlightradarpole: "{% static 'assets/simple_pole_icon.svg' %}",
trafficlightelectricalcabinet: "{% static 'assets/electrical-panel.png' %}", trafficlightelectricalcabinet: "{% static 'assets/electrical-panel.png' %}",
publiclighting: "{% static 'assets/public_lamp_post_icon.svg' %}?v=20260521c", publiclighting: "{% static 'assets/public_lamp_post_icon.svg' %}?v=20260521c",
publiclightingelectricalcabinet: "{% static 'assets/electrical-panel.png' %}", publiclightingelectricalcabinet: "{% static 'assets/electrical-panel.png' %}",
nature: "{% static 'assets/nature_tree_icon.svg' %}?v=20260521a", nature: "{% static 'assets/nature_tree_icon.svg' %}?v=20260521a",
structures: "{% static 'assets/structures_bridge_tunnel_icon.svg' %}?v=20260522b", structures: "{% static 'assets/structures_bridge_tunnel_icon.svg' %}?v=20260522b",
roaddrain: "{% static 'assets/roaddrain_grille_icon.svg' %}?v=20260521a", roaddrain: "{% static 'assets/roaddrain_grille_icon.svg' %}?v=20260521a",
roadconcreteblockset: "{% static 'assets/road_concrete_block_icon.svg' %}?v=20260608a", roadconcreteblockset: "{% static 'assets/road_concrete_block_icon.svg' %}?v=20260608a",
naturebench: "{% static 'assets/nature_bench_icon.svg' %}", naturebench: "{% static 'assets/nature_bench_icon.svg' %}",
artworkfountain: "{% static 'assets/artwork_fountain_icon.svg' %}?v=20260521a", artworkfountain: "{% static 'assets/artwork_fountain_icon.svg' %}?v=20260521a",
artworkmonument: "{% static 'assets/artwork_statue_icon.svg' %}?v=20260521a", artworkmonument: "{% static 'assets/artwork_statue_icon.svg' %}?v=20260521a",
// publiclighting (poteaux, luminaires) uses a dedicated lamp-post SVG // publiclighting (poteaux, luminaires) uses a dedicated lamp-post SVG
}; };
</script> </script>
{% include "assets/includes/panel_rendering_functions.html" %} {% include "assets/includes/panel_rendering_functions.html" %}
<script src="{% static 'common/base_script.js' %}"></script> <script src="{% static 'common/base_script.js' %}"></script>
<script src="{% static 'common/base_map.js' %}"></script> <script src="{% static 'common/base_map.js' %}"></script>
<script src="{% static 'assets/sign_panels_shared.js' %}?v=20260703c"></script> <script src="{% static 'assets/sign_panels_shared.js' %}?v=20260703c"></script>
<script src="{% static 'assets/unified_map_layers.js' %}?v=20260726_pb"></script> <script src="{% static 'assets/unified_map_layers.js' %}?v=20260726_pb"></script>
<script src="{% static 'assets/unified_index.js' %}?v=20260726_pb"></script> <script src="{% static 'assets/unified_index.js' %}?v=20260726_pb"></script>
{% endblock script %} {% endblock script %}