import 'dart:convert'; import 'package:csv/csv.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:nhancepolicy/presentation/policies.dart'; import 'package:universal_html/html.dart' as html; import '../service/secure_pop_scope.dart'; import 'cdTransactionDetails.dart'; import 'package:collection/collection.dart'; import '../customAppBar/base_layout.dart'; import '../service/api_service.dart'; import '../service/token_storage_service.dart'; import 'package:nhancepolicy/logger.dart'; class CdPoliciesList extends StatefulWidget { const CdPoliciesList({Key? key}) : super(key: key); @override State createState() => _CdPoliciesListState(); } class _CdPoliciesListState extends State { final tokenService = TokenStorageService(); Uint8List? fileBytes; List> getCDPolicies = []; bool isLoading = false; bool _isLoading = false; dynamic empClientId; dynamic empClientBranchId; dynamic empHrId; String? _postPreToken = ''; List reversedDataPolicy = []; List> originalData = []; // Original data source List> filteredData = []; // 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 checkIds(); } @override void dispose() { super.dispose(); } Future checkIds() async { logDebug('checkIds'); _postPreToken = await tokenService.getCurrentToken(); empClientId = await tokenService.readValue('empClientId'); // empClientBranchId = await tokenService.readValue('empClientBranchId'); empHrId = await tokenService.readValue('empHrId'); logDebug('$_postPreToken - $empClientId - $empHrId'); await getCDPoliciesDetails(empClientId, empHrId, _postPreToken); } Future getCDPoliciesDetails(empClientId, empHrId, _postPreToken) async { logDebug('9'); setState(() { isLoading = true; }); try { logDebug('10'); final response = await apiService.getCDPoliciesToApi( empClientId, empHrId, _postPreToken); if (response['status'] == 'success') { setState(() { isLoading = false; }); setState(() { getCDPolicies = List>.from(response['data']); originalData = getCDPolicies; filteredData = List.from(originalData); logDebug('filteredData'); logDebug(filteredData); }); } else { setState(() { isLoading = false; }); // ToastHelper.showWarningToast( // context, 'Request failed with status: ${response.statusCode}'); logDebug('Request failed with status: ${response['code']}'); } } catch (e) { setState(() { isLoading = false; }); logDebug('Exception occurred: $e'); } finally { setState(() { _isLoading = false; }); } } void search(String query) { logDebug(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(); }); } logDebug(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 { logDebug('handleExportAction'); _postPreToken = await tokenService.getCurrentToken(); final postId = await tokenService.readValue('empHrId'); final preId = await tokenService.readValue('enrollmentEmpPrimaryId'); var activity = "export_cddata"; logDebug('postId - $postId'); logDebug('preId - $preId'); logDebug('activity - $activity'); try { logDebug('10'); final response = await apiService.getPostLogHrActivity( postId!, preId!, _postPreToken!, activity); if (response['status'] == 'success') { logDebug('Request success'); } else { // ToastHelper.showWarningToast( // context, 'Request failed with status: ${response.statusCode}'); logDebug('Request failed with status: ${response['code']}'); } } catch (e) { logDebug('Exception occurred: $e'); } } Future _showLogoutDialog() async { return await showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: Text("Confirm Logout"), content: Text("Do you want to logout?"), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text("Cancel"), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: Text("Logout"), ), ], ), ) ?? false; } @override Widget build(BuildContext context) { return BaseLayout( child: SecurePopScope( child: _buildContent(context), ), ); } Widget _buildContent(BuildContext context) { 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( crossAxisAlignment: CrossAxisAlignment.center, children: [ /// 🔙 Back + Title (LEFT) Row( children: [ // IconButton( // onPressed: () => {}, // icon: const Icon( // Icons.arrow_back_ios, // size: 18, // color: Colors.black, // ), // padding: EdgeInsets.zero, // constraints: const BoxConstraints(), // ), const SizedBox(width: 6), Text( 'CD', style: GoogleFonts.poppins( fontSize: 22, fontWeight: FontWeight.w500, color: Colors.black, ), ), ], ), /// Push right content to end const Spacer(), /// 🔍 Search Box Container( width: 380, height: 37, decoration: BoxDecoration( color: const Color(0xFFF0F0F0), borderRadius: BorderRadius.circular(8), ), child: TextField( controller: searchController, onChanged: search, style: GoogleFonts.poppins(fontSize: 14), decoration: const InputDecoration( hintText: 'Search', prefixIcon: Icon(Icons.search, size: 18), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), ), ), const SizedBox(width: 12), /// ⬇️ Export Button SizedBox( width: 116, height: 37, child: ElevatedButton( onPressed: () => exportToCsv(filteredData), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFE26728), elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), child: Text( 'Export', style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.w700, color: Colors.white, 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: _buildCDGrid(), ) ], ), ); } Widget _buildCDGrid() { if (filteredData.isEmpty) { return const Center( child: Text('No CD Account Mapped'), ); } ResponsiveGridConfig _getGridConfig( BuildContext context, bool isEnrollment, ) { final width = MediaQuery.of(context).size.width; if (width < 600) { return ResponsiveGridConfig(1, 3.8); } else if (width < 900) { return ResponsiveGridConfig(2, 3.8); } else if (width < 1400) { return ResponsiveGridConfig(3, 4.2); } else { return ResponsiveGridConfig(4, 3.8); } } final config = _getGridConfig(context, true); return GridView.builder( itemCount: filteredData.length, physics: const BouncingScrollPhysics(), padding: EdgeInsets.zero, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: config.crossAxisCount, childAspectRatio: config.childAspectRatio, crossAxisSpacing: 16, mainAxisSpacing: 16, ), itemBuilder: (context, index) { return InkWell( borderRadius: BorderRadius.circular(12), onTap: () async { await tokenService.writeValue( 'hr_cd_insurer_name', filteredData[index]['insurer_name'].toString(), ); await tokenService.writeValue( 'hr_cd_master_account_no', filteredData[index]['cd_master_account_no'].toString(), ); await tokenService.writeValue( 'hr_cd_insurer_id', filteredData[index]['insurer_id'].toString(), ); await tokenService.writeValue( 'hr_cd_ac_pk', filteredData[index]['cd_ac_pk'].toString(), ); await tokenService.writeValue( 'hr_empClientId', empClientId.toString(), ); Navigator.push( context, MaterialPageRoute( settings: const RouteSettings(name: 'cdTransactionDetails'), builder: (_) => cdTransactionDetails( insurerName: filteredData[index]['insurer_name'], cdMasterAccountNo: filteredData[index] ['cd_master_account_no'], insurerId: filteredData[index]['insurer_id'], cd_ac_pk: filteredData[index]['cd_ac_pk'], empClientId: empClientId, ), ), ); }, child: _CDPolicyCard(data: filteredData[index]), ); }, ); } } class _CDPolicyCard extends StatelessWidget { final Map data; const _CDPolicyCard({required this.data}); @override Widget build(BuildContext context) { final balance = double.tryParse(data['balance'].toString()) ?? 0; final Color amountColor = balance < 0 ? Colors.red : balance < 50000 ? Colors.orange : Colors.green; return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFFEAFAFA), borderRadius: BorderRadius.circular(12), border: Border.all(color: const Color(0xFFA0D1D3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 7, child: Text( data['insurer_name'] ?? '', maxLines: 2, overflow: TextOverflow.ellipsis, style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF000000)), ), ), Expanded( flex: 3, child: Text( data['balance'] ?? '', // "₹${balance.toStringAsFixed(0)}", textAlign: TextAlign.right, style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w600, color: amountColor, ), ), ) ], ), /// Top row (amount + arrow) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ /// Account number Expanded( flex: 8, child: Text( 'CD No: ${data['cd_master_account_no']}', maxLines: 1, overflow: TextOverflow.ellipsis, style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w400, color: Color(0xFF000000), ), ), ), Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: const Color(0xFF009195), borderRadius: BorderRadius.circular(6), ), child: const Icon( Icons.open_in_new, color: Colors.white, size: 14, ), ) ], ), ], ), ); } }