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';
|
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 = {
|
||||||
|
|||||||
@ -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") {
|
||||||
|
|||||||
@ -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(
|
||||||
@ -2456,4 +2792,141 @@ class DateInputFormatter extends TextInputFormatter {
|
|||||||
selection: TextSelection.collapsed(offset: formatted.length),
|
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
|
// 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(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user