feat: implement user preference persistence for assets index thematics and filters
This commit is contained in:
parent
abf4470441
commit
9c822719b5
5 changed files with 57 additions and 3 deletions
|
|
@ -586,7 +586,7 @@ function applyVisibleColumnsPreference(visibleColumns) {
|
||||||
* explicit thematic selection (GET params / locked_thematic_code).
|
* explicit thematic selection (GET params / locked_thematic_code).
|
||||||
*/
|
*/
|
||||||
function applyInitialPreferences() {
|
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) {
|
if (ASSETS_INDEX_PREFS && typeof ASSETS_INDEX_PREFS === 'object' && Object.keys(ASSETS_INDEX_PREFS).length > 0) {
|
||||||
const prefs = ASSETS_INDEX_PREFS;
|
const prefs = ASSETS_INDEX_PREFS;
|
||||||
|
|
|
||||||
|
|
@ -718,6 +718,7 @@
|
||||||
const THEMATIC_CATEGORIES = {{ thematic_categories_json|safe }};
|
const THEMATIC_CATEGORIES = {{ thematic_categories_json|safe }};
|
||||||
const ADD_MODAL_CONFIG = {{ add_modal_config_json|safe }};
|
const ADD_MODAL_CONFIG = {{ add_modal_config_json|safe }};
|
||||||
const SELECTED_THEMATICS = {{ selected_thematics|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 SELECTED_CATEGORIES = {{ selected_categories_json|safe }};
|
||||||
const PREVENTIVE_DRAFT_ID = "{{ preventive_draft_id|default:'' }}";
|
const PREVENTIVE_DRAFT_ID = "{{ preventive_draft_id|default:'' }}";
|
||||||
const LOCKED_THEMATIC_CODE = "{{ locked_thematic_code|default:'' }}";
|
const LOCKED_THEMATIC_CODE = "{{ locked_thematic_code|default:'' }}";
|
||||||
|
|
|
||||||
|
|
@ -938,10 +938,17 @@ def unified_assets_index(request):
|
||||||
selected_locations_ids = draft_data.get('locations', []) or []
|
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)
|
# 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')
|
selected_thematics = request.GET.getlist('thematic')
|
||||||
elif locked_thematic_code:
|
elif locked_thematic_code:
|
||||||
selected_thematics = [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:
|
elif user_config and user_config.default_thematic and user_config.default_thematic in accessible_thematics:
|
||||||
selected_thematics = [user_config.default_thematic.code]
|
selected_thematics = [user_config.default_thematic.code]
|
||||||
elif accessible_thematics:
|
elif accessible_thematics:
|
||||||
|
|
@ -991,6 +998,7 @@ def unified_assets_index(request):
|
||||||
'asset_validation_status_choices': list(ASSET_VALIDATION_STATUS_CHOICES.items()),
|
'asset_validation_status_choices': list(ASSET_VALIDATION_STATUS_CHOICES.items()),
|
||||||
'add_modal_config_json': json.dumps(add_modal_config),
|
'add_modal_config_json': json.dumps(add_modal_config),
|
||||||
'selected_thematics': selected_thematics,
|
'selected_thematics': selected_thematics,
|
||||||
|
'has_url_thematics': has_url_thematics,
|
||||||
'selected_categories_json': json.dumps(selected_categories),
|
'selected_categories_json': json.dumps(selected_categories),
|
||||||
'preventive_draft_id': preventive_draft_id,
|
'preventive_draft_id': preventive_draft_id,
|
||||||
'locked_thematic_code': locked_thematic_code,
|
'locked_thematic_code': locked_thematic_code,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ from django.urls import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
from common.models import UserConfig
|
from common.models import UserConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -249,4 +251,41 @@ class HelpPanelTests(TestCase):
|
||||||
self.assertIn("Ajouter un contrôle", data["html"])
|
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"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,8 @@ def save_assets_index_preferences(request):
|
||||||
visible_columns = data.get('visible_columns')
|
visible_columns = data.get('visible_columns')
|
||||||
type_filter = data.get('type_filter', 'all')
|
type_filter = data.get('type_filter', 'all')
|
||||||
status_filters = data.get('status_filters', [])
|
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_center = data.get('map_center')
|
||||||
map_zoom = data.get('map_zoom')
|
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)
|
return JsonResponse({'error': _('Format de filtre type invalide')}, status=400)
|
||||||
if not isinstance(status_filters, list):
|
if not isinstance(status_filters, list):
|
||||||
return JsonResponse({'error': _('Format de filtres statut invalide')}, status=400)
|
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:
|
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_prefs = user_config.assets_index_preferences or {}
|
||||||
existing_visible_columns = existing_prefs.get('visible_columns', []) if isinstance(existing_prefs, dict) else []
|
existing_visible_columns = existing_prefs.get('visible_columns', []) if isinstance(existing_prefs, dict) else []
|
||||||
if not isinstance(existing_visible_columns, list):
|
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,
|
'visible_columns': visible_columns if visible_columns is not None else existing_visible_columns,
|
||||||
'type_filter': type_filter,
|
'type_filter': type_filter,
|
||||||
'status_filters': status_filters,
|
'status_filters': status_filters,
|
||||||
|
'validation_status_filters': validation_status_filters,
|
||||||
|
'include_no_geom': include_no_geom,
|
||||||
'map_center': map_center,
|
'map_center': map_center,
|
||||||
'map_zoom': map_zoom,
|
'map_zoom': map_zoom,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue