import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:supplier_new/common/settings.dart'; import '../resources/drivers_model.dart'; import '../resources/tankers_model.dart'; class AssignDriverScreen extends StatefulWidget { var order; var status; AssignDriverScreen({this.order, this.status}); @override State createState() => _AssignDriverScreenState(); } class _AssignDriverScreenState extends State { int advancePayable = 0; int advance = 0; double amountToPayAfterDelivery = 0.0; double totalFare = 0.0; bool isLoading = false; bool isTankersDataLoading = false; List driversList = []; List tankersList = []; @override void initState() { // TODO: implement initState super.initState(); _fetchTankers(); _fetchDrivers(); advance = 150; advancePayable = advance; totalFare = advance + double.parse(widget.order.quoted_amount); amountToPayAfterDelivery = totalFare - advancePayable; } Future _fetchDrivers() async { setState(() => isLoading = true); try { final response = await AppSettings.getDrivers(); final data = (jsonDecode(response)['data'] as List) .map((e) => DriversModel.fromJson(e)) .toList(); if (!mounted) return; setState(() { driversList = data; isLoading = false; }); } catch (e) { debugPrint("⚠️ Error fetching drivers: $e"); setState(() => isLoading = false); } } Future _fetchTankers() async { setState(() => isTankersDataLoading = true); try { final response = await AppSettings.getTankers(); final data = (jsonDecode(response)['data'] as List) .map((e) => TankersModel.fromJson(e)) .toList(); if (!mounted) return; setState(() { tankersList = data; isTankersDataLoading = false; }); } catch (e) { debugPrint("⚠️ Error fetching tankers: $e"); setState(() => isTankersDataLoading = false); } } void _showAssignTankerBottomSheet() { int? selectedTankerIndex; int? selectedDriverIndex; int _capToLiters(dynamic cap) { if (cap == null) return -1; if (cap is num) return cap.round(); final s = cap.toString().toLowerCase().replaceAll(',', '').trim(); final match = RegExp(r'(\d+(\.\d+)?)').firstMatch(s); if (match == null) return -1; final n = double.tryParse(match.group(1)!) ?? -1; if (n < 0) return -1; if (s.contains('kl')) return (n * 1000).round(); return n.round(); } showModalBottomSheet( backgroundColor: const Color(0XFFFFFFFF), context: context, isScrollControlled: true, isDismissible: false, enableDrag: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return WillPopScope( onWillPop: () async => false, child: StatefulBuilder( builder: (context, setModalState) { return FractionallySizedBox( heightFactor: 0.95, child: Column( children: [ // 🔸 Scrollable Section Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 60, height: 4, margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: const Color(0xFFE0E0E0), borderRadius: BorderRadius.circular(2), ), ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Assign Tanker", style: fontTextStyle( 16, const Color(0XFF2A2A2A), FontWeight.w600), ), GestureDetector( onTap: () => Navigator.pop(context), child: Image.asset( 'images/cross.png', height: 24, width: 24, color: const Color(0XFF2A2A2A), ), ) ], ), const SizedBox(height: 16), // 🏢 Order Info Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Colors.white, border: Border.all( color: const Color(0XFFC9C2F0), width: 0.5), ), child: Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.order.building_name, style: fontTextStyle( 20, const Color(0XFF2D2E30), FontWeight.w600), ), Text( widget.order.displayAddress, style: fontTextStyle( 12, const Color(0XFF939495), FontWeight.w400), ), ], ), const Spacer(), Text( '${widget.order.distanceInKm} Km', style: fontTextStyle( 12, const Color(0XFF939495), FontWeight.w400), ), ], ), ), const SizedBox(height: 16), // 🛻 Tanker List Text( "SELECT TANKER", style: fontTextStyle( 10, const Color(0XFF2D2E30), FontWeight.w600), ), const SizedBox(height: 12), isTankersDataLoading ? const Center(child: CircularProgressIndicator()) : (tankersList.isEmpty ? Center( child: Text( 'No Data Available For Capacity ${widget.order.capacity}', style: fontTextStyle( 12, const Color(0xFF939495), FontWeight.w500), ), ) : ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: tankersList .where((t) => _capToLiters(t.capacity) == _capToLiters(widget.order.capacity)) .length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, idx) { final filteredTankers = tankersList .where((t) => _capToLiters(t.capacity) == _capToLiters(widget.order.capacity)) .toList(); final d = filteredTankers[idx]; final isSelected = selectedTankerIndex == idx; return GestureDetector( onTap: () { setModalState(() { selectedTankerIndex = idx; selectedDriverIndex = null; // reset driver selection if tanker changes }); }, child: Card( elevation: 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: isSelected ? primaryColor : const Color(0XFFC3C4C4), width: 1, ), ), child: TankersCard( title: d.tanker_name, subtitle: d.type_of_water, capacity: d.capacity, code: d.license_plate, owner: d.supplier_name, status: List.from( d.availability), ), ), ); }, )), const SizedBox(height: 8), // 🧍 Driver List Text( "SELECT DRIVER", style: fontTextStyle( 10, const Color(0XFF2D2E30), FontWeight.w600), ), const SizedBox(height: 4), // 🚨 Driver list disabled until tanker is selected selectedTankerIndex == null ? Container( width: double.infinity, padding: const EdgeInsets.all(16), margin: const EdgeInsets.only(top: 8), decoration: BoxDecoration( color: const Color(0XFFFFFFFF), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFC3C4C4)), ), child: Center( child: Text( 'Select a tanker to choose driver', style: fontTextStyle( 14, const Color(0xFF2D2E30), FontWeight.w400), ), ), ) : isLoading ? const Center(child: CircularProgressIndicator()) : (driversList.isEmpty ? Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Text( 'No Data Available', style: fontTextStyle( 12, const Color(0xFF939495), FontWeight.w500), ), ), ) : ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: driversList.length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, idx) { final d = driversList[idx]; final isSelected = selectedDriverIndex == idx; final isAvailable = d.status == "available"; final statusColor = isAvailable ? const Color(0XFF0A9E04) : (d.status == "on delivery" ? const Color(0XFFD0AE3C) : (d.status == "offline" ? const Color(0XFF939495) : Colors.grey)); return GestureDetector( onTap: () { /* if (isAvailable) { setModalState(() { selectedDriverIndex = idx; }); } else { AppSettings.longFailedToast( 'Only available drivers can be selected', ); }*/ setModalState(() { selectedDriverIndex = idx; }); }, child: Card( color: const Color(0XFFFFFFFF), elevation: 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: isSelected ? primaryColor : const Color(0XFFC3C4C4), width: 1, ), ), child: Padding( padding: const EdgeInsets.all(12.0), child: Row( children: [ Image.asset( 'images/avatar.png', fit: BoxFit.cover, width: 20, height: 20, ), const SizedBox(width: 8), Expanded( child: Text( d.driver_name, style: fontTextStyle( 14, const Color(0XFF2D2E30), FontWeight.w500), ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all(color: statusColor), ), child: Text( d.status, style: fontTextStyle(10, statusColor, FontWeight.w400), ), ), ], ), ), ), ); }, )), ], ), ), ), // 📌 Fixed Assign Button SafeArea( top: false, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: SizedBox( width: double.infinity, child: GestureDetector( onTap: () async { if (selectedTankerIndex == null) { AppSettings.longFailedToast('Please select tanker'); return; } /*if (selectedDriverIndex == null) { AppSettings.longFailedToast('Please select driver'); return; }*/ final filteredTankers = tankersList .where((t) => _capToLiters(t.capacity) == _capToLiters(widget.order.capacity)) .toList(); final selectedTanker = filteredTankers[selectedTankerIndex!]; final selectedDriver = driversList[selectedDriverIndex!]; AppSettings.preLoaderDialog(context); bool isOnline = await AppSettings.internetConnectivity(); if (isOnline) { var payload = new Map(); payload["tankerName"] = selectedTanker.tanker_name; if(selectedDriverIndex != null){ payload["delivery_agent"] = selectedDriver.driver_name; payload["delivery_agent_mobile"] = selectedDriver.phone_number; } else{ payload["delivery_agent"] =null; payload["delivery_agent_mobile"] = null; } bool status = await AppSettings.assignTanker(payload, widget.order.dbId); try { if (status) { Navigator.of(context,rootNavigator: true).pop(); AppSettings.longFailedToast("Tanker assigned successfully"); Navigator.pop(context, true); } else{ Navigator.of(context,rootNavigator: true).pop(); AppSettings.longFailedToast("Failed to assign tanker"); } } catch (e) { Navigator.of(context,rootNavigator: true).pop(); print(e); } } else{ Navigator.of(context,rootNavigator: true).pop(); AppSettings.longFailedToast("Please Check internet"); } if (context.mounted) Navigator.pop(context); }, child: Container( decoration: BoxDecoration( color: const Color(0XFF8270DB), borderRadius: BorderRadius.circular(24), ), alignment: Alignment.center, padding: const EdgeInsets.symmetric(vertical: 12), child: Text( 'Assign', style: fontTextStyle( 14, Colors.white, FontWeight.w500), ), ), ), ), ), ), ], ), ); }, ), ); }, ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, extendBodyBehindAppBar: true, appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, scrolledUnderElevation: 0, title: Text( '', style: fontTextStyle(16, Color(0XFF2A2A2A), FontWeight.w600), ), iconTheme: IconThemeData(color: Color(0XFF2A2A2A)), actions: [ Row( children: [ Padding( padding: EdgeInsets.fromLTRB(0, 10, 10, 10), child: IconButton( icon: Image.asset( 'images/help_appbar.png', height: 20, width: 20, color: Color(0XFFFFFFFF), ), onPressed: () {}, ), ) ], ) ], leading: GestureDetector( onTap: () { Navigator.pop(context); }, child: Padding( padding: const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed child: Image.asset( 'images/backbutton_appbar.png', // Replace with your image path fit: BoxFit.contain, color: Color(0XFFFFFFFF), height: 24, width: 24, ), ), ), ), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /// 🔹 Top Card with Image Stack( clipBehavior: Clip.none, children: [ /// 🔹 Background Image ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0), ), child: Image.asset( "images/building.png", height: 220, width: double.infinity, fit: BoxFit.cover, ), ), /// 🔹 Floating Info Card (half on image, half below) Positioned( bottom: -40, // pulls the card out by 40px left: 12, right: 12, child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 6, offset: Offset(0, 3), ), ], ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /*Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Color(0XFFFFFFFF), borderRadius: BorderRadius.circular(4), border: Border.all( color: widget.status.statusColor, width: 0.5, ), ), child: Text(widget.status.status, style: fontTextStyle( 12, widget.status.statusColor, FontWeight.w500)), ),*/ Text( widget.order.building_name, style: fontTextStyle( 20, const Color(0XFF2D2E30), FontWeight.w600), ), SizedBox(height: 4), Text( widget.order.displayAddress, style: fontTextStyle( 12, const Color(0XFF939495), FontWeight.w400), ), ], ), const Spacer(), Text( widget.order.distanceInKm.toString() + 'Km', style: fontTextStyle( 12, const Color(0XFF939495), FontWeight.w400), ), ], ), ), ), ], ), SizedBox( height: MediaQuery.of(context).size.height * .08, ), /// 🔹 Order Details Padding( padding: EdgeInsets.fromLTRB(16, 0, 16, 0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "ORDER DETAILS", style: fontTextStyle( 10, const Color(0XFF2D2E30), FontWeight.w600), ), SizedBox( height: MediaQuery.of(context).size.height * .011, ), _detailTwoRow( "Tanker Price", "₹${AppSettings.formDouble(widget.order.quoted_amount) ?? ''}", "images/financialsBottomIcon.png", "", "", ""), SizedBox( height: MediaQuery.of(context).size.height * .02, ), _detailTwoRow( "Water Type", "${widget.order.type_of_water}", "images/water.png", "Date of Delivery", "${widget.order.date}", "images/calendar_appbar.png", ), SizedBox( height: MediaQuery.of(context).size.height * .02, ), _detailTwoRow( "Capacity", "${widget.order.capacity}", "images/capacity.png", "Time of Delivery", "${widget.order.time}", "images/time.png", ), SizedBox( height: MediaQuery.of(context).size.height * .02, ), _detailTwoRow( "Quantity", "${widget.order.quantity}", "images/quantity.png", "Booking Charges", advance.toString(), "images/advance.png", ), ], ), ), SizedBox( height: MediaQuery.of(context).size.height * .008, ), /// 🔹 Additional Details Padding( padding: EdgeInsets.fromLTRB(16, 0, 16, 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "ADDITIONAL DETAILS", style: fontTextStyle( 10, const Color(0XFF2D2E30), FontWeight.w600), ), SizedBox( height: MediaQuery.of(context).size.height * .011, ), Text( "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " "aliquip ex ea commodo consequat.", style: fontTextStyle( 12, const Color(0XFF646566), FontWeight.w400), ), ], )), ], ), ), /// 🔹 Bottom Action Buttons bottomNavigationBar: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Color(0XFFFFFFFF), border: Border(top: BorderSide(color: Color(0XFFF5F6F6))), ), child: Row( children: [ Expanded( child: OutlinedButton( style: OutlinedButton.styleFrom( foregroundColor: Color(0XFFFFFFFF), backgroundColor: Colors.white, side: BorderSide(color: Color(0XFFFFFFFF)), padding: EdgeInsets.symmetric(vertical: 10), ), onPressed: () async { /*AppSettings.preLoaderDialog(context); bool isOnline = await AppSettings.internetConnectivity(); if (isOnline) { var payload = new Map(); payload["supplierId"] = AppSettings.supplierId; payload["amount"] = int.parse(widget.order.quoted_amount); payload["delivery_charges"] = advance; payload["action"] = 'reject'; bool status = await AppSettings.acceptOrderRequests( payload, widget.order.dbId); try { if (status) { Navigator.of(context, rootNavigator: true).pop(); AppSettings.longSuccessToast( "Order request rejected Successfully"); Navigator.pop(context, true); } else { Navigator.of(context, rootNavigator: true).pop(); AppSettings.longFailedToast( "reject of order request Failed"); } } catch (e) { Navigator.of(context, rootNavigator: true).pop(); print(e); } } else { Navigator.of(context, rootNavigator: true).pop(); AppSettings.longFailedToast("Please Check internet"); }*/ Navigator.pop(context); }, child: Text( "CANCEL", style: fontTextStyle( 14, const Color(0XFFE2483D), FontWeight.w400), ), ), ), SizedBox(width: 8), Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Color(0XFF8270DB), foregroundColor: Color(0XFFFFFFFF), padding: EdgeInsets.symmetric(vertical: 10), ), onPressed: () async { _showAssignTankerBottomSheet(); }, child: Text( "Assign Tanker", style: fontTextStyle( 14, const Color(0XFFFFFFFF), FontWeight.w400), ), ), ), ], )), ); } /// 🔹 Helper widget for rows Widget _detailRow(String title, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( title, style: fontTextStyle(12, const Color(0XFF646566), FontWeight.w400), ), Text( value, style: fontTextStyle(12, const Color(0XFF2D2E30), FontWeight.w500), ), ], ), ); } Widget _detailTwoRow( String title1, String value1, String path1, String title2, String value2, String path2, { EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 6), }) { final titleStyle = fontTextStyle(12, Color(0XFF646566), FontWeight.w400); final valueStyle = fontTextStyle(12, Color(0XFF343637), FontWeight.w500); Widget _col(String t, String v, String path) { return Expanded( child: Padding( padding: const EdgeInsets.only(right: 8.0), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ if (path.isNotEmpty) Image.asset( path, fit: BoxFit.contain, height: 20, width: 20, color: const Color(0XFFC3C4C4), ), if (path.isNotEmpty) const SizedBox(width: 6), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(t, style: titleStyle, maxLines: 1, overflow: TextOverflow.ellipsis), Text(v, style: valueStyle, maxLines: 1, overflow: TextOverflow.ellipsis), ], ), ) ], ), ), ); } return Padding( padding: padding, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _col(title1, value1, path1), _col(title2, value2, path2), ], ), ); } } // ====== TankersCard ====== class TankersCard extends StatelessWidget { final String title; final String subtitle; final String capacity; final String code; final String owner; final List status; const TankersCard({ super.key, required this.title, required this.subtitle, required this.capacity, required this.code, required this.owner, required this.status, }); Color _chipColor(String s) { switch (s) { case 'filled': return const Color(0xFFFFFFFF); case 'available': return const Color(0xFFFFFFFF); case 'empty': return const Color(0xFFFFFFFF); case 'in-use': return const Color(0xFFFFFFFF); case 'maintenance': return const Color(0xFFFFFFFF); default: return const Color(0xFFFFFFFF); } } Color _chipTextColor(String s) { switch (s) { case 'filled': return const Color(0xFF1D7AFC); case 'available': return const Color(0xFF0A9E04); case 'empty': return const Color(0xFFE2483D); case 'in-use': return const Color(0xFFEA843B); case 'maintenance': return const Color(0xFFD0AE3C); default: return Color(0xFF2A2A2A); } } @override Widget build(BuildContext context) { /*ImageProvider avatarProvider = (AppSettings.profilePictureUrl != '' && AppSettings.profilePictureUrl != 'null') ? NetworkImage(AppSettings.profilePictureUrl) : const AssetImage("images/profile_pic.png") as ImageProvider;*/ Widget _statusChip(String s) { final chipTextColor = _chipTextColor(s); return Container( margin: const EdgeInsets.only(left: 6), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _chipColor(s), borderRadius: BorderRadius.circular(8), border: Border.all(color: chipTextColor, width: 1), ), child: Text(s, style: fontTextStyle(10, chipTextColor, FontWeight.w400)), ); } return Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: const Color(0XFFFFFFFF), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0XFFC3C4C4)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( 'images/square_avatar.png', fit: BoxFit.cover, width: 24, height: 24, ), const SizedBox(width: 8), // Title + Chips Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( "$subtitle • $title", maxLines: 1, overflow: TextOverflow.ellipsis, style: fontTextStyle(14, const Color(0xFF2D2E30), FontWeight.w500), ), ), const SizedBox(width: 8), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 160), child: SingleChildScrollView( scrollDirection: Axis.horizontal, reverse: true, child: Row( children: status.map(_statusChip).toList(), ), ), ), ], ), Text( code, style: fontTextStyle(10, const Color(0xFF646566), FontWeight.w400), ), ], ), ), ], ), ], ), ); } }