diff --git a/lib/common/dashboard.dart b/lib/common/dashboard.dart index b0a707b..f3d8214 100644 --- a/lib/common/dashboard.dart +++ b/lib/common/dashboard.dart @@ -771,7 +771,14 @@ class _HomeScreenState extends State { style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), TextButton( - onPressed: () {}, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => FleetStep1Page(), + ), + ); + }, child: const Text( "Edit Profile", style: TextStyle(color: Colors.blue, fontSize: 14), diff --git a/lib/common/settings.dart b/lib/common/settings.dart index 9725b67..099adda 100644 --- a/lib/common/settings.dart +++ b/lib/common/settings.dart @@ -140,6 +140,11 @@ class AppSettings{ static String uploadPicUrl = host + 'uploads-user'; static String getOrderRequestsFromUsersUrl = host + 'getuserRequestbookingsforsupplier'; static String acceptOrderRequestsUrl = host + 'request-booking-with-charges'; + static String getAcceptedOrdersFromUsersUrl = host + 'supplier/booking/advance-paid'; + static String getTankersUrl = host + 'getTankers'; + static String addTankerUrl = host + 'addTankers'; + static String getDriversUrl = host + 'getalldeliveryboys'; + static String addDriversUrl = host + 'addDeliveryboys'; static String formDouble(dynamic s) { @@ -413,6 +418,140 @@ class AppSettings{ } } + static Future getAcceptedOrdersFromUsers() async { + var uri = Uri.parse(getAcceptedOrdersFromUsersUrl+'/'+supplierId); + //uri = uri.replace(query: 'customerId=$customerId'); + + var response = await http.get(uri, headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else if (response.statusCode == 401) { + bool status = await AppSettings.resetToken(); + if (status) { + response = await http.get(uri, headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else { + return ''; + } + } else { + return ''; + } + } else { + return ''; + } + } + + static Future getTankers() async { + var uri = Uri.parse(getTankersUrl); + uri = uri.replace(query: 'supplierId=$supplierId'); + + var response = await http.get(uri, headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else if (response.statusCode == 401) { + bool status = await AppSettings.resetToken(); + if (status) { + response = await http.get(uri, headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else { + return ''; + } + } else { + return ''; + } + } else { + return ''; + } + } + + static Future addTankers(payload) async { + var response = await http.post(Uri.parse(addTankerUrl + '/' + supplierId), + body: json.encode(payload), headers: await buildRequestHeaders()); + + if (response.statusCode == 200) { + try { + var _response = json.decode(response.body); + print(_response); + return true; + } catch (e) { + // display error toast + return false; + } + } else if (response.statusCode == 401) { + bool status = await AppSettings.resetToken(); + if (status) { + response = await http.post(Uri.parse(addTankerUrl + '/' + supplierId), + body: json.encode(payload), headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return true; + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } + } + + static Future getDrivers() async { + var uri = Uri.parse(getDriversUrl+'/'+supplierId); + //uri = uri.replace(query: 'supplierId=$supplierId'); + + var response = await http.get(uri, headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else if (response.statusCode == 401) { + bool status = await AppSettings.resetToken(); + if (status) { + response = await http.get(uri, headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return response.body; + } else { + return ''; + } + } else { + return ''; + } + } else { + return ''; + } + } + + static Future addDrivers(payload) async { + var response = await http.post(Uri.parse(addDriversUrl + '/' + supplierId), + body: json.encode(payload), headers: await buildRequestHeaders()); + + if (response.statusCode == 200) { + try { + var _response = json.decode(response.body); + print(_response); + return true; + } catch (e) { + // display error toast + return false; + } + } else if (response.statusCode == 401) { + bool status = await AppSettings.resetToken(); + if (status) { + response = await http.post(Uri.parse(addTankerUrl + '/' + supplierId), + body: json.encode(payload), headers: await buildRequestHeaders()); + if (response.statusCode == 200) { + return true; + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } + } + /*Apis ends here*/ diff --git a/lib/orders/all_orders.dart b/lib/orders/all_orders.dart index 216152c..8b180e9 100644 --- a/lib/orders/all_orders.dart +++ b/lib/orders/all_orders.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:supplier_new/common/settings.dart'; import 'package:supplier_new/orders/order_requests.dart'; @@ -17,70 +19,60 @@ class AllOrders extends StatefulWidget { class _AllOrdersState extends State { final TextEditingController searchController = TextEditingController(); + bool isLoading=false; + final List orders =[]; + List ordersList = []; + @override + void initState() { + // TODO: implement initState + super.initState(); + _fetchOrders(); + } + Future _fetchOrders() async { + setState(() => isLoading = true); + + try { + final response = await AppSettings.getAcceptedOrdersFromUsers(); + final data = (jsonDecode(response)['data'] as List) + .map((e) => OrdersModel.fromJson(e)) + .toList(); + if (!mounted) return; + setState(() { + ordersList = data; + isLoading = false; + }); + } catch (e) { + debugPrint("⚠️ Error fetching orders: $e"); + setState(() => isLoading = false); + } + } @override Widget build(BuildContext context) { - final List orders = [ - OrdersModel( - date: DateTime(2025, 8, 24), - imageAsset: "images/building.png",// sample image - status: "completed", - title: "Rajpushpa Atria", - location: "Kokapet", - quantity: "15,000L - Bore Water", - time: "01:00 AM, Today", - extraInfo: "Delivered by Suresh", - ), - OrdersModel( - date: DateTime(2025, 8, 24), - imageAsset: "images/building.png",// sample image - status: "in-progress", - title: "Rajpushpa Atria", - location: "Kokapet", - quantity: "15,000L - Bore Water", - time: "01:00 AM, 21/07/2025", - extraInfo: "Track Delivery", - ), - OrdersModel( - date: DateTime(2025, 8, 23), - imageAsset: "images/building.png",// sample image - status: "cancelled", - title: "Lakeview Towers", - location: "Madhapur", - quantity: "8,000L - Tanker Water", - time: "09:30 PM, Yesterday", - extraInfo: "Cancelled by user", - ), - OrdersModel( - date: DateTime(2025, 8, 25), - imageAsset: "images/building.png",// sample image - status: "assigned", - title: "Lakeview Towers", - location: "Madhapur", - quantity: "8,000L - Tanker Water", - time: "09:30 PM, Yesterday", - extraInfo: "Assigned to Madhav", - ), - OrdersModel( - date: DateTime(2025, 8, 26), - imageAsset: "images/building.png",// sample image - status: "pending", - title: "Lakeview Towers", - location: "Madhapur", - quantity: "8,000L - Tanker Water", - time: "09:30 PM, Yesterday", - extraInfo: "", - ), - ]; // ✅ Group orders by date final Map> groupedOrders = {}; - for (var order in orders) { - final formattedDate = DateFormat("dd MMM yyyy").format(order.date); + String formatOrderDate(String? dateStr) { + if (dateStr == null || dateStr.trim().isEmpty) { + return ""; // or return a fallback like "N/A" + } + + try { + final inputFormat = DateFormat("dd-MMM-yyyy"); // matches 10-Sep-2025 + final outputFormat = DateFormat("dd MMM yyyy"); // output 10 Sep 2025 + final parsedDate = inputFormat.parse(dateStr); + return outputFormat.format(parsedDate); + } catch (e) { + print("Date parse error: $e"); + return dateStr; // fallback to original string + } + } + for (var order in ordersList) { + final formattedDate = formatOrderDate(order.date); // "10 Sep 2025" groupedOrders.putIfAbsent(formattedDate, () => []).add(order); } return Scaffold( @@ -299,6 +291,8 @@ class OrderCard extends StatelessWidget { return Color(0XFFF9DBC6); case "pending": return Color(0XFFFDF3D3); + case "advance_paid": + return Color(0XFFFDF3D3); default: return Colors.grey; } @@ -316,6 +310,8 @@ class OrderCard extends StatelessWidget { return Color(0XFFE56910); case "pending": return Color(0XFFD0AE3C); + case "advance_paid": + return Color(0XFFD0AE3C); default: return Colors.grey; } @@ -349,15 +345,15 @@ class OrderCard extends StatelessWidget { topLeft: Radius.circular(12), bottomLeft: Radius.circular(0), ), - child: order.imageAsset != null + child: order.imageAsset != '' ? Image.asset( - order.imageAsset!, + order.imageAsset, height: 145, width: 145, fit: BoxFit.cover, ) : Image.network( - order.imageUrl!, + order.imageAsset, height: 100, width: 100, fit: BoxFit.cover, @@ -389,33 +385,55 @@ class OrderCard extends StatelessWidget { const SizedBox(height: 6), Text( - order.title, + order.building_name, style:fontTextStyle(16,Color(0XFF2D2E30),FontWeight.w600) ), Text( - order.location, + order.displayAddress, style: fontTextStyle(12,Color(0XFF646566),FontWeight.w400) ), const SizedBox(height: 4), Text( - order.quantity, + order.capacity+' - '+order.type_of_water, style: fontTextStyle(14,Color(0XFF444444),FontWeight.w500) ), Text( - order.time, - style:fontTextStyle(12,Color(0XFF939495),FontWeight.w400) + order.time+' , '+order.date, + style:fontTextStyle(8,Color(0XFF646566),FontWeight.w400) ), - const SizedBox(height: 4), + const SizedBox(height: 12), + Visibility( + visible: order.status.toLowerCase().toString()=='advance_paid', + child: GestureDetector( + onTap: (){ + + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(22), + border: Border.all(color: const Color(0XFF939495)), + ), + child: Padding( + padding: EdgeInsets.fromLTRB(8,4,8,4), + child: Text( + "Assign", + style: fontTextStyle( + 14, const Color(0XFF515253), FontWeight.w400), + ), + ) + ), + ),), - Text( + + /*Text( order.extraInfo, style:fontTextStyle(12, order.status == "in-progress" ? Color(0XFF1D7AFC) : Color(0XFF646566),FontWeight.w400) - ), + ),*/ ], ), ), diff --git a/lib/orders/order_requests.dart b/lib/orders/order_requests.dart index 5d0227a..a7766e5 100644 --- a/lib/orders/order_requests.dart +++ b/lib/orders/order_requests.dart @@ -52,7 +52,7 @@ class _OrderRequestsPageState extends State { // ✅ Statuses that ignore time if (dbLower == "reject") return {"status": "Rejected", "color": const Color(0XFFE2483D)}; if (dbLower == "accept") return {"status": "Accepted", "color": const Color(0XFF0A9E04)}; - if (dbLower == "delivered") return {"status": "Delivered", "color": const Color(0XFF2E7D32)}; + if (dbLower == "advance_paid") return {"status": "Accepted", "color": const Color(0XFF0A9E04)}; if (dbLower == "cancelled" || dbLower == "cancelled_by_user" || dbLower == "cancelled_by_supplier") { return {"status": "Cancelled", "color": const Color(0XFF757575)}; } @@ -128,7 +128,7 @@ class _OrderRequestsPageState extends State { price: "₹${AppSettings.formDouble(order.quoted_amount) ?? ''}", ); - final noNavigateStatuses = ["expired", "rejected", "accepted", "delivered", "cancelled"]; + final noNavigateStatuses = ["expired", "rejected", "accepted", "advance_paid", "cancelled"]; final disableNavigation = noNavigateStatuses.contains(status.toLowerCase()); final card = OrderCard(order: cardModel); diff --git a/lib/orders/orders_model.dart b/lib/orders/orders_model.dart index d703708..66ccd18 100644 --- a/lib/orders/orders_model.dart +++ b/lib/orders/orders_model.dart @@ -1,23 +1,70 @@ -class OrdersModel { - final DateTime date; - final String? imageUrl; // Network image - final String? imageAsset; - final String status; - final String title; - final String location; - final String quantity; - final String time; - final String extraInfo; - - OrdersModel({ - required this.date, - this.imageUrl, - this.imageAsset, - required this.status, - required this.title, - required this.location, - required this.quantity, - required this.time, - required this.extraInfo, - }); + + +import 'package:supplier_new/common/settings.dart'; +import 'package:geolocator/geolocator.dart'; + +class OrdersModel { + String building_name = ''; + String address = ''; + String type_of_water = ''; + String capacity = ''; + String quantity = ''; + String time = ''; + String averageTime = ''; + String quoted_amount = ''; + String displayAddress=''; + double lat=0; + double lng=0; + double distanceInMeters=0; + double distanceInKm=0.0; + String dbId = ''; + String status=''; + String date=''; + String imageAsset='images/building.png'; + + OrdersModel(); + + factory OrdersModel.fromJson(Map json){ + OrdersModel rtvm = new OrdersModel(); + + rtvm.building_name = json['buildingName'] ?? ''; + rtvm.dbId = json['_id']?? ''; + rtvm.address = json['address'] ?? ''; + rtvm.type_of_water = json['typeofwater '] ?? ''; + rtvm.capacity = json['capacity'] ?? ''; + rtvm.quantity = json['quantity']?? ''; + rtvm.time = json['time'] ?? ''; + rtvm.date = json['dateOfOrder'] ?? ''; + rtvm.status = json['orderStatus'] ?? ''; + rtvm.quoted_amount = json['price'].toString() ?? ''; + rtvm.lng=json['longitude'] ?? 0.0; + rtvm.lat=json['latitude'] ?? 0.0; + + // Split and trim + List parts = rtvm.address.split(',').map((e) => e.trim()).toList(); + +// Usually, the locality is the part before the main city (Hyderabad)displayAddress = ""; + if (parts.length >= 2) { + rtvm.displayAddress = parts[parts.length -4]; // "Banjara Hills" + } + // Distance in meters + rtvm.distanceInMeters = double.parse( + Geolocator.distanceBetween( + rtvm.lat, + rtvm.lng, + AppSettings.supplierLatitude, + AppSettings.supplierLongitude, + ).toStringAsFixed(2), + ); + +// Distance in km + rtvm.distanceInKm = double.parse( + (rtvm.distanceInMeters / 1000).toStringAsFixed(2), + ); + + return rtvm; + } + Map toJson() => { + "boreName":this.building_name, + }; } \ No newline at end of file diff --git a/lib/resources/drivers_model.dart b/lib/resources/drivers_model.dart new file mode 100644 index 0000000..f01b2d2 --- /dev/null +++ b/lib/resources/drivers_model.dart @@ -0,0 +1,22 @@ +class DriversModel { + String supplier_name=''; + String driver_name=''; + String status=''; + String address=''; + String deliveries='13'; + String commision=''; + List availability= ['filled', 'available']; + DriversModel(); + + factory DriversModel.fromJson(Map json){ + DriversModel rtvm = new DriversModel(); + + + rtvm.supplier_name = json['supplier_name'] ?? ''; + rtvm.driver_name = json['name'] ?? ''; + rtvm.status = json['status'] ?? ''; + rtvm.address = json['address'] ?? ''; + + return rtvm; + } +} \ No newline at end of file diff --git a/lib/resources/employees.dart b/lib/resources/employees.dart index ebc939a..e54eabd 100644 --- a/lib/resources/employees.dart +++ b/lib/resources/employees.dart @@ -5,10 +5,6 @@ import 'package:supplier_new/resources/source_location.dart'; import 'Fleet_1.dart'; -// If you want to navigate on Continue, import your next page here. -// import 'fleet_1.dart'; - -void main() => runApp(const MaterialApp(home: FleetEmployees())); class FleetEmployees extends StatefulWidget { const FleetEmployees({super.key}); @@ -81,22 +77,48 @@ class _FleetEmployeesState extends State { setState(() {}); } - void _addDriver() { + void _addDriver() async{ final ok = _formKey.currentState?.validate() ?? false; setState(() {}); // ensure error texts render if (!ok || selectedLicense == null || selectedExperience == null || - selectedLicense!.isEmpty || selectedExperience!.isEmpty) { - if (selectedLicense == null || selectedExperience == null) { + if (selectedExperience == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Please select License & Experience")), ); } return; } - _drivers.add(_buildPayload()); + var payload = new Map(); + payload["tankerName"] = _nameCtrl.text.toString(); + payload["Name"] = _nameCtrl.text.toString(); + payload["license_number"] =selectedLicense.toString(); + payload["address"] = AppSettings.userAddress; + payload["supplier_name"] = AppSettings.userName; + payload["phone"] = _mobileCtrl.text.toString(); + payload["alternativeContactNumber"] =''; + payload["years_of_experience"] =selectedExperience.toString(); + payload["status"] = 'string'; + + + + bool tankStatus = await AppSettings.addDrivers(payload); + + try { + if (tankStatus) { + AppSettings.longSuccessToast("Tanker Created Successfully"); + _nameCtrl.text = ''; + Navigator.pop(context,true); + + } + else { + AppSettings.longFailedToast("Tanker Creation failed"); + } + } catch (exception) { + print(exception); + } _clearForm(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Driver added (${_drivers.length})")), diff --git a/lib/resources/fleet.dart b/lib/resources/fleet.dart index 11abb75..e8c6399 100644 --- a/lib/resources/fleet.dart +++ b/lib/resources/fleet.dart @@ -2,11 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:supplier_new/common/settings.dart'; -import 'employees.dart'; -import 'fleet_1.dart'; - -void main() => runApp(const MaterialApp(home: FleetStep1Page())); - class FleetStep1Page extends StatefulWidget { const FleetStep1Page({super.key}); @@ -33,6 +28,12 @@ class _FleetStep1PageState extends State { ]; String? selectedType; + final List typeOfWater = [ + "Bore Water", + "Drinking Water", + ]; + String? selectedTypeOfWater; + final List featureOptions = [ "GPS", "Stainless Steel", @@ -99,18 +100,38 @@ class _FleetStep1PageState extends State { setState(() {}); } - void _addTanker() { + void _addTanker() async{ final ok = _formKey.currentState?.validate() ?? false; setState(() {}); // in case you show chip validation below labels - if (!ok || selectedFeatures.isEmpty) { - if (selectedFeatures.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Select at least one Tanker Feature")), - ); + var payload = new Map(); + payload["tankerName"] = _nameCtrl.text.toString(); + payload["capacity"] = _capacityCtrl.text.toString(); + payload["typeofwater"] =selectedTypeOfWater.toString(); + payload["supplier_address"] = AppSettings.userAddress; + payload["supplier_name"] = AppSettings.userName; + payload["phoneNumber"] = AppSettings.phoneNumber; + payload["tanker_type"] =selectedType; + payload["license_plate"] =_plateCtrl.text.trim(); + payload["manufacturing_year"] = _mfgYearCtrl.text.trim(); + payload["insurance_exp_date"] = _insExpiryCtrl.text.trim(); + + + bool tankStatus = await AppSettings.addTankers(payload); + + try { + if (tankStatus) { + AppSettings.longSuccessToast("Tanker Created Successfully"); + _nameCtrl.text = ''; + _capacityCtrl.text = ''; + Navigator.pop(context,true); + + } + else { + AppSettings.longFailedToast("Tanker Creation failed"); } - return; + } catch (exception) { + print(exception); } - _tankers.add(_buildPayload()); _clearForm(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Tanker added (${_tankers.length})")), @@ -267,21 +288,21 @@ class _FleetStep1PageState extends State { ), _LabeledField( - label: "Tanker Features *", + label: "Type of water *", child: DropdownButtonFormField( - value: selectedType, - items: tankerTypes + value: selectedTypeOfWater, + items: typeOfWater .map((t) => DropdownMenuItem(value: t, child: Text(t))) .toList(), - onChanged: (v) => setState(() => selectedType = v), + onChanged: (v) => setState(() => selectedTypeOfWater = v), validator: (v) => - v == null || v.isEmpty ? "Tanker Type is required" : null, + v == null || v.isEmpty ? "Type of water is required" : null, isExpanded: true, alignment: Alignment.centerLeft, // <-- Hint: left-aligned, vertically centered by padding hint: Text( - "Select Features", + "Select type of water", style: fontTextStyle( 14, const Color(0xFF939495), FontWeight.w400), ), @@ -374,29 +395,72 @@ class _FleetStep1PageState extends State { const SizedBox(height: 12), // Continue (primary) - SizedBox( - width: double.infinity, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF8270DB), // purple bg - foregroundColor: Colors.white, // ripple/icon/text color - padding: const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24)), + /* SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), // purple bg + foregroundColor: Colors.white, // ripple/icon/text color + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24)), + ), + onPressed: () async{ + + if (_nameCtrl.text != '' && + _capacityCtrl.text != '' && + _plateCtrl.text != '') { + + var payload = new Map(); + payload["tankerName"] = _nameCtrl.text.toString(); + payload["capacity"] = _capacityCtrl.text.toString(); + payload["typeofwater"] =selectedTypeOfWater.toString(); + payload["supplier_address"] = AppSettings.userAddress; + payload["supplier_name"] = AppSettings.userName; + + bool tankStatus = await AppSettings.addTankers(payload); + + try { + if (tankStatus) { + AppSettings.longSuccessToast( + "Tanker Created Successfully"); + _nameCtrl.text = ''; + //tankerPhoneNumberController.text = ''; + _capacityCtrl.text = ''; + //tankerAlternativePhoneNumberController.text=''; + Navigator.pop(context); + *//* await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => TankersView()), + );*//* + + } + else { + AppSettings.longFailedToast("Tanker Creation failed"); + } + } catch (exception) { + print(exception); + } + } else { + AppSettings.longFailedToast("Please enter valid details"); + } + + _nameCtrl.clear(); + _capacityCtrl.text = "10,000"; + _plateCtrl.text = "AB 05 H 4948"; + _mfgYearCtrl.clear(); + _insExpiryCtrl.clear(); + selectedType = null; + selectedFeatures.clear(); + }, + child: Text( + "Continue", + style: fontTextStyle( + 14, Colors.white, FontWeight.w400), // white text + ), ), - onPressed: () { - // Navigator.push( - // context, - // MaterialPageRoute(builder: (_) => const FleetStep2Page()), - // ); - }, - child: Text( - "Continue", - style: fontTextStyle( - 14, Colors.white, FontWeight.w400), // white text - ), - ), - ) + )*/ ], ) ], diff --git a/lib/resources/resources_drivers.dart b/lib/resources/resources_drivers.dart index 033ad47..0c4da92 100644 --- a/lib/resources/resources_drivers.dart +++ b/lib/resources/resources_drivers.dart @@ -1,5 +1,8 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/drivers_model.dart'; import 'package:supplier_new/resources/resources_sources.dart'; import 'fleet.dart'; import 'resources_drivers.dart'; @@ -20,6 +23,35 @@ class ResourcesDriverScreen extends StatefulWidget { class _ResourcesDriverScreenState extends State { int selectedTab = 1; // default "Drivers" String search = ''; + bool isLoading = false; + List driversList = []; + + @override + void initState() { + // TODO: implement initState + super.initState(); + _fetchDrivers(); + } + + 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 orders: $e"); + setState(() => isLoading = false); + } + } + final List> drivers = [ { @@ -80,7 +112,7 @@ class _ResourcesDriverScreenState extends State { Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text('09', + Text('${driversList.length.toString()}', style: fontTextStyle( 24, const Color(0xFF0D3771), FontWeight.w500)), const SizedBox(height: 6), @@ -167,16 +199,16 @@ class _ResourcesDriverScreenState extends State { // Driver list Expanded( child: ListView.separated( - itemCount: drivers.length, + itemCount: driversList.length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, idx) { - final d = drivers[idx]; + final d = driversList[idx]; return DriverCard( - name: d['name'], - status: d['status'], - location: d['location'], - deliveries: d['deliveries'], - commission: d['commission'], + name: d.driver_name, + status: d.status, + location: d.address, + deliveries: int.parse(d.deliveries), + commission: d.commision, ); }, ), @@ -191,10 +223,18 @@ class _ResourcesDriverScreenState extends State { // Floating Action Button floatingActionButton: FloatingActionButton( - onPressed: () { - // TODO: Navigate to the next step/screen - // Navigator.push(context, MaterialPageRoute(builder: (_) => FleetEmployees())); + onPressed: () async{ + final result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => FleetEmployees(), + ), + ); + // If result indicates API reload + if (result == true) { + _fetchDrivers(); + } }, backgroundColor: Colors.black, shape: const CircleBorder(), diff --git a/lib/resources/resources_fleet.dart b/lib/resources/resources_fleet.dart index 05d08b4..9e17a07 100644 --- a/lib/resources/resources_fleet.dart +++ b/lib/resources/resources_fleet.dart @@ -1,5 +1,9 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:supplier_new/common/settings.dart'; +import 'package:supplier_new/resources/tankers_model.dart'; import 'fleet.dart'; import 'resources_drivers.dart'; import 'resources_sources.dart'; @@ -19,8 +23,50 @@ class ResourcesFleetScreen extends StatefulWidget { } class _ResourcesFleetScreenState extends State { + final _formKey = GlobalKey(); + + // Controllers + final _nameCtrl = TextEditingController(); + final _capacityCtrl = TextEditingController(); // hint-like + final _plateCtrl = TextEditingController(); + final _mfgYearCtrl = TextEditingController(); + final _insExpiryCtrl = TextEditingController(); + + String? _required(String? v, {String field = "This field"}) { + if (v == null || v.trim().isEmpty) return "$field is required"; + return null; + } + int selectedTab = 0; String search = ''; + bool isLoading = false; + List tankersList = []; + + @override + void initState() { + // TODO: implement initState + super.initState(); + _fetchTankers(); + } + + 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(() { + tankersList = data; + isLoading = false; + }); + } catch (e) { + debugPrint("⚠️ Error fetching orders: $e"); + setState(() => isLoading = false); + } + } final List> items = [ { @@ -60,14 +106,179 @@ class _ResourcesFleetScreenState extends State { }, ]; + void openTankerSimpleSheet(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.white, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) { + return Padding( + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 16, + bottom: MediaQuery.of(context).viewInsets.bottom + 20, + ), + child: Form( + key: _formKey, + child: ListView( + shrinkWrap: true, + children: [ + // Tanker Name + Text("Tanker Name *", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600)), + const SizedBox(height: 6), + TextFormField( + controller: _nameCtrl, + validator: (v) => _required(v, field: "Tanker Name"), + decoration: const InputDecoration( + hintText: "Enter Tanker Name", + border: OutlineInputBorder(), + isDense: true, + ), + textInputAction: TextInputAction.next, + ), + const SizedBox(height: 14), + + // Capacity + Text("Tanker Capacity (in L) *", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600)), + const SizedBox(height: 6), + TextFormField( + controller: _capacityCtrl, + validator: (v) => _required(v, field: "Tanker Capacity"), + decoration: InputDecoration( + hintText: "10,000 L", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9,]')), + ], + textInputAction: TextInputAction.next, + ), + const SizedBox(height: 14), + + // License Plate + Text("License Plate *", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600)), + const SizedBox(height: 6), + TextFormField( + controller: _plateCtrl, + validator: (v) => _required(v, field: "License Plate"), + decoration: InputDecoration( + hintText: "AB 05 H 4948", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + textCapitalization: TextCapitalization.characters, + textInputAction: TextInputAction.next, + ), + const SizedBox(height: 14), + + // Manufacturing Year (optional) + Text("Manufacturing Year (opt)", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600)), + const SizedBox(height: 6), + TextFormField( + controller: _mfgYearCtrl, + decoration: InputDecoration( + hintText: "YYYY", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(4), + ], + textInputAction: TextInputAction.next, + ), + const SizedBox(height: 14), + + // Insurance Expiry Date (optional) + Text("Insurance Expiry Date (opt)", + style: fontTextStyle(14, const Color(0xFF646566), FontWeight.w600)), + const SizedBox(height: 6), + TextFormField( + controller: _insExpiryCtrl, + readOnly: true, + decoration: InputDecoration( + hintText: "DD-MM-YYYY", + hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400), + border: const OutlineInputBorder(), + isDense: true, + suffixIcon: const Icon(Icons.calendar_today_outlined, size: 18), + ), + onTap: () async { + final now = DateTime.now(); + final picked = await showDatePicker( + context: context, + initialDate: now, + firstDate: DateTime(now.year - 30), + lastDate: DateTime(now.year + 30), + ); + if (picked != null) { + final dd = picked.day.toString().padLeft(2, '0'); + final mm = picked.month.toString().padLeft(2, '0'); + final yyyy = picked.year.toString(); + _insExpiryCtrl.text = "$dd-$mm-$yyyy"; + } + }, + ), + const SizedBox(height: 20), + + // Save button + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF8270DB), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24), + ), + ), + onPressed: () async { + if (_formKey.currentState?.validate() != true) return; + + // TODO: Replace with your save logic / API call + // Example: + // final ok = await saveTanker(); + // if (ok) Navigator.pop(context, true); + + Navigator.pop(context, true); // close sheet on success + }, + child: Text( + "Save", + style: fontTextStyle(14, Colors.white, FontWeight.w600), + ), + ), + ), + ], + ), + ), + ); + }, + ); + } + + + @override Widget build(BuildContext context) { - final filtered = items.where((it) { + final filtered = tankersList.where((it) { final q = search.trim().toLowerCase(); if (q.isEmpty) return true; - return it['title'].toLowerCase().contains(q) || - it['subtitle'].toLowerCase().contains(q) || - it['owner'].toLowerCase().contains(q); + return it.tanker_name.toLowerCase().contains(q); }).toList(); return Scaffold( @@ -120,7 +331,7 @@ class _ResourcesFleetScreenState extends State { Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, - children: [Text('14', style: fontTextStyle(24, const Color(0xFF0D3771), FontWeight.w500, + children: [Text(tankersList.length.toString(), style: fontTextStyle(24, const Color(0xFF0D3771), FontWeight.w500, ), ), @@ -241,11 +452,12 @@ class _ResourcesFleetScreenState extends State { itemBuilder: (context, idx) { final it = filtered[idx]; return TankCard( - title: it['title'], - subtitle: it['subtitle'], - code: it['code'], - owner: it['owner'], - status: List.from(it['status']), + title: it.tanker_name, + subtitle: it.type_of_water, + capacity: it.capacity, + code: it.license_plate, + owner: it.supplier_name, + status: List.from(it.availability), ); }, ), @@ -258,9 +470,19 @@ class _ResourcesFleetScreenState extends State { ], ), floatingActionButton: FloatingActionButton( - onPressed: () { - // TODO: Navigate to the next step/screen - // Navigator.push(context, MaterialPageRoute(builder: (_) => const FleetStep1Page())); + onPressed: () async{ + // openTankerSimpleSheet(context); + final result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => FleetStep1Page(), + ), + ); + + // If result indicates API reload + if (result == true) { + _fetchTankers(); + } }, backgroundColor: const Color(0xFF000000), @@ -310,6 +532,7 @@ class SmallMetricBox extends StatelessWidget { class TankCard extends StatelessWidget { final String title; final String subtitle; + final String capacity; final String code; final String owner; final List status; @@ -318,6 +541,7 @@ class TankCard extends StatelessWidget { super.key, required this.title, required this.subtitle, + required this.capacity, required this.code, required this.owner, required this.status, @@ -392,13 +616,13 @@ class TankCard extends StatelessWidget { }).toList(), ), const SizedBox(height: 8), - Text(title, + Text (title, style: fontTextStyle( 14, const Color(0xFF343637), FontWeight.w600)), const SizedBox(height: 6), - Text(subtitle, + Text(subtitle+' - '+capacity+' L', style: fontTextStyle( - 10, const Color(0xFF515253), FontWeight.w600)), + 10, const Color(0xFF343637), FontWeight.w600)), const SizedBox(height: 10), Row( children: [ diff --git a/lib/resources/resources_main.dart b/lib/resources/resources_main.dart index 765a64d..4cb941d 100644 --- a/lib/resources/resources_main.dart +++ b/lib/resources/resources_main.dart @@ -7,11 +7,6 @@ import '../common/settings.dart'; import 'employees.dart'; import 'fleet.dart'; -void main() => runApp(const MaterialApp( - debugShowCheckedModeBanner: false, - home: ResourcesMainScreen(), - )); - class ResourcesMainScreen extends StatefulWidget { const ResourcesMainScreen({super.key}); @@ -165,11 +160,11 @@ class _ResourcesMainScreenState extends State ], ), ), - floatingActionButton: AnimatedBuilder( + /*floatingActionButton: AnimatedBuilder( animation: _tabController.animation!, builder: (context, _) => _buildFAB(_tabController.index) ?? const SizedBox.shrink(), - ), + ),*/ ); } } diff --git a/lib/resources/resources_sources.dart b/lib/resources/resources_sources.dart index 66c26d1..371a467 100644 --- a/lib/resources/resources_sources.dart +++ b/lib/resources/resources_sources.dart @@ -234,9 +234,15 @@ class _ResourcesSourceScreenState extends State { floatingActionButton: FloatingActionButton( onPressed: () { - // TODO: Navigate to the next step/screen - // Navigator.push(context, MaterialPageRoute(builder: (_) => const SourceLocation())); - + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const HeroMode( + enabled: false, + child: SourceLocation(), + ), + ), + ); }, backgroundColor: const Color(0xFF000000), shape: const CircleBorder(), diff --git a/lib/resources/tankers_model.dart b/lib/resources/tankers_model.dart new file mode 100644 index 0000000..f92b2b8 --- /dev/null +++ b/lib/resources/tankers_model.dart @@ -0,0 +1,29 @@ +class TankersModel { + String tanker_name = ''; + String address = ''; + String type_of_water = ''; + String capacity = ''; + String dbId = ''; + String status=''; + String license_plate=''; + String supplier_name=''; + List availability= ['filled', 'available']; + 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'] ?? ''; + + return rtvm; + } + Map toJson() => { + "boreName":this.tanker_name, + }; +} \ No newline at end of file