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)
122 lines
4.0 KiB
Dart
122 lines
4.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:hl_lieferservice/feature/delivery/model/delivery_phase.dart';
|
|
|
|
/// Schmaler Info-Banner für die aktuelle Phase.
|
|
///
|
|
/// Wurde durch den [PhaseStepper] als primäre Navigations-Anzeige ersetzt,
|
|
/// bleibt aber als kompakte Alternative für Stellen verfügbar, an denen
|
|
/// kein vollwertiger Stepper sinnvoll ist (z. B. Dialoge, Sub-Pages).
|
|
///
|
|
/// Hinweis: Damit "Schritt X von Y" stimmt, MUSS dem Banner die sichtbare
|
|
/// Phasen-Liste mitgegeben werden — diese ist team-spezifisch (Ein- vs.
|
|
/// Mehrauto-Team) und kann nicht hartkodiert werden.
|
|
class PhaseBanner extends StatelessWidget {
|
|
const PhaseBanner({
|
|
super.key,
|
|
required this.phase,
|
|
required this.visiblePhases,
|
|
this.onAdvance,
|
|
this.onBack,
|
|
this.advanceLabel,
|
|
});
|
|
|
|
final DeliveryPhase phase;
|
|
|
|
/// Die für den aktuellen Fahrer relevanten Phasen — bestimmt die Werte
|
|
/// für "Schritt X von Y" und muss konsistent zum [PhaseStepper] sein.
|
|
final List<DeliveryPhase> visiblePhases;
|
|
|
|
final VoidCallback? onAdvance;
|
|
final VoidCallback? onBack;
|
|
|
|
/// Optionaler Text für den Vorwärts-Button. Default ist "Phase abschließen".
|
|
final String? advanceLabel;
|
|
|
|
Color _color(BuildContext context) {
|
|
return switch (phase) {
|
|
DeliveryPhase.auswaehlen => Colors.blueGrey.shade100,
|
|
DeliveryPhase.sortieren => Colors.indigo.shade100,
|
|
DeliveryPhase.beladen => Theme.of(context).colorScheme.primaryContainer,
|
|
DeliveryPhase.ausliefern => Colors.green.shade100,
|
|
};
|
|
}
|
|
|
|
Color _foreground(BuildContext context) {
|
|
return switch (phase) {
|
|
DeliveryPhase.auswaehlen => Colors.blueGrey.shade800,
|
|
DeliveryPhase.sortieren => Colors.indigo.shade800,
|
|
DeliveryPhase.beladen =>
|
|
Theme.of(context).colorScheme.onPrimaryContainer,
|
|
DeliveryPhase.ausliefern => Colors.green.shade800,
|
|
};
|
|
}
|
|
|
|
IconData _icon() {
|
|
return switch (phase) {
|
|
DeliveryPhase.auswaehlen => Icons.checklist_outlined,
|
|
DeliveryPhase.sortieren => Icons.reorder,
|
|
DeliveryPhase.beladen => Icons.inventory_2_outlined,
|
|
DeliveryPhase.ausliefern => Icons.local_shipping_outlined,
|
|
};
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final fg = _foreground(context);
|
|
|
|
return Material(
|
|
color: _color(context),
|
|
child: SafeArea(
|
|
bottom: false,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
child: Row(
|
|
children: [
|
|
if (onBack != null)
|
|
IconButton(
|
|
onPressed: onBack,
|
|
icon: Icon(Icons.arrow_back, color: fg),
|
|
visualDensity: VisualDensity.compact,
|
|
tooltip: "Zurück",
|
|
)
|
|
else
|
|
const SizedBox(width: 8),
|
|
Icon(_icon(), color: fg, size: 20),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
phase.displayName,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w700,
|
|
color: fg,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
Text(
|
|
"Schritt ${phase.stepNumberIn(visiblePhases)} von ${visiblePhases.length}",
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: fg.withValues(alpha: 0.8),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (onAdvance != null)
|
|
TextButton(
|
|
onPressed: onAdvance,
|
|
style: TextButton.styleFrom(foregroundColor: fg),
|
|
child: Text(advanceLabel ?? "Phase abschließen"),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|