From 697b724f02804fa4ca5eba2eac4cf26227ffc1be Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Tue, 20 Jan 2026 11:45:39 -0500 Subject: [PATCH] auto-claude: subtask-4-2 - Update routes_page.dart to use new dialog components - Replaced inline loading dialogs with LoadingDialog component - Replaced inline notes dialog with NotesDialog component - Replaced inline photo confirmation dialog with PhotoCaptureDialog component - Added missing localization strings for completingDelivery, markingAsUncompleted, and deliveryMarkedUncompleted - Fixed BuildContext usage across async gaps by capturing l10n early - Fixed unused result warnings by using ref.invalidate instead of ref.refresh - Removed unnecessary non-null assertions Co-Authored-By: Claude --- lib/l10n/app_en.arb | 5 +- lib/l10n/app_fr.arb | 5 +- lib/l10n/app_localizations.dart | 18 +++ lib/l10n/app_localizations_en.dart | 9 ++ lib/l10n/app_localizations_fr.dart | 9 ++ lib/pages/routes_page.dart | 215 ++++++----------------------- 6 files changed, 84 insertions(+), 177 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index b132f88..ff0fa88 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -146,5 +146,8 @@ } }, "serverError": "Server error - Please contact support", - "retake": "Retake" + "retake": "Retake", + "completingDelivery": "Completing delivery...", + "markingAsUncompleted": "Marking as uncompleted...", + "deliveryMarkedUncompleted": "Delivery marked as uncompleted" } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index dbb44ca..8f35436 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -146,5 +146,8 @@ } }, "serverError": "Erreur serveur - Veuillez contacter le support", - "retake": "Reprendre" + "retake": "Reprendre", + "completingDelivery": "Completion de la livraison...", + "markingAsUncompleted": "Marquage comme a livrer...", + "deliveryMarkedUncompleted": "Livraison marquee comme a livrer" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 7479dbd..ee43169 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -631,6 +631,24 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Retake'** String get retake; + + /// No description provided for @completingDelivery. + /// + /// In en, this message translates to: + /// **'Completing delivery...'** + String get completingDelivery; + + /// No description provided for @markingAsUncompleted. + /// + /// In en, this message translates to: + /// **'Marking as uncompleted...'** + String get markingAsUncompleted; + + /// No description provided for @deliveryMarkedUncompleted. + /// + /// In en, this message translates to: + /// **'Delivery marked as uncompleted'** + String get deliveryMarkedUncompleted; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index e2a15f0..621e89c 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -302,4 +302,13 @@ class AppLocalizationsEn extends AppLocalizations { @override String get retake => 'Retake'; + + @override + String get completingDelivery => 'Completing delivery...'; + + @override + String get markingAsUncompleted => 'Marking as uncompleted...'; + + @override + String get deliveryMarkedUncompleted => 'Delivery marked as uncompleted'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 6b12a3b..1416596 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -302,4 +302,13 @@ class AppLocalizationsFr extends AppLocalizations { @override String get retake => 'Reprendre'; + + @override + String get completingDelivery => 'Completion de la livraison...'; + + @override + String get markingAsUncompleted => 'Marquage comme a livrer...'; + + @override + String get deliveryMarkedUncompleted => 'Livraison marquee comme a livrer'; } diff --git a/lib/pages/routes_page.dart b/lib/pages/routes_page.dart index 20c1c24..b6fc49d 100644 --- a/lib/pages/routes_page.dart +++ b/lib/pages/routes_page.dart @@ -11,10 +11,12 @@ import '../providers/providers.dart'; import '../api/client.dart'; import '../utils/toast_helper.dart'; import '../api/openapi_config.dart'; -import '../utils/breakpoints.dart'; import '../utils/http_client_factory.dart'; import '../components/collapsible_routes_sidebar.dart'; import '../components/dark_mode_map.dart'; +import '../components/loading_dialog.dart'; +import '../components/notes_dialog.dart'; +import '../components/photo_capture_dialog.dart'; import '../services/location_permission_service.dart'; import 'deliveries_page.dart'; import 'settings_page.dart'; @@ -81,13 +83,14 @@ class _RoutesPageState extends ConsumerState { Delivery delivery, int routeFragmentId, ) async { + // Capture l10n before async operations to avoid BuildContext across async gaps + final l10n = AppLocalizations.of(context); final authService = ref.read(authServiceProvider); // Ensure we have a valid token (automatically refreshes if needed) final token = await authService.ensureValidToken(); if (token == null) { if (mounted) { - final l10n = AppLocalizations.of(context)!; ToastHelper.showError(context, l10n.authenticationRequired); } return; @@ -102,27 +105,7 @@ class _RoutesPageState extends ConsumerState { switch (action) { case 'complete': if (mounted) { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) { - return const Center( - child: Card( - child: Padding( - padding: EdgeInsets.all(24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CircularProgressIndicator(), - SizedBox(height: 16), - Text('Completing delivery...'), - ], - ), - ), - ), - ); - }, - ); + LoadingDialog.show(context, message: l10n.completingDelivery); } final result = await authClient.executeCommand( @@ -134,7 +117,7 @@ class _RoutesPageState extends ConsumerState { result.when( success: (_) async { if (mounted) { - Navigator.of(context).pop(); + LoadingDialog.hide(context); } if (mounted) { @@ -176,23 +159,21 @@ class _RoutesPageState extends ConsumerState { } if (mounted) { - final l10n = AppLocalizations.of(context)!; ToastHelper.showSuccess(context, l10n.deliverySuccessful); } } }, onError: (error) { if (mounted) { - Navigator.of(context).pop(); + LoadingDialog.hide(context); } debugPrint('Complete delivery failed - Type: ${error.type}, Message: ${error.message}'); debugPrint('Error details: ${error.details}'); if (mounted) { - final l10n = AppLocalizations.of(context)!; String errorMessage = l10n.error(error.message); if (error.statusCode == 500) { - errorMessage = 'Server error - Please contact support'; + errorMessage = l10n.serverError; } ToastHelper.showError(context, errorMessage); } @@ -202,37 +183,17 @@ class _RoutesPageState extends ConsumerState { case 'uncomplete': if (mounted) { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) { - return const Center( - child: Card( - child: Padding( - padding: EdgeInsets.all(24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CircularProgressIndicator(), - SizedBox(height: 16), - Text('Marking as uncompleted...'), - ], - ), - ), - ), - ); - }, - ); + LoadingDialog.show(context, message: l10n.markingAsUncompleted); } - final result = await authClient.executeCommand( + final uncompleteResult = await authClient.executeCommand( endpoint: 'markDeliveryAsUncompleted', command: MarkDeliveryAsUncompletedCommand(deliveryId: delivery.id), ); - result.when( + uncompleteResult.when( success: (_) async { if (mounted) { - Navigator.of(context).pop(); + LoadingDialog.hide(context); } if (mounted) { @@ -257,18 +218,16 @@ class _RoutesPageState extends ConsumerState { } if (mounted) { - final l10n = AppLocalizations.of(context)!; - ToastHelper.showSuccess(context, 'Delivery marked as uncompleted'); + ToastHelper.showSuccess(context, l10n.deliveryMarkedUncompleted); } } }, onError: (error) { if (mounted) { - Navigator.of(context).pop(); + LoadingDialog.hide(context); } if (mounted) { - final l10n = AppLocalizations.of(context)!; ToastHelper.showError(context, l10n.error(error.message)); } }, @@ -289,12 +248,12 @@ class _RoutesPageState extends ConsumerState { Delivery delivery, ) async { final authService = ref.read(authServiceProvider); + final l10n = AppLocalizations.of(context); // Ensure we have a valid token (automatically refreshes if needed) final token = await authService.ensureValidToken(); if (token == null) { if (mounted) { - final l10n = AppLocalizations.of(context)!; ToastHelper.showError(context, l10n.authenticationRequired); } return; @@ -309,7 +268,7 @@ class _RoutesPageState extends ConsumerState { ); } catch (e) { if (mounted) { - ToastHelper.showError(context, 'Camera error: $e'); + ToastHelper.showError(context, l10n.cameraError(e.toString())); } return; } @@ -320,45 +279,11 @@ class _RoutesPageState extends ConsumerState { if (!mounted) return; - final bool? confirmed = await showDialog( - context: context, - builder: (BuildContext dialogContext) { - return AlertDialog( - title: const Text('Confirm Photo'), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: MediaQuery.of(dialogContext).size.height * 0.5, - maxWidth: MediaQuery.of(dialogContext).size.width * 0.8, - ), - child: Image.file( - File(pickedFile!.path), - fit: BoxFit.contain, - ), - ), - const SizedBox(height: 16), - Text( - 'Upload this photo for ${delivery.name}?', - textAlign: TextAlign.center, - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(dialogContext).pop(false), - child: const Text('Cancel'), - ), - ElevatedButton( - onPressed: () => Navigator.of(dialogContext).pop(true), - child: const Text('Upload'), - ), - ], - ); - }, + // Show photo confirmation dialog + final bool? confirmed = await PhotoCaptureDialog.show( + context, + imageFile: File(pickedFile.path), + deliveryName: delivery.name, ); if (confirmed != true) { @@ -367,27 +292,8 @@ class _RoutesPageState extends ConsumerState { if (!mounted) return; - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) { - return const Center( - child: Card( - child: Padding( - padding: EdgeInsets.all(24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CircularProgressIndicator(), - SizedBox(height: 16), - Text('Uploading photo...'), - ], - ), - ), - ), - ); - }, - ); + // Show uploading dialog + LoadingDialog.show(context, message: l10n.uploadingPhoto); try { final Uri uploadUrl = Uri.parse( @@ -408,33 +314,31 @@ class _RoutesPageState extends ConsumerState { client.close(); if (mounted) { - Navigator.of(context).pop(); + LoadingDialog.hide(context); } if (response.statusCode >= 200 && response.statusCode < 300) { if (mounted) { - ToastHelper.showSuccess(context, 'Photo uploaded successfully'); + ToastHelper.showSuccess(context, l10n.photoUploadSuccess); } - ref.refresh(allDeliveriesProvider); + ref.invalidate(allDeliveriesProvider); } else { debugPrint('Photo upload failed - Status: ${response.statusCode}'); debugPrint('Response body: ${response.body}'); if (mounted) { - String errorMessage = 'Upload failed'; + String errorMessage = l10n.photoUploadFailed(response.statusCode); if (response.statusCode == 500) { - errorMessage = 'Server error - Please contact support'; + errorMessage = l10n.serverError; } else if (response.statusCode == 401) { - errorMessage = 'Authentication required - Please log in again'; - } else { - errorMessage = 'Upload failed: ${response.statusCode}'; + errorMessage = l10n.authenticationRequired; } ToastHelper.showError(context, errorMessage); } } } catch (e) { if (mounted) { - Navigator.of(context).pop(); - ToastHelper.showError(context, 'Upload error: $e'); + LoadingDialog.hide(context); + ToastHelper.showError(context, l10n.uploadError(e.toString())); } } } @@ -462,53 +366,14 @@ class _RoutesPageState extends ConsumerState { } Future _showNotesDialog(Delivery delivery) async { - final notes = delivery.orders - .where((order) => order.note != null && order.note!.isNotEmpty) - .map((order) => order.note!) - .toList(); - if (!mounted) return; - if (notes.isEmpty) { - ToastHelper.showInfo(context, 'No notes attached to this delivery'); - return; - } + final l10n = AppLocalizations.of(context); + final hasNotes = await NotesDialog.show(context, delivery); - await showDialog( - context: context, - builder: (BuildContext dialogContext) { - return AlertDialog( - title: Text('Notes for ${delivery.name}'), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: notes.map((note) => Padding( - padding: const EdgeInsets.only(bottom: 16.0), - child: Text( - note, - style: Theme.of(dialogContext).textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - )).toList(), - ), - ), - actionsAlignment: MainAxisAlignment.center, - actionsPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16), - actions: [ - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: () => Navigator.of(dialogContext).pop(), - child: const Text('Close'), - ), - ), - ], - ); - }, - ); + if (!hasNotes && mounted) { + ToastHelper.showInfo(context, l10n.noNotesMessage); + } } @override @@ -516,7 +381,7 @@ class _RoutesPageState extends ConsumerState { final routesData = ref.watch(deliveryRoutesProvider); final allDeliveriesData = ref.watch(allDeliveriesProvider); final userProfile = ref.watch(userProfileProvider); - final l10n = AppLocalizations.of(context)!; + final l10n = AppLocalizations.of(context); return Scaffold( appBar: AppBar( @@ -535,8 +400,8 @@ class _RoutesPageState extends ConsumerState { onPressed: (routesData.isLoading || allDeliveriesData.isLoading) ? null : () { - ref.refresh(deliveryRoutesProvider); - ref.refresh(allDeliveriesProvider); + ref.invalidate(deliveryRoutesProvider); + ref.invalidate(allDeliveriesProvider); }, tooltip: 'Refresh', ),