diff --git a/loko/assets/static/assets/unified_index.js b/loko/assets/static/assets/unified_index.js index 4446502..21cf7b9 100644 --- a/loko/assets/static/assets/unified_index.js +++ b/loko/assets/static/assets/unified_index.js @@ -19,7 +19,15 @@ var activeCategories = new Set(SELECTED_CATEGORIES || []); var activeModels = new Set(); var modelFilterCollapsed = true; var activeConditionalFilters = {}; -var currentTypeFilter = 'all'; // 'all' | 'locations' | 'assets' +var currentTypeFilter = 'all'; // legacy fallback + +function getActiveTypeFilters() { + const types = []; + if (document.getElementById('type-cb-assets')?.checked) types.push('assets'); + if (document.getElementById('type-cb-locations')?.checked) types.push('locations'); + if (document.getElementById('type-cb-management-zones')?.checked) types.push('management_zones'); + return types; +} var searchTerms = []; var searchMode = 'and'; var shouldFitSearch = false; @@ -658,9 +666,31 @@ function applyInitialPreferences() { // Type filter if (prefs.type_filter) { - currentTypeFilter = prefs.type_filter; - const rb = document.querySelector(`input[name="unified-type-filter"][value="${prefs.type_filter}"]`); - if (rb) rb.checked = true; + if (Array.isArray(prefs.type_filter)) { + const cbA = document.getElementById('type-cb-assets'); + const cbL = document.getElementById('type-cb-locations'); + const cbZ = document.getElementById('type-cb-management-zones'); + if (cbA) cbA.checked = prefs.type_filter.includes('assets'); + if (cbL) cbL.checked = prefs.type_filter.includes('locations'); + if (cbZ) cbZ.checked = prefs.type_filter.includes('management_zones'); + } else if (typeof prefs.type_filter === 'string') { + const cbA = document.getElementById('type-cb-assets'); + const cbL = document.getElementById('type-cb-locations'); + const cbZ = document.getElementById('type-cb-management-zones'); + if (prefs.type_filter === 'all') { + if (cbA) cbA.checked = true; + if (cbL) cbL.checked = true; + if (cbZ) cbZ.checked = true; + } else if (prefs.type_filter === 'locations') { + if (cbA) cbA.checked = false; + if (cbL) cbL.checked = true; + if (cbZ) cbZ.checked = false; + } else if (prefs.type_filter === 'assets') { + if (cbA) cbA.checked = true; + if (cbL) cbL.checked = false; + if (cbZ) cbZ.checked = false; + } + } } // Include no geometry filter @@ -739,7 +769,7 @@ function savePreferences() { models: Array.from(activeModels), conditional_filters: activeConditionalFilters, visible_columns: getVisibleColumnsPreference(), - type_filter: currentTypeFilter, + type_filter: getActiveTypeFilters(), status_filters: statusFilters, validation_status_filters: validationStatusFilters, include_no_geom: includeNoGeom, @@ -784,9 +814,9 @@ $(document).ready(function () { onUncheckAll: function (rows) { rows.forEach(r => { r.state = false; selectedIds.delete(r.id); }); updateSelectionStatus(); }, onClickRow: function (row) { if (!row.lon || !row.lat) return; - const source = row.is_location ? 'locations' : 'assets'; + const source = row.is_management_zone ? 'management_zones' : (row.is_location ? 'locations' : 'assets'); if (typeof highlightMapFeature === 'function') { - highlightMapFeature(map, source, row.id, row.is_location ? locationshighlightState : assetshighlightState); + highlightMapFeature(map, source, row.id, row.is_management_zone ? managementZonesHighlightState : (row.is_location ? locationshighlightState : assetshighlightState)); } }, }); @@ -865,11 +895,13 @@ $(document).ready(function () { renderModelFilters(); }); - // ─── Type radio ──────────────────────────────────────────────────────── + // ─── Type checkboxes ──────────────────────────────────────────────────── - document.querySelectorAll('input[name="unified-type-filter"]').forEach(rb => { - rb.addEventListener('change', function () { - if (this.checked) currentTypeFilter = this.value; + document.querySelectorAll('input[name="filter-element-type"]').forEach(cb => { + cb.addEventListener('change', function () { + applyFilters(); + updateFilterBadge(); + schedulePreferencesSave(); }); }); @@ -884,26 +916,45 @@ $(document).ready(function () { } // init from locked element type - if (PREVENTIVE_ELEMENT_TYPE === 'assets') currentTypeFilter = 'assets'; - else if (PREVENTIVE_ELEMENT_TYPE === 'locations') currentTypeFilter = 'locations'; + if (PREVENTIVE_ELEMENT_TYPE === 'assets') { + const cbA = document.getElementById('type-cb-assets'); + const cbL = document.getElementById('type-cb-locations'); + const cbZ = document.getElementById('type-cb-management-zones'); + if (cbA) cbA.checked = true; + if (cbL) cbL.checked = false; + if (cbZ) cbZ.checked = false; + } else if (PREVENTIVE_ELEMENT_TYPE === 'locations') { + const cbA = document.getElementById('type-cb-assets'); + const cbL = document.getElementById('type-cb-locations'); + const cbZ = document.getElementById('type-cb-management-zones'); + if (cbA) cbA.checked = false; + if (cbL) cbL.checked = true; + if (cbZ) cbZ.checked = false; + } // ─── Add actions ───────────────────────────────────────────────────── const addConfig = getUnifiedAddModalConfig(); const assetThematics = Array.isArray(addConfig.asset_thematics) ? addConfig.asset_thematics : []; const locationThematics = Array.isArray(addConfig.location_thematics) ? addConfig.location_thematics : []; + const managementZoneThematics = Array.isArray(addConfig.management_zone_thematics) ? addConfig.management_zone_thematics : []; const addButton = document.getElementById('btn-add-asset'); const assetThematicSelect = document.getElementById('unified-add-asset-thematic'); const locationThematicSelect = document.getElementById('unified-add-location-thematic'); + const managementZoneThematicSelect = document.getElementById('unified-add-management-zone-thematic'); const chooserEmpty = document.getElementById('unified-add-chooser-empty'); fillSelectWithOptions(assetThematicSelect, assetThematics, assetThematics.length ? gettext('Sélectionner une thématique...') : gettext('Aucune thématique disponible')); fillSelectWithOptions(locationThematicSelect, locationThematics, locationThematics.length ? gettext('Sélectionner une thématique...') : gettext('Aucune thématique disponible')); + fillSelectWithOptions(managementZoneThematicSelect, managementZoneThematics, managementZoneThematics.length ? gettext('Sélectionner une thématique...') : gettext('Aucune thématique disponible')); if (assetThematicSelect) assetThematicSelect.disabled = !assetThematics.length; if (locationThematicSelect) locationThematicSelect.disabled = !locationThematics.length; - if (chooserEmpty) chooserEmpty.classList.toggle('d-none', assetThematics.length > 0 || locationThematics.length > 0); - if (addButton && !assetThematics.length && !locationThematics.length) { + if (managementZoneThematicSelect) managementZoneThematicSelect.disabled = !managementZoneThematics.length; + + const hasAnyAddRights = assetThematics.length > 0 || locationThematics.length > 0 || managementZoneThematics.length > 0; + if (chooserEmpty) chooserEmpty.classList.toggle('d-none', hasAnyAddRights); + if (addButton && !hasAnyAddRights) { addButton.disabled = true; addButton.title = gettext('Vous n\'avez pas de droits de création'); } @@ -962,6 +1013,17 @@ $(document).ready(function () { } }); + document.getElementById('unified-add-management-zone-open')?.addEventListener('click', function () { + const thematicCode = managementZoneThematicSelect ? managementZoneThematicSelect.value : ''; + if (!thematicCode) { + setModalAlert('unified-add-chooser-alert', gettext('Sélectionnez une thématique pour créer une zone de gestion.'), 'warning'); + return; + } + setModalAlert('unified-add-chooser-alert', '', 'info'); + const baseUrl = addConfig.management_zone_create_url || '/assets/management-zones/add/'; + window.location.href = `${baseUrl}?thematic=${encodeURIComponent(thematicCode)}`; + }); + document.getElementById('unifiedAddAssetCategory')?.addEventListener('change', function () { const thematicCode = document.getElementById('unified-add-asset-thematic-code')?.value || ''; loadUnifiedAssetModels(thematicCode, this.value); @@ -1175,8 +1237,12 @@ $(document).ready(function () { activeModels.clear(); modelFilterCollapsed = true; activeConditionalFilters = {}; - const allRadio = document.getElementById('type-all'); - if (allRadio) { allRadio.checked = true; currentTypeFilter = 'all'; } + const cbA = document.getElementById('type-cb-assets'); + const cbL = document.getElementById('type-cb-locations'); + const cbZ = document.getElementById('type-cb-management-zones'); + if (cbA) cbA.checked = true; + if (cbL) cbL.checked = true; + if (cbZ) cbZ.checked = true; document.querySelectorAll('[name="filter-status"]').forEach(cb => { cb.checked = false; }); document.querySelectorAll('[name="filter-validation-status"]').forEach(cb => { cb.checked = false; }); const includeNoGeomCheckbox = document.getElementById('include-no-geom'); @@ -1860,11 +1926,13 @@ function findUnifiedRowIndex(rowRef) { const rows = $table.bootstrapTable('getData') || []; const isLocation = !!rowRef.is_location; + const isManagementZone = !!rowRef.is_management_zone; const matches = (row) => { if (!!row.is_location !== isLocation) return false; + if (!!row.is_management_zone !== isManagementZone) return false; if (String(row.id) !== String(rowRef.id)) return false; - if (!isLocation) { + if (!isLocation && !isManagementZone) { if (String(row.thematic || '') !== String(rowRef.thematic || '')) return false; if (String(row.asset_type || '') !== String(rowRef.asset_type || '')) return false; } else { @@ -1878,7 +1946,7 @@ function findUnifiedRowIndex(rowRef) { if (idx !== -1) return idx; // Fallback in case some rows do not expose all discriminators. - idx = rows.findIndex(row => !!row.is_location === isLocation && String(row.id) === String(rowRef.id)); + idx = rows.findIndex(row => !!row.is_location === isLocation && !!row.is_management_zone === isManagementZone && String(row.id) === String(rowRef.id)); return idx; } @@ -1953,6 +2021,37 @@ function collectClickedAssets(clickEvent) { return Array.from(unique.values()); } +function collectClickedManagementZones(clickEvent) { + const layers = ['management-zones-fill', 'management-zones-line'].filter(layerId => map.getLayer(layerId)); + if (!layers.length) return []; + + const rendered = map.queryRenderedFeatures(clickEvent.point, { layers }); + const unique = new Map(); + + rendered.forEach(f => { + const p = f.properties || {}; + const id = f.id ?? p.id; + if (id == null) return; + const key = `zone|${p.thematic || ''}|${id}`; + if (unique.has(key)) return; + unique.set(key, { + id: id, + code: p.code || '', + name: p.name || '', + thematic: p.thematic || '', + thematic_name: f._thematic_name || p.thematic_name || p.thematic || '', + element_type: p.element_type || gettext('Zone de gestion'), + category: p.category || p.zone_type_name || '', + detail_url: p.detail_url || null, + color: p.color || '', + feature_id: id, + is_management_zone: true, + }); + }); + + return Array.from(unique.values()); +} + function collectClickedLocations(clickEvent) { const layers = ['locations', 'locations-circle', 'locations-radar', 'locations-radar-outline', 'locations-outline'].filter(layerId => map.getLayer(layerId)); if (!layers.length) return []; @@ -2016,12 +2115,29 @@ function bindAssetPopupActions(popupNode) { } }); }); + + popupNode.querySelectorAll('.zone-popup-focus-btn').forEach(btn => { + btn.addEventListener('click', function (e) { + e.preventDefault(); + const rowRef = { + id: this.dataset.zoneId, + thematic: this.dataset.thematic, + is_management_zone: true, + }; + focusRowInUnifiedTable(rowRef); + + if (typeof highlightMapFeature === 'function') { + highlightMapFeature(map, 'management_zones', rowRef.id, managementZonesHighlightState); + } + }); + }); } function showAssetClickPopup(clickEvent) { const assets = collectClickedAssets(clickEvent); const locations = collectClickedLocations(clickEvent); - if (!assets.length && !locations.length) return; + const managementZones = collectClickedManagementZones(clickEvent); + if (!assets.length && !locations.length && !managementZones.length) return; const maxItems = 8; const visibleAssets = assets.slice(0, maxItems); @@ -2113,10 +2229,56 @@ function showAssetClickPopup(clickEvent) { ` : ''; + const visibleZones = managementZones.slice(0, maxItems); + const hiddenZonesCount = Math.max(0, managementZones.length - visibleZones.length); + + const zoneItemsHtml = visibleZones.map(z => { + const code = escapeHtml(z.code || '-'); + const type = escapeHtml(z.element_type || gettext('Zone de gestion')); + const thematic = escapeHtml(z.thematic_name || z.thematic || ''); + const cat = z.category ? ` • ${escapeHtml(z.category)}` : ''; + const dotHtml = z.color ? `` : ''; + const name = escapeHtml(z.name || ''); + const detailAnchor = z.detail_url + ? `${code}` + : `${code}`; + + return ` +