diff --git a/loko/assets/static/assets/asset_symbology.js b/loko/assets/static/assets/asset_symbology.js index 600dae8..134cc90 100644 --- a/loko/assets/static/assets/asset_symbology.js +++ b/loko/assets/static/assets/asset_symbology.js @@ -1055,6 +1055,89 @@ }; } + /** + * MapLibre layer definition for point asset selection / highlight halo (trees, poles, furniture, etc.). + */ + function getAssetsPointSelectionLayer(sourceId = 'assets') { + const isNatureExpr = [ + 'any', + ['==', ['get', 'asset_type'], 'naturetree'], + ['==', ['get', 'asset_type'], 'natureshrub'], + ['==', ['get', 'thematic'], 'nature'] + ]; + return { + id: 'assets-point-selection', + type: 'circle', + source: sourceId, + filter: ['any', + ['==', ['geometry-type'], 'Point'], + ['==', ['geometry-type'], 'MultiPoint'] + ], + paint: { + 'circle-radius': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + [ + 'case', + isNatureExpr, + [ + 'interpolate', ['linear'], ['zoom'], + 8, 4, + 11, 6, + 13, 8, + 15, 11, + 17, 14, + 19, 17, + 22, 21 + ], + [ + 'interpolate', ['linear'], ['zoom'], + 8, 8, + 11, 12, + 13, 15, + 15, 18, + 17, 22, + 19, 26, + 22, 32 + ] + ], + 0 + ], + 'circle-color': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + '#fef08a', + 'transparent' + ], + 'circle-opacity': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 0.85, + 0 + ], + 'circle-stroke-color': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + '#ca8a04', + 'transparent' + ], + 'circle-stroke-width': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 2.5, + 0 + ], + 'circle-stroke-opacity': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 0.95, + 0 + ], + }, + minzoom: 8, + }; + } + // ─── 6. Public Export ─────────────────────────────────────────────────────── global.AssetSymbology = { THEMATIC_CONFIG, @@ -1072,6 +1155,7 @@ getAssetsFillLayer, getAssetsPolygonOutlineLayer, getAssetsLineLayer, + getAssetsPointSelectionLayer, }; // Ensure UNIFIED_ICON_URLS is globally available if not already defined diff --git a/loko/assets/static/assets/unified_index.js b/loko/assets/static/assets/unified_index.js index cf42478..25eb6fe 100644 --- a/loko/assets/static/assets/unified_index.js +++ b/loko/assets/static/assets/unified_index.js @@ -916,11 +916,13 @@ $(document).ready(function () { row.state = true; selectedIds.add(row.id); updateSelectionStatus(); + setMapFeatureSelectionState(row, true); }, onUncheck: function (row) { row.state = false; selectedIds.delete(row.id); updateSelectionStatus(); + setMapFeatureSelectionState(row, false); }, onCheckAll: function (rows) { selectAllTableRows(); @@ -930,9 +932,14 @@ $(document).ready(function () { }, onClickRow: function (row) { if (!row.lon || !row.lat) return; - const source = row.is_management_zone ? 'management_zones' : (row.is_location ? 'locations' : 'assets'); - if (typeof highlightMapFeature === 'function') { - highlightMapFeature(map, source, row.id, row.is_management_zone ? managementZonesHighlightState : (row.is_location ? locationshighlightState : assetshighlightState)); + // With clickToSelect: true, clicking a row toggles its state (checking or unchecking). + // If the row was already selected, clicking it will deselect it (onUncheck will run). + // Do not highlight a row that is about to be deselected. + if (!selectedIds.has(row.id)) { + const source = row.is_management_zone ? 'management_zones' : (row.is_location ? 'locations' : 'assets'); + if (typeof highlightMapFeature === 'function') { + highlightMapFeature(map, source, row.id, row.is_management_zone ? managementZonesHighlightState : (row.is_location ? locationshighlightState : assetshighlightState)); + } } }, }); @@ -1883,8 +1890,10 @@ function bindUnifiedBulkActions() { .then(data => { if (data.success) { bootstrap.Modal.getOrCreateInstance(bulkStatusModalEl).hide(); + selectedIds.forEach(id => setMapFeatureSelectionState(id, false)); selectedIds.clear(); updateSelectionStatus(); + resetAllHighlightStates(); refreshData(); } else { if (bulkStatusError) { @@ -1943,8 +1952,10 @@ function bindUnifiedBulkActions() { .then(data => { if (data.success) { bootstrap.Modal.getOrCreateInstance(bulkValModalEl).hide(); + selectedIds.forEach(id => setMapFeatureSelectionState(id, false)); selectedIds.clear(); updateSelectionStatus(); + resetAllHighlightStates(); refreshData(); } else { if (bulkValError) { @@ -2105,6 +2116,7 @@ function focusRowInUnifiedTable(rowRef) { function collectClickedAssets(clickEvent) { const layers = [ 'assets', + 'assets-point-selection', 'assets-fill', 'assets-polygon-outline', 'assets-line', @@ -2872,6 +2884,54 @@ var locationshighlightState = { previousId: null }; var assetshighlightState = { previousId: null }; var managementZonesHighlightState = { previousId: null }; +function resetAllHighlightStates(specificId = null) { + [assetshighlightState, locationshighlightState, managementZonesHighlightState].forEach(st => { + if (st) { + if (specificId == null || st.previousId === specificId || String(st.previousId) === String(specificId)) { + st.previousId = null; + } + } + }); +} + +function setMapFeatureSelectionState(rowOrId, isSelected) { + if (typeof map === 'undefined' || !map) return; + const isObj = rowOrId && typeof rowOrId === 'object'; + const id = isObj ? rowOrId.id : rowOrId; + if (id == null) return; + + let targetSource = null; + if (isObj) { + targetSource = rowOrId.is_management_zone ? 'management_zones' : (rowOrId.is_location ? 'locations' : 'assets'); + } + + const sources = targetSource ? [targetSource] : ['assets', 'locations', 'management_zones']; + + sources.forEach(src => { + try { + if (map.getSource(src)) { + if (isSelected) { + map.setFeatureState({ source: src, id: id }, { selected: true }); + } else { + map.setFeatureState({ source: src, id: id }, { selected: false, highlighted: false }); + } + } + } catch (e) {} + }); + + if (!isSelected) { + resetAllHighlightStates(id); + } +} + +function syncMapSelectionFeatureStates() { + if (typeof map === 'undefined' || !map) return; + if (selectedIds.size === 0) return; + selectedIds.forEach(id => { + setMapFeatureSelectionState(id, true); + }); +} + function isPanelTextSpriteId(missingId) { if (!missingId) return false; @@ -2965,6 +3025,8 @@ function rebuildMapSources() { locSrc.setData(mergedLocationsGeojson); asstSrc.setData(mergedAssetsGeojson); if (zoneSrc) zoneSrc.setData(mergedManagementZonesGeojson); + + syncMapSelectionFeatureStates(); } @@ -3129,10 +3191,10 @@ maploader.addEventListener('mapLoaded', () => { }); // Click interaction on map elements: show popup when assets are found at click position. - ['assets', 'assets-fill', 'assets-line', 'assets-point', 'sign-panels-circles', 'sign-panels-symbols', 'sign-poles-orientation', 'sign-poles', 'sign-poles-vertical', 'locations', 'locations-circle', 'locations-radar', 'locations-radar-outline', 'locations-outline', 'management-zones-fill', 'management-zones-line'].forEach(layerId => { + ['assets', 'assets-point-selection', 'assets-fill', 'assets-line', 'assets-point', 'sign-panels-circles', 'sign-panels-symbols', 'sign-poles-orientation', 'sign-poles', 'sign-poles-vertical', 'locations', 'locations-circle', 'locations-radar', 'locations-radar-outline', 'locations-outline', 'management-zones-fill', 'management-zones-line'].forEach(layerId => { if (!map.getLayer(layerId)) return; map.on('click', layerId, showAssetClickPopup); - if (['assets', 'assets-fill', 'assets-line', 'assets-point', 'sign-panels-circles', 'sign-panels-symbols', 'sign-poles-orientation', 'sign-poles', 'sign-poles-vertical', 'management-zones-fill'].includes(layerId)) { + if (['assets', 'assets-point-selection', 'assets-fill', 'assets-line', 'assets-point', 'sign-panels-circles', 'sign-panels-symbols', 'sign-poles-orientation', 'sign-poles', 'sign-poles-vertical', 'management-zones-fill'].includes(layerId)) { map.on('mouseenter', layerId, function () { map.getCanvas().style.cursor = 'pointer'; }); map.on('mouseleave', layerId, function () { map.getCanvas().style.cursor = ''; }); } @@ -3142,7 +3204,7 @@ maploader.addEventListener('mapLoaded', () => { map.on('click', function (e) { if (!assetClickPopup || !assetClickPopup.isOpen()) return; - const interactiveLayers = ['assets', 'assets-fill', 'assets-line', 'assets-point', 'sign-panels-circles', 'sign-panels-symbols', 'sign-poles-orientation', 'sign-poles', 'sign-poles-vertical', 'locations', 'locations-circle', 'locations-radar', 'locations-radar-outline', 'locations-outline', 'management-zones-fill', 'management-zones-line'] + const interactiveLayers = ['assets', 'assets-point-selection', 'assets-fill', 'assets-line', 'assets-point', 'sign-panels-circles', 'sign-panels-symbols', 'sign-poles-orientation', 'sign-poles', 'sign-poles-vertical', 'locations', 'locations-circle', 'locations-radar', 'locations-radar-outline', 'locations-outline', 'management-zones-fill', 'management-zones-line'] .filter(layerId => map.getLayer(layerId)); if (!interactiveLayers.length) { assetClickPopup.remove(); @@ -3234,6 +3296,7 @@ function selectAllTableRows() { allRows.forEach(r => { selectedIds.add(r.id); r.state = true; + setMapFeatureSelectionState(r, true); }); $('#unified-table tbody tr').addClass('selected'); $('#unified-table input[name="btSelectItem"]').prop('checked', true); @@ -3250,11 +3313,16 @@ function deselectAllTableRows() { const allRows = getAllTableRows(); allRows.forEach(r => { r.state = false; - selectedIds.delete(r.id); + setMapFeatureSelectionState(r, false); }); + selectedIds.forEach(id => { + setMapFeatureSelectionState(id, false); + }); + selectedIds.clear(); $('#unified-table tbody tr').removeClass('selected'); $('#unified-table input[name="btSelectItem"]').prop('checked', false); updateSelectionStatus(); + resetAllHighlightStates(); } finally { isBulkSelectionInProgress = false; } diff --git a/loko/assets/static/assets/unified_map_layers.js b/loko/assets/static/assets/unified_map_layers.js index 612d778..6b51f1c 100644 --- a/loko/assets/static/assets/unified_map_layers.js +++ b/loko/assets/static/assets/unified_map_layers.js @@ -429,8 +429,16 @@ const _unified_management_zones_fill = { ['==', ['geometry-type'], 'MultiPolygon'] ], paint: { - 'fill-color': ['coalesce', ['get', 'color'], '#10b981'], - 'fill-opacity': 0.25, + 'fill-color': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#fef08a', + ['coalesce', ['get', 'color'], '#10b981'] + ], + 'fill-opacity': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 0.8, + 0.25 + ], 'fill-antialias': false, }, }; @@ -441,8 +449,16 @@ const _unified_management_zones_line = { type: 'line', source: 'management_zones', paint: { - 'line-color': ['coalesce', ['get', 'color'], '#10b981'], - 'line-width': 2, + 'line-color': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', + ['coalesce', ['get', 'color'], '#10b981'] + ], + 'line-width': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 4, + 2 + ], 'line-dasharray': [3, 2], }, }; @@ -502,7 +518,7 @@ const _unified_locations_outline = { paint: { 'line-color': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3b', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', ['any', ['==', ['get', 'thematic'], 'publicbuildings'], ['==', ['get', 'thematic'], 'buildings']], [ 'match', @@ -521,7 +537,7 @@ const _unified_locations_outline = { ], 'line-width': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], 3, + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 4, ['any', ['==', ['get', 'thematic'], 'publicbuildings'], ['==', ['get', 'thematic'], 'buildings']], 2, 1 ], @@ -549,6 +565,7 @@ const _unified_locations_circle = { // Colour by building usage_type or intersection status 'circle-color': [ 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', ['any', ['==', ['get', 'thematic'], 'publicbuildings'], ['==', ['get', 'thematic'], 'buildings']], [ 'match', @@ -576,10 +593,14 @@ const _unified_locations_circle = { ], 'circle-stroke-color': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3b', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', '#ffffff' ], - 'circle-stroke-width': 1.5, + 'circle-stroke-width': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 3, + 1.5 + ], 'circle-opacity': 0.85, }, minzoom: 7, @@ -604,7 +625,7 @@ const _unified_locations_radar_outline = { paint: { 'icon-color': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3b', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', '#ffffff' ] }, @@ -628,14 +649,18 @@ const _unified_locations_radar = { }, paint: { 'icon-color': [ - 'match', ['get', 'status'], - 'active', '#0094ff', - 'in_preparation', '#f59f00', - 'under_repair', '#f76707', - 'voluntarily_out_of_service', '#e03131', - 'inactive', '#e03131', - 'archived', '#6c757d', - '#0094ff' + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3bbf', + [ + 'match', ['get', 'status'], + 'active', '#0094ff', + 'in_preparation', '#f59f00', + 'under_repair', '#f76707', + 'voluntarily_out_of_service', '#e03131', + 'inactive', '#e03131', + 'archived', '#6c757d', + '#0094ff' + ] ] }, minzoom: 7, @@ -957,7 +982,7 @@ const _unified_assets_symbol = { ['==', ['get', 'asset_type'], 'naturegreensurface'], 0, ['==', ['get', 'asset_type'], 'itsbollardsystem'], 0, ['match', ['geometry-type'], ['LineString', 'MultiLineString'], true, false], 0, - ['boolean', ['feature-state', 'highlighted'], false], 1, + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 1, 0.85 ], 'text-color': '#333333', @@ -988,13 +1013,18 @@ const _unified_sign_poles_circle = { 'circle-opacity': 0.8, 'circle-stroke-color': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3b', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', '#333333' ], 'circle-stroke-width': [ - 'interpolate', ['linear'], ['zoom'], - 8, 0, - 13, 1 * UNIFIED_SIGN_SYMBOL_SCALE + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 3 * UNIFIED_SIGN_SYMBOL_SCALE, + [ + 'interpolate', ['linear'], ['zoom'], + 8, 0, + 13, 1 * UNIFIED_SIGN_SYMBOL_SCALE + ] ] }, minzoom: 8, @@ -1014,8 +1044,17 @@ const _unified_sign_panels_circles = { 17, 10 * UNIFIED_SIGN_SYMBOL_SCALE ], 'circle-color': '#0022ff', - 'circle-stroke-color': '#0011aa', - 'circle-stroke-width': 1 * UNIFIED_SIGN_SYMBOL_SCALE, + 'circle-stroke-color': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', + '#0011aa' + ], + 'circle-stroke-width': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 2.5 * UNIFIED_SIGN_SYMBOL_SCALE, + 1 * UNIFIED_SIGN_SYMBOL_SCALE + ], 'circle-opacity': 0.8 }, maxzoom: 17, @@ -1147,6 +1186,81 @@ const _unified_assets_line = (typeof AssetSymbology !== 'undefined' && AssetSymb minzoom: 8, }; +// ─── Layer 4a — point assets selection halo (circle behind point assets) ────── +const _unified_assets_point_selection = (typeof AssetSymbology !== 'undefined' && AssetSymbology.getAssetsPointSelectionLayer) + ? AssetSymbology.getAssetsPointSelectionLayer('assets') + : { + id: 'assets-point-selection', + type: 'circle', + source: 'assets', + filter: ['any', + ['==', ['geometry-type'], 'Point'], + ['==', ['geometry-type'], 'MultiPoint'] + ], + paint: { + 'circle-radius': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + [ + 'case', + ['any', ['==', ['get', 'asset_type'], 'naturetree'], ['==', ['get', 'asset_type'], 'natureshrub'], ['==', ['get', 'thematic'], 'nature']], + [ + 'interpolate', ['linear'], ['zoom'], + 8, 4, + 11, 6, + 13, 8, + 15, 11, + 17, 14, + 19, 17, + 22, 21 + ], + [ + 'interpolate', ['linear'], ['zoom'], + 8, 8, + 11, 12, + 13, 15, + 15, 18, + 17, 22, + 19, 26, + 22, 32 + ] + ], + 0 + ], + 'circle-color': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + '#fef08a', + 'transparent' + ], + 'circle-opacity': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 0.85, + 0 + ], + 'circle-stroke-color': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + '#ca8a04', + 'transparent' + ], + 'circle-stroke-width': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 2.5, + 0 + ], + 'circle-stroke-opacity': [ + 'case', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], + 0.95, + 0 + ], + }, + minzoom: 8, + }; + // ─── Layer 4b — road assets circles (point roadway/sidewalk assets) ────────── const _unified_assets_point = { id: 'assets-point', @@ -1171,17 +1285,17 @@ const _unified_assets_point = { ], 'circle-stroke-color': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3b', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', '#ffffff' ], 'circle-stroke-width': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], 2, + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 3, 0 ], 'circle-opacity': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], 1.0, + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 1.0, 0.85 ], }, @@ -1202,14 +1316,14 @@ const _unified_assets_polygon_outline = (typeof AssetSymbology !== 'undefined' & paint: { 'line-color': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3b', + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], '#ffeb3b', ['==', ['get', 'thematic'], 'parking'], (typeof AssetSymbology !== 'undefined' ? AssetSymbology.getParkingColorExpr() : '#0d6efd'), THEMATIC_COLOR_EXPR ], 'line-width': [ 'case', - ['boolean', ['feature-state', 'highlighted'], false], 4, + ['any', ['boolean', ['feature-state', 'highlighted'], false], ['boolean', ['feature-state', 'selected'], false]], 4, 2 ], 'line-opacity': 0.9, @@ -1221,21 +1335,22 @@ const _unified_assets_polygon_outline = (typeof AssetSymbology !== 'undefined' & // MapLibre renders layers in insertion order, so the array below defines the // visual stacking: fills at the bottom, then lines, then circles/symbols on top. const UNIFIED_LAYERS = [ - _unified_territory_mask, // 0. territory mask (outside commune) - _unified_territory_outline, // 0b. territory boundary line - _unified_locations_fill, // 1. polygon fills (bottom) + _unified_territory_mask, // 0. territory mask (outside commune) + _unified_territory_outline, // 0b. territory boundary line + _unified_locations_fill, // 1. polygon fills (bottom) _unified_management_zones_fill, // 1b. management zones fill - _unified_assets_fill, // 2. polygon fills - _unified_locations_outline, // 3. lines + _unified_assets_fill, // 2. polygon fills + _unified_locations_outline, // 3. lines _unified_management_zones_line, // 3b. management zones line - _unified_assets_line, // 3c. asset lines - _unified_assets_polygon_outline, // 3d. polygon asset outlines - _unified_locations_circle, // 4. circles + _unified_assets_line, // 3c. asset lines + _unified_assets_polygon_outline,// 3d. polygon asset outlines + _unified_locations_circle, // 4. circles _unified_locations_radar_outline, _unified_locations_radar, - _unified_assets_point, // 4b. road assets circles + _unified_assets_point_selection,// 4a. point asset selection halo (behind symbols) + _unified_assets_point, // 4b. road assets circles _unified_sign_poles_circle, - _unified_assets_symbol, + _unified_assets_symbol, // 5. asset symbols (on top of halo) _unified_sign_panels_circles, _unified_sign_poles_orientation, _unified_sign_poles_vertical, diff --git a/loko/assets/templates/assets/unified_index.html b/loko/assets/templates/assets/unified_index.html index 659fc96..db821e4 100644 --- a/loko/assets/templates/assets/unified_index.html +++ b/loko/assets/templates/assets/unified_index.html @@ -1636,7 +1636,7 @@ - - - + + + {% endblock script %} \ No newline at end of file