FIX_1_BrokerName HightlightForPolicyVerifyScreen

This commit is contained in:
sanjeev.p 2026-01-31 17:22:28 +05:30
parent 2dd4e281d0
commit 1f34df0d2e
5 changed files with 671 additions and 49 deletions

File diff suppressed because one or more lines are too long

View File

@ -1420,12 +1420,12 @@ class ApiService {
query = 'agent_id=$id'; query = 'agent_id=$id';
} }
// final url = Uri.parse(
// '${Env.apiUrl}enquiry/enquiryList?$query&only_policy_data=true&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}&staff_id=$selectedStaffId',
// );
final url = Uri.parse( final url = Uri.parse(
'http://localhost/nhance_partner_be/enquiry/enquiryList?$query&only_policy_data=true&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}&staff_id=$selectedStaffId', '${Env.apiUrl}enquiry/enquiryList?$query&only_policy_data=true&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}&staff_id=$selectedStaffId',
); );
// final url = Uri.parse(
// 'http://localhost/nhance_partner_be/enquiry/enquiryList?$query&only_policy_data=true&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}&staff_id=$selectedStaffId',
// );
// if (role == 'manager') { // if (role == 'manager') {
// url = Uri.parse( // url = Uri.parse(
@ -2186,15 +2186,18 @@ class ApiService {
} }
} }
Future<Map<String, dynamic>> fetchAuditHistory(id) async { Future<Map<String, dynamic>> fetchAuditHistory(id,table) async {
print("fetchAuditHistory - $id "); print("fetchAuditHistory - $id ");
if (_token == null) { if (_token == null) {
await _initializeToken(); 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=80',);
// final url = Uri.parse(
// '${Env.apiUrl}audit/history?table_name=partner_policy&pk=${id ?? ''}',
// );
final url = Uri.parse( final url = Uri.parse(
'${Env.apiUrl}audit/history?table_name=partner_policy&pk=${id ?? ''}', '${Env.apiUrl}audit/history?table_name==${table ?? ''}&pk=${id ?? ''}',
); );
final headers = { final headers = {

View File

@ -861,7 +861,7 @@ class policylistState extends ConsumerState<policylist> {
SizedBox(width: 10), SizedBox(width: 10),
InkWell( InkWell(
onTap: () { onTap: () {
showAuditHistoryModal(context, item['id']); showAuditHistoryModal(context, item['policy_id']);
}, },
child: Container( child: Container(
padding: EdgeInsets.all(5.4), padding: EdgeInsets.all(5.4),
@ -1204,7 +1204,7 @@ class AuditHistoryModalState extends ConsumerState<AuditHistoryModal> {
try { try {
// Use widget.policyId here! // Use widget.policyId here!
final response = await apiService.fetchAuditHistory( final response = await apiService.fetchAuditHistory(
widget.policyId.toString(), widget.policyId.toString(),'partner_policy'
); );
if (response != null && response['status'] == "success") { if (response != null && response['status'] == "success") {

View File

@ -38,14 +38,24 @@ class _policyValidationState extends ConsumerState<policyValidation> {
// --- Api / state // --- Api / state
late ApiService apiService; late ApiService apiService;
bool isLoading = false; bool isLoading = false;
bool isLoadingFile = false;
bool isLoadingVehicleType = false; bool isLoadingVehicleType = false;
bool isLoadingVehicleTypeForCommission = false;
bool isLoadingFuelType = false; bool isLoadingFuelType = false;
bool isLoadingBroker = false;
bool isLoadingPaymentMode = false;
bool isLoadingInsurancePlan = false;
dynamic managerId; dynamic managerId;
dynamic userId; dynamic userId;
String? _cachedPdfUrl; String? _cachedPdfUrl;
String? pdfUrl; // full file path from API String? pdfUrl; // full file path from API
bool highlightEnqBroker = false;
bool highlightDocBroker = false;
String? enqBroker; // Stores the NAME of the enquiry broker
final ValueNotifier<bool> brokerMismatchNotifier = ValueNotifier<bool>(false);
// --- Controllers (all editable fields) // --- Controllers (all editable fields)
final List<String> controllerKeys = [ final List<String> controllerKeys = [
// Policy // Policy
@ -343,6 +353,17 @@ class _policyValidationState extends ConsumerState<policyValidation> {
}; };
update(); update();
_loadInitialData(); _loadInitialData();
// Set the initial enqBroker name from the fetched data list
if (selectedBroker != null) {
final item = filteredBrokerData.firstWhere(
(element) => element['id'].toString() == selectedBroker,
orElse: () => {},
);
enqBroker = item['name']?.toString();
}
// Check immediately after data is loaded
WidgetsBinding.instance.addPostFrameCallback((_) => _validateBrokerMatch());
} }
@ -394,7 +415,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
Future<void> getBroker() async { Future<void> getBroker() async {
print('getBroker called'); print('getBroker called');
setState(() { setState(() {
isLoading = true; isLoadingBroker = true;
}); });
try { try {
@ -417,7 +438,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
print('Exception occurred: $e'); print('Exception occurred: $e');
} finally { } finally {
setState(() { setState(() {
isLoading = false; isLoadingBroker = false;
}); });
} }
} }
@ -425,7 +446,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
Future<void> getInsurancPlanType() async { Future<void> getInsurancPlanType() async {
print('getInsurancPlanType called'); print('getInsurancPlanType called');
setState(() { setState(() {
isLoading = true; isLoadingInsurancePlan = true;
}); });
try { try {
@ -451,13 +472,13 @@ class _policyValidationState extends ConsumerState<policyValidation> {
print('Exception occurred: $e'); print('Exception occurred: $e');
} finally { } finally {
setState(() { setState(() {
isLoading = false; isLoadingInsurancePlan = false;
}); });
} }
} }
Future<void> getPaymentMode() async { Future<void> getPaymentMode() async {
setState(() => isLoading = true); setState(() => isLoadingPaymentMode = true);
try { try {
final response = await apiService.fetchMasterDropDown('PaymentMode'); final response = await apiService.fetchMasterDropDown('PaymentMode');
@ -479,22 +500,22 @@ class _policyValidationState extends ConsumerState<policyValidation> {
} }
}); });
} else { } else {
setState(() { // setState(() {
getPaymentModeData.clear(); getPaymentModeData.clear();
filteredPaymentModeData.clear(); filteredPaymentModeData.clear();
}); // });
} }
} catch (e) { } catch (e) {
print('Exception occurred: $e'); print('Exception occurred: $e');
} finally { } finally {
setState(() => isLoading = false); setState(() => isLoadingPaymentMode = false);
} }
} }
Future<void> getEnquiryVehicleTypeForCommission() async { Future<void> getEnquiryVehicleTypeForCommission() async {
setState(() => isLoading = true); setState(() => isLoadingVehicleTypeForCommission = true);
try { try {
final response = final response =
@ -528,7 +549,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
} catch (e) { } catch (e) {
print('Exception occurred: $e'); print('Exception occurred: $e');
} finally { } finally {
setState(() => isLoading = false); setState(() => isLoadingVehicleTypeForCommission = false);
} }
} }
@ -659,7 +680,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
} }
if (!mounted) return; if (!mounted) return;
setState(() => isLoading = true); setState(() => isLoadingFile = true);
try { try {
final url = Uri.parse( final url = Uri.parse(
@ -675,7 +696,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
print('Exception in getPolicyFilePath: $e\n$st'); print('Exception in getPolicyFilePath: $e\n$st');
} finally { } finally {
if (!mounted) return; if (!mounted) return;
setState(() => isLoading = false); setState(() => isLoadingFile = false);
} }
} }
@ -888,6 +909,23 @@ class _policyValidationState extends ConsumerState<policyValidation> {
// return; // return;
// } // }
// 1. Force a final check
_validateBrokerMatch();
// 2. Check the notifier value instead of the old booleans
if (brokerMismatchNotifier.value) {
final String enqName = (enqBroker ?? 'Not Selected');
final String docName = (controllers['broker_name']?.text ?? 'Empty');
// await _confirmBrokerChange(
// context,
// enquiryBroker: enqName,
// documentBroker: docName,
// );
return; // 🛑 STOP: Does not proceed to API call
}
final payload = {"id": policyId, ...dataDetails(), "updated_by": userId}; final payload = {"id": policyId, ...dataDetails(), "updated_by": userId};
if (!mounted) return; if (!mounted) return;
@ -935,6 +973,91 @@ class _policyValidationState extends ConsumerState<policyValidation> {
} }
} }
void _validateBrokerMatch() {
final String enqName = (enqBroker ?? '').trim().toLowerCase();
final String docName = (controllers['broker_name']?.text ?? '').trim().toLowerCase();
// setState(() {
// if (enqName.isNotEmpty && docName.isNotEmpty && enqName != docName) {
// highlightEnqBroker = true;
// highlightDocBroker = true;
// } else {
// // If they match OR one is empty, remove the red highlight
// highlightEnqBroker = false;
// highlightDocBroker = false;
// }
// });
// Update the notifier instead of a boolean + setState
brokerMismatchNotifier.value = (enqName.isNotEmpty && docName.isNotEmpty && enqName != docName);
}
Future<bool> _confirmBrokerChange(
BuildContext context, {
required String enquiryBroker,
required String documentBroker,
}) async {
return await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
title: Row(
children: [
Icon(Icons.warning_amber_rounded, color: Colors.orange),
SizedBox(width: 8),
Text("Broker Mismatch", style: GoogleFonts.poppins(fontWeight: FontWeight.bold)),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("The brokers do not match:", style: GoogleFonts.poppins(fontSize: 13)),
const SizedBox(height: 16),
_dialogBrokerRow("Enquiry Broker:", enquiryBroker),
const SizedBox(height: 12),
_dialogBrokerRow("Document Broker:", documentBroker),
// const SizedBox(height: 20),
// Text("Do you want to proceed with the update?",
// style: GoogleFonts.poppins(fontWeight: FontWeight.w500, fontSize: 13)),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text("OK", style: TextStyle(color: Colors.grey[700], fontWeight: FontWeight.bold)),
),
// ElevatedButton(
// style: ElevatedButton.styleFrom(
// backgroundColor: const Color(0xFF2E7D6E),
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
// ),
// onPressed: () => Navigator.pop(context, true),
// child: const Text("PROCEED", style: TextStyle(color: Colors.white)),
// ),
],
),
) ?? false;
}
Widget _dialogBrokerRow(String label, String value) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: GoogleFonts.poppins(fontSize: 11, color: Colors.grey[600])),
Text(value,
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.red[700],
fontWeight: FontWeight.bold
)
),
],
);
}
Future<void> _fetchCommision() async { Future<void> _fetchCommision() async {
print('_fetchCommision IN'); print('_fetchCommision IN');
final policyId = widget.item?['policy_id']; final policyId = widget.item?['policy_id'];
@ -1136,10 +1259,10 @@ class _policyValidationState extends ConsumerState<policyValidation> {
onChanged: (val) { onChanged: (val) {
if (val != null) { if (val != null) {
setState(() { // setState(() {
// selectedVehicleType = val; // selectedVehicleType = val;
controllers['vehicleType']?.text = val; controllers['vehicleType']?.text = val;
}); // });
// setState(() {}); // setState(() {});
print("Selected Vehicle Type: $val"); print("Selected Vehicle Type: $val");
} }
@ -1252,10 +1375,10 @@ class _policyValidationState extends ConsumerState<policyValidation> {
onChanged: (val) { onChanged: (val) {
if (val != null) { if (val != null) {
setState(() { // setState(() {
// selectedFuelType = val; // selectedFuelType = val;
controllers['fuelType']?.text = val; controllers['fuelType']?.text = val;
}); // });
// setState(() {}); // setState(() {});
print("Selected Fuel Type: $val"); print("Selected Fuel Type: $val");
} }
@ -1267,7 +1390,120 @@ class _policyValidationState extends ConsumerState<policyValidation> {
Widget buildBrokerAsPerEnquiry(BuildContext context) { Widget buildBrokerAsPerEnquiry(BuildContext context, String EnqID) {
// 1. Find the currently selected broker object from your list
final Map<String, dynamic>? selectedBrokers = selectedBroker != null
? filteredBrokerData.firstWhere(
(item) => item['id'].toString() == selectedBroker,
orElse: () => {},
)
: null;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// HEADER SECTION: Label + Info Icon
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Broker (as per Enquiry)",
style: GoogleFonts.poppins(
fontWeight: FontWeight.w500,
fontSize: 11,
),
),
const SizedBox(width: 6),
Tooltip(
message: 'View Audit History',
child: InkWell(
onTap: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => NewAuditHistoryModal(
enquiryId: EnqID,
),
);
},
child: const Icon(
Icons.info_outline,
size: 14,
color: Color(0xFF2E7D6E),
),
),
),
],
),
const SizedBox(height: 6),
// DROPDOWN SECTION: Searchable + Red Border Logic
ValueListenableBuilder<bool>(
valueListenable: brokerMismatchNotifier, // Listens to the mismatch flag
builder: (context, isMismatched, child) {
return Container(
decoration: BoxDecoration(
// The border turns red instantly if there is a mismatch
border: Border.all(
color: isMismatched ? Colors.red : Colors.transparent,
width: 1.5,
),
borderRadius: BorderRadius.circular(6),
),
child: DropdownSearch<Map<String, dynamic>>(
key: dropDownKeyBroker,
// Define how to compare broker maps (Critical to prevent errors)
compareFn: (item1, item2) =>
item1['id'].toString() == item2['id'].toString(),
// Items to show
items: (filter, _) => filteredBrokerData,
// Display the broker name in the selection
itemAsString: (item) => item['name'].toString(),
selectedItem: (selectedBrokers != null && selectedBrokers.isNotEmpty)
? selectedBrokers
: null,
// SEARCH CONFIGURATION
popupProps: PopupProps.menu(
showSearchBox: true, // Enables the search bar
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search broker name...",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
fit: FlexFit.loose,
),
onChanged: (val) {
if (val != null) {
// Update data variables
selectedBroker = val['id'].toString();
enqBroker = val['name'].toString();
controllers['enquiry_broker_id_for_commission']?.text = val['id'].toString();
// Trigger the highlight check WITHOUT calling setState
_validateBrokerMatch();
}
},
validator: (val) => val == null ? "" : null,
),
);
},
)
],
);
}
Widget buildBOASPerEnquiry(BuildContext context, String EnqID) {
final Map<String, dynamic>? selectedBrokers = final Map<String, dynamic>? selectedBrokers =
selectedBroker != null selectedBroker != null
? filteredBrokerData.firstWhere( ? filteredBrokerData.firstWhere(
@ -1279,16 +1515,48 @@ class _policyValidationState extends ConsumerState<policyValidation> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Row(
"Broker (as per Enquiry)", mainAxisSize: MainAxisSize.min,
style: GoogleFonts.poppins( children: [
fontWeight: FontWeight.w500, Text(
fontSize: 11, "Broker (as per Enquiry)",
), style: GoogleFonts.poppins(
fontWeight: FontWeight.w500,
fontSize: 11,
),
),
const SizedBox(width: 6),
Tooltip(
message: 'View Audit History',
child: InkWell(
onTap: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => NewAuditHistoryModal(
enquiryId: EnqID, // Note: Ensure this class is defined correctly as discussed
),
);
},
child: const Icon(
Icons.info_outline,
size: 14,
color: Color(0xFF2E7D6E),
),
),
),
],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Container(
DropdownSearch<Map<String, dynamic>>( decoration: BoxDecoration(
border: Border.all(
color: highlightEnqBroker ? Colors.red : Colors.transparent,
width: 1.5,
),
borderRadius: BorderRadius.circular(6),
),
child: DropdownSearch<Map<String, dynamic>>(
key: dropDownKeyBroker, key: dropDownKeyBroker,
items: (filter, _) => filteredBrokerData, items: (filter, _) => filteredBrokerData,
@ -1303,19 +1571,23 @@ class _policyValidationState extends ConsumerState<policyValidation> {
compareFn: (item1, item2) => compareFn: (item1, item2) =>
item1['id'].toString() == item2['id'].toString(), item1['id'].toString() == item2['id'].toString(),
onChanged: (val) { onChanged: (val) {
if (val != null) { if (val != null) {
selectedBroker = val['id'].toString(); setState(() {
controllers['enquiry_broker_id_for_commission']?.text = selectedBroker = val['id'].toString();
val['id'].toString(); enqBroker = val['name'].toString();
} controllers['enquiry_broker_id_for_commission']?.text = val['id'].toString();
}, });
_validateBrokerMatch(); // 👈 Check mismatch immediately
}
},
validator: (val) { validator: (val) {
if (val == null) return ""; if (val == null) return "";
return null; return null;
}, },
), ),
)
], ],
); );
} }
@ -1593,6 +1865,8 @@ class _policyValidationState extends ConsumerState<policyValidation> {
List<TextInputFormatter>? inputFormatters, // 👈 ADD THIS List<TextInputFormatter>? inputFormatters, // 👈 ADD THIS
ValueChanged<String>? onChanged, // 👈 ADD THIS ValueChanged<String>? onChanged, // 👈 ADD THIS
String? rightLabel, // 👈 ADD THIS String? rightLabel, // 👈 ADD THIS
Widget? infoIcon, // 👈 ADD THIS
bool highlight = false, // 👈 ADD THIS
}) { }) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -1608,6 +1882,20 @@ class _policyValidationState extends ConsumerState<policyValidation> {
label, label,
style: GoogleFonts.poppins(fontWeight: FontWeight.w500, fontSize: 11), style: GoogleFonts.poppins(fontWeight: FontWeight.w500, fontSize: 11),
), ),
// if (required)
// const Text(
// ' *',
// style: TextStyle(color: Colors.red, fontSize: 11),
// ),
if (infoIcon != null) ...[
const SizedBox(width: 6),
infoIcon,
],
const Spacer(),
if (rightLabel != null) if (rightLabel != null)
Text( Text(
rightLabel, rightLabel,
@ -1642,7 +1930,20 @@ class _policyValidationState extends ConsumerState<policyValidation> {
return null; return null;
}, },
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)), border: OutlineInputBorder(borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(
color: highlight ? Colors.red : Colors.grey,
width: highlight ? 1.6 : 1,
),
),
// Inside _buildInput decoration:
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(
color: highlight ? Colors.red : Colors.grey.shade400,
width: highlight ? 1.6 : 1,
),
),
isDense: true, isDense: true,
hintText: hintText, hintText: hintText,
hintStyle: hintText == null hintStyle: hintText == null
@ -2325,7 +2626,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
SizedBox(height: 10), SizedBox(height: 10),
Row( Row(
children: [ children: [
Expanded(child:buildBrokerAsPerEnquiry(context)), Expanded(child:buildBrokerAsPerEnquiry(context,widget.item['id'].toString())),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded(child:buildInsurancPlanType(context)), Expanded(child:buildInsurancPlanType(context)),
], ],
@ -2333,12 +2634,47 @@ class _policyValidationState extends ConsumerState<policyValidation> {
Row( Row(
children: [ children: [
Expanded( Expanded(
child: _buildInput( child: ValueListenableBuilder<bool>(
'Broker (as per Document) *', valueListenable: brokerMismatchNotifier,
controllers['broker_name']!, builder: (context, isMismatched, child) {
required: true, return _buildInput(
'Broker (as per Document) *',
controllers['broker_name']!,
required: true,
highlight: isMismatched, // 👈 Passes true/false to your _buildInput
onChanged: (val) {
_validateBrokerMatch();
},
);
},
), ),
), ),
// Expanded(
// child: _buildInput(
// 'Broker (as per Document)',
// controllers['broker_name']!,
// required: true,
// infoIcon: Tooltip(
// message: 'View Audit History',
// child: InkWell(
// onTap: () {
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (_) => NewAuditHistoryModal(
// policyId: '878',
// ),
// );
// },
// child: const Icon(
// Icons.info_outline,
// size: 14,
// color: Color(0xFF2E7D6E),
// ),
// ),
// ),
// ),
// ),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(
child: _buildInput( child: _buildInput(
@ -2457,3 +2793,140 @@ class DateInputFormatter extends TextInputFormatter {
); );
} }
} }
// 1. THE WIDGET CLASS
class NewAuditHistoryModal extends ConsumerStatefulWidget {
final String enquiryId;
const NewAuditHistoryModal({super.key, required this.enquiryId});
@override
NewAuditHistoryModalState createState() => NewAuditHistoryModalState();
}
// 2. THE STATE CLASS
class NewAuditHistoryModalState extends ConsumerState<NewAuditHistoryModal> {
late ApiService apiService;
bool isLoadingHistory = true;
List<Map<String, dynamic>> getAuditHistoryData = [];
@override
void initState() {
super.initState();
apiService = ApiService();
fetchAuditHistory();
}
Future<void> fetchAuditHistory() async {
try {
final response = await apiService.fetchAuditHistory(
widget.enquiryId.toString(),'partner_enquiry'
);
if (response != null && response['status'] == "success") {
if (mounted) {
setState(() {
getAuditHistoryData =
List<Map<String, dynamic>>.from(response['data'])
.where((e) => e['column_name']?.toString() == 'broker_name')
.toList();
});
}
}
} catch (e) {
debugPrint("Audit History Error: $e");
} finally {
if (mounted) setState(() => isLoadingHistory = false);
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
contentPadding: const EdgeInsets.all(16),
content: SizedBox(
width: MediaQuery.of(context).size.width * 0.55,
height: MediaQuery.of(context).size.height * 0.7,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
"Audit History",
style: GoogleFonts.poppins(
fontSize: 14,
color: const Color(0xFF50A398),
fontWeight: FontWeight.w500,
),
),
const Spacer(),
IconButton(
icon: const Icon(Icons.close, size: 18),
onPressed: () => Navigator.pop(context),
),
],
),
const Divider(),
Expanded(
child: isLoadingHistory
? const Center(child: CircularProgressIndicator())
: getAuditHistoryData.isEmpty
? const Center(child: Text("No Audit History Data Found"))
: ListView.builder(
itemCount: getAuditHistoryData.length,
itemBuilder: (context, index) {
return _buildTimelineItem(
data: getAuditHistoryData[index],
isLast: index == getAuditHistoryData.length - 1,
);
},
),
),
],
),
),
);
}
Widget _buildTimelineItem({
required Map<String, dynamic> data,
required bool isLast,
}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
const CircleAvatar(radius: 5, backgroundColor: Color(0xFF2E7D6E)),
if (!isLast)
Container(width: 2, height: 60, color: Colors.grey.shade300),
],
),
const SizedBox(width: 12),
Expanded(
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
DateFormat('dd-MM-yyyy').format(DateTime.parse(data['changed_on'])),
style: GoogleFonts.poppins(fontSize: 11),
),
const SizedBox(height: 4),
Text(data['message'] ?? "-"),
// const SizedBox(height: 4),
// Text(
// "Changed by ${data['changed_by'] ?? '-'}",
// style: GoogleFonts.poppins(fontSize: 11),
// ),
],
),
),
),
],
);
}
}

