From 9c822719b59c2d66a544f67fd3cf60e900c766cd Mon Sep 17 00:00:00 2001 From: kdeterme Date: Tue, 28 Jul 2026 15:08:12 +0200 Subject: [PATCH] feat: implement user preference persistence for assets index thematics and filters --- .../assets/static/assets/unified_index.js | 2 +- .../templates/assets/unified_index.html | 1 + streetup/assets/views/core.py | 10 ++++- streetup/common/tests.py | 39 +++++++++++++++++++ streetup/common/views.py | 8 +++- 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/streetup/assets/static/assets/unified_index.js b/streetup/assets/static/assets/unified_index.js index d015fe3..af1471e 100644 --- a/streetup/assets/static/assets/unified_index.js +++ b/streetup/assets/static/assets/unified_index.js @@ -586,7 +586,7 @@ function applyVisibleColumnsPreference(visibleColumns) { * explicit thematic selection (GET params / locked_thematic_code). */ function applyInitialPreferences() { - if (PREVENTIVE_DRAFT_ID || LOCKED_THEMATIC_CODE || SELECTED_THEMATICS.length > 0) return; + if (PREVENTIVE_DRAFT_ID || LOCKED_THEMATIC_CODE || (typeof HAS_URL_THEMATICS !== 'undefined' && HAS_URL_THEMATICS)) return; if (ASSETS_INDEX_PREFS && typeof ASSETS_INDEX_PREFS === 'object' && Object.keys(ASSETS_INDEX_PREFS).length > 0) { const prefs = ASSETS_INDEX_PREFS; diff --git a/streetup/assets/templates/assets/unified_index.html b/streetup/assets/templates/assets/unified_index.html index ecc6b56..2fcb0e3 100644 --- a/streetup/assets/templates/assets/unified_index.html +++ b/streetup/assets/templates/assets/unified_index.html @@ -718,6 +718,7 @@ const THEMATIC_CATEGORIES = {{ thematic_categories_json|safe }}; const ADD_MODAL_CONFIG = {{ add_modal_config_json|safe }}; const SELECTED_THEMATICS = {{ selected_thematics|safe }}; + const HAS_URL_THEMATICS = {% if has_url_thematics %}true{% else %}false{% endif %}; const SELECTED_CATEGORIES = {{ selected_categories_json|safe }}; const PREVENTIVE_DRAFT_ID = "{{ preventive_draft_id|default:'' }}"; const LOCKED_THEMATIC_CODE = "{{ locked_thematic_code|default:'' }}"; diff --git a/streetup/assets/views/core.py b/streetup/assets/views/core.py index 1361b03..db76c45 100644 --- a/streetup/assets/views/core.py +++ b/streetup/assets/views/core.py @@ -938,10 +938,17 @@ def unified_assets_index(request): selected_locations_ids = draft_data.get('locations', []) or [] # Thématiques présélectionnées (GET, intervention, thématique par défaut ou toutes les thématiques accessibles) - if 'thematic' in request.GET: + has_url_thematics = 'thematic' in request.GET + if has_url_thematics: selected_thematics = request.GET.getlist('thematic') elif locked_thematic_code: selected_thematics = [locked_thematic_code] + elif user_config and user_config.assets_index_preferences and user_config.assets_index_preferences.get('thematics'): + saved_thematics = user_config.assets_index_preferences.get('thematics') + accessible_codes = {t.code for t in accessible_thematics} + selected_thematics = [code for code in saved_thematics if code in accessible_codes] + if not selected_thematics and accessible_thematics: + selected_thematics = [t.code for t in accessible_thematics] elif user_config and user_config.default_thematic and user_config.default_thematic in accessible_thematics: selected_thematics = [user_config.default_thematic.code] elif accessible_thematics: @@ -991,6 +998,7 @@ def unified_assets_index(request): 'asset_validation_status_choices': list(ASSET_VALIDATION_STATUS_CHOICES.items()), 'add_modal_config_json': json.dumps(add_modal_config), 'selected_thematics': selected_thematics, + 'has_url_thematics': has_url_thematics, 'selected_categories_json': json.dumps(selected_categories), 'preventive_draft_id': preventive_draft_id, 'locked_thematic_code': locked_thematic_code, diff --git a/streetup/common/tests.py b/streetup/common/tests.py index 8fad7e0..82798d7 100644 --- a/streetup/common/tests.py +++ b/streetup/common/tests.py @@ -5,6 +5,8 @@ from django.urls import reverse from django.conf import settings from django.utils.translation import gettext as _ +import json + from common.models import UserConfig @@ -249,4 +251,41 @@ class HelpPanelTests(TestCase): self.assertIn("Ajouter un contrôle", data["html"]) +class AssetsIndexPreferencesTests(TestCase): + def setUp(self): + from common.models import Thematic + self.superuser = User.objects.create_superuser(username="superprefuser", password="password123") + Thematic.objects.create(code="sign", name_fr="Signalisation") + Thematic.objects.create(code="roads", name_fr="Voirie") + + def test_save_and_restore_assets_index_preferences_for_superuser(self): + self.client.force_login(self.superuser) + save_url = reverse("common:save_assets_index_preferences") + payload = { + "thematics": ["sign", "roads"], + "categories": [1, 2], + "models": [], + "conditional_filters": {}, + "visible_columns": ["code", "status"], + "type_filter": "assets", + "status_filters": ["installed"], + "validation_status_filters": ["validated"], + "include_no_geom": False, + } + response = self.client.post(save_url, json.dumps(payload), content_type="application/json") + self.assertEqual(response.status_code, 200) + self.assertTrue(response.json().get("success")) + + # Verify unified_assets_index restores saved thematics and sets has_url_thematics to False + index_url = reverse("assets:index") + response = self.client.get(index_url) + self.assertEqual(response.status_code, 200) + self.assertFalse(response.context["has_url_thematics"]) + self.assertEqual(response.context["selected_thematics"], ["sign", "roads"]) + saved_prefs = json.loads(response.context["assets_index_prefs_json"]) + self.assertEqual(saved_prefs["thematics"], ["sign", "roads"]) + self.assertEqual(saved_prefs["validation_status_filters"], ["validated"]) + + + diff --git a/streetup/common/views.py b/streetup/common/views.py index af48eaa..5aef4f3 100644 --- a/streetup/common/views.py +++ b/streetup/common/views.py @@ -432,6 +432,8 @@ def save_assets_index_preferences(request): visible_columns = data.get('visible_columns') type_filter = data.get('type_filter', 'all') status_filters = data.get('status_filters', []) + validation_status_filters = data.get('validation_status_filters', []) + include_no_geom = data.get('include_no_geom', True) map_center = data.get('map_center') map_zoom = data.get('map_zoom') @@ -449,9 +451,11 @@ def save_assets_index_preferences(request): return JsonResponse({'error': _('Format de filtre type invalide')}, status=400) if not isinstance(status_filters, list): return JsonResponse({'error': _('Format de filtres statut invalide')}, status=400) + if not isinstance(validation_status_filters, list): + return JsonResponse({'error': _('Format de filtres statut de validation invalide')}, status=400) try: - user_config = request.user.config + user_config, _ = UserConfig.objects.get_or_create(user=request.user) existing_prefs = user_config.assets_index_preferences or {} existing_visible_columns = existing_prefs.get('visible_columns', []) if isinstance(existing_prefs, dict) else [] if not isinstance(existing_visible_columns, list): @@ -465,6 +469,8 @@ def save_assets_index_preferences(request): 'visible_columns': visible_columns if visible_columns is not None else existing_visible_columns, 'type_filter': type_filter, 'status_filters': status_filters, + 'validation_status_filters': validation_status_filters, + 'include_no_geom': include_no_geom, 'map_center': map_center, 'map_zoom': map_zoom, }