BIG FAT
This commit is contained in:
@ -9,16 +9,20 @@ import 'package:hl_lieferservice/feature/delivery/overview/service/distance_serv
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/service/reorder_service.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/util.dart';
|
||||
import 'package:hl_lieferservice/model/tour.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_event.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/exceptions.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
OperationBloc opBloc;
|
||||
AuthBloc authBloc;
|
||||
TourRepository tourRepository;
|
||||
StreamSubscription? _combinedSubscription;
|
||||
|
||||
TourBloc({required this.opBloc, required this.tourRepository})
|
||||
TourBloc({required this.opBloc, required this.authBloc, required this.tourRepository})
|
||||
: super(TourInitial()) {
|
||||
_combinedSubscription = CombineLatestStream.combine2(
|
||||
tourRepository.tour,
|
||||
@ -61,17 +65,23 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
@override
|
||||
Future<void> close() {
|
||||
_combinedSubscription?.cancel();
|
||||
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _handleError(Object e, String fallbackMessage) {
|
||||
if (e is UserUnauthorized) {
|
||||
authBloc.add(SessionExpiredEvent());
|
||||
} else {
|
||||
opBloc.add(FailOperation(message: fallbackMessage));
|
||||
}
|
||||
}
|
||||
|
||||
void _setArticleAmount(
|
||||
SetArticleAmountEvent event,
|
||||
Emitter<TourState> emit,
|
||||
) async {
|
||||
final currentState = state;
|
||||
if (currentState is TourLoaded) {
|
||||
opBloc.add(LoadOperation());
|
||||
try {
|
||||
await tourRepository.setArticleAmount(
|
||||
event.deliveryId,
|
||||
@ -79,15 +89,9 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
event.amount,
|
||||
event.reason,
|
||||
);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Ändern der Menge des Artikels"),
|
||||
);
|
||||
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Ändern der Menge des Artikels");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,7 +130,6 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
Emitter<TourState> emit,
|
||||
) async {
|
||||
Map<String, double> distances = {};
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
emit(TourRequestingDistances(tour: event.tour, payments: event.payments));
|
||||
|
||||
@ -135,7 +138,7 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
distances[delivery.id] = await DistanceService.getDistanceByRoad(
|
||||
delivery.customer.address.toString(),
|
||||
);
|
||||
} catch (e,st) {
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Laden der Distanz: $e");
|
||||
debugPrint("$st");
|
||||
|
||||
@ -145,7 +148,6 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
}
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
// If an error occurred, then the distances will be empty
|
||||
// If the distances are empty then they shouldn't be displayed
|
||||
add(
|
||||
@ -251,17 +253,11 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
) async {
|
||||
final currentState = state;
|
||||
if (currentState is TourLoaded) {
|
||||
opBloc.add(LoadOperation());
|
||||
try {
|
||||
await tourRepository.reactivateDelivery(event.deliveryId);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Zurückstellen der Lieferung"),
|
||||
);
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Zurückstellen der Lieferung");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -269,17 +265,11 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
void _holdDelivery(HoldDeliveryEvent event, Emitter<TourState> emit) async {
|
||||
final currentState = state;
|
||||
if (currentState is TourLoaded) {
|
||||
opBloc.add(LoadOperation());
|
||||
try {
|
||||
await tourRepository.holdDelivery(event.deliveryId);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Zurückstellen der Lieferung"),
|
||||
);
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Zurückstellen der Lieferung");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -290,24 +280,17 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
) async {
|
||||
final currentState = state;
|
||||
if (currentState is TourLoaded) {
|
||||
opBloc.add(LoadOperation());
|
||||
try {
|
||||
await tourRepository.cancelDelivery(event.deliveryId);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Zurückstellen der Lieferung"),
|
||||
);
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Stornieren der Lieferung");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _scan(ScanArticleEvent event, Emitter<TourState> emit) async {
|
||||
final currentState = state;
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
if (currentState is TourLoaded) {
|
||||
try {
|
||||
@ -333,9 +316,8 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
break;
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint("FEHLER beim Scannen eines Artikels: $e");
|
||||
debugPrint("$st");
|
||||
opBloc.add(FailOperation(message: "Fehler beim Scannen des Artikels"));
|
||||
debugPrint("FEHLER beim Scannen eines Artikels: $e $st");
|
||||
_handleError(e, "Fehler beim Scannen des Artikels");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -347,17 +329,15 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is TourLoaded) {
|
||||
opBloc.add(LoadOperation());
|
||||
try {
|
||||
await tourRepository.scanArticle(
|
||||
event.deliveryId,
|
||||
event.carId,
|
||||
event.internalArticleId,
|
||||
);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint(st.toString());
|
||||
opBloc.add(FailOperation(message: "Fehler beim Scannen des Artikels"));
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Scannen des Artikels");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -365,34 +345,27 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
Future<void> _assignCar(AssignCarEvent event, Emitter<TourState> emit) async {
|
||||
final currentState = state;
|
||||
if (currentState is TourLoaded) {
|
||||
opBloc.add(LoadOperation());
|
||||
try {
|
||||
await tourRepository.assignCar(event.deliveryId, event.carId);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint(st.toString());
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Zuweisen des Fahrzeugs"),
|
||||
);
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Zuweisen des Fahrzeugs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _load(LoadTour event, Emitter<TourState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
try {
|
||||
emit(TourLoading());
|
||||
await tourRepository.loadTourOfToday(event.teamId);
|
||||
await tourRepository.loadPaymentOptions();
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e) {
|
||||
// go to the error state in order to give the user the chance
|
||||
// to reload if necessary.
|
||||
if (e is UserUnauthorized) {
|
||||
authBloc.add(SessionExpiredEvent());
|
||||
return;
|
||||
}
|
||||
emit(TourLoadingFailed());
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Laden der heutigen Fahrten"),
|
||||
);
|
||||
opBloc.add(FailOperation(message: "Fehler beim Laden der heutigen Fahrten"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -401,7 +374,6 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
Emitter<TourState> emit,
|
||||
) async {
|
||||
final currentState = state;
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
if (currentState is TourLoaded) {
|
||||
try {
|
||||
@ -415,11 +387,9 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
);
|
||||
|
||||
await tourRepository.finishDelivery(event.deliveryId);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
opBloc.add(FailOperation(message: "Failed to update delivery"));
|
||||
debugPrint(st.toString());
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Abschließen der Lieferung");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -429,14 +399,10 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
Emitter<TourState> emit,
|
||||
) async {
|
||||
try {
|
||||
opBloc.add(LoadOperation());
|
||||
await tourRepository.updatePayment(event.deliveryId, event.payment);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint(st.toString());
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Aktualisieren des Betrags"),
|
||||
);
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Aktualisieren des Betrags");
|
||||
}
|
||||
}
|
||||
|
||||
@ -445,18 +411,14 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
Emitter<TourState> emit,
|
||||
) async {
|
||||
try {
|
||||
opBloc.add(LoadOperation());
|
||||
await tourRepository.updateOption(
|
||||
event.deliveryId,
|
||||
event.key,
|
||||
event.value,
|
||||
);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("$st");
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Aktualisieren der Optionen"),
|
||||
);
|
||||
debugPrint("$e $st");
|
||||
_handleError(e, "Fehler beim Aktualisieren der Optionen");
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,26 +426,15 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
UpdateDiscountEvent event,
|
||||
Emitter<TourState> emit,
|
||||
) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
opBloc.add(FinishOperation());
|
||||
await tourRepository.updateDiscount(
|
||||
event.deliveryId,
|
||||
event.reason,
|
||||
event.value,
|
||||
);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint(
|
||||
"Fehler beim Hinzufügen eins Discounts zur Lieferung: ${event.deliveryId}:",
|
||||
);
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Hinzufügen des Discounts: $e"),
|
||||
);
|
||||
debugPrint("Fehler beim Aktualisieren des Discounts: $e $st");
|
||||
_handleError(e, "Fehler beim Aktualisieren des Discounts");
|
||||
}
|
||||
}
|
||||
|
||||
@ -491,51 +442,28 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
RemoveDiscountEvent event,
|
||||
Emitter<TourState> emit,
|
||||
) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await tourRepository.removeDiscount(event.deliveryId);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint(
|
||||
"Fehler beim Löschen des Discounts der Lieferung: ${event.deliveryId}:",
|
||||
);
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Löschen des Discounts: $e"),
|
||||
);
|
||||
debugPrint("Fehler beim Löschen des Discounts: $e $st");
|
||||
_handleError(e, "Fehler beim Löschen des Discounts");
|
||||
}
|
||||
}
|
||||
|
||||
void _addDiscount(AddDiscountEvent event, Emitter<TourState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await tourRepository.addDiscount(
|
||||
event.deliveryId,
|
||||
event.reason,
|
||||
event.value,
|
||||
);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint(
|
||||
"Fehler beim Hinzufügen eins Discounts zur Lieferung: ${event.deliveryId}:",
|
||||
);
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Hinzufügen des Discounts: $e"),
|
||||
);
|
||||
debugPrint("Fehler beim Hinzufügen des Discounts: $e $st");
|
||||
_handleError(e, "Fehler beim Hinzufügen des Discounts");
|
||||
}
|
||||
}
|
||||
|
||||
void _unscan(UnscanArticleEvent event, Emitter<TourState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await tourRepository.unscan(
|
||||
event.deliveryId,
|
||||
@ -543,29 +471,18 @@ class TourBloc extends Bloc<TourEvent, TourState> {
|
||||
event.newAmount,
|
||||
event.reason,
|
||||
);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Unscan des Artikels: ${event.articleId}:");
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(FailOperation(message: "Fehler beim Unscan des Artikels: $e"));
|
||||
debugPrint("Fehler beim Unscan des Artikels ${event.articleId}: $e $st");
|
||||
_handleError(e, "Fehler beim Unscan des Artikels");
|
||||
}
|
||||
}
|
||||
|
||||
void _resetAmount(ResetScanAmountEvent event, Emitter<TourState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await tourRepository.resetScan(event.articleId, event.deliveryId);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Unscan des Artikels: ${event.articleId}:");
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(FailOperation(message: "Fehler beim Zurücksetzen: $e"));
|
||||
debugPrint("Fehler beim Zurücksetzen Artikel ${event.articleId}: $e $st");
|
||||
_handleError(e, "Fehler beim Zurücksetzen");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,9 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_event.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/exceptions.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
@ -15,6 +18,7 @@ import 'package:hl_lieferservice/feature/delivery/detail/repository/note_reposit
|
||||
class NoteBloc extends Bloc<NoteEvent, NoteState> {
|
||||
final NoteRepository repository;
|
||||
final OperationBloc opBloc;
|
||||
final AuthBloc authBloc;
|
||||
final String deliveryId;
|
||||
|
||||
StreamSubscription? _combinedSubscription;
|
||||
@ -22,6 +26,7 @@ class NoteBloc extends Bloc<NoteEvent, NoteState> {
|
||||
NoteBloc({
|
||||
required this.repository,
|
||||
required this.opBloc,
|
||||
required this.authBloc,
|
||||
required this.deliveryId,
|
||||
}) : super(NoteInitial()) {
|
||||
_combinedSubscription = CombineLatestStream.combine3(
|
||||
@ -60,10 +65,17 @@ class NoteBloc extends Bloc<NoteEvent, NoteState> {
|
||||
@override
|
||||
Future<void> close() {
|
||||
_combinedSubscription?.cancel();
|
||||
|
||||
return super.close();
|
||||
}
|
||||
|
||||
void _handleError(Object e, String fallbackMessage) {
|
||||
if (e is UserUnauthorized) {
|
||||
authBloc.add(SessionExpiredEvent());
|
||||
} else {
|
||||
opBloc.add(FailOperation(message: fallbackMessage));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _dataUpdated(DataUpdated event, Emitter<NoteState> emit) async {
|
||||
emit(
|
||||
NoteLoaded(
|
||||
@ -82,32 +94,21 @@ class NoteBloc extends Bloc<NoteEvent, NoteState> {
|
||||
RemoveImageNote event,
|
||||
Emitter<NoteState> emit,
|
||||
) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await repository.deleteImage(event.deliveryId, event.objectId);
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Löschen des Bildes: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
debugPrint("Fehler beim Löschen des Bildes: $e $st");
|
||||
_handleError(e, "Fehler beim Löschen des Bildes");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _upload(AddImageNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
Uint8List imageBytes = await event.file.readAsBytes();
|
||||
await repository.addImage(event.deliveryId, imageBytes);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen des Bildes: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
debugPrint("Fehler beim Hinzufügen des Bildes: $e $st");
|
||||
_handleError(e, "Fehler beim Hinzufügen des Bildes");
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,61 +118,41 @@ class NoteBloc extends Bloc<NoteEvent, NoteState> {
|
||||
try {
|
||||
await repository.loadNotes(event.delivery.id);
|
||||
await repository.loadTemplates();
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Herunterladen der Notizen: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Notizen konnten nicht heruntergeladen werden."),
|
||||
);
|
||||
|
||||
debugPrint("Fehler beim Herunterladen der Notizen: $e $st");
|
||||
if (e is UserUnauthorized) {
|
||||
authBloc.add(SessionExpiredEvent());
|
||||
return;
|
||||
}
|
||||
opBloc.add(FailOperation(message: "Notizen konnten nicht heruntergeladen werden."));
|
||||
emit.call(NoteLoadingFailed());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _add(AddNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await repository.addNote(event.deliveryId, event.note);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen der Notiz: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
debugPrint("Fehler beim Hinzufügen der Notiz: $e $st");
|
||||
_handleError(e, "Fehler beim Hinzufügen der Notiz");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _edit(EditNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await repository.editNote(event.noteId, event.content);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen der Notiz: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
debugPrint("Fehler beim Editieren der Notiz: $e $st");
|
||||
_handleError(e, "Fehler beim Editieren der Notiz");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _remove(RemoveNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
await repository.deleteNote(event.noteId);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen der Notiz: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Notizen konnte nicht gelöscht werden."),
|
||||
);
|
||||
debugPrint("Fehler beim Löschen der Notiz: $e $st");
|
||||
_handleError(e, "Notiz konnte nicht gelöscht werden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../../bloc/tour_bloc.dart';
|
||||
import '../../../bloc/tour_state.dart';
|
||||
|
||||
enum _StatusAction { hold, cancel, reactivate }
|
||||
|
||||
class DeliveryStepInfo extends StatefulWidget {
|
||||
final Delivery delivery;
|
||||
|
||||
@ -39,76 +41,73 @@ class _DeliveryStepInfo extends State<DeliveryStepInfo> {
|
||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
|
||||
Widget _deliveryStatusChangeActions() {
|
||||
List<Widget> actions = [];
|
||||
Widget _statusOverflow() {
|
||||
final state = widget.delivery.state;
|
||||
final List<PopupMenuEntry<_StatusAction>> entries;
|
||||
|
||||
if (widget.delivery.state == DeliveryState.ongoing) {
|
||||
actions = [
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
context.read<TourBloc>().add(
|
||||
HoldDeliveryEvent(deliveryId: widget.delivery.id),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.change_circle,
|
||||
color: Colors.orangeAccent,
|
||||
size: 42,
|
||||
),
|
||||
),
|
||||
Text("Zurückstellen"),
|
||||
],
|
||||
if (state == DeliveryState.ongoing) {
|
||||
entries = const [
|
||||
PopupMenuItem(
|
||||
value: _StatusAction.hold,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.change_circle, color: Colors.orangeAccent),
|
||||
SizedBox(width: 12),
|
||||
Text("Zurückstellen"),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
context.read<TourBloc>().add(
|
||||
CancelDeliveryEvent(deliveryId: widget.delivery.id),
|
||||
);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
//style: IconButton.styleFrom(backgroundColor: Colors.red),
|
||||
icon: Icon(Icons.cancel, color: Colors.red, size: 42),
|
||||
),
|
||||
Text("Abbrechen"),
|
||||
],
|
||||
PopupMenuItem(
|
||||
value: _StatusAction.cancel,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.cancel, color: Colors.red),
|
||||
SizedBox(width: 12),
|
||||
Text("Abbrechen"),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
} else {
|
||||
entries = const [
|
||||
PopupMenuItem(
|
||||
value: _StatusAction.reactivate,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.published_with_changes, color: Colors.blueAccent),
|
||||
SizedBox(width: 12),
|
||||
Text("Reaktivieren"),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if (widget.delivery.state == DeliveryState.canceled ||
|
||||
widget.delivery.state == DeliveryState.onhold ||
|
||||
widget.delivery.state == DeliveryState.finished) {
|
||||
actions = [
|
||||
Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
context.read<TourBloc>().add(
|
||||
ReactivateDeliveryEvent(deliveryId: widget.delivery.id),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.published_with_changes,
|
||||
color: Colors.blueAccent,
|
||||
size: 42
|
||||
),
|
||||
),
|
||||
Text("Reaktivieren"),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: actions,
|
||||
return PopupMenuButton<_StatusAction>(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
tooltip: "Status ändern",
|
||||
itemBuilder: (context) => entries,
|
||||
onSelected: (action) {
|
||||
switch (action) {
|
||||
case _StatusAction.hold:
|
||||
context.read<TourBloc>().add(
|
||||
HoldDeliveryEvent(deliveryId: widget.delivery.id),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
break;
|
||||
case _StatusAction.cancel:
|
||||
context.read<TourBloc>().add(
|
||||
CancelDeliveryEvent(deliveryId: widget.delivery.id),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
break;
|
||||
case _StatusAction.reactivate:
|
||||
context.read<TourBloc>().add(
|
||||
ReactivateDeliveryEvent(deliveryId: widget.delivery.id),
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -119,55 +118,46 @@ class _DeliveryStepInfo extends State<DeliveryStepInfo> {
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
IconButton.filled(
|
||||
onPressed:
|
||||
widget.delivery.contactPerson?.phoneNumber != null
|
||||
? () async {
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final phone = widget.delivery.contactPerson?.phoneNumber;
|
||||
final bool hasPhone = phone != null && phone.isNotEmpty;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton.filled(
|
||||
onPressed: hasPhone
|
||||
? () async {
|
||||
await launchUrl(
|
||||
Uri(
|
||||
scheme: "tel",
|
||||
path:
|
||||
widget
|
||||
.delivery
|
||||
.contactPerson
|
||||
?.phoneNumber!,
|
||||
),
|
||||
Uri(scheme: "tel", path: phone),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
icon: Icon(Icons.phone),
|
||||
),
|
||||
Text("Anrufen"),
|
||||
],
|
||||
),
|
||||
|
||||
Column(
|
||||
children: [
|
||||
IconButton.filled(
|
||||
onPressed: () {
|
||||
_launchMapsUrl("google");
|
||||
},
|
||||
icon: Icon(Icons.map_outlined),
|
||||
),
|
||||
Text("Google Maps"),
|
||||
],
|
||||
),
|
||||
],
|
||||
: null,
|
||||
icon: const Icon(Icons.phone),
|
||||
),
|
||||
const Text("Anrufen"),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Divider(),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton.filled(
|
||||
onPressed: () => _launchMapsUrl("google"),
|
||||
icon: const Icon(Icons.map_outlined),
|
||||
),
|
||||
const Text("Google Maps"),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
_deliveryStatusChangeActions(),
|
||||
_statusOverflow(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -176,6 +166,16 @@ class _DeliveryStepInfo extends State<DeliveryStepInfo> {
|
||||
}
|
||||
|
||||
Widget _customerInformation() {
|
||||
final phone = widget.delivery.contactPerson?.phoneNumber;
|
||||
final String phoneText = (phone != null && phone.isNotEmpty)
|
||||
? phone
|
||||
: "keine Nummer angegeben";
|
||||
|
||||
final email = widget.delivery.customer.email;
|
||||
final String emailText = (email != null && email.isNotEmpty)
|
||||
? email
|
||||
: "keine E-Mail angegeben";
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Card(
|
||||
@ -228,9 +228,24 @@ class _DeliveryStepInfo extends State<DeliveryStepInfo> {
|
||||
Icon(Icons.phone, color: Theme.of(context).primaryColor),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
widget.delivery.contactPerson?.phoneNumber.toString() ??
|
||||
"",
|
||||
child: Text(phoneText),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 15),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.mail, color: Theme.of(context).primaryColor),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
emailText,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -275,28 +290,66 @@ class _DeliveryStepInfo extends State<DeliveryStepInfo> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _deliveryAgreements() {
|
||||
Widget _agreementsAndDesiredTime() {
|
||||
String agreements = "keine Vereinbarungen getroffen!";
|
||||
if (widget.delivery.specialAgreements != null &&
|
||||
widget.delivery.specialAgreements != "") {
|
||||
agreements = widget.delivery.specialAgreements!;
|
||||
}
|
||||
|
||||
final desiredTime = widget.delivery.desiredTime;
|
||||
final bool hasDesiredTime = desiredTime != null && desiredTime.isNotEmpty;
|
||||
final primary = Theme.of(context).primaryColor;
|
||||
|
||||
return Card(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(15),
|
||||
child: Icon(
|
||||
Icons.warning,
|
||||
color: Theme.of(context).primaryColor,
|
||||
size: 28,
|
||||
if (hasDesiredTime) ...[
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Icon(Icons.schedule, color: primary, size: 28),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Wunschtermin",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
desiredTime,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(height: 24),
|
||||
],
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Icon(Icons.warning, color: primary, size: 28),
|
||||
),
|
||||
Expanded(child: Text(agreements)),
|
||||
],
|
||||
),
|
||||
Expanded(child: Text(agreements)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -330,7 +383,7 @@ class _DeliveryStepInfo extends State<DeliveryStepInfo> {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: _deliveryAgreements(),
|
||||
child: _agreementsAndDesiredTime(),
|
||||
),
|
||||
|
||||
Padding(
|
||||
|
||||
@ -275,10 +275,14 @@ class NoteService {
|
||||
LocalDocuFrameConfiguration config = getConfig();
|
||||
|
||||
return urls.map((url) async {
|
||||
return (await http.get(
|
||||
final response = await http.get(
|
||||
Uri.parse("${config.backendUrl}$url"),
|
||||
headers: getSessionOrThrow(),
|
||||
)).bodyBytes;
|
||||
);
|
||||
if (response.statusCode == HttpStatus.unauthorized) {
|
||||
throw UserUnauthorized();
|
||||
}
|
||||
return response.bodyBytes;
|
||||
}).toList();
|
||||
} catch (e, st) {
|
||||
debugPrint("An error occured:");
|
||||
|
||||
@ -1,80 +1,86 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart' show DeliveryState;
|
||||
import 'package:hl_lieferservice/model/tour.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class DeliveryInfo extends StatelessWidget {
|
||||
final Tour tour;
|
||||
final int? selectedCarId;
|
||||
|
||||
const DeliveryInfo({super.key, required this.tour});
|
||||
const DeliveryInfo({super.key, required this.tour, this.selectedCarId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String date = DateFormat("dd.MM.yyyy").format(tour.date);
|
||||
String amountDeliveries = tour.deliveries.length.toString();
|
||||
final String date = DateFormat("dd.MM.yyyy").format(tour.date);
|
||||
final relevantDeliveries = selectedCarId != null
|
||||
? tour.deliveries.where((d) => d.carId == selectedCarId).toList()
|
||||
: tour.deliveries;
|
||||
final total = relevantDeliveries.length;
|
||||
final done = relevantDeliveries
|
||||
.where((d) => d.state == DeliveryState.finished)
|
||||
.length;
|
||||
final progress = total > 0 ? done / total : 0.0;
|
||||
final allDone = total > 0 && done == total;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Text(
|
||||
"Informationen",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Card(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Card(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.calendar_month),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: Text("Datum"),
|
||||
),
|
||||
],
|
||||
const Icon(Icons.calendar_month),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 5),
|
||||
child: Text("Datum"),
|
||||
),
|
||||
Text(date),
|
||||
],
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 15),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.local_shipping_outlined),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: Text("Lieferungen"),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(amountDeliveries),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(date),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.local_shipping_outlined),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 5),
|
||||
child: Text("Lieferungen"),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text("$done / $total"),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: LinearProgressIndicator(
|
||||
value: progress,
|
||||
minHeight: 6,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
allDone ? Colors.green : Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/delivery_detail_page.dart';
|
||||
|
||||
@ -18,60 +19,132 @@ class DeliveryListItem extends StatelessWidget {
|
||||
required this.distance,
|
||||
});
|
||||
|
||||
Widget _leading(BuildContext context) {
|
||||
if (delivery.state == DeliveryState.finished) {
|
||||
return Icon(Icons.check_circle, color: Colors.green);
|
||||
}
|
||||
|
||||
if (delivery.state == DeliveryState.canceled) {
|
||||
return Icon(Icons.cancel_rounded, color: Colors.red);
|
||||
}
|
||||
|
||||
if (delivery.state == DeliveryState.onhold) {
|
||||
return Icon(Icons.pause_circle, color: Colors.orange);
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Icon(Icons.location_on, color: Theme.of(context).primaryColor),
|
||||
Text("${distance.toStringAsFixed(2)}km"),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _goToDelivery(BuildContext context) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(context) => BlocProvider(
|
||||
create:
|
||||
(context) => NoteBloc(
|
||||
deliveryId: delivery.id,
|
||||
opBloc: context.read<OperationBloc>(),
|
||||
repository: NoteRepository(
|
||||
service: NoteService(),
|
||||
),
|
||||
),
|
||||
|
||||
child: DeliveryDetail(deliveryId: delivery.id),
|
||||
),
|
||||
builder: (context) => BlocProvider(
|
||||
create: (context) => NoteBloc(
|
||||
deliveryId: delivery.id,
|
||||
opBloc: context.read<OperationBloc>(),
|
||||
authBloc: context.read<AuthBloc>(),
|
||||
repository: NoteRepository(service: NoteService()),
|
||||
),
|
||||
child: DeliveryDetail(deliveryId: delivery.id),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
(Color, Color, IconData, String) _stateStyle(BuildContext context) {
|
||||
switch (delivery.state) {
|
||||
case DeliveryState.finished:
|
||||
return (
|
||||
Colors.green.withValues(alpha: 0.07),
|
||||
Colors.green.withValues(alpha: 0.35),
|
||||
Icons.check_circle_rounded,
|
||||
"Abgeschlossen",
|
||||
);
|
||||
case DeliveryState.canceled:
|
||||
return (
|
||||
Colors.red.withValues(alpha: 0.07),
|
||||
Colors.red.withValues(alpha: 0.35),
|
||||
Icons.cancel_rounded,
|
||||
"Storniert",
|
||||
);
|
||||
case DeliveryState.onhold:
|
||||
return (
|
||||
Colors.orange.withValues(alpha: 0.07),
|
||||
Colors.orange.withValues(alpha: 0.35),
|
||||
Icons.pause_circle_rounded,
|
||||
"Pausiert",
|
||||
);
|
||||
case DeliveryState.ongoing:
|
||||
return (
|
||||
Theme.of(context).colorScheme.surfaceContainerLow,
|
||||
Colors.transparent,
|
||||
Icons.local_shipping_outlined,
|
||||
"${distance.toStringAsFixed(1)} km",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
delivery.customer.name,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
final (cardColor, borderColor, icon, statusLabel) = _stateStyle(context);
|
||||
final isOngoing = delivery.state == DeliveryState.ongoing;
|
||||
|
||||
final iconColor = switch (delivery.state) {
|
||||
DeliveryState.finished => Colors.green,
|
||||
DeliveryState.canceled => Colors.red,
|
||||
DeliveryState.onhold => Colors.orange,
|
||||
DeliveryState.ongoing => Theme.of(context).primaryColor,
|
||||
};
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
elevation: 0,
|
||||
color: cardColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: BorderSide(color: borderColor),
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () => _goToDelivery(context),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: iconColor, size: 28),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
delivery.customer.name,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isOngoing ? null : iconColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
delivery.customer.address.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
statusLabel,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isOngoing
|
||||
? Theme.of(context).colorScheme.onSurfaceVariant
|
||||
: iconColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
leading: _leading(context),
|
||||
tileColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
subtitle: Text(delivery.customer.address.toString()),
|
||||
trailing: Icon(Icons.arrow_forward_ios),
|
||||
onTap: () => _goToDelivery(context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/bloc/tour_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/bloc/tour_state.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/model/sorting_information.dart';
|
||||
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/presentation/delivery_overview.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
@ -56,79 +56,66 @@ class _DeliveryListState extends State<DeliveryList> {
|
||||
builder: (context, state) {
|
||||
final currentState = state;
|
||||
if (currentState is TourLoaded) {
|
||||
List<Delivery> deliveries =
|
||||
currentState.tour.deliveries
|
||||
.where(
|
||||
(delivery) =>
|
||||
delivery.carId == widget.selectedCarId &&
|
||||
delivery.allArticlesScanned() &&
|
||||
delivery.state != DeliveryState.finished,
|
||||
)
|
||||
.toList();
|
||||
if (widget.sortType == SortType.custom) {
|
||||
return _showCustomSortedList(
|
||||
currentState.tour.deliveries,
|
||||
currentState.sortingInformation[widget.selectedCarId.toString()] ?? [],
|
||||
currentState.distances ?? {},
|
||||
);
|
||||
}
|
||||
|
||||
List<Delivery> finishedDeliveries =
|
||||
currentState.tour.deliveries
|
||||
.where(
|
||||
(delivery) =>
|
||||
delivery.state == DeliveryState.finished &&
|
||||
delivery.carId == widget.selectedCarId,
|
||||
)
|
||||
.toList();
|
||||
final allDeliveries = currentState.tour.deliveries
|
||||
.where((d) => d.carId == widget.selectedCarId)
|
||||
.toList();
|
||||
|
||||
if (deliveries.isEmpty) {
|
||||
if (allDeliveries.isEmpty) {
|
||||
return ListView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Center(child: const Text("Keine Auslieferungen gefunden")),
|
||||
children: const [
|
||||
Center(child: Text("Keine Auslieferungen gefunden")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final ongoing = allDeliveries
|
||||
.where((d) => d.state == DeliveryState.ongoing)
|
||||
.toList();
|
||||
final nonOngoing = allDeliveries
|
||||
.where((d) => d.state != DeliveryState.ongoing)
|
||||
.toList();
|
||||
|
||||
int Function(Delivery, Delivery) comparator;
|
||||
switch (widget.sortType) {
|
||||
case SortType.custom:
|
||||
return _showCustomSortedList(
|
||||
currentState.tour.deliveries,
|
||||
currentState.sortingInformation[widget.selectedCarId.toString()] ?? [],
|
||||
currentState.distances ?? {},
|
||||
);
|
||||
|
||||
case SortType.nameAsc:
|
||||
deliveries.sort(
|
||||
(a, b) => a.customer.name.compareTo(b.customer.name),
|
||||
);
|
||||
comparator = (a, b) => a.customer.name.compareTo(b.customer.name);
|
||||
break;
|
||||
|
||||
case SortType.nameDesc:
|
||||
deliveries.sort(
|
||||
(a, b) => b.customer.name.compareTo(a.customer.name),
|
||||
);
|
||||
comparator = (a, b) => b.customer.name.compareTo(a.customer.name);
|
||||
break;
|
||||
|
||||
case SortType.distance:
|
||||
deliveries.sort(
|
||||
(a, b) => (currentState.distances![a.id] ?? 0.0).compareTo(
|
||||
currentState.distances![b.id] ?? 0.0,
|
||||
),
|
||||
);
|
||||
comparator = (a, b) =>
|
||||
(currentState.distances?[a.id] ?? 0.0)
|
||||
.compareTo(currentState.distances?[b.id] ?? 0.0);
|
||||
break;
|
||||
default:
|
||||
comparator = (a, b) => a.customer.name.compareTo(b.customer.name);
|
||||
}
|
||||
|
||||
//deliveries.addAll(finishedDeliveries);
|
||||
ongoing.sort(comparator);
|
||||
nonOngoing.sort(comparator);
|
||||
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => const Divider(height: 0),
|
||||
final sorted = [...ongoing, ...nonOngoing];
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
Delivery delivery = deliveries[index];
|
||||
|
||||
return DeliveryListItem(
|
||||
delivery: delivery,
|
||||
distance: currentState.distances?[delivery.id] ?? 0.0,
|
||||
);
|
||||
},
|
||||
itemCount: deliveries.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
itemCount: sorted.length,
|
||||
itemBuilder: (context, index) => DeliveryListItem(
|
||||
delivery: sorted[index],
|
||||
distance: currentState.distances?[sorted[index].id] ?? 0.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
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/delivery/bloc/tour_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/bloc/tour_event.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/presentation/delivery_info.dart';
|
||||
@ -34,8 +36,14 @@ class _DeliveryOverviewState extends State<DeliveryOverview> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Select the first car for initialization
|
||||
_selectedCarId = widget.tour.driver.cars.firstOrNull?.id;
|
||||
// Pre-select today's car from the daily car selection.
|
||||
// Falls back to the first available car if no selection exists.
|
||||
final carSelectState = context.read<CarSelectBloc>().state;
|
||||
if (carSelectState is CarSelectComplete) {
|
||||
_selectedCarId = carSelectState.selectedCar.id;
|
||||
} else {
|
||||
_selectedCarId = widget.tour.driver.cars.firstOrNull?.id;
|
||||
}
|
||||
_sortType = SortType.nameAsc;
|
||||
}
|
||||
|
||||
@ -44,54 +52,6 @@ class _DeliveryOverviewState extends State<DeliveryOverview> {
|
||||
context.read<TourBloc>().add(LoadTour(teamId: state.user.number));
|
||||
}
|
||||
|
||||
Widget _carSelection() {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children:
|
||||
widget.tour.driver.cars.map((car) {
|
||||
Color? backgroundColor;
|
||||
Color? iconColor = Theme.of(context).primaryColor;
|
||||
Color? textColor;
|
||||
|
||||
if (_selectedCarId == car.id) {
|
||||
backgroundColor = Theme.of(context).primaryColor;
|
||||
textColor = Theme.of(context).colorScheme.onSecondary;
|
||||
iconColor = Theme.of(context).colorScheme.onSecondary;
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedCarId = car.id;
|
||||
});
|
||||
},
|
||||
child: Chip(
|
||||
backgroundColor: backgroundColor,
|
||||
label: Row(
|
||||
children: [
|
||||
Icon(Icons.local_shipping, color: iconColor, size: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: Text(
|
||||
car.plate,
|
||||
style: TextStyle(color: textColor, fontSize: 12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Highlight the text of the active sorting type.
|
||||
TextStyle? _popupItemTextStyle() {
|
||||
return TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold);
|
||||
@ -99,17 +59,23 @@ class _DeliveryOverviewState extends State<DeliveryOverview> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
return BlocListener<CarSelectBloc, CarSelectState>(
|
||||
listener: (context, carState) {
|
||||
if (carState is CarSelectComplete) {
|
||||
setState(() => _selectedCarId = carState.selectedCar.id);
|
||||
}
|
||||
},
|
||||
child: RefreshIndicator(
|
||||
onRefresh: _loadTour,
|
||||
child: ListView(
|
||||
//crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
DeliveryInfo(tour: widget.tour),
|
||||
DeliveryInfo(tour: widget.tour, selectedCarId: _selectedCarId),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10,
|
||||
right: 10,
|
||||
top: 15,
|
||||
top: 0,
|
||||
bottom: 10,
|
||||
),
|
||||
child: Row(
|
||||
@ -191,16 +157,13 @@ class _DeliveryOverviewState extends State<DeliveryOverview> {
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 20),
|
||||
child: _carSelection(),
|
||||
),
|
||||
DeliveryList(
|
||||
selectedCarId: _selectedCarId,
|
||||
sortType: _sortType,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
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/delivery/overview/presentation/delivery_fail_page.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/presentation/delivery_overview.dart';
|
||||
|
||||
@ -16,25 +18,55 @@ class DeliveryOverviewPage extends StatefulWidget {
|
||||
class _DeliveryOverviewPageState extends State<DeliveryOverviewPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TourBloc, TourState>(
|
||||
builder: (context, state) {
|
||||
if (state is TourLoaded) {
|
||||
final currentState = state;
|
||||
final carState = context.watch<CarSelectBloc>().state;
|
||||
|
||||
return Center(
|
||||
child: DeliveryOverview(
|
||||
tour: currentState.tour,
|
||||
distances: currentState.distances ?? {},
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Auslieferung"),
|
||||
centerTitle: false,
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
||||
actions: [
|
||||
if (carState is CarSelectComplete)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.local_shipping,
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
carState.selectedCar.plate,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<TourBloc, TourState>(
|
||||
builder: (context, state) {
|
||||
if (state is TourLoaded) {
|
||||
return DeliveryOverview(
|
||||
tour: state.tour,
|
||||
distances: state.distances ?? {},
|
||||
);
|
||||
}
|
||||
|
||||
if (state is TourLoadingFailed) {
|
||||
return DeliveryLoadingFailedPage();
|
||||
}
|
||||
if (state is TourLoadingFailed) {
|
||||
return DeliveryLoadingFailedPage();
|
||||
}
|
||||
|
||||
return Container();
|
||||
},
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,6 +80,8 @@ class TourRepository {
|
||||
|
||||
if (article.scannedAmount < article.amount) {
|
||||
article.scannedAmount += 1;
|
||||
delivery.carId = int.tryParse(carId) ?? delivery.carId;
|
||||
await service.assignCar(deliveryId, carId);
|
||||
_tourStream.add(tour);
|
||||
return ScanResult.scanned;
|
||||
} else {
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:hl_lieferservice/dto/delivery_response.dart';
|
||||
import 'package:hl_lieferservice/dto/delivery_update.dart';
|
||||
import 'package:hl_lieferservice/dto/delivery_update_response.dart';
|
||||
@ -271,10 +272,24 @@ class TourService {
|
||||
|
||||
Future<BasicResponseDTO> finishDelivery(String deliveryId) async {
|
||||
try {
|
||||
// ISO-8601 mit T-Separator: sprachunabhaengig fuer SQL-Server datetime.
|
||||
// ('yyyy-MM-dd HH:mm:ss' OHNE T wird unter DATEFORMAT=DMY (DE) als YDM
|
||||
// geparst und schlaegt fuer Tag > 12 fehl.)
|
||||
// ERPframe arbeitet mit lokaler Zeit -> bewusst keine UTC-Konvertierung.
|
||||
final String deliveredAt = DateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ss",
|
||||
).format(DateTime.now());
|
||||
|
||||
var headers = {"Content-Type": "application/json"};
|
||||
headers.addAll(getSessionOrThrow());
|
||||
|
||||
var response = await post(
|
||||
urlBuilder("_web_finishDelivery"),
|
||||
headers: getSessionOrThrow(),
|
||||
body: {"delivery_id": deliveryId},
|
||||
headers: headers,
|
||||
body: jsonEncode({
|
||||
"delivery_id": deliveryId,
|
||||
"delivered_at": deliveredAt,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == HttpStatus.unauthorized) {
|
||||
|
||||
Reference in New Issue
Block a user