fix: Fehlerbehandlung beim Laden und Meldungen überarbeiten

Reklamationen der Fahrer: Meldungen verdeckten Buttons, und nach dem
Abschluss stand „Lieferung nicht in der Tour gefunden" ohne Ausweg da.

Meldungen:
- Erfolgs-SnackBars entfernt, bei Erfolg bleibt der Spinner
- einheitliche, schwebende Meldungen statt Stau über den Buttons
- Server-Rohtexte durch verständliche Texte ersetzt (api_failure_text)

Laden:
- fehlt eine geöffnete Lieferung, wird die Tour einmal neu geholt; die
  Meldung kommt nur nach erfolgreichem Abruf ohne die Lieferung
- Endlos-Spinner auf Lieferdetail, Filial-Abholung und Beladen ersetzt
  durch Kopfzeile, Fehlergrund und „Erneut versuchen"
- Tour wird nach einem Tageswechsel nachgeladen, offene Seiten bleiben
  dabei unberührt
- Abschluss und Zuweisung überschreiben kein zwischenzeitliches Neuladen,
  alte Antworten drehen einen Abschluss nicht mehr zurück
- leere Server-Antworten gelten als Fehler statt als „keine Daten"

Anmeldung:
- Token-Erneuerung wartet höchstens 12 s, der native Aufruf läuft weiter
- App-Start ohne Netz zeigt einen Hinweis mit „Erneut versuchen"
- Fehlerseite der Fahrzeugauswahl bietet „Abmelden"

Außerdem Podfile.lock an die Plugins angeglichen. Tests für die neuen
Fehlerpfade ergänzt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-10-30 20:28:24 +01:00
parent 2ff331294e
commit bc95fe9577
40 changed files with 2063 additions and 365 deletions

View File

@ -0,0 +1,40 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
/// Übersetzt eine [DioException] in einen Fehlertext für den Fahrer.
///
/// Die `message` des Backends ist für Entwickler gedacht („validation:
/// delivery is not active; cannot complete") und erreicht die UI deshalb nie
/// direkt: [byServerText] ordnet bekannten Textbausteinen eine verständliche
/// Meldung zu, alles andere bekommt einen allgemeinen Text. Das Detail landet
/// im Log.
String describeApiFailure(
DioException e, {
required String operation,
Map<int, String> byStatus = const {},
Map<String, String> byServerText = const {},
}) {
final status = e.response?.statusCode;
final body = e.response?.data;
final serverText = body is Map ? body['message']?.toString() : null;
debugPrint('$operation fehlgeschlagen: HTTP $status, ${serverText ?? e.type}');
if (serverText != null) {
for (final entry in byServerText.entries) {
if (serverText.contains(entry.key)) return entry.value;
}
}
final known = byStatus[status];
if (known != null) return known;
if (status == 401) return 'Sitzung abgelaufen';
if (status == null) {
return switch (e.type) {
DioExceptionType.connectionTimeout ||
DioExceptionType.sendTimeout ||
DioExceptionType.receiveTimeout =>
'Der Server antwortet nicht. Bitte Verbindung prüfen',
_ => 'Server nicht erreichbar. Bitte Verbindung prüfen',
};
}
return '$operation fehlgeschlagen. Bitte erneut versuchen';
}