import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hl_lieferservice/domain/entity/article.dart'; import 'package:hl_lieferservice/domain/entity/customer.dart'; import 'package:hl_lieferservice/domain/entity/delivery.dart'; import 'package:hl_lieferservice/domain/entity/delivery_item.dart'; import 'package:hl_lieferservice/domain/entity/tour_details.dart'; import 'package:hl_lieferservice/domain/entity/warehouse.dart'; import 'package:hl_lieferservice/feature/car_selection/bloc/bloc.dart'; import 'package:hl_lieferservice/feature/car_selection/bloc/state.dart'; import 'package:hl_lieferservice/feature/delivery/detail/presentation/delivery_detail_page.dart'; import 'package:hl_lieferservice/feature/delivery/overview/presentation/delivery_info.dart'; import 'package:hl_lieferservice/feature/delivery/pickup/presentation/filiale_pickup_scan_page.dart'; /// Entscheidet beim Tap auf eine Lieferung, wohin navigiert wird: /// /// * Aktive Lieferung mit noch offenen Filial-Artikeln → zuerst der /// Filial-Abhol-Scan-Screen. Der Fahrer ist an der Filiale und muss die /// Ware abscannen, bevor er ausliefern kann. Nach dem Scan kehrt er zur /// Übersicht zurück (die Lieferung verliert ihren Filial-Hinweis). /// * Sonst → direkt die Auslieferung (`DeliveryDetail`), die beim Kunden /// bearbeitet wird. /// /// Damit „schaltet" dieselbe Lieferung zustandsabhängig um, ohne dass es /// dafür einen eigenen Status braucht. void _openDelivery( BuildContext context, Delivery delivery, TourDetails details, ) { final needsPickup = delivery.state == DeliveryState.active && details.hasPendingExternalWarehouseItems(delivery); Navigator.of(context).push( MaterialPageRoute( builder: (_) => needsPickup ? FilialePickupScanPage(deliveryId: delivery.id) : DeliveryDetail(deliveryId: delivery.id), ), ); } /// Sektionen-Übersicht der Auslieferungs-Phase. /// /// Architektur-Schwester von `LoadingOverviewPage`: gleiche Sektions-Logik, /// gleicher Tile-Stil, aber andere Bucket-Semantik — wir sortieren nicht /// nach Beladestand, sondern nach Lebenszyklus der Lieferung: /// /// * Eine **prominente Karte** für die nächste anstehende Lieferung /// (großer Kundenname, Adresse, Uhrzeit, Sonderwünsche). Wenn dort noch /// Filial-Artikel offen sind, sagt sie dem Fahrer Klartext, dass er /// zuerst das Lager ansteuern muss — inkl. Artikel-Auflistung. /// * Sektionen darunter: `Offen`, `Pausiert`, `Fertig`, `Abgebrochen`. /// /// `assignedCarId` wird zur Filterung herangezogen — Multi-Car-Teams sollen /// nicht die Lieferungen der Kollegen sehen. class DeliveryOverview extends StatefulWidget { const DeliveryOverview({super.key, required this.details}); final TourDetails details; @override State createState() => _DeliveryOverviewState(); } class _DeliveryOverviewState extends State { String? _selectedCarId; @override void initState() { super.initState(); final carSelectState = context.read().state; if (carSelectState is CarSelectComplete) { _selectedCarId = carSelectState.selectedCar.id; } else { // Falls keine Car-Auswahl getroffen ist (z. B. Single-Car-Team ohne // expliziten Wechsel), das erstbeste zugewiesene Auto übernehmen. final assigned = widget.details.deliveries .map((d) => d.assignedCarId) .whereType() .toList(); _selectedCarId = assigned.isNotEmpty ? assigned.first : null; } } @override Widget build(BuildContext context) { return BlocListener( listener: (context, carState) { if (carState is CarSelectComplete) { setState(() => _selectedCarId = carState.selectedCar.id); } }, child: _DeliveryOverviewBody( details: widget.details, selectedCarId: _selectedCarId, ), ); } } class _DeliveryOverviewBody extends StatelessWidget { const _DeliveryOverviewBody({ required this.details, required this.selectedCarId, }); final TourDetails details; final String? selectedCarId; /// Sortiert nach `sortOrder` aufsteigend, weil das die vom Fahrer im /// Sortieren-Schritt festgelegte Reihenfolge ist und identisch zur /// Auslieferungs-Reihenfolge. List _ownDeliveriesInDeliveryOrder() { final all = details.deliveriesSorted; if (selectedCarId == null) return all; return all.where((d) => d.assignedCarId == selectedCarId).toList(); } @override Widget build(BuildContext context) { final deliveries = _ownDeliveriesInDeliveryOrder(); // Untere System-Navigationsleiste (Home/Zurück/Recent) freihalten, damit // das letzte Listenelement nicht dahinter rutscht. Es gibt in dieser // Phase keinen Bottom-Bar, der den Inset abdecken würde. final bottomInset = MediaQuery.viewPaddingOf(context).bottom; if (deliveries.isEmpty) { return ListView( padding: EdgeInsets.only(bottom: bottomInset), children: [ DeliveryInfo(details: details, selectedCarId: selectedCarId), const SizedBox(height: 48), const Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 32), child: Text( 'Keine Auslieferungen für dieses Fahrzeug.', textAlign: TextAlign.center, ), ), ), ], ); } // Lieferungen in Buckets aufteilen. Position-Nr. bezieht sich auf die // Auslieferungs-Reihenfolge (`sortOrder`) — bleibt sichtbar, auch wenn // die Karte in „Pausiert" / „Fertig" einsortiert wird. final active = <_DeliveryEntry>[]; final paused = <_DeliveryEntry>[]; final done = <_DeliveryEntry>[]; final canceled = <_DeliveryEntry>[]; for (int i = 0; i < deliveries.length; i++) { final entry = _DeliveryEntry(position: i + 1, delivery: deliveries[i]); switch (entry.delivery.state) { case DeliveryState.active: active.add(entry); case DeliveryState.held: paused.add(entry); case DeliveryState.completed: done.add(entry); case DeliveryState.canceled: canceled.add(entry); } } // Erste aktive Lieferung ist die „Nächste" — wird aus der Offen-Liste // herausgezogen und in eigener prominenter Karte gezeigt. Beim nächsten // Build (nach Status-Wechsel) rutscht automatisch die nächste nach. final _DeliveryEntry? nextUp = active.isEmpty ? null : active.removeAt(0); // Wenn unter der „Nächste Lieferung"-Karte sonst nichts käme (typisch: // es gibt nur diese eine Lieferung), einen dezenten Platzhalter zeigen, // damit der Bereich nicht leer wirkt. final nothingElseBelow = active.isEmpty && paused.isEmpty && done.isEmpty && canceled.isEmpty; return ListView( padding: EdgeInsets.only(bottom: 24 + bottomInset), children: [ DeliveryInfo(details: details, selectedCarId: selectedCarId), if (nextUp != null) _NextDeliverySection(entry: nextUp, details: details), if (nextUp != null && nothingElseBelow) const _NoFurtherDeliveriesHint(), if (active.isNotEmpty) _BucketSection( title: 'Offen', count: active.length, color: Theme.of(context) .colorScheme .primary .withValues(alpha: 0.55), entries: active, details: details, ), if (paused.isNotEmpty) _BucketSection( title: 'Pausiert', count: paused.length, color: Colors.orange.shade800, icon: Icons.pause_circle_outline, entries: paused, details: details, ), if (done.isNotEmpty) _BucketSection( title: 'Fertig', count: done.length, color: Colors.green.shade700, icon: Icons.check_circle_outline, entries: done, details: details, ), if (canceled.isNotEmpty) _BucketSection( title: 'Abgebrochen', count: canceled.length, color: Colors.red.shade700, icon: Icons.cancel_outlined, entries: canceled, details: details, ), ], ); } } /// Lieferung + Position innerhalb der Auslieferungs-Reihenfolge. Position /// überlebt das Aufsplitten in Buckets — der Fahrer sieht im Tile immer /// „seine" Nummer, egal in welcher Sektion. class _DeliveryEntry { const _DeliveryEntry({required this.position, required this.delivery}); final int position; final Delivery delivery; } /// Dezenter Platzhalter unter der „Nächste Lieferung"-Karte, wenn es keine /// weiteren Lieferungen gibt (z. B. nur eine Lieferung insgesamt) — damit der /// Bereich nicht leer wirkt. Bekommt dieselbe „Offen"-Zwischenüberschrift wie /// die anderen Sektionen (farbiger Balken + Titel + Zähler). class _NoFurtherDeliveriesHint extends StatelessWidget { const _NoFurtherDeliveriesHint(); @override Widget build(BuildContext context) { final theme = Theme.of(context); final headerColor = theme.colorScheme.primary.withValues(alpha: 0.55); final muted = theme.colorScheme.onSurfaceVariant; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header im selben Stil wie _BucketSection (Balken + Titel + Pill). Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 4), child: Row( children: [ Container( width: 4, height: 18, decoration: BoxDecoration( color: headerColor, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Text( 'Offen', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: headerColor, letterSpacing: 0.4, ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), decoration: BoxDecoration( color: headerColor.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), ), child: Text( '0', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: headerColor, ), ), ), ], ), ), Padding( padding: const EdgeInsets.fromLTRB(16, 4, 16, 4), child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHighest .withValues(alpha: 0.5), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(Icons.inbox_outlined, size: 18, color: muted), const SizedBox(width: 10), Expanded( child: Text( 'Keine weiteren offenen Lieferungen.', style: theme.textTheme.bodyMedium?.copyWith(color: muted), ), ), ], ), ), ), ], ); } } // ─── „Nächste Lieferung" prominent ────────────────────────────────────── /// Sektions-Header für „Nächste Lieferung" + die große Karte darunter. Der /// Header läuft im Primary-Akzent, damit das Auge sofort dort landet. class _NextDeliverySection extends StatelessWidget { const _NextDeliverySection({required this.entry, required this.details}); final _DeliveryEntry entry; final TourDetails details; @override Widget build(BuildContext context) { final color = Theme.of(context).colorScheme.primary; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 4), child: Row( children: [ Container( width: 4, height: 18, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), Icon(Icons.local_shipping_outlined, size: 16, color: color), const SizedBox(width: 6), Text( 'Nächste Lieferung', style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: color, letterSpacing: 0.4, ), ), ], ), ), _NextDeliveryCard(entry: entry, details: details), ], ); } } /// Große Highlight-Karte für die nächste anstehende Lieferung. Layout-Ziel: /// alle „Wer / Wohin / Wann"-Infos auf einen Blick, ohne dass der Fahrer /// in die Detail-Page muss. Pflicht-Hinweis bei offenen Filial-Items, /// damit er nicht losfährt, ohne vorher das Lager anzusteuern. class _NextDeliveryCard extends StatelessWidget { const _NextDeliveryCard({required this.entry, required this.details}); final _DeliveryEntry entry; final TourDetails details; @override Widget build(BuildContext context) { final theme = Theme.of(context); final delivery = entry.delivery; final customer = details.customerOf(delivery); final pendingExternal = details.pendingExternalWarehouseGroups(delivery); final hasPendingExternal = pendingExternal.isNotEmpty; return Card( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), elevation: 1, color: theme.colorScheme.primaryContainer.withValues(alpha: 0.55), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), side: BorderSide( color: theme.colorScheme.primary.withValues(alpha: 0.45), width: 1.2, ), ), child: InkWell( borderRadius: BorderRadius.circular(14), onTap: () => _openDelivery(context, delivery, details), child: Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _HeaderRow( position: entry.position, customer: customer, desiredTime: delivery.desiredTime, ), const SizedBox(height: 10), _InfoRow( icon: Icons.location_on_outlined, text: delivery.deliveryAddressSnapshot.oneLine, emphasized: true, ), if (delivery.specialAgreements != null && delivery.specialAgreements!.isNotEmpty) ...[ const SizedBox(height: 6), _InfoRow( icon: Icons.sticky_note_2_outlined, text: delivery.specialAgreements!, ), ], if (hasPendingExternal) ...[ const SizedBox(height: 12), _PendingExternalBanner( groups: pendingExternal, articleLookup: details.articleOf, ), ], if (delivery.prepaidAmount > 0) ...[ const SizedBox(height: 8), _InfoRow( icon: Icons.payments_outlined, text: 'Anzahlung: ${delivery.prepaidAmount.toStringAsFixed(2)} €', ), ], const SizedBox(height: 8), Align( alignment: Alignment.centerRight, child: TextButton.icon( onPressed: () => _openDelivery(context, delivery, details), icon: Icon( hasPendingExternal ? Icons.qr_code_scanner : Icons.arrow_forward, ), label: Text( hasPendingExternal ? 'Artikel aus Filiale scannen' : 'Details öffnen', ), ), ), ], ), ), ), ); } } class _HeaderRow extends StatelessWidget { const _HeaderRow({ required this.position, required this.customer, required this.desiredTime, }); final int position; final Customer? customer; final String? desiredTime; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ CircleAvatar( backgroundColor: theme.colorScheme.primary, foregroundColor: theme.colorScheme.onPrimary, radius: 20, child: Text( '$position', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( customer?.name ?? '⟨Unbekannter Kunde⟩', style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, ), ), if (desiredTime != null && desiredTime!.isNotEmpty) ...[ const SizedBox(height: 4), Row( children: [ Icon( Icons.access_time, size: 16, color: theme.colorScheme.primary, ), const SizedBox(width: 4), Text( desiredTime!, style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w600, color: theme.colorScheme.primary, ), ), ], ), ], ], ), ), ], ); } } class _InfoRow extends StatelessWidget { const _InfoRow({ required this.icon, required this.text, this.emphasized = false, }); final IconData icon; final String text; final bool emphasized; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 2), child: Icon( icon, size: 16, color: theme.colorScheme.onSurfaceVariant, ), ), const SizedBox(width: 6), Expanded( child: Text( text, style: TextStyle( fontSize: emphasized ? 14 : 13, fontWeight: emphasized ? FontWeight.w600 : FontWeight.w500, color: theme.colorScheme.onSurface, ), ), ), ], ); } } /// Roter Banner direkt auf der „Nächste Lieferung"-Karte. Listet die noch /// nicht beladenen Filial-Artikel auf, damit der Fahrer sofort sieht, /// dass er erst ins Lager muss — *und* welche Artikel ihn dort erwarten. /// Bewusst kein dezenter Hinweis: das ist die wichtigste Information auf /// dem Bildschirm, sobald sie zutrifft. class _PendingExternalBanner extends StatelessWidget { const _PendingExternalBanner({ required this.groups, required this.articleLookup, }); /// Filiale + offene Items pro Lager. final List<({Warehouse warehouse, List items})> groups; final Article? Function(String articleId) articleLookup; String _itemLabel(DeliveryItem item) { final article = articleLookup(item.articleId); final name = article?.name ?? '⟨Unbekannter Artikel⟩'; final number = article?.articleNumber ?? ''; final qty = item.requiredQuantity; if (number.isEmpty) return '$qty × $name'; return '$qty × $name ($number)'; } @override Widget build(BuildContext context) { final color = Colors.amber.shade800; return Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.amber.withValues(alpha: 0.18), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.amber.withValues(alpha: 0.7)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.warehouse_outlined, size: 18, color: color), const SizedBox(width: 6), Expanded( child: Text( 'Erst Artikel aus der Filiale holen!', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, color: color, ), ), ), ], ), const SizedBox(height: 8), for (final group in groups) ...[ Text( group.warehouse.name, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w700, color: color, letterSpacing: 0.3, ), ), const SizedBox(height: 2), for (final item in group.items) Padding( padding: const EdgeInsets.only(left: 4, bottom: 2), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '• ', style: TextStyle(color: color, fontSize: 13), ), Expanded( child: Text( _itemLabel(item), style: TextStyle( fontSize: 13, color: Colors.brown.shade900, ), ), ), ], ), ), const SizedBox(height: 4), ], ], ), ); } } // ─── Standard-Sektion + Tile ──────────────────────────────────────────── /// Generische Bucket-Sektion mit Farb-Akzent, Pill-Counter und Tiles. /// Visuelle Sprache identisch zur Beladen-Übersicht, damit der Fahrer /// keine zwei UI-Paradigmen lernen muss. class _BucketSection extends StatelessWidget { const _BucketSection({ required this.title, required this.count, required this.color, required this.entries, required this.details, this.icon, }); final String title; final int count; final Color color; final List<_DeliveryEntry> entries; final TourDetails details; final IconData? icon; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 4), child: Row( children: [ Container( width: 4, height: 18, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 8), if (icon != null) ...[ Icon(icon, size: 16, color: color), const SizedBox(width: 6), ], Text( title, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: color, letterSpacing: 0.4, ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 1, ), decoration: BoxDecoration( color: color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), ), child: Text( '$count', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: color, ), ), ), ], ), ), for (final entry in entries) _DeliveryTile(entry: entry, details: details), ], ); } } /// Kompakter Tile in den Sektionen unterhalb der „Nächste Lieferung"- /// Karte. Zeigt Position, Kundenname, Adresse, Uhrzeit (falls vorhanden), /// Status — und ein Filial-Badge, wenn dort noch Artikel offen sind. class _DeliveryTile extends StatelessWidget { const _DeliveryTile({required this.entry, required this.details}); final _DeliveryEntry entry; final TourDetails details; @override Widget build(BuildContext context) { final theme = Theme.of(context); final delivery = entry.delivery; final customer = details.customerOf(delivery); final pendingExternal = details.pendingExternalWarehouseGroups(delivery); final hasPendingExternal = pendingExternal.isNotEmpty; final style = _TileStyle.forState(theme, delivery.state); return Opacity( opacity: delivery.state == DeliveryState.canceled ? 0.65 : 1.0, child: Card( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), elevation: 0, color: style.cardColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide(color: style.borderColor), ), child: InkWell( borderRadius: BorderRadius.circular(12), onTap: () => _openDelivery(context, delivery, details), child: Padding( padding: const EdgeInsets.all(12), child: Row( children: [ CircleAvatar( backgroundColor: style.avatarColor, foregroundColor: theme.colorScheme.onPrimary, radius: 18, child: Text( '${entry.position}', style: const TextStyle(fontWeight: FontWeight.bold), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( customer?.name ?? '⟨Unbekannter Kunde⟩', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: style.titleColor, decoration: delivery.state == DeliveryState.canceled ? TextDecoration.lineThrough : TextDecoration.none, ), ), const SizedBox(height: 2), Text( delivery.deliveryAddressSnapshot.oneLine, style: TextStyle( fontSize: 12, color: theme.colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 2), child: Icon( style.statusIcon, size: 14, color: style.titleColor, ), ), const SizedBox(width: 4), Expanded( child: Text( style.statusLabel, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: style.titleColor, ), ), ), if (delivery.desiredTime != null && delivery.desiredTime!.isNotEmpty) ...[ const SizedBox(width: 8), Icon( Icons.access_time, size: 12, color: theme.colorScheme.onSurfaceVariant, ), const SizedBox(width: 2), Text( delivery.desiredTime!, style: TextStyle( fontSize: 12, color: theme.colorScheme.onSurfaceVariant, ), ), ], ], ), if (delivery.stateReason != null && delivery.stateReason!.isNotEmpty && (delivery.state == DeliveryState.held || delivery.state == DeliveryState.canceled)) Padding( padding: const EdgeInsets.only(left: 18, top: 2), child: Text( 'Grund: ${delivery.stateReason!}', style: TextStyle( fontSize: 11, color: style.titleColor, ), ), ), // Filial-Hinweis nur bei aktiven Lieferungen mit // offenen Items — pausiert/abgebrochen sind ohnehin // nicht in Arbeit, und „fertig" hat trivialerweise // keine offenen Items mehr. if (delivery.state == DeliveryState.active && hasPendingExternal) ...[ const SizedBox(height: 6), _PendingExternalBadge( warehouses: pendingExternal .map((g) => g.warehouse.name) .toList(growable: false), ), ], ], ), ), const Icon(Icons.chevron_right), ], ), ), ), ), ); } } /// Visuelle Sprache je nach Lebenszyklus der Lieferung. Bewusst getrennt, /// damit der Tile-Builder eine einzige `_TileStyle.forState(...)` baut und /// nicht in jeder Zeile wieder ein `switch` braucht. class _TileStyle { const _TileStyle({ required this.cardColor, required this.borderColor, required this.titleColor, required this.avatarColor, required this.statusLabel, required this.statusIcon, }); final Color cardColor; final Color borderColor; final Color titleColor; final Color avatarColor; final String statusLabel; final IconData statusIcon; factory _TileStyle.forState(ThemeData theme, DeliveryState state) { switch (state) { case DeliveryState.active: return _TileStyle( cardColor: theme.colorScheme.surfaceContainerLow, borderColor: Colors.transparent, titleColor: theme.colorScheme.onSurface, avatarColor: theme.colorScheme.primary, statusLabel: 'Offen', statusIcon: Icons.radio_button_unchecked, ); case DeliveryState.held: return _TileStyle( cardColor: Colors.orange.withValues(alpha: 0.07), borderColor: Colors.orange.withValues(alpha: 0.45), titleColor: Colors.orange.shade800, avatarColor: Colors.orange.shade800, statusLabel: 'Pausiert', statusIcon: Icons.pause_circle_outline, ); case DeliveryState.completed: return _TileStyle( cardColor: Colors.green.withValues(alpha: 0.07), borderColor: Colors.green.withValues(alpha: 0.35), titleColor: Colors.green.shade700, avatarColor: Colors.green.shade700, statusLabel: 'Abgeschlossen', statusIcon: Icons.check_circle_outline, ); case DeliveryState.canceled: return _TileStyle( cardColor: Colors.red.withValues(alpha: 0.06), borderColor: Colors.red.withValues(alpha: 0.35), titleColor: Colors.red.shade700, avatarColor: Colors.red.shade700, statusLabel: 'Abgebrochen', statusIcon: Icons.cancel_outlined, ); } } } /// Kompaktes Filial-Badge fürs Tile (Sektionen-Liste). Anders als das /// `_PendingExternalBanner` auf der „Nächste Lieferung"-Karte zeigt es /// nur die Lager-Namen — die Artikel-Detail-Auflistung würde den Tile /// optisch sprengen. class _PendingExternalBadge extends StatelessWidget { const _PendingExternalBadge({required this.warehouses}); final List warehouses; @override Widget build(BuildContext context) { final color = Colors.amber.shade800; final text = warehouses.isEmpty ? 'Filial-Artikel offen' : 'Erst aus Filiale holen: ${warehouses.join(", ")}'; return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.amber.withValues(alpha: 0.22), borderRadius: BorderRadius.circular(6), border: Border.all(color: Colors.amber.withValues(alpha: 0.7)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.warehouse_outlined, size: 14, color: color), const SizedBox(width: 4), Flexible( child: Text( text, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: color, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ], ), ); } }