Files
Holzleitner-Lieferservice-App/lib/widget/phase_banner.dart
Dennis Nemec 456fb59668 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)
2026-05-14 22:27:56 +02:00

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"),
),
],
),
),
),
);
}
}