diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index d9ff488..3ee5dd1 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -1971,4 +1971,117 @@ class ApiService { final response = await _makeGetRequest(url, headers); return response; } + + + Future generatePolicyExcel(managerId,fromDate,toDate) async { + + final url = Uri.parse('${Env.apiUrl}reports/policy-excel?manager_id=$managerId&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}',); + + print('getPdfDownload policy-excel - $url'); + await _initializeToken(); + + if (_token == null) throw Exception('Token not found. Please log in.'); + + final headers = { + 'Authorization': 'Bearer $_token', + // 'App-Signature': Env.App_Signature, + 'app-signature': Env.App_Signature, + }; + + final response = await _makeGethttpRequest(url, headers); + + if (response.statusCode == 200) { + final contentType = response.headers['content-type'] ?? ''; + if (contentType.contains('application/json')) { + final jsonResponse = jsonDecode(response.body); + if (jsonResponse['code'] == 404 || jsonResponse['code'] == 500) { + ToastHelper.show('File Not Found', Colors.red); + } else { + ToastHelper.show('No File Found', Colors.red); + } + return; + } else { + final blob = html.Blob([response.bodyBytes]); + final blobUrl = html.Url.createObjectUrlFromBlob(blob); + print('getPdfDownload $blobUrl'); + final contentDisp = response.headers['content-disposition']; + print('getPdfDownloadcontentDisp $contentDisp'); + + String fileName = extractFileName(contentDisp, "policy_excel"); + print('Resolved fileName: $fileName'); + final anchor = html.AnchorElement(href: blobUrl) + ..setAttribute('download', fileName) + ..click(); + + html.Url.revokeObjectUrl(blobUrl); + } + } else if (response.statusCode == 404) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('File not found.'), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ); + } else if (response.statusCode == 403) { + await clearLocalStorageAndRedirect(); + } else if (response.statusCode == 500) { + ToastHelper.show('No File Found', Colors.red); + } else if (response.statusCode == 302) { + print('getPdfDownload 3-PP-302'); + ToastHelper.show('No File Found', Colors.red); + } else { + ToastHelper.show('No File Found', Colors.red); + throw Exception('Failed to download file'); + } + } + + Future> fetchAuditHistory(id) async { + + print( "fetchAuditHistory - $id " ); + + if (_token == null) { + await _initializeToken(); + } + // final url = Uri.parse('${Env.apiUrl}audit/history?table_name=partner_policy&pk=80',); + final url = Uri.parse('${Env.apiUrl}audit/history?table_name=partner_policy&pk=${id ?? ''}',); + + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'app-signature': Env.App_Signature, + }; + + final response = await _makeGetRequest(url, headers); + + return response; + } + + // Future> fetchPolicyExcel(managerId,fromDate,toDate) async { + // + // print( "fetchPolicyExcel - mangerID- $managerId - Dated - $fromDate - $toDate " ); + // + // if (_token == null) { + // await _initializeToken(); + // } + // + // final String query; + // + // query = 'manager_id=$managerId'; + // + // final url = Uri.parse('${Env.apiUrl}reports/policy-excel?$query&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}',); + // + // final headers = { + // 'Authorization': 'Bearer $_token' ?? '', + // 'app-signature': Env.App_Signature, + // }; + // + // final response = await _makeGetRequest(url, headers); + // + // return response; + // } } diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart index 105eeb0..c7a4ab3 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart @@ -227,7 +227,8 @@ class EnquiryStaffState extends ConsumerState { // late Timer timer; Timer? timer; int selectedSeconds = 30; - bool isActionable = false; + bool isActionable = false; // current UI state + bool userPreference = false; // memory of what the user chose bool _listened = false; //---------------------- Assign Staff Ends -------------------------------- // @@ -311,11 +312,25 @@ class EnquiryStaffState extends ConsumerState { getInsuranceType(); } + // Future loadPreferences() async { + // final prefs = await SharedPreferences.getInstance(); + // + // setState(() { + // isActionable = prefs.getBool('isActionable') ?? false; + // selectedSeconds = prefs.getInt('selectedSeconds') ?? 30; + // + // _showFilterRow = true; + // }); + // + // startTimer(); // restart timer using stored value + // } Future loadPreferences() async { final prefs = await SharedPreferences.getInstance(); setState(() { - isActionable = prefs.getBool('isActionable') ?? false; + bool savedValue = prefs.getBool('isActionable') ?? false; + isActionable = savedValue; + userPreference = savedValue; // Initialize the backup selectedSeconds = prefs.getInt('selectedSeconds') ?? 30; _showFilterRow = true; @@ -1719,8 +1734,10 @@ class EnquiryStaffState extends ConsumerState { value: !isActionable, onChanged: (v) async { bool newValue = !v; - setState(() => isActionable = newValue); - + setState(() { + isActionable = newValue; + userPreference = newValue; // Save this as the new "normal" + }); final prefs = await SharedPreferences.getInstance(); await prefs.setBool( @@ -1780,27 +1797,41 @@ class EnquiryStaffState extends ConsumerState { SizedBox(width: 10), MouseRegion( onEnter: (_) { - setState(() { - isActionable = true; - }); + // setState(() { + // isActionable = true; + // }); }, onExit: (_) { - setState(() { - // isActionable = false; - isActionable = _searchStaffController.text.isNotEmpty; - }); + // setState(() { + // // isActionable = false; + // isActionable = _searchStaffController.text.isNotEmpty; + // }); }, child: ThemedSearchField( hintText: 'Search', backgroundColor: Color(0xFFFFFFFF), txtHeight: 30, - // backgroundColor: Colors.white, - onChanged: filterData, - controller: _searchStaffController, txtwidth: ResponsiveLayout.isMobile(context) ? MediaQuery.of(context).size.width * 0.7 : MediaQuery.of(context).size.width * 0.13, + + // Combine the logic into one function block + onChanged: (value) { + // 1. Call your existing filter function + filterData(value); + + // 2. Perform the logic to toggle the switch UI + setState(() { + if (value.isNotEmpty) { + // CASE 1: Searching -> Force switch to "OFF" (isActionable = true) + isActionable = true; + } else { + // CASE 2: Search cleared -> Restore the status from memory + isActionable = userPreference; + } + }); + }, ), ), SizedBox(width: 10), diff --git a/lib/presentation/screens/staff/policy/policy_list.dart b/lib/presentation/screens/staff/policy/policy_list.dart index d4a71a8..20a201e 100644 --- a/lib/presentation/screens/staff/policy/policy_list.dart +++ b/lib/presentation/screens/staff/policy/policy_list.dart @@ -39,6 +39,8 @@ class policylistState extends ConsumerState { List> getStaffData = []; List> originalData = []; List> filteredData = []; + List> excelResponseData = []; + List> auditHistoryResponseData = []; bool isLoading = false; dynamic roleId; final _formKey = GlobalKey(); @@ -292,6 +294,34 @@ class policylistState extends ConsumerState { } } + Future fetchPolicyExcelReport() async { + + final String fromDate = controllers['startDate']!.text; // "11-01-2025" + final String toDate = controllers['endDate']!.text; // "20-01-2025" + final response = await apiService.generatePolicyExcel(managerId,fromDate,toDate); + // Map your controllers and IDs to the API parameters + // final String fromDate = controllers['startDate']!.text; // "11-01-2025" + // final String toDate = controllers['endDate']!.text; // "20-01-2025" + // print("fetchPolicyExcelReport function called here."); + // + // try { + // // Assuming you have a generic _makeGetRequest method + // final response = await apiService.fetchPolicyExcel(managerId,fromDate,toDate); + // + // if (response != null && response['status'] == 200) { + // setState(() { + // // Store the list of data in your variable + // excelResponseData = response['data']; + // }); + // print("Data fetched. Ready to export."); + // } + // } catch (e) { + // debugPrint("Export Error: $e"); + // print("Failed to fetch report data."); + // } + } + + @override Widget build(BuildContext context) { return MainLayout( @@ -465,35 +495,63 @@ class policylistState extends ConsumerState { ResponsiveLayout.isMobile(context) ? Spacer() : SizedBox(width: 10), - ExportBtn( - sheetName: "Policy", - fileName: "Policy_list", - data: filteredData, - txt: !ResponsiveLayout.isMobile(context) ? true : false, - displayHeaders: [ - // "Received Date & Time", - "Assigned To", - "Partner", - - "Insurer", - "Vehicle.No.", - "Insured Name", - "Premium", - "Payment Mode", - "Policy Number", - ], - keys: [ - // "updated_on", - "assigned_to_name", - "agent_name", - "insurer_short_name", - "reg_no", - "insured_name", - "premium_amount", - "payment_mode_value", - "policy_number", - ], + // ExportBtn( + // sheetName: "Policy", + // fileName: "Policy_list", + // data: filteredData, + // txt: !ResponsiveLayout.isMobile(context) ? true : false, + // displayHeaders: [ + // // "Received Date & Time", + // "Assigned To", + // "Partner", + // + // "Insurer", + // "Vehicle.No.", + // "Insured Name", + // "Premium", + // "Payment Mode", + // "Policy Number", + // ], + // keys: [ + // // "updated_on", + // "assigned_to_name", + // "agent_name", + // "insurer_short_name", + // "reg_no", + // "insured_name", + // "premium_amount", + // "payment_mode_value", + // "policy_number", + // ], + // ), + // SizedBox(width: 10), + Tooltip( + message: 'Export Excel', + child: InkWell( + onTap: () async { + await fetchPolicyExcelReport(); + }, + child: Container( + padding: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + color: const Color(0xFF2E7D6E), + // color: const Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/export.png", + height: 15, + width: 15, + ), + ], + ), + ), + ), ), + SizedBox(width: 10), ], ), ), @@ -763,6 +821,33 @@ class policylistState extends ConsumerState { ), ), ), + SizedBox(width: 10), + InkWell( + onTap: () { + showAuditHistoryModal(context,item['id']); + }, + child: Container( + padding: EdgeInsets.all(5.4), + decoration: BoxDecoration( + color: const Color(0xFF2E7D6E), + // color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Tooltip( + message: 'Audit History Information', + child: Icon( + Icons.file_open_outlined, + color: Colors.white, + size: 12, + ), + ), + ], + ), + ), + ), ], ), ), @@ -1039,3 +1124,163 @@ class policylistState extends ConsumerState { fontSize: 12, ); } + +void showAuditHistoryModal(BuildContext context, dynamic policyId) { + showDialog( + context: context, + builder: (context) => AuditHistoryModal(policyId: policyId), // Pass ID here + ); +} + +class AuditHistoryModal extends ConsumerStatefulWidget { + final dynamic policyId; // Add this + const AuditHistoryModal({super.key, required this.policyId}); // Update constructor + + @override + ConsumerState createState() => AuditHistoryModalState(); +} + +class AuditHistoryModalState extends ConsumerState { + late ApiService apiService; + bool isLoading = true; // Start with loading true + List> getAuditHistoryData = []; + + @override + void initState() { + super.initState(); + apiService = ApiService(); + + // Fetch the audit history as soon as the modal opens + fetchAuditHistory(); + } + + Future fetchAuditHistory() async { + setState(() => isLoading = true); + try { + // Use widget.policyId here! + final response = await apiService.fetchAuditHistory(widget.policyId.toString()); + + if (response != null && response['status'] == "success") { + setState(() { + getAuditHistoryData = List>.from(response['data']); + }); + } + } catch (e) { + debugPrint("Audit History Error: $e"); + } finally { + if (mounted) setState(() => isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: Colors.white, + content: Container( + width: MediaQuery.of(context).size.width * 0.55, + height: MediaQuery.of(context).size.height * 0.7, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + "Audit History", + style: GoogleFonts.poppins( + fontSize: 14, + color: Color(0xFF50A398), + fontWeight: FontWeight.w500, + ), + ), + Spacer(), + Tooltip( + message: 'Close', + child: IconButton( + icon: const Icon( + Icons.close, + size: 18, + // color: Color(0xFF425B5B), + color: Color(0xFF2E7D6E), + ), + onPressed: () { + Navigator.pop(context); + }, + splashRadius: 28, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(8), + constraints: const BoxConstraints(), + ), + ), + ], + ), + const SizedBox(height: 5), + Expanded( + child: isLoading + ? const Center(child: CircularProgressIndicator()) + : getAuditHistoryData.isEmpty + ? const Center(child: Text("No Audit History Data Found")) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Theme( + data: Theme.of(context).copyWith( + dividerColor: Colors.grey.shade200, + ), + child: DataTable( + headingRowColor: WidgetStateProperty.all(Color(0xFFF8FAFC)), + columnSpacing: 20, + columns: [ + DataColumn(label: Text("Column Name", style: _headerStyle())), + DataColumn(label: Text("Message", style: _headerStyle())), + DataColumn(label: Text("Changed On", style: _headerStyle())), + DataColumn(label: Text("Changed By", style: _headerStyle())), + ], + // Inside your build method's DataTable + rows: getAuditHistoryData.map((data) { + return DataRow(cells: [ + DataCell(Text(data['column_name'] ?? "-", style: _cellStyle())), + DataCell( + SizedBox( + width: 300, // Give message more width + child: Text( + data['message'] ?? "-", + style: _cellStyle(), + softWrap: true, // Ensure text wraps to next line + ), + ) + ), + DataCell(Text( + formatIndianDate(data['changed_on'] ?? ""), + style: _cellStyle(), + )), + DataCell(Text(data['changed_by'] ?? "-", style: _cellStyle())), + ]); + }).toList(), + ), + ), + ), + ), + + ], + ), + ), + ); + } +} + + // Helper styles to keep code clean + TextStyle _headerStyle() => GoogleFonts.poppins( + fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF475569)); + + TextStyle _cellStyle() => GoogleFonts.poppins( + fontSize: 11, color: Color(0xFF1E293B)); + + String formatIndianDate(String dateStr) { + try { + DateTime dateTime = DateTime.parse(dateStr); + // Format: Day-Month-Year Hour:Minute AM/PM + return DateFormat('dd-MM-yyyy hh:mm a').format(dateTime); + } catch (e) { + return dateStr; // Fallback to original string if parsing fails + } + }