From da038b7c4f269fed62b413bcd62cba968a57da17 Mon Sep 17 00:00:00 2001 From: Karl Determe Date: Tue, 1 Sep 2026 15:45:28 +0200 Subject: [PATCH] feat: implement mobile-optimized intervention list view with user-specific preference persistence and associated tests --- .../_interventions_list_mobile.html | 120 +++++++++++------- loko/interventions/views/mobile.py | 6 + loko/mobile/tests.py | 19 +++ 3 files changed, 101 insertions(+), 44 deletions(-) diff --git a/loko/interventions/templates/interventions/_interventions_list_mobile.html b/loko/interventions/templates/interventions/_interventions_list_mobile.html index e11821a..6445426 100644 --- a/loko/interventions/templates/interventions/_interventions_list_mobile.html +++ b/loko/interventions/templates/interventions/_interventions_list_mobile.html @@ -39,53 +39,85 @@ {% for intervention in interventions %}
-
-
-
- - {{ intervention.code }} {{ intervention.title }} -
- {{ intervention.get_status_display }} - {% if intervention.prep_state %} - {% if intervention.prep_state == "processed" %} - - - - {% elif intervention.prep_state == "not_processed" %} - - +
+
+
+
+ + {{ intervention.code }} {{ intervention.title }} +
+ {{ intervention.get_status_display }} + {% if intervention.prep_state %} + {% if intervention.prep_state == "processed" %} + + + + {% elif intervention.prep_state == "not_processed" %} + + + + {% endif %} + {% endif %} + {% if intervention.list_begin_time %} +
+ + {{ intervention.list_begin_time|localtime|date:"d/m/Y H:i" }} +
+ {% endif %} +
+
+ {% if intervention.lat and intervention.lon %} + + {% endif %} + {% if not intervention.assigned_member %} + + {% else %} + + {% endif %} +
+
+ + {% if intervention.preparation_orders.all %} +
+ + {% for order in intervention.preparation_orders.all %} + {% if order.is_loan %} + + {% translate "Prêt" %} {% endif %} - {% endif %} - {% if intervention.list_begin_time %} -
- - {{ intervention.list_begin_time|localtime|date:"d/m/Y H:i" }} -
- {% endif %} -
-
- {% if intervention.lat and intervention.lon %} - - {% endif %} - {% if not intervention.assigned_member %} - - {% else %} - - {% endif %} + {% for item in order.preparationorderitem_set.all|slice:":3" %} + + {{ item.product.name }} × {{ item.quantity }} + + {% endfor %} + {% for item in order.other_items.all|slice:":2" %} + + {{ item.name }} × {{ item.quantity }} + + {% endfor %} + {% with total_count=order.preparationorderitem_set.all|length|add:order.other_items.all|length %} + {% if total_count > 3 %} + + +{{ total_count|add:"-3" }} + + {% endif %} + {% endwith %} + {% endfor %}
+ {% endif %}
{% empty %} diff --git a/loko/interventions/views/mobile.py b/loko/interventions/views/mobile.py index 81d9b88..e4feb92 100644 --- a/loko/interventions/views/mobile.py +++ b/loko/interventions/views/mobile.py @@ -364,6 +364,12 @@ def interventions_index_mobile(request): # Apply limit (slice) at the very end, after all filtering interventions = interventions[:100] + # Preload preparation orders and items to avoid N+1 queries in mobile list cards + interventions = interventions.prefetch_related( + 'preparation_orders__preparationorderitem_set__product', + 'preparation_orders__other_items', + ) + # Evaluate queryset once to avoid double DB round-trip interventions_list = list(interventions) diff --git a/loko/mobile/tests.py b/loko/mobile/tests.py index 8fd6267..d9917c4 100644 --- a/loko/mobile/tests.py +++ b/loko/mobile/tests.py @@ -172,4 +172,23 @@ class MobileInterventionDetailStockTests(TestCase): self.assertFalse(response.context["can_create_preparation_order"]) self.assertEqual(len(response.context["preparation_orders"]), 1) + def test_stock_items_rendered_in_mobile_interventions_list(self): + url = reverse("mobile:interventions_index_mobile") + response = self.client.get(url, follow=True) + + self.assertEqual(response.status_code, 200) + content = response.content.decode("utf-8") + self.assertIn("Poteau feux", content) + self.assertIn("× 3", content) + + def test_stock_items_rendered_in_mobile_ajax_list(self): + url = reverse("mobile:interventions_index_mobile") + "?status=in_progress" + response = self.client.get(url, headers={"x-requested-with": "XMLHttpRequest"}) + + self.assertEqual(response.status_code, 200) + payload = response.json() + self.assertIn("html", payload) + self.assertIn("Poteau feux", payload["html"]) + self.assertIn("× 3", payload["html"]) +