FIX_Minor Issues Fixes and Excel api changes in polict report and Audit History Modal

This commit is contained in:
sanjeev.p 2025-12-22 09:34:39 +05:30
parent 4dd43e1ed1
commit a52b8d6c04
3 changed files with 431 additions and 42 deletions

View File

@ -1971,4 +1971,117 @@ class ApiService {
final response = await _makeGetRequest(url, headers);
return response;
}
Future<void> 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<Map<String, dynamic>> 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<Map<String, dynamic>> 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;
// }
}

View File

@ -227,7 +227,8 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
// 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<EnquiryListStaffInline> {
getInsuranceType();
}
// Future<void> 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<void> 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<EnquiryListStaffInline> {
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<EnquiryListStaffInline> {
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),

View File

@ -39,6 +39,8 @@ class policylistState extends ConsumerState<policylist> {
List<Map<String, dynamic>> getStaffData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
List<Map<String, dynamic>> excelResponseData = [];
List<Map<String, dynamic>> auditHistoryResponseData = [];
bool isLoading = false;
dynamic roleId;
final _formKey = GlobalKey<FormState>();
@ -292,6 +294,34 @@ class policylistState extends ConsumerState<policylist> {
}
}
Future<void> 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<policylist> {
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<policylist> {
),
),
),
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<policylist> {
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<AuditHistoryModal> createState() => AuditHistoryModalState();
}
class AuditHistoryModalState extends ConsumerState<AuditHistoryModal> {
late ApiService apiService;
bool isLoading = true; // Start with loading true
List<Map<String, dynamic>> getAuditHistoryData = [];
@override
void initState() {
super.initState();
apiService = ApiService();
// Fetch the audit history as soon as the modal opens
fetchAuditHistory();
}
Future<void> 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<Map<String, dynamic>>.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
}
}