import 'dart:convert'; import 'package:csv/csv.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:universal_html/html.dart' as html; import '../../presentation/cdTransactionDetails.dart'; import '../api_service.dart'; import 'package:collection/collection.dart'; import '../token_storage_service.dart'; class CdPolicies extends StatefulWidget { final String empClientId; final String empClientBranchId; final String empHrId; final String postToken; const CdPolicies( {Key? key, required this.empClientId, required this.empClientBranchId, required this.empHrId, required this.postToken}); @override State createState() => _CdPolicieState(); } class _CdPolicieState extends State { final tokenService = TokenStorageService(); Uint8List? fileBytes; List> getCDPolicies = []; bool isLoading = false; bool _isLoading = false; // dynamic clintID; late TabController _tabController; // List dataPolicy = []; List reversedDataPolicy = []; List> originalData = []; // Original data source List> filteredData = []; // Filtered data source // List> filteredData = [ // { // "client_id": "58", // "insurer_id": "2", // "cd_ac_pk": "94", // "insurer_name": "ICICI Prudential Life Insurance Co. Ltd", // "cd_master_account_no": "CD-IOCL-887744559966", // "balance": "10000000.00" // } // ]; // Filtered data source dynamic argumentsData; dynamic policyType; dynamic policyName; dynamic clientPolicyId; dynamic clientId; dynamic empRefId; int inceptionType = 0; TextEditingController searchController = TextEditingController(); late ApiService apiService; int _currentPage = 1; int _rowsPerPage = 5; List get _paginatedData { final startIndex = (_currentPage - 1) * _rowsPerPage; final endIndex = (_currentPage * _rowsPerPage).clamp(0, filteredData.length); return filteredData.sublist(startIndex, endIndex); } @override void initState() { super.initState(); apiService = ApiService(context); // Initialize ApiService here getCDPoliciesDetails(); } @override void dispose() { super.dispose(); } Future getCDPoliciesDetails() async { print('9'); setState(() { isLoading = true; }); try { print('10'); final response = await apiService.getCDPoliciesToApi( widget.empClientId, widget.empHrId, widget.postToken); if (response['status'] == 'success') { setState(() { isLoading = false; }); setState(() { getCDPolicies = List>.from(response['data']); originalData = getCDPolicies; filteredData = List.from(originalData); print('filteredData'); print(filteredData); }); } else { setState(() { isLoading = false; }); // ToastHelper.showWarningToast( // context, 'Request failed with status: ${response.statusCode}'); print('Request failed with status: ${response['code']}'); } } catch (e) { setState(() { isLoading = false; }); print('Exception occurred: $e'); } finally { setState(() { _isLoading = false; }); } } void search(String query) { print(query); // Check if the query is empty if (query.isEmpty) { // If search query is empty, show all data setState(() { filteredData = List.from(originalData); }); } else { // Filter the original data based on the search query setState(() { filteredData = originalData.where((row) { // Implement your filter logic here // For example, check if any field in the row contains the query // Adjust this logic based on your data structure return row['insurer_name'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['cd_master_account_no'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['balance'] .toString() .toLowerCase() .contains(query.toLowerCase()); }).toList(); }); } print(filteredData.length); } void exportToCsv(List> data) { List> rows = []; // Header rows.add(['Insurer Name', 'CD Account Number', 'Current Balance']); // Data rows for (var item in data) { rows.add([ item['insurer_name'] ?? '', item['cd_master_account_no'] ?? '', '₹${item['balance'] ?? '0'}', ]); } // Convert to CSV string String csvData = const ListToCsvConverter().convert(rows); // For Web: Create download final bytes = utf8.encode(csvData); final blob = html.Blob([bytes]); final url = html.Url.createObjectUrlFromBlob(blob); final anchor = html.AnchorElement(href: url) ..setAttribute("download", "CD_Policies.csv") ..click(); html.Url.revokeObjectUrl(url); handleExportAction(); } Future handleExportAction() async { print('handleExportAction'); final postId = await tokenService.readValue('empHrId'); final preId = await tokenService.readValue('enrollmentEmpPrimaryId'); var activity = "export_cddata"; print('postId - $postId'); print('preId - $preId'); print('activity - $activity'); try { print('10'); final response = await apiService.getPostLogHrActivity( postId!, preId!, widget.postToken, activity); if (response['status'] == 'success') { print('Request success'); } else { // ToastHelper.showWarningToast( // context, 'Request failed with status: ${response.statusCode}'); print('Request failed with status: ${response['code']}'); } } catch (e) { print('Exception occurred: $e'); } } @override Widget build(BuildContext context) { // TODO: implement build return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), // 👈 set your desired radius ), // height: 400, padding: const EdgeInsets.all(16.0), child: Column( children: [ Row( children: [ Expanded( flex: 4, child: Align( alignment: Alignment.centerLeft, child: Container( width: 400, height: 37, decoration: BoxDecoration( color: Color(0xFFF0F0F0), borderRadius: BorderRadius.circular(8), ), child: TextField( decoration: InputDecoration( hintText: 'Search', prefixIcon: Icon(Icons.search, size: 18), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), border: InputBorder .none, // No border since Container handles it ), controller: searchController, onChanged: search, style: GoogleFonts.poppins(fontSize: 14), ), ), ), ), SizedBox(width: 12), Expanded( flex: 2, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ SizedBox( width: 116, height: 37, child: ElevatedButton( onPressed: () { exportToCsv(filteredData); }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFFE26728), padding: EdgeInsets.all(10), // Internal padding shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), // Border radius side: BorderSide( color: Colors.transparent, // Optional border color width: 1, // Border width ), ), elevation: 0, ), child: Text( 'Export', style: GoogleFonts.poppins( fontSize: 16, color: Color(0xFFFFFFFF), fontWeight: FontWeight.w700, letterSpacing: 1), ), ), ), ], ), ), ], ), SizedBox(height: 20), isLoading ? Expanded( // color: Color(0x98FFFCE5), // semi-transparent overlay child: Center( child: Image.asset( 'assets/nhance-loader.gif', height: 60, width: 60, ), ), ) : Expanded( child: _buildCDDataTable(context), ) ], ), ); } Widget _buildCDDataTable(BuildContext context) { // Loader overlay if (filteredData.isEmpty) { return const SizedBox( height: 50, child: Center(child: Text('No CD Accout Mapped')), ); } return ListView.builder( itemCount: _paginatedData.length + 2, // +1 for header, +1 for pagination itemBuilder: (context, index) { if (index == 0) return _buildHeader(); if (index == _paginatedData.length + 1) return _buildPagination(context); final item = _paginatedData[index - 1]; return _buildDataRow(item); }, ); // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // // Header row // Container( // decoration: BoxDecoration( // color: Color(0xFF00A6A6), // borderRadius: BorderRadius.circular(6), // ), // padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), // child: Row( // children: [ // Expanded( // flex: 4, // child: Text( // 'Insurer Name', // style: GoogleFonts.poppins( // color: Colors.white, fontWeight: FontWeight.bold), // ), // ), // Expanded( // flex: 3, // child: Text( // 'CD Account number', // style: GoogleFonts.poppins( // color: Colors.white, fontWeight: FontWeight.bold), // ), // ), // Expanded( // flex: 2, // child: Text( // 'Current Balance', // style: GoogleFonts.poppins( // color: Colors.white, fontWeight: FontWeight.bold), // ), // ), // Expanded( // flex: 1, // child: Text( // 'Action', // textAlign: TextAlign.center, // style: GoogleFonts.poppins( // color: Colors.white, fontWeight: FontWeight.bold), // ), // ), // ], // ), // ), // // const SizedBox(height: 6), // // // Table body rows // ..._paginatedData.mapIndexed((index, item) { // return Container( // margin: const EdgeInsets.only(bottom: 8), // padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 16), // decoration: BoxDecoration( // color: index % 2 == 0 ? Color(0xFFE6FAFB) : Colors.white, // borderRadius: BorderRadius.circular(6), // ), // child: Row( // children: [ // Expanded( // flex: 4, // child: Text( // item['insurer_name'] ?? '-', // style: GoogleFonts.poppins(color: Color(0xFF000000)), // ), // ), // Expanded( // flex: 3, // child: Text( // item['cd_master_account_no'] ?? '-', // style: GoogleFonts.poppins(color: Color(0xFF000000)), // ), // ), // Expanded( // flex: 2, // child: Text( // "₹${item['balance'] ?? '0'}", // style: GoogleFonts.poppins(color: Color(0xFF000000)), // ), // ), // Expanded( // flex: 1, // child: Center( // child: IconButton( // icon: const Icon(Icons.remove_red_eye_outlined), // onPressed: () { // Navigator.push( // context, // MaterialPageRoute( // builder: (context) => cdTransactionDetails( // insurerName: item['insurer_name'], // cdMasterAccountNo: item['cd_master_account_no'], // insurerId: item['insurer_id'], // cd_ac_pk: item['cd_ac_pk'], // empClientId: widget.empClientId, // postToken: widget.postToken), // ), // ); // }, // ), // ), // ), // ], // ), // ); // }).toList(), // // // ], // ); } Widget _buildDataRow(Map item) { return Container( padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), margin: const EdgeInsets.only(top: 10), decoration: BoxDecoration( color: Color(0xFFE0F7F9), border: const Border( bottom: BorderSide(color: Color(0xFFD7E9EB), width: 1), ), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Expanded( flex: 4, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(item['insurer_name'] ?? '-', style: _dataBold), ], ), ), Expanded( flex: 3, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(item['cd_master_account_no'] ?? '-', style: _dataBold), ], ), ), Expanded( flex: 2, child: Text("₹${item['balance'] ?? '0'}", style: _dataBold), ), Expanded( flex: 1, child: Center( child: IconButton( icon: const Icon(Icons.remove_red_eye_outlined), onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => cdTransactionDetails( insurerName: item['insurer_name'], cdMasterAccountNo: item['cd_master_account_no'], insurerId: item['insurer_id'], cd_ac_pk: item['cd_ac_pk'], empClientId: widget.empClientId), ), ); }, ), ), ), ], ), ); } Widget _buildHeader() { return Container( decoration: BoxDecoration( color: Color(0xFF00A6A6), borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: const Row( children: [ Expanded(flex: 4, child: Text('Insurer Name', style: _headerStyle)), Expanded( flex: 3, child: Text('CD Account Number', style: _headerStyle)), Expanded( flex: 2, child: Text('Current Balance', style: _headerStyle)), Expanded(flex: 1, child: Text('Actions', style: _headerStyle)), ], ), ); } static final _dataBold = GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Color(0xFF000000), ); static final _dataSub = GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w300, color: Color(0xFF585757), ); static const _headerStyle = TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ); Widget _buildPagination(BuildContext context) { final totalPages = (filteredData.length / _rowsPerPage).ceil(); const visiblePageCount = 5; List getVisiblePages() { if (totalPages <= visiblePageCount) { return List.generate(totalPages, (i) => i + 1); } if (_currentPage <= 3) { return [1, 2, 3, 4, 5]; } else if (_currentPage >= totalPages - 2) { return [ totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages ]; } else { return [ _currentPage - 2, _currentPage - 1, _currentPage, _currentPage + 1, _currentPage + 2, ]; } } List visiblePages = getVisiblePages(); return Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Row( children: [ // Dropdown for rows per page DropdownButton( value: _rowsPerPage, items: [5, 10, 15, 20, 50].map((int value) { return DropdownMenuItem( value: value, child: Text(' $value ', style: GoogleFonts.poppins(fontSize: 15)), ); }).toList(), onChanged: (newValue) { setState(() { _rowsPerPage = newValue!; _currentPage = 1; }); }, ), // Previous button IconButton( onPressed: _currentPage > 1 ? () => setState(() => _currentPage--) : null, icon: const Icon(Icons.chevron_left), ), // First page + left ellipsis if (!visiblePages.contains(1)) Row(children: [ _buildPageButton(1), const Padding( padding: EdgeInsets.symmetric(horizontal: 4), child: Text("..."), ), ]), // Visible page buttons for (int page in visiblePages) _buildPageButton(page), // Right ellipsis + last page if (!visiblePages.contains(totalPages)) Row(children: [ const Padding( padding: EdgeInsets.symmetric(horizontal: 4), child: Text("..."), ), _buildPageButton(totalPages), ]), // Next button IconButton( onPressed: _currentPage < totalPages ? () => setState(() => _currentPage++) : null, icon: const Icon(Icons.chevron_right), ), ], ), ), ], ); } Widget _buildPageButton(int page) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: _currentPage == page ? const Color(0xFF00A6A6) : Colors.grey[300], foregroundColor: _currentPage == page ? Colors.white : Colors.black, minimumSize: const Size(36, 36), padding: EdgeInsets.zero, ), onPressed: () { setState(() { _currentPage = page; }); }, child: Text(page.toString()), ), ); } } class _DependenceDataSource0 extends DataTableSource { final List> _data; final BuildContext context; _DependenceDataSource0(this._data, this.context); @override DataRow getRow(int index) { // Calculate real row index (even: actual data, odd: spacer) final realIndex = index ~/ 2; // Add spacing after each row if (index.isOdd) { return DataRow( color: MaterialStateProperty.all(Colors.transparent), cells: [ DataCell(SizedBox(height: 10)), // empty spacer cell DataCell(SizedBox(height: 10)), DataCell(SizedBox(height: 10)), ], ); } final row = _data[realIndex]; return DataRow( color: MaterialStateColor.resolveWith((states) => Color(0xFFE0F7F9)), cells: [ DataCell(Text(row['insurer_name'] ?? '-')), DataCell(Text(row['cd_master_account_no'] ?? '-')), DataCell(Text('₹${row['balance'] ?? '0'}')), ], ); } @override bool get isRowCountApproximate => false; @override int get rowCount => _data.length * 2; // double the rows for gaps @override int get selectedRowCount => 0; } // Sample Data class representing each element in the array class Data { final dynamic value; final int row; final int column; final String sheet; Data(this.value, this.row, this.column, this.sheet); }