import 'dart:convert'; import 'dart:core'; import 'dart:typed_data'; 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 'package:universal_html/html.dart' as html; import '../../routes/custom_appBar.dart'; import '../../routes/custom_drawer.dart'; import '../../services/apiService.dart'; import '../../utils/auth_utils.dart'; import '../../utils/pagination.dart'; import '../../widgets/custom_breadcrumb_navigation.dart'; import '../../widgets/custom_text_field.dart'; class ServicesAnalysis extends StatefulWidget { const ServicesAnalysis({super.key}); @override _ServicesAnalysisState createState() => _ServicesAnalysisState(); } class _ServicesAnalysisState extends State with SingleTickerProviderStateMixin { final ApiService apiService = ApiService(); String? userId; String? orgId; String? roleUser; String? token; Color? layoutColor; Color? bodyColor; int currentPage1 = 0; int itemsPerPage1 = 10; int currentPage2 = 0; int itemsPerPage2 = 10; late TabController _tabController; String selectedTabName = 'domestic'; Map focusNodes = {}; Map focusStates = {}; Map textControllers = {}; Map errorMessages = {}; // Map ServicesAnalysisReport = {}; List> domesticData = []; List> internationalData = []; // Map domesticData = {}; // Map internationalData = {}; List> filteredDomestic = []; List> filteredInternational = []; TextEditingController searchDomesticController = TextEditingController(); TextEditingController searchInternationalController = TextEditingController(); bool loaderFlag = false; @override void initState() { super.initState(); loadInitialData(); _checkAuthAndLoadData(); if (!textControllers.containsKey("from_date")) { textControllers["from_date"] = TextEditingController(); } if (!textControllers.containsKey("to_date")) { textControllers["to_date"] = TextEditingController(); } if (!focusNodes.containsKey("from_date")) { focusNodes["from_date"] = FocusNode(); } if (!focusNodes.containsKey("to_date")) { focusNodes["to_date"] = FocusNode(); } if (!focusStates.containsKey("to_date")) { focusStates["to_date"] = false; } if (!focusStates.containsKey("from_date")) { focusStates["from_date"] = false; } _tabController = TabController(length: 2, vsync: this); _tabController.addListener(() { if (_tabController.indexIsChanging == false) { // Only fire when tab change is complete setState(() { selectedTabName = getTabName(_tabController.index); }); print(" :) :X Selected Tab: ${getTabName(_tabController.index)}"); } }); // Add focus listeners focusNodes.forEach((key, focusNode) { _addFocusListener(focusNode, (focus) { setState(() { focusStates[key] = focus; }); }); }); getToken(); // fetchServicesAnalysisReport(); } String getTabName(int index) { switch (index) { case 0: return "domestic"; case 1: return "international"; default: return "domestic"; // return "Unknown"; } } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } 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(); if (token == null || userId == null) { print("Token or USerId missing"); return; } } 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; } if (!mounted) return; try { WidgetsBinding.instance.addPostFrameCallback((_) { loadInitialData(); }); } catch (e) { print("service report : $e"); } } 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'); } Future handleSubmit() async { errorMessages.remove("from_date"); errorMessages.remove("to_date"); setState(() { domesticData = []; internationalData = []; currentPage1 = 0; itemsPerPage1 = 10; currentPage2 = 0; itemsPerPage2 = 10; }); final fromDateText = textControllers["from_date"]?.text; final toDateText = textControllers["to_date"]?.text; if (fromDateText == null || fromDateText.isEmpty || toDateText == null || toDateText.isEmpty) { fromDateText == null || fromDateText.isEmpty ? errorMessages["from_date"] = "Required" : errorMessages.remove("from_date"); toDateText == null || toDateText.isEmpty ? errorMessages["to_date"] = "Required" : errorMessages.remove("to_date"); setState(() {}); return; } // Parse the string to DateTime using the correct format final dateFormat = DateFormat('dd-MM-yyyy'); final DateTime fromDate = dateFormat.parse(fromDateText); final DateTime toDate = dateFormat.parse(toDateText); // Check if from date is after to date if (fromDate.isAfter(toDate)) { errorMessages["to_date"] = "Invalid Date Range"; setState(() {}); return; } setState(() { loaderFlag = true; }); // Format for API request final formattedfromDate = DateFormat('yyyy-MM-dd').format(fromDate); final formattedtoDate = DateFormat('yyyy-MM-dd').format(toDate); // Fetch and update loaderFlag = true; try { final result = await apiService.CallReports( 'misServicesAnalysis', jsonEncode({ "fromDate": '$formattedfromDate', "toDate": '$formattedtoDate', }), ); setState(() { domesticData = List>.from( result['status'] == 'success' ? result['data']['domestic'] ?? [] : [], ); internationalData = List>.from( result['status'] == 'success' ? result['data']['international'] ?? [] : [], ); filteredDomestic = domesticData; filteredInternational = internationalData; loaderFlag = false; }); } catch (e) { // handle error setState(() { loaderFlag = false; domesticData = []; internationalData = []; }); } } Future handleDownload() async { final dateFormat = DateFormat('dd-MM-yyyy'); if (textControllers["from_date"]!.text.isEmpty || textControllers["to_date"]!.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: Colors.redAccent, content: Text("Please choose a valid date range."), behavior: SnackBarBehavior.floating, ), ); return; } final DateTime fromDate = dateFormat.parse( textControllers["from_date"]!.text, ); final DateTime toDate = dateFormat.parse(textControllers["to_date"]!.text); final formattedFromDate = DateFormat('yyyy-MM-dd').format(fromDate); final formattedToDate = DateFormat('yyyy-MM-dd').format(toDate); final name = 'MIS Services Analysis - $selectedTabName Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})'; final safeName = name.replaceAll(RegExp(r'[^\w\s-]'), ''); final excelName = '$safeName'; final result = await apiService.reportExcelDownload( 'misServicesAnalysis', jsonEncode({ "fromDate": formattedFromDate, "toDate": formattedToDate, "export": selectedTabName, }), excelName, ); ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: result['status'] ? Colors.green : Colors.red, content: Text(result['message']), behavior: SnackBarBehavior.floating, ), ); } void handleDomesticFilter(String query) { final lowerQuery = query.toLowerCase(); setState(() { filteredDomestic = domesticData.where((object) { return (object['Service']?.toLowerCase().contains(lowerQuery) ?? false) || (object['service_count']?.toLowerCase().contains(lowerQuery) ?? false) || (object['plan_trip_type']?.toLowerCase().contains(lowerQuery)); }).toList(); currentPage1 = 0; }); print("handleDomesticFilter > Total entries: ${filteredDomestic?.length ?? 0}"); } void handleInternationalFilter(String query) { final lowerQuery = query.toLowerCase(); setState(() { filteredInternational = internationalData.where((object) { return (object['Service']?.toLowerCase().contains(lowerQuery) ?? false) || (object['service_count']?.toLowerCase().contains(lowerQuery) ?? false) || (object['plan_trip_type']?.toLowerCase().contains(lowerQuery)); }).toList(); currentPage2 = 0; }); print("handleInternationalFilter > Total entries: ${filteredInternational?.length ?? 0}"); } @override // Widget build(BuildContext context) { // // TODO: implement build // throw UnimplementedError(); // } Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( backgroundColor: Color(0xFFf5f5f5), 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), // const Expanded(child: Center(child: Text("User Page Content"))), Expanded(child: buildServicesAnalysisLayout(isDesktop)), ], ), ), ); }, ); } Widget buildServicesAnalysisLayout(bool isDesktop) { return Container(child: buildServicesAnalysisFormLayout(isDesktop)); } Widget buildServicesAnalysisFormLayout(bool isDesktop) { return Container( margin: isDesktop ? EdgeInsets.all(10.0) : null, padding: const EdgeInsets.only(top: 5, bottom: 5, left: 20, right: 20), // color: Colors.brown, height: isDesktop ? MediaQuery.of(context).size.height * 1 : MediaQuery.of(context).size.height, decoration: BoxDecoration( border: isDesktop ? Border.all(width: 2, color: Colors.white) : null, color: isDesktop ? Colors.white : Color(0xFFFCFCFC), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // ------------------- First Row ------------------- Container( color: Colors.white, child: isDesktop ? Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ buildTitle(isDesktop), Spacer(), buildExports(isDesktop) ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildTitle(isDesktop), SizedBox(height: 8), buildExports(isDesktop) ], ) ), SizedBox(height: 5), // ------------------- Second Row ------------------- Container( color: Colors.white, child: isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildFromDateField(isDesktop), Spacer(), buildToDateField(isDesktop), Spacer(), // Space after Last Name buildSearchButton(isDesktop), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildFromDateField(isDesktop), SizedBox(height: 8), // Vertical space buildToDateField(isDesktop), SizedBox(height: 8), buildSearchButton(isDesktop), ], ), ), SizedBox(height: 5), // ------------------- Third Row ------------------- DefaultTabController( length: 2, child: Column( children: [ TabBar( controller: _tabController, labelColor: Colors.black, unselectedLabelColor: Colors.grey, indicatorColor: Color(0xFF114D8B), tabs: [Tab(text: "Domestic"), Tab(text: "International")], ), SizedBox(height: 5), Container( // color:Colors.grey, height: MediaQuery.of(context).size.height * 0.6, // height: 200, // Or use Expanded for flexible height child: TabBarView( controller: _tabController, children: [ loaderFlag ? Center(child: CircularProgressIndicator()) : domesticData.isEmpty ? Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox(height: MediaQuery.of(context).size.height / 4), Text( "No Records Found", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.w500, color: Colors.black54, ), ), const SizedBox(height: 10), Text( "Please ensure the selected dates contain report data.", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.black54, ), ), ], ), ), ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 16.0, vertical: 8, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ /// Title on the left Expanded( child: Text( "Domestic Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})", style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700], ), overflow: TextOverflow .ellipsis, // Ensures title doesn't overflow ), ), Container( width: MediaQuery.of(context).size.width * 0.2, height: 40, child: TextField( controller: searchDomesticController, onChanged: handleDomesticFilter, 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), // /// Buttons on the right // Row( // children: [ // ElevatedButton( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF114D8B), // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // side: BorderSide(color: Color(0xFF114D8B), width: 2), // ), // padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), // ), // onPressed: handleDownload, // child: Row( // children: [ // Text( // "Excel", // style: GoogleFonts.poppins( // fontSize: isDesktop ? 13 : 11, // color: Colors.white, // ), // ), // SizedBox(width: 8), // Icon(Icons.file_present_outlined, size: 15, color: Colors.white), // ], // ), // ), // SizedBox(width: 10), // ElevatedButton( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF114D8B), // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // side: BorderSide(color: Color(0xFF114D8B), width: 2), // ), // padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), // ), // onPressed: handleDownload, // child: Row( // children: [ // Text( // "PDF", // style: GoogleFonts.poppins( // fontSize: isDesktop ? 13 : 11, // color: Colors.white, // ), // ), // SizedBox(width: 8), // Icon(Icons.picture_as_pdf_outlined, size: 15, color: Colors.white), // ], // ), // ), // ], // ), ], ), ), /* For pagination for list ... */ // Expanded( // child: SingleChildScrollView( // scrollDirection: Axis.horizontal, // child: SingleChildScrollView( // scrollDirection: Axis.vertical, // child: DataTable( // dividerThickness: 0.5, // columnSpacing: isDesktop ? 30.0 : 24.0, // border: TableBorder( // horizontalInside: BorderSide( // width: 0.5, // color: Colors.grey.shade200, // ), // ), // columns: [ // DataColumn(label: Text('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // ], // rows: domesticData.map((entry) { // return DataRow(cells: [ // DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))), // ]); // }).toList(), // ), // ), // ), // ), // -------- Table with Pagination -------- Expanded( child: LayoutBuilder( builder: (context, constraints) { final double minWidth = isDesktop ? constraints.maxWidth : 1300; List> newDomesticData =filteredDomestic.isNotEmpty ? filteredDomestic : domesticData; // Pagination logic List> paginatedDomestic = newDomesticData .skip(currentPage1 * itemsPerPage1) .take(itemsPerPage1) .toList(); return ( (searchDomesticController.text.isNotEmpty && filteredDomestic.isEmpty) ? Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox(height: MediaQuery.of(context).size.height / 4), Text( "No Records Found", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.w500, color: Colors.black54, ), ), const SizedBox(height: 10), Text( "Please ensure the selected dates contain report data.", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.black54, ), ), ], ), ), ) : Column( children: [ Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: ConstrainedBox( constraints: BoxConstraints( minWidth: minWidth, ), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: DataTable( dividerThickness: 0.5, columnSpacing: isDesktop ? 30.0 : 24.0, border: TableBorder( horizontalInside: BorderSide( width: 0.5, color: Colors .grey .shade200, ), ), columns: [ DataColumn( label: Text( 'Service', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight .w600, ), ), ), DataColumn( label: Text( 'Count', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight .w600, ), ), ), DataColumn( label: Text( 'Plan Trip Type', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight .w600, ), ), ), ], rows: paginatedDomestic.map(( entry, ) { return DataRow( cells: [ DataCell( Text( '${entry['Service']}', style: GoogleFonts.poppins( fontSize: 12, ), ), ), DataCell( Text( '${entry['service_count']}', style: GoogleFonts.poppins( fontSize: 12, ), ), ), DataCell( Text( '${entry['plan_trip_type']}', style: GoogleFonts.poppins( fontSize: 12, ), ), ), ], ); }).toList(), ), ), ), ), ), // Pagination controls PaginationControls( currentPage: currentPage1, itemsPerPage: itemsPerPage1, totalItems: newDomesticData.length, activeColor: layoutColor, onPageChanged: (page) { setState(() { currentPage1 = page; }); }, onItemsPerPageChanged: (items) { setState(() { itemsPerPage1 = items; currentPage1 = 0; }); }, ), ], )); }, ), ), ], ), // International Tab internationalData.isEmpty ? Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox(height: MediaQuery.of(context).size.height / 4), Text( "No Records Found", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.w500, color: Colors.black54, ), ), const SizedBox(height: 10), Text( "Please ensure the selected dates contain report data.", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.black54, ), ), ], ), ), ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Padding( // padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), // child: Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, // crossAxisAlignment: CrossAxisAlignment.center, // children: [ // /// Title on the left // Expanded( // child: Text( // "International Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})", // style: GoogleFonts.poppins( // fontSize: 13, // fontWeight: FontWeight.w500, // color: Colors.grey[700], // ), // overflow: TextOverflow.ellipsis, // Ensures title doesn't overflow // ), // ), // SizedBox(width: 16), // /// Buttons on the right // Row( // children: [ // ElevatedButton( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF114D8B), // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // side: BorderSide(color: Color(0xFF114D8B), width: 2), // ), // padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), // ), // onPressed: () { // // Excel export logic // }, // child: Row( // children: [ // Text( // "Excel", // style: GoogleFonts.poppins( // fontSize: isDesktop ? 13 : 11, // color: Colors.white, // ), // ), // SizedBox(width: 8), // Icon(Icons.file_present_outlined, size: 15, color: Colors.white), // ], // ), // ), // SizedBox(width: 10), // ElevatedButton( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF114D8B), // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // side: BorderSide(color: Color(0xFF114D8B), width: 2), // ), // padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), // ), // onPressed: () { // // PDF export logic // }, // child: Row( // children: [ // Text( // "PDF", // style: GoogleFonts.poppins( // fontSize: isDesktop ? 13 : 11, // color: Colors.white, // ), // ), // SizedBox(width: 8), // Icon(Icons.picture_as_pdf_outlined, size: 15, color: Colors.white), // ], // ), // ), // ], // ), // ], // ), // ), // Expanded( // child: SingleChildScrollView( // scrollDirection: Axis.horizontal, // child: SingleChildScrollView( // scrollDirection: Axis.vertical, // 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('Plan Id', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Employee Code', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('SO Number', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Department', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Flight Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Plan Trip Type', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // DataColumn(label: Text('Sector', style: GoogleFonts.poppins(fontSize: 12, fontWeight: FontWeight.w600))), // ], // rows: internationalData.map((entry) { // return DataRow(cells: [ // DataCell(Text('${entry['plan_id']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['employee_code']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['so_number']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['functional_department']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['flight_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['plan_trip_type']}', style: GoogleFonts.poppins(fontSize: 12))), // DataCell(Text('${entry['sector']}', style: GoogleFonts.poppins(fontSize: 12))), // ]); // }).toList(), // ), // ), // ), // ), Padding( padding: const EdgeInsets.symmetric( horizontal: 16.0, vertical: 8, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ /// Title on the left Expanded( child: Text( "International Report (From: ${textControllers["from_date"]?.text} To: ${textControllers["to_date"]?.text})", style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[700], ), overflow: TextOverflow.ellipsis, // Ensures title doesn't overflow ), ), SizedBox(width: 16), Container( width: MediaQuery.of(context).size.width * 0.2, height: 40, child: TextField( controller: searchInternationalController, onChanged: handleInternationalFilter, 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), ), ), ]) ), Container( height: isDesktop ? MediaQuery.of(context).size.height * 0.51 : 200, // color:Colors.amberAccent, child: LayoutBuilder( builder: (context, constraints) { final double minWidth = isDesktop ? constraints.maxWidth : 1300; List> newInternationalData =filteredInternational.isNotEmpty ? filteredInternational : internationalData; // Pagination logic List> paginatedInternational = newInternationalData .skip(currentPage2 * itemsPerPage2) .take(itemsPerPage2) .toList(); return ((searchInternationalController.text.isNotEmpty && filteredInternational.isEmpty) ? Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox(height: MediaQuery.of(context).size.height / 4), Text( "No Records Found", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.w500, color: Colors.black54, ), ), const SizedBox(height: 10), Text( "Please ensure the selected dates contain report data.", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.black54, ), ), ], ), ), ) : Column( children: [ Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: ConstrainedBox( constraints: BoxConstraints( minWidth: minWidth, ), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: DataTable( dividerThickness: 0.5, columnSpacing: isDesktop ? 30.0 : 24.0, border: TableBorder( horizontalInside: BorderSide( width: 0.5, color: Colors .grey .shade200, ), ), columns: [ DataColumn( label: Text( 'Service', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight .w600, ), ), ), DataColumn( label: Text( 'Count', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight .w600, ), ), ), DataColumn( label: Text( 'Plan Trip Type', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight .w600, ), ), ), ], rows: paginatedInternational.map(( entry, ) { return DataRow( cells: [ DataCell( Text( '${entry['Service']}', style: GoogleFonts.poppins( fontSize: 12, ), ), ), DataCell( Text( '${entry['service_count']}', style: GoogleFonts.poppins( fontSize: 12, ), ), ), DataCell( Text( '${entry['plan_trip_type']}', style: GoogleFonts.poppins( fontSize: 12, ), ), ), ], ); }).toList(), ), ), ), ), ), // Pagination controls PaginationControls( currentPage: currentPage2, itemsPerPage: itemsPerPage2, totalItems: newInternationalData.length, activeColor: layoutColor, onPageChanged: (page) { setState(() { currentPage2 = page; }); }, onItemsPerPageChanged: (items) { setState(() { itemsPerPage2 = items; currentPage2 = 0; }); }, ), ], )); }, ), ), ], ), ], ), ), ], ), ), // SizedBox(height: 15), ], ), ); } Widget buildTitle(bool isDesktop){ return // Left side: Breadcrumb inside a Container (optional) Container( child: BreadcrumbNavigation( isDesktop: isDesktop, breadcrumbItems: [ BreadcrumbItem( title: 'Report List', tooltip: 'Go To Report List', onTap: (context) { // Navigator.pushNamed(context, '/report'); context.go("/report"); } ), BreadcrumbItem( title: 'Service Analysis Report', ), ], ) ); } Widget buildExports(bool isDesktop){ return Row( children: [ ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: Color(0xFF114D8B), width: 2), ), padding: EdgeInsets.symmetric( horizontal: 20, vertical: 12, ), ), onPressed: handleDownload, child: Row( children: [ Text( "Excel", style: GoogleFonts.poppins( fontSize: isDesktop ? 13 : 11, color: Colors.white, ), ), SizedBox(width: 8), Icon( Icons.file_present_outlined, size: 15, color: Colors.white, ), ], ), ), // SizedBox(width: 10), // ElevatedButton( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF114D8B), // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // side: BorderSide(color: Color(0xFF114D8B), width: 2), // ), // padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), // ), // onPressed: () { // // Your PDF export logic here // }, // child: Row( // children: [ // Text( // "PDF", // style: GoogleFonts.poppins( // fontSize: isDesktop ? 13 : 11, // color: Colors.white, // ), // ), // SizedBox(width: 8), // Icon(Icons.picture_as_pdf_outlined, size: 15, color: Colors.white), // ], // ), // ), ], ); } Widget buildFromDateField(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "From Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["from_date"] ?? false, isDesktop: isDesktop, width: isDesktop ? null : double.infinity, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { final pickedDate = await showDatePicker( context: context, initialDate: DateTime.now(), initialEntryMode: DatePickerEntryMode.calendarOnly, firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (pickedDate != null) { textControllers["from_date"]?.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); } }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["from_date"], controller: textControllers["from_date"], readOnly: true, style: const TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Select Date", labelStyle: const TextStyle( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric( vertical: 16, ), suffixIcon: const Icon( Icons.calendar_today, size: 16, color: Colors.grey, ), ), ), ), ), ), ), if (errorMessages["from_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["from_date"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ); } Widget buildToDateField(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "To Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["to_date"] ?? false, isDesktop: isDesktop, width: isDesktop ? null : double.infinity, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { final fromDateText = textControllers["from_date"]?.text; DateTime? mintoDate; if (fromDateText != null && fromDateText.isNotEmpty) { mintoDate = DateFormat( 'dd-MM-yyyy', ).parse(fromDateText); } final pickedDate = await showDatePicker( context: context, initialDate: mintoDate ?? DateTime.now(), initialEntryMode: DatePickerEntryMode.calendarOnly, firstDate: mintoDate ?? DateTime(2000), lastDate: DateTime(2100), ); if (pickedDate != null) { textControllers["to_date"]?.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); } }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["to_date"], controller: textControllers["to_date"], readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", labelStyle: TextStyle( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric( vertical: 16, ), suffixIcon: Icon( Icons.calendar_today, size: 16, color: Colors.grey, ), ), ), ), ), ), ), if (errorMessages["to_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["to_date"]!, style: const TextStyle(color: Colors.red, fontSize: 12), maxLines: 2, // Allow it to wrap onto two lines overflow: TextOverflow .ellipsis, // Add ellipsis if it still overflows ), ], ], ); } Widget buildSearchButton(bool isDesktop) { return Padding( padding: isDesktop ? const EdgeInsets.only(top: 22) : EdgeInsets.zero, child: SizedBox( width: isDesktop ? null : double.infinity, child: 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: () { handleSubmit(); }, child: Row( mainAxisSize: MainAxisSize.min, // Ensures content fits nicely children: [ Text( "Search", style: GoogleFonts.poppins( fontSize: isDesktop ? 13 : 11, ), ), SizedBox(width: 8), // spacing between icon and text Icon(Icons.search, size: 15, color: Colors.white), ], ), ), ), ); } }