diff --git a/loko/projects/locale/en/LC_MESSAGES/django.mo b/loko/projects/locale/en/LC_MESSAGES/django.mo index d4a0a6a..7b653a8 100644 Binary files a/loko/projects/locale/en/LC_MESSAGES/django.mo and b/loko/projects/locale/en/LC_MESSAGES/django.mo differ diff --git a/loko/projects/locale/en/LC_MESSAGES/django.po b/loko/projects/locale/en/LC_MESSAGES/django.po index 28b98b1..de30d2c 100644 --- a/loko/projects/locale/en/LC_MESSAGES/django.po +++ b/loko/projects/locale/en/LC_MESSAGES/django.po @@ -863,3 +863,28 @@ msgstr "Invalid date for “%(name)s”. " #: projects/views.py:1104 msgid "Valeurs sauvegardées avec succès." msgstr "Values saved successfully." + +#: projects/templates/projects/projects_index.html +msgid "Filtrer les projets" +msgstr "Filter projects" + +#: projects/templates/projects/projects_index.html +msgid "Sélectionnez les critères à afficher sur la carte et dans le tableau" +msgstr "Select the criteria to display on the map and in the table" + +#: projects/templates/projects/projects_index.html +msgid "Filtres" +msgstr "Filters" + +#: projects/templates/projects/projects_index.html +msgid "Période" +msgstr "Period" + +#: projects/templates/projects/projects_index.html +msgid "Effacer dates" +msgstr "Clear dates" + +#: projects/templates/projects/projects_index.html +msgid "Tout effacer" +msgstr "Clear all" + diff --git a/loko/projects/locale/fr/LC_MESSAGES/django.mo b/loko/projects/locale/fr/LC_MESSAGES/django.mo index df8ab05..21704c4 100644 Binary files a/loko/projects/locale/fr/LC_MESSAGES/django.mo and b/loko/projects/locale/fr/LC_MESSAGES/django.mo differ diff --git a/loko/projects/locale/fr/LC_MESSAGES/django.po b/loko/projects/locale/fr/LC_MESSAGES/django.po index e6b84e1..b08bc09 100644 --- a/loko/projects/locale/fr/LC_MESSAGES/django.po +++ b/loko/projects/locale/fr/LC_MESSAGES/django.po @@ -850,3 +850,28 @@ msgstr "Date invalide pour « %(name)s ». Format attendu : AAAA-MM-JJ." #: projects/views.py:1104 msgid "Valeurs sauvegardées avec succès." msgstr "Valeurs sauvegardées avec succès." + +#: projects/templates/projects/projects_index.html +msgid "Filtrer les projets" +msgstr "Filtrer les projets" + +#: projects/templates/projects/projects_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" + +#: projects/templates/projects/projects_index.html +msgid "Filtres" +msgstr "Filtres" + +#: projects/templates/projects/projects_index.html +msgid "Période" +msgstr "Période" + +#: projects/templates/projects/projects_index.html +msgid "Effacer dates" +msgstr "Effacer dates" + +#: projects/templates/projects/projects_index.html +msgid "Tout effacer" +msgstr "Tout effacer" + diff --git a/loko/projects/locale/nl/LC_MESSAGES/django.mo b/loko/projects/locale/nl/LC_MESSAGES/django.mo index dbfaed3..aa4764f 100644 Binary files a/loko/projects/locale/nl/LC_MESSAGES/django.mo and b/loko/projects/locale/nl/LC_MESSAGES/django.mo differ diff --git a/loko/projects/locale/nl/LC_MESSAGES/django.po b/loko/projects/locale/nl/LC_MESSAGES/django.po index 2afe035..c07f867 100644 --- a/loko/projects/locale/nl/LC_MESSAGES/django.po +++ b/loko/projects/locale/nl/LC_MESSAGES/django.po @@ -850,3 +850,28 @@ msgstr "Ongeldige datum voor “%(name)s”. Verwacht formaat: JJJJ-MM-DD." #: projects/views.py:1104 msgid "Valeurs sauvegardées avec succès." msgstr "Waarden succesvol opgeslagen." + +#: projects/templates/projects/projects_index.html +msgid "Filtrer les projets" +msgstr "Projecten filteren" + +#: projects/templates/projects/projects_index.html +msgid "Sélectionnez les critères à afficher sur la carte et dans le tableau" +msgstr "Selecteer de criteria die op de kaart en in de tabel moeten worden weergegeven" + +#: projects/templates/projects/projects_index.html +msgid "Filtres" +msgstr "Filters" + +#: projects/templates/projects/projects_index.html +msgid "Période" +msgstr "Periode" + +#: projects/templates/projects/projects_index.html +msgid "Effacer dates" +msgstr "Datums wissen" + +#: projects/templates/projects/projects_index.html +msgid "Tout effacer" +msgstr "Alles wissen" + diff --git a/loko/projects/static/projects/projects_index.js b/loko/projects/static/projects/projects_index.js index 6f62ad0..6d45346 100644 --- a/loko/projects/static/projects/projects_index.js +++ b/loko/projects/static/projects/projects_index.js @@ -79,11 +79,49 @@ projectsgeojsonloader.addEventListener("geojsonLoaded", event => { -// Apply the correct width to the dropdown-menu +// Filter badge & active filter count +function countActiveFilters() { + const urlParams = new URLSearchParams(window.location.search); + let count = 0; + ['status', 'thematic', 'tag'].forEach(param => { + const values = urlParams.getAll(param); + if (values.length > 0 && values[0] !== '') { + count++; + } + }); + if (urlParams.get('begin_date') || urlParams.get('end_date')) { + count++; + } + return count; +} + +function updateFilterBadge() { + const count = countActiveFilters(); + const badge = document.getElementById('filter-badge'); + const countSpan = document.getElementById('filter-count'); + if (badge && countSpan) { + if (count > 0) { + countSpan.textContent = count; + badge.classList.remove('d-none'); + } else { + badge.classList.add('d-none'); + } + } +} + document.addEventListener("DOMContentLoaded", function () { - const parent_col = $('#col-left')[0]; - const parentWidth = Math.max(550, parent_col.offsetWidth - 40); - $('.filter-dropdown').width(`${parentWidth}px`); + updateFilterBadge(); + + const clearDatesBtn = document.getElementById('clear-dates-btn'); + if (clearDatesBtn) { + clearDatesBtn.addEventListener('click', function(e) { + e.preventDefault(); + const beginInput = document.getElementById('begin_date'); + const endInput = document.getElementById('end_date'); + if (beginInput) beginInput.value = ''; + if (endInput) endInput.value = ''; + }); + } }); // Apply filters @@ -109,6 +147,9 @@ document.getElementById('apply-filters').addEventListener('click', function(e) { const newUrl = `${window.location.pathname}?${params.toString()}`; window.history.pushState({}, '', newUrl); + // Update filter badge + updateFilterBadge(); + // Load filtered data handleGeojsonMapLoading(init=false, projectsgeojsonloader, 'projects'); projectsgeojsonloader.loadGeojson(projectsgeojsonUrl+`?${params}`); @@ -116,11 +157,10 @@ document.getElementById('apply-filters').addEventListener('click', function(e) { // Save preferences savePreferencesFromForm(params); - // Trouver le dropdown parent et le refermer - const dropdownEl = this.closest('.dropdown'); - const dropdown = bootstrap.Dropdown.getInstance(dropdownEl.querySelector('[data-bs-toggle="dropdown"]')); - if (dropdown) { - dropdown.hide(); // ✅ ferme proprement le dropdown + // Fermer la modale + const filterModal = bootstrap.Modal.getInstance(document.getElementById('filterModal')); + if (filterModal) { + filterModal.hide(); } }); @@ -131,9 +171,7 @@ document.getElementById('reset-filters').addEventListener('click', function(e) { form.reset(); // Reset form fields // Uncheck all filter checkboxes - form.querySelectorAll('input[type="checkbox"][name="status"]').forEach(cb => { cb.checked = false; }); - form.querySelectorAll('input[type="checkbox"][name="thematic"]').forEach(cb => { cb.checked = false; }); - form.querySelectorAll('input[type="checkbox"][name="tag"]').forEach(cb => { cb.checked = false; }); + form.querySelectorAll('input[type="checkbox"]').forEach(cb => { cb.checked = false; }); // Explicitly clear date fields document.getElementById('begin_date').value = ''; @@ -142,6 +180,9 @@ document.getElementById('reset-filters').addEventListener('click', function(e) { // Remove URL parameters window.history.pushState({}, '', window.location.pathname); + // Update filter badge + updateFilterBadge(); + // Reload all data without filters handleGeojsonMapLoading(init=false, projectsgeojsonloader, 'projects'); projectsgeojsonloader.loadGeojson(projectsgeojsonUrl); @@ -155,11 +196,10 @@ document.getElementById('reset-filters').addEventListener('click', function(e) { }).catch(() => {}); } - // Trouver le dropdown parent et le refermer - const dropdownEl = this.closest('.dropdown'); - const dropdown = bootstrap.Dropdown.getInstance(dropdownEl.querySelector('[data-bs-toggle="dropdown"]')); - if (dropdown) { - dropdown.hide(); // ✅ ferme proprement le dropdown + // Fermer la modale + const filterModal = bootstrap.Modal.getInstance(document.getElementById('filterModal')); + if (filterModal) { + filterModal.hide(); } }); diff --git a/loko/projects/templates/projects/projects_index.html b/loko/projects/templates/projects/projects_index.html index a05cf2d..e250989 100644 --- a/loko/projects/templates/projects/projects_index.html +++ b/loko/projects/templates/projects/projects_index.html @@ -17,19 +17,132 @@ margin-top: 2px; } - /* Styles pour afficher les options de filtre sous forme de badges checkables */ + /* ─── Modern Airbnb-style Filter Modal ──────────────────── */ + #filterModal .modal-dialog { + 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: 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 { + padding: 1.5rem; + background-color: #f8fafc; + flex: 1; + overflow-y: auto; + } + + #filterModal .modal-footer { + padding: 1rem 1.5rem; + background-color: #ffffff; + border-top: 1px solid #f1f5f9; + flex-shrink: 0; + display: flex; + justify-content: space-between; + align-items: center; + } + + /* Filter Section Cards */ + .filter-section { + background-color: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 14px; + padding: 1.15rem; + margin-bottom: 1rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02); + transition: border-color 0.15s ease, box-shadow 0.15s ease; + } + + .filter-section:hover { + border-color: #cbd5e1; + } + + .filter-section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.85rem; + gap: 1rem; + } + + .filter-section-header label, + .filter-section-header .section-title { + font-size: 0.95rem; + font-weight: 700; + color: #0f172a; + margin: 0; + letter-spacing: -0.01em; + } + + .filter-section-buttons { + display: flex; + gap: 0.35rem; + margin-left: auto; + } + + .filter-section-buttons button { + padding: 0.25rem 0.65rem; + font-size: 0.75rem; + font-weight: 500; + border-radius: 6px; + border: 1px solid #e2e8f0; + background-color: #ffffff; + color: #64748b; + transition: all 0.15s ease; + } + + .filter-section-buttons button:hover { + background-color: #f1f5f9; + color: #0f172a; + border-color: #cbd5e1; + } + + /* Filter Options Badges & Chips */ .filter-options { display: flex; flex-wrap: wrap; - gap: 0.5rem; - padding: 0.5rem; - background-color: #f8f9fa; - border-radius: 0.375rem; - border: 1px solid #dee2e6; + gap: 0.45rem; + background-color: transparent !important; + border: none !important; + padding: 0 !important; + min-height: auto !important; } .filter-option { - display: flex; + display: inline-flex; align-items: center; } @@ -38,55 +151,204 @@ opacity: 0; width: 0; height: 0; - cursor: pointer; + pointer-events: none; } - .filter-option label.badge-label { + .filter-option label { display: inline-flex; align-items: center; - justify-content: center; - margin: 0; + gap: 0.35rem; + padding: 0.4rem 0.85rem; + font-size: 0.82rem; 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.6rem; - font-size: 0.75rem; - border: 1px solid #b8d4f1; - transition: all 0.2s ease; - text-align: center; + transition: all 0.15s ease; + user-select: none; white-space: nowrap; } - /* Default badges - checked state */ - .filter-option input[type="checkbox"]:checked + label.badge-label { - background-color: #0c5cc9; - color: white; - border-color: #0c5cc9; + .filter-option label:hover { + background-color: #f8fafc; + border-color: #94a3b8; + color: #0f172a; } - /* Default badges - hover */ - .filter-option input[type="checkbox"]:hover + label.badge-label { - background-color: #cfe2f3; + /* Default badges (Thematics, Tags) - checked state */ + .filter-option input[type="checkbox"]:checked + label:not(.context-badge) { + 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 color badges - inactive state (outline style) */ + .filter-option label.context-badge { + border-color: currentColor; + color: inherit; + background-color: transparent; + } + + .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: #b45309; } + .filter-option label.btn-outline-success { border-color: #198754; color: #198754; } + .filter-option label.btn-outline-info { border-color: #0dcaf0; color: #087990; } + .filter-option label.btn-outline-dark { border-color: #343a40; color: #343a40; } + .filter-option label.btn-outline-secondary { border-color: #6c757d; color: #6c757d; } + + /* Contextual status badges - checked state (filled solid) */ + .filter-option input[type="checkbox"]:checked + label.context-badge { + color: #ffffff !important; + font-weight: 600; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12); + } + + .filter-option input[type="checkbox"]:checked + label.btn-outline-primary { background-color: #0c5cc9 !important; border-color: #0c5cc9 !important; } + .filter-option input[type="checkbox"]:checked + label.btn-outline-danger { background-color: #dc3545 !important; border-color: #dc3545 !important; } + .filter-option input[type="checkbox"]:checked + label.btn-outline-warning { background-color: #ffc107 !important; border-color: #ffc107 !important; color: #212529 !important; } + .filter-option input[type="checkbox"]:checked + label.btn-outline-success { background-color: #198754 !important; border-color: #198754 !important; } + .filter-option input[type="checkbox"]:checked + label.btn-outline-info { background-color: #0dcaf0 !important; border-color: #0dcaf0 !important; color: #212529 !important; } + .filter-option input[type="checkbox"]:checked + label.btn-outline-dark { background-color: #343a40 !important; border-color: #343a40 !important; } + .filter-option input[type="checkbox"]:checked + label.btn-outline-secondary { background-color: #6c757d !important; border-color: #6c757d !important; } + + /* Filter trigger badge */ + #filter-badge { + background-color: #0f172a !important; + color: #ffffff !important; + font-size: 0.72rem; + font-weight: 700; } {% endblock head %} -#interventions-table-container { - position: relative; - } - - #filter-dropdown-wrapper { - position: absolute; - top: 0.5rem; - left: 0.5rem; - z-index: 10; - margin-top: 2px; - } - - {% block content %} + +
+