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:intl/intl.dart'; import 'package:nhancepolicy/service/api_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:universal_html/html.dart' as html; import 'package:collection/collection.dart'; import 'customAppBar/customAppBar.dart'; import 'customAppBar/customFooter.dart'; class cdTransactionDetails extends StatefulWidget { final String insurerName; final String cdMasterAccountNo; final String insurerId; final String cd_ac_pk; final String empClientId; final String postToken; const cdTransactionDetails( {Key? key, required this.insurerName, required this.cdMasterAccountNo, required this.insurerId, required this.cd_ac_pk, required this.empClientId, required this.postToken}); @override State createState() => _cdTransactionDetailsState(); } class _cdTransactionDetailsState extends State { Uint8List? fileBytes; List> getCDTransData = []; 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> getCDTransDataAmount = []; // Filtered data source dynamic argumentsData; dynamic policyType; dynamic policyName; dynamic clientPolicyId; dynamic clientId; dynamic empRefId; dynamic total_deposit; dynamic total_consumed; dynamic total_refund; dynamic currect_balance; 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 getCdTransactionDetails(); } @override void dispose() { super.dispose(); } Future getCdTransactionDetails() async { print('9'); setState(() { isLoading = true; }); try { print('10'); final response = await apiService.getCdTransactionData(widget.empClientId, widget.insurerId, widget.cd_ac_pk, widget.postToken); if (response['status'] == 'success') { setState(() { isLoading = false; }); setState(() { getCDTransData = List>.from(response['data']['deposit_data']); originalData = getCDTransData; filteredData = List.from(originalData); total_deposit = response['data']['total_deposit']; total_consumed = response['data']['total_consumed']; total_refund = response['data']['total_refund']; currect_balance = response['data']['currect_balance']; 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['created_at'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['record_date'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['unit'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['policy_type'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['policy_no'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['endorsement_no'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['sub_type'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['transaction_type'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['amount'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['balance'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['description'] .toString() .toLowerCase() .contains(query.toLowerCase()) || row['username'] .toString() .toLowerCase() .contains(query.toLowerCase()); }).toList(); }); } print(filteredData.length); } void exportToCsv(List> data) { List> rows = []; // Header rows.add([ 'Date', 'Record Date', 'Unit', 'Policy', 'Endorsement No', 'Sub Type', 'Credit', 'Debit', 'Balance', 'Description', 'User' ]); // Data rows for (var item in data) { rows.add([ item['created_at'] ?? '', item['record_date'] ?? '', item['unit'] ?? '', item['policy_type'] != null && item['policy_type'].toString().trim().isNotEmpty && item['policy_no'] != null && item['policy_no'].toString().trim().isNotEmpty ? '${item['policy_type']} - ${item['policy_no']}' : '-', item['endorsement_no'] ?? '', item['sub_type'] ?? '', item['transaction_type'] == 'Credit' ? '₹${item['amount']}' : '-', item['transaction_type'] == 'Debit' ? '₹${item['amount']}' : '-', '₹${item['balance'] ?? '0'}', item['description'] ?? '', item['username'] ?? '', ]); } // 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 SharedPreferences prefs = await SharedPreferences.getInstance(); final postId = prefs.getString('empHrId'); final preId = prefs.getString('enrollmentEmpPrimaryId'); var activity = "export_cdsummary"; 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'); } } String formatDate(String? dateString) { if (dateString == null || dateString.isEmpty) return '-'; try { DateTime parsedDate = DateTime.parse(dateString); return DateFormat('dd-MM-yyyy hh:mm a').format(parsedDate); } catch (e) { return '-'; } } String formatDateNextLine(String? dateString) { if (dateString == null || dateString.isEmpty) return '-'; try { DateTime parsedDate = DateTime.parse(dateString); String date = DateFormat('dd-MM-yyyy').format(parsedDate); String time = DateFormat('hh:mm a').format(parsedDate); return '$date\n$time'; } catch (e) { return '-'; } } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: CustomAppBar(), backgroundColor: Color(0xFFEFF3F6), body: Stack(children: [ SingleChildScrollView( child: Container( padding: EdgeInsets.only( top: 30, bottom: 200, left: 50, right: 50), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MouseRegion( cursor: SystemMouseCursors .click, // 👈 shows pointer on hover child: GestureDetector( onTap: () { Navigator.pop(context); // or your desired action }, child: Row( children: [ Icon(Icons.arrow_back_ios_new_outlined, color: Color(0xFF707070)), SizedBox(width: 8), Expanded( child: Text( 'Transaction Details - ${widget.insurerName} (${widget.cdMasterAccountNo})', style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF101010), ), ), ), ], ), ), ), SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildInfoCard('Deposits', '₹$total_deposit', Color(0xFF39D45B), Icons.arrow_upward), _buildInfoCard('Consumed', '₹$total_consumed', Color(0xFFED1D24), Icons.arrow_downward), _buildInfoCard('Refund', '₹$total_refund', Color(0xFF39D45B), Icons.arrow_upward), _buildInfoCard('Current Balance', '₹$currect_balance', Colors.black, null), ], ), SizedBox(height: 16), 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: 14, color: Color(0xFFFFFFFF), fontWeight: FontWeight.w700, letterSpacing: 1), ), ), ), ], ), ), ], ), SizedBox(height: 20), Row( children: [ Expanded( child: SingleChildScrollView( child: _buildCDDataTable(context), ), ) ], ), ], ), ) ], ))), if (isLoading) Container( color: Color(0x98FFFCE5), // Semi-transparent background child: Center( child: // Your GIF loader widget Image.asset( height: 60, width: 60, 'assets/nhance-loader.gif'), // Adjust path to your GIF loader ), ), Align( alignment: Alignment.bottomCenter, child: Container( width: double.infinity, // Make the footer full width child: CustomFooter(), ), ), ])); } Widget _buildInfoCard( String label, String value, Color iconColor, IconData? icon) { return Expanded( child: Container( margin: EdgeInsets.symmetric(horizontal: 4), padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 2, offset: Offset(0, 1), ), ], ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, // label left, value right children: [ Text( label, style: GoogleFonts.poppins( color: Color(0xFF737373), fontSize: 14, fontWeight: FontWeight.w400), textAlign: TextAlign.left, ), Row( children: [ if (icon != null) Icon(icon, size: 20, color: iconColor), if (icon != null) SizedBox(width: 4), Text( value, style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xFF000000), ), ), ], ), ], ), ], ), ), ); } Widget _buildCDDataTable(BuildContext context) { if (filteredData.isEmpty) { return const SizedBox( height: 50, child: Center(child: Text('No available data')), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header row Container( decoration: BoxDecoration( color: Color(0xFFD7E9EB), borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: Row( children: [ Expanded( flex: 2, child: Text( 'Date', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( 'Record Date', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 4, child: Text( 'Unit', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 3, child: Text( 'Policy', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 3, child: Text( 'Endorsement No', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( 'Sub Type', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( 'Credit', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( 'Debit', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( 'Balance', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 3, child: Text( 'Description', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( 'User', textAlign: TextAlign.left, style: GoogleFonts.poppins( fontSize: 12, color: Color(0xFF000000), 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, color: Colors.white, borderRadius: BorderRadius.circular(6), border: Border( bottom: BorderSide( color: Color(0xFFD7E9EB), // 👈 Bottom border color width: 1, // 👈 Optional: thickness ), ), ), child: Row( children: [ Expanded( flex: 2, child: Text( formatDateNextLine(item['created_at']), textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 2, child: Text( item['record_date'] ?? '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 4, child: Text( // 'The Kancheepuram District Consumers Operative Wholesale Stores Limited-5526', item['unit'] ?? '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 3, child: Text( // 'Motor - 3001/379707905/00/000', item['policy_type'] != null && item['policy_type'].toString().trim().isNotEmpty && item['policy_no'] != null && item['policy_no'].toString().trim().isNotEmpty // ? '${item['policy_no']}' ? '${item['policy_type']} - ${item['policy_no']}' : '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 3, child: Text( item['endorsement_no'] ?? '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 2, child: Text( item['sub_type'] ?? '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 2, child: Text( item['transaction_type'] == 'Credit' ? '₹${item['amount']}' : '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 2, child: Text( item['transaction_type'] == 'Debit' ? '₹${item['amount']}' : '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 2, child: Text( "₹${item['balance'] ?? '-'}", textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 3, child: Text( item['description'] ?? '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), Expanded( flex: 2, child: Text( item['username'] ?? '-', textAlign: TextAlign.left, style: GoogleFonts.poppins( color: Color(0xFF000000), fontSize: 12, ), ), ), ], ), ); }).toList(), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ 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; }); }, ), IconButton( onPressed: _currentPage > 1 ? () { setState(() { _currentPage--; }); } : null, icon: Icon(Icons.chevron_left), ), for (int i = 1; i <= (filteredData.length / _rowsPerPage).ceil(); i++) Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: _currentPage == i ? Color(0xFF00A6A6) : Colors.grey[300], foregroundColor: _currentPage == i ? Colors.white : Colors.black, minimumSize: Size(36, 36), padding: EdgeInsets.zero, ), onPressed: () { setState(() { _currentPage = i; }); }, child: Text(i.toString()), ), ), IconButton( onPressed: _currentPage < (filteredData.length / _rowsPerPage).ceil() ? () { setState(() { _currentPage++; }); } : null, icon: Icon(Icons.chevron_right), ), ], ), ), ], ), ], ); } } // 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); }