part of 'deliveries.dart'; // ------------------------------------------------------------------------- // SKIP REASONS SHEET // ------------------------------------------------------------------------- Future _showMyOptionsSheet( BuildContext context, Map delivery, _MyDeliveriesState? parentState, ) async { int selected = -1; bool isLoading = false; final List skipReasons = [ 'Customer unreachable', 'Customer not at the location', ]; await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (context) { return SafeArea( top: false, left: false, right: false, bottom: true, child: StatefulBuilder( builder: (context, setState) { Widget optionBox(String title, int index) { final bool isSelected = selected == index; return GestureDetector( onTap: () { if (!isLoading) { setState(() => selected = index); } }, child: Container( padding: const EdgeInsets.all(16), margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: isSelected ? ColorConstants.primaryColor.withOpacity(0.1) : Colors.grey.shade100, borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? ColorConstants.primaryColor : Colors.grey.shade300, width: isSelected ? 2 : 1, ), ), child: Row( children: [ Icon( isSelected ? Icons.check_circle : Icons.circle_outlined, color: isSelected ? ColorConstants.primaryColor : Colors.grey, size: 28, ), const SizedBox(width: 12), Text( title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87, fontFamily: FontConstants.fontFamily, ), ), ], ), ), ); } Future handleConfirm() async { if (selected == -1 || isLoading) return; // Check skip limits before proceeding final dc = Get.put(DeliveriesController(), permanent: true); final skipStatus = await dc.checkSkipStatus(); final int skipCount = skipStatus['count'] ?? 0; bool applyPenalty = false; if (skipCount >= 2) { // Show Styled Alert Dialog final bool? confirm = await showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), elevation: 5, backgroundColor: Colors.white, child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.red.shade50, shape: BoxShape.circle, ), child: Icon( Icons.warning_amber_rounded, color: Colors.red.shade600, size: 40, ), ), const SizedBox(height: 20), Text( 'Skip Limit Reached', textAlign: TextAlign.center, style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, fontFamily: FontConstants.fontFamily, color: Colors.black87, ), ), const SizedBox(height: 12), Text( 'You have exceeded the limit of 2 skips within 3 hours.', textAlign: TextAlign.center, style: TextStyle( fontSize: 16, fontFamily: FontConstants.fontFamily, color: Colors.black54, ), ), const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(10), border: Border.all( color: Colors.orange.shade200, ), ), child: Row( children: [ Icon(Icons.info_outline, color: Colors.orange.shade800, size: 20), const SizedBox(width: 8), Expanded( child: Text( "Proceeding will forfeit your bonus points for this session.", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Colors.orange.shade900, fontFamily: FontConstants.fontFamily, ), ), ), ], ), ), const SizedBox(height: 24), Row( children: [ Expanded( child: OutlinedButton( onPressed: () => Navigator.pop(context, false), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), side: BorderSide(color: Colors.grey.shade300), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( "Cancel", style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black54, fontFamily: FontConstants.fontFamily, ), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade600, padding: const EdgeInsets.symmetric(vertical: 14), elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( "Confirm Skip", style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white, fontFamily: FontConstants.fontFamily, ), ), ), ), ], ), ], ), ), ); }, ); if (confirm != true) return; // User cancelled or dismissed applyPenalty = true; } setState(() => isLoading = true); try { final dc = Get.put(DeliveriesController(), permanent: true); final d = delivery; final int deliveryId = int.tryParse('${d['deliveryid'] ?? 0}') ?? 0; final int orderHeaderId = int.tryParse('${d['orderheaderid'] ?? 0}') ?? 0; final String reason = selected >= 0 && selected < skipReasons.length ? skipReasons[selected] : ''; if (deliveryId > 0 && orderHeaderId > 0) { String riderLatStr = '0'; String riderLngStr = '0'; try { final position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high, timeLimit: const Duration(seconds: 5), ); riderLatStr = position.latitude.toStringAsFixed(6); riderLngStr = position.longitude.toStringAsFixed(6); } catch (e) { debugPrint('[SKIP] Error getting rider location: $e'); try { final lastPos = await Geolocator.getLastKnownPosition(); if (lastPos != null) { riderLatStr = lastPos.latitude.toStringAsFixed(6); riderLngStr = lastPos.longitude.toStringAsFixed(6); } } catch (_) {} } debugPrint( '[SKIP] Updating status for deliveryId=$deliveryId orderHeaderId=$orderHeaderId reason="$reason"', ); final ok = await dc.updateSkippedStatus( deliveryId: deliveryId, orderHeaderId: orderHeaderId, ridersLat: riderLatStr, ridersLng: riderLngStr, notes: reason, ); debugPrint('[SKIP] Status update result: $ok'); if (ok && context.mounted) { // Register skip locally only on success await dc.registerSkip(applyPenalty: applyPenalty); if (context.mounted) { Navigator.pop(context); // Close the sheet if (parentState != null) { // Only update list if called from the list view parentState.markOrderAsSkipped(d, reason); } else { // If called from Navigation/Map (where parentState is null), // we just close the sheet and let the caller handle the UI update // typically by popping the route or showing a snackbar. // We DO NOT force navigation to "MyDeliveries". debugPrint( '[SKIP] Skipped from Nav/Map screen, sheet closed.', ); } } } else if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( 'Failed to skip delivery. Please try again.', ), backgroundColor: Colors.red, ), ); } } else { debugPrint( '[SKIP] Invalid deliveryId: $deliveryId or orderHeaderId: $orderHeaderId', ); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Invalid delivery information.'), backgroundColor: Colors.red, ), ); } } } catch (e) { debugPrint('[SKIP] Error updating skip status: $e'); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('An error occurred. Please try again.'), backgroundColor: Colors.red, ), ); } } finally { if (context.mounted) { setState(() => isLoading = false); } } } return Padding( padding: const EdgeInsets.fromLTRB(16, 20, 16, 30), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Select Reason', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, fontFamily: FontConstants.fontFamily, ), ), IconButton( icon: const Icon(Icons.cancel, color: Colors.red, size: 32), onPressed: isLoading ? null : () => Navigator.pop(context), ), ], ), const SizedBox(height: 20), optionBox('Customer unreachable', 0), optionBox('Customer not at the location', 1), const SizedBox(height: 20), SizedBox( width: double.infinity, height: 55, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: (selected == -1 || isLoading) ? Colors.grey.shade300 : ColorConstants.primaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: (selected == -1 || isLoading) ? null : handleConfirm, child: isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text( 'Confirm', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, fontFamily: FontConstants.fontFamily, color: (selected == -1 || isLoading) ? Colors.black45 : Colors.white, ), ), ), ), ], ), ); }, ), ); }, ); }