// ------------- FULL FILE STARTS HERE --------------- import 'dart:convert'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:bookatanker/common/settings.dart'; import '../models/tanksview_model.dart'; class ArrivalScreen extends StatefulWidget { var orderDetails; ArrivalScreen({ required this.orderDetails, }); @override State createState() => _ArrivalScreenState(); } class _ArrivalScreenState extends State { bool isLoading = false; List tankLevelsList = []; List sumpTanks = []; GetTanksDetailsModel? selectedTank; String otp = ''; String unload_complete_otp = ''; String? selectedTankId; bool _showOrderSummary = false; int currentStep = 0; bool allowTankChange = true; // ------------------------------------------- // FETCH TANK DATA // ------------------------------------------- Future getAllTanksData() async { isLoading = true; var response1 = await AppSettings.getTankLevels(); setState(() { tankLevelsList = ((jsonDecode(response1)['data']) as List).map((dynamic model) { return GetTanksDetailsModel.fromJson(model); }).toList(); sumpTanks = tankLevelsList .where((product) => product.tank_location.toString().toUpperCase() == 'SUMP') .toList(); isLoading = false; }); } @override void initState() { getAllTanksData(); super.initState(); otp = widget.orderDetails.tank_otp ?? ''; unload_complete_otp = widget.orderDetails.stop_otp ?? ''; _setChecklistStepFromStatus(); } // ------------------------------------------- // DETERMINE CHECKLIST STEP + LOCK TANK CHANGE // ------------------------------------------- void _setChecklistStepFromStatus() { String status = (widget.orderDetails.orderStatus ?? "").toLowerCase().trim(); if (status == "unloading_started" || status == "in_progress") { currentStep = 1; // Step 1 active allowTankChange = false; } else if (status == "unloading_stopped" || status == "unloading_completed") { currentStep = 2; // Step 2 active → View Bill ENABLED allowTankChange = false; } else if (status == "payment_pending" || status == "payment_waiting") { currentStep = 2; // Still show Step 2 allowTankChange = false; } else { currentStep = 0; // Initial stage allowTankChange = true; // Only here tank change is allowed } setState(() {}); } // ------------------------------------------- // REFRESH ALL DATA // ------------------------------------------- Future _refreshAllData() async { try { var response = await AppSettings.updateBookingDetailsById(widget.orderDetails.dbId); if (response != '') { final data = jsonDecode(response)['data'] ?? {}; widget.orderDetails.orderStatus = data["orderStatus"] ?? widget.orderDetails.orderStatus; widget.orderDetails.tank_otp = data["tank_otp"] ?? widget.orderDetails.tank_otp; widget.orderDetails.stop_otp = data["stop_otp"] ?? widget.orderDetails.stop_otp; widget.orderDetails.tankName = data["tankName"] ?? widget.orderDetails.tankName; otp = widget.orderDetails.tank_otp ?? ''; unload_complete_otp = widget.orderDetails.stop_otp ?? ''; selectedTankId = widget.orderDetails.tankName ?? ''; } await getAllTanksData(); _setChecklistStepFromStatus(); } catch (e) { print("Refresh failed: $e"); } } // ------------------------------------------- // UPDATE TANK NAME TO SERVER // ------------------------------------------- Future _updateTankName(String tankName) async { var payload = {"tankName": tankName}; var updateTankStatus = await AppSettings.updateTankNameWhileDelivery( widget.orderDetails.dbId, payload); if (updateTankStatus != '') { AppSettings.longSuccessToast('Updated successfully'); final updatedData = jsonDecode(updateTankStatus)['data'] ?? {}; final updatedTankName = updatedData["tankName"]?.toString() ?? tankName; final updatedOtp = updatedData["tank_otp"]?.toString() ?? ""; setState(() { selectedTankId = updatedTankName; otp = updatedOtp; try { widget.orderDetails.tankName = updatedTankName; widget.orderDetails.tank_otp = updatedOtp; } catch (_) {} }); /*Navigator.of(context).pop(true);*/ } else { AppSettings.longFailedToast('Update failed'); } } // ------------------------------------------- // BUILD MAIN UI // ------------------------------------------- @override Widget build(BuildContext context) { final bool hasTankAlready = widget.orderDetails.tankName != null && widget.orderDetails.tankName!.isNotEmpty; GetTanksDetailsModel? preSelectedTank; if (hasTankAlready) { try { preSelectedTank = sumpTanks.firstWhere( (t) => t.tank_name == widget.orderDetails.tankName); } catch (e) { preSelectedTank = null; } } return Scaffold( backgroundColor: Colors.white, appBar: AppSettings.supplierAppBarWithoutActions( 'Order#${widget.orderDetails.bookingid}', context), body: WillPopScope( onWillPop: () async { Navigator.pop(context, true); return false; }, child: SafeArea( child: RefreshIndicator( onRefresh: _refreshAllData, displacement: 60, strokeWidth: 3, color: Color(0xFF1D7AFC), backgroundColor: Colors.white, triggerMode: RefreshIndicatorTriggerMode.onEdge, child: CustomScrollView( physics: AlwaysScrollableScrollPhysics(), slivers: [ SliverToBoxAdapter( child: buildMainBody( context, hasTankAlready, preSelectedTank), ), ], ), ), ), ), ); } // ------------------------------------------- // YOUR ENTIRE UI BODY // ------------------------------------------- Widget buildMainBody(context, hasTankAlready, preSelectedTank) { return SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // ------------ BANNER ----------------- Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Color(0xFFFFF4D9), borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Text( "Arrived at your location!", style: fontTextStyle(20, Color(0xFF232527), FontWeight.w800), ), SizedBox(height: 4), RichText( text: TextSpan( children: [ TextSpan( text: "HOME", style: fontTextStyle( 11, Color(0xFF343637), FontWeight.w500), ), TextSpan( text: " - ${AppSettings.userAddress}", style: fontTextStyle( 11, Color(0xFF343637), FontWeight.w400), ), ], ), ), ], ), ), SizedBox(height: 16), // ------------ PROFILE ----------------- Row( children: [ CircleAvatar( radius: 20, backgroundColor: Color(0XFFE8F2FF), child: Image.asset('images/profile_user.png', width: 50, height: 50), ), SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.orderDetails.delivery_agent, style: fontTextStyle( 12, Color(0xFF343637), FontWeight.w500), ), SizedBox(height: 4), Text( "TS J8 8905", style: fontTextStyle( 12, Color(0xFF646566), FontWeight.w500), ), ], ), Spacer(), Row(children: [ Image.asset('images/message.png', width: 24, height: 24), SizedBox(width: 16), Image.asset('images/phone.png', width: 24, height: 24), ]) ], ), SizedBox(height: 16), // ------------ TANK DROPDOWN ----------------- buildTankDropdown(hasTankAlready, preSelectedTank), SizedBox(height: 16), // ------------ OTP Boxes ----------------- if (selectedTankId != null || hasTankAlready) buildStartOtp(), SizedBox(height: 16), if (unload_complete_otp != '') buildStopOtp(), SizedBox(height: 16), // ------------ ORDER SUMMARY ----------------- buildOrderSummary(), SizedBox(height: 16), // ------------ CHECKLIST ----------------- buildChecklist(), ], ), ), ); } // ------------------------------------------- // DROPDOWN WITH BLOCKED LOGIC // ------------------------------------------- Widget buildTankDropdown(hasTankAlready, preSelectedTank) { return Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(8), ), child: Column(children: [ Row( children: [ Expanded( child: Text( hasTankAlready ? "Change tank to unload water to generate OTP" : "Choose a tank to unload water to generate OTP", style: fontTextStyle(12, Color(0xFF444444), FontWeight.w600), ), ), SizedBox(width: 8), Expanded( child: isLoading ? Center( child: CircularProgressIndicator( color: primaryColor, strokeWidth: 5, ), ) : _buildDropdownField( hint: hasTankAlready ? widget.orderDetails.tankName : "Select Tank", value: selectedTank ?? preSelectedTank, items: sumpTanks, // 🔥 OPTION B — TANK CHANGE BLOCKED HERE onChanged: (GetTanksDetailsModel? tank) { if (tank == null) return; // ❌ BLOCK AFTER UNLOADING STARTED if (!allowTankChange) { AppSettings.longFailedToast( "Tank cannot be changed after unloading has started."); return; } // MOTOR CHECK bool motorOn = false; if (tank.inConnections.isNotEmpty) { motorOn = tank.inConnections.any( (conn) => conn['motor_status']?.toString() == '2'); } if (!motorOn && tank.outConnections.isNotEmpty) { motorOn = tank.outConnections.any( (conn) => conn['motor_status']?.toString() == '2'); } if (motorOn) { showMotorOnAlert(tank); return; } showTankConfirmDialog(tank); }, ), ), ], ) ]), ); } // ------------------------------------------- // ALERT WHEN MOTOR ON // ------------------------------------------- void showMotorOnAlert(GetTanksDetailsModel tank) { showDialog( context: context, barrierDismissible: false, builder: (_) => AlertDialog( backgroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), title: Center( child: Text( "The motor of \"${tank.tank_name}\" is turned on. Please turn it off to start unloading.", style: fontTextStyle(14, Colors.black, FontWeight.w500), textAlign: TextAlign.center, ), ), actions: [ Center( child: Row( children: [ Expanded( child: GestureDetector( onTap: () => Navigator.pop(context), child: Container( alignment: Alignment.center, padding: EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( border: Border.all(color: Color(0xFF1D7AFC)), borderRadius: BorderRadius.circular(24), ), child: Text("Cancel", style: fontTextStyle( 12, Color(0xFF1D7AFC), FontWeight.w600)), ), ), ), SizedBox(width: 12), Expanded( child: GestureDetector( onTap: () { Navigator.pop(context); setState(() => selectedTank = tank); }, child: Container( alignment: Alignment.center, padding: EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: Color(0xFFC03D34), borderRadius: BorderRadius.circular(24), ), child: Text( "Stop Motor", style: fontTextStyle( 12, Colors.white, FontWeight.w600), ), ), ), ), ], ), ), ], ), ); } // ------------------------------------------- // CONFIRM TANK CHANGE // ------------------------------------------- void showTankConfirmDialog(GetTanksDetailsModel tank) { showDialog( context: context, barrierDismissible: false, builder: (_) => AlertDialog( backgroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), title: Center( child: Text( "You have selected \"${tank.tank_name}\" for water unloading. Do you confirm?", style: fontTextStyle(14, Colors.black, FontWeight.w500), textAlign: TextAlign.center, ), ), actions: [ Center( child: Row( children: [ Expanded( child: GestureDetector( onTap: () => Navigator.pop(context), child: Container( alignment: Alignment.center, padding: EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( border: Border.all(color: Color(0xFF1D7AFC)), borderRadius: BorderRadius.circular(24), ), child: Text( "Cancel", style: fontTextStyle( 12, Color(0xFF1D7AFC), FontWeight.w600), ), ), ), ), SizedBox(width: 12), Expanded( child: GestureDetector( onTap: () async { await _updateTankName(tank.tank_name); setState(() { selectedTank = tank; selectedTankId = tank.tank_name; }); Navigator.pop(context); }, child: Container( alignment: Alignment.center, padding: EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: Color(0xFF1D7AFC), borderRadius: BorderRadius.circular(24), ), child: Text( "Confirm", style: fontTextStyle( 12, Colors.white, FontWeight.w600), ), ), ), ), ], ), ), ], ), ); } // ------------------------------------------- // OTP BOXES // ------------------------------------------- Widget buildStartOtp() { return Container( decoration: BoxDecoration( color: Color(0xFFE8F2FF), borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.all(8), child: Row( children: [ Expanded( child: Text("Share the OTP to start unloading", style: fontTextStyle( 12, Color(0xFF444444), FontWeight.w600))), Expanded(child: buildOtpBoxes(otp)), ], ), ); } Widget buildStopOtp() { return Container( decoration: BoxDecoration( color: Color(0xFFE8F2FF), borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.all(8), child: Row( children: [ Expanded( child: Text("Share the OTP to stop unloading", style: fontTextStyle( 12, Color(0xFF444444), FontWeight.w600))), Expanded(child: buildOtpBoxes(unload_complete_otp)), ], ), ); } Row buildOtpBoxes(String otp) { return Row( mainAxisAlignment: MainAxisAlignment.end, children: (otp.isNotEmpty ? otp : "----") .split('') .map((digit) { return Container( width: 28, height: 28, alignment: Alignment.center, margin: EdgeInsets.symmetric(horizontal: 1), decoration: BoxDecoration( color: Color(0xFF4692FD), borderRadius: BorderRadius.circular(4), ), child: Text( digit, style: fontTextStyle(12, Colors.white, FontWeight.w500), ), ); }).toList(), ); } // ------------------------------------------- // ORDER SUMMARY // ------------------------------------------- Widget buildOrderSummary() { return Column( children: [ InkWell( onTap: () => setState(() => _showOrderSummary = !_showOrderSummary), child: Row( children: [ Text("View Order Summary", style: fontTextStyle( 14, Color(0xFF1D7AFC), FontWeight.w600)), Spacer(), Image.asset( _showOrderSummary ? 'images/arrow-up.png' : 'images/arrow-down.png', width: 24, height: 24, color: Color(0xFF1D7AFC), ) ], ), ), if (_showOrderSummary) AnimatedContainer( duration: Duration(milliseconds: 300), child: Card( color: Colors.white, child: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("ITEMS", style: fontTextStyle( 12, Color(0xFF444444), FontWeight.w700)), SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("10,000 L Drinking water x 1", style: fontTextStyle( 12, Color(0xFF515253), FontWeight.w400)), Text("₹2,500", style: fontTextStyle( 12, Color(0xFF515253), FontWeight.w400)), ], ), Divider(), _priceRow("Item Total", "₹2,346.00"), _priceRow("Delivery Charges", "₹150.00"), _priceRow("Platform Fee", "₹6.00"), _priceRow("Taxes", "₹12.49"), Divider(), _priceRow("Total Bill", "₹2,514", isBold: true), Divider(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Mode of Payment", style: fontTextStyle( 12, Color(0xFF444444), FontWeight.w500)), Row( children: [ Image.asset('images/success-toast.png', width: 12, height: 12), SizedBox(width: 4), Text("Cash on delivery", style: fontTextStyle( 12, Color(0xFF444444), FontWeight.w500)), ], ) ], ) ], ), ), ), ) ], ); } // ------------------------------------------- // CHECKLIST // ------------------------------------------- Widget buildChecklist() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Arrival Checklist", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), SizedBox(height: 16), _buildChecklistItem( icon: Icons.play_circle_fill, title: "Start unloading water", subtitle: "The status will be updated once the delivery agent starts unloading water.", stepIndex: 0, currentStep: currentStep, selectedTankId: selectedTankId, onTap: () {}, ), _buildChecklistItem( icon: Icons.check_circle, title: "Complete unloading", subtitle: "The status will be updated after you confirm that unloading is complete.", stepIndex: 1, currentStep: currentStep, selectedTankId: selectedTankId, onTap: () {}, ), _buildChecklistItem( icon: Icons.receipt_long, title: "View your bill", subtitle: "The bill will be calculated after unloading is complete.", stepIndex: 2, currentStep: currentStep, selectedTankId: selectedTankId, onTap: () {}, ), ], ); } // ------------------------------------------- // HELPERS // ------------------------------------------- Widget _buildDropdownField({ required String hint, required GetTanksDetailsModel? value, required List items, String? path, required ValueChanged onChanged, }) { return DropdownButtonHideUnderline( child: DropdownButton2( isExpanded: true, hint: Row( children: [ if (path != null && path.isNotEmpty) Image.asset(path, width: 16, height: 16), SizedBox(width: 6), Text(hint, style: fontTextStyle( 14, Color(0XFF939495), FontWeight.w400)), ], ), value: value, items: items.map((tank) { return DropdownMenuItem( value: tank, child: Padding( padding: EdgeInsets.symmetric(vertical: 4), child: Text(tank.tank_name, style: fontTextStyle( 14, Color(0xFF2D2E30), FontWeight.w400)), ), ); }).toList(), onChanged: onChanged, buttonStyleData: ButtonStyleData( height: 48, padding: EdgeInsets.only(left: 0, right: 12), decoration: BoxDecoration( border: Border.all(color: Color(0XFF939495)), borderRadius: BorderRadius.circular(12), ), ), iconStyleData: IconStyleData( icon: Padding( padding: EdgeInsets.only(right: 4), child: Image.asset('images/arrow_down.png', width: 16, height: 16, color: Color(0XFF939495)), ), ), ), ); } } Widget _buildChecklistItem({ required IconData icon, required String title, required String subtitle, required int stepIndex, required int currentStep, required VoidCallback onTap, required String? selectedTankId, }) { bool isCompleted = currentStep > stepIndex; bool isActive = currentStep == stepIndex; // 🔥 Only allow clicking when this is the ACTIVE step AND it is step 2 bool isClickable = (stepIndex == 2 && isActive); return GestureDetector( onTap: isClickable ? onTap : null, child: Opacity( opacity: 1, child: Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( children: [ Icon( (isActive || isCompleted) ? Icons.radio_button_checked : Icons.radio_button_unchecked, color: (isActive || isCompleted) ? Colors.blue : Colors.grey, ), if (stepIndex < 2) Container( width: 2, height: 50, color: isCompleted ? Colors.blue : Colors.grey[300], ), ], ), const SizedBox(width: 12), Expanded( child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFF5F7FA), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon( icon, color: isCompleted ? Colors.blue : Colors.grey, ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 14, color: isCompleted ? Colors.black : Colors.grey, ), ), const SizedBox(height: 4), Text( subtitle, style: const TextStyle(fontSize: 12, color: Colors.black54), ), ], ), ), ], ), ), ) ], ), ), ), ); } Widget _priceRow(String label, String amount, {bool isBold = false}) { return Padding( padding: EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: fontTextStyle( 12, Color(0xFF444444), isBold ? FontWeight.w600 : FontWeight.w400)), Text(amount, style: fontTextStyle( 12, Color(0xFF444444), isBold ? FontWeight.w600 : FontWeight.w400)), ], ), ); } // ------------- FULL FILE ENDS HERE ---------------