From 875efc67f4496c640b060e37bc30826577e69f64 Mon Sep 17 00:00:00 2001 From: kdeterme Date: Wed, 7 Oct 2026 15:54:57 +0200 Subject: [PATCH] feat(home): add user_history widget for viewers and adjust quick action shortcuts --- .../templates/contracts/contracts_detail.html | 20 +++ loko/home/locale/en/LC_MESSAGES/django.mo | Bin 17751 -> 19136 bytes loko/home/locale/en/LC_MESSAGES/django.po | 45 +++++ loko/home/locale/fr/LC_MESSAGES/django.mo | Bin 18837 -> 20309 bytes loko/home/locale/fr/LC_MESSAGES/django.po | 45 +++++ loko/home/locale/nl/LC_MESSAGES/django.mo | Bin 18437 -> 19887 bytes loko/home/locale/nl/LC_MESSAGES/django.po | 45 +++++ .../0010_alter_homewidget_widget_code.py | 18 ++ .../0011_create_user_history_widget.py | 49 ++++++ loko/home/models.py | 1 + loko/home/static/home/home_index.js | 12 +- loko/home/templates/home/home_index.html | 15 ++ .../home/includes/_user_history_modal.html | 155 +++++++++++++++++ .../templates/home/widgets/map_today.html | 7 +- .../templates/home/widgets/user_history.html | 161 ++++++++++++++++++ loko/home/views.py | 130 ++++++++++++++ .../interventions/intervention_detail.html | 18 ++ 17 files changed, 710 insertions(+), 11 deletions(-) create mode 100644 loko/home/migrations/0010_alter_homewidget_widget_code.py create mode 100644 loko/home/migrations/0011_create_user_history_widget.py create mode 100644 loko/home/templates/home/includes/_user_history_modal.html create mode 100644 loko/home/templates/home/widgets/user_history.html diff --git a/loko/contracts/templates/contracts/contracts_detail.html b/loko/contracts/templates/contracts/contracts_detail.html index 2fce608..eb8e363 100644 --- a/loko/contracts/templates/contracts/contracts_detail.html +++ b/loko/contracts/templates/contracts/contracts_detail.html @@ -142,5 +142,25 @@ +{% endblock %} +{% block script %} + {% endblock %} \ No newline at end of file diff --git a/loko/home/locale/en/LC_MESSAGES/django.mo b/loko/home/locale/en/LC_MESSAGES/django.mo index 474eb1a292143544b514a81ec5f99fd76cbb96a4..ee5d5f74a3a799774a757806c05a2a54e60b1c0d 100644 GIT binary patch delta 6698 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 delta 5381 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 E0n1!Qg#Z8m diff --git a/loko/home/locale/fr/LC_MESSAGES/django.po b/loko/home/locale/fr/LC_MESSAGES/django.po index f40a182..f7ccc1c 100644 --- a/loko/home/locale/fr/LC_MESSAGES/django.po +++ b/loko/home/locale/fr/LC_MESSAGES/django.po @@ -1155,3 +1155,48 @@ msgstr "à traiter · en cours" msgid "Aucun bon à traiter ou en cours" msgstr "Aucun bon à traiter ou en cours" +msgid "Activité des trois derniers mois" +msgstr "Activité des trois derniers mois" + +msgid "Rechercher une intervention" +msgstr "Rechercher une intervention" + +msgid "Consulter les contrats" +msgstr "Consulter les contrats" + +msgid "Calendrier" +msgstr "Calendrier" + +msgid "Mon historique" +msgstr "Mon historique" + +msgid "Vos consultations récentes d'interventions et de contrats" +msgstr "Vos consultations récentes d'interventions et de contrats" + +msgid "Aucun élément consulté récemment" +msgstr "Aucun élément consulté récemment" + +msgid "Les interventions et contrats que vous consultez s'afficheront ici automatiquement pour vous permettre d'y revenir rapidement." +msgstr "Les interventions et contrats que vous consultez s'afficheront ici automatiquement pour vous permettre d'y revenir rapidement." + +msgid "Historique basé sur vos consultations dans l'application" +msgstr "Historique basé sur vos consultations dans l'application" + +msgid "Vos dernières consultations" +msgstr "Vos dernières consultations" + +msgid "consultation(s)" +msgstr "consultation(s)" + +msgid "Ouvrir en grand" +msgstr "Ouvrir en grand" + +msgid "Vos consultations d'interventions et de contrats apparaîtront ici pour un accès rapide." +msgstr "Vos consultations d'interventions et de contrats apparaîtront ici pour un accès rapide." + +msgid "Toutes les interventions" +msgstr "Toutes les interventions" + +msgid "Vue détaillée" +msgstr "Vue détaillée" + diff --git a/loko/home/locale/nl/LC_MESSAGES/django.mo b/loko/home/locale/nl/LC_MESSAGES/django.mo index 1fe79dd69cd0e9bd04eec02c8fe9e126a67244e4..d46f0e045f22bac966ff51d39f21668ea39775f8 100644 GIT binary patch delta 6777 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 q+Cs delta 5373 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 RS2$&cH!~~Kw;?Oc^&1%?K41U< diff --git a/loko/home/locale/nl/LC_MESSAGES/django.po b/loko/home/locale/nl/LC_MESSAGES/django.po index 7f23eb3..2c383bc 100644 --- a/loko/home/locale/nl/LC_MESSAGES/django.po +++ b/loko/home/locale/nl/LC_MESSAGES/django.po @@ -1152,3 +1152,48 @@ msgstr "te verwerken · in behandeling" msgid "Aucun bon à traiter ou en cours" msgstr "Geen bonnen te verwerken of in behandeling" +msgid "Activité des trois derniers mois" +msgstr "Activiteit van de afgelopen drie maanden" + +msgid "Rechercher une intervention" +msgstr "Een interventie zoeken" + +msgid "Consulter les contrats" +msgstr "Contracten raadplegen" + +msgid "Calendrier" +msgstr "Kalender" + +msgid "Mon historique" +msgstr "Mijn geschiedenis" + +msgid "Vos consultations récentes d'interventions et de contrats" +msgstr "Uw recente raadplegingen van interventies en contracten" + +msgid "Aucun élément consulté récemment" +msgstr "Geen recent geraadpleegde items" + +msgid "Les interventions et contrats que vous consultez s'afficheront ici automatiquement pour vous permettre d'y revenir rapidement." +msgstr "De interventies en contracten die u raadpleegt, worden hier automatisch weergegeven zodat u er snel naar kunt terugkeren." + +msgid "Historique basé sur vos consultations dans l'application" +msgstr "Geschiedenis gebaseerd op uw raadplegingen in de applicatie" + +msgid "Vos dernières consultations" +msgstr "Uw laatste raadplegingen" + +msgid "consultation(s)" +msgstr "raadpleging(en)" + +msgid "Ouvrir en grand" +msgstr "Vergroten" + +msgid "Vos consultations d'interventions et de contrats apparaîtront ici pour un accès rapide." +msgstr "Uw raadplegingen van interventies en contracten verschijnen hier voor snelle toegang." + +msgid "Toutes les interventions" +msgstr "Alle interventies" + +msgid "Vue détaillée" +msgstr "Gedetailleerde weergave" + diff --git a/loko/home/migrations/0010_alter_homewidget_widget_code.py b/loko/home/migrations/0010_alter_homewidget_widget_code.py new file mode 100644 index 0000000..869f0b4 --- /dev/null +++ b/loko/home/migrations/0010_alter_homewidget_widget_code.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.7 on 2026-10-07 13:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0009_alter_homewidget_widget_code'), + ] + + operations = [ + migrations.AlterField( + model_name='homewidget', + name='widget_code', + field=models.CharField(choices=[('territory_kpis', 'Métriques du patrimoine'), ('recent_activity', "Radar d'activité"), ('intervention_stats', 'Statistiques interventions'), ('contracts_summary', 'Résumé contrats'), ('map_today', 'Carte du jour'), ('operator_shortcuts', 'Raccourcis opérateur'), ('assets_links', 'Liens assets'), ('stock_summary', 'Résumé stock'), ('observer_shortcuts', 'Raccourcis observateur'), ('incident_counter', 'Compteur incidents à traiter'), ('intervention_to_verify', 'Compteur interventions à vérifier'), ('intervention_to_plan', 'Compteur interventions à planifier'), ('intervention_to_approve', 'Compteur interventions à approuver'), ('intervention_checked_to_approve', 'Compteur interventions vérifiées à approuver'), ('intervention_finished', 'Compteur interventions terminées'), ('intervention_on_pause', 'Compteur interventions en pause'), ('external_interventions', 'Interventions externes'), ('stock_actions', 'Actions stock'), ('exceptional_transport', 'Transport exceptionnel'), ('controller_shortcuts', 'Raccourcis contrôleur'), ('user_history', 'Mon historique')], max_length=50, unique=True, verbose_name='Code du widget'), + ), + ] diff --git a/loko/home/migrations/0011_create_user_history_widget.py b/loko/home/migrations/0011_create_user_history_widget.py new file mode 100644 index 0000000..f1ef068 --- /dev/null +++ b/loko/home/migrations/0011_create_user_history_widget.py @@ -0,0 +1,49 @@ +from django.db import migrations + + +def create_user_history_widget(apps, schema_editor): + HomeWidget = apps.get_model('home', 'HomeWidget') + HomeWidgetConditionGroup = apps.get_model('home', 'HomeWidgetConditionGroup') + HomeWidgetCondition = apps.get_model('home', 'HomeWidgetCondition') + Role = apps.get_model('common', 'Role') + + widget, created = HomeWidget.objects.get_or_create( + widget_code='user_history', + defaults={'is_active': True} + ) + + if not widget.condition_groups.exists(): + viewer_role, _ = Role.objects.get_or_create(name='viewer') + if viewer_role: + group = HomeWidgetConditionGroup.objects.create( + widget=widget, + label="Lecteurs (Viewers)", + order=20, + col_width=12, + priority=0, + is_active=True + ) + HomeWidgetCondition.objects.create( + group=group, + condition_type='role', + role=viewer_role, + is_active=True, + is_negated=False + ) + + +def remove_user_history_widget(apps, schema_editor): + HomeWidget = apps.get_model('home', 'HomeWidget') + HomeWidget.objects.filter(widget_code='user_history').delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0010_alter_homewidget_widget_code'), + ('common', '0001_initial'), + ] + + operations = [ + migrations.RunPython(create_user_history_widget, remove_user_history_widget), + ] diff --git a/loko/home/models.py b/loko/home/models.py index cf1e926..57dadb7 100644 --- a/loko/home/models.py +++ b/loko/home/models.py @@ -24,6 +24,7 @@ WIDGET_CODE_CHOICES = [ ('stock_actions', _('Actions stock')), ('exceptional_transport', _('Transport exceptionnel')), ('controller_shortcuts', _('Raccourcis contrôleur')), + ('user_history', _('Mon historique')), ] # Condition type choices diff --git a/loko/home/static/home/home_index.js b/loko/home/static/home/home_index.js index 6436641..a28f368 100644 --- a/loko/home/static/home/home_index.js +++ b/loko/home/static/home/home_index.js @@ -48,7 +48,7 @@ if (mapContainer && typeof maplibregl !== 'undefined') { map.addControl(new maplibregl.NavigationControl(), 'top-left'); - let currentMode = 'heatmap'; // 'heatmap' or 'points' + let currentMode = 'points'; // 'heatmap' or 'points' let activityGeojsonData = null; const THEMATIC_COLORS = { @@ -279,6 +279,9 @@ if (mapContainer && typeof maplibregl !== 'undefined') { type: 'heatmap', source: 'activity-data', maxzoom: 17, + layout: { + 'visibility': 'none' + }, paint: { 'heatmap-weight': 1, 'heatmap-intensity': [ @@ -350,12 +353,7 @@ if (mapContainer && typeof maplibregl !== 'undefined') { 'icon-anchor': 'center' }, paint: { - // En mode Heatmap : les symboles s'estompent au dézoom pour laisser le champ libre à la heatmap - 'icon-opacity': [ - 'interpolate', ['linear'], ['zoom'], - 13.8, 0, - 15.2, 1 - ] + 'icon-opacity': 1 } }); diff --git a/loko/home/templates/home/home_index.html b/loko/home/templates/home/home_index.html index 24f38aa..02e5f27 100644 --- a/loko/home/templates/home/home_index.html +++ b/loko/home/templates/home/home_index.html @@ -49,6 +49,20 @@
+ {% if user.config|has_any_role:"viewer" and not user.config|has_any_role:"manager,admin,controller,operator,inspector" %} + + + {% translate "Rechercher une intervention" %} + + + + {% translate "Calendrier" %} + + + + {% translate "Mon historique" %} + + {% endif %} {% if user.config|has_any_role:"observer,manager,controller,admin,external_manager,top_manager" %} @@ -149,6 +163,7 @@ {% include "notifications/_new_discussion_modal.html" %} + {% include "home/includes/_user_history_modal.html" %} {% endblock content%} diff --git a/loko/home/templates/home/includes/_user_history_modal.html b/loko/home/templates/home/includes/_user_history_modal.html new file mode 100644 index 0000000..5dba300 --- /dev/null +++ b/loko/home/templates/home/includes/_user_history_modal.html @@ -0,0 +1,155 @@ +{% load i18n %} +{% load static %} + + + + + diff --git a/loko/home/templates/home/widgets/map_today.html b/loko/home/templates/home/widgets/map_today.html index c9c6ea9..8977975 100644 --- a/loko/home/templates/home/widgets/map_today.html +++ b/loko/home/templates/home/widgets/map_today.html @@ -8,16 +8,15 @@
-
{% translate "Activité du Territoire" %}
- {% translate "Mise en lumière & intensité des 90 derniers jours" %} +
{% translate "Activité des trois derniers mois" %}
- -
diff --git a/loko/home/templates/home/widgets/user_history.html b/loko/home/templates/home/widgets/user_history.html new file mode 100644 index 0000000..9251646 --- /dev/null +++ b/loko/home/templates/home/widgets/user_history.html @@ -0,0 +1,161 @@ +{% load i18n %} +{% load static %} + +
+
+
+
+ +
+
+
{% translate "Mon historique" %}
+ {% translate "Vos dernières consultations" %} +
+
+
+ + {{ user_recent_history|length|default:"0" }} {% translate "consultation(s)" %} + + +
+
+ + + + +
+ + diff --git a/loko/home/views.py b/loko/home/views.py index 5d826c6..219df1f 100644 --- a/loko/home/views.py +++ b/loko/home/views.py @@ -10,8 +10,11 @@ from datetime import timedelta from collections import defaultdict import json import logging +import re import time +logger = logging.getLogger(__name__) + # logger = logging.getLogger('home.perf') @@ -84,6 +87,19 @@ def index(request): if result: resolved_widgets.append(result) + # Fallback pour le rôle viewer : s'assurer que le widget d'historique s'affiche toujours + if not any(r['widget'].widget_code == 'user_history' for r in resolved_widgets): + user_roles_set = set(user_config.roles.values_list('name', flat=True)) + if 'viewer' in user_roles_set and not (user_roles_set & {'admin', 'manager', 'controller', 'operator', 'inspector'}): + user_history_widget = HomeWidget.objects.filter(widget_code='user_history').first() or HomeWidget(widget_code='user_history') + resolved_widgets.append({ + 'widget': user_history_widget, + 'order': 20, + 'template_name': 'home/widgets/user_history.html', + 'col_width': 12, + 'col_order': 0, + }) + resolved_widgets.sort(key=lambda r: (r['order'], r['col_order'])) # Group widgets by order into rows; within each row sort by col_order. @@ -786,10 +802,12 @@ def index(request): territory_kpis = _get_territory_kpis(request.user, user_contracts) recent_activity = _get_recent_activity(request.user, user_contracts) + user_recent_history = _get_user_recent_history(request.user) context = { 'territory_kpis': territory_kpis, 'recent_activity': recent_activity, + 'user_recent_history': user_recent_history, 'is_inspector': is_inspector, 'inspection_configs': inspection_configs, 'series_data': json.dumps(series_data), @@ -1230,6 +1248,118 @@ def _get_recent_activity(user, allowed_contracts): return recent_items +def _get_user_recent_history(user, limit=10): + """ + Rassemble l'historique des dernières consultations (interventions et contrats) + de l'utilisateur, vérifié selon ses droits d'accès. + """ + try: + from analytics.models import PageView + views = ( + PageView.objects.filter( + user=user, + method='GET', + path__regex=r"^/(interventions|contracts)/[0-9]+/?$" + ) + .order_by('-timestamp')[:40] + ) + + seen_paths = set() + items = [] + for v in views: + clean_path = v.path.rstrip('/') + '/' + if clean_path in seen_paths: + continue + seen_paths.add(clean_path) + + m = re.match(r"^/(interventions|contracts)/([0-9]+)/$", clean_path) + if not m: + continue + items.append({ + 'type': m.group(1), + 'id': int(m.group(2)), + 'timestamp': v.timestamp, + 'path': clean_path, + }) + if len(items) >= limit: + break + + interv_ids = [it['id'] for it in items if it['type'] == 'interventions'] + contract_ids = [it['id'] for it in items if it['type'] == 'contracts'] + + interv_map = {} + if interv_ids: + from interventions.permissions import filter_viewable_interventions_for_user + interv_qs = filter_viewable_interventions_for_user(user).filter( + id__in=interv_ids + ).select_related('thematic') + interv_map = {i.id: i for i in interv_qs} + + contract_map = {} + if contract_ids: + from contracts.permissions import get_allowed_contracts_for_user + allowed_contracts = get_allowed_contracts_for_user(user) + contract_qs = Contract.objects.filter( + id__in=contract_ids + ).filter(id__in=allowed_contracts).select_related('company') + contract_map = {c.id: c for c in contract_qs} + + now = timezone.now() + result = [] + for it in items: + if it['type'] == 'interventions' and it['id'] in interv_map: + interv = interv_map[it['id']] + diff = now - it['timestamp'] + if diff.days == 0: + hours = int(diff.total_seconds() // 3600) + relative_time = "Il y a 1h" if hours <= 1 else f"Il y a {hours}h" if hours > 0 else "À l'instant" + elif diff.days == 1: + relative_time = "Hier" + elif diff.days < 7: + relative_time = f"Il y a {diff.days}j" + else: + relative_time = it['timestamp'].strftime("%d/%m/%Y") + + result.append({ + 'type': 'intervention', + 'id': interv.id, + 'title': interv.title or f"Intervention #{interv.code or interv.id}", + 'subtitle': interv.thematic.name if interv.thematic else '', + 'status': interv.get_status_display(), + 'status_code': interv.status, + 'url': reverse('interventions:intervention_detail', args=[interv.id]), + 'timestamp': it['timestamp'], + 'relative_time': relative_time, + }) + elif it['type'] == 'contracts' and it['id'] in contract_map: + contract = contract_map[it['id']] + diff = now - it['timestamp'] + if diff.days == 0: + hours = int(diff.total_seconds() // 3600) + relative_time = "Il y a 1h" if hours <= 1 else f"Il y a {hours}h" if hours > 0 else "À l'instant" + elif diff.days == 1: + relative_time = "Hier" + elif diff.days < 7: + relative_time = f"Il y a {diff.days}j" + else: + relative_time = it['timestamp'].strftime("%d/%m/%Y") + + result.append({ + 'type': 'contract', + 'id': contract.id, + 'title': f"Marché {contract.contract_number}", + 'subtitle': (contract.description[:60] + '...') if len(contract.description) > 60 else contract.description, + 'company': contract.company.name if contract.company else '', + 'url': reverse('contracts:contracts_detail', args=[contract.id]), + 'timestamp': it['timestamp'], + 'relative_time': relative_time, + }) + return result + except Exception as e: + logger.warning(f"Error fetching user recent history: {e}") + return [] + + @login_required def activity_geojson(request): """ diff --git a/loko/interventions/templates/interventions/intervention_detail.html b/loko/interventions/templates/interventions/intervention_detail.html index eee6b9e..127a693 100644 --- a/loko/interventions/templates/interventions/intervention_detail.html +++ b/loko/interventions/templates/interventions/intervention_detail.html @@ -53,4 +53,22 @@ {% block script %} {% include "interventions/detail/scripts.html" %} + {% endblock script %}