feat(tour): Tour-Neuladen ueberall + Drawer in Leer-/Ladezustaenden

- PhaseStepper: Reload-Button (RefreshTour, Spinner waehrend Refresh)
- Beladen-Empty-State: 'Neu laden'-Button (LoadTour) + Hinweis 'keine Tour verfuegbar'
- Drawer + AppBar in TourEmpty/Lade-Branches (Beladen-Uebersicht, Lieferungen auswaehlen, Sortieren) -> kein Festsitzen ohne Logout

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-06-18 13:08:18 +02:00
parent 467f4b4ed2
commit a206636ed0
4 changed files with 91 additions and 6 deletions

View File

@ -8,6 +8,9 @@ import 'package:hl_lieferservice/feature/cars/bloc/cars_state.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/phase_bloc.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/phase_event.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/phase_state.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/tour_bloc.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/tour_event.dart';
import 'package:hl_lieferservice/feature/delivery/bloc/tour_state.dart';
import 'package:hl_lieferservice/feature/delivery/model/delivery_phase.dart';
/// Horizontaler Phasen-Stepper für die drei bzw. vier Schritte
@ -33,6 +36,10 @@ import 'package:hl_lieferservice/feature/delivery/model/delivery_phase.dart';
/// Fahrer beliebig zwischen besuchten Schritten hin- und herspringen.
/// * Noch nicht besuchte Phasen sind sperren (SnackBar-Hinweis).
/// * Über das Menü-Icon links wird der Drawer geöffnet (Fahrzeuge/Settings).
/// * Daneben sitzt ein Reload-Button, der über `RefreshTour` einen
/// Backend-Refresh anstößt (z. B. wenn ein Disponent eine Lieferung
/// nachgetragen oder umverteilt hat). Während ein Refresh läuft,
/// ersetzt ein kleiner Spinner das Icon und der Button ist gesperrt.
/// * Rechts steht das Plate des aktiv gewählten Fahrzeugs.
class PhaseStepper extends StatelessWidget {
const PhaseStepper({
@ -144,6 +151,7 @@ class PhaseStepper extends StatelessWidget {
tooltip: "Menü",
onPressed: () => Scaffold.of(context).openDrawer(),
),
_ReloadButton(onPrimary: onPrimary),
const Spacer(),
BlocBuilder<CarSelectBloc, CarSelectState>(
builder: (context, state) {
@ -395,3 +403,47 @@ class _Connector extends StatelessWidget {
);
}
}
/// Reload-Button in der oberen Reihe des [PhaseStepper]s. Feuert
/// `RefreshTour` am [TourBloc] — refresht im Hintergrund und behält den
/// aktuellen `TourLoaded`-Snapshot sichtbar (kein Flicker zurück auf den
/// Lade-Spinner). Während des Refreshs ersetzt eine kleine
/// Fortschrittsanzeige das Icon und der Button ist gesperrt, um
/// Doppel-Triggern zu verhindern.
class _ReloadButton extends StatelessWidget {
const _ReloadButton({required this.onPrimary});
final Color onPrimary;
@override
Widget build(BuildContext context) {
return BlocBuilder<TourBloc, TourState>(
// Nur neu rendern, wenn sich der Refresh-Status ändert — sonst
// läuft der Builder bei jedem Scan-Tick mit.
buildWhen: (prev, curr) {
final p = prev is TourLoaded && prev.isRefreshing;
final c = curr is TourLoaded && curr.isRefreshing;
return p != c || prev.runtimeType != curr.runtimeType;
},
builder: (context, state) {
final isRefreshing = state is TourLoaded && state.isRefreshing;
return IconButton(
tooltip: 'Tour aktualisieren',
onPressed: isRefreshing
? null
: () => context.read<TourBloc>().add(const RefreshTour()),
icon: isRefreshing
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: onPrimary,
),
)
: Icon(Icons.refresh, color: onPrimary),
);
},
);
}
}