import 'dart:convert'; import 'dart:core'; import 'package:frontend/Screens/allTrips/remarks_list.dart'; import 'package:frontend/data/models/plan.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:frontend/config/apiUrl.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; import '../../utils/pagination.dart'; import '../../utils/travelAgent_remarks.dart'; import '../../widgets/custom_popup.dart'; import '../../widgets/popup_listPlan_action.dart'; class ListAllPlans extends StatefulWidget { const ListAllPlans({super.key}); @override _ListAllPlansState createState() => _ListAllPlansState(); } class _ListAllPlansState extends State { final ApiService apiService = ApiService(); int currentPage = 0; int itemsPerPage = 10; String? userId; String? orgId; String? roleUser; String? token; String? TripPlanAction; Color? layoutColor; Color? bodyColor; late Future> futurePlans; List allPlans = []; List filteredPlans = []; TextEditingController searchController = TextEditingController(); @override void initState() { super.initState(); getToken(); WidgetsBinding.instance.addPostFrameCallback((_) { initializeData(); loadInitialData(); // futurePlans.then((plans) { // setState(() { // allPlans = plans; // filteredPlans = plans; // }); // }); }); // futurePlans = fetchPlans(); } void filterPlans(String query) { print("allPlans before filtering: $allPlans"); final lowerQuery = query.toLowerCase(); setState(() { filteredPlans = allPlans.where((plan) { return (plan.planId?.toLowerCase().contains(lowerQuery) ?? false) || (plan.employeeCode?.toLowerCase().contains(lowerQuery) ?? false) || (plan.tripTitle?.toLowerCase().contains(lowerQuery) ?? false) || (plan.userName?.toLowerCase().contains(lowerQuery) ?? false) || (plan.travellerName?.toLowerCase().contains(lowerQuery) ?? false) || (plan.tripType?.toLowerCase().contains(lowerQuery) ?? false) || (plan.createdOn?.toLowerCase().contains(lowerQuery) ?? false) || (plan.statusValue?.toLowerCase().contains(lowerQuery) ?? false); }).toList(); }); print("filteredPlans: $filteredPlans"); } void loadInitialData() async { String? layoutString = await getLayoutColor(); String? bodyStringColor = await getBodyColor(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor)) : Colors.white; }); } Future initializeData() async { token = await getToken(); userId = await getUserId(); orgId = await getOrgId(); roleUser = await getRoleUser(); TripPlanAction = await getTripPlanAction(); if (token == null || userId == null) { print("Token or USerId missing"); return; } else { setState(() { futurePlans = fetchPlans(); }); // Wait for futurePlans to be fetched and update allPlans futurePlans.then((plans) { setState(() { allPlans = plans; // Set allPlans after the fetch is complete filteredPlans = allPlans; // You can update filteredPlans too if needed }); }); } } void refresh() { print("Refresj"); setState(() { futurePlans = fetchPlans(); }); // Wait for futurePlans to be fetched and update allPlans futurePlans.then((plans) { setState(() { allPlans = plans; // Set allPlans after the fetch is complete filteredPlans = allPlans; // You can update filteredPlans too if needed }); }); } Future getUserId() async { final prefs = await SharedPreferences.getInstance(); final String? userDataString = prefs.getString('user_data'); if (userDataString != null) { try { final Map userData = jsonDecode(userDataString); return userData["user_id"]?.toString(); } catch (e) { return null; } } return null; } Future getOrgId() async { final prefs = await SharedPreferences.getInstance(); final String? userDataString = prefs.getString('user_data'); if (userDataString != null) { try { final Map userData = jsonDecode(userDataString); return userData["org_id"]?.toString(); } catch (e) { return null; } } return null; } Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); } // Fetch API Data Future> fetchPlans() async { late String apiUrldata; apiUrldata = '$apiUrl/api/plans?org_id=$orgId'; // if (roleUser == "Org Admin") { // apiUrldata = '$apiUrl/api/plans?org_id=$orgId'; // } else { // apiUrldata = // '$apiUrl/api/plans/findTravelAgentPlanList?org_id=$orgId&user_id=$userId'; // } if (token == null) { throw Exception('Token not found. Please log in.'); } final response = await http.get( Uri.parse(apiUrldata), headers: { 'Authorization': 'Bearer $token', // Add token here 'Content-Type': 'application/json', }, ); if (response.statusCode == 200) { final data = json.decode(response.body); List plansJson = data['data']; return plansJson.map((json) => Plan.fromJson(json)).toList(); } else { throw Exception('Failed to load plans'); } } Future postPlanData(planData, planId) async { final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan'; final token = await getToken(); // Fetch token if (token == null) { throw Exception('Token not found. Please log in.'); } planData['is_active'] = "0"; print("POSTPlanTesting------- $planData"); // Add plan_id for update try { final response = await http.post( Uri.parse(apiUrldata), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, body: jsonEncode(planData), // Convert map to JSON ); if (response.statusCode == 200) { print("Plan Deleted successfully!"); print("Response: ${response.body}"); initializeData(); } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); } } catch (e) { print(" Error submitting plan: $e"); } } void deletePlan(String planId) async { bool confirmed = await apiService.showCancelConfirmationDialog(context, layoutColor); if (confirmed) { try { Map planData = await ApiService.getViewPlan(planId); print("ViewAAA - $planData"); refresh(); // postPlanData(planData, planId); } catch (e) { print("Error fetching plan: $e"); } } else { print("User cancelled"); } } Widget build(BuildContext context) { return ResponsiveBuilder(builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( backgroundColor: Color(0xFFf5f5f5), // backgroundColor: Color(0xFFFCFCFC), // appBar: isDesktop ? null : const CustomAppBar(title: 'Home'), // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), appBar: CustomAppBar(isDesktop: isDesktop), drawer: CustomDrawer(isDesktop: false), body: Padding( padding: isDesktop ? EdgeInsets.symmetric( horizontal: MediaQuery.of(context).size.width * 0.1, // 30% of screen width as horizontal padding vertical: MediaQuery.of(context).size.height * 0, // 5% of screen height as vertical padding ) : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), Expanded(child: buildGroupListLayout(isDesktop)) ], ), ), ); }); } Widget buildGroupListLayout(bool isDesktop) { return Container( // decoration: BoxDecoration( // color: Colors.amber, // // color: bodyColor, // // color: Color(0xFFE1F5FE), // // border: Border.all( // // // color: Color(0xFFF7F7FB), // // color: Colors.white, // // width: 3.5) // ), child: buildTableLayout(isDesktop), ); } Widget buildTableLayout(isDesktop) { // String _formatDate(String rawDate) { // try { // final dateTime = DateTime.parse(rawDate); // return DateFormat('dd, MMM yyyy hh:m a').format(dateTime); // } catch (e) { // return rawDate; // fallback if parsing fails // } // } String _formatDate(String rawDate) { try { final dateTime = DateTime.parse(rawDate); return DateFormat('dd, MMM yyyy HH:mm') .format(dateTime); // 24-hour format } catch (e) { return rawDate; // fallback if parsing fails } } return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, padding: const EdgeInsets.only(top: 15, bottom: 15, left: 20, right: 20), height: isDesktop ? MediaQuery.of(context).size.height * 0.98 : MediaQuery.of(context).size.height, decoration: BoxDecoration( border: isDesktop ? Border.all( width: 2, color: Colors.white, // color: Color(0xFFF7F7FB), ) : null, color: isDesktop ? Colors.white : Color(0xFFFCFCFC), // color: Color(0xFFF7F7FB), // color: Colors.amber, ), child: Padding( padding: const EdgeInsets.all(1.0), child: Container( // padding: const EdgeInsets.all(10.0), color: isDesktop ? Colors.white : Color(0xFFFCFCFC), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Divider( // thickness: 0.2, // how "thick" the line is // color: Colors.grey, // optional // ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Text( 'All Trips', style: GoogleFonts.poppins( fontSize: isDesktop ? 16 : 14, fontWeight: FontWeight.w600, color: Colors.black, ), // style: TextStyle( // fontFamily: "Inter", // fontSize: isDesktop ? 16 : 14, // fontWeight: FontWeight.w600, // color: const Color(0xFF212121) ) ), ], ), SizedBox( width: 1, ), Spacer(), if (isDesktop) Container( width: MediaQuery.of(context).size.width * 0.2, height: 40, child: TextField( controller: searchController, onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", hintStyle: TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), size: 18, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade200, width: 0.5), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade300, width: 1), ), ), style: GoogleFonts.poppins( fontSize: 12, ), ), ), // SizedBox(width: 16), Spacer(), // ElevatedButton( // style: ElevatedButton.styleFrom( // foregroundColor: Colors.white, // backgroundColor: Colors.blueAccent, // ), // onPressed: () { // context.go('/createPlan', extra: { // 'orgId': orgId, // }); // if (!isDesktop) Navigator.pop(context); // }, // child: Row( // children: [ // Icon(Icons.add_circle, color: Colors.white), // SizedBox(width: 5), // Text('NewPlan'), // // ], // ), // ), // ElevatedButton( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF114D8B), // foregroundColor: Colors.white, // disabledBackgroundColor: Color(0xFF114D8B), // disabledForegroundColor: Colors.white, // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // side: BorderSide(color: Color(0xFF114D8B), width: 2), // ), // padding: // EdgeInsets.symmetric(horizontal: 20, vertical: 12), // ), // onPressed: () { // if (TripPlanAction == "Plan Creation Not Allowed") { // showDialog( // context: context, // builder: (context) => AlertDialog( // title: Text( // "Action Not Allowed", // style: TextStyle( // fontSize: 18, fontWeight: FontWeight.bold), // ), // content: Text( // "Plan Creation Not Allowed For This User."), // actions: [ // TextButton( // onPressed: () => Navigator.pop(context), // child: Text( // "OK", // style: TextStyle(color: layoutColor), // ), // ), // ], // ), // ); // } else { // context.go('/createPlan', extra: { // 'orgId': orgId, // }); // if (!isDesktop) Navigator.pop(context); // } // }, // child: Row( // mainAxisSize: // MainAxisSize.min, // Ensures content fits nicely // children: [ // Text( // "Add New Plan", // style: TextStyle(fontSize: 13), // ), // SizedBox(width: 8), // spacing between icon and text // Icon( // Icons.add_circle_outline_rounded, // size: 15, // color: Colors.white, // ), // ], // ), // ), ], ), const SizedBox(height: 10), if (!isDesktop) Row( children: [ Container( width: MediaQuery.of(context).size.width * 0.85, height: 35, child: TextField( controller: searchController, onChanged: filterPlans, decoration: InputDecoration( hintText: "Search...", hintStyle: TextStyle(fontSize: 12, color: Color(0xFF9E9DBD)), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), size: 18, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade200, width: 0.5), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade300, width: 1), ), ), style: GoogleFonts.poppins( fontSize: 12, ), ), ), ], ), FutureBuilder>( future: futurePlans, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { return Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ // const Icon(Icons.error_outline, // color: Colors.redAccent, size: 60), // const SizedBox(height: 1), // Text( // "Oops!", // style: GoogleFonts.poppins( // fontSize: 12, // fontWeight: FontWeight.bold, // color: Colors.redAccent), // // ), const SizedBox(height: 15), Text( "No Trips", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.w600, color: Colors.black54), ), const SizedBox(height: 10), Text( "Please Create Trip", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 12, color: Colors.grey), ), ], ), ), ); } // List plans = snapshot.data!; // List plans = // filteredPlans.isNotEmpty ? filteredPlans : allPlans; List plans = searchController.text.isEmpty ? allPlans : filteredPlans; plans.sort((a, b) => int.parse(b.planId).compareTo(int.parse(a.planId))); List paginatedPlans = plans .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { double minWidth = isDesktop ? constraints.maxWidth : 1300; return ConstrainedBox( constraints: BoxConstraints(minWidth: minWidth), child: DataTable( dividerThickness: 0.5, columnSpacing: isDesktop ? 24.0 : 16.0, border: TableBorder( horizontalInside: BorderSide( width: 0.5, color: Colors.grey.shade200), ), columns: [ DataColumn( label: Text( 'Trip ID', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), DataColumn( label: Text( 'Trip Name', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), DataColumn( label: Text( 'Emp Code', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), DataColumn( label: Text( 'Traveller', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), DataColumn( label: Text( 'Trip Type', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), DataColumn( label: Text( 'Created On', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), DataColumn( label: Text( 'Status', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), DataColumn( label: Text( 'Actions', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600), )), ], rows: paginatedPlans.map((plan) { return DataRow(cells: [ DataCell(Text(plan.planId, style: TextStyle( fontSize: 13, fontFamily: "Inter", ))), DataCell(Text(plan.tripTitle, style: TextStyle( fontSize: 13, fontFamily: "Inter", ), softWrap: true, overflow: TextOverflow.ellipsis)), DataCell(Text(plan.employeeCode ?? " - ", style: TextStyle( fontSize: 13, fontFamily: "Inter", ))), DataCell(Text( plan.userName.isNotEmpty ? plan.userName : plan.travellerName, style: TextStyle( fontSize: 13, fontFamily: "Inter", ))), DataCell(Text(plan.tripType, style: TextStyle( fontSize: 13, fontFamily: "Inter", ))), DataCell(Text(_formatDate(plan.createdOn), // plan.createdOn, style: TextStyle( fontSize: 13, fontFamily: "Inter", ))), DataCell( Container( width: double .infinity, // Set your desired fixed size (equal width and height) height: 25, alignment: Alignment.center, decoration: BoxDecoration( color: getStatusColor(plan.statusValue), borderRadius: BorderRadius.circular(10), ), child: Text( plan.statusValue, textAlign: TextAlign.center, style: TextStyle( color: getStatusTextColor(plan.statusValue), fontSize: 12, fontFamily: "Inter", fontWeight: FontWeight.w400, ), ), ), ), DataCell( Row( children: [ PopupMenuButton( color: Colors.white, padding: EdgeInsets.zero, offset: Offset(0, 30), icon: Icon( Icons.more_vert, color: Color(0xFF475569), size: 14, ), itemBuilder: (context) => [ CustomPopupMenuEntry( child: Container( padding: EdgeInsets.symmetric( horizontal: 8, vertical: 8), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: Icon( Icons.remove_red_eye, color: Color(0xFF475569), size: 18), onPressed: () { Navigator.pop( context); // Close popup manually ApiService.viewPlan( context, plan.planId, isViewMode: true); }, ), IconButton( icon: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, height: 15), onPressed: () { Navigator.pop(context); ApiService.viewPlan( context, plan.planId, isViewMode: false); }, ), IconButton( icon: Icon( Icons.cancel_rounded, size: 18), onPressed: () { Navigator.pop(context); deletePlan(plan.planId); }, ), IconButton( icon: Icon(Icons.download, color: Color(0xFF114D8B), size: 18), onPressed: () { Navigator.pop(context); apiService.getPdfDownload( plan.planId); }, ), IconButton( icon: const Icon( Icons.comment, color: Color(0xFF475569), size: 11, ), onPressed: () { showDialog( context: context, builder: (context) => CommentModalList( // planId: plan.planId, planId: plan .planId .toString(), layoutColorForUser: layoutColor!, role: "Admin"), ); }), ], ), ), ), ], ), ], ), ), ]); }).toList(), ), ); }, ); Widget buildMobileCardView(List paginatedPlans) { return ListView.builder( itemCount: paginatedPlans.length, itemBuilder: (context, index) { final plan = paginatedPlans[index]; return Card( color: Colors.white, margin: EdgeInsets.symmetric(horizontal: 12, vertical: 6), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 3, // Control the strength of the shadow // shadowColor: Colors.black54, // Softer shadow // clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(12.0), // padding: EdgeInsets.symmetric(vertical: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ // Status and Employee Code Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: EdgeInsets.symmetric( horizontal: 8, vertical: 4), decoration: BoxDecoration( color: getStatusColor( plan.statusValue), borderRadius: BorderRadius.circular(8), ), child: Text( plan.statusValue, style: TextStyle( color: getStatusTextColor( plan.statusValue), fontSize: 10, fontWeight: FontWeight.w600, ), ), ), ], ), Column( children: [ PlanPopupMenu( plan: plan, deletePlan: deletePlan, apiService: apiService, layoutColor: layoutColor, ), ], ), ], ), SizedBox(height: 2), // Trip Id and Trip Name Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(' ${plan.tripTitle}', style: TextStyle( fontSize: 12, fontFamily: "Inter", fontWeight: FontWeight.bold)), ], ), ], ), SizedBox(height: 2), // Type and Created On Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(' ${plan.tripType}', style: TextStyle( fontSize: 9, color: Colors.black87, fontFamily: "Inter", )), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('${_formatDate(plan.createdOn)}', style: TextStyle( fontSize: 9, color: Colors.black87, fontFamily: "Inter", )), ], ), ], ), SizedBox(height: 3), // Name Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( plan.userName.isNotEmpty ? '${plan.userName}' : '${plan.travellerName}', style: TextStyle( fontSize: 9, fontFamily: "Inter", color: Colors.black87), ), ], ), Spacer(), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( plan.employeeCode ?? 'No Data', style: TextStyle( fontSize: 9, fontFamily: "Inter", color: Colors.black87), ), ], ), ], ), // Actions ], ), ), ); }, ); } return Expanded( child: Column( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: isDesktop ? (searchController.text.isNotEmpty && filteredPlans.isEmpty ? Center( child: Text( "No matches found", style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey), ), ) : SingleChildScrollView( scrollDirection: Axis.vertical, child: table, )) : (searchController.text.isNotEmpty && filteredPlans.isEmpty ? Center( child: Text( "No matches found", style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey), ), ) : buildMobileCardView(paginatedPlans)), // child: isDesktop // ? SingleChildScrollView( // scrollDirection: Axis.vertical, // child: table, // <-- your existing table // ) // : buildMobileCardView(paginatedPlans), ), PaginationControls( currentPage: currentPage, itemsPerPage: itemsPerPage, totalItems: plans.length, activeColor: layoutColor, // your theme color onPageChanged: (page) { setState(() { currentPage = page; }); }, onItemsPerPageChanged: (items) { setState(() { itemsPerPage = items; currentPage = 0; }); }, ), ], ), ); }, ) ], ), ), ), ); } // Helper functions for colors Color getStatusColor(String status) { if (status == "Partially Approved") return Colors.yellow.shade100; if (status == "Approved") return Colors.green.shade100; if (status == "Completed") return Colors.green.shade500; if (status == "Rejected") return Colors.red.shade100; return Colors.grey.shade100; } Color getStatusTextColor(String status) { if (status == "Partially Approved" || status == "Approved" || status == "Rejected") { return Colors.black; } else if (status == "Completed") { return Colors.white; } return Colors.grey; } }