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? details; const planclaimsform({Key? key, this.details}) : super(key: key); @override State createState() => _planclaimsformState(); } class _planclaimsformState extends State { late ApiService apiService; bool isLoading = false; dynamic _token; dynamic empCodeString; dynamic empPrimaryId; dynamic client_id; List> policyList = []; dynamic policyDataIsEmpty = 1; dynamic policyHeading; dynamic policyName; dynamic EmployeePolicy; List> employeeDetails = []; dynamic argumentsData; dynamic claimsDetails; int? fromClaimsPage; dynamic decodedToken; List> departmentList = []; List> 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> employeePolicyList = []; String? selectedFileNames; // html.File? uploadedFile; // List uploadedFiles = []; List uploadedFiles = []; final fileService = FileUploadService(); bool isSubmitting = false; Map claimTypeMap = {}; List> 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 selectedDates = { "Accident Date": null, "Date of Death": null, }; List> filteredPoliciesList = []; // Create a unique form key for each accordion section final GlobalKey formKey = GlobalKey(); 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) { 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 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 _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? 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 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> activeData = isActiveSuccess ? List>.from(responseActive['data']) : []; final List> inactiveData = isInactiveSuccess ? List>.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 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 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 departments = response['ticket_type']; setState(() { departmentList = departments.map((department) { return { 'id': department['id'], 'name': department['name'], // 'private': department['private'], }; }).toList(); claimTypeMap = Map.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 types = Map.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> 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 firstPolicy = filteredPoliciesList.first; // Check if 'EmployeePolicy' key exists and is a List if (firstPolicy.containsKey('EmployeePolicy') && firstPolicy['EmployeePolicy'] is List) { List 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 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 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 _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 _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 _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? 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 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> itemsList, String displayField, int? selectedValue, // Added selectedValue parameter {bool isRequired = false,} ) { return DropdownButtonFormField( value: selectedValue, decoration: InputDecoration(labelText: isRequired ? '$label *' : label,), items: itemsList.map>((item) { return DropdownMenuItem( 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; }, ); } }