import 'dart:convert'; import 'dart:core'; import 'package:frontend/Screens/allTrips/plan_info_mdl.dart'; 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 '../../routes/mainLayout.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(); late bool _listDialogShown = false; @override void initState() { super.initState(); _checkAuthAndLoadData(); // // If token exists, load the dashboard data // WidgetsBinding.instance.addPostFrameCallback((_) { // initializeData(); // loadInitialData(); // // // futurePlans.then((plans) { // // setState(() { // // allPlans = plans; // // filteredPlans = plans; // // }); // // }); // }); // // futurePlans = fetchPlans(); } void _checkAuthAndLoadData() async { final String? token = await getToken(); // Your async function to get token if (token == null || token.isEmpty) { // Token doesn't exist → redirect to login context.go( "/", ); // or use: router.go("/") if you're using `GoRouter` directly return; } else { getToken(); initializeData(); loadInitialData(); } } 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(); currentPage = 0; }); 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', 'app-signature': 'ts-traveltool-2025-signature-123456', }, ); if (response.statusCode == 200) { final data = json.decode(response.body); // List plansJson = []; List plansJson = data['data']; return plansJson.map((json) => Plan.fromJson(json)).toList(); } else if (response.statusCode == 403) { print("403-FORB"); await apiService.logout(context); return []; // throw Exception('Failed to load users'); } 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', 'app-signature': 'ts-traveltool-2025-signature-123456', }, body: jsonEncode(planData), // Convert map to JSON ); if (response.statusCode == 200) { print("Plan Deleted successfully!"); print("Response: ${response.body}"); initializeData(); } else if (response.statusCode == 403) { print("403-FORB"); await apiService.logout(context); return null; // throw Exception('Failed to load users'); } 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, context, ); print("ViewAAA - $planData"); refresh(); // postPlanData(planData, planId); } catch (e) { print("Error fetching plan: $e"); } } else { print("User cancelled"); } } Future _showDetails(id) async { _listDialogShown = true; final String apiUrldata = '$apiUrl/api/plans/get_plan_approval_status?plan_id=$id'; final String? token = await getToken(); String label = ""; List> approverData = []; if (token == null) { print('Token not found. Please log in.'); label = "Error - Token not found"; } else { try { final response = await http.get( Uri.parse(apiUrldata), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', 'app-signature': 'ts-traveltool-2025-signature-123456', }, ); if (response.statusCode == 200) { final data = json.decode(response.body); label = data['data']['model_heading'] ?? ""; approverData = List>.from( data['data']['approver_data'] ?? [], ); } else if (response.statusCode == 403) { print("403-FORB"); await apiService.logout(context); return null; // throw Exception('Failed to load users'); } else { print('Failed to load'); label = "Error - Failed to load data"; } } catch (e) { print('Exception occurred: $e'); label = "Error - Something went wrong"; } } showDialog( context: context, builder: (context) => AlertDialog( title: Text( label, style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.black, ), overflow: TextOverflow.ellipsis, ), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ if (approverData.isNotEmpty) SizedBox( height: 250, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: SingleChildScrollView( scrollDirection: Axis.vertical, child: DataTable( columns: [ // DataColumn(label: Text('S.No', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Approver ID', style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600))), DataColumn( label: Text( 'Status', style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w600, ), ), ), ], rows: approverData.asMap().entries.map((entry) { final index = entry.key + 1; final item = entry.value; final approverIdKey = item.keys.firstWhere( (k) => k.endsWith('_id'), orElse: () => '', ); final statusKey = item.keys.firstWhere( (k) => k.endsWith('_status'), orElse: () => '', ); final approverId = item[approverIdKey]?.toString() ?? '-'; final statusText = item[statusKey]?.toString() ?? 'Unknown'; return DataRow( cells: [ // DataCell(Text('$index')), // DataCell(SizedBox(width: 120, child: Text(approverId, overflow: TextOverflow.ellipsis))), DataCell( SizedBox( child: Text( statusText, overflow: TextOverflow.ellipsis, ), ), ), ], ); }).toList(), ), ), ), ) else Text( "-- no data. --", style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w300, ), ), ], ), ), actions: [ TextButton( onPressed: () { Navigator.pop(context); _listDialogShown = false; }, child: Text("Close"), ), ], ), ); } 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: isDesktop ? const EdgeInsets.only(top: 5, bottom: 5, left: 20, right: 20) : EdgeInsets.all(3.0), 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.only(top: 15, bottom: 15, left: 5, right: 5), 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: [ Padding( padding: isDesktop ? const EdgeInsets.all(0) : const EdgeInsets.only(left: 14.0), child: 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( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: MediaQuery.of(context).size.width * 0.88, 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) { final adjHgt = MediaQuery.of(context).size.height; 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), // // ), SizedBox(height: adjHgt / 4), Text( " No Data Found", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.bold, 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 SingleChildScrollView( scrollDirection: Axis.horizontal, child: SingleChildScrollView( scrollDirection: Axis.vertical, child: 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( 'S.NO', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600, ), ), ), DataColumn( label: Text( 'Trip ID', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600, ), ), ), DataColumn( label: Expanded( flex: 1, child: Text( 'Trip Name', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600, ), ), ), ), DataColumn( label: Text( 'Trip Type', 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( '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) paginatedPlans.asMap().entries.map((entry) { int index = entry.key; var plan = entry.value; print("fordIf - ${plan.forexId}"); return DataRow( cells: [ DataCell( Text( // "${index + 1}", "${(currentPage * itemsPerPage) + index + 1}", style: TextStyle( fontSize: 13, fontFamily: "Inter", ), ), ), DataCell( Text( plan.planId, style: TextStyle( fontSize: 13, fontFamily: "Inter", ), ), ), DataCell( SizedBox( width: 130, child: Text( plan.tripTitle, style: TextStyle( fontSize: 13, fontFamily: "Inter", ), softWrap: true, maxLines: 2, // overflow: TextOverflow.ellipsis, ), ), ), DataCell( Text( plan.tripType, style: TextStyle( fontSize: 13, fontFamily: "Inter", ), ), ), 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( _formatDate(plan.createdOn), // plan.createdOn, style: TextStyle( fontSize: 13, fontFamily: "Inter", ), ), ), DataCell( GestureDetector( onTap: () => _showDetails(plan.planId), child: 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, ), tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context, ); // Close popup manually ApiService().viewPlan( context, plan.planId, isViewMode: true, ); }, ), if (plan.statusValue == "Pending Approval" || plan.statusValue == "Partially Approved") IconButton( icon: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, height: 15, ), tooltip: 'Edit The Trip Details', onPressed: () { Navigator.pop( context, ); ApiService().viewPlan( context, plan.planId, isViewMode: false, ); }, ), IconButton( icon: Icon( Icons .cancel_rounded, size: 18, ), tooltip: 'Cancellation The Trip Details', onPressed: () { Navigator.pop( context, ); deletePlan( plan.planId, ); }, ), IconButton( icon: Icon( Icons .download, color: Color( 0xFF475569, ), size: 18, ), tooltip: 'Download The PDF', onPressed: () { Navigator.pop( context, ); apiService .getPdfDownload( context, plan.planId, ); }, ), if (plan.statusValue == "Approved" && plan.forexId != null && plan.forexId != '') IconButton( icon: Icon( Icons .monetization_on_outlined, color: Color( 0xFF475569, ), size: 18, ), tooltip: 'Download Forex PDF', onPressed: () { Navigator.pop( context, ); apiService.getForexPdfDownload( context, plan.forexId, ); }, ), IconButton( icon: const Icon( Icons.comment, color: Color( 0xFF475569, ), size: 11, ), tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, builder: ( context, ) => CommentModalList( // planId: plan.planId, planId: plan.planId.toString(), layoutColorForUser: layoutColor!, role: "Admin", ), ); }, ), IconButton( icon: const Icon( Icons .info_outlined, color: Color( 0xFF475569, ), size: 18, ), tooltip: 'Trip Info', onPressed: () { showDialog( context: context, builder: ( context, ) => TripInformation( // planId: plan.planId, planId: plan.planId.toString(), layoutColorForUser: layoutColor!, ), ); }, ), ], ), ), ), ], ), ], ), ), ], ); }).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(10.0), child: Row( children: [ // Container( // // color: Colors.red.shade50, // child: Column( // mainAxisAlignment: MainAxisAlignment.center, // crossAxisAlignment: // CrossAxisAlignment.center, // children: [ // Image.asset( // plan.tripType == 'Domestic' // ? 'assets/images/IconsImg/Domestic_new.png' // : 'assets/images/IconsImg/International_new.png', // // width: 65, // height: // plan.tripType == 'Domestic' // ? 37 // : 40, // ), // ], // ), // ), // SizedBox(width: 10), Expanded( child: Container( // color: Colors.yellow.shade50, child: Column( children: [ Row( // mainAxisAlignment: // MainAxisAlignment.spaceBetween, children: [ Container( // color: Colors.red.shade50, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( plan.tripType == 'Domestic' ? 'assets/images/IconsImg/Domestic_new.png' : 'assets/images/IconsImg/International_new.png', // width: 65, height: plan.tripType == 'Domestic' ? 28 : 30, ), ], ), ), SizedBox(width: 10), Expanded( flex: 1, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${plan.tripTitle}', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, ), ), ], ), ), SizedBox(width: 10), Column( children: [ // PlanPopupMenu( // plan: plan, // deletePlan: deletePlan, // apiService: apiService, // layoutColor: layoutColor, // ), 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: 1, vertical: 1, // 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, ), tooltip: 'View The Trip Details', onPressed: () { Navigator.pop( context, ); // Close popup manually ApiService().viewPlan( context, plan.planId, isViewMode: true, ); }, ), if (plan.statusValue == "Pending Approval" || plan.statusValue == "Partially Approved") IconButton( icon: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, height: 15, ), tooltip: 'Edit The Trip Details', onPressed: () { Navigator.pop( context, ); ApiService().viewPlan( context, plan.planId, isViewMode: false, ); }, ), IconButton( icon: Icon( Icons .cancel_rounded, size: 18, ), tooltip: 'Cancellation The Trip Details', onPressed: () { Navigator.pop( context, ); deletePlan( plan.planId, ); }, ), IconButton( icon: Icon( Icons .download, color: Color( 0xFF475569, ), size: 18, ), tooltip: 'Download The PDF', onPressed: () { Navigator.pop( context, ); apiService.getPdfDownload( context, plan.planId, ); }, ), if (plan.statusValue == "Approved" && plan.forexId != null && plan.forexId != '') IconButton( icon: Icon( Icons .monetization_on_outlined, color: Color( 0xFF475569, ), size: 18, ), tooltip: 'Download Forex PDF', onPressed: () { Navigator.pop( context, ); apiService.getForexPdfDownload( context, plan.forexId, ); }, ), IconButton( icon: const Icon( Icons .comment, color: Color( 0xFF475569, ), size: 11, ), tooltip: 'Trip Comments', onPressed: () { showDialog( context: context, builder: ( context, ) => CommentModalList( // planId: plan.planId, planId: plan.planId.toString(), layoutColorForUser: layoutColor!, role: "Admin", ), ); }, ), IconButton( icon: const Icon( Icons .info_outline_rounded, color: Color( 0xFF475569, ), size: 20, ), tooltip: 'Trip Info', onPressed: () { showDialog( context: context, builder: ( context, ) => TripInformation( // planId: plan.planId, planId: plan.planId.toString(), layoutColorForUser: layoutColor!, ), ); }, ), ], ), ), ), ], ), ], ), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [ Flexible( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( // plan.userName.isNotEmpty // ? plan.userName // : '${plan.travellerName}', '${plan.userName.isNotEmpty ? plan.userName : plan.travellerName} (${plan.employeeCode ?? 'No Data'})', style: GoogleFonts.poppins( fontSize: 10, color: Colors.black87, ), // overflow: TextOverflow.ellipsis, // maxLines: 2, ), // Text( // // plan.userName.isNotEmpty // // ? plan.userName // // : '${plan.travellerName}', // // '(${plan.employeeCode ?? 'No Data'})', // plan.employeeCode ?? // 'No Data', // style: GoogleFonts.poppins( // fontSize: 9, // color: Colors.black87, // ), // ), Text( '${_formatDate(plan.createdOn)}', style: GoogleFonts.poppins( fontSize: 9, color: Colors.grey, ), ), ], ), ), Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ GestureDetector( onTap: () => _showDetails( plan.planId, ), child: Container( padding: EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: getStatusColor( plan.statusValue, ), borderRadius: BorderRadius.circular( 8, ), ), child: Text( plan.statusValue, style: GoogleFonts.poppins( color: getStatusTextColor( plan.statusValue, ), fontSize: 9, fontWeight: FontWeight.w600, ), ), ), ), ], ), ], ), ], ), ), ), ], ), // padding: EdgeInsets.symmetric(vertical: 12), // child: Container( // // color: Colors.yellow, // child: Column( // crossAxisAlignment: CrossAxisAlignment.start, // mainAxisAlignment: MainAxisAlignment.start, // children: [ // // Status and Employee Code // Row( // mainAxisAlignment: // MainAxisAlignment.spaceBetween, // children: [ // Column( // crossAxisAlignment: // CrossAxisAlignment.start, // children: [ // GestureDetector( // onTap: // () => _showDetails(plan.planId), // child: 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, // // ), // 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: 1, // vertical: 1, // // 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, // ), // tooltip: // 'View The Trip Details', // onPressed: () { // Navigator.pop( // context, // ); // Close popup manually // ApiService.viewPlan( // context, // plan.planId, // isViewMode: // true, // ); // }, // ), // if (plan.statusValue == // "Pending Approval" || // plan.statusValue == // "Partially Approved") // IconButton( // icon: Image.asset( // 'assets/images/IconsImg/edit.png', // width: 20, // height: 15, // ), // tooltip: // 'Edit The Trip Details', // onPressed: () { // Navigator.pop( // context, // ); // ApiService.viewPlan( // context, // plan.planId, // isViewMode: // false, // ); // }, // ), // IconButton( // icon: Icon( // Icons // .cancel_rounded, // size: 18, // ), // tooltip: // 'Cancellation The Trip Details', // onPressed: () { // Navigator.pop( // context, // ); // deletePlan( // plan.planId, // ); // }, // ), // IconButton( // icon: Icon( // Icons.download, // color: Color( // 0xFF114D8B, // ), // size: 18, // ), // tooltip: // 'Download The PDF', // onPressed: () { // Navigator.pop( // context, // ); // apiService // .getPdfDownload( // plan.planId, // ); // }, // ), // IconButton( // icon: const Icon( // Icons.comment, // color: Color( // 0xFF475569, // ), // size: 11, // ), // tooltip: // 'Trip Comments', // onPressed: () { // showDialog( // context: // context, // builder: // ( // context, // ) => CommentModalList( // // planId: plan.planId, // planId: // plan.planId // .toString(), // layoutColorForUser: // layoutColor!, // role: // "Admin", // ), // ); // }, // ), // ], // ), // ), // ), // ], // ), // ], // ), // ], // ), // // 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; } }