import 'dart:convert'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import '../config/environment.dart'; import '../customAppBar/toastHelper.dart'; import '../responsive.dart'; import '../service/api_service.dart'; import '../service/file_upload_service.dart'; import 'package:http/http.dart' as http; import 'package:file_picker/file_picker.dart'; import '../service/multi_file_upload_widget.dart'; import '../service/token_storage_service.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:nhancepolicy/logger.dart'; class RaiseClaimDialog extends StatefulWidget { final BuildContext parentContext; final VoidCallback onSuccess; const RaiseClaimDialog({ Key? key, required this.parentContext, required this.onSuccess, }) : super(key: key); @override State createState() => _RaiseClaimDialogState(); } class _RaiseClaimDialogState extends State { // const RaiseClaimDialog({super.key}); bool hasSubmitted = false; late ApiService apiService; bool isLoading = false; dynamic empPrimaryId; dynamic empClientId; dynamic empClientBranchId; dynamic empHrId; final tokenService = TokenStorageService(); String? _postPreToken = ''; 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; String? serviceName; 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 client_policy_id; dynamic selfDetails; dynamic emailId; List> employeePolicyList = []; String? selectedFileNames; // html.File? uploadedFile; // List uploadedFiles = []; List uploadedFiles = []; final fileService = FileUploadService(); bool isSubmitting = false; dynamic getPostCardArrays = []; List> activePoliciesList = []; int stausVal = 1; Map getClaimPoliciesApi = {}; int? selectedPolicyTypeId; int? selectedClientPolicyId; Map? selectedMemberObject; TextEditingController searchController = TextEditingController(); 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, }; final Map> servicePolicyTypeMap = { 1: [2, 3, 4, 5], 2: [1], 3: [6], 4: [7], }; 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 isAccidentService = false; bool isDeathDateValid = true; @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; claimTypeId = null; departmentList.clear(); super.dispose(); fileService.clearAll(); } Future _loadToken() async { _postPreToken = tokenService.getCurrentToken(); empClientId = await tokenService.readValue('empClientId'); empClientBranchId = await tokenService.readValue('empClientBranchId'); empHrId = await tokenService.readValue('empHrId'); getClaimsPoliciesDetails(); await getPostCashDepositDetails( empClientBranchId, empClientId, empHrId, _postPreToken); } Future getClaimsPoliciesDetails() async { logDebug('9'); setState(() { isLoading = true; }); try { logDebug('10'); final response = await apiService.getClaimPoliciesToApi(_postPreToken!, empClientId); if (response['status'] == 'success') { setState(() { isLoading = false; }); setState(() { getClaimPoliciesApi = Map.from(response['data']); departmentList = (getClaimPoliciesApi['ticket_type'] as List) .map>((item) => { 'id': int.parse(item['ticket_type'].toString()), 'name': item['type_name'].toString(), }) .toList(); claimTypeMap = Map.from(getClaimPoliciesApi['claim_type']); logDebug('claimTypeMap $claimTypeMap'); }); } else { setState(() { isLoading = false; }); // ToastHelper.showWarningToast( // context, 'Request failed with status: ${response.statusCode}'); logDebug('Request failed with status: ${response['code']}'); } } catch (e) { setState(() { // isLoading = false; }); logDebug('Exception occurred: $e'); } finally { setState(() { // _isLoading = false; }); } } Future getPostCashDepositDetails( empClientBranchId, empClientId, empHrId, _postPreToken) async { logDebug('IN'); logDebug("clintBranchId -$empClientBranchId"); logDebug("clintID -$empClientId"); logDebug("hr_id -$empHrId"); logDebug("token -$_postPreToken"); isLoading = true; // setState(() { // _isLoading = true; // }); try { if (empClientBranchId == null || empClientId == null) { return; } final response = await apiService.getActiveCashDepositDetailsToApi( empClientId!, empClientBranchId!, empHrId, _postPreToken, stausVal); // final response = await apiService.getCashDepositDetailsToApi( // clintID!, clintBranchId!, hr_id, token); logDebug('IN1'); if (response['status'] == 'success') { isLoading = false; setState(() { logDebug('response'); logDebug(response['data']); getPostCardArrays = List>.from(response['data']); logDebug('getPostCardArrays'); logDebug(getPostCardArrays); activePoliciesList = getPostCardArrays; }); logDebug('IN2'); } else { isLoading = false; logDebug('API request failed with status'); setState(() { activePoliciesList = []; }); logDebug('API request failed with status'); } } catch (e) { logDebug('Exception occurred: $e'); } } void filterPoliciesByService(int serviceTicketType) { final allowedPolicyTypes = servicePolicyTypeMap[serviceTicketType] ?? []; final filtered = activePoliciesList.where((policy) { final policyTypeId = int.tryParse( policy['policy_type_id'].toString(), ); return allowedPolicyTypes.contains(policyTypeId); }).toList(); setState(() { policyNumberId = null; policyNumberList = filtered.map>((policy) { return { // ✅ VALUE (what gets sent) 'id': int.parse(policy['client_policy_id'].toString()), // ✅ DISPLAY (what user sees) 'label': '${policy['type']} - ${policy['policy_no']}', // keep extras if needed 'policy_type_id': int.parse(policy['policy_type_id'].toString()), }; }).toList(); isPolicyValid = true; }); } void loadClaimTypes() { logDebug('serviceId $serviceId'); if (serviceId == null) return; String key = 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(); logDebug('claimTypeList $claimTypeList'); // // ✅ AUTO SELECT FIRST CLAIM TYPE // if (claimTypeList.isNotEmpty) { // claimTypeId = claimTypeList.first['id']; // } } else { claimTypeList = []; claimTypeId = null; setState(() {}); } } Future getCDPoliciesDetails() async { logDebug('9'); setState(() { isLoading = true; }); try { logDebug('10'); final response = await apiService.getEmployeeAndDependenceToApi( empClientId, selectedClientPolicyId, empClientBranchId, _postPreToken!); if (response['status'] == 'success') { final List> members = List>.from(response['data']); setState(() { employeePolicyList = members.map((m) { return { 'id': int.parse(m['id'].toString()), // ✅ UNIQUE 'name': m['name'], // ✅ DISPLAY 'client_policy_id': m['client_policy_id'] ?? '', 'emp_id': m['self_employee_id'] ?? '', 'insured_emp_id': m['employee_id'] ?? '', 'emp_code': m['emp_code'] ?? '', 'mobile': m['mobile'] ?? ' ', 'empEmailCorporate': m['email_corporate'] ?? '', 'relationship': m['relationship'], 'gender': m['gender'], 'dob': m['dob'], 'policy_name': serviceId == 1 ? null : m['policy_name'], }; }).toList(); }); } } catch (e) { setState(() { isLoading = false; }); logDebug('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } Future sendFormDataToApi() async { setState(() { hasSubmitted = serviceId != null ? true : false; isServiceValid = serviceId != null; isPolicyValid = selectedClientPolicyId != null; isMemberValid = selectedMemberId != null; MultiFileUploadWidget.showValidation = serviceId != null && FileUploadService().files.isEmpty; // MultiFileUploadWidget.showValidation = FileUploadService().files.isEmpty; // isClaimTypeValid = claimTypeId != null; // isSubjectValid = subjectController.text.trim().isNotEmpty; // 🏥 GMC / Topup final isGmc = serviceId == 1 || serviceId == 72; isHospitalNameValid = !isGmc || hospitalNameController.text.trim().isNotEmpty; isHospitalAddressValid = !isGmc || hospitalAddressController.text.trim().isNotEmpty; isHospitalCityValid = !isGmc || hospitalCityController.text.trim().isNotEmpty; isHospitalStateValid = !isGmc || hospitalStateController.text.trim().isNotEmpty; isHospitalPincodeValid = !isGmc || hospitalPinCodeController.text.trim().isNotEmpty; // isHospitalPhoneNoValid = !isGmc || hospitalPhoneNoController.text.trim().isNotEmpty; // isHospitalPhoneNoValid = !isGmc || (hospitalPhoneNoController.text.trim().length == 10); String phone = hospitalPhoneNoController.text.trim(); // isHospitalPhoneNoValid = !isGmc || (RegExp(r'^\d{10}$').hasMatch(phone)); isHospitalPhoneNoValid = !isGmc || phone.isNotEmpty; isAdmitDateValid = !isGmc || admitDate != null; isDischargeDateValid = !isGmc || dischargeDate != null; isClaimTypeValid = !isGmc || claimTypeId != null; isClaimAmountValid = !isGmc || claimAmountController.text.trim().isNotEmpty; // ☠ Accident / Death // final isAccident = [2, 3, 4].contains(serviceId); // // isAccidentService = isAccident ? true : false; // // isAccidentDateValid = !isAccident || accidentDate != null; // isIntimationDateValid = !isAccident || intimationDate != null; // 🎯 GPA / GTLI / EDLI validation // 🎯 GPA / GTLI / EDLI dynamic validation // if (serviceId == 2) { // logDebug('serviceId == 2'); // isAccidentDateValid = accidentDate != null; // isDeathDateValid = true; // } // else if (serviceId == 3 || serviceId == 4) { // logDebug('serviceId == 3 || serviceId == 4'); // isDeathDateValid = deathDate != null; // isAccidentDateValid = true; // } // else { // logDebug('ELSE'); // isAccidentDateValid = true; // isDeathDateValid = true; // } if (serviceId == 2) { // GPA isAccidentDateValid = accidentDate != null; isDeathDateValid = true; isIntimationDateValid = true; } else if (serviceId == 3) { // GTLI isDeathDateValid = deathDate != null; isAccidentDateValid = true; isIntimationDateValid = true; } else if (serviceId == 4) { // EDLI isDeathDateValid = deathDate != null; isAccidentDateValid = true; isIntimationDateValid = true; } else { isAccidentDateValid = true; isDeathDateValid = true; isIntimationDateValid = true; } }); // if (!isDeathDateValid) { // ToastHelper.showErrorToast( // context, 'Date of Death is required'); // return; // } // // if (!isAccidentDateValid) { // ToastHelper.showErrorToast( // context, 'Accident Date is required'); // return; // } if (isServiceValid && isPolicyValid && isMemberValid && isHospitalNameValid && isHospitalAddressValid && isHospitalStateValid && isHospitalCityValid && isHospitalPincodeValid && isAdmitDateValid && isHospitalPhoneNoValid && isDischargeDateValid && isClaimAmountValid && isAccidentDateValid && isDeathDateValid && isIntimationDateValid) { // ✅ Phone validation AFTER all required fields pass // ✅ Exact 10 digit validation AFTER all required fields filled if ((serviceId == 1 || serviceId == 72) && hospitalPhoneNoController.text.trim().isNotEmpty && !RegExp(r'^\d{10}$') .hasMatch(hospitalPhoneNoController.text.trim())) { ToastHelper.showErrorToast( context, 'Hospital phone number must be exactly 10 digits', ); return; } if (FileUploadService().files.isEmpty) { ToastHelper.showErrorToast( context, 'Please upload at least one document', ); return; } } else { ToastHelper.showErrorToast( context, 'Please Fill Required Fields', ); return; } setState(() => isSubmitting = true); // 🔥 start loader setState(() { isLoading = true; }); try { logDebug('Check One'); Map fields = { 'client_policy_id': selectedClientPolicyId, // 'policy_type_id': selectedPolicyTypeId, 'emp_id': selectedMemberObject?['emp_id'], 'ticket_type_id': serviceId, 'subject': serviceName, 'message': messageController.text, 'emp_code': selectedMemberObject?['emp_code'] ?? '', 'mobile_number': selectedMemberObject?['mobile'] ?? ' ', 'email': selectedMemberObject?['empEmailCorporate'] ?? '', 'fullname': selectedMemberObject?['name'] ?? '', 'client_id': empClientId, 'relationship': selectedMemberObject?['relationship'], 'gender': selectedMemberObject?['gender'], 'age': selectedMemberObject?['dob'], 'policy_name': serviceId == 1 ? null : selectedMemberObject?['policy_name'], }; if (serviceId == 1 || serviceId == 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'] = selectedMemberObject?['name']; fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id']; } else { if (accidentDate != null) { fields['date_of_accident'] = DateFormat('yyyy-MM-dd').format(accidentDate!); } if (deathDate != null) { fields['date_of_death'] = DateFormat('yyyy-MM-dd').format(deathDate!); } if (birthDate != null) { fields['dob'] = DateFormat('yyyy-MM-dd').format(birthDate!); } if (intimationDate != null) { fields['date_of_intimat'] = DateFormat('yyyy-MM-dd').format(intimationDate!); } fields['si_amt'] = sumInsuredController.text; fields['policyholder_name'] = employeePolicyList[0]['name']; fields['member_name'] = selectedMemberName; fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id']; } final request = http.MultipartRequest( 'POST', Uri.parse('${Environment.apiUrlPost}initiateClaim')); request.headers['Authorization'] = 'Bearer $_postPreToken'; 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); 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(); // converted PDF bytes // replace file name with .pdf extension 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) { resetFormOnServiceChange(); // 1️⃣ Close dialog first Navigator.pop(context); // 2️⃣ Call parent page function widget.onSuccess(); 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; }); Navigator.pop(context); ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}"); } } catch (e) { setState(() { isLoading = false; }); logDebug('Error submitting form data: $e'); } finally { setState(() => isSubmitting = false); // 🔥 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; } void resetFormOnServiceChange() { hasSubmitted = false; MultiFileUploadWidget.showValidation = false; // Reset dropdown selections selectedClientPolicyId = null; selectedPolicyTypeId = null; policyNumberId = null; selectedMemberId = null; selectedMemberName = null; selectedMemberObject = null; claimTypeId = null; // Clear lists employeePolicyList.clear(); policyNumberList.clear(); // Clear all text controllers messageController.clear(); accidentDetailsController.clear(); hospitalNameController.clear(); hospitalAddressController.clear(); hospitalCityController.clear(); hospitalStateController.clear(); hospitalPinCodeController.clear(); hospitalPhoneNoController.clear(); claimAmountController.clear(); sumInsuredController.clear(); admitDateController.clear(); dischargeDateController.clear(); // Reset dates accidentDate = null; deathDate = null; intimationDate = null; birthDate = null; admitDate = null; dischargeDate = null; // Reset validation flags isPolicyValid = true; isMemberValid = true; isHospitalNameValid = true; isHospitalAddressValid = true; isHospitalCityValid = true; isHospitalStateValid = true; isHospitalPincodeValid = true; isHospitalPhoneNoValid = true; isAdmitDateValid = true; isDischargeDateValid = true; isClaimAmountValid = true; isAccidentDateValid = true; isIntimationDateValid = true; isDeathDateValid = true; // Clear uploaded files fileService.clearAll(); MultiFileUploadWidget.hasFiles = false; } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { resetFormOnServiceChange(); return true; }, child: Dialog( backgroundColor: Colors.white, // ✅ PURE WHITE popup insetPadding: const EdgeInsets.all(20), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: SizedBox( width: MediaQuery.of(context).size.width * 0.75, // Desktop popup width // height: MediaQuery.of(context).size.height * 0.85, child: Stack( children: [ /// MAIN CONTENT (YOUR EXISTING UI) SingleChildScrollView( child: Container( padding: EdgeInsets.all(20), color: Colors.white, child: Column( children: [ /// 🔹 HEADER ROW (Title + Close) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Raise Insurance Claim", style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.w600, ), ), IconButton( icon: const Icon(Icons.close), onPressed: () => { setState(() { resetFormOnServiceChange(); }), Navigator.pop(context) }, ), ], ), const SizedBox(height: 16), Form( key: formKey, child: Column( children: [ _row([ buildDropdownField( 'Service', (value) { final selectedItem = departmentList.firstWhere( (item) => item['id'] == value, orElse: () => {}, ); setState(() { resetFormOnServiceChange(); serviceId = value; serviceName = selectedItem['name']; policyNumberId = null; isServiceValid = true; }); logDebug( '🔥 serviceId set to $serviceId'); filterPoliciesByService(value!); loadClaimTypes(); }, departmentList, 'name', serviceId, required: true, isValid: isServiceValid, ), buildDropdownField( 'Select Policy', (value) { final selectedPolicy = policyNumberList.firstWhere( (p) => p['id'] == value); setState(() { // ✅ THIS is what you send to API selectedClientPolicyId = selectedPolicy['id']; // optional selectedPolicyTypeId = selectedPolicy['policy_type_id']; policyNumberId = value; isPolicyValid = true; }); getCDPoliciesDetails(); }, policyNumberList, 'label', // 👈 DISPLAY FIELD policyNumberId, required: true, isValid: isPolicyValid, ), if (serviceId == 1 || serviceId == 72) buildDropdownField( 'Select Claim Type', (value) { setState(() { claimTypeId = value; isClaimTypeValid = true; }); }, claimTypeList, 'name', claimTypeId, required: true, isValid: isClaimTypeValid, ), buildDropdownFieldSearch( 'Member Name', (value) { final member = employeePolicyList.firstWhere( (m) => m['id'] == value, ); setState(() { selectedMemberId = value; selectedMemberObject = member; // ✅ FULL OBJECT selectedMemberName = member['name']; isMemberValid = true; logDebug( 'selectedMemberObject $selectedMemberObject'); }); }, employeePolicyList, 'name', selectedMemberId, required: true, isValid: isMemberValid, ), ]), _row([ buildTextField( 'Message', messageController), if (serviceId == 1 || serviceId == 72) ...[ buildTextField('Hospital Name', hospitalNameController, required: true, isValid: isHospitalNameValid), buildTextField('Hospital Address', hospitalAddressController, required: true, isValid: isHospitalAddressValid), ] ]), if (serviceId == 1 || serviceId == 72) _row([ buildTextField('Hospital City', hospitalCityController, required: true, isValid: isHospitalCityValid, inputFormatters: [ FilteringTextInputFormatter.allow( RegExp(r"[a-zA-Z\s]")), ]), buildTextField('Hospital State', hospitalStateController, required: true, isValid: isHospitalStateValid, inputFormatters: [ FilteringTextInputFormatter.allow( RegExp(r"[a-zA-Z\s]")), ]), buildTextField('Hospital Pincode', hospitalPinCodeController, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter .digitsOnly, LengthLimitingTextInputFormatter(6), ], required: true, isValid: isHospitalPincodeValid), ]), if (serviceId == 1 || serviceId == 72) _row([ buildTextField( 'Hospital Phone No', hospitalPhoneNoController, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter .digitsOnly, LengthLimitingTextInputFormatter(10), ], required: true, isValid: !hasSubmitted || serviceId == null || (serviceId != 1 && serviceId != 72) || RegExp(r'^\d{10}$').hasMatch( hospitalPhoneNoController.text .trim()), ), buildDatePickerField( label: 'Admit Date', selectedDate: admitDate, allowFuture: false, onDateSelected: (d) { setState(() { admitDate = d; dischargeDate = null; }); }, required: true, isValid: isAdmitDateValid), buildDatePickerField( label: 'Discharge Date', selectedDate: dischargeDate, allowFuture: true, minDate: admitDate ?.add(const Duration(days: 1)), onDateSelected: (d) => setState(() => dischargeDate = d), required: true, isValid: isDischargeDateValid), buildTextField('Claims Amount', claimAmountController, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter .digitsOnly ], required: true, isValid: isClaimAmountValid), ]), if ([2, 3, 4].contains(serviceId)) _row([ buildDatePickerField( label: 'Date of Birth', selectedDate: birthDate, allowFuture: false, onDateSelected: (d) => setState(() => birthDate = d), ), buildDatePickerField( label: 'Accident Date', selectedDate: accidentDate, allowFuture: false, onDateSelected: (d) { setState(() { accidentDate = d; deathDate = null; intimationDate = null; }); }, required: serviceId == 2, isValid: isAccidentDateValid), buildDatePickerField( label: 'Date of Death', selectedDate: deathDate, allowFuture: false, onDateSelected: (d) => setState(() => deathDate = d), required: (serviceId == 3 || serviceId == 4) ? true : false, // 🔥 GTLI & EDLI isValid: isDeathDateValid, ), ]), if ([2, 3, 4].contains(serviceId)) _row([ buildDatePickerField( label: 'Date of Intimation', selectedDate: intimationDate, allowFuture: false, onDateSelected: (d) => setState( () => intimationDate = d)), buildTextField( 'Sum Insured', sumInsuredController, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly ], ), const SizedBox(), ]), _row([ MultiFileUploadWidget(), ]), Align( alignment: Alignment.centerRight, child: SizedBox( width: 120, height: 42, child: ElevatedButton( onPressed: (isSubmitting || serviceId == null) ? null : sendFormDataToApi, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFE26728), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), child: isSubmitting ? const SizedBox( height: 18, width: 18, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text( 'Send', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w600), ), ), ), ), ], ), ), ], ), ), ), /// 🔥 LOADER OVERLAY if (isLoading) Positioned.fill( child: Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.6), borderRadius: BorderRadius.circular(20), ), child: Center( child: Image.asset( 'assets/nhance-loader.gif', height: 60, width: 60, ), ), ), ), ], ), ), ))); } /// ---------- HELPERS ---------- Widget _row(List children) { return Padding( padding: const EdgeInsets.only(bottom: 14), child: Row( children: children .map((e) => Expanded( child: Padding( padding: const EdgeInsets.only(right: 12), child: e, ))) .toList(), ), ); } Widget fieldLabel(String text, {bool required = false}) { return Padding( padding: const EdgeInsets.only(bottom: 6), child: RichText( text: TextSpan( text: text, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.black, ), children: required ? const [ TextSpan( text: ' *', style: TextStyle(color: Colors.red), ) ] : [], ), ), ); } Widget errorText(bool isValid) { return SizedBox( height: 16, // fixed height for alignment child: isValid ? null : const Text( "Required", style: TextStyle( color: Colors.red, fontSize: 11, ), ), ); } Widget buildTextField( String label, TextEditingController controller, { bool required = false, bool isValid = true, TextInputType keyboardType = TextInputType.text, List? inputFormatters, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ fieldLabel(label, required: required), formBox( child: TextField( controller: controller, keyboardType: keyboardType, inputFormatters: inputFormatters, decoration: const InputDecoration( isDense: true, border: InputBorder.none, ), ), ), errorText(isValid), ], ); } Widget buildTextAreaField(String label, TextEditingController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ fieldLabel(label), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: const Color(0xFFF1F1F1), borderRadius: BorderRadius.circular(8), ), child: TextField( controller: controller, maxLines: 3, decoration: const InputDecoration( isDense: true, border: InputBorder.none, ), ), ), ], ); } Widget buildDropdownField( String label, void Function(int?) onChanged, List> itemsList, String displayField, int? selectedValue, { bool required = false, bool isValid = true, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ fieldLabel(label, required: required), formBox( child: DropdownButtonHideUnderline( child: DropdownButton( isExpanded: true, value: selectedValue, hint: const Text('Select'), icon: const Icon(Icons.keyboard_arrow_down), /// ✅ This controls selected value (closed state) selectedItemBuilder: (context) { return itemsList.map((item) { return Align( alignment: Alignment.centerLeft, child: Text( item[displayField] ?? '', maxLines: 1, overflow: TextOverflow.ellipsis, softWrap: false, ), ); }).toList(); }, /// ✅ This controls dropdown list (open state) items: itemsList.map>((item) { return DropdownMenuItem( value: item['id'], child: Text( item[displayField] ?? '', style: const TextStyle(fontSize: 13), ), // FULL TEXT here ); }).toList(), onChanged: onChanged, ), ), ), if (required) errorText(isValid), ], ); } Widget buildDropdownFieldSearch( String label, void Function(int?) onChanged, List> itemsList, String displayField, int? selectedValue, { bool required = false, bool isValid = true, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ fieldLabel(label, required: required), const SizedBox(height: 4), Container( height: 40, padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( color: Color(0xFFF1F1F1), borderRadius: BorderRadius.circular(8), // boxShadow: [ // BoxShadow( // color: Colors.black.withOpacity(0.08), // blurRadius: 8, // offset: const Offset(0, 2), // ), // ], ), child: DropdownButtonHideUnderline( child: DropdownButton2( isExpanded: true, value: selectedValue, hint: const Text( 'Select', style: TextStyle(fontSize: 12), ), iconStyleData: const IconStyleData( icon: Icon(Icons.keyboard_arrow_down), ), dropdownStyleData: DropdownStyleData( maxHeight: 260, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), ), ), // 🔍 SEARCH SUPPORT dropdownSearchData: DropdownSearchData( searchController: searchController, searchInnerWidgetHeight: 50, searchInnerWidget: Padding( padding: const EdgeInsets.all(8), child: TextField( controller: searchController, style: const TextStyle(fontSize: 12), decoration: InputDecoration( hintText: 'Search...', hintStyle: const TextStyle(fontSize: 12), isDense: true, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), searchMatchFn: (item, searchValue) { final matchedItem = itemsList.firstWhere( (e) => e['id'] == item.value, orElse: () => {}, ); final text = matchedItem[displayField]?.toString().toLowerCase() ?? ''; return text.contains(searchValue.toLowerCase()); }, ), // 🔹 CLEAR SEARCH WHEN CLOSED onMenuStateChange: (isOpen) { if (!isOpen) searchController.clear(); }, items: itemsList.map((item) { return DropdownMenuItem( value: item['id'], child: Text( item[displayField] ?? '', style: const TextStyle(fontSize: 12), overflow: TextOverflow.ellipsis, ), ); }).toList(), onChanged: onChanged, ), ), ), if (required) errorText(isValid), ], ); } Widget buildDatePickerField({ required String label, required DateTime? selectedDate, required bool allowFuture, required ValueChanged onDateSelected, DateTime? minDate, DateTime? maxDate, bool required = false, bool isValid = true, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ fieldLabel(label, required: required), Container( height: 42, padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( color: const Color(0xFFF1F1F1), borderRadius: BorderRadius.circular(8), // border: required && !isValid // ? Border.all(color: Colors.red) // : null, ), child: InkWell( onTap: () async { final DateTime now = DateTime.now(); final DateTime first = minDate ?? DateTime(1980); final DateTime last = allowFuture ? (maxDate ?? DateTime(2100)) : now; final DateTime initialDate = selectedDate ?? (first.isAfter(now) ? first : now); final picked = await showDatePicker( context: context, initialDate: initialDate, firstDate: first, lastDate: last, initialEntryMode: DatePickerEntryMode.calendarOnly, ); if (picked != null) { onDateSelected(picked); } }, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( selectedDate != null ? DateFormat('dd-MM-yyyy').format(selectedDate) : 'Select', style: const TextStyle(color: Colors.black), ), // ✅ Show clear button only if date selected if (selectedDate != null) GestureDetector( onTap: () { onDateSelected(null); // 🔥 Clear date }, child: const Icon( Icons.close, size: 18, color: Colors.grey, ), ) else const Icon(Icons.calendar_today, size: 18), ], ), ), ), errorText(isValid), ], ); } // Widget uploadBox() { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // fieldLabel('Upload Documents'), // Container( // height: 120, // width: double.infinity, // decoration: BoxDecoration( // color: const Color(0xFFF9F9F9), // borderRadius: BorderRadius.circular(10), // border: Border.all( // color: const Color(0xFF00A6A6), // width: 1, // ), // ), // child: const MultiFileUploadWidget(), // ), // ], // ); // } Widget formBox({required Widget child}) { return Container( height: 42, padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( color: const Color(0xFFF1F1F1), // 👈 light grey borderRadius: BorderRadius.circular(8), ), child: Center(child: child), ); } } // class RaiseClaimDialog extends StatelessWidget { // const RaiseClaimDialog({super.key}); // // @override // Widget build(BuildContext context) { // return Dialog( // backgroundColor: Colors.white, // 👈 THIS // insetPadding: const EdgeInsets.all(24), // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(16), // ), // child: Container( // width: MediaQuery.of(context).size.width * 0.75, // padding: const EdgeInsets.all(24), // child: Column( // mainAxisSize: MainAxisSize.min, // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // /// HEADER // Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ // Text( // 'Raise Insurance Claim', // style: GoogleFonts.poppins( // fontSize: 20, // fontWeight: FontWeight.w600, // ), // ), // IconButton( // icon: const Icon(Icons.close), // onPressed: () => Navigator.pop(context), // ), // ], // ), // // const SizedBox(height: 20), // // /// FORM // Expanded( // child: SingleChildScrollView( // child: Column( // children: [ // _row([ // _dropdown('Select Policy*'), // _dropdown('Member Name'), // _input('Hospital Name'), // ]), // _row([ // _input('Hospital Address'), // _input('Hospital City'), // _input('Hospital State'), // ]), // _row([ // _input('Hospital Pincode'), // _input('Hospital Phone Number'), // _date('Admit Date'), // ]), // _row([ // _date('Discharge Date'), // _input('Claim Amount'), // _input('Message'), // ]), // // const SizedBox(height: 20), // // /// UPLOAD // _uploadBox(), // ], // ), // ), // ), // // const SizedBox(height: 20), // // /// FOOTER // Align( // alignment: Alignment.centerRight, // child: SizedBox( // width: 120, // height: 42, // child: ElevatedButton( // onPressed: () {}, // style: ElevatedButton.styleFrom( // backgroundColor: const Color(0xFFE26728), // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(10), // ), // ), // child: const Text( // 'Send', // style: TextStyle(fontWeight: FontWeight.w600), // ), // ), // ), // ), // ], // ), // ), // ); // } // // /// ---------- HELPERS ---------- // // Widget _row(List children) { // return Padding( // padding: const EdgeInsets.only(bottom: 14), // child: Row( // children: children // .map((e) => Expanded(child: Padding( // padding: const EdgeInsets.only(right: 12), // child: e, // ))) // .toList(), // ), // ); // } // // Widget _input(String label) { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text(label, style: _labelStyle), // const SizedBox(height: 6), // _box( // TextField( // decoration: const InputDecoration( // border: InputBorder.none, // ), // ), // ), // ], // ); // } // // Widget _dropdown(String label) { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text(label, style: _labelStyle), // const SizedBox(height: 6), // _box( // DropdownButtonHideUnderline( // child: DropdownButton( // isExpanded: true, // hint: const Text('Select'), // items: const [], // onChanged: (_) {}, // ), // ), // ), // ], // ); // } // // Widget _date(String label) { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text(label, style: _labelStyle), // const SizedBox(height: 6), // _box( // TextField( // readOnly: true, // decoration: const InputDecoration( // suffixIcon: Icon(Icons.calendar_today, size: 18), // border: InputBorder.none, // ), // ), // ), // ], // ); // } // // Widget _box(Widget child) { // return Container( // height: 42, // padding: const EdgeInsets.symmetric(horizontal: 12), // decoration: BoxDecoration( // color: const Color(0xFFF1F1F1), // borderRadius: BorderRadius.circular(8), // ), // child: Center(child: child), // ); // } // // Widget _uploadBox() { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text('Upload Documents', style: _labelStyle), // const SizedBox(height: 8), // Container( // height: 120, // width: double.infinity, // decoration: BoxDecoration( // borderRadius: BorderRadius.circular(10), // border: Border.all( // color: const Color(0xFF00A6A6), // style: BorderStyle.solid, // ), // color: const Color(0xFFF9F9F9), // ), // child: Column( // mainAxisAlignment: MainAxisAlignment.center, // children: const [ // Icon(Icons.insert_drive_file, // size: 36, color: Color(0xFF00A6A6)), // SizedBox(height: 6), // Text( // 'Sample_file.PDF', // style: TextStyle(fontWeight: FontWeight.w500), // ), // SizedBox(height: 4), // Text( // '(Supported formats: PDF, PNG, JPG, JPEG, HEIC)', // style: TextStyle(fontSize: 11, color: Colors.grey), // ), // ], // ), // ), // ], // ); // } // } /// ---------- STYLES ---------- final _labelStyle = GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, );