refactor(controls): replace bootstrap-table with custom table and column selector
This commit is contained in:
parent
a6378eee90
commit
fc8cdb579f
10 changed files with 1757 additions and 490 deletions
Binary file not shown.
|
|
@ -1414,3 +1414,64 @@ msgstr "Error generating the PDF"
|
|||
|
||||
#~ msgid "Mise à jour"
|
||||
#~ msgstr "Last update"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Sélectionnez les critères à afficher sur la carte et dans le tableau"
|
||||
msgstr "Select criteria to display on the map and in the table"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Colonnes"
|
||||
msgstr "Columns"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Choisir les colonnes à afficher"
|
||||
msgstr "Choose columns to display"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Toutes"
|
||||
msgstr "All"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Défaut"
|
||||
msgstr "Default"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Afficher par"
|
||||
msgstr "Show by"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Filtres actifs"
|
||||
msgstr "Active filters"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Supprimer ce critère"
|
||||
msgstr "Remove this criterion"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Tout effacer"
|
||||
msgstr "Clear all"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Aucun contrôle trouvé"
|
||||
msgstr "No controls found"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Aucun contrôle ne correspond à vos critères de recherche actuels."
|
||||
msgstr "No controls match your current search criteria."
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Réinitialiser les filtres"
|
||||
msgstr "Reset filters"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Affichage de <strong>%(start)s</strong> à <strong>%(end)s</strong> sur <strong>%(total)s</strong> contrôle(s)"
|
||||
msgstr "Showing <strong>%(start)s</strong> to <strong>%(end)s</strong> of <strong>%(total)s</strong> control(s)"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Pagination des contrôles"
|
||||
msgstr "Controls pagination"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Excel"
|
||||
msgstr "Excel"
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1419,3 +1419,64 @@ msgstr "Erreur lors de la génération du PDF"
|
|||
|
||||
#~ msgid "Mise à jour"
|
||||
#~ msgstr "Mise à jour"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Sélectionnez les critères à afficher sur la carte et dans le tableau"
|
||||
msgstr "Sélectionnez les critères à afficher sur la carte et dans le tableau"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Colonnes"
|
||||
msgstr "Colonnes"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Choisir les colonnes à afficher"
|
||||
msgstr "Choisir les colonnes à afficher"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Toutes"
|
||||
msgstr "Toutes"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Défaut"
|
||||
msgstr "Défaut"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Afficher par"
|
||||
msgstr "Afficher par"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Filtres actifs"
|
||||
msgstr "Filtres actifs"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Supprimer ce critère"
|
||||
msgstr "Supprimer ce critère"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Tout effacer"
|
||||
msgstr "Tout effacer"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Aucun contrôle trouvé"
|
||||
msgstr "Aucun contrôle trouvé"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Aucun contrôle ne correspond à vos critères de recherche actuels."
|
||||
msgstr "Aucun contrôle ne correspond à vos critères de recherche actuels."
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Réinitialiser les filtres"
|
||||
msgstr "Réinitialiser les filtres"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Affichage de <strong>%(start)s</strong> à <strong>%(end)s</strong> sur <strong>%(total)s</strong> contrôle(s)"
|
||||
msgstr "Affichage de <strong>%(start)s</strong> à <strong>%(end)s</strong> sur <strong>%(total)s</strong> contrôle(s)"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Pagination des contrôles"
|
||||
msgstr "Pagination des contrôles"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Excel"
|
||||
msgstr "Excel"
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1417,3 +1417,64 @@ msgstr "Fout bij het genereren van de PDF"
|
|||
|
||||
#~ msgid "Mise à jour"
|
||||
#~ msgstr "Laatste update"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Sélectionnez les critères à afficher sur la carte et dans le tableau"
|
||||
msgstr "Selecteer de criteria om weer te geven op de kaart en in de tabel"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Colonnes"
|
||||
msgstr "Kolommen"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Choisir les colonnes à afficher"
|
||||
msgstr "Kies de weer te geven kolommen"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Toutes"
|
||||
msgstr "Alle"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Défaut"
|
||||
msgstr "Standaard"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Afficher par"
|
||||
msgstr "Weergeven per"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Filtres actifs"
|
||||
msgstr "Actieve filters"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Supprimer ce critère"
|
||||
msgstr "Dit criterium verwijderen"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Tout effacer"
|
||||
msgstr "Alles wissen"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Aucun contrôle trouvé"
|
||||
msgstr "Geen controles gevonden"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Aucun contrôle ne correspond à vos critères de recherche actuels."
|
||||
msgstr "Geen controles komen overeen met uw huidige zoekcriteria."
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Réinitialiser les filtres"
|
||||
msgstr "Filters resetten"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Affichage de <strong>%(start)s</strong> à <strong>%(end)s</strong> sur <strong>%(total)s</strong> contrôle(s)"
|
||||
msgstr "Weergave van <strong>%(start)s</strong> tot <strong>%(end)s</strong> van <strong>%(total)s</strong> controle(s)"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Pagination des contrôles"
|
||||
msgstr "Paginering van controles"
|
||||
|
||||
#: controls/templates/controls/controls_index.html
|
||||
msgid "Excel"
|
||||
msgstr "Excel"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap');
|
||||
|
||||
/* ==========================================================================
|
||||
Controls Index CSS
|
||||
========================================================================== */
|
||||
|
||||
/* Supprime le contour bleu Bootstrap au focus sur l'input de recherche */
|
||||
/* Typography & Global Layout */
|
||||
.controls-layout {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
|
||||
/* Supprime le contour bleu Bootstrap au focus sur les inputs */
|
||||
#controls-search-input:focus,
|
||||
#project-autocomplete-input:focus {
|
||||
outline: none;
|
||||
|
|
@ -28,86 +35,220 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Styles pour la modale de filtre */
|
||||
/* ─── Airbnb-style Filter Modal ────────────────────────────────────── */
|
||||
#filterModal .modal-dialog {
|
||||
max-width: 95%;
|
||||
max-width: 820px;
|
||||
margin: 1.5rem auto;
|
||||
}
|
||||
|
||||
#filterModal .modal-content {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 90vh;
|
||||
max-height: 88vh;
|
||||
overflow: hidden;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#filterModal .modal-header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#filterModal .modal-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
#filterModal .btn-close {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.5rem;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
#filterModal .btn-close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#filterModal .modal-body {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#filterModal .modal-footer {
|
||||
flex-shrink: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Filter Sections */
|
||||
.filter-section {
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.filter-section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.filter-section-header label {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.filter-section-buttons {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.filter-section-buttons .btn {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e2e8f0;
|
||||
background-color: #ffffff;
|
||||
color: #64748b;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.filter-section-buttons .btn:hover {
|
||||
background-color: #f1f5f9;
|
||||
color: #0f172a;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
/* Filter Options (Pills) */
|
||||
.filter-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-option {
|
||||
display: inline-flex;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.filter-option input[type="checkbox"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Default badge styling */
|
||||
.filter-option label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
gap: 0.35rem;
|
||||
padding: 0.35rem 0.85rem;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
background-color: #ffffff;
|
||||
border: 1.5px solid #e2e8f0;
|
||||
border-radius: 9999px;
|
||||
cursor: pointer;
|
||||
background-color: #e7f1ff;
|
||||
color: #0c5cc9;
|
||||
border-radius: 1rem;
|
||||
padding: 0.4rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
border: 1px solid #b8d4f1;
|
||||
transition: all 0.2s ease;
|
||||
width: 140px;
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
transition: all 0.15s ease;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Contextual color badges - inactive state */
|
||||
.filter-option label.context-badge {
|
||||
border-color: currentColor;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
.filter-option label:hover {
|
||||
background-color: #f8fafc;
|
||||
border-color: #94a3b8;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.filter-option label.btn-outline-primary { border-color: #0c5cc9; color: #0c5cc9; }
|
||||
.filter-option label.btn-outline-danger { border-color: #dc3545; color: #dc3545; }
|
||||
.filter-option label.btn-outline-warning { border-color: #ffc107; color: #c69500; }
|
||||
.filter-option label.btn-outline-success { border-color: #28a745; color: #28a745; }
|
||||
.filter-option label.btn-outline-info { border-color: #17a2b8; color: #17a2b8; }
|
||||
.filter-option label.btn-outline-dark { border-color: #343a40; color: #343a40; }
|
||||
.filter-option label.btn-outline-secondary { border-color: #6c757d; color: #6c757d; }
|
||||
|
||||
/* Default badges - checked state */
|
||||
/* Standard Checked State */
|
||||
.filter-option input[type="checkbox"]:checked + label:not(.context-badge) {
|
||||
background-color: #0c5cc9; color: white; border-color: #0c5cc9;
|
||||
background-color: #0f172a !important;
|
||||
border-color: #0f172a !important;
|
||||
color: #ffffff !important;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 2px 6px rgba(15, 23, 42, 0.16);
|
||||
}
|
||||
|
||||
/* Contextual badges - inactive state (outline) */
|
||||
.filter-option label.context-badge {
|
||||
background-color: #ffffff;
|
||||
border-width: 1.5px;
|
||||
border-style: solid;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.filter-option label.btn-outline-primary,
|
||||
.filter-option label.status-primary { border-color: #0c5cc9; color: #0c5cc9; }
|
||||
.filter-option label.btn-outline-danger,
|
||||
.filter-option label.status-danger { border-color: #dc3545; color: #dc3545; }
|
||||
.filter-option label.btn-outline-warning,
|
||||
.filter-option label.status-warning { border-color: #f59e0b; color: #d97706; }
|
||||
.filter-option label.btn-outline-success,
|
||||
.filter-option label.status-success { border-color: #198754; color: #198754; }
|
||||
.filter-option label.btn-outline-info,
|
||||
.filter-option label.status-info { border-color: #0284c7; color: #0284c7; }
|
||||
.filter-option label.btn-outline-dark,
|
||||
.filter-option label.status-dark { border-color: #334155; color: #334155; }
|
||||
.filter-option label.btn-outline-secondary,
|
||||
.filter-option label.status-secondary { border-color: #64748b; color: #64748b; }
|
||||
|
||||
/* Contextual badges - hover state */
|
||||
.filter-option input[type="checkbox"]:not(:checked):hover + label.btn-outline-primary { background-color: #eff6ff; }
|
||||
.filter-option input[type="checkbox"]:not(:checked):hover + label.btn-outline-danger { background-color: #fef2f2; }
|
||||
.filter-option input[type="checkbox"]:not(:checked):hover + label.btn-outline-warning { background-color: #fffbeb; }
|
||||
.filter-option input[type="checkbox"]:not(:checked):hover + label.btn-outline-success { background-color: #f0fdf4; }
|
||||
.filter-option input[type="checkbox"]:not(:checked):hover + label.btn-outline-info { background-color: #f0f9ff; }
|
||||
.filter-option input[type="checkbox"]:not(:checked):hover + label.btn-outline-dark { background-color: #f8fafc; }
|
||||
.filter-option input[type="checkbox"]:not(:checked):hover + label.btn-outline-secondary { background-color: #f8fafc; }
|
||||
|
||||
/* Contextual badges - checked state */
|
||||
.filter-option input[type="checkbox"]:checked + label.context-badge { color: white; }
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-primary { background-color: #0c5cc9; border-color: #0c5cc9; }
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-danger { background-color: #dc3545; border-color: #dc3545; }
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-warning { background-color: #ffc107; border-color: #ffc107; color: #212529 !important; }
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-success { background-color: #28a745; border-color: #28a745; }
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-info { background-color: #17a2b8; border-color: #17a2b8; }
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-dark { background-color: #343a40; border-color: #343a40; }
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-secondary { background-color: #6c757d; border-color: #6c757d; }
|
||||
|
||||
/* Default badges - hover */
|
||||
.filter-option input[type="checkbox"]:hover + label:not(.context-badge) { background-color: #cfe2f3; }
|
||||
.filter-option input[type="checkbox"]:checked + label.context-badge {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-primary {
|
||||
background-color: #0c5cc9 !important; border-color: #0c5cc9 !important; box-shadow: 0 2px 6px rgba(12, 92, 201, 0.2);
|
||||
}
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-danger {
|
||||
background-color: #dc3545 !important; border-color: #dc3545 !important; box-shadow: 0 2px 6px rgba(220, 53, 69, 0.2);
|
||||
}
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-warning {
|
||||
background-color: #f59e0b !important; border-color: #f59e0b !important; box-shadow: 0 2px 6px rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-success {
|
||||
background-color: #198754 !important; border-color: #198754 !important; box-shadow: 0 2px 6px rgba(25, 135, 84, 0.2);
|
||||
}
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-info {
|
||||
background-color: #0284c7 !important; border-color: #0284c7 !important; box-shadow: 0 2px 6px rgba(2, 132, 199, 0.2);
|
||||
}
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-dark {
|
||||
background-color: #1e293b !important; border-color: #1e293b !important; box-shadow: 0 2px 6px rgba(30, 41, 59, 0.2);
|
||||
}
|
||||
.filter-option input[type="checkbox"]:checked + label.btn-outline-secondary {
|
||||
background-color: #64748b !important; border-color: #64748b !important; box-shadow: 0 2px 6px rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
/* Subcategories */
|
||||
.filter-option.subcategory-option label {
|
||||
font-size: 0.875em;
|
||||
color: #555;
|
||||
font-size: 0.76rem;
|
||||
color: #64748b;
|
||||
}
|
||||
.filter-option-break {
|
||||
flex-basis: 100%;
|
||||
|
|
@ -118,74 +259,34 @@
|
|||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.filter-section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.75rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
.filter-section-header label {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.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 {
|
||||
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"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
cursor: pointer;
|
||||
.filter-subcat-controls button {
|
||||
font-size: 0.72rem;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #e2e8f0;
|
||||
background: #fff;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* Project Autocomplete & Chips */
|
||||
.project-chip {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
border-radius: 1rem !important;
|
||||
border-radius: 9999px !important;
|
||||
background-color: #ffffff !important;
|
||||
border: 1px solid #cbd5e1 !important;
|
||||
color: #1e293b !important;
|
||||
transition: background-color 0.15s ease;
|
||||
border-color: #ced4da !important;
|
||||
}
|
||||
|
||||
.project-chip:hover {
|
||||
background-color: #f1f3f5 !important;
|
||||
background-color: #f1f5f9 !important;
|
||||
}
|
||||
|
||||
.project-chip .remove-project-btn {
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.project-chip .remove-project-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
@ -200,43 +301,417 @@
|
|||
|
||||
#project-autocomplete-dropdown .list-group-item:hover,
|
||||
#project-autocomplete-dropdown .list-group-item.active-item {
|
||||
background-color: #e9ecef;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
#project-autocomplete-dropdown .list-group-item.selected-project {
|
||||
background-color: #e7f1ff;
|
||||
background-color: #eff6ff;
|
||||
color: #0c5cc9;
|
||||
}
|
||||
|
||||
/* Limites de largeur pour les colonnes Code et Thématiques */
|
||||
#controls-table th[data-field="control_code"],
|
||||
#controls-table td.col-code {
|
||||
/* ─── Header Tabs & New Button ─────────────────────────────────────── */
|
||||
.controls-tabs {
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.controls-tabs .nav-link {
|
||||
font-size: 0.92rem;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -2px;
|
||||
padding: 0.5rem 1rem;
|
||||
background: transparent;
|
||||
transition: all 0.15s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.controls-tabs .nav-link:hover {
|
||||
color: #0f172a;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
.controls-tabs .nav-link.active {
|
||||
color: #0f172a;
|
||||
border-bottom-color: #0f172a;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tab-counter-badge {
|
||||
font-size: 0.74rem;
|
||||
font-weight: 600;
|
||||
padding: 2px 7px;
|
||||
border-radius: 9999px;
|
||||
background-color: #f1f5f9;
|
||||
color: #475569;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.controls-tabs .nav-link.active .tab-counter-badge {
|
||||
background-color: #0f172a;
|
||||
color: #ffffff;
|
||||
border-color: #0f172a;
|
||||
}
|
||||
|
||||
.btn-new-control {
|
||||
background-color: #0f172a;
|
||||
color: #ffffff;
|
||||
border: 1px solid #0f172a;
|
||||
border-radius: 8px;
|
||||
padding: 0.42rem 0.85rem;
|
||||
font-size: 0.84rem;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
transition: all 0.15s ease;
|
||||
text-decoration: none;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.btn-new-control:hover {
|
||||
background-color: #1e293b;
|
||||
color: #ffffff;
|
||||
border-color: #1e293b;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 6px -1px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
/* ─── Active Filters Bar ───────────────────────────────────────────── */
|
||||
.active-filters-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0 0.15rem;
|
||||
}
|
||||
|
||||
.active-filter-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: #94a3b8;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.active-filter-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.2rem 0.6rem;
|
||||
font-size: 0.76rem;
|
||||
font-weight: 500;
|
||||
border-radius: 9999px;
|
||||
background-color: #f1f5f9;
|
||||
border: 1px solid #e2e8f0;
|
||||
color: #334155;
|
||||
transition: all 0.12s ease;
|
||||
}
|
||||
|
||||
.active-filter-chip .remove-filter-btn {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin-left: 2px;
|
||||
color: #94a3b8;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.active-filter-chip .remove-filter-btn:hover {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.btn-clear-all-filters {
|
||||
font-size: 0.76rem;
|
||||
color: #dc2626;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.12s ease;
|
||||
}
|
||||
|
||||
.btn-clear-all-filters:hover {
|
||||
background-color: #fef2f2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
/* ─── Table Toolbar & Column Chooser ───────────────────────────────── */
|
||||
.controls-toolbar {
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.filter-trigger-btn {
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 8px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 500;
|
||||
padding: 0.32rem 0.75rem;
|
||||
background-color: #ffffff;
|
||||
color: #334155;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.filter-trigger-btn:hover {
|
||||
background-color: #f8fafc;
|
||||
border-color: #94a3b8;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
#filter-badge {
|
||||
background-color: #0f172a !important;
|
||||
color: #ffffff !important;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||
top: -5px !important;
|
||||
right: -7px !important;
|
||||
left: auto !important;
|
||||
transform: none !important;
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
padding: 0 4px !important;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
#filter-badge:not(.d-none) {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#columns-dropdown-btn {
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 8px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 500;
|
||||
padding: 0.32rem 0.75rem;
|
||||
background-color: #ffffff;
|
||||
color: #334155;
|
||||
}
|
||||
#columns-dropdown-btn:hover {
|
||||
background-color: #f8fafc;
|
||||
border-color: #94a3b8;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.columns-dropdown-menu {
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.12), 0 8px 10px -6px rgba(0, 0, 0, 0.08);
|
||||
min-width: 250px;
|
||||
z-index: 1060;
|
||||
}
|
||||
|
||||
.columns-checkbox-list {
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.columns-checkbox-list .form-check {
|
||||
margin-bottom: 0.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.columns-checkbox-list .form-check-input {
|
||||
cursor: pointer;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.columns-checkbox-list .form-check-label {
|
||||
cursor: pointer;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.page-size-selector {
|
||||
font-size: 0.82rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.page-size-select {
|
||||
padding: 0.25rem 0.55rem;
|
||||
font-size: 0.82rem;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 7px;
|
||||
background-color: #ffffff;
|
||||
color: #334155;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
.page-size-select:focus {
|
||||
outline: none;
|
||||
border-color: #2563eb;
|
||||
}
|
||||
|
||||
/* ─── Custom Controls Table Card ───────────────────────────────────── */
|
||||
.controls-table-card {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#controls-table-scrollable {
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* Scrollbar discrète */
|
||||
#controls-table-scrollable::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
#controls-table-scrollable::-webkit-scrollbar-track {
|
||||
background: #f8fafc;
|
||||
}
|
||||
#controls-table-scrollable::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
#controls-table-scrollable::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
.controls-table {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* Table Headers (Sticky) */
|
||||
.controls-table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 5;
|
||||
background-color: #f8fafc !important;
|
||||
color: #475569 !important;
|
||||
font-size: 0.74rem !important;
|
||||
font-weight: 600 !important;
|
||||
text-transform: uppercase !important;
|
||||
letter-spacing: 0.04em !important;
|
||||
border-bottom: 1.5px solid #e2e8f0 !important;
|
||||
padding: 0.55rem 0.75rem !important;
|
||||
vertical-align: middle !important;
|
||||
white-space: nowrap !important;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.controls-table thead th.sortable {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.12s ease;
|
||||
}
|
||||
|
||||
.controls-table thead th.sortable:hover {
|
||||
background-color: #f1f5f9 !important;
|
||||
color: #0f172a !important;
|
||||
}
|
||||
|
||||
.sort-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Table Rows & Cells */
|
||||
.controls-table tbody td {
|
||||
padding: 0.45rem 0.75rem !important;
|
||||
vertical-align: middle !important;
|
||||
font-size: 0.82rem !important;
|
||||
color: #334155;
|
||||
border-bottom: 1px solid #f1f5f9 !important;
|
||||
line-height: 1.35 !important;
|
||||
}
|
||||
|
||||
.controls-table tbody tr {
|
||||
transition: background-color 0.12s ease;
|
||||
}
|
||||
|
||||
.controls-table tbody tr:hover {
|
||||
background-color: #f8fafc !important;
|
||||
}
|
||||
|
||||
.controls-table tbody tr.selected,
|
||||
.controls-table tbody tr.rowhighlight {
|
||||
background-color: #eff6ff !important;
|
||||
}
|
||||
|
||||
/* Code Link Badge in Table */
|
||||
.table-code-link {
|
||||
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
|
||||
font-size: 0.79rem !important;
|
||||
font-weight: 600 !important;
|
||||
color: #0f172a !important;
|
||||
text-decoration: none !important;
|
||||
white-space: nowrap !important;
|
||||
display: inline-block;
|
||||
padding: 2px 7px;
|
||||
border-radius: 5px;
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.table-code-link:hover {
|
||||
color: #2563eb !important;
|
||||
border-color: #93c5fd;
|
||||
background-color: #eff6ff;
|
||||
}
|
||||
|
||||
/* Action button / Zoom button */
|
||||
.zoom-btn,
|
||||
.table-action-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
color: #64748b;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.15s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
.zoom-btn:hover,
|
||||
.table-action-btn:hover {
|
||||
color: #0f172a;
|
||||
background-color: #f1f5f9;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
/* Limites de largeur pour colonnes */
|
||||
.controls-table th.col-control_code,
|
||||
.controls-table td.col-control_code {
|
||||
max-width: 140px;
|
||||
width: 140px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#controls-table td.col-code a {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#controls-table th[data-field="thematic_names"],
|
||||
#controls-table td.col-thematics {
|
||||
.controls-table th.col-thematic_names,
|
||||
.controls-table td.col-thematic_names {
|
||||
max-width: 180px;
|
||||
width: 180px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#controls-table td.col-thematics .thematic-content {
|
||||
.controls-table td.col-thematics .thematic-content {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
|
|
@ -245,29 +720,60 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Layout helpers */
|
||||
.controls-layout #controls-table-container {
|
||||
position: relative;
|
||||
min-height: 420px;
|
||||
/* Empty State */
|
||||
.table-empty-state {
|
||||
padding: 2.5rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#filter-dropdown-wrapper {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
left: 0.5rem;
|
||||
z-index: 10;
|
||||
/* ─── Custom Pagination Bar ────────────────────────────────────────── */
|
||||
.controls-pagination-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.65rem 1rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
background-color: #ffffff;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-dropdown {
|
||||
min-width: min(640px, calc(100vw - 4rem));
|
||||
.controls-pagination-bar .pagination-info {
|
||||
font-size: 0.82rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.controls-pagination-bar .pagination .page-item .page-link {
|
||||
border-radius: 6px;
|
||||
margin: 0 2px;
|
||||
border-color: #e2e8f0;
|
||||
color: #334155;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.25rem 0.55rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.controls-pagination-bar .pagination .page-item.active .page-link {
|
||||
background-color: #0f172a;
|
||||
border-color: #0f172a;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.controls-pagination-bar .pagination .page-item.disabled .page-link {
|
||||
color: #cbd5e1;
|
||||
background-color: transparent;
|
||||
border-color: #f1f5f9;
|
||||
}
|
||||
|
||||
/* ─── Map Container ────────────────────────────────────────────────── */
|
||||
.controls-map {
|
||||
height: calc(100vh - 210px);
|
||||
height: calc(100vh - 190px);
|
||||
min-height: 360px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-top: 50px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
margin-top: 44px;
|
||||
}
|
||||
|
||||
.controls-map-filter {
|
||||
|
|
@ -276,6 +782,9 @@
|
|||
right: 2.75rem;
|
||||
z-index: 5;
|
||||
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.15);
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.popup-scrollable {
|
||||
|
|
@ -289,16 +798,8 @@
|
|||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.controls-layout #filter-dropdown-wrapper {
|
||||
position: static;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.controls-layout #controls-table-container {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.filter-dropdown {
|
||||
width: 100%;
|
||||
.controls-layout .controls-toolbar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,24 +8,27 @@
|
|||
|
||||
{% block content %}
|
||||
|
||||
<!-- Modale de filtre des contrôles -->
|
||||
<!-- ─── Modale de Filtres des contrôles ────────────────────────── -->
|
||||
<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-dialog-centered modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h5 class="modal-title" id="filterModalLabel">{% translate "Filtrer les contrôles" %}</h5>
|
||||
<div class="text-muted small" style="font-size: 0.8rem;">{% translate "Sélectionnez les critères à afficher sur la carte et dans le tableau" %}</div>
|
||||
</div>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{% translate 'Fermer' %}"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="get" id="filter-form" class="mb-4">
|
||||
<form method="get" id="filter-form" class="mb-0">
|
||||
|
||||
<!-- Thématique -->
|
||||
<div class="col-12 filter-section">
|
||||
<div class="filter-section">
|
||||
<div class="filter-section-header">
|
||||
<label>{% translate "Thématique" %}</label>
|
||||
<div class="filter-section-buttons">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary select-all-btn" data-field="thematic">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary deselect-all-btn" data-field="thematic">{% translate "Aucun" %}</button>
|
||||
<button type="button" class="btn select-all-btn" data-field="thematic">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn deselect-all-btn" data-field="thematic">{% translate "Aucun" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-options" data-field="thematic">
|
||||
|
|
@ -41,12 +44,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Catégorie -->
|
||||
<div class="col-12 filter-section">
|
||||
<div class="filter-section">
|
||||
<div class="filter-section-header">
|
||||
<label>{% translate "Catégorie" %}</label>
|
||||
<div class="filter-section-buttons">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary select-all-btn" data-field="category">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary deselect-all-btn" data-field="category">{% translate "Aucun" %}</button>
|
||||
<button type="button" class="btn select-all-btn" data-field="category">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn deselect-all-btn" data-field="category">{% translate "Aucun" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-options" data-field="category">
|
||||
|
|
@ -61,12 +64,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Type de contrôle -->
|
||||
<div class="col-12 filter-section">
|
||||
<div class="filter-section">
|
||||
<div class="filter-section-header">
|
||||
<label>{% translate "Type de contrôle" %}</label>
|
||||
<div class="filter-section-buttons">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary select-all-btn" data-field="type">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary deselect-all-btn" data-field="type">{% translate "Aucun" %}</button>
|
||||
<button type="button" class="btn select-all-btn" data-field="type">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn deselect-all-btn" data-field="type">{% translate "Aucun" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-options" data-field="type">
|
||||
|
|
@ -82,12 +85,12 @@
|
|||
</div>
|
||||
|
||||
<!-- Statut -->
|
||||
<div class="col-12 filter-section">
|
||||
<div class="filter-section">
|
||||
<div class="filter-section-header">
|
||||
<label>{% translate "Statut" %}</label>
|
||||
<div class="filter-section-buttons">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary select-all-btn" data-field="status">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary deselect-all-btn" data-field="status">{% translate "Aucun" %}</button>
|
||||
<button type="button" class="btn select-all-btn" data-field="status">{% translate "Tous" %}</button>
|
||||
<button type="button" class="btn deselect-all-btn" data-field="status">{% translate "Aucun" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-options" data-field="status">
|
||||
|
|
@ -101,11 +104,11 @@
|
|||
</div>
|
||||
|
||||
<!-- Projets avec Autocomplete & Ajout -->
|
||||
<div class="col-12 filter-section">
|
||||
<div class="filter-section">
|
||||
<div class="filter-section-header">
|
||||
<label>{% translate "Projets" %}</label>
|
||||
<div class="filter-section-buttons">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="clear-all-projects-btn">{% translate "Effacer tout" %}</button>
|
||||
<button type="button" class="btn select-all-btn" id="clear-all-projects-btn">{% translate "Effacer tout" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="position-relative mb-2">
|
||||
|
|
@ -133,19 +136,23 @@
|
|||
</div>
|
||||
|
||||
<!-- Période et mes contrôles -->
|
||||
<div class="row mt-3">
|
||||
<div class="filter-section mb-0">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">{% translate "Période (date de création)" %}</label>
|
||||
<label class="form-label fw-bold mb-2 text-dark" style="font-size: 0.9rem;">{% translate "Période (date de création)" %}</label>
|
||||
<div class="d-flex gap-2 align-items-center">
|
||||
<input type="date" name="date_from" id="date_from" class="form-control" value="{{ date_from }}" title="{% translate 'Du' %}">
|
||||
<input type="date" name="date_from" id="date_from" class="form-control form-control-sm" value="{{ date_from }}" title="{% translate 'Du' %}">
|
||||
<span class="text-muted small">–</span>
|
||||
<input type="date" name="date_to" id="date_to" class="form-control" value="{{ date_to }}" title="{% translate 'Au' %}">
|
||||
<input type="date" name="date_to" id="date_to" class="form-control form-control-sm" value="{{ date_to }}" title="{% translate 'Au' %}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 d-flex align-items-center">
|
||||
<div class="form-check mt-3 mt-md-4">
|
||||
<div class="col-md-6">
|
||||
<div class="pt-md-4">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="mine" value="1" id="mine" {% if show_mine_only %}checked{% endif %}>
|
||||
<label class="form-check-label" for="mine">{% translate "Mes contrôles uniquement" %}</label>
|
||||
<label class="form-check-label small fw-medium" for="mine">{% translate "Mes contrôles uniquement" %}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -153,16 +160,23 @@
|
|||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="reset-filters" class="btn btn-secondary">{% translate "Réinitialiser" %}</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="reset-filters" class="btn btn-link text-muted p-0 text-decoration-none d-flex align-items-center gap-1" style="font-size: 0.85rem; font-weight: 500;">
|
||||
<i class="bi bi-arrow-counterclockwise"></i> {% translate "Réinitialiser" %}
|
||||
</button>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary px-3 py-2" data-bs-dismiss="modal" style="border-radius: 8px; font-weight: 500;">{% translate "Annuler" %}</button>
|
||||
<button type="button" id="apply-filters" class="btn btn-sm btn-dark px-4 py-2" style="background-color: #0f172a; border-color: #0f172a; border-radius: 8px; font-weight: 600;">
|
||||
<i class="bi bi-check2 me-1"></i>{% translate "Filtrer" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ─── Contenu Principal ──────────────────────────────────────── -->
|
||||
<div class="position-relative">
|
||||
<!-- Layout selection buttons -->
|
||||
<!-- Boutons de sélection de mise en page (Tableau / Partagée / Carte) -->
|
||||
<div class="position-absolute top-0 end-0 mt-2" style="z-index: 10;">
|
||||
<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" title="{% translate 'Vue tableau uniquement' %}">
|
||||
|
|
@ -177,7 +191,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Widget de recherche — centré sur toute la largeur (tableau + carte) -->
|
||||
<!-- Widget de recherche — centré au-dessus de la vue partagée -->
|
||||
<div id="controls-search-widget"
|
||||
style="position:absolute; top:4px; left:50%; transform:translateX(-50%); z-index:10; pointer-events:none; max-width:calc(100% - 310px);">
|
||||
<div class="input-group" style="pointer-events:auto; flex-wrap:nowrap;">
|
||||
|
|
@ -209,71 +223,230 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Disposition Tableau (gauche) & Carte (droite) -->
|
||||
<div class="row mt-2 controls-layout" style="height: calc(100vh - 130px);">
|
||||
<div class="col-md-6" id="col-left" style="max-height: calc(100vh - 130px);">
|
||||
<ul class="nav nav-tabs" id="tablesTab" role="tablist">
|
||||
|
||||
<!-- ─── Colonne Gauche : Tableau des contrôles ──────────── -->
|
||||
<div class="col-md-6" id="col-left" style="max-height: calc(100vh - 130px); display: flex; flex-direction: column;">
|
||||
|
||||
<!-- Entête d'onglet et bouton d'action -->
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-2">
|
||||
<ul class="nav nav-tabs controls-tabs" id="tablesTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="controls-table-tab" data-bs-toggle="tab" data-bs-target="#controls-table-content" type="button" role="tab" aria-controls="controls-table" aria-selected="true">{% translate "Contrôles" %}</button>
|
||||
<button class="nav-link active" id="controls-table-tab" data-bs-toggle="tab" data-bs-target="#controls-table-content" type="button" role="tab" aria-controls="controls-table-content" aria-selected="true">
|
||||
<i class="bi bi-clipboard-check me-1"></i>
|
||||
{% translate "Contrôles" %}
|
||||
<span class="tab-counter-badge" id="controls-counter-badge">0</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% if is_add_control_allowed %}
|
||||
<div class="my-1 ms-2">
|
||||
<a href="{% url 'controls:controls_add' %}" class="btn btn-light" title="{% translate 'Nouveau contrôle' %}">
|
||||
<i class="bi bi-plus-circle"></i>
|
||||
<a href="{% url 'controls:controls_add' %}" class="btn-new-control" title="{% translate 'Nouveau contrôle' %}">
|
||||
<i class="bi bi-plus-circle me-1"></i>
|
||||
<span>{% translate "Nouveau contrôle" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Puces des filtres actifs -->
|
||||
{% if active_filters_list %}
|
||||
<div class="active-filters-bar" id="active-filters-bar">
|
||||
<span class="active-filter-label">{% translate "Filtres actifs" %}:</span>
|
||||
{% for filter in active_filters_list %}
|
||||
<span class="active-filter-chip">
|
||||
{% if filter.is_status %}
|
||||
<span class="badge rounded-pill status-badge {{ filter.status_badge }}" style="padding: 2px 6px; font-size: 0.72rem;">{{ filter.label }}</span>
|
||||
{% else %}
|
||||
{{ filter.label }}
|
||||
{% endif %}
|
||||
<button type="button" class="remove-filter-btn" data-param="{{ filter.param }}" data-val="{{ filter.val }}" title="{% translate 'Supprimer ce critère' %}">
|
||||
<i class="bi bi-x-circle-fill"></i>
|
||||
</button>
|
||||
</span>
|
||||
{% endfor %}
|
||||
<a href="{% url 'controls:controls_index' %}" class="btn-clear-all-filters">
|
||||
<i class="bi bi-x me-1"></i>{% translate "Tout effacer" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<div class="tab-content my-2" id="tablesTabContent">
|
||||
<div class="tab-pane fade show active" id="controls-table-content" role="tabpanel" aria-labelledby="controls-table-tab">
|
||||
<div class="d-flex justify-content-between flex-wrap align-items-center gap-2 mt-3">
|
||||
<div class="text-muted small" id="controls-result-count">
|
||||
{% translate "Aucun contrôle chargé" %}
|
||||
<!-- Barre d'outils du tableau : Filtre, Colonnes, Export, Affichage par page -->
|
||||
<div class="controls-toolbar d-flex justify-content-between align-items-center flex-wrap gap-2 mb-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<!-- Bouton Filtre avec badge -->
|
||||
<button type="button" class="btn btn-light position-relative filter-trigger-btn" data-bs-toggle="modal" data-bs-target="#filterModal">
|
||||
<i class="bi bi-funnel"></i> {% translate "Filtre" %}
|
||||
<span id="filter-badge" class="position-absolute badge rounded-pill bg-primary {% if not active_filters_count %}d-none{% endif %}">
|
||||
<span id="filter-count">{{ active_filters_count|default:0 }}</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Dropdown Sélecteur de Colonnes -->
|
||||
<div class="dropdown d-inline-block">
|
||||
<button type="button" class="btn btn-light dropdown-toggle" id="columns-dropdown-btn" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" title="{% translate 'Choisir les colonnes à afficher' %}">
|
||||
<i class="bi bi-layout-three-columns me-1"></i>{% translate "Colonnes" %}
|
||||
</button>
|
||||
<div class="dropdown-menu shadow-sm py-2 px-3 columns-dropdown-menu" aria-labelledby="columns-dropdown-btn">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 pb-1 border-bottom">
|
||||
<span class="fw-semibold text-dark small">{% translate "Colonnes" %}</span>
|
||||
<div class="d-flex gap-1">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none text-primary small" id="cols-select-all">{% translate "Toutes" %}</button>
|
||||
<span class="text-muted small">·</span>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none text-muted small" id="cols-reset-default">{% translate "Défaut" %}</button>
|
||||
</div>
|
||||
<span id="filter-limit-alert" class="alert alert-info py-1 px-2 mb-0 d-none">
|
||||
</div>
|
||||
<div id="columns-list-container" class="columns-checkbox-list">
|
||||
<!-- Injecté dynamiquement par JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bouton Export Excel -->
|
||||
<a id="export-excel-btn" href="{% url 'controls:controls_export_excel' %}" class="btn btn-light" title="{% translate 'Exporter en Excel' %}">
|
||||
<i class="bi bi-file-earmark-spreadsheet me-1"></i><span class="d-none d-sm-inline">{% translate "Excel" %}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span id="filter-limit-alert" class="alert alert-info py-1 px-2 mb-0 d-none small">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
{% translate "Contenu limité à 5 000 éléments." %}
|
||||
</span>
|
||||
|
||||
<div class="page-size-selector d-flex align-items-center gap-1">
|
||||
<label for="page-size-select" class="small text-muted mb-0 d-none d-sm-inline">{% translate "Afficher par" %}</label>
|
||||
<select id="page-size-select" class="form-select form-select-sm page-size-select">
|
||||
<option value="25" {% if page_size == '25' %}selected{% endif %}>25</option>
|
||||
<option value="50" {% if page_size == '50' or not page_size %}selected{% endif %}>50</option>
|
||||
<option value="100" {% if page_size == '100' %}selected{% endif %}>100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Spinner de chargement -->
|
||||
<div id="controls-spinner" class="table-spinner py-5 text-center">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">{% translate "Chargement..." %}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="controls-table-container" class="d-none">
|
||||
<div id="filter-dropdown-wrapper" class="d-flex align-items-center gap-2">
|
||||
<button type="button" class="btn btn-light" data-bs-toggle="modal" data-bs-target="#filterModal">
|
||||
<i class="bi bi-funnel me-1"></i>{% translate "Filtre" %}
|
||||
</button>
|
||||
<a id="export-excel-btn" href="{% url 'controls:controls_export_excel' %}" class="btn btn-light" title="{% translate 'Exporter en Excel' %}">
|
||||
<i class="bi bi-file-earmark-spreadsheet"></i>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Carte du Tableau Custom (remplace bootstrap-table) -->
|
||||
<div id="controls-table-container" class="d-none flex-grow-1 d-flex flex-column" style="min-height: 0;">
|
||||
<div class="controls-table-card flex-grow-1 d-flex flex-column" style="min-height: 0;">
|
||||
|
||||
<table id="controls-table" class="table"
|
||||
data-height="550"
|
||||
data-pagination="true"
|
||||
data-page-size="50"
|
||||
data-show-columns="true"
|
||||
data-unique-id="id"
|
||||
data-sort-name="updated_at"
|
||||
data-sort-order="desc">
|
||||
<div class="table-responsive flex-grow-1" id="controls-table-scrollable">
|
||||
<table id="controls-table" class="controls-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-actions" style="width: 48px; text-align: center;"></th>
|
||||
<th class="sortable col-control_code" data-field="control_code">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Code" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-thematic_names" data-field="thematic_names">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Thématiques" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-category_names" data-field="category_names">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Catégories" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-control_type_label" data-field="control_type_label">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Type" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-status_label" data-field="status_label">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Statut" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-project_name" data-field="project_name">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Projet" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-location" data-field="location">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Localisation" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-author" data-field="author">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Auteur" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-remarks_count text-center" data-field="remarks_count">
|
||||
<div class="sort-header justify-content-center">
|
||||
<span>{% translate "Remarques" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-created_at" data-field="created_at">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Création" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-updated_at" data-field="updated_at">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Mise à jour" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sortable col-description" data-field="description">
|
||||
<div class="sort-header">
|
||||
<span>{% translate "Description" %}</span>
|
||||
<i class="bi bi-arrow-down-up sort-icon"></i>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="controls-table-tbody">
|
||||
<!-- Lignes injectées dynamiquement par JS -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Barre de pagination personnalisée -->
|
||||
<div class="controls-pagination-bar" id="controls-pagination-bar">
|
||||
<div class="pagination-info" id="controls-pagination-info">
|
||||
<!-- e.g. Affichage de 1 à 50 sur 120 contrôle(s) -->
|
||||
</div>
|
||||
<nav aria-label="{% translate 'Pagination des contrôles' %}">
|
||||
<ul class="pagination pagination-sm mb-0" id="controls-pagination-list">
|
||||
<!-- Boutons injectés par JS -->
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ─── Colonne Droite : Carte ─────────────────────────── -->
|
||||
<div class="col-md-6" id="col-right">
|
||||
<div id="map" class="rounded-2 controls-map">
|
||||
<button id="filterzone" class="btn btn-light btn-sm controls-map-filter" type="button">
|
||||
{% translate "Filtrer sur cette zone" %}
|
||||
<i class="bi bi-bounding-box-circles me-1"></i>{% translate "Filtrer sur cette zone" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -469,6 +469,85 @@ def controls_index(request):
|
|||
valid_selected_project_ids = [int(x) for x in selected_projects if str(x).isdigit()]
|
||||
selected_projects_objs = list(viewable_projects.filter(id__in=valid_selected_project_ids)) if valid_selected_project_ids else []
|
||||
|
||||
active_filters_list = []
|
||||
if search_query:
|
||||
active_filters_list.append({
|
||||
'type': 'search',
|
||||
'label': f'"{search_query}"',
|
||||
'param': 'q',
|
||||
'val': search_query,
|
||||
})
|
||||
if selected_thematics:
|
||||
thematic_dict = {t.code: t.name for t in thematics}
|
||||
for th in selected_thematics:
|
||||
active_filters_list.append({
|
||||
'type': 'thematic',
|
||||
'label': thematic_dict.get(th, th),
|
||||
'param': 'thematic',
|
||||
'val': th,
|
||||
})
|
||||
if selected_categories:
|
||||
cat_dict = {str(c.id): c.get_name() for c in categories}
|
||||
for cid in selected_categories:
|
||||
active_filters_list.append({
|
||||
'type': 'category',
|
||||
'label': cat_dict.get(str(cid), str(cid)),
|
||||
'param': 'category',
|
||||
'val': cid,
|
||||
})
|
||||
if selected_types:
|
||||
for t in selected_types:
|
||||
active_filters_list.append({
|
||||
'type': 'type',
|
||||
'label': str(CONTROL_TYPE_CHOICES.get(t, t)),
|
||||
'param': 'type',
|
||||
'val': t,
|
||||
})
|
||||
if selected_statuses:
|
||||
for s in selected_statuses:
|
||||
active_filters_list.append({
|
||||
'type': 'status',
|
||||
'label': str(CONTROL_STATUS_CHOICES.get(s, s)),
|
||||
'param': 'status',
|
||||
'val': s,
|
||||
'status_badge': _CONTROL_STATUS_BADGE_MAP.get(s, 'secondary'),
|
||||
'is_status': True,
|
||||
})
|
||||
if selected_projects_objs:
|
||||
for p in selected_projects_objs:
|
||||
active_filters_list.append({
|
||||
'type': 'project',
|
||||
'label': p.name,
|
||||
'param': 'project',
|
||||
'val': str(p.id),
|
||||
})
|
||||
if date_from:
|
||||
active_filters_list.append({
|
||||
'type': 'date_from',
|
||||
'label': f"≥ {date_from}",
|
||||
'param': 'date_from',
|
||||
'val': date_from,
|
||||
})
|
||||
if date_to:
|
||||
active_filters_list.append({
|
||||
'type': 'date_to',
|
||||
'label': f"≤ {date_to}",
|
||||
'param': 'date_to',
|
||||
'val': date_to,
|
||||
})
|
||||
if show_mine_only:
|
||||
active_filters_list.append({
|
||||
'type': 'mine',
|
||||
'label': _("Mes contrôles"),
|
||||
'param': 'mine',
|
||||
'val': '1',
|
||||
})
|
||||
active_filters_count = len(active_filters_list)
|
||||
|
||||
page_size_val = request.GET.get('page_size', '50')
|
||||
if page_size_val not in ('25', '50', '100'):
|
||||
page_size_val = '50'
|
||||
|
||||
context = {
|
||||
'thematic_choices': [(t.code, t.name, t.icon) for t in thematics],
|
||||
'selected_thematics': selected_thematics,
|
||||
|
|
@ -490,6 +569,9 @@ def controls_index(request):
|
|||
'date_to': date_to,
|
||||
'saved_sort': json.dumps(saved_sort, ensure_ascii=False),
|
||||
'is_add_control_allowed': user_can_add_control(request.user),
|
||||
'active_filters_list': active_filters_list,
|
||||
'active_filters_count': active_filters_count,
|
||||
'page_size': page_size_val,
|
||||
}
|
||||
|
||||
return render(request, 'controls/controls_index.html', context)
|
||||
|
|
|
|||
Loading…
Reference in a new issue