enrollment-app/lib/presentation/RaiseClaimForm.dart
2026-02-05 19:15:46 +05:30

1495 lines
51 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
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<RaiseClaimDialog> createState() => _RaiseClaimDialogState();
}
class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
// const RaiseClaimDialog({super.key});
late ApiService apiService;
bool isLoading = false;
dynamic empPrimaryId;
dynamic empClientId;
dynamic empClientBranchId;
dynamic empHrId;
final tokenService = TokenStorageService();
String? _postPreToken = '';
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;
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<Map<String, dynamic>> employeePolicyList = [];
String? selectedFileNames;
// html.File? uploadedFile;
// List<html.File> uploadedFiles = [];
List<PlatformFile> uploadedFiles = [];
final fileService = FileUploadService();
bool isSubmitting = false;
dynamic getPostCardArrays = [];
List<Map<String, dynamic>> activePoliciesList = [];
int stausVal = 1;
Map<String, dynamic> getClaimPoliciesApi = {};
int? selectedPolicyTypeId;
int? selectedClientPolicyId;
Map<String, dynamic>? selectedMemberObject;
// 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,
};
final Map<int, List<int>> servicePolicyTypeMap = {
1: [2, 3, 4, 5],
2: [1],
3: [6],
4: [7],
};
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;
@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();
}
Future<void> _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<void> getClaimsPoliciesDetails() async {
print('9');
setState(() {
isLoading = true;
});
try {
print('10');
final response = await apiService.getClaimPoliciesToApi(_postPreToken!);
if (response['status'] == 'success') {
setState(() {
isLoading = false;
});
setState(() {
getClaimPoliciesApi = Map<String, dynamic>.from(response['data']);
departmentList = (getClaimPoliciesApi['ticket_type'] as List)
.map<Map<String, dynamic>>((item) => {
'id': int.parse(item['ticket_type'].toString()),
'name': item['type_name'].toString(),
})
.toList();
});
} else {
setState(() {
isLoading = false;
});
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
print('Request failed with status: ${response['code']}');
}
} catch (e) {
setState(() {
// isLoading = false;
});
print('Exception occurred: $e');
} finally {
setState(() {
// _isLoading = false;
});
}
}
Future<void> getPostCashDepositDetails(
empClientBranchId, empClientId, empHrId, _postPreToken) async {
print('IN');
print("clintBranchId -$empClientBranchId");
print("clintID -$empClientId");
print("hr_id -$empHrId");
print("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);
print('IN1');
if (response['status'] == 'success') {
isLoading = false;
setState(() {
print('response');
print(response['data']);
getPostCardArrays = List<Map<String, dynamic>>.from(response['data']);
print('getPostCardArrays');
print(getPostCardArrays);
activePoliciesList = getPostCardArrays;
});
print('IN2');
} else {
isLoading = false;
print('API request failed with status');
setState(() {
activePoliciesList = [];
});
print('API request failed with status');
}
} catch (e) {
print('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<Map<String, dynamic>>((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;
});
}
Future<void> getCDPoliciesDetails() async {
print('9');
setState(() {
isLoading = true;
});
try {
print('10');
final response = await apiService.getEmployeeAndDependenceToApi(
empClientId, selectedClientPolicyId, empClientBranchId, _postPreToken!);
if (response['status'] == 'success') {
final List<Map<String, dynamic>> members =
List<Map<String, dynamic>>.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;
});
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
Future<void> sendFormDataToApi() async {
setState(() {
isServiceValid = serviceId != null;
isPolicyValid = selectedClientPolicyId != null;
isMemberValid = selectedMemberId != null;
// isSubjectValid = subjectController.text.trim().isNotEmpty;
// 🏥 GMC / Topup
final isGmc = serviceId == 1 || serviceId == 4;
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;
isAdmitDateValid = !isGmc || admitDate != null;
isDischargeDateValid = !isGmc || dischargeDate != null;
isClaimAmountValid = !isGmc || claimAmountController.text.trim().isNotEmpty;
// ☠ Accident / Death
final isAccident = [2, 3, 4].contains(serviceId);
isAccidentDateValid = !isAccident || accidentDate != null;
isIntimationDateValid = !isAccident || intimationDate != null;
});
if (isServiceValid &&
isPolicyValid &&
isMemberValid &&
// isSubjectValid &&
isHospitalNameValid &&
isHospitalAddressValid &&
isHospitalStateValid &&
isHospitalCityValid &&
isHospitalPincodeValid &&
isHospitalPhoneNoValid &&
isAdmitDateValid &&
isDischargeDateValid &&
isClaimAmountValid &&
isAccidentDateValid &&
isIntimationDateValid) {
if (FileUploadService().files.isEmpty) {
setState(() {
MultiFileUploadWidget.hasFiles = false;
});
ToastHelper.showErrorToast(context, 'Please upload at least one document');
return;
}
// Proceed to submit
} else {
ToastHelper.showErrorToast(context, 'Please Fill Required Fields');
return;
}
setState(() => isSubmitting = true); // 🔥 start loader
setState(() {
isLoading = true;
});
try {
print('Check One');
Map<String, dynamic> 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['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 {
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'] = 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() ?? ''));
request.fields.addAll(stringFields);
// get uploaded files
final uploadedFiles = FileUploadService().files;
print("📁 Total uploaded files: ${uploadedFiles.length}");
print("📂 File list (original names): ${uploadedFiles.map((f) => f.file.name).toList()}");
// Validate all labels present (optional but recommended)
for (final uf in uploadedFiles) {
print("📝 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');
print('📄 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
print("Payload being sent:");
print("Files: ${uploadedFiles.map((f) => f.file.name).toList()}");
print("Names (JSON): $encodedNames");
final response = await request.send();
final responseBody = await response.stream.bytesToString();
final decoded = jsonDecode(responseBody);
if (decoded['status'] == true) {
// 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);
print('Form data submitted successfully');
fileService.clearAll();
} else {
setState(() {
isLoading = false;
});
Navigator.pop(context);
ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}");
}
} catch (e) {
setState(() {
isLoading = false;
});
print('Error submitting form data: $e');
} finally {
setState(() => isSubmitting = false); // 🔥 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 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: () => 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(() {
serviceId = value;
serviceName = selectedItem['name'];
policyNumberId = null;
isServiceValid = true;
});
print('🔥 serviceId set to $serviceId');
filterPoliciesByService(value!);
},
departmentList,
'name',
serviceId,
),
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,
),
buildDropdownFieldSearch(
'Member Name',
(value) {
final member = employeePolicyList.firstWhere(
(m) => m['id'] == value,
);
setState(() {
selectedMemberId = value;
selectedMemberObject = member; // ✅ FULL OBJECT
selectedMemberName = member['name'];
isMemberValid = true;
print('selectedMemberObject $selectedMemberObject');
});
},
employeePolicyList,
'name',
selectedMemberId,
),
]),
_row([
buildTextField('Message', messageController),
if (serviceId == 1 || serviceId == 72)...[
buildTextField('Hospital Name', hospitalNameController),
buildTextField('Hospital Address', hospitalAddressController),
]
]),
if (serviceId == 1 || serviceId == 72)
_row([
buildTextField('Hospital City', hospitalCityController),
buildTextField('Hospital State', hospitalStateController),
buildTextField(
'Hospital Pincode',
hospitalPinCodeController,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(6),
],
),
]),
if (serviceId == 1 || serviceId == 72)
_row([
buildTextField(
'Hospital Phone No',
hospitalPhoneNoController,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(10),
],
),
buildDatePickerField(
label: 'Admit Date',
selectedDate: admitDate,
allowFuture: false,
onDateSelected: (d) {
setState(() {
admitDate = d;
dischargeDate = null;
});
},
),
buildDatePickerField(
label: 'Discharge Date',
selectedDate: dischargeDate,
allowFuture: true,
minDate: admitDate?.add(const Duration(days: 1)),
onDateSelected: (d) => setState(() => dischargeDate = d),
),
buildTextField(
'Claims Amount',
claimAmountController,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
),
]),
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;
});
},
),
buildDatePickerField(
label: 'Date of Death',
selectedDate: deathDate,
allowFuture: false,
onDateSelected: (d) => setState(() => deathDate = d),
),
]),
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 ? 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 (UNCHANGED)
if (isLoading)
Container(
color: const Color(0x98FFFCE5),
child: Center(
child: Image.asset(
'assets/nhance-loader.gif',
height: 60,
width: 60,
),
),
),
],
),
),
)
);
}
/// ---------- HELPERS ----------
Widget _row(List<Widget> 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 buildTextField(
String label,
TextEditingController controller, {
TextInputType keyboardType = TextInputType.text,
List<TextInputFormatter>? inputFormatters,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel(label),
formBox(
child: TextField(
controller: controller,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
decoration: const InputDecoration(
isDense: true,
border: InputBorder.none,
),
),
),
],
);
}
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<Map<String, dynamic>> itemsList,
String displayField,
int? selectedValue,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel(label),
formBox(
child: DropdownButtonHideUnderline(
child: DropdownButton<int>(
isExpanded: true,
value: selectedValue,
hint: const Text('Select'),
icon: const Icon(Icons.keyboard_arrow_down),
items: itemsList.map<DropdownMenuItem<int>>((item) {
return DropdownMenuItem<int>(
value: item['id'],
child: Text(item[displayField]),
);
}).toList(),
onChanged: onChanged,
),
),
),
],
);
}
Widget buildDropdownFieldSearch(
String label,
void Function(int?) onChanged,
List<Map<String, dynamic>> itemsList,
String displayField,
int? selectedValue,
) {
final TextEditingController searchController = TextEditingController();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel(label),
formBox(
child: DropdownButtonHideUnderline(
child: DropdownButton2<int>(
isExpanded: true,
value: selectedValue,
hint: const Text('Select'),
iconStyleData: const IconStyleData(
icon: Icon(Icons.keyboard_arrow_down),
),
// 🔹 SEARCH CONFIG
dropdownSearchData: DropdownSearchData(
searchController: searchController,
searchInnerWidgetHeight: 50,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: searchController,
decoration: InputDecoration(
hintText: 'Search...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
isDense: true,
),
),
),
searchMatchFn: (item, searchValue) {
return item.value
.toString()
.toLowerCase()
.contains(searchValue.toLowerCase()) ||
item.child
.toString()
.toLowerCase()
.contains(searchValue.toLowerCase());
},
),
// 🔹 CLEAR SEARCH WHEN CLOSED
onMenuStateChange: (isOpen) {
if (!isOpen) searchController.clear();
},
items: itemsList.map((item) {
return DropdownMenuItem<int>(
value: item['id'],
child: Text(
item[displayField],
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
onChanged: onChanged,
),
),
),
],
);
}
Widget buildDatePickerField({
required String label,
required DateTime? selectedDate,
required bool allowFuture,
required ValueChanged<DateTime?> onDateSelected,
DateTime? minDate,
DateTime? maxDate,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel(label),
formBox(
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,
);
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),
),
const Icon(Icons.calendar_today, size: 18),
],
),
),
),
],
);
}
// 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),
);
}
Widget fieldLabel(String text) {
return Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Text(
text,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
);
}
}
// 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<Widget> 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<String>(
// 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,
);