From f12ad5d3c0621a818549554df3105987685aa054 Mon Sep 17 00:00:00 2001 From: Dennis Nemec Date: Wed, 24 Jun 2026 11:11:21 +0200 Subject: [PATCH] =?UTF-8?q?feat(loading):=20Set-Kopf=20wird=20gr=C3=BCn,?= =?UTF-8?q?=20wenn=20alle=20Komponenten=20geladen=20sind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ein nicht-scanbarer Set-Kopf (Parent-Artikel) hat keinen eigenen Scan-Status. Sobald alle scanbaren, nicht entfernten Komponenten fertig (isDone) sind, wird der Kopf jetzt ebenfalls grün dargestellt (effectiveDone = isDone || setParentComplete) und zeigt statt des unterdrückten Hinweises ein grünes „Komplett geladen". Co-Authored-By: Claude Opus 4.8 (1M context) --- .../presentation/loading_customer_page.dart | 76 +++++++++++++++++-- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/lib/feature/loading/presentation/loading_customer_page.dart b/lib/feature/loading/presentation/loading_customer_page.dart index 425de61..e76e533 100644 --- a/lib/feature/loading/presentation/loading_customer_page.dart +++ b/lib/feature/loading/presentation/loading_customer_page.dart @@ -558,6 +558,17 @@ class _CustomerBody extends StatelessWidget { }) .map((it) => it.id) .toSet(); + // Set-Kopf „komplett": alle scanbaren, nicht entfernten Komponenten fertig. + // Dann wird auch der (selbst nicht scanbare) Kopf grün dargestellt. + bool setParentComplete(DeliveryItem parent) { + final nr = artNrOf(parent); + if (nr == null) return false; + final comps = delivery.items.where((c) => + c.parentArtikelNr == nr && + !c.isRemoved && + (details.articleOf(c.articleId)?.scannable ?? false)); + return comps.isNotEmpty && comps.every((c) => c.isDone); + } // Set-Köpfe je Lagergruppe (warehouseId → einzuhängende Köpfe) + // gesammelte IDs, um sie aus der Dienstleistungs-Sektion zu entfernen. final injectedParentsByWarehouseId = >{}; @@ -686,6 +697,8 @@ class _CustomerBody extends StatelessWidget { details: details, onAction: (action) => onItemAction(item, action), suppressScanHint: setParentIds.contains(item.id), + setParentComplete: setParentIds.contains(item.id) && + setParentComplete(item), ), ], // Gebuchte Dienstleistungen (nicht-scanbare Positionen ohne @@ -701,6 +714,8 @@ class _CustomerBody extends StatelessWidget { details: details, onAction: (action) => onItemAction(item, action), suppressScanHint: setParentIds.contains(item.id), + setParentComplete: setParentIds.contains(item.id) && + setParentComplete(item), ), ], ], @@ -807,6 +822,44 @@ class _ScanNotRequiredHint extends StatelessWidget { } } +/// Grünes „Komplett geladen" für einen Set-Kopf (Parent-Artikel), dessen +/// (scanbare) Komponenten alle geladen sind. Ersetzt an dieser Stelle den +/// „Kein Scanvorgang notwendig"-Hinweis. +class _SetCompleteHint extends StatelessWidget { + const _SetCompleteHint(); + + @override + Widget build(BuildContext context) { + final color = Colors.green.shade700; + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.green.withValues(alpha: 0.10), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.check_circle, size: 16, color: color), + const SizedBox(width: 6), + Flexible( + child: Text( + 'Komplett geladen', + style: TextStyle( + fontSize: 13, + color: color, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ); + } +} + /// Sektions-Kopf vor den Items eines Lagers. Visuell klar getrennt /// (Standardlager vs. Filiale): Standardlager ist der primäre /// Arbeitsplatz und damit neutral koloriert; Filial-Sections @@ -1166,6 +1219,7 @@ class _ItemRow extends StatelessWidget { required this.details, required this.onAction, this.suppressScanHint = false, + this.setParentComplete = false, }); final DeliveryItem item; @@ -1177,6 +1231,10 @@ class _ItemRow extends StatelessWidget { /// (scanbaren) Komponenten darunter sehr wohl gescannt werden. final bool suppressScanHint; + /// `true`, wenn dies ein Set-Kopf ist, dessen Komponenten alle fertig + /// geladen sind. Dann wird der (selbst nicht scanbare) Kopf grün dargestellt. + final bool setParentComplete; + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -1188,6 +1246,9 @@ class _ItemRow extends StatelessWidget { // Mengen-Zähler. Aus dem Artikel abgeleitet, damit ein in eine Lagergruppe // eingehängter nicht-scanbarer Set-Kopf automatisch korrekt rendert. final scanNotRequired = !(article?.scannable ?? false); + // Effektiv „fertig": eigener Scan-Status ODER (Set-Kopf, dessen Komponenten + // alle geladen sind) → der Kopf wird dann ebenfalls grün. + final effectiveDone = item.isDone || setParentComplete; final isExternalWarehouse = warehouse != null && !warehouse.isStandard; // Manueller Fallback-Button: nur für scanbare, noch offene Positionen // (nicht done/entfernt/pausiert) — analog dazu, was ein Barcode-Scan @@ -1224,7 +1285,7 @@ class _ItemRow extends StatelessWidget { leadingIcon = Icons.pause_circle_outline; leadingIconColor = Colors.orange.shade800; statusBadgeLabel = 'Pausiert'; - } else if (item.isDone) { + } else if (effectiveDone) { cardColor = Colors.green.withValues(alpha: 0.07); borderColor = Colors.green.withValues(alpha: 0.35); titleColor = Colors.green.shade700; @@ -1413,11 +1474,14 @@ class _ItemRow extends StatelessWidget { ), ), ], - // Dienstleistung (nicht-scanbar): Hinweis statt Scan/Manuell- - // Aktion. Steht an derselben Stelle wie der Manuell-Button. - // Bei Set-Köpfen (Parent-Artikel) bewusst unterdrückt — dort - // wäre „kein Scanvorgang notwendig" irreführend. - if (scanNotRequired && !suppressScanHint) ...[ + // Set-Kopf, dessen Komponenten alle geladen sind → grünes + // „Komplett geladen". Sonst (nicht-scanbare Position, KEIN + // Set-Kopf) der normale „Kein Scanvorgang notwendig"-Hinweis; + // bei Set-Köpfen ist der unterdrückt (irreführend). + if (scanNotRequired && setParentComplete) ...[ + const SizedBox(height: 8), + const _SetCompleteHint(), + ] else if (scanNotRequired && !suppressScanHint) ...[ const SizedBox(height: 8), const _ScanNotRequiredHint(), ],