From 2df4621e5b637ff5ca2b6c5c5b8323404cb4a891 Mon Sep 17 00:00:00 2001 From: kdeterme Date: Fri, 25 Sep 2026 22:35:29 +0200 Subject: [PATCH] =?UTF-8?q?feat(interventions):=20afficher=20les=20statuts?= =?UTF-8?q?=20de=20la=20chronologie=20sous=20forme=20de=20cercles=20num?= =?UTF-8?q?=C3=A9rot=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Reprend le code visuel du stepper supérieur pour les transitions de statut dans l'historique - Affiche une boule colorée avec le numéro canonique (1 à 15) et le libellé sous chaque statut - Aligne la flèche de transition au centre des cercles - Ajuste les contrastes de texte (noir/blanc) selon la luminosité du statut --- loko/interventions/models.py | 30 +++++++++ .../static/interventions/timeline.css | 62 +++++++++++++++++++ .../detail/card_chronologie.html | 28 ++++++--- .../interventions/detail/styles.html | 61 ++++++++++++++++++ .../tests/test_timeline_history.py | 12 +++- 5 files changed, 184 insertions(+), 9 deletions(-) diff --git a/loko/interventions/models.py b/loko/interventions/models.py index c1e91df..e5589b3 100644 --- a/loko/interventions/models.py +++ b/loko/interventions/models.py @@ -58,6 +58,10 @@ STATUS_ORDERS = { 'canceled': 200, } +# Numérotation 1-15 des statuts dans l'ordre canonique du stepper +STATUS_STEP_NUMBERS = {key: idx + 1 for idx, key in enumerate(STATUS_CHOICES.keys())} + + # Couleurs canoniques des statuts (hex). Source de vérité partagée avec status_colors.js. STATUS_COLORS = { 'in_preparation': '#757575', @@ -1328,6 +1332,32 @@ class InterventionTimeLine(models.Model): def get_to_status_color(self): return STATUS_COLOR_MAP.get(self.to_status, "secondary") + def get_from_status_number(self): + return STATUS_STEP_NUMBERS.get(self.from_status, '') + + def get_to_status_number(self): + return STATUS_STEP_NUMBERS.get(self.to_status, '') + + def get_from_status_hex(self): + return STATUS_COLORS.get(self.from_status, '#757575') + + def get_to_status_hex(self): + return STATUS_COLORS.get(self.to_status, '#757575') + + def get_from_status_text_color(self): + return '#000000' if self.from_status in STATUS_COLORS_DARK_TEXT else '#ffffff' + + def get_to_status_text_color(self): + return '#000000' if self.to_status in STATUS_COLORS_DARK_TEXT else '#ffffff' + + from_status_number = property(get_from_status_number) + to_status_number = property(get_to_status_number) + from_status_hex = property(get_from_status_hex) + to_status_hex = property(get_to_status_hex) + from_status_text_color = property(get_from_status_text_color) + to_status_text_color = property(get_to_status_text_color) + + def get_badge_class(self): return { 'creation': 'bg-success', diff --git a/loko/interventions/static/interventions/timeline.css b/loko/interventions/static/interventions/timeline.css index 2afcee2..040ec61 100644 --- a/loko/interventions/static/interventions/timeline.css +++ b/loko/interventions/static/interventions/timeline.css @@ -261,4 +261,66 @@ color: #ffffff !important; } +/* Transition de statut : boules de couleur avec numéro et libellé en-dessous */ +.history-status-transition { + display: inline-flex; + align-items: flex-start; +} + +.history-status-step { + display: inline-flex; + flex-direction: column; + align-items: center; + width: 90px; + text-align: center; +} + +.history-status-circle { + width: 28px; + height: 28px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: 700; + line-height: 1; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + margin-bottom: 3px; + flex-shrink: 0; + user-select: none; +} + +.history-status-arrow { + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + color: #94a3b8; + font-size: 1.15rem; + padding: 0 6px; + flex-shrink: 0; +} + +.history-status-label { + font-size: 11px; + font-weight: 500; + color: #334155; + line-height: 1.2; + text-align: center; + word-break: normal; + overflow-wrap: break-word; +} + +.history-status-change-badge { + align-self: flex-start; + margin-top: 3px; +} + +.history-status-desc { + margin-top: 5px; + font-style: italic; +} + + diff --git a/loko/interventions/templates/interventions/detail/card_chronologie.html b/loko/interventions/templates/interventions/detail/card_chronologie.html index 9699847..0771ec2 100644 --- a/loko/interventions/templates/interventions/detail/card_chronologie.html +++ b/loko/interventions/templates/interventions/detail/card_chronologie.html @@ -55,14 +55,28 @@
{% if event.event_type == 'status_change' %} - {% translate "Statut" %} - {% if event.from_status %} - {{ event.get_from_status_display }} - - {% endif %} - {{ event.get_to_status_display }} + {% translate "Statut" %} +
+ {% if event.from_status %} +
+
+ {{ event.from_status_number }} +
+
{{ event.get_from_status_display }}
+
+
+ +
+ {% endif %} +
+
+ {{ event.to_status_number }} +
+
{{ event.get_to_status_display }}
+
+
{% if event.event_description %} - ({{ event.event_description }}) + ({{ event.event_description }}) {% endif %} {% elif event.event_type == 'assignment' %} {% translate "Assignation" %} diff --git a/loko/interventions/templates/interventions/detail/styles.html b/loko/interventions/templates/interventions/detail/styles.html index 4b26e8d..1c79fe1 100644 --- a/loko/interventions/templates/interventions/detail/styles.html +++ b/loko/interventions/templates/interventions/detail/styles.html @@ -606,4 +606,65 @@ background-color: #6f42c1 !important; color: #ffffff !important; } + + /* Transition de statut : boules de couleur avec numéro et libellé en-dessous */ + .history-status-transition { + display: inline-flex; + align-items: flex-start; + } + + .history-status-step { + display: inline-flex; + flex-direction: column; + align-items: center; + width: 90px; + text-align: center; + } + + .history-status-circle { + width: 28px; + height: 28px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: 700; + line-height: 1; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + margin-bottom: 3px; + flex-shrink: 0; + user-select: none; + } + + .history-status-arrow { + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + color: #94a3b8; + font-size: 1.15rem; + padding: 0 6px; + flex-shrink: 0; + } + + .history-status-label { + font-size: 11px; + font-weight: 500; + color: #334155; + line-height: 1.2; + text-align: center; + word-break: normal; + overflow-wrap: break-word; + } + + .history-status-change-badge { + align-self: flex-start; + margin-top: 3px; + } + + .history-status-desc { + margin-top: 5px; + font-style: italic; + } diff --git a/loko/interventions/tests/test_timeline_history.py b/loko/interventions/tests/test_timeline_history.py index bb34cc4..27e0c93 100644 --- a/loko/interventions/tests/test_timeline_history.py +++ b/loko/interventions/tests/test_timeline_history.py @@ -81,7 +81,7 @@ class TimelineHistoryTests(TestCase): event_time=timezone.now(), event_type='status_change', from_status='in_preparation', - to_status='planned', + to_status='to_be_planned', event_description='Passage en planifié' ) self.assertTrue(event_status.is_status_change) @@ -90,6 +90,12 @@ class TimelineHistoryTests(TestCase): self.assertFalse(event_status.is_assignment) self.assertEqual(event_status.get_badge_class(), 'bg-purple') self.assertEqual(event_status.get_icon(), 'bi-arrow-repeat') + self.assertEqual(event_status.from_status_number, 1) + self.assertEqual(event_status.to_status_number, 3) + self.assertEqual(event_status.from_status_hex, '#757575') + self.assertEqual(event_status.to_status_hex, '#ff8a80') + self.assertEqual(event_status.from_status_text_color, '#ffffff') + self.assertEqual(event_status.to_status_text_color, '#000000') # Assignment event event_assign = InterventionTimeLine.objects.create( @@ -450,6 +456,7 @@ class TimelineHistoryTests(TestCase): def test_detail_view_shows_all_in_full_history(self): """Test that notes, documents, and links appear as detailed events in timeline.""" + record_timeline_event(self.intervention, self.user, event_type='status_change', from_status='in_preparation', to_status='to_be_processed') record_note_event(self.intervention, self.user, action='create', note_type='comment', content='Note test') record_document_event(self.intervention, self.user, action='upload', filenames=['doc_test.png']) record_relation_link_event(self.intervention, self.user, relation_label='Intervention', target_labels=['I999999']) @@ -467,7 +474,8 @@ class TimelineHistoryTests(TestCase): self.assertContains(response, 'data-event-type="document"') self.assertContains(response, 'data-event-type="link"') self.assertContains(response, 'history-event-badge') - self.assertContains(response, 'history-status-badge') + self.assertContains(response, 'history-status-circle') + self.assertContains(response, 'history-status-transition') self.assertContains(response, 'bg-purple')