import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:supplier_new/common/settings.dart'; import '../resources/tankers_model.dart'; class SetRatesScreen extends StatefulWidget { const SetRatesScreen({super.key}); @override State createState() => _SetRatesScreenState(); } class _SetRatesScreenState extends State with SingleTickerProviderStateMixin { late TabController _tabController; final Map controllers = {}; // Define categories + tankers final Map> tankerGroups = {}; final Map deliveryControllers = {}; /*delivery fee controllers*/ final TextEditingController pumpFeeController = TextEditingController(); List tankersList = []; bool isLoading = false; Future _fetchTankers() async { setState(() => isLoading = true); try { final response = await AppSettings.getTankers(); final data = (jsonDecode(response)['data'] as List) .map((e) => TankersModel.fromJson(e)) .toList(); if (!mounted) return; setState(() { // 🧹 Clear old data before adding new tankersList.clear(); tankerGroups.clear(); controllers.clear(); deliveryControllers.clear(); // 🆕 Assign new data tankersList = data; isLoading = false; // 🧭 Group data and create controllers for (final item in data) { final category = item.type_of_water; final size = item.capacity; final price = item.price.toString(); if (!tankerGroups.containsKey(category)) { tankerGroups[category] = []; } // avoid duplicate sizes for same category if (!tankerGroups[category]!.contains(size)) { tankerGroups[category]!.add(size); } final controllerKey = "$category-$size"; controllers[controllerKey] = TextEditingController(text: price); } final capacities = tankersList.map((t) => normalizeCap(t.capacity)).toSet(); for (final cap in capacities) { // take the first tanker of this capacity to prefill (optional) final matched = tankersList.firstWhere( (t) => normalizeCap(t.capacity) == cap, orElse: () => TankersModel(), ); final deliveryFee = matched.delivery_fee.toString() ?? ""; deliveryControllers.putIfAbsent( cap, () => TextEditingController(text: deliveryFee), ); } }); } catch (e) { debugPrint("⚠️ Error fetching tankers: $e"); setState(() => isLoading = false); } } Future _fetchPumpFee() async { try { final response = await AppSettings.getSupplierDetails(); final data = jsonDecode(response); if (!mounted) return; setState(() { pumpFeeController.text=data['pumping_fee']; }); } catch (e) { debugPrint("⚠️ Error fetching tankers: $e"); } } @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); _fetchTankers(); _fetchPumpFee(); } @override void dispose() { // Dispose all controllers for (final controller in controllers.values) { controller.dispose(); } for (final controller in deliveryControllers.values) { controller.dispose(); } super.dispose(); } Widget buildTextField(String label, TextEditingController controller) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: TextField( controller: controller, cursorColor: primaryColor, readOnly: false, keyboardType: TextInputType.number, decoration: InputDecoration( counterText: '', filled: false, fillColor: Colors.white, prefixIconConstraints: BoxConstraints( minWidth: 24, minHeight: 24, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(4.0), borderSide: BorderSide( color: Color(0XFFC3C4C4), width: 1, )), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(4.0), borderSide: BorderSide( color: Color(0XFF8270DB), width: 1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(4.0), borderSide: BorderSide(color: Color(0XFFC3C4C4)), ), hintText: label, hintStyle: fontTextStyle(14, Color(0XFF939495), FontWeight.w400), /* TextStyle(color: greyColor, fontWeight: FontWeight.bold //<-- SEE HERE ),*/ ), style: fontTextStyle(14, Color(0XFF515253), FontWeight.w500), )); } Widget labelText(String label) { return Text( label, style: fontTextStyle(12, const Color(0XFF515253), FontWeight.w500), ); } Widget WaterCharges() { return isLoading ? const Center(child: CircularProgressIndicator()):SingleChildScrollView( padding: const EdgeInsets.all(16), child: tankersList.isEmpty?Center( child: Text( 'No Data Available', style: fontTextStyle(16,Color(0XFF000000),FontWeight.w700), ), ) :Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (final entry in tankerGroups.entries) ...[ Text( entry.key, style: fontTextStyle(10, const Color(0XFF2D2E30), FontWeight.w600), ), const SizedBox(height: 8), for (final size in entry.value) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ labelText("$size L (in ₹)*"), const SizedBox(height: 4), buildTextField( "₹500", controllers["${entry.key}-$size"]!, ), const SizedBox(height: 12), ], ), const SizedBox(height: 10), ], SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0XFF8270DB), foregroundColor: const Color(0XFFFFFFFF), padding: const EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), ), ), onPressed: () async { final List> tankersPayload = []; tankerGroups.forEach((category, sizes) { for (final size in sizes) { final key = "$category-$size"; final amount = controllers[key]?.text.trim() ?? "0"; // find the tanker in tankersList by capacity (size) final matchedTanker = tankersList.firstWhere( (t) => t.capacity == size, orElse: () => TankersModel(), ); final tankerName = matchedTanker.tanker_name.isNotEmpty ? matchedTanker.tanker_name : "$category $size L"; // fallback if not found tankersPayload.add({ "tankerName": tankerName, "amount": amount.isEmpty ? "0" : amount, }); } }); final payload = { "price_type": "price", "tankers": tankersPayload, }; print(payload); try { final ok = await AppSettings.setRatesDaily(payload); if (ok) { AppSettings.longSuccessToast("Prices updated successfully"); _fetchTankers(); } else { AppSettings.longFailedToast("Update failed"); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Error: $e")), ); } }, child: Text( "Save", style: fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600), ), ), ) ], ), ); } String normalizeCap(String cap) => cap.replaceAll(',', '').replaceAll(' ', ''); Widget DeliveryFee() { // Group by normalized capacity final Map> capacityGroups = {}; for (final t in tankersList) { final normCap = normalizeCap(t.capacity); capacityGroups.putIfAbsent(normCap, () => []).add(t); } // Sorted list by numeric capacity (if parseable), else lexicographic final capacities = capacityGroups.keys.toList() ..sort((a, b) { final ai = int.tryParse(a); final bi = int.tryParse(b); if (ai != null && bi != null) return ai.compareTo(bi); return a.compareTo(b); }); return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "ADDITIONAL RATES", style: fontTextStyle(10, const Color(0xFF2D2E30), FontWeight.w600), ), SizedBox(height: MediaQuery.of(context).size.height * .004), Text( "Add your price per Kilometer for every tanker capacity", style: fontTextStyle(12, const Color(0xFF939495), FontWeight.w400), ), SizedBox(height: MediaQuery.of(context).size.height * .020), // one field per normalized capacity for (final normCap in capacities) ...[ // show without commas (normalized already) labelText('${normCap} L tanker (per KM)*'), SizedBox(height: MediaQuery.of(context).size.height * .004), buildTextField( "+ ₹12", // controller keyed by normalized cap deliveryControllers[normCap] ?? TextEditingController(), ), const SizedBox(height: 12), ], const SizedBox(height: 10), SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: const Color(0XFF8270DB), foregroundColor: const Color(0XFFFFFFFF), padding: const EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), ), ), onPressed: () async { final List> tankersPayload = []; // loop groups; use the same fee for all tankers in the group capacityGroups.forEach((normCap, list) { final fee = (deliveryControllers[normCap]?.text ?? "").trim(); final amt = fee.isEmpty ? "0" : fee; for (final t in list) { final tankerName = t.tanker_name.isNotEmpty ? t.tanker_name : "Tanker ${normalizeCap(t.capacity)} L"; tankersPayload.add({ "tankerName": tankerName, "amount": amt, }); } }); final payload = { "price_type": "delivery_fee", "tankers": tankersPayload, }; print(payload); try { final ok = await AppSettings.setRatesDaily(payload); if (ok) { AppSettings.longSuccessToast("Delivery fees updated successfully"); _fetchTankers(); } else { AppSettings.longFailedToast("Update failed"); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Error: $e")), ); } }, child: Text( "Save", style: fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600), ), ), ), ], ), ); } Widget PumpFee() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "TANKER TYPE", style: fontTextStyle(10, Color(0XFF2D2E30), FontWeight.w600), ), SizedBox(height: MediaQuery.of(context).size.height * .004), Text( "Add your price addition for tankers with pump", style: fontTextStyle(12, Color(0XFF939495), FontWeight.w400), ), SizedBox(height: MediaQuery.of(context).size.height * .024), labelText('Tanker with pump*'), SizedBox(height: MediaQuery.of(context).size.height * .004), buildTextField("+ ₹50 ", pumpFeeController), SizedBox(height: MediaQuery.of(context).size.height * .024), SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Color(0XFF8270DB), foregroundColor: Color(0XFFFFFFFF), padding: EdgeInsets.symmetric(vertical: 10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), // <-- set your radius here ), ), onPressed: () async{ var payload = new Map(); payload["pumping_fee"] = pumpFeeController.text.toString(); bool tankStatus = await AppSettings.updatePumpFee(payload); try { if (tankStatus) { AppSettings.longSuccessToast("Pump fee Updated Successfully"); _fetchPumpFee(); } else { AppSettings.longFailedToast("Pump fee updation failed"); } } catch (exception) { print(exception); } }, child: Text( "Save", style: fontTextStyle(14, const Color(0XFFFFFFFF), FontWeight.w600), ), ), ) ]), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0XFFFFFFFF), appBar: AppSettings.supplierAppBarWithActionsText('Set Rates', context), body: Column( children: [ Container( width: double.infinity, decoration: const BoxDecoration( color: Color(0XFFF3F1FB), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24), ), ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), child: Column( children: [ SizedBox(height: MediaQuery.of(context).size.height * .096), const CircleAvatar( radius: 50, backgroundColor: Color(0XFFC9C2F0)), SizedBox(height: MediaQuery.of(context).size.height * .016), Text( "What’s today’s water price?", style: fontTextStyle(20, Color(0XFF343637), FontWeight.w600), ), SizedBox(height: MediaQuery.of(context).size.height * .008), Text( "Set your daily rate so customers know what to expect", style: fontTextStyle(12, Color(0XFF343637), FontWeight.w400), textAlign: TextAlign.center, ), ], ), ), const SizedBox(height: 20), Container( height: 30, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: AnimatedBuilder( animation: _tabController, builder: (context, _) { return TabBar( controller: _tabController, indicatorColor: Colors.transparent, // remove underline dividerColor: Colors.transparent, isScrollable: false, overlayColor: MaterialStateProperty.all( Colors.transparent), // equal width tabs: List.generate(3, (index) { final labels = ['Water Type', 'Delivery Fee', 'Pump']; final isSelected = _tabController.index == index; return Container( decoration: BoxDecoration( color: isSelected ? const Color(0XFFF1F1F1) : Colors.transparent, borderRadius: BorderRadius.circular(27), ), alignment: Alignment.center, child: Text( labels[index], style: isSelected ? fontTextStyle( 12, const Color(0XFF101214), FontWeight.w600, ) : fontTextStyle( 12, const Color(0XFF646464), FontWeight.w600, ), ), ); }), ); }, ), ), Expanded( child: TabBarView( controller: _tabController, children: [ // Water Type Tab WaterCharges(), // Delivery Fee Tab DeliveryFee(), // Pump Tab PumpFee() ], ), ), ], ), ); } }