feat(loading): Abschluss-Dialog 'Alles gescannt - Auslieferung starten?' im Scanner

Sobald im Beladen-Scanner der letzte Pflicht-Scan erledigt ist (alle eigenen,
aktiven Lieferungen im Standardlager fertig - gleiche Bedingung wie der
'Auslieferungs-Phase starten'-Gate der Uebersicht), erscheint einmalig ein
Dialog: bestaetigt 'alles gescannt' und fragt, ob die Auslieferung starten soll.
'Auslieferung starten' -> PhaseSet(ausliefern) + Scanner schliessen; 'Spaeter'
-> nur schliessen. BlocConsumer<TourBloc>-Listener + Einmal-Flag (Reset wenn
wieder etwas offen).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-06-18 16:07:22 +02:00
parent 9ec3bba047
commit 446cf73347

View File

@ -11,9 +11,12 @@ import 'package:hl_lieferservice/feature/car_selection/bloc/bloc.dart';
import 'package:hl_lieferservice/feature/car_selection/bloc/state.dart';
import 'package:hl_lieferservice/feature/cars/bloc/cars_bloc.dart';
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/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/loading/widget/reason_catalog.dart';
import 'package:hl_lieferservice/feature/loading/widget/reason_picker_sheet.dart';
import 'package:hl_lieferservice/widget/scanner/article_scanner_stripe.dart';
@ -57,6 +60,10 @@ class _LoadingCustomerPageState extends State<LoadingCustomerPage> {
/// vom (lebenslang einen) Scanner für die Barcode-Auflösung benutzt.
int _currentIndex = 0;
/// Verhindert, dass der „Alles gescannt"-Abschluss-Dialog mehrfach erscheint.
/// Wird zurückgesetzt, sobald wieder etwas zu beladen offen ist.
bool _completionPromptShown = false;
@override
void initState() {
super.initState();
@ -331,13 +338,78 @@ class _LoadingCustomerPageState extends State<LoadingCustomerPage> {
}
}
/// Reagiert auf jedes TourBloc-Update: Sind ALLE eigenen, **aktiven**
/// Lieferungen im Standardlager fertig beladen (dieselbe Bedingung wie der
/// „Auslieferungs-Phase starten"-Gate der Beladen-Übersicht)? Genau beim
/// Übergang „noch offen → alles fertig" (also nach dem letzten Pflicht-Scan)
/// erscheint einmalig der Abschluss-Dialog.
void _maybePromptLoadingComplete(
BuildContext context,
TourState state,
String carId,
) {
if (state is! TourLoaded) return;
final deliveries = _ownInLoadingOrder(context, state.details, carId);
final active =
deliveries.where((d) => d.state == DeliveryState.active).toList();
final allDone = active.isNotEmpty &&
active.every(state.details.standardWarehouseLoadingDone);
if (!allDone) {
// Wieder etwas offen (Item entfernt/zurückgesetzt) → Dialog darf erneut.
_completionPromptShown = false;
return;
}
if (_completionPromptShown) return;
_completionPromptShown = true;
_showLoadingCompleteDialog(carId);
}
/// Abschluss-Dialog der Beladung: weist darauf hin, dass alles gescannt ist,
/// und fragt, ob die Auslieferung gestartet werden soll. Bei „Ja" wird die
/// Phase auf `ausliefern` gesetzt (PhaseBloc, app-weit) und der Scanner
/// geschlossen — der Phasen-Router (`home`) zeigt dann die Auslieferungs-
/// Übersicht. „Später" schließt nur den Dialog (Start wie bisher über die
/// Übersicht möglich).
Future<void> _showLoadingCompleteDialog(String carId) async {
final start = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
icon: const Icon(Icons.check_circle, color: Colors.green, size: 40),
title: const Text('Alles gescannt'),
content: const Text(
'Alle zu beladenden Artikel sind gescannt. '
'Möchten Sie die Auslieferung starten?',
),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(false),
child: const Text('Später'),
),
FilledButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: const Text('Auslieferung starten'),
),
],
),
);
if (start != true || !mounted) return;
context
.read<PhaseBloc>()
.add(PhaseSet(carId: carId, phase: DeliveryPhase.ausliefern));
// Scanner schließen → zurück zum Phasen-Router, der nun 'ausliefern' zeigt.
Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<CarSelectBloc, CarSelectState>(
builder: (context, carState) {
final carId =
carState is CarSelectComplete ? carState.selectedCar.id : '';
return BlocBuilder<TourBloc, TourState>(
return BlocConsumer<TourBloc, TourState>(
listener: (context, tourState) =>
_maybePromptLoadingComplete(context, tourState, carId),
builder: (context, tourState) {
if (tourState is! TourLoaded) {
return const Scaffold(