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:
@ -374,6 +374,7 @@ class _DeliverySelectionPageState extends State<DeliverySelectionPage> {
|
|||||||
}
|
}
|
||||||
if (state is TourEmpty) {
|
if (state is TourEmpty) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
drawer: const HomeAppDrawer(),
|
||||||
appBar: AppBar(title: const Text('Lieferungen auswählen')),
|
appBar: AppBar(title: const Text('Lieferungen auswählen')),
|
||||||
body: const Center(
|
body: const Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -388,8 +389,11 @@ class _DeliverySelectionPageState extends State<DeliverySelectionPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (state is! TourLoaded) {
|
if (state is! TourLoaded) {
|
||||||
return const Scaffold(
|
// Drawer auch hier — Fahrer soll im Lade-Hang ausloggen können.
|
||||||
body: Center(child: CircularProgressIndicator()),
|
return Scaffold(
|
||||||
|
drawer: const HomeAppDrawer(),
|
||||||
|
appBar: AppBar(title: const Text('Lieferungen auswählen')),
|
||||||
|
body: const Center(child: CircularProgressIndicator()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -223,15 +223,21 @@ class _DeliverySortPageState extends State<DeliverySortPage> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
// Drawer in jedem Branch beibehalten — sonst sitzt der Fahrer im
|
||||||
|
// „Keine Tour heute"- oder Lade-Screen fest, ohne Zugriff auf
|
||||||
|
// Einstellungen / Logout.
|
||||||
if (state is TourEmpty) {
|
if (state is TourEmpty) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
drawer: const HomeAppDrawer(),
|
||||||
appBar: AppBar(title: const Text('Sortieren')),
|
appBar: AppBar(title: const Text('Sortieren')),
|
||||||
body: _emptyState(),
|
body: _emptyState(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (state is! TourLoaded) {
|
if (state is! TourLoaded) {
|
||||||
return const Scaffold(
|
return Scaffold(
|
||||||
body: Center(child: CircularProgressIndicator()),
|
drawer: const HomeAppDrawer(),
|
||||||
|
appBar: AppBar(title: const Text('Sortieren')),
|
||||||
|
body: const Center(child: CircularProgressIndicator()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ 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_bloc.dart';
|
||||||
import 'package:hl_lieferservice/feature/delivery/bloc/phase_event.dart';
|
import 'package:hl_lieferservice/feature/delivery/bloc/phase_event.dart';
|
||||||
import 'package:hl_lieferservice/feature/delivery/bloc/tour_bloc.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/bloc/tour_state.dart';
|
||||||
import 'package:hl_lieferservice/feature/delivery/model/delivery_phase.dart';
|
import 'package:hl_lieferservice/feature/delivery/model/delivery_phase.dart';
|
||||||
import 'package:hl_lieferservice/feature/delivery/overview/presentation/delivery_fail_page.dart';
|
import 'package:hl_lieferservice/feature/delivery/overview/presentation/delivery_fail_page.dart';
|
||||||
@ -74,8 +75,12 @@ class LoadingOverviewPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (tourState is! TourLoaded) {
|
if (tourState is! TourLoaded) {
|
||||||
return const Scaffold(
|
// Drawer auch im Ladezustand — analog zum TourEmpty-Branch,
|
||||||
body: Center(child: CircularProgressIndicator()),
|
// damit der Fahrer beim Hängen nicht ohne Logout dasitzt.
|
||||||
|
return Scaffold(
|
||||||
|
drawer: const HomeAppDrawer(),
|
||||||
|
appBar: AppBar(title: const Text('Beladung')),
|
||||||
|
body: const Center(child: CircularProgressIndicator()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,6 +871,24 @@ class _EmptyOverview extends StatelessWidget {
|
|||||||
'Keine Lieferungen zum Beladen',
|
'Keine Lieferungen zum Beladen',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Für heute ist aktuell keine Tour verfügbar.',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(color: scheme.onSurfaceVariant),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
// Erneut die heutige Tour vom Backend laden. `LoadTour` blendet
|
||||||
|
// währenddessen den Seiten-Ladeindikator ein (TourLoading-Zweig) und
|
||||||
|
// landet danach wieder hier (TourEmpty) oder in der Tour-Ansicht.
|
||||||
|
FilledButton.tonalIcon(
|
||||||
|
onPressed: () => context.read<TourBloc>().add(const LoadTour()),
|
||||||
|
icon: const Icon(Icons.refresh),
|
||||||
|
label: const Text('Neu laden'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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_bloc.dart';
|
||||||
import 'package:hl_lieferservice/feature/delivery/bloc/phase_event.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/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';
|
import 'package:hl_lieferservice/feature/delivery/model/delivery_phase.dart';
|
||||||
|
|
||||||
/// Horizontaler Phasen-Stepper für die drei bzw. vier Schritte
|
/// 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.
|
/// Fahrer beliebig zwischen besuchten Schritten hin- und herspringen.
|
||||||
/// * Noch nicht besuchte Phasen sind sperren (SnackBar-Hinweis).
|
/// * Noch nicht besuchte Phasen sind sperren (SnackBar-Hinweis).
|
||||||
/// * Über das Menü-Icon links wird der Drawer geöffnet (Fahrzeuge/Settings).
|
/// * Ü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.
|
/// * Rechts steht das Plate des aktiv gewählten Fahrzeugs.
|
||||||
class PhaseStepper extends StatelessWidget {
|
class PhaseStepper extends StatelessWidget {
|
||||||
const PhaseStepper({
|
const PhaseStepper({
|
||||||
@ -144,6 +151,7 @@ class PhaseStepper extends StatelessWidget {
|
|||||||
tooltip: "Menü",
|
tooltip: "Menü",
|
||||||
onPressed: () => Scaffold.of(context).openDrawer(),
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||||
),
|
),
|
||||||
|
_ReloadButton(onPrimary: onPrimary),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
BlocBuilder<CarSelectBloc, CarSelectState>(
|
BlocBuilder<CarSelectBloc, CarSelectState>(
|
||||||
builder: (context, state) {
|
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),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user