diff --git a/images/warning.png b/images/warning.png new file mode 100644 index 0000000..b3df9dd Binary files /dev/null and b/images/warning.png differ diff --git a/lib/common/dashboard.dart b/lib/common/dashboard.dart index 4c98958..9d83bcd 100644 --- a/lib/common/dashboard.dart +++ b/lib/common/dashboard.dart @@ -876,7 +876,7 @@ class _HomeScreenState extends State { for(var order in data){ String status = - (order['status'] ?? "") + (order['my_supplier_entry']['status'] ?? "") .toString() .toLowerCase(); @@ -1116,25 +1116,39 @@ class _HomeScreenState extends State { /// percentage calculation double percent = 0; - if(yesterday != 0){ + if(yesterday > 0){ percent = ((today - yesterday) / yesterday) * 100; + } + else if(today > 0){ + + percent = 100; + } String text; Color color; - if(percent > 0){ + if(today > yesterday){ - text = - "↑ ${percent.abs().toStringAsFixed(0)}% from yesterday"; + if(yesterday == 0){ + + text = "↑ New revenue"; + + } + else{ + + text = + "↑ ${percent.abs().toStringAsFixed(0)}% from yesterday"; + + } color = Colors.green; } - else if(percent < 0){ + else if(today < yesterday){ text = "↓ ${percent.abs().toStringAsFixed(0)}% from yesterday"; diff --git a/lib/common/settings.dart b/lib/common/settings.dart index e89cc68..dafe760 100644 --- a/lib/common/settings.dart +++ b/lib/common/settings.dart @@ -211,6 +211,13 @@ class AppSettings{ static int resourcesInitialTab = 0; static List existingCreditCustomerIds = []; + static String moneyConvertion(String value) { + var comma = NumberFormat('#,##,###.00', 'en_IN'); + return comma.format( + double.parse(value), + ); + } + static String formDouble(dynamic s) { var comma = NumberFormat('#,##,###.##', 'en_IN'); diff --git a/lib/orders/accept_order_requests.dart b/lib/orders/accept_order_requests.dart index be000be..18e800c 100644 --- a/lib/orders/accept_order_requests.dart +++ b/lib/orders/accept_order_requests.dart @@ -1,7 +1,13 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:supplier_new/common/settings.dart'; import 'package:supplier_new/orders/edit_order_requests.dart'; +import '../resources/tankers_model.dart'; +import 'best_options.dart'; + class AcceptOrderRequests extends StatefulWidget { var order; var status; @@ -12,22 +18,188 @@ class AcceptOrderRequests extends StatefulWidget { class _AcceptOrderRequestsState extends State { int advancePayable = 0; - int advance =0; - double amountToPayAfterDelivery = 0.0; double totalFare = 0.0; + final TextEditingController tankerPriceController = TextEditingController(); + double platformFee = 0.0; + double totalTaxes = 0.0; + double subtotal = 0.0; + double cgst = 0.0; + double sgst = 0.0; + double total = 0.0; + @override void initState() { // TODO: implement initState super.initState(); - advance = 150; - advancePayable = advance; - totalFare = advance + double.parse(widget.order.quoted_amount); - amountToPayAfterDelivery = totalFare - advancePayable; + tankerPriceController.addListener(() => setState(() {})); + fetchTankers(); + platformFee = 11; + tankerPriceController.text = '${widget.order.quoted_amount}'; } + List tankersList = []; + bool isLoadingTankers = false; + + Future fetchTankers() async { + + setState(() { + isLoadingTankers = true; + }); + + try { + + final response = + await AppSettings.getTankers(); + + final data = + (jsonDecode(response)['data'] as List) + + .map((e)=> + TankersModel.fromJson(e)) + + .toList(); + + tankersList = data; + + setState(() { + isLoadingTankers = false; + }); + + } + catch(e){ + + print(e); + + setState(() { + isLoadingTankers = false; + }); + + } + + } + + double getDeliveryFee(){ + + if(tankersList.isEmpty) + return 0; + + String orderCapacity = + widget.order.capacity + .replaceAll(",", "") + .replaceAll("Litres","") + .replaceAll("L","") + .replaceAll(" ","") + .trim(); + + var matched = + tankersList.firstWhere( + + (t){ + + String tankerCap = + t.capacity + .replaceAll(",", "") + .replaceAll("Litres","") + .replaceAll("L","") + .replaceAll(" ","") + .trim(); + + return tankerCap + == orderCapacity; + + }, + + orElse: ()=>TankersModel(), + + ); + + return double.tryParse( + matched.delivery_fee.toString() + + ) ?? 0; + + } + + double calculateTransport(){ + + double distance = + double.tryParse( + widget.order.distanceInKm + .toString() + ) ?? 0; + + double deliveryFee = + getDeliveryFee(); + + double qty = + double.tryParse( + widget.order.quantity + .toString() + ) ?? 1; + + return (distance * + deliveryFee * + qty); + + } + + + @override Widget build(BuildContext context) { + + int tankerPrice = int.tryParse(tankerPriceController.text) ?? 0; + int updatedQuantity=int.tryParse(widget.order.quantity) ?? 0; + String updatedCapacity=widget.order.capacity; + int bookingCharges = + calculateTransport().round(); + + //int totalPrice = (tankerPrice * updatedQuantity) + bookingCharges; + int advancePayable = bookingCharges; // + //int amountToPayAfterDelivery=totalPrice-bookingCharges; + + double distance = + double.tryParse( + widget.order.distanceInKm.toString() + ) ?? 0; + + double perKm = + getDeliveryFee(); + + double transport = + calculateTransport(); + double actualPrice = + double.tryParse( + tankerPriceController.text + ) ?? 0; + + double quantity = + double.tryParse( + widget.order.quantity.toString() + ) ?? 1; + + double subtotal = + actualPrice * quantity; + + double cgst = + subtotal * 0.09; + + double sgst = + subtotal * 0.09; + + double totalTaxes = + cgst + sgst; + + double totalPrice = + subtotal + + transport + + totalTaxes + + platformFee; + + double amountToPayAfterDelivery = + totalPrice - transport; + return Scaffold( backgroundColor: Colors.white, extendBodyBehindAppBar: true, @@ -182,16 +354,72 @@ class _AcceptOrderRequestsState extends State { SizedBox( height: MediaQuery.of(context).size.height * .011, ), - _detailTwoRow( + _twoFields(tankerPriceController, "Tanker Price", + 'images/price.png', false, null, null, null, null), + /*_detailTwoRow( "Tanker Price", "₹${AppSettings.formDouble(widget.order.quoted_amount) ?? ''}", "images/financialsBottomIcon.png", "", "", - ""), - SizedBox( - height: MediaQuery.of(context).size.height * .02, + ""),*/ + + + + _detailTwoRow( + + "Transport Charges", + + "₹ " + + AppSettings.formDouble( + transport.toString()), + + "images/advance.png", + + "", + "", + "", + ), + + + Padding( + + padding: EdgeInsets.only(left:4), + + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Image.asset( + 'images/warning.png', + fit: BoxFit.cover, + width: + 22, // Match the diameter of the CircleAvatar + height: 22, + ), + SizedBox(width: MediaQuery.of(context).size.width * .020), + Expanded( + + child: Text( + + "Transport charges are calculated based on distance ${distance.toStringAsFixed(2)} km and rate ₹${perKm.toStringAsFixed(0)} per km. Total transport charges are ₹${transport.toStringAsFixed(0)}.", + + softWrap:true, + + style: fontTextStyle( + 11, + Color(0XFF646566), + FontWeight.w400), + + ), + + ), + ], + ) + ), + + _detailTwoRow( "Water Type", "${widget.order.type_of_water}", @@ -218,9 +446,9 @@ class _AcceptOrderRequestsState extends State { "Quantity", "${widget.order.quantity}", "images/quantity.png", - "Booking Charges", - advance.toString(), - "images/advance.png", + "", + "", + "", ), ], ), @@ -230,7 +458,7 @@ class _AcceptOrderRequestsState extends State { ), /// 🔹 Additional Details - Padding( + /* Padding( padding: EdgeInsets.fromLTRB(16, 0, 16, 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -252,10 +480,10 @@ class _AcceptOrderRequestsState extends State { 12, const Color(0XFF646566), FontWeight.w400), ), ], - )), + )),*/ /// 🔹 Payment Summary - Padding( + /*Padding( padding: EdgeInsets.fromLTRB(16, 0, 16, 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -297,7 +525,35 @@ class _AcceptOrderRequestsState extends State { '₹${AppSettings.formDouble(amountToPayAfterDelivery.toString()) ?? ''}'), ], ), - ), + ),*/ + const SizedBox(height: 20), + Padding( padding: EdgeInsets.fromLTRB(16, 0, 16, 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("UPDATED PAYMENT SUMMARY", + style: fontTextStyle(12,Color(0XFF2D2E30),FontWeight.w600),), + const SizedBox(height: 12), + + _summaryRow("Quantity", " $updatedQuantity"), + _summaryRow("Capacity", "$updatedCapacity"), + _summaryRow("Tanker Price", "₹ $tankerPrice"), + _summaryRow("Transport Charges", "₹ $bookingCharges"), + _summaryRow('Platform Fee', '₹${AppSettings.moneyConvertion(platformFee.toString())}'), + _summaryRow('Taxes', '₹${AppSettings.moneyConvertion(totalTaxes.toString())}'), + _summaryRow( + "Total Price", + "₹ ${AppSettings.formDouble(totalPrice.toString())}" + ), + const Divider(), + + _summaryRow( + "Transport Charges", + "₹ ${AppSettings.formDouble(transport.toString())}" + ), + _summaryRow("Amount to Pay (After Delivery)", "₹ $amountToPayAfterDelivery"), + ], + )), const SizedBox(height: 80), // space for bottom buttons ], @@ -313,7 +569,7 @@ class _AcceptOrderRequestsState extends State { ), child: Row( children: [ - Expanded( + /* Expanded( child: OutlinedButton( style: OutlinedButton.styleFrom( foregroundColor: Color(0XFF000000), @@ -348,7 +604,7 @@ class _AcceptOrderRequestsState extends State { ), ), ), - SizedBox(width: 8), + SizedBox(width: 8),*/ Expanded( child: OutlinedButton( style: OutlinedButton.styleFrom( @@ -365,8 +621,8 @@ class _AcceptOrderRequestsState extends State { if (isOnline) { var payload = new Map(); payload["supplierId"] = AppSettings.supplierId; - payload["amount"] = int.parse(widget.order.quoted_amount); - payload["delivery_charges"] = advance; + payload["amount"] = int.parse(tankerPriceController.text); + payload["delivery_charges"] = calculateTransport().round(); payload["action"] = 'reject'; bool status = await AppSettings.acceptOrderRequests( @@ -422,8 +678,8 @@ class _AcceptOrderRequestsState extends State { if (isOnline) { var payload = new Map(); payload["supplierId"] = AppSettings.supplierId; - payload["amount"] = int.parse(widget.order.quoted_amount); - payload["delivery_charges"] = advance; + payload["amount"] = int.parse(tankerPriceController.text); + payload["delivery_charges"] = calculateTransport().round(); payload["action"] = 'accept'; bool status = await AppSettings.acceptOrderRequests( @@ -448,6 +704,21 @@ class _AcceptOrderRequestsState extends State { AppSettings.longFailedToast("Please Check internet"); } }, + /*onPressed:(){ + + Navigator.push( + + context, + + MaterialPageRoute( + + builder:(_)=>TankerOptionsDemo() + + ) + + ); + + },*/ child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -487,6 +758,121 @@ class _AcceptOrderRequestsState extends State { ); } + Widget _twoFields( + TextEditingController? controller1, + String? label1, + String? path1, + bool? readOnly1, + TextEditingController? controller2, + String? label2, + String? path2, + bool? readOnly2) { + return Row( + children: [ + Expanded( + child: _textField(controller1, label1, path1, readOnly1!), + ), + const SizedBox(width: 10), + if (controller2 != null) + Expanded( + child: _textField(controller2, label2, path2, readOnly2!), + ), + ], + ); + } + + Widget _textField(TextEditingController? controller, String? label, + String? path, bool readOnly) { + return Container( + margin: const EdgeInsets.only(bottom: 12), + child: TextField( + controller: controller, + cursorColor: primaryColor, + readOnly: readOnly, + decoration: InputDecoration( + counterText: '', + filled: false, + fillColor: Colors.white, + prefixIcon: Padding( + padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 6.0), + child: SizedBox( + width: 18, + height: 18, + child: Image.asset( + path!, + fit: BoxFit.contain, + color: Color(0XFFC3C4C4), + ), + ), + ), + prefixIconConstraints: BoxConstraints( + minWidth: 24, + minHeight: 24, + ), + suffixIcon: readOnly + ? null + : Padding( + padding: const EdgeInsets.only(right: 8), + child: Image.asset( + 'images/edit.png', + height: 18, + width: 18, + color: const Color(0XFFC3C4C4), + ), + ), + suffixIconConstraints: BoxConstraints( + minWidth: 24, + minHeight: 24, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.0), + borderSide: BorderSide( + color: Color(0XFFC3C4C4), + width: 1, + )), + focusedBorder: !readOnly + ? OutlineInputBorder( + borderRadius: BorderRadius.circular(4.0), + borderSide: BorderSide( + color: Color(0XFF8270DB), + width: 1, + ), + ) + : OutlineInputBorder( + borderRadius: BorderRadius.circular(4.0), + borderSide: BorderSide( + color: Color(0XFFC3C4C4), + width: 1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(4.0), + borderSide: BorderSide(color: Color(0XFFC3C4C4)), + ), + labelText: label, + labelStyle: fontTextStyle(12, Color(0XFF646566), FontWeight.w400), + /* TextStyle(color: greyColor, fontWeight: FontWeight.bold //<-- SEE HERE + ),*/ + ), + style: fontTextStyle(12, Color(0XFF343637), FontWeight.w500), + ), + ); + } + + Widget _summaryRow(String title, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(title, style: fontTextStyle(12,Color(0XFF646566),FontWeight.w400),), + Text(value, + style: fontTextStyle(12,Color(0XFF2D2E30),FontWeight.w500),), + ], + ), + ); + } + Widget _detailTwoRow( String title1, String value1, diff --git a/lib/orders/all_orders.dart b/lib/orders/all_orders.dart index 7d59955..7299246 100644 --- a/lib/orders/all_orders.dart +++ b/lib/orders/all_orders.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -517,26 +518,231 @@ class FilterChipWidget extends StatelessWidget { } } -class OrderCard extends StatelessWidget { +class OrderCard extends StatefulWidget { + final OrdersModel order; final VoidCallback? onRefresh; - const OrderCard({super.key, required this.order, this.onRefresh}); + const OrderCard({ + super.key, + required this.order, + this.onRefresh + }); + + @override + State createState() => _OrderCardState(); + +} + +class _OrderCardState extends State{ + + Timer? timer; + + @override + void initState(){ + + super.initState(); + + timer = Timer.periodic( + const Duration(minutes:1), + (_) { + + if(mounted){ + setState(() {}); + } + + } + ); + + } + + @override + void dispose(){ + + timer?.cancel(); + + super.dispose(); + + } + + /// DATE PARSE + DateTime? get orderDate{ + + try{ + return DateFormat("dd-MMM-yyyy") + .parse(widget.order.date); + }catch(_){ + return null; + } + + } + + /// TODAY DATE ONLY + DateTime get today{ + + final now = DateTime.now(); + + return DateTime( + now.year, + now.month, + now.day + ); + + } + + /// ORDER DAY ONLY + DateTime? get orderDay{ + + if(orderDate==null){ + return null; + } + + return DateTime( + orderDate!.year, + orderDate!.month, + orderDate!.day + ); + + } + + /// EXPIRE ONLY IF BEFORE TODAY + bool get isExpired{ + + if(orderDay==null){ + return false; + } + + String s = + widget.order.status.toLowerCase(); + + /// Only pending orders expire + if( + s!='advance_paid' && + s!='accepted' + ){ + return false; + } + + return orderDay!.isBefore(today); + + } + + /// STATUS NORMALIZATION + String get normalizedStatus{ + + if(isExpired){ + return "expired"; + } + + String s = + widget.order.status.toLowerCase(); + + if(s=='advance_paid' || s=='accepted'){ + return "pending"; + } + + if(s=='deliveryboy_assigned' || s=='tanker_assigned'){ + return "assigned"; + } + + if(s=='delivered'){ + return "completed"; + } + + return s; + + } + + /// ACTION RULES + bool get canAssign{ + + if(isExpired){ + return false; + } + + String s = + widget.order.status.toLowerCase(); + + return s=='advance_paid' || + s=='accepted'; + + } + + bool get canCancel{ + + if(isExpired){ + return false; + } + + String s = + widget.order.status.toLowerCase(); + + return s=='advance_paid' || + s=='accepted'; + + } + + bool get canTrack{ + + if(isExpired){ + return false; + } + + return normalizedStatus=='in_progress'; + + } - Color _getStatusColor() { - String st = order.status.toLowerCase(); + bool get canChange{ - if (st == 'advance_paid' || st == 'accepted') { - st = 'pending'; + if(isExpired){ + return false; } - else if (st == 'deliveryboy_assigned' || st == 'tanker_assigned') { - st = 'assigned'; + + return normalizedStatus=='assigned'; + + } + + bool get canChangeAction{ + + if(orderDay == null){ + return false; } - else if (st == 'delivered') { - st = 'completed'; + + /// disable for past orders + if(orderDay!.isBefore(today)){ + return false; } - switch (st) { + return normalizedStatus == 'assigned'; + + } + + bool get isCompleted{ + + return normalizedStatus=='completed'; + + } + + /// PRIORITY = FUTURE TODAY ORDERS ONLY + bool get isPriority{ + + if(orderDay==null){ + return false; + } + + return orderDay==today && + normalizedStatus=='pending'; + + } + + /// STATUS COLOR + Color get statusColor{ + + switch(normalizedStatus){ + + case "expired": + return Color(0XFFFCEDEC); + case "completed": return Color(0XFFC4E8C3); @@ -549,38 +755,24 @@ class OrderCard extends StatelessWidget { case "assigned": return Color(0XFFF9DBC6); - case "pickup_started": - case "start_loading": - case "loading_completed": - case "out_for_delivery": - case "arrived": - case "unloading_started": - case "unloading_stopped": - case "payment_pending": - return Color(0XFFF9DBC6); - case "pending": return Color(0XFFFDF3D3); default: return Colors.grey; + } + } - Color _getTextStatusColor() { - String st = order.status.toLowerCase(); + /// TEXT COLOR + Color get textStatusColor{ - if (st == 'advance_paid' || st == 'accepted') { - st = 'pending'; - } - else if (st == 'deliveryboy_assigned' || st == 'tanker_assigned') { - st = 'assigned'; - } - else if (st == 'delivered') { - st = 'completed'; - } + switch(normalizedStatus){ + + case "expired": + return Color(0XFFE2483D); - switch (st) { case "completed": return Color(0XFF0A9E04); @@ -593,403 +785,674 @@ class OrderCard extends StatelessWidget { case "assigned": return Color(0XFFE56910); - case "pickup_started": - case "start_loading": - case "loading_completed": - case "out_for_delivery": - case "arrived": - case "unloading_started": - case "unloading_stopped": - case "payment_pending": - return Color(0XFFE56910); - case "pending": return Color(0XFFD0AE3C); default: return Colors.grey; + } + } - @override - Widget build(BuildContext context) { + /// PRIORITY BADGE + Widget buildPriority(){ + if(!isPriority){ + return SizedBox(); + } - final statusColor = _getStatusColor(); - final textStatusColor = _getTextStatusColor(); - - String s = order.status.toString().toLowerCase(); - - String st = (s == 'advance_paid') - ? 'pending' - : (s == 'accepted') - ? 'pending' - : (s == 'delivered') - ? 'completed' - : (s == 'deliveryboy_assigned') - ? 'assigned' - : (s == 'tanker_assigned') - ? 'assigned' - : (s == 'pickup_started' || - s == 'start_loading' || - s == 'loading_completed' || - s == 'out_for_delivery' || - s == 'arrived' || - s == 'unloading_started' || - s == 'unloading_stopped' || - s == 'payment_pending') - ? s - : s; - - bool isTodayOrder = false; + return Container( - try { - DateTime orderDate = DateFormat("dd-MMM-yyyy").parse(order.date); - DateTime now = DateTime.now(); + padding: EdgeInsets.symmetric( + horizontal:6, + vertical:2 + ), - DateTime today = DateTime(now.year, now.month, now.day); - DateTime orderOnly = DateTime(orderDate.year, orderDate.month, orderDate.day); + decoration: BoxDecoration( - if (orderOnly == today) { - isTodayOrder = true; - } - } catch (_) {} + color: Color(0XFFFFE0B2), + + borderRadius: + BorderRadius.circular(4) + + ), + + child: Text( + + "TODAY", + + style: fontTextStyle( + 8, + Color(0XFFE56910), + FontWeight.w700 + ), + + ), + + ); + + } + + @override + Widget build(BuildContext context){ return Padding( - padding: const EdgeInsets.fromLTRB(0, 8, 0, 0), - child: Container( - margin: const EdgeInsets.only(bottom: 12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 5, - offset: const Offset(0, 3), - ) - ], + + padding:EdgeInsets.fromLTRB(0,8,0,0), + + child:Container( + + margin:EdgeInsets.only(bottom:12), + + decoration:BoxDecoration( + + color:Colors.white, + + borderRadius:BorderRadius.circular(12), + + boxShadow:[ + BoxShadow( + color:Colors.black.withOpacity(0.05), + blurRadius:5, + offset:Offset(0,3) + ) + ] + ), - child: IntrinsicHeight( - child: Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ + child:IntrinsicHeight( + + child:Row( + + crossAxisAlignment:CrossAxisAlignment.stretch, + + children:[ + ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(12), - bottomLeft: Radius.circular(12), + + borderRadius:BorderRadius.only( + topLeft:Radius.circular(12), + bottomLeft:Radius.circular(12) ), - child: SizedBox( - width: 145, - child: order.imageAsset.isNotEmpty - ? Image.asset( - order.imageAsset, - fit: BoxFit.cover, - ) - : Image.network( - order.imageAsset, - fit: BoxFit.cover, - ), + + child:SizedBox( + + width:145, + + child:widget.order.imageAsset.isNotEmpty + ? Image.asset(widget.order.imageAsset,fit:BoxFit.cover) + : Image.network(widget.order.imageAsset,fit:BoxFit.cover) + ), + ), Expanded( - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 2, - ), - decoration: BoxDecoration( - color: statusColor, - borderRadius: BorderRadius.circular(4), - ), - child: Text( - st, - style: fontTextStyle(10, textStatusColor, FontWeight.w400), - ), + + child:Padding( + + padding:EdgeInsets.all(10), + + child:Column( + + crossAxisAlignment:CrossAxisAlignment.start, + + children:[ + + Row( + + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + + children:[ + + Container( + + padding:EdgeInsets.symmetric( + horizontal:8, + vertical:2 + ), + + decoration:BoxDecoration( + color:statusColor, + borderRadius:BorderRadius.circular(4) + ), + + child:Text( + + normalizedStatus, + + style:fontTextStyle( + 10, + textStatusColor, + FontWeight.w400 + ), + + ), + + ), + + buildPriority() + + ], + ), - const SizedBox(height: 6), + + SizedBox(height:6), + if(normalizedStatus == "cancelled" && + widget.order.remarks.isNotEmpty) + + Padding( + + padding: EdgeInsets.only(top:4), + + child: Container( + + padding: EdgeInsets.all(6), + + decoration: BoxDecoration( + + color: Color(0XFFFCEDEC), + + borderRadius: BorderRadius.circular(6), + + border: Border.all( + color: Color(0XFFE2483D) + ), + + ), + + child: Row( + + children:[ + + Icon( + Icons.info_outline, + size:14, + color: Color(0XFFE2483D) + ), + + SizedBox(width:6), + + Expanded( + + child: Text( + + "${widget.order.remarks}", + + style: fontTextStyle( + 10, + Color(0XFFE2483D), + FontWeight.w400 + ), + + ), + + ) + + ], + + ), + + ), + + ), Text( - order.building_name, - style: fontTextStyle(16, const Color(0XFF2D2E30), FontWeight.w600), + widget.order.building_name, + style:fontTextStyle( + 16, + Color(0XFF2D2E30), + FontWeight.w600 + ) ), + Text( - order.displayAddress, - style: fontTextStyle(12, const Color(0XFF646566), FontWeight.w400), + widget.order.displayAddress, + style:fontTextStyle( + 12, + Color(0XFF646566), + FontWeight.w400 + ) ), - const SizedBox(height: 4), + + SizedBox(height:4), Text( - '${order.capacity} - ${order.type_of_water}', - style: fontTextStyle(14, const Color(0XFF444444), FontWeight.w500), + '${widget.order.capacity} - ${widget.order.type_of_water}', + style:fontTextStyle( + 14, + Color(0XFF444444), + FontWeight.w500 + ) ), + + Text( - '${order.time} , ${order.date}', - style: fontTextStyle(8, const Color(0XFF646566), FontWeight.w400), + '${widget.order.time} , ${widget.order.date}', + style:fontTextStyle( + 8, + Color(0XFF646566), + FontWeight.w400 + ) ), - const SizedBox(height: 12), + SizedBox(height:12), + + /// ASSIGN CANCEL Visibility( - visible: order.status.toLowerCase() == 'advance_paid' - || order.status.toLowerCase() == 'accepted', // ⭐ ADDED - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ + + visible:canAssign || canCancel, + + child:Row( + + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + + children:[ + GestureDetector( - onTap: isTodayOrder - ? () async { - final result = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AssignDriverScreen(order: order), - ), + + onTap:canAssign?() async{ + + final result= + await Navigator.push( + + context, + + MaterialPageRoute( + builder:(context)=> + AssignDriverScreen( + order:widget.order + ) + ) + ); - if (result == true) { - onRefresh?.call(); + + if(result==true){ + widget.onRefresh?.call(); } - } - : null, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(22), - border: Border.all( - color: isTodayOrder ? const Color(0XFF939495) : Colors.grey, - ), + + }:null, + + child:Container( + + decoration:BoxDecoration( + + borderRadius: + BorderRadius.circular(22), + + border:Border.all( + color:canAssign + ? Color(0XFF939495) + : Colors.grey + ) + ), - child: Padding( - padding: EdgeInsets.fromLTRB(8, 4, 8, 4), - child: Text( - "Assign", - style: TextStyle( - fontSize: 14, - color: isTodayOrder ? const Color(0XFF515253) : Colors.grey, - fontWeight: FontWeight.w400, - ), + + child:Padding( + + padding: + EdgeInsets.fromLTRB(8,4,8,4), + + child:Text( + + "Assign", + + style:TextStyle( + fontSize:14, + color:canAssign + ? Color(0XFF515253) + : Colors.grey, + fontWeight:FontWeight.w400 ), + + ), + ), + ), + ), + GestureDetector( - onTap: () async{ - /*Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DeliveryUpdatesPage( - orderId: order.dbId, - initialStatus: order.status, - ), - ), - );*/ - final result = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => CancelOrderScreen( - order: order, - status: order.status, - ), - ), + + onTap:canCancel?() async{ + + final result= + await Navigator.push( + + context, + + MaterialPageRoute( + builder:(context)=> + CancelOrderScreen( + order:widget.order, + status:widget.order.status + ) + ) + ); - if (result == true) { - onRefresh?.call(); + if(result==true){ + widget.onRefresh?.call(); } - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(22), - border: Border.all(color: const Color(0XFFE2483D)), - color: const Color(0XFFE2483D), + + }:null, + + child:Container( + + decoration:BoxDecoration( + + borderRadius: + BorderRadius.circular(22), + + border:Border.all( + color:canCancel + ? Color(0XFFE2483D) + : Colors.grey + ), + + color:canCancel + ? Color(0XFFE2483D) + : Colors.grey.shade400 + ), - child: const Padding( - padding: EdgeInsets.fromLTRB(8, 4, 8, 4), - child: Text( + + child:Padding( + + padding: + EdgeInsets.fromLTRB(8,4,8,4), + + child:Text( + "Cancel", - style: TextStyle( - fontSize: 14, - color: Colors.white, - fontWeight: FontWeight.w400), + + style:TextStyle( + fontSize:14, + color:Colors.white, + fontWeight:FontWeight.w400 + ), + ), + ), + ), + ), + ], + ), + ), + /// TRACK Visibility( - visible: order.status.toLowerCase() == 'in_progress', - child: GestureDetector( - onTap: () async { - final result = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AssignDriverScreen(order: order), - ), + + visible:canTrack, + + child:GestureDetector( + + onTap:() async{ + + final result= + await Navigator.push( + + context, + + MaterialPageRoute( + builder:(context)=> + AssignDriverScreen( + order:widget.order + ) + ) + ); - if (result == true) { - onRefresh?.call(); + + if(result==true){ + widget.onRefresh?.call(); } + }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(22), - color: const Color(0XFF8270DB), - border: Border.all(color: const Color(0XFF8270DB)), + + child:Container( + + decoration:BoxDecoration( + + borderRadius: + BorderRadius.circular(22), + + color:Color(0XFF8270DB), + + border:Border.all( + color:Color(0XFF8270DB) + ) + ), - child: const Padding( - padding: EdgeInsets.fromLTRB(8, 4, 8, 4), - child: Text( + + child:Padding( + + padding: + EdgeInsets.fromLTRB(8,4,8,4), + + child:Text( + "Track Delivery", - style: TextStyle( - fontSize: 14, - color: Colors.white, - fontWeight: FontWeight.w400), + + style:TextStyle( + fontSize:14, + color:Colors.white, + fontWeight:FontWeight.w400 + ), + ), + ), + ), + ), + ), + /// ASSIGNED Visibility( - visible: st.toLowerCase() == 'assigned', - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.asset( + + visible:canChange, + + child:Column( + + crossAxisAlignment: + CrossAxisAlignment.start, + + children:[ + + Row( + + children:[ + + Image.asset( 'images/avatar.png', - fit: BoxFit.cover, - width: 12, - height: 12, - ), - const SizedBox(width: 8), - - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: Text( - order.delivery_agent_name!=''? - "Assigned to ${order.delivery_agent_name}" - :"Assigned to ${order.tanker_name}", - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: fontTextStyle( - 8, const Color(0xFF646566), FontWeight.w400), - ), - ), - ], - ), - - ], + width:12, + height:12 + ), + + SizedBox(width:8), + + Expanded( + + child:Text( + + widget.order.delivery_agent_name!='' + ? "Assigned to ${widget.order.delivery_agent_name}" + : "Assigned to ${widget.order.tanker_name}", + + maxLines:1, + + overflow:TextOverflow.ellipsis, + + style:fontTextStyle( + 8, + Color(0XFF646566), + FontWeight.w400 ), + ), - ], - ), - SizedBox(height: MediaQuery.of(context).size.height * .004), - GestureDetector( - onTap: () async { - final result = await Navigator.push( + ) + + ], + + ), + + SizedBox(height:6), + + GestureDetector( + + onTap: canChangeAction ? () async{ + + final result = + await Navigator.push( + context, + MaterialPageRoute( - builder: (context) => ChangeDriverScreen(order: order), - ), - ); - if (result == true) { - onRefresh?.call(); - } - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(22), - color: const Color(0XFF8270DB), - border: Border.all(color: const Color(0XFF8270DB)), - ), - child: const Padding( - padding: EdgeInsets.fromLTRB(8, 4, 8, 4), - child: Text( - "Change", - style: TextStyle( - fontSize: 14, - color: Colors.white, - fontWeight: FontWeight.w400), + builder:(context)=> + ChangeDriverScreen( + order:widget.order + ) + ) + + ); + + if(result==true){ + widget.onRefresh?.call(); + } + + } : null, + + child:Container( + + decoration:BoxDecoration( + + borderRadius: + BorderRadius.circular(22), + + color: canChangeAction + ? Color(0XFF8270DB) + : Colors.grey.shade400, + + border:Border.all( + + color: canChangeAction + ? Color(0XFF8270DB) + : Colors.grey + + ) + + ), + + child:Padding( + + padding: + EdgeInsets.fromLTRB(8,4,8,4), + + child:Text( + + "Change", + + style:TextStyle( + + fontSize:14, + + color: canChangeAction + ? Colors.white + : Colors.white70, + + fontWeight:FontWeight.w400 + ), + ), + ), + ), - ], - ) + + ), + + ], + + ), ), + /// COMPLETED Visibility( - visible: st.toLowerCase() == 'completed', - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.asset( - 'images/avatar.png', - fit: BoxFit.cover, - width: 12, - height: 12, - ), - const SizedBox(width: 8), - - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: Text( - order.delivery_agent_name!=''? - "Delivered by ${order.delivery_agent_name}" - :"Assigned to ${order.tanker_name}", - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: fontTextStyle( - 8, const Color(0xFF646566), FontWeight.w400), - ), - ), - ], - ), - - ], - ), - ), - ], + visible:isCompleted, + + child:Row( + + children:[ + + Image.asset( + 'images/avatar.png', + width:12, + height:12 + ), + + SizedBox(width:8), + + Expanded( + + child:Text( + + widget.order.delivery_agent_name!='' + ? "Delivered by ${widget.order.delivery_agent_name}" + : "Assigned to ${widget.order.tanker_name}", + + maxLines:1, + + overflow:TextOverflow.ellipsis, + + style:fontTextStyle( + 8, + Color(0XFF646566), + FontWeight.w400 + ), + ), - ], - ) + + ) + + ], + + ), ), ], + ), + ), - ), + + ) + ], + ), + ), + ), + ); + } + } diff --git a/lib/orders/assign_driver.dart b/lib/orders/assign_driver.dart index b46e4c9..cb2809b 100644 --- a/lib/orders/assign_driver.dart +++ b/lib/orders/assign_driver.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:supplier_new/common/settings.dart'; import 'package:supplier_new/resources/source_loctaions_model.dart'; import '../resources/drivers_model.dart'; @@ -92,6 +93,165 @@ class _AssignDriverScreenState extends State { } } + DateTime parseOrderDateTime(){ + + DateTime d = + DateFormat("dd-MMM-yyyy") + .parse(widget.order.date); + + DateTime t = + DateFormat("hh:mm a") + .parse(widget.order.time); + + return DateTime( + d.year, + d.month, + d.day, + t.hour, + t.minute + ); + + } + + bool isTankerBlocked(TankersModel tanker){ + + if(tanker.blocked_dates == null || + tanker.blocked_dates.isEmpty){ + return false; + } + + DateTime orderDT = + parseOrderDateTime(); + + for(var slot in tanker.blocked_dates){ + + if(slot['date'] != widget.order.date){ + continue; + } + + String timeRange = + slot['time']; + + List parts = + timeRange.split("to"); + + if(parts.length != 2){ + continue; + } + + DateTime start = + DateFormat("hh:mm a") + .parse(parts[0].trim()); + + DateTime end = + DateFormat("hh:mm a") + .parse(parts[1].trim()); + + DateTime startDT = + DateTime( + orderDT.year, + orderDT.month, + orderDT.day, + start.hour, + start.minute + ); + + DateTime endDT = + DateTime( + orderDT.year, + orderDT.month, + orderDT.day, + end.hour, + end.minute + ); + + /// inside range + if( + orderDT.isAfter(startDT) && + orderDT.isBefore(endDT) + ){ + return true; + } + + /// exact start + if(orderDT == startDT){ + return true; + } + + /// exact end + if(orderDT == endDT){ + return true; + } + + } + + return false; + + } + + String? getDriverBlockedTime(DriversModel driver){ + + if(driver.blocked_dates == null || + driver.blocked_dates.isEmpty){ + return null; + } + + DateTime orderDT = parseOrderDateTime(); + + for(var slot in driver.blocked_dates){ + + if(slot['date'] != widget.order.date){ + continue; + } + + String timeRange = slot['time']; + + List parts = timeRange.split("to"); + + if(parts.length != 2){ + continue; + } + + DateTime start = + DateFormat("hh:mm a") + .parse(parts[0].trim()); + + DateTime end = + DateFormat("hh:mm a") + .parse(parts[1].trim()); + + DateTime startDT = DateTime( + orderDT.year, + orderDT.month, + orderDT.day, + start.hour, + start.minute + ); + + DateTime endDT = DateTime( + orderDT.year, + orderDT.month, + orderDT.day, + end.hour, + end.minute + ); + + if(orderDT.isAfter(startDT) && + orderDT.isBefore(endDT)){ + return timeRange; + } + + if(orderDT == startDT || + orderDT == endDT){ + return timeRange; + } + + } + + return null; + + } + void _showAssignTankerBottomSheet() { int? selectedTankerIndex; int? selectedDriverIndex; @@ -238,55 +398,100 @@ class _AssignDriverScreenState extends State { physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, - itemCount: tankersList - .where((t) => - _capToLiters(t.capacity) == - _capToLiters( - widget.order.capacity)) - .length, + itemCount: tankersList + .where((t) => + _capToLiters(t.capacity) == + _capToLiters(widget.order.capacity) && + t.type_of_water.toLowerCase().trim() == + widget.order.type_of_water.toLowerCase().trim()) + .length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, idx) { - final filteredTankers = tankersList - .where((t) => - _capToLiters(t.capacity) == - _capToLiters( - widget.order.capacity)) - .toList(); + final filteredTankers = tankersList.where((t) => + _capToLiters(t.capacity) == + _capToLiters(widget.order.capacity) && + t.type_of_water.toLowerCase().trim() == + widget.order.type_of_water.toLowerCase().trim() + ).toList(); final d = filteredTankers[idx]; + + bool blocked = + isTankerBlocked(d); + final isSelected = selectedTankerIndex == idx; return GestureDetector( - onTap: () { + onTap: blocked ? null : () { + setModalState(() { + selectedTankerIndex = idx; - selectedDriverIndex = - null; // reset driver selection if tanker changes + selectedDriverIndex=null; + }); + }, + child: Opacity( + + opacity: blocked ? 0.4 : 1, child: Card( elevation: 1, shape: RoundedRectangleBorder( borderRadius: - BorderRadius.circular(12), + BorderRadius.circular(12), side: BorderSide( - color: isSelected + color: blocked + ? Colors.grey + : (isSelected ? primaryColor - : const Color(0XFFC3C4C4), + : 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), - ), + + child: Column( + + crossAxisAlignment: + CrossAxisAlignment.start, + + children:[ + 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), + ), + if(blocked) + Padding( + + padding: EdgeInsets.only( + left:10, + top:4 + ), + + child: Text( + + "Booked at ${widget.order.time}", + + style: fontTextStyle( + 10, + Colors.red, + FontWeight.w500 + ), + + ), + + ), + ]), ), + ) + + ); }, )), @@ -298,163 +503,294 @@ class _AssignDriverScreenState extends State { 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), - ), - ), - ) + + 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( + ? 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; + + String? blockedTime = + getDriverBlockedTime(d); + + bool blocked = + blockedTime != null; + + String driverStatus = + d.status.toLowerCase().trim(); + + final isAvailable = + driverStatus == "available" && + !blocked; + + final statusColor = blocked + ? Colors.red + : (isAvailable + ? const Color(0XFF0A9E04) + : (driverStatus == "on delivery" + ? const Color(0XFFD0AE3C) + : (driverStatus == "offline" + ? const Color(0XFF939495) + : Colors.grey))); + + return GestureDetector( + + onTap: isAvailable ? () { + + setModalState(() { + + selectedDriverIndex = idx; + + }); + + } : null, + + child: Opacity( + + opacity: + isAvailable ? 1 : 0.4, + + 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: Column( + + crossAxisAlignment: + CrossAxisAlignment.start, + + children: [ + + Text( + + d.driver_name, + + style: + fontTextStyle( + + 14, + + const Color( + 0XFF2D2E30), + + FontWeight + .w500), + + ), + + if(blockedTime != null) + Text( + "Booked $blockedTime", + style: fontTextStyle( + 10, + Colors.red, + FontWeight.w500), + ), + + ], + + ), + + ), + + const SizedBox( + width: 8), + + Container( + padding: - const EdgeInsets.symmetric( - vertical: 12), + const EdgeInsets + .symmetric( + + horizontal: 6, + + vertical: 2), + + decoration: + BoxDecoration( + + borderRadius: + BorderRadius + .circular( + 4), + + border: Border.all( + + color: + statusColor), + + ), + child: Text( - 'No Data Available', - style: fontTextStyle( - 12, - const Color(0xFF939495), - FontWeight.w500), + + blocked + ? "booked" + : d.status, + + style: + fontTextStyle( + + 10, + + statusColor, + + FontWeight + .w400), + ), + ), - ) - : 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), - ), - ), - ], - ), - ), - ), - ); - }, - )), + + ], + + ), + + ), + + ), + + ), + + ); + + }, + + )), const SizedBox(height: 8), diff --git a/lib/orders/assign_driver_new_design.dart b/lib/orders/assign_driver_new_design.dart new file mode 100644 index 0000000..510ff94 --- /dev/null +++ b/lib/orders/assign_driver_new_design.dart @@ -0,0 +1,639 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:flutter_polyline_points/flutter_polyline_points.dart'; +import 'package:http/http.dart' as http; +import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/source_loctaions_model.dart'; +import '../resources/drivers_model.dart'; +import '../resources/tankers_model.dart'; + +class AssignDriverScreenNewDesign extends StatefulWidget { + final dynamic order; + final dynamic status; + + const AssignDriverScreenNewDesign({ + super.key, + this.order, + this.status, + }); + + @override + State createState() => + _AssignDriverScreenNewDesignState(); +} + +class _AssignDriverScreenNewDesignState + extends State { + GoogleMapController? _mapController; + + List driversList = []; + List tankersList = []; + List sourceLocationsList = []; + + DriversModel? selectedDriver; + TankersModel? selectedTanker; + SourceLocationsModel? selectedSource; + + bool loading = true; + bool routeLoading = false; + + Set _markers = {}; + Set _polylines = {}; + + String eta = ''; + String distanceText = ''; + String routeError = ''; + + static const String googleApiKey = 'AIzaSyDJpK9RVhlBejtJu9xSGfneuTN6HOfJgSM'; + + @override + void initState() { + super.initState(); + loadData(); + } + + Future loadData() async { + try { + final d = await AppSettings.getDrivers(); + driversList = (jsonDecode(d)['data'] as List) + .map((e) => DriversModel.fromJson(e)) + .toList(); + + final t = await AppSettings.getTankers(); + tankersList = (jsonDecode(t)['data'] as List) + .map((e) => TankersModel.fromJson(e)) + .toList(); + + final s = await AppSettings.getSourceLoctaions(); + sourceLocationsList = (jsonDecode(s)['data'] as List) + .map((e) => SourceLocationsModel.fromJson(e)) + .toList(); + } catch (e) { + debugPrint('Load error: $e'); + } + + if (!mounted) return; + + setState(() { + loading = false; + _rebuildMarkers(); + }); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _fitMap(); + }); + } + + double? _toDouble(dynamic value) { + if (value == null) return null; + if (value is double) return value; + if (value is int) return value.toDouble(); + return double.tryParse(value.toString().trim()); + } + + LatLng? _deliveryPosition() { + try { + final lat = _toDouble( + widget.order?.lat ?? + widget.order?.delivery_lat ?? + widget.order?.location_lat, + ); + final lng = _toDouble( + widget.order?.lng ?? + widget.order?.delivery_lng ?? + widget.order?.location_lng, + ); + + if (lat == null || lng == null) return null; + return LatLng(lat, lng); + } catch (e) { + debugPrint('Delivery parse error: $e'); + return null; + } + } + + LatLng? _sourcePosition(SourceLocationsModel source) { + try { + final lat = _toDouble(source.latitude); + final lng = _toDouble(source.longitude); + if (lat == null || lng == null) return null; + return LatLng(lat, lng); + } catch (e) { + debugPrint('Source parse error: $e'); + return null; + } + } + + void _rebuildMarkers() { + final Set markers = {}; + + final delivery = _deliveryPosition(); + if (delivery != null) { + markers.add( + Marker( + markerId: const MarkerId('delivery'), + position: delivery, + icon: BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueRed, + ), + infoWindow: InfoWindow( + title: widget.order?.building_name?.toString() ?? 'Delivery', + snippet: 'Customer location', + ), + ), + ); + } + + for (final source in sourceLocationsList) { + final pos = _sourcePosition(source); + if (pos == null) continue; + + final bool isSelected = selectedSource?.dbId == source.dbId; + + markers.add( + Marker( + markerId: MarkerId('source_${source.dbId}'), + position: pos, + icon: BitmapDescriptor.defaultMarkerWithHue( + isSelected + ? BitmapDescriptor.hueViolet + : BitmapDescriptor.hueAzure, + ), + infoWindow: InfoWindow( + title: source.source_name?.toString() ?? 'Source', + snippet: isSelected ? 'Selected source' : 'Tap to select source', + ), + onTap: () async { + setState(() { + selectedSource = source; + routeError = ''; + eta = ''; + distanceText = ''; + _rebuildMarkers(); + }); + + await _loadRouteAndEta(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _fitMap(); + }); + }, + ), + ); + } + + _markers = markers; + } + + Future _loadRouteAndEta() async { + final source = selectedSource; + final sourcePos = source == null ? null : _sourcePosition(source); + final deliveryPos = _deliveryPosition(); + + if (sourcePos == null || deliveryPos == null) { + setState(() { + routeError = 'Source or delivery coordinates missing'; + _polylines = {}; + }); + return; + } + + setState(() { + routeLoading = true; + routeError = ''; + }); + + try { + await Future.wait([ + _loadDirectionsPolyline(sourcePos, deliveryPos), + _loadEtaWithDirections(sourcePos, deliveryPos), + ]); + } catch (e) { + debugPrint('Route load error: $e'); + if (mounted) { + setState(() { + routeError = 'Unable to load route'; + }); + } + } + + if (!mounted) return; + setState(() { + routeLoading = false; + }); + } + + Future _loadEtaWithDirections( + LatLng origin, + LatLng destination, + ) async { + try { + final uri = Uri.parse( + 'https://maps.googleapis.com/maps/api/directions/json' + '?origin=${origin.latitude},${origin.longitude}' + '&destination=${destination.latitude},${destination.longitude}' + '&mode=driving' + '&departure_time=now' + '&traffic_model=best_guess' + '&key=$googleApiKey', + ); + + final response = await http.get(uri); + final data = jsonDecode(response.body); + + debugPrint('Directions ETA response: $data'); + + if (data['status'] != 'OK') { + setState(() { + routeError = 'ETA unavailable: ${data['status']}'; + }); + return; + } + + final routes = data['routes'] as List?; + if (routes == null || routes.isEmpty) { + setState(() { + routeError = 'ETA unavailable'; + }); + return; + } + + final legs = routes.first['legs'] as List?; + if (legs == null || legs.isEmpty) { + setState(() { + routeError = 'ETA unavailable'; + }); + return; + } + + final leg = legs.first; + final distance = leg['distance']; + final duration = leg['duration']; + final durationInTraffic = leg['duration_in_traffic']; + + setState(() { + distanceText = (distance?['text'] ?? '').toString(); + eta = (durationInTraffic?['text'] ?? duration?['text'] ?? '').toString(); + }); + } catch (e) { + debugPrint('ETA error: $e'); + setState(() { + routeError = 'ETA request failed'; + }); + } + } + + Future _loadDirectionsPolyline( + LatLng origin, + LatLng destination, + ) async { + try { + final uri = Uri.parse( + 'https://maps.googleapis.com/maps/api/directions/json' + '?origin=${origin.latitude},${origin.longitude}' + '&destination=${destination.latitude},${destination.longitude}' + '&mode=driving' + '&departure_time=now' + '&traffic_model=best_guess' + '&key=$googleApiKey', + ); + + final response = await http.get(uri); + final data = jsonDecode(response.body); + + debugPrint('Directions route response: $data'); + + if (data['status'] != 'OK') { + setState(() { + routeError = 'Route unavailable: ${data['status']}'; + _polylines = {}; + }); + return; + } + + final routes = data['routes'] as List?; + if (routes == null || routes.isEmpty) { + setState(() { + routeError = 'No route found'; + _polylines = {}; + }); + return; + } + + final encoded = routes.first['overview_polyline']?['points']?.toString(); + if (encoded == null || encoded.isEmpty) { + setState(() { + routeError = 'Route polyline missing'; + _polylines = {}; + }); + return; + } + + final polylinePoints = PolylinePoints(); + final decoded = polylinePoints.decodePolyline(encoded); + + final points = decoded + .map((p) => LatLng(p.latitude, p.longitude)) + .toList(); + + if (points.isEmpty) { + setState(() { + routeError = 'Failed to decode route'; + _polylines = {}; + }); + return; + } + + setState(() { + _polylines = { + Polyline( + polylineId: const PolylineId('source_delivery_route'), + points: points, + color: Colors.blue, + width: 6, + ), + }; + }); + } catch (e) { + debugPrint('Polyline error: $e'); + setState(() { + routeError = 'Route request failed'; + _polylines = {}; + }); + } + } + + Future _fitMap() async { + if (_mapController == null) return; + + final List points = []; + + final delivery = _deliveryPosition(); + if (delivery != null) points.add(delivery); + + for (final source in sourceLocationsList) { + final pos = _sourcePosition(source); + if (pos != null) points.add(pos); + } + + if (points.isEmpty) return; + + if (points.length == 1) { + await _mapController!.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition(target: points.first, zoom: 14), + ), + ); + return; + } + + double minLat = points.first.latitude; + double maxLat = points.first.latitude; + double minLng = points.first.longitude; + double maxLng = points.first.longitude; + + for (final point in points) { + if (point.latitude < minLat) minLat = point.latitude; + if (point.latitude > maxLat) maxLat = point.latitude; + if (point.longitude < minLng) minLng = point.longitude; + if (point.longitude > maxLng) maxLng = point.longitude; + } + + try { + await _mapController!.animateCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + southwest: LatLng(minLat, minLng), + northeast: LatLng(maxLat, maxLng), + ), + 80, + ), + ); + } catch (_) { + await Future.delayed(const Duration(milliseconds: 300)); + try { + await _mapController!.animateCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + southwest: LatLng(minLat, minLng), + northeast: LatLng(maxLat, maxLng), + ), + 80, + ), + ); + } catch (_) {} + } + } + + Future assign() async { + if (selectedDriver == null) { + _msg('Select driver'); + return; + } + + if (selectedTanker == null) { + _msg('Select tanker'); + return; + } + + if (selectedSource == null) { + _msg('Select source from map'); + return; + } + + final payload = {}; + payload["tankerName"] = selectedTanker!.tanker_name; + payload["delivery_agent"] = selectedDriver!.driver_name; + payload["delivery_agent_mobile"] = selectedDriver!.phone_number; + payload["water_source_location"] = selectedSource!.source_name; + + AppSettings.preLoaderDialog(context); + + bool status = false; + try { + status = await AppSettings.assignTanker(payload, widget.order.dbId); + } catch (e) { + debugPrint('Assign error: $e'); + } + + if (mounted) { + Navigator.pop(context); + } + + if (!mounted) return; + + if (status) { + _msg('Assigned successfully'); + Navigator.pop(context, true); + } else { + _msg('Assignment failed'); + } + } + + void _msg(String message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(message)), + ); + } + + Widget _buildInfoRow(IconData icon, String text) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + Icon(icon, size: 18), + const SizedBox(width: 8), + Expanded(child: Text(text)), + ], + ), + ); + } + + Widget _buildBottomCard() { + return Container( + margin: const EdgeInsets.all(15), + padding: const EdgeInsets.all(15), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(18), + boxShadow: const [ + BoxShadow( + blurRadius: 10, + color: Colors.black26, + offset: Offset(0, 3), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + DropdownButton( + isExpanded: true, + hint: const Text('Select Driver'), + value: selectedDriver, + items: driversList.map((driver) { + return DropdownMenuItem( + value: driver, + child: Text(driver.driver_name?.toString() ?? 'Driver'), + ); + }).toList(), + onChanged: (value) { + setState(() { + selectedDriver = value; + }); + }, + ), + const SizedBox(height: 8), + DropdownButton( + isExpanded: true, + hint: const Text('Select Tanker'), + value: selectedTanker, + items: tankersList.map((tanker) { + return DropdownMenuItem( + value: tanker, + child: Text( + '${tanker.tanker_name} (${tanker.capacity})', + ), + ); + }).toList(), + onChanged: (value) { + setState(() { + selectedTanker = value; + }); + }, + ), + const SizedBox(height: 8), + _buildInfoRow( + Icons.water_drop_outlined, + selectedSource?.source_name?.toString() ?? 'Select source from map', + ), + if (routeLoading) ...[ + const SizedBox(height: 8), + const LinearProgressIndicator(), + ], + if (distanceText.isNotEmpty || eta.isNotEmpty) ...[ + const SizedBox(height: 10), + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0XFFF5F4FF), + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + if (distanceText.isNotEmpty) + _buildInfoRow(Icons.route, 'Distance: $distanceText'), + if (eta.isNotEmpty) + _buildInfoRow(Icons.timer_outlined, 'ETA: $eta'), + ], + ), + ), + ], + if (routeError.isNotEmpty) ...[ + const SizedBox(height: 8), + Text( + routeError, + style: const TextStyle( + color: Colors.red, + fontSize: 12, + ), + ), + ], + const SizedBox(height: 12), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: assign, + child: const Text('ASSIGN'), + ), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + if (loading) { + return const Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), + ); + } + + return Scaffold( + appBar: AppBar( + title: const Text('Enterprise Dispatch Map'), + ), + body: Stack( + children: [ + GoogleMap( + initialCameraPosition: CameraPosition( + target: _deliveryPosition() ?? const LatLng(17.3850, 78.4867), + zoom: 12, + ), + markers: _markers, + polylines: _polylines, + trafficEnabled: true, + zoomControlsEnabled: true, + myLocationButtonEnabled: true, + mapToolbarEnabled: false, + onMapCreated: (controller) { + _mapController = controller; + Future.delayed(const Duration(milliseconds: 400), () { + _fitMap(); + }); + }, + ), + Align( + alignment: Alignment.bottomCenter, + child: _buildBottomCard(), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/orders/best_options.dart b/lib/orders/best_options.dart new file mode 100644 index 0000000..395bdbc --- /dev/null +++ b/lib/orders/best_options.dart @@ -0,0 +1,428 @@ +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +class TankerOptionsDemo extends StatefulWidget { + + @override + State createState() => _TankerOptionsDemoState(); + +} + +class _TankerOptionsDemoState extends State { + + GoogleMapController? mapController; + + LatLng customerLocation = + LatLng(17.3850,78.4867); + + String selectedTanker="TNK1"; + + List tankers=[ + + { + "id":"TNK1", + "lat":17.387, + "lng":78.489, + "distance":2.5, + "eta":15, + "status":"AVAILABLE" + }, + + { + "id":"TNK2", + "lat":17.381, + "lng":78.480, + "distance":5, + "eta":30, + "status":"ON_DELIVERY", + "availableIn":20 + }, + + { + "id":"TNK3", + "lat":17.370, + "lng":78.470, + "distance":9, + "eta":50, + "status":"FAR" + } + + ]; + + Set markers={}; + + @override + void initState(){ + + super.initState(); + + createMarkers(); + + } + + void createMarkers(){ + + markers.add( + + Marker( + + markerId: MarkerId("customer"), + + position: customerLocation, + + infoWindow: + InfoWindow(title:"Delivery Location"), + + icon: BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueBlue) + + ) + + ); + + for(var t in tankers){ + + BitmapDescriptor color; + + if(t["status"]=="AVAILABLE"){ + + color= + BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueGreen); + + } + + else if(t["status"]=="ON_DELIVERY"){ + + color= + BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueOrange); + + } + + else{ + + color= + BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueRed); + + } + + markers.add( + + Marker( + + markerId: MarkerId(t["id"]), + + position: LatLng( + t["lat"], + t["lng"] + ), + + infoWindow: + InfoWindow( + + title:t["id"], + + snippet: + "ETA ${t["eta"]} min" + + ), + + icon:color + + ) + + ); + + } + + setState(() {}); + + } + + @override + Widget build(BuildContext context){ + + return Scaffold( + + appBar: AppBar( + + title: + Text("Select Tanker"), + + backgroundColor: + Colors.white, + + ), + + body: + + Column( + + children:[ + + Container( + + height:300, + + child: + + GoogleMap( + + initialCameraPosition: + + CameraPosition( + + target: + customerLocation, + + zoom:13 + + ), + + markers:markers, + + ), + + ), + + Expanded( + + child: + + ListView( + + children: + + tankers.map((t){ + + return tankerCard(t); + + }).toList(), + + ) + + ) + + ], + + ), + + bottomNavigationBar: + + Container( + + padding:EdgeInsets.all(12), + + child: + + ElevatedButton( + + style: + + ElevatedButton.styleFrom( + + backgroundColor: + Colors.green + + ), + + onPressed:(){ + + Navigator.pop(context); + + }, + + child: + + Text("Confirm Selection") + + ), + + ), + + ); + + } + + Widget tankerCard(var tanker){ + + Color color; + + if(tanker["status"]=="AVAILABLE"){ + + color=Colors.green; + + } + + else if(tanker["status"]=="ON_DELIVERY"){ + + color=Colors.orange; + + } + + else{ + + color=Colors.red; + + } + + return GestureDetector( + + onTap:(){ + + selectedTanker=tanker["id"]; + + setState(() {}); + + }, + + child: + + Container( + + margin: + EdgeInsets.all(10), + + padding: + EdgeInsets.all(12), + + decoration: + + BoxDecoration( + + borderRadius: + BorderRadius.circular(10), + + border: + + Border.all( + + color: + + selectedTanker==tanker["id"] + + ? + + Colors.green + + : + + Colors.grey + + ), + + ), + + child: + + Column( + + crossAxisAlignment: + CrossAxisAlignment.start, + + children:[ + + Row( + + children:[ + + Text( + + tanker["id"], + + style: + + TextStyle( + + fontSize:18, + + fontWeight: + FontWeight.bold + + ), + + ), + + Spacer(), + + Container( + + padding: + EdgeInsets.all(6), + + decoration: + + BoxDecoration( + + color: + color, + + borderRadius: + BorderRadius.circular(5) + + ), + + child: + + Text( + + tanker["status"], + + style: + + TextStyle( + color:Colors.white) + + ), + + ) + + ], + + ), + + SizedBox(height:8), + + Text( + "Distance : ${tanker["distance"]} km"), + + Text( + "ETA : ${tanker["eta"]} min"), + + if(tanker["status"]=="ON_DELIVERY") + + Text( + "Available in ${tanker["availableIn"]} min"), + + if(tanker["status"]=="FAR") + + Text( + "⚠ Transport charges higher"), + + SizedBox(height:6), + + if(tanker["status"]=="AVAILABLE") + + Text( + "BEST OPTION", + + style: + + TextStyle( + color:Colors.green) + + ) + + ], + + ), + + ), + + ); + + } + +} \ No newline at end of file diff --git a/lib/orders/cancel_order.dart b/lib/orders/cancel_order.dart index b1bd0e9..be57db3 100644 --- a/lib/orders/cancel_order.dart +++ b/lib/orders/cancel_order.dart @@ -16,7 +16,6 @@ class _CancelOrderScreenState extends State { final TextEditingController reasonController = TextEditingController(); final List reasons = [ - "Changed my mind", "Tanker not available", "Vehicle got damaged", "Buyer asked me to cancel", diff --git a/lib/orders/orders_model.dart b/lib/orders/orders_model.dart index a554750..6eb7779 100644 --- a/lib/orders/orders_model.dart +++ b/lib/orders/orders_model.dart @@ -24,6 +24,7 @@ class OrdersModel { String delivery_agent_name = ''; String tanker_name = ''; String water_source_location=''; + String remarks=''; OrdersModel(); @@ -45,6 +46,7 @@ class OrdersModel { rtvm.delivery_agent_name = json['delivery_agent'] ?? ''; rtvm.tanker_name = json['tankerName'] ?? ''; rtvm.water_source_location = json['water_source_location'] ?? ''; + rtvm.remarks = json['remarks'] ?? ''; // Split and trim List parts = rtvm.address.split(',').map((e) => e.trim()).toList(); diff --git a/lib/resources/driver_details.dart b/lib/resources/driver_details.dart index 0347c05..890707c 100644 --- a/lib/resources/driver_details.dart +++ b/lib/resources/driver_details.dart @@ -336,8 +336,12 @@ class _DriverDetailsPageState extends State { label: "Age *", child: TextFormField( controller: _ageCtrl, - validator: (v) => - _validatePhone(v, label: "Age"), + validator: (value) { + if (value == null || value.trim().isEmpty) { + return "Driver Age required"; + } + return null; + }, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, diff --git a/lib/resources/drivers_model.dart b/lib/resources/drivers_model.dart index 60885a4..fefbb67 100644 --- a/lib/resources/drivers_model.dart +++ b/lib/resources/drivers_model.dart @@ -13,7 +13,9 @@ class DriversModel { String age=''; String license_expiry_date=''; String picture=''; + String dbId=''; List licenseImages= []; + List blocked_dates = []; DriversModel(); factory DriversModel.fromJson(Map json) { @@ -22,6 +24,7 @@ class DriversModel { rtvm.supplier_name = json['suppliername'] ?? ''; rtvm.driver_name = json['name'] ?? ''; // ✅ Correct rtvm.status = json['status'] ?? ''; + rtvm.dbId = json['_id'] ?? ''; rtvm.address = json['address'] ?? ''; rtvm.phone_number = json['phone'] ?? ''; rtvm.alt_phone_number = json['alternativeContactNumber'] ?? ''; @@ -32,6 +35,8 @@ class DriversModel { rtvm.license_expiry_date=json['license_number'] ?? ''; rtvm.age=json['age'] ?? ''; rtvm.licenseImages = json['images'] ?? []; + rtvm.blocked_dates = + json['blocked_dates'] ?? []; return rtvm; } } \ No newline at end of file diff --git a/lib/resources/resources_fleet.dart b/lib/resources/resources_fleet.dart index 072a24e..f9cae21 100644 --- a/lib/resources/resources_fleet.dart +++ b/lib/resources/resources_fleet.dart @@ -1395,8 +1395,14 @@ class _ResourcesFleetScreenState extends State { final q = search.trim().toLowerCase(); + String capacityClean = + it.capacity.replaceAll(",", ""); + bool matchesSearch = - q.isEmpty || it.tanker_name.toLowerCase().contains(q); + q.isEmpty || + it.tanker_name.toLowerCase().contains(q) || + capacityClean.contains(q.replaceAll(",", "")) || + it.type_of_water.toLowerCase().contains(q); bool matchesFilter = true; @@ -1512,16 +1518,52 @@ class _ResourcesFleetScreenState extends State { ), const SizedBox(height: 12), IntrinsicHeight( - child: Row( + child: /*Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded(child: SmallMetricBox(title: 'Active', value: activeCount.toString())), - const SizedBox(width: 8), - Expanded(child: SmallMetricBox(title: 'Inactive', value: inactiveCount.toString())), + *//*const SizedBox(width: 8), + Expanded(child: SmallMetricBox(title: 'Inactive', value: inactiveCount.toString())),*//* const SizedBox(width: 8), Expanded(child: SmallMetricBox(title: 'Under Maintenance', value: maintenanceCount.toString())), ], - ), + ),*/ + Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + + Expanded( + child: GestureDetector( + onTap: (){ + setState(() { + selectedFilter = "Active"; + }); + }, + child: SmallMetricBox( + title: 'Active', + value: activeCount.toString() + ), + ), + ), + + SizedBox(width: 8), + + Expanded( + child: GestureDetector( + onTap: (){ + setState(() { + selectedFilter = "Maintenance"; + }); + }, + child: SmallMetricBox( + title: 'Under Maintenance', + value: maintenanceCount.toString() + ), + ), + ), + + ], + ) ), ], ), diff --git a/lib/resources/tankers_model.dart b/lib/resources/tankers_model.dart index 55f04ae..da38299 100644 --- a/lib/resources/tankers_model.dart +++ b/lib/resources/tankers_model.dart @@ -1,4 +1,5 @@ -class TankersModel { +class TankersModel { + String tanker_name = ''; String address = ''; String type_of_water = ''; @@ -13,32 +14,77 @@ class TankersModel { String insurance_expiry=''; String tanker_type=''; String pumping_fee=''; + List availability= []; List images= []; + + /// ADD THIS + List blocked_dates = []; + TankersModel(); factory TankersModel.fromJson(Map json){ - TankersModel rtvm = new TankersModel(); - - rtvm.tanker_name = json['tankerName']?? ''; - rtvm.dbId = json['_id']?? ''; - rtvm.address = json['supplier_address']?? ''; - rtvm.type_of_water = json['typeofwater'] ?? ''; - rtvm.capacity = json['capacity'] ?? ''; - rtvm.license_plate = json['license_plate'] ?? ''; - rtvm.supplier_name = json['supplier_name'] ?? ''; - rtvm.price = json['price'] ?? ''; - rtvm.delivery_fee = json['delivery_fee'] ?? ''; - rtvm.availability = json['availability'] ?? []; - rtvm.images = json['images'] ?? []; - - rtvm.manufacturing_year = json['manufacturing_year'] ?? ''; - rtvm.insurance_expiry = json['insurance_exp_date'] ?? ''; - rtvm.tanker_type = json['tanker_type'] ?? ''; - rtvm.pumping_fee = json['pumping_fee'] ?? ''; + + TankersModel rtvm = + TankersModel(); + + rtvm.tanker_name = + json['tankerName'] ?? ''; + + rtvm.dbId = + json['_id'] ?? ''; + + rtvm.address = + json['supplier_address'] ?? ''; + + rtvm.type_of_water = + json['typeofwater'] ?? ''; + + rtvm.capacity = + json['capacity'] ?? ''; + + rtvm.license_plate = + json['license_plate'] ?? ''; + + rtvm.supplier_name = + json['supplier_name'] ?? ''; + + rtvm.price = + json['price'] ?? ''; + + rtvm.delivery_fee = + json['delivery_fee'] ?? ''; + + rtvm.availability = + json['availability'] ?? []; + + rtvm.images = + json['images'] ?? []; + + /// ADD THIS + rtvm.blocked_dates = + json['blocked_dates'] ?? []; + + rtvm.manufacturing_year = + json['manufacturing_year'] ?? ''; + + rtvm.insurance_expiry = + json['insurance_exp_date'] ?? ''; + + rtvm.tanker_type = + json['tanker_type'] ?? ''; + + rtvm.pumping_fee = + json['pumping_fee'] ?? ''; + return rtvm; + } + Map toJson() => { - "boreName":this.tanker_name, + + "tankerName": tanker_name, + }; + } \ No newline at end of file