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