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/order_requests_model.dart'; import 'package:supplier_new/plans/accept_plan_requests.dart'; import 'package:supplier_new/plans/plan_requests_model.dart'; class PlanRequestsPage extends StatefulWidget { const PlanRequestsPage({super.key}); @override State createState() => _PlanRequestsPageState(); } class _PlanRequestsPageState extends State { final TextEditingController searchController = TextEditingController(); List planRequestsList = []; List filteredList = []; bool isLoading = false; @override void initState() { super.initState(); _fetchPlanRequests(); // 🔹 Add listener for search searchController.addListener(() { _filterOrders(searchController.text); }); } void _filterOrders(String query) { final lowerQuery = query.toLowerCase(); setState(() { if (lowerQuery.isEmpty) { filteredList = planRequestsList; } else { filteredList = planRequestsList.where((order) { final title = order.building_name.toLowerCase() ?? ''; final address = order.displayAddress.toLowerCase() ?? ''; final waterType = order.type_of_water.toLowerCase() ?? ''; final capacity = order.capacity.toLowerCase() ?? ''; return title.contains(lowerQuery) || address.contains(lowerQuery) || waterType.contains(lowerQuery) || capacity.contains(lowerQuery); }).toList(); } }); } Future _fetchPlanRequests() async { setState(() => isLoading = true); try { final response = await AppSettings.getPlanRequestsFromUsers(); final data = (jsonDecode(response)['data'] as List) .map((e) => PlanRequestsModel.fromJson(e)) .toList(); if (!mounted) return; setState(() { planRequestsList = data; filteredList = data; isLoading = false; }); } catch (e) { debugPrint("⚠️ Error fetching orders: $e"); setState(() => isLoading = false); } } /// 🔹 Helper to get status based on order time Map getOrderStatus(String orderTimeStr, String dbStatus) { String status = "Invalid time"; Color color = Colors.grey; final dbLower = (dbStatus ?? "").toLowerCase().trim(); // ✅ 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 == "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)}; } // ✅ Time-based status (only if not rejected/accepted/etc.) if (orderTimeStr.isEmpty) { // fallback for pending without time return {"status": "Pending", "color": const Color(0XFFE56910)}; } try { final format = DateFormat("dd-MM-yyyy HH:mm"); final orderTime = format.parse(orderTimeStr); final now = DateTime.now(); final difference = now.difference(orderTime); if (difference.inDays < 2) { status = "New"; color = const Color(0XFF1D7AFC); // Blue } else if (difference.inDays < 10) { final remaining = 20 - difference.inDays; status = "Expires in ${remaining} D"; // show time for pending color = difference.inDays < 10 ? const Color(0XFFE56910) : const Color(0XFFE2483D); } else { // Expired overrides pending status = "Expired"; color = const Color(0XFF757575); } } catch (e) { debugPrint("⚠️ Error parsing time: $e"); status = "Invalid time"; color = Colors.grey; } return {"status": status, "color": color}; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppSettings.SupplierAppBarWithHelpAction('Plan Requests', context), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ /// 🔹 Search Bar _buildSearchBar(), const SizedBox(height: 16), /// 🔹 Orders List Expanded( child: isLoading ? const Center(child: CircularProgressIndicator()) : filteredList .isEmpty?Center( child: Text( 'No Data Available', style: fontTextStyle(16,Color(0XFF000000),FontWeight.w700), ), ):ListView.builder( itemCount: filteredList .length, itemBuilder: (context, index) { final order = filteredList [index]; final statusMap = getOrderStatus(order.time ?? "", order.status ?? ""); final status = statusMap['status']; final color = statusMap['color']; final cardModel = OrderCardModel( status: status, statusColor: color, title: order.building_name ?? "", location: order.displayAddress ?? "", description: "${order.capacity ?? ''} - ${order.type_of_water ?? ''}", price: "₹${AppSettings.formDouble(order.quoted_amount) ?? ''}", ); final noNavigateStatuses = ["expired", "rejected", "accepted", "advance_paid", "cancelled"]; final disableNavigation = noNavigateStatuses.contains(status.toLowerCase()); final card = OrderCard(order: cardModel); return disableNavigation ? card // just show the card, no tap : GestureDetector( onTap: () async { final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => AcceptPlanRequests(order: order, status: cardModel), ), ); // If result indicates API reload if (result == true) { _fetchPlanRequests(); } }, child: card, ); }, ), ), ], ), ), ); } Widget _buildSearchBar() { return Row( children: [ Expanded( child: Container( height: 42, decoration: BoxDecoration( borderRadius: BorderRadius.circular(22), border: Border.all(color: const Color(0XFF939495)), ), child: TextField( controller: searchController, decoration: InputDecoration( hintText: "Search", hintStyle: fontTextStyle(16, const Color(0XFF646566), FontWeight.w400), prefixIcon: SizedBox( height: 20, width: 20, child: Padding( padding: const EdgeInsets.all(8.0), child: Image.asset( 'images/search.png', fit: BoxFit.contain, ), ), ), border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 10), ), ), ), ), const SizedBox(width: 10), Container( height: 42, width: 42, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300), color: Color(0XFFF5F6F6), ), child: IconButton( icon: Image.asset( 'images/filter.png', // your image path height: 20, width: 20, fit: BoxFit.contain, ), onPressed: () { // Your onPressed action }, ) ), const SizedBox(width: 5), Container( height: 42, width: 42, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300), color: Color(0XFFF5F6F6), ), child: IconButton( icon: Image.asset( 'images/sort.png', // your image path height: 20, width: 20, fit: BoxFit.contain, ), onPressed: () { // Your onPressed action }, ) ), ], ); } } /// 🔹 UI Model for rendering OrderCard class OrderCardModel { final String status; final Color statusColor; final String title; final String location; final String description; final String price; OrderCardModel({ required this.status, required this.statusColor, required this.title, required this.location, required this.description, required this.price, }); } /// 🔹 Card widget class OrderCard extends StatelessWidget { final OrderCardModel order; const OrderCard({super.key, required this.order}); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Color(0XFFF6F6F6), borderRadius: BorderRadius.circular(12), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ /// 🔹 Left content Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /// Status chip Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Color(0XFFF6F6F6), borderRadius: BorderRadius.circular(4), border: Border.all( color: order.statusColor, width: 0.5, ), ), child: Text( order.status, style: fontTextStyle(12, order.statusColor, FontWeight.w500) ), ), SizedBox(height:MediaQuery.of(context).size.height * .008,), /// Title Text( order.title, style: fontTextStyle(16, Color(0XFF2D2E30), FontWeight.w600) ), /// Location Text( order.location, style: fontTextStyle(10, Color(0XFF939495), FontWeight.w400) ), const SizedBox(height: 4), /// Description Text( order.description, style: fontTextStyle(14, Color(0XFF8270DB), FontWeight.w500) ), ], ), ), /// 🔹 Price Text( order.price, style: fontTextStyle(16, Color(0XFF444444), FontWeight.w600) ), ], ), ); } }