View File

@ -827,7 +827,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
); );
} }
Widget buildSelectInsurer(BuildContext context) { Widget buildSelectInsurer_withoutsearch(BuildContext context) {
// Ensure this list has data BEFORE building dropdown // Ensure this list has data BEFORE building dropdown
final List<Map<String, dynamic>> insurerOptions = final List<Map<String, dynamic>> insurerOptions =
@ -956,6 +956,152 @@ class _DateFilterRowState extends ConsumerState<DateFilterRow> {
); );
} }
Widget buildSelectInsurer(BuildContext context) {
// Ensure this list has data BEFORE building dropdown
final List<Map<String, dynamic>> insurerOptions =
List<Map<String, dynamic>>.from(filteredInsurerData);
// Selected item logic
Map<String, dynamic>? selectedItem;
if (widget.selectedInsurerVal != null &&
widget.selectedInsurerVal.toString().isNotEmpty &&
insurerOptions.isNotEmpty) {
try {
selectedItem = insurerOptions.firstWhere(
(e) => e['id'].toString() == widget.selectedInsurerVal.toString(),
);
} catch (e) {
selectedItem = null;
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 35,
child: Container(
width: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.14,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: DropdownSearch<Map<String, dynamic>>(
selectedItem: selectedItem,
// FILTER LOGIC: This filters the list as the user types
items: (filter, infiniteScrollProps) {
if (filter.isEmpty) {
return insurerOptions;
}
return insurerOptions
.where((item) => item['short_name']
.toString()
.toLowerCase()
.contains(filter.toLowerCase()))
.toList();
},
itemAsString: (item) => item['short_name']?.toString() ?? '',
compareFn: (item, selected) {
if (selected == null) return false;
return item['id'].toString() == selected['id'].toString();
},
// FIELD UI (The collapsed state)
dropdownBuilder: (context, selectedItem) {
return Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem != null
? selectedItem['short_name'].toString()
: '',
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.black,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
},
decoratorProps: DropDownDecoratorProps(
decoration: AppInputDecorations.dropdownDecoration(
label: "Select Insurer",
).copyWith(
filled: true,
fillColor: Colors.white,
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 1,
),
),
),
// POPUP UI (The expanded state with search)
popupProps: PopupProps.menu(
showSearchBox: true, // Enabled searching
fit: FlexFit.loose,
constraints: const BoxConstraints(maxHeight: 300),
menuProps: const MenuProps(backgroundColor: Colors.white),
// Customizing the search field appearance
searchFieldProps: TextFieldProps(
style: GoogleFonts.poppins(fontSize: 13),
decoration: InputDecoration(
hintText: "Search insurer...",
hintStyle: GoogleFonts.poppins(fontSize: 12),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
),
),
itemBuilder: (context, item, isDisabled, isSelected) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 10,
),
decoration: BoxDecoration(
color: isSelected ? Colors.blue.withOpacity(0.1) : Colors.transparent,
),
child: Text(
item['short_name'].toString(),
style: GoogleFonts.inter(
fontSize: 13,
color: Colors.black,
),
),
);
},
),
// ON CHANGE
onChanged: (val) {
if (val == null) return;
// Note: Ensure 'selectedInsurer' is defined in your state/class
selectedInsurer = val['id'];
widget.onInsurerChanged?.call(val['id']);
// Safe UI refresh
Future.microtask(() {
widget.onFilter();
});
},
),
),
),
],
);
}
static const _textStyle = TextStyle( static const _textStyle = TextStyle(