post_enrollment_app/lib/pages/postEnrollment/planclaimsform.dart
2026-07-06 09:55:31 +05:30

1767 lines
85 KiB
Dart
Executable File

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'package:nhance_app_pwa/customAppBar/customAppBar.dart';
import 'package:nhance_app_pwa/pages/postEnrollment/service/api_service.dart';
import 'package:nhance_app_pwa/pages/postEnrollment/service/file_upload_service.dart';
import 'package:nhance_app_pwa/pages/postEnrollment/service/multi_file_upload_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/environment.dart';
import '../../customAppBar/customFooter.dart';
import '../../customAppBar/responsive.dart';
import '../../customAppBar/tabs.dart';
import '../../customAppBar/toastHelper.dart';
import 'package:http/http.dart' as http;
// import 'package:universal_html/html.dart' as html;
import 'package:file_picker/file_picker.dart';
import '../service/SessionManager.dart';
import '../service/TokenService.dart';
import '../service/popup_helper.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'package:flutter/services.dart';
import 'package:nhance_app_pwa/logger.dart';
class planclaimsform extends StatefulWidget {
final Map<String, dynamic>? details;
const planclaimsform({Key? key, this.details}) : super(key: key);
@override
State<planclaimsform> createState() => _planclaimsformState();
}
class _planclaimsformState extends State<planclaimsform> {
late ApiService apiService;
bool isLoading = false;
dynamic _token;
dynamic empCodeString;
dynamic empPrimaryId;
dynamic client_id;
List<Map<String, dynamic>> policyList = [];
dynamic policyDataIsEmpty = 1;
dynamic policyHeading;
dynamic policyName;
dynamic EmployeePolicy;
List<Map<String, String>> employeeDetails = [];
dynamic argumentsData;
dynamic claimsDetails;
int? fromClaimsPage;
dynamic decodedToken;
List<Map<String, dynamic>> departmentList = [];
List<Map<String, dynamic>> policyNumberList = [];
int? serviceId;
int? policyNumberId;
int? selectedMemberId;
String? selectedMemberName;
String? selectedServiceName;
String? selectedPolicyNo;
dynamic claimsDepartmentId;
dynamic claimSubject;
dynamic rawPolicyStartDate;
dynamic rawPolicyEndDate;
dynamic claimsPolicyNo;
dynamic client_branch_id;
dynamic mobileNo;
dynamic policyTypeCondition;
dynamic client_policy_id;
dynamic selfDetails;
dynamic emailId;
List<Map<String, dynamic>> employeePolicyList = [];
String? selectedFileNames;
// html.File? uploadedFile;
// List<html.File> uploadedFiles = [];
List<PlatformFile> uploadedFiles = [];
final fileService = FileUploadService();
bool isSubmitting = false;
Map<String, dynamic> claimTypeMap = {};
List<Map<String, dynamic>> claimTypeList = [];
int? claimTypeId;
bool isClaimTypeValid = true;
// Declare subjectController and bodyController as instance variables
late TextEditingController subjectController;
late TextEditingController messageController;
late TextEditingController accidentDetailsController;
late TextEditingController hospitalNameController;
late TextEditingController hospitalAddressController;
late TextEditingController hospitalCityController;
late TextEditingController hospitalStateController;
late TextEditingController hospitalPinCodeController;
late TextEditingController hospitalPhoneNoController;
late TextEditingController claimAmountController;
late TextEditingController sumInsuredController;
late TextEditingController admitDateController;
late TextEditingController dischargeDateController;
DateTime? accidentDate;
DateTime? deathDate;
DateTime? intimationDate;
DateTime? birthDate;
DateTime? admitDate;
DateTime? dischargeDate;
DateTime? parsedPolicyStartDate;
DateTime? parsedPolicyEndDate;
Map<String, DateTime?> selectedDates = {
"Accident Date": null,
"Date of Death": null,
};
List<Map<String, dynamic>> filteredPoliciesList = [];
// Create a unique form key for each accordion section
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
bool isServiceValid = true;
bool isPolicyValid = true;
bool isMemberValid = true;
bool isSubjectValid = true;
bool isHospitalNameValid = true;
bool isHospitalAddressValid = true;
bool isHospitalCityValid = true;
bool isHospitalStateValid = true;
bool isHospitalPincodeValid = true;
bool isHospitalPhoneNoValid = true;
bool isAdmitDischargeValid = true;
bool isClaimAmountValid = true;
bool isAccidentDateValid = true;
bool isIntimationDateValid = true;
bool isAdmitDateValid = true;
bool isDischargeDateValid = true;
bool showFileError = false;
final session = SessionManager();
@override
void initState() {
super.initState();
fileService.clearAll();
subjectController = TextEditingController();
messageController = TextEditingController();
accidentDetailsController = TextEditingController();
hospitalNameController = TextEditingController();
hospitalAddressController = TextEditingController();
hospitalCityController = TextEditingController();
hospitalStateController = TextEditingController();
hospitalPinCodeController = TextEditingController();
hospitalPhoneNoController = TextEditingController();
claimAmountController = TextEditingController();
sumInsuredController = TextEditingController();
admitDateController = TextEditingController();
dischargeDateController = TextEditingController();
apiService = ApiService(context); // Initialize ApiService here
_loadToken();
// Check if fromClaimsPage is 0 and set serviceId to 'Claims' (id is 2)
// if (fromClaimsPage == 0) {
// setInitialServiceId();
// }
}
@override
void dispose() {
// Dispose the controllers to avoid memory leaks
subjectController.dispose();
messageController.dispose();
accidentDetailsController.dispose();
hospitalNameController.dispose();
hospitalAddressController.dispose();
hospitalCityController.dispose();
hospitalStateController.dispose();
hospitalPinCodeController.dispose();
hospitalPhoneNoController.dispose();
claimAmountController.dispose();
sumInsuredController.dispose();
admitDateController.dispose();
dischargeDateController.dispose();
serviceId = null;
departmentList.clear();
super.dispose();
fileService.clearAll();
}
// void repositionBotman() {
// if (kIsWeb) {
// js.context.callMethod('repositionBotmanWidget');
// }
// }
@override
void didChangeDependencies() {
logDebug('didChangeDependencies');
super.didChangeDependencies();
// dynamic arguments = ModalRoute.of(context)!.settings.arguments;
dynamic arguments = widget.details;
if (arguments != null && arguments is Map<String, dynamic>) {
argumentsData = arguments;
claimsDetails = argumentsData['claimsDetails'];
logDebug('claimsDetails $claimsDetails');
if (argumentsData.containsKey('fromClaimPage')) {
fromClaimsPage = argumentsData['fromClaimPage'];
logDebug('fromClaimsPage $fromClaimsPage');
}
if (fromClaimsPage == 0) {
setState(() {
claimSubject = claimsDetails['claim_subject'];
logDebug('claimSubject $claimSubject');
claimsDepartmentId = claimsDetails['ticket_type_id'];
rawPolicyStartDate = claimsDetails['policy_start_date'];
logDebug('rawPolicyStartDate $rawPolicyStartDate');
rawPolicyEndDate = claimsDetails['claims_grace_date'];
logDebug('rawpolicyEndDate $rawPolicyEndDate');
logDebug('claimsDepartmentId $claimsDepartmentId');
claimsPolicyNo = claimsDetails['policy_no'];
client_policy_id = claimsDetails['client_policy_id'];
logDebug('Current Client ID : ${client_policy_id}');
});
// employeePolicyList = claimsDetails['EmployeePolicy'];
// logDebug('employeePolicyList : $employeePolicyList');
List<dynamic> empList = claimsDetails['EmployeePolicy'];
setState(() {
subjectController.text = claimSubject;
employeePolicyList = empList.map((memberList) {
return {
'id': int.parse(memberList['id']),
'name': memberList['name'],
'relationship': memberList['relationship'],
};
}).toList();
logDebug('employeePolicyList : $employeePolicyList');
});
// if (claimsDetails['ticket_type_id'] == 3 ||
// claimsDetails['ticket_type_id'] == 4) {
policyTypeCondition = claimsDetails['ticket_type_id'];
// } else {
// String policyType = claimsDetails['policy_type'];
// policyTypeCondition = policyType.split('-')[0].trim();
// logDebug('Output : $policyTypeCondition'); // Output: GMC
// }
}
}
}
void
setInitialServiceId(departmentListDetails) {
logDebug('departmentList : $departmentListDetails');
for (var department in departmentListDetails) {
logDebug(department['id'] == claimsDepartmentId);
if (department['id'] == claimsDepartmentId) {
setState(() {
serviceId = department['id'];
});
break;
}
}
}
Future<void> _loadToken() async {
logDebug('_loadToken');
final String? token = await TokenService.getPostToken();
if (token != null && token.isNotEmpty) {
// Decode the JWT token received from the API response
Map<String, dynamic>? decodedToken = Jwt.parseJwt(token);
logDebug(decodedToken);
mobileNo = session.mobileNo;
client_branch_id = session.empClientBranchId;
empCodeString = session.empCodeString;
logDebug(empCodeString); // Check if emp_code is correct
empPrimaryId = session.empPrimaryId;
client_id = session.client_id;
emailId = session.empEmailCorporate;
logDebug(client_id);
getActiveAndInactivePolicyDetails();
fetchDepartmentList();
getSelfEmployeeProfile();
}
}
Future<void> getActiveAndInactivePolicyDetails() async {
if (client_id == null || empCodeString == null) {
return;
}
try {
// Call both APIs
final responseActive = await apiService.getActiveAndInactivePolicyDetails(
client_id!, empCodeString!, 'Active', client_branch_id,mobileNo,emailId
);
final responseInactive = await apiService.getActiveAndInactivePolicyDetails(
client_id!, empCodeString!, 'Inactive', client_branch_id,mobileNo,emailId
);
final bool isActiveSuccess = responseActive['status'] == 'success' && responseActive['data'] != null;
final bool isInactiveSuccess = responseInactive['status'] == 'success' && responseInactive['data'] != null;
final List<Map<String, dynamic>> activeData = isActiveSuccess
? List<Map<String, dynamic>>.from(responseActive['data'])
: [];
final List<Map<String, dynamic>> inactiveData = isInactiveSuccess
? List<Map<String, dynamic>>.from(responseInactive['data'])
: [];
final combinedPolicies = [...activeData, ...inactiveData];
setState(() {
policyList = combinedPolicies;
policyDataIsEmpty = combinedPolicies.isEmpty ? 0 : 1;
});
if (fromClaimsPage == 0) {
logDebug('fromClaimsPage == 0');
fetchPoliciesBasedOnService(claimsDepartmentId);
}
logDebug("Merged policyList: $policyList");
} catch (e) {
logDebug('Error occurred while fetching policies: $e');
setState(() {
policyDataIsEmpty = 0;
});
}
}
Future<void> getSelfEmployeeProfile() async {
logDebug(getSelfEmployeeProfile);
if (client_id == null || empCodeString == null) {
return;
}
logDebug('check 1');
final response = await apiService.getSelfEmployeeProfileDetails(
client_id!, empCodeString!, client_branch_id!);
logDebug('check 1');
if (response['status'] == 'success') {
if (response.containsKey('data')) {
setState(() {
selfDetails = response['data'];
logDebug(selfDetails);
});
} else {
// Handle other status messages if needed
// ToastHelper.showErrorToast(
// context, 'API request failed with status: ${data['status']}');
logDebug('API request failed with status: ${response['status']}');
}
} else {
logDebug('API request failed with status: ${response['status']}');
}
}
Future<void> fetchDepartmentList() async {
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchDepartmentList(client_id!, empCodeString!);
logDebug(response);
if (response['ticket_type'] != null) {
logDebug(response['ticket_type']);
List<dynamic> departments = response['ticket_type'];
setState(() {
departmentList = departments.map((department) {
return {
'id': department['id'],
'name': department['name'],
// 'private': department['private'],
};
}).toList();
claimTypeMap = Map<String, dynamic>.from(response['claim_type']);
isLoading = false;
});
// ✅ AUTO SELECT SERVICE = 1 (GMC)
if (fromClaimsPage == 1 && departmentList.isNotEmpty) {
serviceId = 1;
loadClaimTypes(serviceId);
}
if (fromClaimsPage == 0) {
setInitialServiceId(departmentList);
loadClaimTypes(claimsDepartmentId);
}
// if (fromClaimsPage == 0) {
// setInitialServiceId(departmentList);
// }
} else {
throw Exception('Failed to fetch department list');
}
} catch (error) {
logDebug('Error fetching department list: $error');
}
}
void loadClaimTypes(int? serviceId) {
if (serviceId == null) return;
String key = serviceId == 72 ? '1' : serviceId.toString();
if (claimTypeMap.containsKey(key)) {
Map<String, dynamic> types =
Map<String, dynamic>.from(claimTypeMap[key]);
claimTypeList = types.entries.map((entry) {
return {
'id': int.parse(entry.key),
'name': entry.value,
};
}).toList();
// // ✅ AUTO SELECT FIRST CLAIM TYPE
// if (claimTypeList.isNotEmpty) {
// claimTypeId = claimTypeList.first['id'];
// }
setState(() {});
} else {
claimTypeList = [];
claimTypeId = null;
setState(() {});
}
}
void fetchPoliciesBasedOnService(int? serviceId) {
if (policyList.isEmpty) {
logDebug('Policy list is empty. Ensure data is fetched before filtering.');
return;
}
setState(() {
policyNumberId = null;
policyTypeCondition = null;
// client_policy_id = null;
});
logDebug('1111 serviceId : $serviceId');
if (serviceId == 1 || serviceId == 2 || serviceId == 3 || serviceId == 4 || serviceId == 72) {
logDebug('2222');
// Filter policies based on the ticket_type_id
List<Map<String, dynamic>> filteredPolicies = policyList.where((policy) {
return policy['ticket_type_id'] == serviceId;
}).toList();
logDebug('Filtered matching policies: $filteredPolicies');
// if (filteredPolicies[0]['ticket_type_id'] == 3 ||
// filteredPolicies[0]['ticket_type_id'] == 4) {
setState(() {
// Ensure unique IDs for dropdown values
policyNumberList = filteredPolicies.asMap().entries.map((entry) {
final index = entry.key;
final policy = entry.value;
// Assign unique ID
return {
'policy_no': policy['policy_no'],
'policy_name': policy['policy_name'],
'id': index + 1, // Use index + 1 as a unique ID
'policy_start_date' : policy['policy_start_date'],
'claims_grace_date' : policy['claims_grace_date'],
'claim_subject' : policy['claim_subject'],
};
}).toList();
logDebug('policyNumberList : $policyNumberList');
if (fromClaimsPage == 0) {
// Find the object where 'policy_no' matches 'claimsPolicyNo'
final matchingPolicy = policyNumberList.firstWhere(
(item) => item['policy_no'] == claimsPolicyNo,
orElse: () => {});
// Set policyNumberId to the matching policy's ID
if (matchingPolicy.isNotEmpty) {
policyNumberId = matchingPolicy['id'];
}
}
setState(() {
// if(serviceId == 1){
rawPolicyStartDate = filteredPolicies[0]['policy_start_date'];
rawPolicyEndDate = filteredPolicies[0]['claims_grace_date'];
parsedPolicyStartDate = DateFormat('dd-MMM-yyyy').parse(rawPolicyStartDate);
parsedPolicyEndDate = DateFormat('dd-MMM-yyyy').parse(rawPolicyEndDate);
// }
});
policyTypeCondition = filteredPolicies[0]['ticket_type_id'];
// client_policy_id = filteredPolicies[0]['client_policy_id'];
logDebug('Output : $policyTypeCondition'); // Output: GMC
});
} else {
setState(() {
policyTypeCondition = null;
});
}
// else if (serviceId == 2) {
// setState(() {
// // Ensure unique IDs for dropdown values
// policyNumberList = policyList.asMap().entries.map((entry) {
// final index = entry.key;
// final policy = entry.value;
//
// // Assign unique ID
// return {
// 'policy_no': policy['policy_no'],
// 'policy_name': policy['policy_name'],
// 'id': index + 1 // Use index + 1 as a unique ID
// };
// }).toList();
//
// logDebug('policyNumberList : $policyNumberList');
//
// policyTypeCondition = serviceId;
// logDebug('Output : $policyTypeCondition');
// });
// } else {
// setState(() {
// policyTypeCondition = null;
// });
// }
}
void fetchMemberBasedOnPolicy(selectedPolicyNo) {
if (policyList.isEmpty) {
logDebug('Policy list is empty. Ensure data is fetched before filtering.');
return;
}
if (serviceId == 1 || serviceId == 2 || serviceId == 3 || serviceId == 4) {
// Filter policies based on the ticket_type_id
logDebug('policyNumberId : $policyNumberId');
filteredPoliciesList = policyList.where((policy) {
return policy['policy_no'] == selectedPolicyNo;
}).toList();
logDebug('Filtered matching policy_no: $filteredPoliciesList');
// client_policy_id = filteredPoliciesList[0]['client_policy_id'];
subjectController.text = filteredPoliciesList[0]['claim_subject'];
if (filteredPoliciesList.isNotEmpty) {
// Assuming you need to access the first matched policy
Map<String, dynamic> firstPolicy = filteredPoliciesList.first;
// Check if 'EmployeePolicy' key exists and is a List
if (firstPolicy.containsKey('EmployeePolicy') &&
firstPolicy['EmployeePolicy'] is List) {
List<dynamic> empList = firstPolicy['EmployeePolicy'];
setState(() {
employeePolicyList = empList.map((memberList) {
return {
'id': int.parse(memberList['id']),
'name': memberList['name'],
'relationship': memberList['relationship'],
};
}).toList();
logDebug('employeePolicyList : $employeePolicyList');
});
} else {
logDebug('No EmployeePolicy found in the filtered policy.');
setState(() {
employeePolicyList = []; // Clear the list or handle it as needed
});
}
} else {
logDebug('No policies matched the selected policy number.');
setState(() {
employeePolicyList = []; // Clear the list or handle it as needed
});
}
}
// else if (serviceId == 2) {
// filteredPoliciesList = policyList.where((policy) {
// return policy['policy_no'] == selectedPolicyNo;
// }).toList();
//
// logDebug('Filtered matching policy_no: $filteredPoliciesList');
// }
}
Future<void> sendFormDataToApi() async {
final isServiceValid = serviceId != null;
final isPolicyValid = policyNumberId != null;
final isMemberValid = selectedMemberId != null;
final isSubjectValid = subjectController.text.trim().isNotEmpty;
final isHospitalNameValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalNameController.text.trim().isNotEmpty;
final isHospitalAddressValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalAddressController.text.trim().isNotEmpty;
final isHospitalStateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalStateController.text.trim().isNotEmpty;
final isHospitalCityValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalCityController.text.trim().isNotEmpty;
final isHospitalPincodeValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalPinCodeController.text.trim().isNotEmpty;
final isHospitalPhoneNoValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || (hospitalPhoneNoController.text.trim().length == 10);
final isAdmitDateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || admitDate != null;
final isDischargeDateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || dischargeDate != null;
final isClaimAmountValid = serviceId != 1 || claimAmountController.text.trim().isNotEmpty;
final isAccidentDateValid = (serviceId == 2 || serviceId == 3 || serviceId == 4) ? accidentDate != null : true;
final isIntimationDateValid = (serviceId == 2 || serviceId == 3 || serviceId == 4) ? intimationDate != null : true;
final areFilesUploaded = FileUploadService().files.isNotEmpty;
if (!isServiceValid ||
!isPolicyValid ||
!isMemberValid ||
!isSubjectValid ||
!isHospitalNameValid ||
!isHospitalAddressValid ||
!isHospitalStateValid ||
!isHospitalCityValid ||
!isHospitalPincodeValid ||
!isHospitalPhoneNoValid ||
!isAdmitDateValid ||
!isDischargeDateValid ||
!isClaimAmountValid ||
!isAccidentDateValid ||
!isIntimationDateValid) {
setState(() {
this.isServiceValid = isServiceValid;
this.isPolicyValid = isPolicyValid;
this.isMemberValid = isMemberValid;
this.isSubjectValid = isSubjectValid;
this.isHospitalNameValid = isHospitalNameValid;
this.isHospitalAddressValid = isHospitalAddressValid;
this.isHospitalStateValid = isHospitalStateValid;
this.isHospitalCityValid = isHospitalCityValid;
this.isHospitalPincodeValid = isHospitalPincodeValid;
this.isHospitalPhoneNoValid = isHospitalPhoneNoValid;
this.isAdmitDateValid = isAdmitDateValid;
this.isDischargeDateValid = isDischargeDateValid;
this.isClaimAmountValid = isClaimAmountValid;
this.isAccidentDateValid = isAccidentDateValid;
this.isIntimationDateValid = isIntimationDateValid;
showFileError = true; // Also show file error if other fields are invalid
});
ToastHelper.showErrorToast(context, 'Please Fill Required Fields');
return;
}
if (!areFilesUploaded) {
setState(() {
showFileError = true;
});
ToastHelper.showErrorToast(context, 'Please upload the document');
return;
}
setState(() {
isSubmitting = true;
isLoading = true;
});
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String? emp_id = session.empPrimaryId;
logDebug('Check One $client_policy_id');
try {
logDebug('Check One');
Map<String, dynamic> fields = {
'emp_id': emp_id,
'client_policy_id': client_policy_id,
'ticket_type_id': serviceId,
'subject': subjectController.text,
'message': messageController.text,
'emp_code': empCodeString ?? ' ',
'mobile_number': mobileNo ?? '',
'email': session.empEmailCorporate ?? '',
'fullname': session.gpaEmpName ?? '',
'client_id': selfDetails['client_id'],
'relationship': selfDetails['relationship'],
'gender': selfDetails['gender'],
'age': calculateAge(selfDetails['dob']).toString(),
'policy_name': serviceId == 1
? null
: (fromClaimsPage == 0
? claimsDetails['policy_name']
: filteredPoliciesList[0]['policy_name']),
};
if (policyTypeCondition == 1 || policyTypeCondition == 72) {
String formattedAdmitDate = DateFormat('yyyy-MM-dd').format(admitDate!);
String formattedDischargeDate = DateFormat('yyyy-MM-dd').format(dischargeDate!);
fields['claim_type'] = claimTypeId;
fields['hospital_name'] = hospitalNameController.text;
fields['hospital_address'] = hospitalAddressController.text;
fields['hospital_city'] = hospitalCityController.text;
fields['hospital_state'] = hospitalStateController.text;
fields['hospital_pin_code'] = hospitalPinCodeController.text;
fields['hospital_phone_no'] = hospitalPhoneNoController.text;
fields['doa'] = formattedAdmitDate;
fields['dod'] = formattedDischargeDate;
fields['claim_amount'] = claimAmountController.text;
fields['policyholder_name'] = employeePolicyList[0]['name'];
fields['member_name'] = selectedMemberName;
fields['insured_emp_id'] = selectedMemberId;
} else {
String formattedAccidentDate =
DateFormat('yyyy-MM-dd').format(accidentDate!);
String formattedDeathDate = DateFormat('yyyy-MM-dd').format(deathDate!);
String formattedBirthDate = DateFormat('yyyy-MM-dd').format(birthDate!);
String formattedIntimationDate =
DateFormat('yyyy-MM-dd').format(intimationDate!);
fields['date_of_accident'] = formattedAccidentDate;
fields['dob'] = formattedBirthDate;
fields['date_of_intimat'] = formattedIntimationDate;
fields['date_of_death'] = formattedDeathDate;
fields['si_amt'] = sumInsuredController.text;
fields['policyholder_name'] = employeePolicyList[0]['name'];
fields['member_name'] = selectedMemberName;
fields['insured_emp_id'] = selectedMemberId;
}
_token = await TokenService.getPostToken();
final request = http.MultipartRequest(
'POST', Uri.parse('${Environment.apiUrl}initiateClaim'));
request.headers['Authorization'] = 'Bearer $_token';
request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
final stringFields = fields.map((key, value) => MapEntry(key, value?.toString() ?? ''));
logDebug("📁 stringFields: ${stringFields}");
request.fields.addAll(stringFields);
// get uploaded files
final uploadedFiles = FileUploadService().files;
logDebug("📁 Total uploaded files: ${uploadedFiles.length}");
logDebug("📂 File list (original names): ${uploadedFiles.map((f) => f.file.name).toList()}");
// Validate all labels present (optional but recommended)
for (final uf in uploadedFiles) {
logDebug("📝 Checking label for file: ${uf.file.name}, current label: '${uf.label}'");
if ((uf.label ?? '').trim().isEmpty) {
ToastHelper.showErrorToast(context, 'Please enter name for all uploaded documents');
setState(() {
isLoading = false;
isSubmitting = false; // ✅ reset loader
});
return;
}
}
// ✅ NEW VALIDATION: At least one PDF must be uploaded
bool hasPdf = uploadedFiles.any((uf) {
final ext = uf.file.extension?.toLowerCase() ?? '';
return ext == 'pdf';
});
if (!hasPdf) {
ToastHelper.showErrorToast(context, 'Please upload at least one PDF document');
setState(() => isLoading = false);
return;
}
// Add files
for (var uf in uploadedFiles) {
final pf = uf.file;
if (pf.bytes != null) {
request.files.add(http.MultipartFile.fromBytes(
'claim_docs[]',
pf.bytes!,
filename: pf.name,
));
}
}
// Add files — convert images to PDF if needed
// for (var uf in uploadedFiles) {
// final pf = uf.file;
// final ext = pf.extension?.toLowerCase() ?? '';
// if (pf.bytes != null) {
// Uint8List fileBytes = pf.bytes!;
// if (['jpg', 'jpeg', 'png', 'heic'].contains(ext)) {
// // ✅ Convert image → PDF
// final pdf = pw.Document();
// final image = pw.MemoryImage(fileBytes);
// pdf.addPage(pw.Page(
// build: (pw.Context context) =>
// pw.Center(child: pw.Image(image, fit: pw.BoxFit.contain)),
// ));
// fileBytes = await pdf.save();
// final pdfFileName = pf.name.replaceAll(RegExp(r'\.\w+$'), '.pdf');
// logDebug('📄 Converted image ${pf.name} → PDF ($pdfFileName)');
// request.files.add(http.MultipartFile.fromBytes(
// 'claim_docs[]', fileBytes, filename: pdfFileName));
// } else {
// // ✅ Already a PDF
// request.files.add(http.MultipartFile.fromBytes(
// 'claim_docs[]', fileBytes, filename: pf.name));
// }
// }
// }
// ✅ Combine all names into a JSON array string
final claimDocNames = uploadedFiles.map((uf) => uf.label.trim()).toList();
final encodedNames = jsonEncode(claimDocNames);
request.fields['claim_doc_names'] = encodedNames;
// ✅ Debug print
logDebug("Payload being sent:");
logDebug("Files: ${uploadedFiles.map((f) => f.file.name).toList()}");
logDebug("Names (JSON): $encodedNames");
final response = await request.send();
final responseBody = await response.stream.bytesToString();
final decoded = jsonDecode(responseBody);
if (decoded['status'] == true) {
ToastHelper.showSuccessToast(context, decoded['message']);
serviceId = null;
departmentList.clear();
setState(() => isLoading = false);
context.go('/claims', extra: 2);
logDebug('Form data submitted successfully');
fileService.clearAll();
} else {
setState(() => isLoading = false);
ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}");
}
} catch (e) {
setState(() => isLoading = false);
logDebug('Error submitting form data: $e');
} finally {
setState(() => isSubmitting = false); // 🔥 always stop loader
}
}
Future<void> _pickDate(BuildContext context, String label) async {
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: selectedDates[label] ?? DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime.now(),
);
if (pickedDate != null && pickedDate != selectedDates[label]) {
setState(() {
selectedDates[label] = pickedDate;
});
}
}
Future<void> _pickFromToDate(BuildContext context) async {
// Define the policy date range
final DateTime policyStartDate = DateFormat('dd-MMM-yyyy').parse(rawPolicyStartDate);
final DateTime policyEndDate = DateFormat('dd-MMM-yyyy').parse(rawPolicyEndDate);
// Show the date range picker within the policy date limits
final DateTimeRange? pickedDateRange = await showDateRangePicker(
context: context,
firstDate: policyStartDate,
lastDate: policyEndDate,
initialDateRange: admitDate != null && dischargeDate != null
? DateTimeRange(start: admitDate!, end: dischargeDate!)
: null,
);
// Update values if a valid date range is picked
if (pickedDateRange != null) {
setState(() {
admitDate = pickedDateRange.start;
dischargeDate = pickedDateRange.end;
admitDateController.text = DateFormat('dd-MM-yyyy').format(admitDate!);
dischargeDateController.text =
DateFormat('dd-MM-yyyy').format(dischargeDate!);
});
}
}
// Future<void> _pickFromToDate(BuildContext context) async {
// final DateTimeRange? pickedDateRange = await showDateRangePicker(
// context: context,
// firstDate: DateTime(2000),
// lastDate: DateTime(2100),
// initialDateRange: admitDate != null && dischargeDate != null
// ? DateTimeRange(start: admitDate!, end: dischargeDate!)
// : null,
// );
//
// if (pickedDateRange != null) {
// setState(() {
// admitDate = pickedDateRange.start;
// dischargeDate = pickedDateRange.end;
// admitDateController.text = DateFormat('yyyy-MM-dd').format(admitDate!);
// dischargeDateController.text =
// DateFormat('yyyy-MM-dd').format(dischargeDate!);
// });
// }
// }
int calculateAge(String dob) {
DateTime birthDate = DateTime.parse(dob); // Parse the dob string
DateTime currentDate = DateTime.now();
int age = currentDate.year - birthDate.year;
// Adjust age if the birthday has not occurred yet this year
if (currentDate.month < birthDate.month ||
(currentDate.month == birthDate.month &&
currentDate.day < birthDate.day)) {
age--;
}
return age;
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
context.pop();
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: CustomAppBar(),
body: Stack(children: [
SingleChildScrollView(
child: Container(
padding: Responsive.isDesktop(context)
? EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width *
0.2, // 30% of screen width as horizontal padding
vertical: MediaQuery.of(context).size.height *
0.05, // 5% of screen height as vertical padding
)
: EdgeInsets.all(10),
color: Colors.white,
child: Column(children: [
Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(15.0), // Set border radius here
),
child: Container(
decoration: BoxDecoration(
color: Colors.white, // Set background color to white
borderRadius: BorderRadius.circular(
15.0), // Set border radius for Container
),
padding: Responsive.isDesktop(context)
? EdgeInsets.only(top: 15, bottom: 15, left: 15, right: 15)
: EdgeInsets.only(
top: 10,
bottom: 10,
left: 10,
right: 10), // Add padding to the container
child: Row(
children: [
Expanded(
flex: 12,
child: Container(
alignment: Alignment.centerLeft,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: Color(
0xFFFFFCE5), // Set background color for the container
borderRadius: BorderRadius.circular(
10), // Set border radius for the container
),
padding: Responsive.isDesktop(context)
? EdgeInsets.only(
top: 20,
bottom: 20,
left: 0,
right: 0)
: EdgeInsets.only(
top: 10,
bottom: 10,
left: 10,
right: 10),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: InkWell(
onTap: () {
context.pop();
},
child: Icon(
Icons
.chevron_left, // Replace with your desired icon
color: Color(0xFF000000),
size: 30,
),
),
),
Expanded(
flex: 11,
child: Row(
mainAxisAlignment:
Responsive.isDesktop(context)
? MainAxisAlignment.center
: MainAxisAlignment.start,
children: [
Text(
fromClaimsPage == 0
? claimsDetails[
'policy_name']
: 'Raise a Request',
textAlign: TextAlign.start,
style: GoogleFonts.poppins(
fontSize:
Responsive.isDesktop(
context)
? 20
: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF000000),
),
)
],
),
)
],
), // Space between rows
// Add more rows as needed
],
),
),
SizedBox(height: 15),
Container(
child: Column(
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Container(
width: double.infinity,
alignment: Alignment.centerLeft,
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Form(
key: formKey,
child: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildDropdownField(
'Service',isRequired: true,
(value) {
setState(() {
serviceId = value;
selectedServiceName = departmentList.firstWhere(
(serList) => serList['id'] == value,
orElse: () => {'name': ''},
)['name'];
policyNumberId = null;
if (fromClaimsPage == 1) {
fetchPoliciesBasedOnService(serviceId);
}
isServiceValid = true;
});
},
fromClaimsPage == 0,
departmentList,
'name',
serviceId,
),
if (!isServiceValid)
Text('Please select a service', style: TextStyle(color: Colors.red)),
],
),
// buildDropdownField(
// 'Service',
// (value) {
// setState(() {
// serviceId = value;
// selectedServiceName =
// departmentList
// .firstWhere(
// (serList) =>
// serList[
// 'id'] ==
// value,
// orElse: () =>
// {'name': ''},
// )['name'];
// policyNumberId =
// null;
// if (fromClaimsPage ==
// 1) {
// fetchPoliciesBasedOnService(
// serviceId);
// }
// });
// },
// fromClaimsPage == 0,
// departmentList,
// 'name',
// serviceId, // Pass current serviceId as selectedValue
// ),
SizedBox(height: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildDropdownField(
'Select Policy',isRequired: true,
(value) {
setState(() {
policyNumberId = value;
selectedMemberId = null;
String? selectedPolicyNo = policyNumberList.firstWhere(
(item) => item['id'] == value,
orElse: () => {'policy_no': ''},
)['policy_no'];
if (selectedPolicyNo != null && fromClaimsPage == 1) {
fetchMemberBasedOnPolicy(selectedPolicyNo);
}
isPolicyValid = true;
});
},
fromClaimsPage == 0,
policyNumberList,
'policy_no',
policyNumberId,
),
if (!isPolicyValid)
Text('Please select a policy', style: TextStyle(color: Colors.red)),
],
),
SizedBox(height: 15),
if (policyTypeCondition == 1 || policyTypeCondition == 72)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildDropdownField(
'Select Claim Type',
(value) {
setState(() {
claimTypeId = value;
isClaimTypeValid = true;
});
},
false,
claimTypeList,
'name',
claimTypeId,
),
if (!isClaimTypeValid)
Text(
'Please select a claim type',
style: TextStyle(color: Colors.red),
),
],
),
// // if (fromClaimsPage == 1)
// buildDropdownField(
// 'Select Policy',
// (value) {
// setState(() {
// policyNumberId =
// value;
// selectedMemberId =
// null;
// String?
// selectedPolicyNo =
// policyNumberList
// .firstWhere(
// (item) =>
// item['id'] ==
// value,
// orElse: () =>
// {
// 'policy_no': ''
// })['policy_no'];
//
// if (selectedPolicyNo !=
// null &&
// fromClaimsPage ==
// 1) {
// fetchMemberBasedOnPolicy(
// selectedPolicyNo);
// }
// });
// },
// fromClaimsPage == 0,
// policyNumberList,
// 'policy_no',
// policyNumberId, // Pass current policyNumberId as selectedValue
// ),
SizedBox(height: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildDropdownField(
'Member Name',isRequired: true,
(value) {
setState(() {
selectedMemberId = value;
selectedMemberName = employeePolicyList.firstWhere(
(member) => member['id'] == value,
orElse: () => {'name': ''},
)['name'];
isMemberValid = true;
});
},
false,
employeePolicyList,
'name',
selectedMemberId,
),
if (!isMemberValid)
Text('Please select a member', style: TextStyle(color: Colors.red)),
],
),
SizedBox(height: 15),
// buildDropdownField(
// 'Member Name',
// (value) {
// setState(() {
// selectedMemberId =
// value;
// selectedMemberName =
// employeePolicyList
// .firstWhere(
// (member) =>
// member[
// 'id'] ==
// value,
// orElse: () =>
// {'name': ''},
// )['name'];
// });
// },
// false,
// employeePolicyList,
// 'name',
// selectedMemberId, // Pass current selectedMemberId as selectedValue
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// buildTextField('Subject',
// subjectController),
// if (!isSubjectValid)
// Text('Please enter the Subject', style: TextStyle(color: Colors.red)),
SizedBox(height: 15),
buildTextAreaField(
'Message',
messageController),
SizedBox(height: 15),
// if (policyTypeCondition ==
// 3)
// buildTextField(
// 'Accident Details',
// accidentDetailsController),
SizedBox(height: 15),
if (policyTypeCondition == 1 || policyTypeCondition == 72) ...[
buildTextField('Hospital Name', hospitalNameController, isRequired: true),
if (!isHospitalNameValid)
Text('Please enter the Hospital Name', style: TextStyle(color: Colors.red)),
SizedBox(height: 15),
buildTextAreaField('Hospital Address', hospitalAddressController, isRequired: true),
if (!isHospitalAddressValid)
Text('Please enter the Hospital Address', style: TextStyle(color: Colors.red)),
SizedBox(height: 15),
buildTextField('Hospital City', hospitalCityController,isRequired: true,inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z ]'))]),
if (!isHospitalCityValid)
Text('Please enter the Hospital City', style: TextStyle(color: Colors.red)),
SizedBox(height: 15),
buildTextField('Hospital State', hospitalStateController,isRequired: true,inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z ]'))]),
if (!isHospitalStateValid)
Text('Please enter the Hospital State', style: TextStyle(color: Colors.red)),
SizedBox(height: 15),
buildTextField('Hospital Pincode', hospitalPinCodeController,isRequired: true,keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(6),
]),
if (!isHospitalPincodeValid)
Text('Please enter the Hospital Pincode', style: TextStyle(color: Colors.red)),
SizedBox(height: 15),
buildTextField('Hospital Phone No', hospitalPhoneNoController,
isRequired: true,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(10),
],minLength: 10,),
if (!isHospitalPhoneNoValid)
Text(
hospitalPhoneNoController.text.trim().isEmpty
? 'Please enter the Hospital Phone No'
: 'Hospital Phone No must be 10 digits', // ✅ specific message
style: TextStyle(color: Colors.red),
),
],
SizedBox(height: 15),
if (serviceId == 2 ||
serviceId == 3 ||
serviceId == 4) ...[
buildDatePickerField(
label:
'Date of Birth',
selectedDate:
birthDate,
allowFuture: false,
onDateSelected:
(selectedDate) {
setState(() {
birthDate =
selectedDate;
});
},
),
SizedBox(height: 15),
buildDatePickerField(
label: 'Accident Date',
isRequired: true,
selectedDate: accidentDate,
allowFuture: false,
minDate: parsedPolicyStartDate,
maxDate: parsedPolicyEndDate,
onDateSelected: (selectedDate) {
setState(() {
if (deathDate != null) deathDate = null;
if (intimationDate != null) intimationDate = null;
accidentDate = selectedDate;
});
},
),
if (!isAccidentDateValid)
Text('Please select the Accident Date', style: TextStyle(color: Colors.red)),
SizedBox(height: 15),
buildDatePickerField(
label:
'Date of Death',
selectedDate:
deathDate,
isRequired: true,
allowFuture: false,
minDate: accidentDate ?? parsedPolicyStartDate,
maxDate: parsedPolicyEndDate,
onDateSelected:
(selectedDate) {
setState(() {
deathDate =
selectedDate;
});
},
),
SizedBox(height: 15),
buildDatePickerField(
label: 'Date of Intimation',
isRequired: true,
selectedDate: intimationDate,
allowFuture: false,
minDate: accidentDate ?? deathDate ?? parsedPolicyStartDate,
maxDate: parsedPolicyEndDate,
onDateSelected: (selectedDate) {
setState(() {
intimationDate = selectedDate;
});
},
),
if (!isIntimationDateValid)
Text('Please select the Intimation Date', style: TextStyle(color: Colors.red)),
],
SizedBox(height: 15),
// buildTextAreaField(
// 'Accident Details',
// accidentDetailsController),
// SizedBox(height: 15),
if (policyTypeCondition == 1 || policyTypeCondition == 72) ...[
// 🟡 Admit Date — no future allowed
buildDatePickerField(
label: 'Admit Date',
isRequired: true,
selectedDate: admitDate,
allowFuture: false,
onDateSelected: (selectedDate) {
setState(() {
admitDate = selectedDate;
dischargeDate = null; // reset discharge when admit changes
});
},
),
if (!isAdmitDateValid)
const Text('Please select the Admit Date', style: TextStyle(color: Colors.red)),
const SizedBox(height: 20),
// 🟢 Discharge Date — must be after Admit Date
buildDatePickerField(
label: 'Discharge Date',
isRequired: true,
selectedDate: dischargeDate,
allowFuture: true,
minDate: admitDate != null
? admitDate!.add(const Duration(days: 1))
: DateTime.now().add(const Duration(days: 1)),
maxDate: null,
onDateSelected: (selectedDate) {
setState(() {
dischargeDate = selectedDate;
});
},
),
if (!isDischargeDateValid)
const Text('Please select the Discharge Date', style: TextStyle(color: Colors.red)),
],
SizedBox(height: 15),
if (serviceId == 1) ...[
buildTextField('Claims Amount', claimAmountController, isRequired: true, keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
]),
if (!isClaimAmountValid)
Text('Please enter the Claims Amount', style: TextStyle(color: Colors.red)),
],
SizedBox(height: 15),
if (serviceId == 2 ||
serviceId == 3 ||
serviceId == 4)
buildTextField(
'Sum Insured',
sumInsuredController,
isRequired: true,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
]
),
SizedBox(height: 15),
// ThemedUploadField(
// hintText: "Upload Documents",
// txtwidth: MediaQuery.of(context).size.width < 600
// ? MediaQuery.of(context).size.width // Full width on mobile
// : MediaQuery.of(context).size.width * 0.26, // 26% on desktop
// txtheight: 45,
// onFilesSelected: (files) {
// logDebug("Picked files: ${files.map((f) => f.name).toList()}");
// setState(() {
// uploadedFiles = files; // store all selected files
// });
// },
// ),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
MultiFileUploadWidget(isRequired: true,showError: showFileError),
],
),
),
],
),
),
],
),
),
],
),
SizedBox(height: 15),
Row(
children: [
Expanded(
flex: 12,
child: Container(
alignment: Alignment.centerRight,
child: ElevatedButton(
onPressed: () {
sendFormDataToApi();
},
child: isSubmitting
? SizedBox(
height: 22,
width: 22,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: Text(
'Send',
style: GoogleFonts.poppins(
color: Colors.white),
),
style: ElevatedButton.styleFrom(
backgroundColor:
Color(0xFFE26728),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
5),
),
),
),
),
),
],
),
],
),
),
],
))),
],
),
),
),
SizedBox(height: Responsive.isDesktop(context) ? 40 : 60),
]),
)),
if (isLoading)
Container(
color: Color(0x98FFFCE5), // Semi-transparent background
child: Center(
child: // Your GIF loader widget
Image.asset(
height: 60,
width: 60,
'assets/nhance-loader.gif'), // Adjust path to your GIF loader
),
),
if (Responsive.isDesktop(context))
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity, // Make the footer full width
child: CustomFooter(),
),
),
]),
// floatingActionButton: Responsive.isDesktop(context)
// ? null
// : FloatingActionButton(
// onPressed: () {
// Navigator.pushNamed(context, 'chatbot');
// },
// child: Icon(Icons.chat),
// ),
floatingActionButtonLocation: Responsive.isDesktop(context)
? null
: FloatingActionButtonLocation.miniEndFloat,
bottomNavigationBar: Responsive.isDesktop(context)
? null
: CustomBottomNavigationBar(
onTabChanged: (index) {
// Add your navigation logic here
// repositionBotman();
if (index == 0) {
context.push('/home');
} else if (index == 1) {
context.push('/claims');
} else if (index == 2) {
context.push('/faqs');
} else if (index == 3) {
context.push('/profile');
} else if (index == 4) {
context.push('/help');
}
// else if (index == 4) {
// // context.push('/wellness');
// // Wellness tab clicked → show popup
// if(!isRetailLoggedIn)
// PopupHelper.showRedirectPopup(
// context: context,
// apiService: apiService,
// empPrimaryId: session.empPrimaryId,
// );
// }
},
icons: [
Icons.home_outlined,
Icons.sticky_note_2_outlined,
Icons.question_answer_outlined,
Icons.person_outline_outlined,
Icons.headset_mic_outlined,
],
labels: ["Home", "Claims", "FAQs", "Profile","Help"],
initialIndex: 0, // Initial index of the bottom navigation bar
),
)
);
}
Widget buildTextField(
String label,
TextEditingController controller, {
bool isRequired = false, // 👈 ADD THIS
TextInputType keyboardType = TextInputType.text,
List<TextInputFormatter>? inputFormatters,
int? minLength,
}) {
return TextFormField(
controller: controller,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
decoration: InputDecoration(
labelText: isRequired ? '$label *' : label, // 👈 ADD THIS
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter the $label';
}
if (minLength != null && value.trim().length < minLength) { // 👈 ADD this
return '$label must be at least $minLength digits';
}
return null;
},
);
}
Widget buildTextAreaField(String label, TextEditingController controller ,{ bool isRequired = false}) {
return TextFormField(
controller: controller,
decoration: InputDecoration(labelText: isRequired ? '$label *' : label),
maxLines: 5,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter the $label';
}
return null;
},
);
}
Widget buildDatePickerField({
required String label,
required DateTime? selectedDate,
required bool allowFuture,
required ValueChanged<DateTime?> onDateSelected,
bool isRequired = false,
DateTime? minDate,
DateTime? maxDate,
}) {
return InkWell(
onTap: () async {
final DateTime now = DateTime.now();
// 👇 Special rules only for Admit & Discharge
DateTime first;
DateTime last;
if (label == 'Admit Date') {
// ❌ Admit date cannot be future
first = minDate ?? DateTime(2000);
last = now;
} else if (label == 'Discharge Date') {
// ✅ Discharge date must be AFTER admit date, future allowed
first = minDate ?? now.add(const Duration(days: 1));
last = maxDate ?? DateTime(2100);
} else {
// All other date pickers keep original behavior
first = minDate ?? DateTime(1980);
last = allowFuture ? (maxDate ?? DateTime(2100)) : now;
}
// final DateTime? pickedDate = await showDatePicker(
// context: context,
// initialDate: selectedDate ?? now,
// firstDate: label == "Date of Birth" ? DateTime(1900) : first,
// lastDate: label == "Date of Birth" ? now : last,
// );
// ✅ Fix: Ensure initialDate always within valid range
DateTime initialDate = selectedDate ?? now;
if (initialDate.isBefore(first)) initialDate = first;
if (initialDate.isAfter(last)) initialDate = last;
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: first,
lastDate: last,
);
if (pickedDate != null) {
onDateSelected(pickedDate);
}
},
child: InputDecorator(
decoration: InputDecoration(
labelText: isRequired ? '$label *' : label,
border: const OutlineInputBorder(),
),
child: Text(
selectedDate != null
? DateFormat('dd-MM-yyyy').format(selectedDate)
: 'Select $label',
style: const TextStyle(color: Colors.black),
),
),
);
}
Widget buildDropdownField(
String label,
void Function(int?) onChanged,
bool readOnly,
List<Map<String, dynamic>> itemsList,
String displayField,
int? selectedValue, // Added selectedValue parameter
{bool isRequired = false,}
) {
return DropdownButtonFormField<int>(
value: selectedValue,
decoration: InputDecoration(labelText: isRequired ? '$label *' : label,),
items: itemsList.map<DropdownMenuItem<int>>((item) {
return DropdownMenuItem<int>(
value: item['id'], // Ensure 'id' is correctly referenced
child: Text(item[displayField]),
);
}).toList(),
onChanged: readOnly ? null : onChanged,
validator: (value) {
if (value == null) {
return 'Please select a $label';
}
return null;
},
);
}
}