Phasenbasierte Lieferübersicht + Beladen-Flow, plus Migrationsplan für Rust-Backend

UI-Restructuring:
- TabBar in scan_page durch dedizierte Phasen ersetzt: Sortieren / Beladen / Ausliefern
- PhaseBloc + PhaseService leiten Phase aus Tour-/Item-States ab
- DeliverySelectionPage (ab 2 Autos) und DeliverySortPage als eigene Flows
- LoadingOverviewPage / LoadingCustomerPage für die Beladephase
- PhaseStepper-Widget im Home für Phasen-Anzeige
- Lager-Differenzierung (Standardlager 0 vs. Außenlager) via WarehouseBadge

Process-Stubs:
- ProcessRepository für Hold/Cancel/Sort/Assign-Flows (stub, bereit für Backend-Anbindung)

Doku:
- docs/BACKEND_MIGRATION.md: Phasenplan für Umstellung auf das neue
  Rust-Backend (OpenAPI-Generator, Keycloak OIDC, Clean-Arch-Layering)
This commit is contained in:
Dennis Nemec
2026-05-14 22:27:56 +02:00
parent ac6b03227d
commit 456fb59668
29 changed files with 5425 additions and 1015 deletions

View File

@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
/// Einheitliche Visualisierung für "Artikel aus Außenlager".
///
/// Wir nutzen bewusst nur eine Farbe (Amber) — selbst bei 510 möglichen
/// Lagern bleibt die Karte mit einem zusätzlichen Lagernamen als Text
/// lesbar. Mehrere Lager-Farben hätten zu Konfusion geführt und einzelne
/// Lager nicht eindeutig zugeordnet.
///
/// [warehouseNames] ist optional: ohne Namen erscheint nur "Außenlager".
class WarehouseBadge extends StatelessWidget {
const WarehouseBadge({
super.key,
this.warehouseNames = const [],
this.compact = false,
});
/// Die Distinct-Liste der Lagernamen (kommt typischerweise aus
/// Delivery.distinctExternalWarehouseNames).
final List<String> warehouseNames;
/// Kompakte Darstellung für enge Bereiche wie Sortier- und Scan-Listen.
final bool compact;
@override
Widget build(BuildContext context) {
final fg = Colors.amber.shade700;
final bg = Colors.amber.shade100;
final label = _buildLabel();
final iconSize = compact ? 14.0 : 16.0;
final textStyle = TextStyle(
color: fg,
fontWeight: FontWeight.w600,
fontSize: compact ? 11 : 12,
);
return Semantics(
label: "Artikel aus $label",
child: Container(
padding: EdgeInsets.symmetric(
horizontal: compact ? 6 : 8,
vertical: compact ? 2 : 4,
),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: fg.withValues(alpha: 0.4)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.warehouse, size: iconSize, color: fg),
SizedBox(width: compact ? 4 : 6),
Flexible(
child: Text(
label,
style: textStyle,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
);
}
String _buildLabel() {
if (warehouseNames.isEmpty) return "Außenlager";
if (warehouseNames.length == 1) return warehouseNames.first;
return warehouseNames.join(" + ");
}
}