FIX_1_BrokerName HightlightForPolicyVerifyScreen
This commit is contained in:
parent
2dd4e281d0
commit
1f34df0d2e
File diff suppressed because one or more lines are too long
@ -1420,12 +1420,12 @@ class ApiService {
|
||||
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(
|
||||
'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') {
|
||||
// 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 ");
|
||||
|
||||
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 url = Uri.parse(
|
||||
'${Env.apiUrl}audit/history?table_name=partner_policy&pk=${id ?? ''}',
|
||||
'${Env.apiUrl}audit/history?table_name==${table ?? ''}&pk=${id ?? ''}',
|
||||
);
|
||||
|
||||
final headers = {
|
||||
|
||||
@ -861,7 +861,7 @@ class policylistState extends ConsumerState<policylist> {
|
||||
SizedBox(width: 10),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showAuditHistoryModal(context, item['id']);
|
||||
showAuditHistoryModal(context, item['policy_id']);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5.4),
|
||||
@ -1204,7 +1204,7 @@ class AuditHistoryModalState extends ConsumerState<AuditHistoryModal> {
|
||||
try {
|
||||
// Use widget.policyId here!
|
||||
final response = await apiService.fetchAuditHistory(
|
||||
widget.policyId.toString(),
|
||||
widget.policyId.toString(),'partner_policy'
|
||||
);
|
||||
|
||||
if (response != null && response['status'] == "success") {
|
||||
|
||||
@ -38,14 +38,24 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
// --- Api / state
|
||||
late ApiService apiService;
|
||||
bool isLoading = false;
|
||||
bool isLoadingFile = false;
|
||||
bool isLoadingVehicleType = false;
|
||||
bool isLoadingVehicleTypeForCommission = false;
|
||||
bool isLoadingFuelType = false;
|
||||
bool isLoadingBroker = false;
|
||||
bool isLoadingPaymentMode = false;
|
||||
bool isLoadingInsurancePlan = false;
|
||||
dynamic managerId;
|
||||
dynamic userId;
|
||||
|
||||
String? _cachedPdfUrl;
|
||||
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)
|
||||
final List<String> controllerKeys = [
|
||||
// Policy
|
||||
@ -343,6 +353,17 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
};
|
||||
update();
|
||||
_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 {
|
||||
print('getBroker called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
isLoadingBroker = true;
|
||||
});
|
||||
|
||||
try {
|
||||
@ -417,7 +438,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
isLoadingBroker = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -425,7 +446,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
Future<void> getInsurancPlanType() async {
|
||||
print('getInsurancPlanType called');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
isLoadingInsurancePlan = true;
|
||||
});
|
||||
|
||||
try {
|
||||
@ -451,13 +472,13 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
isLoadingInsurancePlan = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getPaymentMode() async {
|
||||
setState(() => isLoading = true);
|
||||
setState(() => isLoadingPaymentMode = true);
|
||||
|
||||
try {
|
||||
final response = await apiService.fetchMasterDropDown('PaymentMode');
|
||||
@ -479,22 +500,22 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
// setState(() {
|
||||
getPaymentModeData.clear();
|
||||
filteredPaymentModeData.clear();
|
||||
});
|
||||
// });
|
||||
}
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() => isLoading = false);
|
||||
setState(() => isLoadingPaymentMode = false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<void> getEnquiryVehicleTypeForCommission() async {
|
||||
setState(() => isLoading = true);
|
||||
setState(() => isLoadingVehicleTypeForCommission = true);
|
||||
|
||||
try {
|
||||
final response =
|
||||
@ -528,7 +549,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
} catch (e) {
|
||||
print('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() => isLoading = false);
|
||||
setState(() => isLoadingVehicleTypeForCommission = false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,7 +680,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() => isLoading = true);
|
||||
setState(() => isLoadingFile = true);
|
||||
|
||||
try {
|
||||
final url = Uri.parse(
|
||||
@ -675,7 +696,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
print('Exception in getPolicyFilePath: $e\n$st');
|
||||
} finally {
|
||||
if (!mounted) return;
|
||||
setState(() => isLoading = false);
|
||||
setState(() => isLoadingFile = false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -888,6 +909,23 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
// 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};
|
||||
|
||||
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 {
|
||||
print('_fetchCommision IN');
|
||||
final policyId = widget.item?['policy_id'];
|
||||
@ -1136,10 +1259,10 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
setState(() {
|
||||
// setState(() {
|
||||
// selectedVehicleType = val;
|
||||
controllers['vehicleType']?.text = val;
|
||||
});
|
||||
// });
|
||||
// setState(() {});
|
||||
print("Selected Vehicle Type: $val");
|
||||
}
|
||||
@ -1252,10 +1375,10 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
setState(() {
|
||||
// setState(() {
|
||||
// selectedFuelType = val;
|
||||
controllers['fuelType']?.text = val;
|
||||
});
|
||||
// });
|
||||
// setState(() {});
|
||||
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 =
|
||||
selectedBroker != null
|
||||
? filteredBrokerData.firstWhere(
|
||||
@ -1279,16 +1515,48 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Broker (as per Enquiry)",
|
||||
style: GoogleFonts.poppins(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 11,
|
||||
),
|
||||
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, // Note: Ensure this class is defined correctly as discussed
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.info_outline,
|
||||
size: 14,
|
||||
color: Color(0xFF2E7D6E),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
|
||||
DropdownSearch<Map<String, dynamic>>(
|
||||
Container(
|
||||
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,
|
||||
|
||||
items: (filter, _) => filteredBrokerData,
|
||||
@ -1303,19 +1571,23 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
compareFn: (item1, item2) =>
|
||||
item1['id'].toString() == item2['id'].toString(),
|
||||
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
selectedBroker = val['id'].toString();
|
||||
controllers['enquiry_broker_id_for_commission']?.text =
|
||||
val['id'].toString();
|
||||
}
|
||||
},
|
||||
onChanged: (val) {
|
||||
if (val != null) {
|
||||
setState(() {
|
||||
selectedBroker = val['id'].toString();
|
||||
enqBroker = val['name'].toString();
|
||||
controllers['enquiry_broker_id_for_commission']?.text = val['id'].toString();
|
||||
});
|
||||
_validateBrokerMatch(); // 👈 Check mismatch immediately
|
||||
}
|
||||
},
|
||||
|
||||
validator: (val) {
|
||||
if (val == null) return "";
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -1593,6 +1865,8 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
List<TextInputFormatter>? inputFormatters, // 👈 ADD THIS
|
||||
ValueChanged<String>? onChanged, // 👈 ADD THIS
|
||||
String? rightLabel, // 👈 ADD THIS
|
||||
Widget? infoIcon, // 👈 ADD THIS
|
||||
bool highlight = false, // 👈 ADD THIS
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -1608,6 +1882,20 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
label,
|
||||
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)
|
||||
Text(
|
||||
rightLabel,
|
||||
@ -1642,7 +1930,20 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
return null;
|
||||
},
|
||||
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,
|
||||
hintText: hintText,
|
||||
hintStyle: hintText == null
|
||||
@ -2325,7 +2626,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child:buildBrokerAsPerEnquiry(context)),
|
||||
Expanded(child:buildBrokerAsPerEnquiry(context,widget.item['id'].toString())),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child:buildInsurancPlanType(context)),
|
||||
],
|
||||
@ -2333,12 +2634,47 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildInput(
|
||||
'Broker (as per Document) *',
|
||||
controllers['broker_name']!,
|
||||
required: true,
|
||||
child: ValueListenableBuilder<bool>(
|
||||
valueListenable: brokerMismatchNotifier,
|
||||
builder: (context, isMismatched, child) {
|
||||
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),
|
||||
Expanded(
|
||||
child: _buildInput(
|
||||
@ -2456,4 +2792,141 @@ class DateInputFormatter extends TextInputFormatter {
|
||||
selection: TextSelection.collapsed(offset: formatted.length),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
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(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user