This commit is contained in:
Surendiran 2026-03-17 10:55:37 +05:30
parent ad396fefb0
commit 72b253918a
17 changed files with 3949 additions and 2169 deletions

View File

@ -220,7 +220,7 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
);
}).toList(),
if(postModules.isNotEmpty)
if(postModules.isNotEmpty && postModules.contains(5))
_SideItem(
// icon: Icons.dashboard,
icon: SvgPicture.string(

View File

@ -69,6 +69,7 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
late String _verificationId;
dynamic empMobileNo;
dynamic empEmailid;
final tokenService = TokenStorageService();
@override
void initState() {
@ -225,12 +226,53 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
// // Show a Snackbar if the OTP is invalid
// print('Invalid OTP. Please try again');
// }
} else if (response.statusCode == 401) {
setState(() {
_isLoading = false;
});
await tokenService.clearAll(); // 🔐 clears flutter_secure_storage
ToastHelper.showErrorToast(context, 'Session Out');
if (!context.mounted) return;
Navigator.pushNamedAndRemoveUntil(
context,
'hrLogin',
(route) => false,
);
} else if (response.statusCode == 403) {
setState(() {
_isLoading = false;
});
await tokenService.clearAll(); // 🔐 clears flutter_secure_storage
ToastHelper.showErrorToast(context, 'Session Out');
if (!context.mounted) return;
Navigator.pushNamedAndRemoveUntil(
context,
'hrLogin',
(route) => false,
);
} else if (response.statusCode == 451) {
setState(() {
_isLoading = false;
});
final body = jsonDecode(response.body);
final message = body['message'];
ToastHelper.showWarningToast(context, message);
} else if (response.statusCode == 429) {
setState(() {
_isLoading = false;
});
final body = jsonDecode(response.body);
final message = body['message'];
ToastHelper.showWarningToast(context, message);
} else {
setState(() {
_isLoading = false;
});
ToastHelper.showWarningToast(context, 'Something went wrong');
throw Exception('Failed to verify OTP');
throw Exception('Failed to load data');
}
} catch (e) {
setState(() {

View File

@ -1,6 +1,7 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
@ -12,6 +13,7 @@ import 'package:nhancepolicy/responsive.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_animated_button/flutter_animated_button.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:url_launcher/url_launcher.dart';
import 'config/environment.dart';
import 'email_verify.dart';
@ -517,28 +519,34 @@ class _MyPhoneState extends State<MyHrLogin> {
),
SizedBox(height: 20),
Container(
width: double
.infinity, // Make the footer full width
child: Container(
width: double.infinity,
alignment: Alignment.bottomCenter,
padding:
EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.symmetric(vertical: 8),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text:
'By continuing, you agree with our ',
text: 'By continuing, you agree with our ',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 9,
),
children: <TextSpan>[
children: [
TextSpan(
text: 'privacy policy ',
style: GoogleFonts.poppins(
color: Color(0xFFE26828),
color: const Color(0xFFE26828),
fontSize: 9,
// decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = Uri.parse(
'https://nhanceindia.in/privacy-policy/');
if (await canLaunchUrl(url)) {
await launchUrl(url,
mode: LaunchMode.externalApplication);
}
},
),
TextSpan(
text: 'and ',
@ -550,15 +558,67 @@ class _MyPhoneState extends State<MyHrLogin> {
TextSpan(
text: 'terms of use',
style: GoogleFonts.poppins(
color: Color(0xFFE26828),
color: const Color(0xFFE26828),
fontSize: 9,
// decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = Uri.parse(
'https://nhanceindia.in/privacy-policy/');
if (await canLaunchUrl(url)) {
await launchUrl(url,
mode: LaunchMode.externalApplication);
}
},
),
],
),
),
),
),
// Container(
// width: double
// .infinity, // Make the footer full width
// child: Container(
// alignment: Alignment.bottomCenter,
// padding:
// EdgeInsets.symmetric(vertical: 8),
// child: RichText(
// textAlign: TextAlign.center,
// text: TextSpan(
// text:
// 'By continuing, you agree with our ',
// style: GoogleFonts.poppins(
// color: Colors.black,
// fontSize: 9,
// ),
// children: <TextSpan>[
// TextSpan(
// text: 'privacy policy ',
// style: GoogleFonts.poppins(
// color: Color(0xFFE26828),
// fontSize: 9,
// ),
// ),
// TextSpan(
// text: 'and ',
// style: GoogleFonts.poppins(
// color: Colors.black,
// fontSize: 9,
// ),
// ),
// TextSpan(
// text: 'terms of use',
// style: GoogleFonts.poppins(
// color: Color(0xFFE26828),
// fontSize: 9,
// ),
// ),
// ],
// ),
// ),
// ),
// ),
],
),
),

View File

@ -62,25 +62,158 @@ Future<void> startApp() async {
projectId: 'nhance-ee8d1'));
// await dotenv.load(fileName: Environment.fileName);
runApp(MaterialApp(
// runApp(MaterialApp(
// title: 'Nhance HR',
// onGenerateTitle: (context) => "Nhance HR",
// initialRoute: 'hrLogin',
// debugShowCheckedModeBanner: false,
// theme: ThemeData(
// primaryColor: Color(0xFF00999E), // Primary theme color
// scaffoldBackgroundColor: Colors.white,
// colorScheme: ColorScheme.fromSeed(
// seedColor: Color(0xFF00999E),
// ),
// textTheme: GoogleFonts.poppinsTextTheme(),
// elevatedButtonTheme: ElevatedButtonThemeData(
// style: ElevatedButton.styleFrom(
// backgroundColor: Color(0xFF00999E), // Button background
// ),
// ),
// ),
// routes: {
// 'phone': (context) => MyPhone(),
// 'mailVerify': (context) => MyEmailVerify(
// type: '',
// value: '',
//
// ),
// 'verify': (context) => MyVerify(
// verificationId: '',
// mobileNumber: '',
// resendToken: null,
// onResendCode: (String, int) {},
// ),
// 'home': (context) => MyApp(),
// 'hrLogin': (context) => MyHrLogin(),
// // 'hrVerify': (context) => MyHrVerify(
// // verificationId: '',
// // mobileNumber: '',
// // resendToken: null,
// // onResendCode: (String, int) {},
// // ),
// 'hrHome': (context) => MyHrHome(),
// 'preFileUpload': (context) => const preFileUpload(
// ClientId: '',
// policyTypeId: '',
// ClientPoliyId: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// cardType: '',
// cardPolicyNo: '',
// cardInsurer_name: '',
// cardPolicy_name: '',
// cardPolicy_ExpDate: '',
// total_premium: '',
// ),
// 'postFileUpload': (context) => const postFileUpload(
// ClientId: '',
// policyTypeId: '',
// ClientPoliyId: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// cardType: '',
// cardPolicyNo: '',
// cardInsurer_name: '',
// cardPolicy_name: '',
// cardPolicy_ExpDate: '',
// total_premium: '',
// ),
// 'excelErrorScreen': (context) => const excelErrorScreen(
// ClientId: '',
// policy_no: '',
// action: '',
// created_at: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// id: ''
// ),
// 'empDetails': (context) => empDetails(),
// 'addOnsDetails': (context) => addOnsDetails(),
// 'empReviewDetails': (context) => empReviewDetails(),
// 'hrDashboard': (context) => hrDashboard(),
// 'hrPolicyDetails': (context) => hrPolicyDetails(
// ClientId: '',
// policyTypeId: '',
// ClientPoliyId: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// cardType: '',
// cardPolicyNo: '',
// cardInsurer_name: '',
// cardPolicy_name: '',
// cardPolicy_ExpDate: '',
// total_premium: '',
// is_ecard_bulk_download_for_employee: 0,
// ),
// 'oldPolicy': (context) => oldPolicy(),
// 'branchSelection': (context) => BranchSelectionPage(),
// 'policies': (context) => policies(),
// 'CdPoliciesList': (context) => CdPoliciesList(),
// 'ClaimsPolicies': (context) => ClaimsPolicies(
// empCode:'',
// ),
// 'cdTransactionDetails': (context) => cdTransactionDetails(
// insurerName: '',
// cdMasterAccountNo: '',
// insurerId: '',
// cd_ac_pk: '',
// empClientId: '',
// ),
// },
// ));
final tokenService = TokenStorageService();
final token = await tokenService.getCurrentToken();
runApp(MyApp(initialToken: token));
}
class MyApp extends StatelessWidget {
final String? initialToken;
const MyApp({super.key, this.initialToken});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nhance HR',
onGenerateTitle: (context) => "Nhance HR",
initialRoute: 'hrLogin',
debugShowCheckedModeBanner: false,
initialRoute: (initialToken == null || initialToken!.isEmpty)
? 'hrLogin'
: 'hrHome',
theme: ThemeData(
primaryColor: Color(0xFF00999E), // Primary theme color
primaryColor: const Color(0xFF00999E),
scaffoldBackgroundColor: Colors.white,
colorScheme: ColorScheme.fromSeed(
seedColor: Color(0xFF00999E),
seedColor: const Color(0xFF00999E),
),
textTheme: GoogleFonts.poppinsTextTheme(),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF00999E), // Button background
backgroundColor: const Color(0xFF00999E),
),
),
),
routes: {
routes: appRoutes,
);
}
}
final Map<String, WidgetBuilder> appRoutes = {
'phone': (context) => MyPhone(),
'mailVerify': (context) => MyEmailVerify(
type: '',
@ -173,6 +306,38 @@ Future<void> startApp() async {
cd_ac_pk: '',
empClientId: '',
),
},
));
};
/// 🔐 Global Auth Wrapper (Protects All Pages)
class AuthWrapper extends StatefulWidget {
final Widget child;
const AuthWrapper({super.key, required this.child});
@override
State<AuthWrapper> createState() => _AuthWrapperState();
}
class _AuthWrapperState extends State<AuthWrapper> {
@override
void initState() {
super.initState();
_checkLogin();
}
void _checkLogin() {
final token = TokenStorageService().getCurrentToken();
if (token == null || token.isEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushNamedAndRemoveUntil(
context, 'hrLogin', (route) => false);
});
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}

View File

@ -23,8 +23,11 @@ class RaiseClaimDialog extends StatefulWidget {
final BuildContext parentContext;
final VoidCallback onSuccess;
const RaiseClaimDialog({Key? key, required this.parentContext, required this.onSuccess,})
: super(key: key);
const RaiseClaimDialog({
Key? key,
required this.parentContext,
required this.onSuccess,
}) : super(key: key);
@override
State<RaiseClaimDialog> createState() => _RaiseClaimDialogState();
@ -32,6 +35,7 @@ class RaiseClaimDialog extends StatefulWidget {
class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
// const RaiseClaimDialog({super.key});
bool hasSubmitted = false;
late ApiService apiService;
bool isLoading = false;
dynamic empPrimaryId;
@ -86,6 +90,11 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
Map<String, dynamic>? selectedMemberObject;
TextEditingController searchController = TextEditingController();
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;
@ -145,6 +154,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
bool isAdmitDateValid = true;
bool isDischargeDateValid = true;
bool isAccidentService = false;
bool isDeathDateValid = true;
@override
void initState() {
@ -188,6 +198,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
admitDateController.dispose();
dischargeDateController.dispose();
serviceId = null;
claimTypeId = null;
departmentList.clear();
super.dispose();
fileService.clearAll();
@ -210,7 +221,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
});
try {
print('10');
final response = await apiService.getClaimPoliciesToApi(_postPreToken!);
final response = await apiService.getClaimPoliciesToApi(_postPreToken!,empClientId);
if (response['status'] == 'success') {
setState(() {
isLoading = false;
@ -223,6 +234,9 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
'name': item['type_name'].toString(),
})
.toList();
claimTypeMap = Map<String, dynamic>.from(getClaimPoliciesApi['claim_type']);
print('claimTypeMap $claimTypeMap');
});
} else {
setState(() {
@ -319,11 +333,40 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
};
}).toList();
isPolicyValid = true;
});
}
void loadClaimTypes() {
print('serviceId $serviceId');
if (serviceId == null) return;
String key = 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();
print('claimTypeList $claimTypeList');
// // AUTO SELECT FIRST CLAIM TYPE
// if (claimTypeList.isNotEmpty) {
// claimTypeId = claimTypeList.first['id'];
// }
} else {
claimTypeList = [];
claimTypeId = null;
setState(() {});
}
}
Future<void> getCDPoliciesDetails() async {
print('9');
setState(() {
@ -333,7 +376,10 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
print('10');
final response = await apiService.getEmployeeAndDependenceToApi(
empClientId, selectedClientPolicyId, empClientBranchId, _postPreToken!);
empClientId,
selectedClientPolicyId,
empClientBranchId,
_postPreToken!);
if (response['status'] == 'success') {
final List<Map<String, dynamic>> members =
@ -353,9 +399,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
'relationship': m['relationship'],
'gender': m['gender'],
'dob': m['dob'],
'policy_name': serviceId == 1
? null
: m['policy_name'],
'policy_name': serviceId == 1 ? null : m['policy_name'],
};
}).toList();
});
@ -373,60 +417,144 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
}
Future<void> 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 == 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;
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;
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;
// 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) {
// print('serviceId == 2');
// isAccidentDateValid = accidentDate != null;
// isDeathDateValid = true;
// }
// else if (serviceId == 3 || serviceId == 4) {
// print('serviceId == 3 || serviceId == 4');
// isDeathDateValid = deathDate != null;
// isAccidentDateValid = true;
// }
// else {
// print('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 &&
// isSubjectValid &&
isHospitalNameValid &&
isHospitalAddressValid &&
isHospitalStateValid &&
isHospitalCityValid &&
isHospitalPincodeValid &&
isHospitalPhoneNoValid &&
isAdmitDateValid &&
isHospitalPhoneNoValid &&
isDischargeDateValid &&
isClaimAmountValid &&
isAccidentDateValid &&
isDeathDateValid &&
isIntimationDateValid) {
if (FileUploadService().files.isEmpty) {
setState(() {
MultiFileUploadWidget.hasFiles = false;
});
ToastHelper.showErrorToast(context, 'Please upload at least one document');
// 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;
}
// Proceed to submit
if (FileUploadService().files.isEmpty) {
ToastHelper.showErrorToast(
context,
'Please upload at least one document',
);
return;
}
} else {
ToastHelper.showErrorToast(context, 'Please Fill Required Fields');
ToastHelper.showErrorToast(
context,
'Please Fill Required Fields',
);
return;
}
setState(() => isSubmitting = true); // 🔥 start loader
@ -453,16 +581,15 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
'relationship': selectedMemberObject?['relationship'],
'gender': selectedMemberObject?['gender'],
'age': selectedMemberObject?['dob'],
'policy_name': serviceId == 1
? null
: selectedMemberObject?['policy_name'],
'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!);
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;
@ -476,39 +603,54 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
fields['member_name'] = selectedMemberObject?['name'];
fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id'];
} else {
String formattedAccidentDate =
if (accidentDate != null) {
fields['date_of_accident'] =
DateFormat('yyyy-MM-dd').format(accidentDate!);
String formattedDeathDate = DateFormat('yyyy-MM-dd').format(deathDate!);
String formattedBirthDate = DateFormat('yyyy-MM-dd').format(birthDate!);
String formattedIntimationDate =
}
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['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'));
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.headers['APP-SIGNATURE'] =
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
final stringFields =
fields.map((key, value) => MapEntry(key, value?.toString() ?? ''));
print("📁 stringFields: ${stringFields}");
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()}");
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}'");
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');
ToastHelper.showErrorToast(
context, 'Please enter name for all uploaded documents');
setState(() => isLoading = false);
return;
}
@ -581,7 +723,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
}
}
// Combine all names into a JSON array string
final claimDocNames = uploadedFiles.map((uf) => uf.label.trim()).toList();
final encodedNames = jsonEncode(claimDocNames);
@ -592,12 +733,12 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
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) {
resetFormOnServiceChange();
// 1 Close dialog first
Navigator.pop(context);
@ -620,8 +761,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
Navigator.pop(context);
ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}");
}
} catch (e) {
setState(() {
isLoading = false;
@ -713,9 +852,73 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
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 Dialog(
return WillPopScope(
onWillPop: () async {
resetFormOnServiceChange();
return true;
},
child: Dialog(
backgroundColor: Colors.white, // PURE WHITE popup
insetPadding: const EdgeInsets.all(20),
shape: RoundedRectangleBorder(
@ -724,7 +927,8 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.75, // Desktop popup width
width:
MediaQuery.of(context).size.width * 0.75, // Desktop popup width
// height: MediaQuery.of(context).size.height * 0.85,
child: Stack(
children: [
@ -748,7 +952,12 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
onPressed: () => {
setState(() {
resetFormOnServiceChange();
}),
Navigator.pop(context)
},
),
],
),
@ -763,11 +972,13 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
buildDropdownField(
'Service',
(value) {
final selectedItem = departmentList.firstWhere(
final selectedItem =
departmentList.firstWhere(
(item) => item['id'] == value,
orElse: () => {},
);
setState(() {
resetFormOnServiceChange();
serviceId = value;
serviceName = selectedItem['name'];
policyNumberId = null;
@ -775,6 +986,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
});
print('🔥 serviceId set to $serviceId');
filterPoliciesByService(value!);
loadClaimTypes();
},
departmentList,
'name',
@ -785,15 +997,17 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
buildDropdownField(
'Select Policy',
(value) {
final selectedPolicy =
policyNumberList.firstWhere((p) => p['id'] == value);
final selectedPolicy = policyNumberList
.firstWhere((p) => p['id'] == value);
setState(() {
// THIS is what you send to API
selectedClientPolicyId = selectedPolicy['id'];
selectedClientPolicyId =
selectedPolicy['id'];
// optional
selectedPolicyTypeId = selectedPolicy['policy_type_id'];
selectedPolicyTypeId =
selectedPolicy['policy_type_id'];
policyNumberId = value;
isPolicyValid = true;
@ -807,20 +1021,37 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
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(
final member =
employeePolicyList.firstWhere(
(m) => m['id'] == value,
);
setState(() {
selectedMemberId = value;
selectedMemberObject = member; // FULL OBJECT
selectedMemberObject =
member; // FULL OBJECT
selectedMemberName = member['name'];
isMemberValid = true;
print('selectedMemberObject $selectedMemberObject');
print(
'selectedMemberObject $selectedMemberObject');
});
},
employeePolicyList,
@ -829,42 +1060,63 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
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),
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),
buildTextField('Hospital State', hospitalStateController,required: true, isValid: isHospitalStateValid),
buildTextField(
'Hospital Pincode',
'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
),
required: true,
isValid: isHospitalPincodeValid),
]),
if (serviceId == 1 || serviceId == 72)
_row([
buildTextField(
'Hospital Phone No',
buildTextField('Hospital Phone No',
hospitalPhoneNoController,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(10),
],
required: true, isValid: isHospitalPhoneNoValid
required: true,
isValid: !hasSubmitted ||
serviceId == null ||
(serviceId != 1 && serviceId != 72) ||
RegExp(r'^\d{10}$').hasMatch(hospitalPhoneNoController.text.trim()),
),
buildDatePickerField(
label: 'Admit Date',
@ -876,32 +1128,35 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
dischargeDate = null;
});
},
required: true, isValid: isAdmitDateValid
),
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
),
minDate: admitDate
?.add(const Duration(days: 1)),
onDateSelected: (d) =>
setState(() => dischargeDate = d),
required: true,
isValid: isDischargeDateValid),
buildTextField(
'Claims Amount',
claimAmountController,
'Claims Amount', claimAmountController,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
required: true, isValid: isClaimAmountValid
),
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),
onDateSelected: (d) =>
setState(() => birthDate = d),
),
buildDatePickerField(
label: 'Accident Date',
@ -914,13 +1169,16 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
intimationDate = null;
});
},
required: true, isValid: isAccidentDateValid
),
required: serviceId == 2,
isValid: isAccidentDateValid),
buildDatePickerField(
label: 'Date of Death',
selectedDate: deathDate,
allowFuture: false,
onDateSelected: (d) => setState(() => deathDate = d),
onDateSelected: (d) =>
setState(() => deathDate = d),
required: (serviceId == 3 || serviceId == 4) ? true : false, // 🔥 GTLI & EDLI
isValid: isDeathDateValid,
),
]),
if ([2, 3, 4].contains(serviceId))
@ -929,18 +1187,18 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
label: 'Date of Intimation',
selectedDate: intimationDate,
allowFuture: false,
onDateSelected: (d) => setState(() => intimationDate = d),
required: true, isValid: isIntimationDateValid
),
onDateSelected: (d) =>
setState(() => intimationDate = d)),
buildTextField(
'Sum Insured',
sumInsuredController,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
),
const SizedBox(),
]),
_row([
MultiFileUploadWidget(),
]),
@ -950,7 +1208,9 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
width: 120,
height: 42,
child: ElevatedButton(
onPressed: isSubmitting ? null : sendFormDataToApi,
onPressed: (isSubmitting || serviceId == null)
? null
: sendFormDataToApi,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE26728),
shape: RoundedRectangleBorder(
@ -968,7 +1228,8 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
)
: const Text(
'Send',
style: TextStyle(color: Colors.white,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600),
),
),
@ -982,10 +1243,14 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
),
),
/// 🔄 LOADER OVERLAY (UNCHANGED)
/// 🔥 LOADER OVERLAY
if (isLoading)
Container(
color: const Color(0x98FFFCE5),
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',
@ -994,12 +1259,11 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
),
),
),
),
],
),
),
)
);
)));
}
/// ---------- HELPERS ----------
@ -1058,7 +1322,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
);
}
Widget buildTextField(
String label,
TextEditingController controller, {
@ -1087,7 +1350,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
);
}
Widget buildTextAreaField(String label, TextEditingController controller) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -1168,9 +1430,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
);
}
Widget buildDropdownFieldSearch(
String label,
void Function(int?) onChanged,
@ -1180,7 +1439,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
bool required = false,
bool isValid = true,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1245,10 +1503,8 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
orElse: () => {},
);
final text = matchedItem[displayField]
?.toString()
.toLowerCase() ??
'';
final text =
matchedItem[displayField]?.toString().toLowerCase() ?? '';
return text.contains(searchValue.toLowerCase());
},
@ -1279,7 +1535,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
);
}
Widget buildDatePickerField({
required String label,
required DateTime? selectedDate,
@ -1294,7 +1549,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel(label, required: required),
Container(
height: 42,
padding: const EdgeInsets.symmetric(horizontal: 12),
@ -1320,6 +1574,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
initialDate: initialDate,
firstDate: first,
lastDate: last,
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (picked != null) {
@ -1335,18 +1590,29 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
: '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,
@ -1380,7 +1646,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
child: Center(child: child),
);
}
}
// class RaiseClaimDialog extends StatelessWidget {

View File

@ -73,11 +73,12 @@ class _CdPoliciesListState extends State<CdPoliciesList> {
}
Future<void> checkIds() async {
print('checkIds');
_postPreToken = await tokenService.getCurrentToken();
empClientId = await tokenService.readValue('empClientId');
// empClientBranchId = await tokenService.readValue('empClientBranchId');
empHrId = await tokenService.readValue('empHrId');
print('$_postPreToken - $empClientId - $empHrId');
await getCDPoliciesDetails(empClientId, empHrId, _postPreToken);
}
@ -165,7 +166,7 @@ class _CdPoliciesListState extends State<CdPoliciesList> {
rows.add([
item['insurer_name'] ?? '',
item['cd_master_account_no'] ?? '',
'${item['balance'] ?? '0'}',
'${item['balance'] ?? '0'}',
]);
}
@ -393,6 +394,30 @@ class _CdPoliciesListState extends State<CdPoliciesList> {
return InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () async {
await tokenService.writeValue(
'hr_cd_insurer_name',
filteredData[index]['insurer_name'].toString(),
);
await tokenService.writeValue(
'hr_cd_master_account_no',
filteredData[index]['cd_master_account_no'].toString(),
);
await tokenService.writeValue(
'hr_cd_insurer_id',
filteredData[index]['insurer_id'].toString(),
);
await tokenService.writeValue(
'hr_cd_ac_pk',
filteredData[index]['cd_ac_pk'].toString(),
);
await tokenService.writeValue(
'hr_empClientId',
empClientId.toString(),
);
Navigator.push(
context,
@ -462,7 +487,8 @@ class _CDPolicyCard extends StatelessWidget {
Expanded(
flex: 3,
child: Text(
"${balance.toStringAsFixed(0)}",
data['balance'] ?? '',
// "${balance.toStringAsFixed(0)}",
textAlign: TextAlign.right,
style: GoogleFonts.poppins(
fontSize: 14,

View File

@ -1,7 +1,6 @@
import 'dart:convert';
import 'package:csv/csv.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -13,12 +12,12 @@ import 'package:nhancepolicy/service/api_service.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:universal_html/html.dart' as html;
import 'package:collection/collection.dart';
import 'package:url_launcher/url_launcher.dart';
import '../config/environment.dart';
import '../customAppBar/base_layout.dart';
import '../customAppBar/customAppBar.dart';
import '../customAppBar/customFooter.dart';
import 'cdList.dart';
import 'claims.dart';
class cdTransactionDetails extends StatefulWidget {
final String insurerName;
@ -42,6 +41,14 @@ class cdTransactionDetails extends StatefulWidget {
}
class _cdTransactionDetailsState extends State<cdTransactionDetails> {
String? localInsurerId;
String? localCdAcPk;
String? localEmpClientId;
String? localInsurerName;
String? localCdMasterAccountNo;
final tokenService = TokenStorageService();
Uint8List? fileBytes;
List<Map<String, dynamic>> getCDTransData = [];
@ -85,7 +92,9 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
void initState() {
super.initState();
apiService = ApiService(context); // Initialize ApiService here
getCdTransactionDetails();
restoreTransactionData();
}
@override
@ -93,9 +102,51 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
super.dispose();
}
// downloadPolicyFiles?file_id=13
// getPolicyAndEndorsementFiles?cd_ac_pk=12
Future<void> restoreTransactionData() async {
localInsurerId = widget.insurerId.trim().isNotEmpty
? widget.insurerId
: await tokenService.readValue('hr_cd_insurer_id');
localCdAcPk = widget.cd_ac_pk.trim().isNotEmpty
? widget.cd_ac_pk
: await tokenService.readValue('hr_cd_ac_pk');
localEmpClientId = widget.empClientId.trim().isNotEmpty
? widget.empClientId
: await tokenService.readValue('hr_empClientId');
localInsurerName = widget.insurerName.trim().isNotEmpty
? widget.insurerName
: await tokenService.readValue('hr_cd_insurer_name');
localCdMasterAccountNo = widget.cdMasterAccountNo.trim().isNotEmpty
? widget.cdMasterAccountNo
: await tokenService.readValue('hr_cd_master_account_no');
print('restore localInsurerId = $localInsurerId');
print('restore localCdAcPk = $localCdAcPk');
print('restore localEmpClientId = $localEmpClientId');
if (localInsurerId != null &&
localCdAcPk != null &&
localEmpClientId != null) {
getCdTransactionDetails();
}
}
Future<void> clearPolicyStorage() async {
await tokenService.removeValue('hr_cd_insurer_id');
await tokenService.removeValue('hr_cd_ac_pk');
await tokenService.removeValue('hr_empClientId');
await tokenService.removeValue('hr_cd_insurer_name');
await tokenService.removeValue('hr_cd_master_account_no');
}
Future<void> getCdTransactionDetails() async {
print('9');
setState(() {
@ -104,8 +155,8 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getCdTransactionData(widget.empClientId,
widget.insurerId, widget.cd_ac_pk, _postPreToken!);
final response = await apiService.getCdTransactionData(localEmpClientId!,
localInsurerId!, localCdAcPk!, _postPreToken!);
if (response['status'] == 'success') {
setState(() {
isLoading = false;
@ -115,11 +166,16 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
List<Map<String, dynamic>>.from(response['data']['deposit_data']);
originalData = getCDTransData;
filteredData = List.from(originalData);
total_deposit = formatAmount(response['data']['total_deposit']);
total_consumed = formatAmount(response['data']['total_consumed']);
total_refund = formatAmount(response['data']['total_refund']);
currect_balance = formatAmount(response['data']['currect_balance']);
insurer_short_name = formatAmount(response['data']['insurer_short_name']);
total_deposit = response['data']['total_deposit'];
// total_deposit = formatAmount(response['data']['total_deposit']);
total_consumed = response['data']['total_consumed'];
// total_consumed = formatAmount(response['data']['total_consumed']);
total_refund = response['data']['total_refund'];
// total_refund = formatAmount(response['data']['total_refund']);
currect_balance = response['data']['currect_balance'];
// currect_balance = formatAmount(response['data']['currect_balance']);
insurer_short_name = response['data']['insurer_short_name'];
// insurer_short_name = formatAmount(response['data']['insurer_short_name']);
print('filteredData');
print(filteredData);
});
@ -144,25 +200,53 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
}
}
Future<void> _openEndorsementFile(id) async {
print('9');
try {
print('10');
Future<void> _openEndorsementFile(id, file_name) async {
// final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token);
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getOpenEndorsementFileData(id, _postPreToken!);
if (response['status'] == false) {
ToastHelper.showErrorToast(context, response['message']);
} else {
print("**********-------*****");
final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/downloadPolicyFiles?file_id=$id';
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
print('Request failed with status: ${response['code']}');
}
final response = await http.get(
Uri.parse(url),
headers: {
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $_postPreToken',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
if (response.statusCode == 200) {
try {
print("PDF Downloaded");
// Create a blob from the response body bytes
final blob = html.Blob([response.bodyBytes]);
// Generate a download URL
final url = html.Url.createObjectUrlFromBlob(blob);
// Trigger file download automatically
final anchor = html.AnchorElement(href: url)
..setAttribute('download', '$file_name')
..click();
// Revoke the URL to free memory
html.Url.revokeObjectUrl(url);
ToastHelper.showSuccessToast(context, 'File Downloaded Successfully');
} catch (e) {
print('Exception occurred: $e');
throw Exception('Error parsing response: $e');
}
} else {
ToastHelper.showErrorToast(context, 'Failed to download');
print("Download failed with status: ${response.statusCode}");
}
}
Future<void> getCdEndorsementDetails(id) async {
print('9');
setState(() {
@ -243,7 +327,7 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
),
onTap: () {
// Navigator.pop(context); // close popup
_openEndorsementFile(file['id']);
_openEndorsementFile(file['id'],file['file_name']);
},
);
},
@ -387,9 +471,12 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
: '-',
item['endorsement_no'] ?? '',
item['sub_type_text'] ?? '',
item['transaction_type'] == 'Credit' ? '${formatAmount(item['amount'])}' : '-',
item['transaction_type'] == 'Debit' ? '${formatAmount(item['amount'])}' : '-',
'${formatAmount(item['balance']) ?? '0'}',
item['transaction_type'] == 'Credit' ? '${item['amount']}' : '-',
// item['transaction_type'] == 'Credit' ? '${formatAmount(item['amount'])}' : '-',
item['transaction_type'] == 'Debit' ? '${item['amount']}' : '-',
// item['transaction_type'] == 'Debit' ? '${formatAmount(item['amount'])}' : '-',
'${item['balance'] ?? '0'}',
// '${formatAmount(item['balance']) ?? '0'}',
item['description'] ?? '',
item['username'] ?? '',
]);
@ -541,7 +628,21 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
children: [
IconButton(
tooltip: 'Previous Page',
onPressed: () => Navigator.pop(context),
onPressed: () async {
await clearPolicyStorage();
if (Navigator.canPop(context)) {
Navigator.pop(context);
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'cdPoliciesList'),
builder: (_) => CdPoliciesList(),
),
);
}
},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
@ -552,7 +653,7 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
),
const SizedBox(width: 6),
Text(
'Transaction Details - ${insurer_short_name} (${widget.cdMasterAccountNo})',
'Transaction Details - ${insurer_short_name} (${localCdMasterAccountNo ?? widget.cdMasterAccountNo})',
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w500,
@ -762,21 +863,36 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
SizedBox(width: 10),
_cell(
item['transaction_type'] == 'Credit'
? formatAmount(item['amount'])
? item['amount']
: '-',
2,
alignRight: true,
),
// _cell(
// item['transaction_type'] == 'Credit'
// ? formatAmount(item['amount'])
// : '-',
// 2,
// alignRight: true,
// ),
SizedBox(width: 10),
_cell(
item['transaction_type'] == 'Debit'
? formatAmount(item['amount'])
? item['amount']
: '-',
2,
alignRight: true,
),
// _cell(
// item['transaction_type'] == 'Debit'
// ? formatAmount(item['amount'])
// : '-',
// 2,
// alignRight: true,
// ),
SizedBox(width: 10),
_cell(formatAmount(item['balance']), 2, alignRight: true),
_cell(item['balance'], 2, alignRight: true),
// _cell(formatAmount(item['balance']), 2, alignRight: true),
SizedBox(width: 10),
_cell(item['description'], 3),
SizedBox(width: 10),
@ -784,25 +900,72 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
SizedBox(width: 10),
Expanded(
flex: 2,
child: SizedBox(
height: 36,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (isAllowedSubType)
_ActionIconButton(
/// --- PDF ICON SLOT ---
SizedBox(
width: 36,
height: 36,
child: Visibility(
visible: isAllowedSubType,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: _ActionIconButton(
icon: Icons.picture_as_pdf_outlined,
toolTip: 'View Endorsement PDF',
onTap: () => getCdEndorsementDetails(item['id']),
),
),
),
const SizedBox(width: 8),
if (isAllowedSubType && hasSplitUpFile)
_ActionIconButton(
/// --- FOLDER ICON SLOT ---
SizedBox(
width: 36,
height: 36,
child: Visibility(
visible: isAllowedSubType && hasSplitUpFile,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: _ActionIconButton(
icon: Icons.folder_open_outlined,
toolTip: 'View Files',
onTap: () => _launchURL(item['split_up_url']),
),
),
),
],
),
),
),
// Expanded(
// flex: 2,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// if (isAllowedSubType)
// _ActionIconButton(
// icon: Icons.picture_as_pdf_outlined,
// toolTip: 'View Endorsement PDF',
// onTap: () => getCdEndorsementDetails(item['id']),
// ),
// const SizedBox(width: 8),
// if (isAllowedSubType && hasSplitUpFile)
// _ActionIconButton(
// icon: Icons.folder_open_outlined,
// toolTip: 'View Files',
// onTap: () => _launchURL(item['split_up_url']),
// ),
// ],
// ),
// ),
],
),
);

View File

@ -66,6 +66,8 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
return filteredData.sublist(startIndex, endIndex);
}
int? appliedClaimStatus;
Color getStatusColor(String status) {
switch (status.toLowerCase()) {
case 'claim received':
@ -182,7 +184,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
});
try {
print('10');
final response = await apiService.getClaimPoliciesToApi(_postPreToken!);
final response = await apiService.getClaimPoliciesToApi(_postPreToken!,'');
if (response['status'] == 'success') {
setState(() {
isLoading = false;
@ -272,6 +274,9 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
final data = claim_Detials();
print("data -- $data");
getClaimList();
setState(() {
appliedClaimStatus = selectedClaimStatus; // apply only after API call
});
}
Future<void> reset() async {
@ -285,6 +290,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
selectedClaimStatus = null;
selectedClaimStatusName = null;
_currentPage = 1;
appliedClaimStatus = null;
getClaimList();
});
@ -482,16 +488,19 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
/// 🔙 Back + Title (LEFT)
Row(
children: [
// IconButton(
// onPressed: () => {},
// icon: const Icon(
// Icons.arrow_back_ios,
// size: 18,
// color: Colors.black,
// ),
// padding: EdgeInsets.zero,
// constraints: const BoxConstraints(),
// ),
if(widget.empCode.isNotEmpty)
IconButton(
onPressed: () => {
Navigator.of(context).pop(),
},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
color: Colors.black,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 6),
Text(
'Claims',
@ -707,11 +716,6 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
);
}
Widget _buildDataRow(Map<String, dynamic> item) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
@ -866,21 +870,28 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
if (statusMaster.isEmpty) return const SizedBox();
final List<dynamic> displayStatuses = appliedClaimStatus != null
? statusMaster.where((status) =>
int.parse(status['id'].toString()) == appliedClaimStatus)
.toList()
: statusMaster;
return SizedBox(
height: 30,
height: 35,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: statusMaster.length,
itemCount: displayStatuses.length,
separatorBuilder: (_, __) => const SizedBox(width: 10),
itemBuilder: (context, index) {
final statusName =
statusMaster[index]['claim_status']?.toString() ?? '';
displayStatuses[index]['claim_status']?.toString() ?? '';
final count = getStatusCount(statusName);
final color = getStatusColor(statusName);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(6),

View File

@ -34,6 +34,9 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
final tokenService = TokenStorageService();
final FocusNode _policyFocusNode = FocusNode();
bool isTpaDashboardEnabled = false;
bool isTpaSelected = false;
// Variables for API data
dynamic empClientBranchId;
dynamic empHrId;
@ -41,6 +44,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
String? _postPreToken = '';
int stausVal = 1;
bool _isPolicyDropdownOpen = false;
html.IFrameElement? _currentIframe;
@override
void initState() {
@ -114,17 +118,32 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
_metabaseLoaded = false;
});
// REMOVE OLD IFRAME COMPLETELY
_currentIframe?.remove();
_currentIframe = null;
// CLEAR OLD VIEW TYPES
_registeredViewTypes.clear();
final response = await apiService.postHrDashboard({
"client_id": empClientId,
"client_policy_id": clientPolicyId,
}, _postPreToken);
if (response['status'] == 'success') {
// ADD THIS LINE
isTpaDashboardEnabled = response['is_tpa_dashboard_enable'] == true;
setState(() {
isTpaSelected = false;
});
_registerMetabaseIframe(
token: response['data']['metabaseToken'],
url: response['data']['metabaseUrl'],
clientPolicyId: clientPolicyId,
);
setState(() => _metabaseLoaded = true);
} else {
ToastHelper.showErrorToast(context, response['message']);
@ -136,26 +155,74 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
}
}
Future<void> _loadTpaDashboard(String clientPolicyId) async {
try {
setState(() {
isDashboardLoading = true;
_metabaseLoaded = false;
});
// REMOVE OLD IFRAME
_currentIframe?.remove();
_currentIframe = null;
_registeredViewTypes.clear();
final response = await apiService.postHrTpaDashboard({
"client_id": empClientId,
"client_policy_id": clientPolicyId,
}, _postPreToken);
if (response['status'] == 'success') {
setState(() {
isTpaSelected = true;
});
_registerMetabaseIframe(
token: response['data']['metabaseToken'],
url: response['data']['metabaseUrl'],
clientPolicyId: clientPolicyId,
);
setState(() => _metabaseLoaded = true);
} else {
ToastHelper.showErrorToast(context, response['message']);
}
} catch (e) {
ToastHelper.showErrorToast(context, 'TPA Dashboard loading failed');
} finally {
setState(() => isDashboardLoading = false);
}
}
void _registerMetabaseIframe({
required String token,
required String url,
required String clientPolicyId,
}) {
final viewType = 'metabase-dashboard-$clientPolicyId';
// 🔥 ALWAYS CREATE UNIQUE VIEW TYPE
final viewType =
'metabase-dashboard-${clientPolicyId}-${DateTime.now().millisecondsSinceEpoch}';
_dashboardViewType = viewType;
if (_registeredViewTypes.contains(viewType)) return;
final embedUrl =
"$url/embed/dashboard/$token"
"#theme=light&bordered=true&titled=true"
"&v=${DateTime.now().millisecondsSinceEpoch}"; // 🔥 cache buster
final embedUrl = "$url/embed/dashboard/$token#theme=light&bordered=true&titled=true";
ui.platformViewRegistry.registerViewFactory(
viewType,
(int viewId) => html.IFrameElement()
final iframe = html.IFrameElement()
..src = embedUrl
..style.border = 'none'
..style.width = '100%'
..style.height = '100%'
..allowFullscreen = true,
..allowFullscreen = true;
_currentIframe = iframe;
ui.platformViewRegistry.registerViewFactory(
viewType,
(int viewId) => iframe,
);
_registeredViewTypes.add(viewType);
@ -185,7 +252,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
Widget _buildDashboardView() {
if (postModules.isNotEmpty && activePoliciesList.isEmpty) {
return const Center(child: Text('No dashboard data available for your account'));
return const Center(child: Text('No active policy found.'));
}
return Padding(
@ -339,6 +406,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
color: Colors.white,
child: Row(
children: [
if (!isTpaSelected)...[
const Text(
'Select Policy',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13),
@ -429,6 +497,37 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
),
),
],
const Spacer(),
if (isTpaDashboardEnabled)
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: isTpaSelected
? Colors.teal
: Colors.grey.shade300,
foregroundColor:
isTpaSelected ? Colors.white : Colors.black,
),
onPressed: () {
if (selectedPolicyId == null) return;
if (isTpaSelected) {
// 🔄 Switch BACK to Normal Dashboard
_loadDashboardByPolicy(selectedPolicyId!);
} else {
// 🔄 Switch TO TPA Dashboard
_loadTpaDashboard(selectedPolicyId!);
}
},
child: Text(
isTpaSelected
? "Insights from Nhance"
: "Insights from TPA ",
),
),
],
),
);
}

File diff suppressed because it is too large Load Diff

View File

@ -106,11 +106,56 @@ class _policiesState extends State<policies>
}
}
Future<void> _loadToken() async {
final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]"
final postRaw = await tokenService.readValue('empAllowed_modules'); // "[2,3,4]"
// Future<void> _loadToken() async {
// final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]"
// final postRaw = await tokenService.readValue('empAllowed_modules'); // "[2,3,4]"
//
// // Decode safely
// enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
// ? List<int>.from(jsonDecode(enrollmentRaw))
// : [];
//
// postModules = postRaw != null && postRaw.isNotEmpty
// ? List<int>.from(jsonDecode(postRaw))
// : [];
//
// print('enrollmentModules $enrollmentModules');
// print('postModules $postModules');
//
// _postPreToken = await tokenService.getCurrentToken();
// print(_postPreToken);
//
// if (enrollmentModules.contains(1)) {
// enrollmentClient_id = await tokenService.readValue('enrollmentClient_id');
// enrollmentEmpClientBranchId =
// await tokenService.readValue('enrollmentEmpClientBranchId');
// enrollmentHrId = await tokenService.readValue('enrollmentHrId');
//
// await getPreCashDepositDetails(enrollmentEmpClientBranchId,
// enrollmentClient_id, enrollmentHrId, _postPreToken);
// }
//
// if (postModules.contains(2)) {
// empClientId = await tokenService.readValue('empClientId');
// empClientBranchId = await tokenService.readValue('empClientBranchId');
// empHrId = await tokenService.readValue('empHrId');
//
// await getPostCashDepositDetails(empClientBranchId, empClientId, empHrId, _postPreToken);
// }
//
// }
Future<void> _loadToken() async {
setState(() {
isLoading = true; // 🔥 START LOADER HERE
});
try {
final enrollmentRaw =
await tokenService.readValue('enrollmentAllowed_modules');
final postRaw =
await tokenService.readValue('empAllowed_modules');
// Decode safely
enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
? List<int>.from(jsonDecode(enrollmentRaw))
: [];
@ -119,30 +164,56 @@ class _policiesState extends State<policies>
? List<int>.from(jsonDecode(postRaw))
: [];
print('enrollmentModules $enrollmentModules');
print('postModules $postModules');
_postPreToken = await tokenService.getCurrentToken();
print(_postPreToken);
List<Future> apiCalls = [];
/// 👇 Add APIs dynamically
if (enrollmentModules.contains(1)) {
enrollmentClient_id = await tokenService.readValue('enrollmentClient_id');
enrollmentClient_id =
await tokenService.readValue('enrollmentClient_id');
enrollmentEmpClientBranchId =
await tokenService.readValue('enrollmentEmpClientBranchId');
enrollmentHrId = await tokenService.readValue('enrollmentHrId');
enrollmentHrId =
await tokenService.readValue('enrollmentHrId');
await getPreCashDepositDetails(enrollmentEmpClientBranchId,
enrollmentClient_id, enrollmentHrId, _postPreToken);
apiCalls.add(
getPreCashDepositDetails(
enrollmentEmpClientBranchId,
enrollmentClient_id,
enrollmentHrId,
_postPreToken,
),
);
}
if (postModules.contains(2)) {
empClientId = await tokenService.readValue('empClientId');
empClientBranchId = await tokenService.readValue('empClientBranchId');
empClientBranchId =
await tokenService.readValue('empClientBranchId');
empHrId = await tokenService.readValue('empHrId');
await getPostCashDepositDetails(empClientBranchId, empClientId, empHrId, _postPreToken);
apiCalls.add(
getPostCashDepositDetails(
empClientBranchId,
empClientId,
empHrId,
_postPreToken,
),
);
}
/// 🔥 WAIT FOR ALL APIs
await Future.wait(apiCalls);
} catch (e) {
print("Error in _loadToken: $e");
} finally {
if (mounted) {
setState(() {
isLoading = false; // 🔥 STOP LOADER ONLY ONCE
});
}
}
}
Future<void> getPreCashDepositDetails(enrollmentEmpClientBranchId,
@ -153,7 +224,6 @@ class _policiesState extends State<policies>
print("hr_id -$enrollmentHrId");
print("token -$_postPreToken");
isLoading = true;
// setState(() {
// _isLoading = true;
// });
@ -171,7 +241,6 @@ class _policiesState extends State<policies>
// clintID!, clintBranchId!, hr_id, token);
print('IN1');
if (response['status'] == 'success') {
isLoading = false;
setState(() {
print('response');
print(response['data']);
@ -198,7 +267,6 @@ class _policiesState extends State<policies>
print("hr_id -$empHrId");
print("token -$_postPreToken");
isLoading = true;
// setState(() {
// _isLoading = true;
// });
@ -213,7 +281,6 @@ class _policiesState extends State<policies>
// clintID!, clintBranchId!, hr_id, token);
print('IN1');
if (response['status'] == 'success') {
isLoading = false;
setState(() {
print('response');
print(response['data']);
@ -226,7 +293,6 @@ class _policiesState extends State<policies>
print('IN2');
} else {
isLoading = false;
print('API request failed with status');
setState(() {
activePoliciesList = [];
@ -345,7 +411,16 @@ class _policiesState extends State<policies>
required List<Map<String, dynamic>> openEnrollment,
required List<Map<String, dynamic>> activePolicies,
}) {
return Scaffold(
return isLoading ? Container(
color: Colors.transparent, // 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
),
) : Scaffold(
body: SingleChildScrollView(
// padding: const EdgeInsets.all(20),
@ -385,12 +460,15 @@ class _policiesState extends State<policies>
SizedBox(height: 15),
Container(
width: double.infinity,
height: 400,
// constraints: const BoxConstraints(
// minHeight: 180,
// ),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -401,23 +479,26 @@ class _policiesState extends State<policies>
SizedBox(height: 14),
/// SCROLLABLE AREA
Expanded(
child: openEnrollment.isEmpty
openEnrollment.isEmpty
? _EmptyBox('No policies open for enrollment')
: _PolicyGrid(
policies: openEnrollment,
isEnrollment: true,
),
),
],
),
)
),
],
if(postModules.contains(2))...[
SizedBox(height: 20),
Container(
width: double.infinity,
height: 400,
// constraints: const BoxConstraints(
// minHeight: 180,
// ),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
@ -454,15 +535,14 @@ class _policiesState extends State<policies>
const SizedBox(height: 14),
/// SCROLLABLE GRID
Expanded(
child: activePolicies.isEmpty
activePolicies.isEmpty
? stausVal == 0 ? _EmptyBox('You dont have any expired policies at the moment.') : _EmptyBox('No active policies found')
: _PolicyGrid(
policies: activePolicies,
isEnrollment: false,
onBulkDownload: getEcardBulkDownload,
),
),
const SizedBox(height: 8),
Align(
@ -513,31 +593,62 @@ class _PolicyGrid extends StatelessWidget {
) {
final width = MediaQuery.of(context).size.width;
if (width < 600) {
return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15);
} else if (width < 900) {
return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45);
} else if (width < 1400) {
return ResponsiveGridConfig(3, isEnrollment ? 3.1 : 2.5);
// if (width < 600) {
// return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15);
// } else if (width < 900) {
// return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45);
// } else if (width < 1400) {
// return ResponsiveGridConfig(3, isEnrollment ? 3.1 : 2.5);
// } else {
// return ResponsiveGridConfig(4, isEnrollment ? 3.1 : 2.4);
// }
if (width >= 1400) {
return const ResponsiveGridConfig(4, 2.6); // Big screen
} else if (width >= 1000) {
return const ResponsiveGridConfig(3, 2.3); // Small desktop
} else if (width >= 600) {
return const ResponsiveGridConfig(2, 2.0); // Tablet
} else {
return ResponsiveGridConfig(4, isEnrollment ? 3.1 : 2.4);
return const ResponsiveGridConfig(1, 1.8); // Mobile
}
}
@override
@override
Widget build(BuildContext context) {
final tokenService = TokenStorageService();
final config = _getGridConfig(context, isEnrollment);
return GridView.builder(
final int rowCount = (policies.length / config.crossAxisCount).ceil();
double cardHeight;
if (isEnrollment) {
cardHeight = 140;
} else {
cardHeight = 170;
}
final double totalHeight =
rowCount * cardHeight + ((rowCount - 1) * 16);
return SizedBox(
height: totalHeight,
child: GridView.builder(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: config.crossAxisCount,
childAspectRatio: config.childAspectRatio,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
mainAxisExtent: isEnrollment ? 120 : 150, // fixed card height
),
itemCount: policies.length,
itemBuilder: (context, index) {
@ -590,12 +701,15 @@ class _PolicyGrid extends StatelessWidget {
onBulkDownload: onBulkDownload,
onTap: () async {
final token = await tokenService.getCurrentToken();
final clientId =
await tokenService.readValue('empClientId');
final branchId =
await tokenService.readValue('empClientBranchId');
final clientId = await tokenService.readValue('empClientId');
final branchId = await tokenService.readValue('empClientBranchId');
print("token: $token");
print("clientId: $clientId");
print("branchId: $branchId");
if (token == null || clientId == null || branchId == null) {
print("Missing required values");
return;
}
@ -630,6 +744,7 @@ class _PolicyGrid extends StatelessWidget {
},
);
},
),
);
}
}

View File

@ -51,8 +51,7 @@ class postFileUpload extends StatefulWidget {
required this.cardInsurer_name,
required this.cardPolicy_name,
required this.cardPolicy_ExpDate,
required this.total_premium
})
required this.total_premium})
: super(key: key);
@override
@ -61,6 +60,23 @@ class postFileUpload extends StatefulWidget {
class _postFileUploadState extends State<postFileUpload> {
final tokenService = TokenStorageService();
String localClientId = '';
String localPolicyTypeId = '';
String localClientPolicyId = '';
String localClientBranchId = '';
String localToken = '';
String localTokenType = '';
String localCardType = '';
String localCardPolicyNo = '';
String localCardInsurerName = '';
String localCardPolicyName = '';
String localCardPolicyExpDate = '';
String localTotalPremium = '';
Uint8List? fileBytes;
Uint8List? fileBytes2;
late String _token;
@ -101,9 +117,8 @@ class _postFileUploadState extends State<postFileUpload> {
bool showSampleButton = false;
String? currentApiValue; // To store the 'value' for the 2nd param
int _currentPage = 1;
int _rowsPerPage = 5;
int _rowsPerPage = 6;
List<dynamic> get _paginatedData {
final startIndex = (_currentPage - 1) * _rowsPerPage;
@ -125,9 +140,12 @@ class _postFileUploadState extends State<postFileUpload> {
void initState() {
super.initState();
apiService = ApiService(context);
restoreUploadData().then((_) {
_loadToken();
getFileUploadMasterDetails();
getFileListDetails();
});
}
@override
@ -137,22 +155,85 @@ class _postFileUploadState extends State<postFileUpload> {
}
Future<void> _loadToken() async {
// final token = prefs.getString('hrtoken');
final token = widget.Token;
final token = localToken.isNotEmpty
? localToken
: await tokenService.readValue('upload_Token');
if (token != null && token.isNotEmpty) {
setState(() {
_token = token;
});
Map<String, dynamic>? decodedToken = Jwt.parseJwt(token);
print('decodedToken $decodedToken');
} else {
// Token is empty or null, handle accordingly (e.g., navigate to login screen)
// For now, let's navigate to the login screen
ToastHelper.showErrorToast(context, 'Session Out');
Navigator.pushReplacementNamed(context, 'hrLogin');
}
}
Future<void> restoreUploadData() async {
localClientId = widget.ClientId.isNotEmpty
? widget.ClientId
: await tokenService.readValue('upload_ClientId') ?? '';
localPolicyTypeId = widget.policyTypeId.isNotEmpty
? widget.policyTypeId
: await tokenService.readValue('upload_policyTypeId') ?? '';
localClientPolicyId = widget.ClientPoliyId.isNotEmpty
? widget.ClientPoliyId
: await tokenService.readValue('upload_ClientPoliyId') ?? '';
localClientBranchId = widget.clientBranchId.isNotEmpty
? widget.clientBranchId
: await tokenService.readValue('upload_clientBranchId') ?? '';
localToken = widget.Token.isNotEmpty
? widget.Token
: await tokenService.readValue('upload_Token') ?? '';
localTokenType = widget.TokenType.isNotEmpty
? widget.TokenType
: await tokenService.readValue('upload_TokenType') ?? '';
localCardType = widget.cardType.isNotEmpty
? widget.cardType
: await tokenService.readValue('upload_cardType') ?? '';
localCardPolicyNo = widget.cardPolicyNo.isNotEmpty
? widget.cardPolicyNo
: await tokenService.readValue('upload_cardPolicyNo') ?? '';
localCardInsurerName = widget.cardInsurer_name.isNotEmpty
? widget.cardInsurer_name
: await tokenService.readValue('upload_cardInsurer_name') ?? '';
localCardPolicyName = widget.cardPolicy_name.isNotEmpty
? widget.cardPolicy_name
: await tokenService.readValue('upload_cardPolicy_name') ?? '';
localCardPolicyExpDate = widget.cardPolicy_ExpDate.isNotEmpty
? widget.cardPolicy_ExpDate
: await tokenService.readValue('upload_cardPolicy_ExpDate') ?? '';
localTotalPremium = widget.total_premium.isNotEmpty
? widget.total_premium
: await tokenService.readValue('upload_total_premium') ?? '';
}
Future<void> clearUploadStorage() async {
await tokenService.removeValue('upload_ClientId');
await tokenService.removeValue('upload_policyTypeId');
await tokenService.removeValue('upload_ClientPoliyId');
await tokenService.removeValue('upload_clientBranchId');
await tokenService.removeValue('upload_Token');
await tokenService.removeValue('upload_TokenType');
await tokenService.removeValue('upload_cardType');
await tokenService.removeValue('upload_cardPolicyNo');
await tokenService.removeValue('upload_cardInsurer_name');
await tokenService.removeValue('upload_cardPolicy_name');
await tokenService.removeValue('upload_cardPolicy_ExpDate');
await tokenService.removeValue('upload_total_premium');
}
// Future<void> getPolicyDetails() async {
// setState(() {
// clientPolicyId = argumentsData['client_policy_id'];
@ -184,7 +265,7 @@ class _postFileUploadState extends State<postFileUpload> {
Future<void> getFileUploadMasterDetails() async {
print('9');
try {
final response = await apiService.getFileUploadMastersToApi(widget.Token);
final response = await apiService.getFileUploadMastersToApi(localToken);
if (response['status'] == true) {
print('getFileUploadMasterList1');
@ -220,8 +301,8 @@ class _postFileUploadState extends State<postFileUpload> {
print('9');
try {
final response = await apiService.getFileListToApi(
empPrimaryId, widget.cardPolicyNo, empClientId,widget.Token,widget.TokenType);
final response = await apiService.getFileListToApi(empPrimaryId,
localCardPolicyNo, empClientId, localToken, localTokenType);
if (response['status'] == 'success') {
print('getThrFileList');
@ -248,18 +329,20 @@ class _postFileUploadState extends State<postFileUpload> {
}
Future<void> getHrFileDownload(id, file_name) async {
// final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token);
// final http.Response response = await apiService.getHrFileDownloadToApi(id, localToken);
print("**********-------*****");
final encryptClientId = widget.ClientId;
final encryptClientId = localClientId;
print(encryptClientId);
final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId';
final token = widget.Token;
final String url =
'$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId';
final token = localToken;
final response = await http.get(
Uri.parse(url),
headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
@ -301,12 +384,13 @@ class _postFileUploadState extends State<postFileUpload> {
print("fun Sam f - name $post_file_name");
final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/downloadSampleExcel/$apiParam';
final token = widget.Token;
final token = localToken;
final response = await http.get(
Uri.parse(url),
headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
@ -475,7 +559,8 @@ class _postFileUploadState extends State<postFileUpload> {
print('else');
// Attach the file to the request
// Set authorization token in headers
request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
request.headers['APP-SIGNATURE'] =
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
request.headers['Authorization'] = 'Bearer $_token';
// request.files.add(http.MultipartFile.fromBytes('file', fileBytes,
// filename: fileName));
@ -492,13 +577,13 @@ class _postFileUploadState extends State<postFileUpload> {
));
print('clintID: $clintID');
request.fields['client_id'] = widget.ClientId;
request.fields['policy_no'] = widget.cardPolicyNo;
request.fields['client_branch_id'] = widget.clientBranchId;
request.fields['client_id'] = localClientId;
request.fields['policy_no'] = localCardPolicyNo;
request.fields['client_branch_id'] = localClientBranchId;
request.fields['file_action'] = selectedKey!;
// request.fields['status'] = selectedKey!;
request.fields['created_by'] = empPrimaryId;
request.fields['policy_id'] = widget.ClientPoliyId;
request.fields['policy_id'] = localClientBranchId;
// "client_id": 1,
// "client_branch_id": 2,
// "policy_no": "POL123456",
@ -520,15 +605,15 @@ class _postFileUploadState extends State<postFileUpload> {
isLoading = false;
});
ToastHelper.showSuccessToast(context, data['message']);
getFileListDetails();
setState(() {
selectedValue = null;
selectedKey = null;
resetErrorCount();
getFileListDetails();
});
} else {
getFileListDetails();
setState(() {
getFileListDetails();
isLoading = false;
selectedValue = null;
selectedKey = null;
@ -587,7 +672,8 @@ class _postFileUploadState extends State<postFileUpload> {
}
Widget _buildContent(BuildContext context) {
return isLoading ? Container(
return isLoading
? Container(
color: Colors.transparent, // Semi-transparent background
child: Center(
child: // Your GIF loader widget
@ -596,21 +682,61 @@ class _postFileUploadState extends State<postFileUpload> {
width: 60,
'assets/nhance-loader.gif'), // Adjust path to your GIF loader
),
): Container(
)
: Container(
child: Column(
children: [
Row(
children: [
IconButton(
tooltip: 'Previous Page',
onPressed: () => {Navigator.pop(context)},
onPressed: () async {
if (Navigator.canPop(context)) {
Navigator.pop(context);
return;
}
final clientId = await tokenService.readValue('hr_ClientId') ?? '';
final policyTypeId = await tokenService.readValue('hr_policyTypeId') ?? '';
final clientPolicyId = await tokenService.readValue('hr_ClientPoliyId') ?? '';
final clientBranchId = await tokenService.readValue('hr_clientBranchId') ?? '';
final token = await tokenService.readValue('hr_Token') ?? '';
final tokenType = await tokenService.readValue('hr_TokenType') ?? '';
final cardType = await tokenService.readValue('hr_cardType') ?? '';
final policyNo = await tokenService.readValue('hr_cardPolicyNo') ?? '';
final insurer = await tokenService.readValue('hr_cardInsurer_name') ?? '';
final policyName = await tokenService.readValue('hr_cardPolicy_name') ?? '';
final expDate = await tokenService.readValue('hr_cardPolicy_ExpDate') ?? '';
final totalPremium = await tokenService.readValue('hr_total_premium') ?? '';
final bulkDownload = await tokenService.readValue('hr_is_ecard_bulk_download_for_employee') ?? '0';
Navigator.pushReplacement(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'hrPolicyDetails'),
builder: (_) => hrPolicyDetails(
ClientId: clientId,
policyTypeId: policyTypeId,
ClientPoliyId: clientPolicyId,
clientBranchId: clientBranchId,
Token: token,
TokenType: tokenType,
cardType: cardType,
cardPolicyNo: policyNo,
cardInsurer_name: insurer,
cardPolicy_name: policyName,
cardPolicy_ExpDate: expDate,
total_premium: totalPremium,
is_ecard_bulk_download_for_employee: int.tryParse(bulkDownload) ?? 0,
),
),
);
},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
color: Colors.black,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 6),
Expanded(
@ -619,7 +745,7 @@ class _postFileUploadState extends State<postFileUpload> {
// mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"${widget.cardType} - ${widget.cardPolicyNo} " ??
"${localCardType} - ${localCardPolicyNo} " ??
'',
style: GoogleFonts.poppins(
color: Colors.black,
@ -628,10 +754,13 @@ class _postFileUploadState extends State<postFileUpload> {
),
),
Text(
widget.TokenType == 'pre'
? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})"
: "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})",
style: GoogleFonts.poppins(color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w400),
localTokenType == 'pre'
? "${localCardPolicyName} (${localCardPolicyExpDate})"
: "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 12,
fontWeight: FontWeight.w400),
),
],
),
@ -650,11 +779,15 @@ class _postFileUploadState extends State<postFileUpload> {
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE26728),
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
child: Text(
'Sample Excel',
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white),
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w700,
color: Colors.white),
),
),
),
@ -663,6 +796,11 @@ class _postFileUploadState extends State<postFileUpload> {
],
),
SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -676,7 +814,8 @@ class _postFileUploadState extends State<postFileUpload> {
onChanged: (val) {
setState(() {
selectedKey = val;
final selectedItem = getFileUploadMasterList.firstWhere((e) => e['key'] == val);
final selectedItem = getFileUploadMasterList
.firstWhere((e) => e['key'] == val);
selectedValue = selectedItem['value'];
currentApiValue = selectedItem['key'];
showSampleButton = true;
@ -725,7 +864,8 @@ class _postFileUploadState extends State<postFileUpload> {
});
_dragAndDropFile(droppedFile);
},
builder: (context, candidateData, rejectedData) {
builder:
(context, candidateData, rejectedData) {
return GestureDetector(
onTap: () {
if (selectedValue != null) {
@ -739,10 +879,12 @@ class _postFileUploadState extends State<postFileUpload> {
},
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 12),
padding: const EdgeInsets.symmetric(
horizontal: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
borderRadius:
BorderRadius.circular(8),
border: Border.all(
color: const Color(0xFF00A6A6),
width: 1,
@ -752,8 +894,10 @@ class _postFileUploadState extends State<postFileUpload> {
children: [
Expanded(
child: Text(
fileName ?? 'Upload Your Documents',
overflow: TextOverflow.ellipsis,
fileName ??
'Upload Your Documents',
overflow:
TextOverflow.ellipsis,
style: GoogleFonts.poppins(
fontSize: 13,
color: fileName == null
@ -769,8 +913,7 @@ class _postFileUploadState extends State<postFileUpload> {
),
],
),
)
);
));
},
),
],
@ -779,25 +922,17 @@ class _postFileUploadState extends State<postFileUpload> {
],
),
SizedBox(height: 20),
Row(
children: [
Expanded(
child: Column(
Column(
children: [
_buildFileUploadedGrid(),
const SizedBox(height: 16),
_buildPagination(context),
],
),
)
],
),
])))
],
),
);
}
Widget buildUploadBox({
@ -852,8 +987,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
Widget buildStyledDropdown({
required String label,
required String? value,
@ -906,7 +1039,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
Widget _buildFileUploadedGrid() {
if (filteredData.isEmpty) {
return const SizedBox(
@ -916,13 +1048,14 @@ class _postFileUploadState extends State<postFileUpload> {
}
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, // IMPORTANT
physics: const NeverScrollableScrollPhysics(), // Disable inner scroll
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 👈 2 cards per row
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 10, // 👈 card height
childAspectRatio: 10,
),
itemCount: _paginatedData.length,
itemBuilder: (context, index) {
@ -930,6 +1063,8 @@ class _postFileUploadState extends State<postFileUpload> {
return _buildFileCard(item);
},
);
}
Widget _buildFileCard(Map<String, dynamic> item) {
@ -1014,9 +1149,7 @@ class _postFileUploadState extends State<postFileUpload> {
children: [
/// 🔴 Error + Status
Row(
children: [
],
children: [],
),
const SizedBox(height: 8),
@ -1028,36 +1161,39 @@ class _postFileUploadState extends State<postFileUpload> {
onTap: () async {
print(item);
// return;
final String? token = await tokenService.getCurrentToken();
final String? empClientId = await tokenService.readValue('empClientId');
final String? empBranchId = await tokenService.readValue('empClientBranchId');
final String? token =
await tokenService.getCurrentToken();
final String? empClientId =
await tokenService.readValue('empClientId');
final String? empBranchId =
await tokenService.readValue('empClientBranchId');
print(item);
print(empClientId);
print(widget.policyTypeId);
print(localPolicyTypeId);
print(empBranchId);
print(token);
print('post');
print(widget.cardType);
print(widget.cardPolicyNo);
print(widget.cardInsurer_name);
print(widget.cardPolicy_name);
print(widget.cardPolicy_ExpDate);
print(localCardType);
print(localCardPolicyNo);
print(localCardInsurerName);
print(localCardPolicyName);
print(localCardPolicyExpDate);
print(item['id']);
// SAFETY CHECK
if (token == null ||
empClientId == null ||
empBranchId == null) {
debugPrint('❌ Missing required data for navigation ${token}');
debugPrint(
'❌ Missing required data for navigation ${token}');
return;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
excelErrorScreen(
builder: (context) => excelErrorScreen(
ClientId: empClientId,
policy_no: item['policy_no'],
action: item['file_action'],
@ -1065,8 +1201,7 @@ class _postFileUploadState extends State<postFileUpload> {
clientBranchId: empBranchId,
Token: token,
TokenType: 'post',
id: item['id']
),
id: item['id']),
),
);
},
@ -1100,8 +1235,8 @@ class _postFileUploadState extends State<postFileUpload> {
),
],
),
/// Download
/// Download
],
),
],
@ -1109,7 +1244,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
Widget _buildStatusChip(String status) {
final s = status.toLowerCase();
@ -1142,7 +1276,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
static final _dataBold = GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
@ -1161,14 +1294,14 @@ class _postFileUploadState extends State<postFileUpload> {
);
Widget _buildPagination(BuildContext context) {
final totalItems = filteredData.length;
final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1;
final int startEntry =
totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1;
int endEntry = _currentPage * _rowsPerPage;
if (endEntry > totalItems) endEntry = totalItems;
final totalPages = (filteredData.length / _rowsPerPage).ceil();
const visiblePageCount = 5;
const visiblePageCount = 6;
List<int> getVisiblePages() {
if (totalPages <= visiblePageCount) {
@ -1177,7 +1310,8 @@ class _postFileUploadState extends State<postFileUpload> {
if (_currentPage <= 3) {
return [1, 2, 3, 4, 5];
} if (_currentPage >= totalPages - 2) {
}
if (_currentPage >= totalPages - 2) {
return [
totalPages - 4,
totalPages - 3,
@ -1193,7 +1327,6 @@ class _postFileUploadState extends State<postFileUpload> {
_currentPage + 1,
_currentPage + 2,
];
}
List<int> visiblePages = getVisiblePages();
@ -1208,7 +1341,7 @@ class _postFileUploadState extends State<postFileUpload> {
// Dropdown for rows per page
DropdownButton<int>(
value: _rowsPerPage,
items: [5, 10, 15, 20, 50].map((int value) {
items: [6, 10, 15, 20, 50].map((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(' $value ',

View File

@ -18,6 +18,7 @@ import 'package:csv/csv.dart';
import '../config/environment.dart';
import '../customAppBar/base_layout.dart';
import 'excelVerification.dart';
import 'hrPolicyDetails.dart';
class preFileUpload extends StatefulWidget {
final String ClientId;
@ -32,8 +33,8 @@ class preFileUpload extends StatefulWidget {
final String cardPolicy_name;
final String cardPolicy_ExpDate;
final String total_premium;
const preFileUpload(
{Key? key,
const preFileUpload({
Key? key,
required this.ClientId,
required this.policyTypeId,
required this.ClientPoliyId,
@ -46,9 +47,7 @@ class preFileUpload extends StatefulWidget {
required this.cardPolicy_name,
required this.cardPolicy_ExpDate,
required this.total_premium,
})
: super(key: key);
}) : super(key: key);
@override
State<preFileUpload> createState() => _excelVerifyState();
@ -56,6 +55,20 @@ class preFileUpload extends StatefulWidget {
class _excelVerifyState extends State<preFileUpload> {
final tokenService = TokenStorageService();
String localClientId = '';
String localPolicyTypeId = '';
String localClientPolicyId = '';
String localClientBranchId = '';
String localToken = '';
String localTokenType = '';
String localCardType = '';
String localCardPolicyNo = '';
String localCardInsurerName = '';
String localCardPolicyName = '';
String localCardPolicyExpDate = '';
String localTotalPremium = '';
Uint8List? fileBytes;
Uint8List? fileBytes2;
late String _token;
@ -92,7 +105,7 @@ class _excelVerifyState extends State<preFileUpload> {
final List<String> _allowedExtensions = ['xlsx', 'xls'];
int _currentPage = 1;
int _rowsPerPage = 5;
int _rowsPerPage = 6;
List<dynamic> get _paginatedData {
final startIndex = (_currentPage - 1) * _rowsPerPage;
@ -105,8 +118,10 @@ class _excelVerifyState extends State<preFileUpload> {
void initState() {
super.initState();
apiService = ApiService(context);
getFileListDetails();
restoreUploadData().then((_) {
_loadToken();
getFileListDetails();
});
}
@override
@ -116,21 +131,85 @@ class _excelVerifyState extends State<preFileUpload> {
}
Future<void> _loadToken() async {
final token = widget.Token;
final token = localToken.isNotEmpty
? localToken
: await tokenService.readValue('upload_Token');
if (token != null && token.isNotEmpty) {
setState(() {
_token = token;
});
Map<String, dynamic>? decodedToken = Jwt.parseJwt(token);
print('decodedToken $decodedToken');
} else {
// Token is empty or null, handle accordingly (e.g., navigate to login screen)
// For now, let's navigate to the login screen
ToastHelper.showErrorToast(context, 'Session Out');
Navigator.pushReplacementNamed(context, 'hrLogin');
}
}
Future<void> restoreUploadData() async {
localClientId = widget.ClientId.isNotEmpty
? widget.ClientId
: await tokenService.readValue('upload_ClientId') ?? '';
localPolicyTypeId = widget.policyTypeId.isNotEmpty
? widget.policyTypeId
: await tokenService.readValue('upload_policyTypeId') ?? '';
localClientPolicyId = widget.ClientPoliyId.isNotEmpty
? widget.ClientPoliyId
: await tokenService.readValue('upload_ClientPoliyId') ?? '';
localClientBranchId = widget.clientBranchId.isNotEmpty
? widget.clientBranchId
: await tokenService.readValue('upload_clientBranchId') ?? '';
localToken = widget.Token.isNotEmpty
? widget.Token
: await tokenService.readValue('upload_Token') ?? '';
localTokenType = widget.TokenType.isNotEmpty
? widget.TokenType
: await tokenService.readValue('upload_TokenType') ?? '';
localCardType = widget.cardType.isNotEmpty
? widget.cardType
: await tokenService.readValue('upload_cardType') ?? '';
localCardPolicyNo = widget.cardPolicyNo.isNotEmpty
? widget.cardPolicyNo
: await tokenService.readValue('upload_cardPolicyNo') ?? '';
localCardInsurerName = widget.cardInsurer_name.isNotEmpty
? widget.cardInsurer_name
: await tokenService.readValue('upload_cardInsurer_name') ?? '';
localCardPolicyName = widget.cardPolicy_name.isNotEmpty
? widget.cardPolicy_name
: await tokenService.readValue('upload_cardPolicy_name') ?? '';
localCardPolicyExpDate = widget.cardPolicy_ExpDate.isNotEmpty
? widget.cardPolicy_ExpDate
: await tokenService.readValue('upload_cardPolicy_ExpDate') ?? '';
localTotalPremium = widget.total_premium.isNotEmpty
? widget.total_premium
: await tokenService.readValue('upload_total_premium') ?? '';
}
Future<void> clearUploadStorage() async {
await tokenService.removeValue('upload_ClientId');
await tokenService.removeValue('upload_policyTypeId');
await tokenService.removeValue('upload_ClientPoliyId');
await tokenService.removeValue('upload_clientBranchId');
await tokenService.removeValue('upload_Token');
await tokenService.removeValue('upload_TokenType');
await tokenService.removeValue('upload_cardType');
await tokenService.removeValue('upload_cardPolicyNo');
await tokenService.removeValue('upload_cardInsurer_name');
await tokenService.removeValue('upload_cardPolicy_name');
await tokenService.removeValue('upload_cardPolicy_ExpDate');
await tokenService.removeValue('upload_total_premium');
}
// Future<void> getPolicyDetails() async {
// setState(() {
// clientPolicyId = argumentsData['client_policy_id'];
@ -210,15 +289,14 @@ class _excelVerifyState extends State<preFileUpload> {
}
bool _validateDatesBeforeUpload() {
if (openDateController.text.isEmpty ||
closeDateController.text.isEmpty) {
ToastHelper.showErrorToast2(context,'','Please select both Enrolment Open Date and Close Date');
if (openDateController.text.isEmpty || closeDateController.text.isEmpty) {
ToastHelper.showErrorToast2(
context, '', 'Please select both Enrolment Open Date and Close Date');
return false;
}
return true;
}
void _processExcelData(Uint8List fileBytes, fileName) {
List<List<Data>> dataArray;
if (fileName.endsWith('.xlsx')) {
@ -461,7 +539,6 @@ class _excelVerifyState extends State<preFileUpload> {
}
Future<void> sendExcelFIleTOAPI(Uint8List fileBytes, fileName) async {
// Future.delayed(Duration(seconds: 3), () {
// setState(() {
isLoading = true;
@ -486,7 +563,8 @@ class _excelVerifyState extends State<preFileUpload> {
print('else');
// Attach the file to the request
// Set authorization token in headers
request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
request.headers['APP-SIGNATURE'] =
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
request.headers['Authorization'] = 'Bearer $_token';
// request.files.add(http.MultipartFile.fromBytes('file', fileBytes,
// filename: fileName));
@ -497,10 +575,10 @@ class _excelVerifyState extends State<preFileUpload> {
filename: fileName ?? 'default_filename.xlsx',
));
request.fields['client_id'] = widget.ClientId;
request.fields['client_id'] = localClientId;
// if (policyFirstPart == 'GPA') {
request.fields['policy_id'] = widget.ClientPoliyId;
request.fields['client_branch_id'] = widget.clientBranchId;
request.fields['policy_id'] = localClientPolicyId;
request.fields['client_branch_id'] = localClientBranchId;
request.fields['enrollment_open_date'] = openDateController.text;
request.fields['enrollment_close_date'] = closeDateController.text;
request.fields['created_by'] = enrollmentHrId!;
@ -527,18 +605,21 @@ class _excelVerifyState extends State<preFileUpload> {
isSuccess = true;
successContent = data['message'];
excelValidationStaus = 0;
});
resetErrorCount();
handleImportAction();
getFileListDetails();
});
} else {
setState(() {
isLoading = false;
});
handleImportAction();
});
ToastHelper.showErrorToast2(context, "", data['message']);
setState(() {
resetErrorCount();
getFileListDetails();
});
// ToastHelper.showErrorToast(context, data['message']);
print('Table');
}
@ -570,7 +651,7 @@ class _excelVerifyState extends State<preFileUpload> {
print('10');
response = await apiService.getImportLogHrActivity(
postId!, preId!, widget.Token, activity);
postId!, preId!, localToken, activity);
if (response['status'] == 'success') {
print('Request success');
@ -585,13 +666,15 @@ class _excelVerifyState extends State<preFileUpload> {
}
Future<void> getFileListDetails() async {
final enrollmentPrimaryId = await tokenService.readValue('enrollmentEmpPrimaryId');
final enrollmentClientId = await tokenService.readValue('enrollmentClient_id');
final enrollmentPrimaryId =
await tokenService.readValue('enrollmentEmpPrimaryId');
final enrollmentClientId =
await tokenService.readValue('enrollmentClient_id');
print('9');
try {
final response = await apiService.getFileListToApi(
enrollmentPrimaryId, widget.cardPolicyNo, enrollmentClientId,widget.Token,widget.TokenType);
final response = await apiService.getFileListToApi(enrollmentPrimaryId,
localCardPolicyNo, enrollmentClientId, localToken, localTokenType);
if (response['status'] == true) {
print('getThrFileList');
@ -640,7 +723,7 @@ class _excelVerifyState extends State<preFileUpload> {
// Future<void> downloadSampleFile() async {
//
// final response = await apiService.getSampleFileDownload(widget.Token);
// final response = await apiService.getSampleFileDownload(localToken);
// print('check 1');
// if (response['status'] == 'success') {
// final url = response['data'];
@ -669,15 +752,16 @@ class _excelVerifyState extends State<preFileUpload> {
}
Future<void> getHrFileDownload(id, file_name) async {
// final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token);
// final http.Response response = await apiService.getHrFileDownloadToApi(id, localToken);
final apiurl = Environment.apiUrl;
final String url = '$apiurl/hrFileDownload?id=$id';
final token = widget.Token;
final token = localToken;
final response = await http.get(
Uri.parse(url),
headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
@ -717,7 +801,8 @@ class _excelVerifyState extends State<preFileUpload> {
}
Widget _buildContent(BuildContext context) {
return isLoading ? Container(
return isLoading
? Container(
color: Colors.transparent, // Semi-transparent background
child: Center(
child: // Your GIF loader widget
@ -726,7 +811,8 @@ class _excelVerifyState extends State<preFileUpload> {
width: 60,
'assets/nhance-loader.gif'), // Adjust path to your GIF loader
),
) : Container(
)
: Container(
// padding: const EdgeInsets.all(20),
// color: Color(0xFFEFF3F6),
child: Column(
@ -735,14 +821,53 @@ class _excelVerifyState extends State<preFileUpload> {
children: [
IconButton(
tooltip: 'Previous Page',
onPressed: () => {Navigator.pop(context)},
onPressed: () async {
if (Navigator.canPop(context)) {
Navigator.pop(context);
return;
}
final clientId = await tokenService.readValue('hr_ClientId') ?? '';
final policyTypeId = await tokenService.readValue('hr_policyTypeId') ?? '';
final clientPolicyId = await tokenService.readValue('hr_ClientPoliyId') ?? '';
final clientBranchId = await tokenService.readValue('hr_clientBranchId') ?? '';
final token = await tokenService.readValue('hr_Token') ?? '';
final tokenType = await tokenService.readValue('hr_TokenType') ?? '';
final cardType = await tokenService.readValue('hr_cardType') ?? '';
final policyNo = await tokenService.readValue('hr_cardPolicyNo') ?? '';
final insurer = await tokenService.readValue('hr_cardInsurer_name') ?? '';
final policyName = await tokenService.readValue('hr_cardPolicy_name') ?? '';
final expDate = await tokenService.readValue('hr_cardPolicy_ExpDate') ?? '';
final totalPremium = await tokenService.readValue('hr_total_premium') ?? '';
final bulkDownload = await tokenService.readValue('hr_is_ecard_bulk_download_for_employee') ?? '0';
Navigator.pushReplacement(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'hrPolicyDetails'),
builder: (_) => hrPolicyDetails(
ClientId: clientId,
policyTypeId: policyTypeId,
ClientPoliyId: clientPolicyId,
clientBranchId: clientBranchId,
Token: token,
TokenType: tokenType,
cardType: cardType,
cardPolicyNo: policyNo,
cardInsurer_name: insurer,
cardPolicy_name: policyName,
cardPolicy_ExpDate: expDate,
total_premium: totalPremium,
is_ecard_bulk_download_for_employee: int.tryParse(bulkDownload) ?? 0,
),
),
);
},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
color: Colors.black,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 6),
Container(
@ -752,8 +877,7 @@ class _excelVerifyState extends State<preFileUpload> {
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"${widget.cardType} - ${widget.cardPolicyNo} " ??
'',
"${localCardType} - ${localCardPolicyNo} " ?? '',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 14,
@ -761,9 +885,9 @@ class _excelVerifyState extends State<preFileUpload> {
),
),
Text(
widget.TokenType == 'pre'
? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})"
: "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})",
localTokenType == 'pre'
? "${localCardPolicyName} (${localCardPolicyExpDate})"
: "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 12,
@ -776,6 +900,11 @@ class _excelVerifyState extends State<preFileUpload> {
],
),
SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SizedBox(
@ -801,7 +930,6 @@ class _excelVerifyState extends State<preFileUpload> {
openDateController.text = formatted;
}
},
),
),
@ -812,9 +940,9 @@ class _excelVerifyState extends State<preFileUpload> {
label: 'Enrolment Close Date',
controller: closeDateController,
onTap: () async {
if (openDateController.text.isEmpty) {
ToastHelper.showErrorToast(context, 'Please select Enrolment Open Date first');
ToastHelper.showErrorToast(context,
'Please select Enrolment Open Date first');
return;
}
@ -823,7 +951,8 @@ class _excelVerifyState extends State<preFileUpload> {
final picked = await showDatePicker(
context: context,
firstDate: openDate, // Cannot select before open date
firstDate:
openDate, // Cannot select before open date
lastDate: DateTime(2100),
initialDate: openDate,
);
@ -852,8 +981,7 @@ class _excelVerifyState extends State<preFileUpload> {
),
SizedBox(height: 5),
Row(
mainAxisAlignment:
MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
@ -871,7 +999,8 @@ class _excelVerifyState extends State<preFileUpload> {
onTap: () {
if (!_validateDatesBeforeUpload()) return;
if (fileName == null) {
_uploadFile('Policy Name'); // same function
_uploadFile(
'Policy Name'); // same function
}
},
child: DragTarget<html.File>(
@ -891,64 +1020,96 @@ class _excelVerifyState extends State<preFileUpload> {
return Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
fileName != null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
SizedBox(
width: 40,
height: 40,
child: Tooltip(
message: 'Upload', // The text that appears on hover
message:
'Upload', // The text that appears on hover
child: ElevatedButton(
onPressed: () => null,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFD4F1F2),
onPressed: () =>
null,
style:
ElevatedButton
.styleFrom(
backgroundColor:
const Color(
0xFFD4F1F2),
elevation: 0,
padding: EdgeInsets.zero, // IMPORTANT
alignment: Alignment.center, // FORCE CENTER
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: const BorderSide( // BORDER ADDED
color: Color(0xFF00999E),
padding: EdgeInsets
.zero, // IMPORTANT
alignment: Alignment
.center, // FORCE CENTER
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius
.circular(
10),
side:
const BorderSide(
// BORDER ADDED
color: Color(
0xFF00999E),
width: 1,
),
),
),
child: Icon(
Icons.file_upload_outlined,
Icons
.file_upload_outlined,
size: 22,
color: Color(0xFF00999E),
)
color: Color(
0xFF00999E),
)),
),
),
),
const SizedBox(height: 15),
const SizedBox(
height: 15),
Text(
fileName!,
style: const TextStyle(fontSize: 16),
style: const TextStyle(
fontSize: 16),
),
const SizedBox(height: 15),
const SizedBox(
height: 15),
MouseRegion(
cursor: SystemMouseCursors.click,
cursor:
SystemMouseCursors
.click,
child: GestureDetector(
onTap: resetErrorCount,
onTap:
resetErrorCount,
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
Icon(
Icons.delete_forever,
Icons
.delete_forever,
size: 20,
color: Colors.red,
color:
Colors.red,
),
SizedBox(width: 4),
SizedBox(
width: 4),
Text(
'Remove',
style: TextStyle(
style:
TextStyle(
fontSize: 13,
color: Color(0xFF727272),
color: Color(
0xFF727272),
),
),
],
@ -963,51 +1124,76 @@ class _excelVerifyState extends State<preFileUpload> {
width: 40,
height: 40,
child: Tooltip(
message: 'Upload', // The text that appears on hover
message:
'Upload', // The text that appears on hover
child: ElevatedButton(
onPressed: () {
if (!_validateDatesBeforeUpload()) return;
if (fileName == null) {
_uploadFile('Policy Name');
if (!_validateDatesBeforeUpload())
return;
if (fileName ==
null) {
_uploadFile(
'Policy Name');
}
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFD4F1F2),
style:
ElevatedButton
.styleFrom(
backgroundColor:
const Color(
0xFFD4F1F2),
elevation: 0,
padding: EdgeInsets.zero, // IMPORTANT
alignment: Alignment.center, // FORCE CENTER
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: const BorderSide( // BORDER ADDED
color: Color(0xFF00999E),
padding: EdgeInsets
.zero, // IMPORTANT
alignment: Alignment
.center, // FORCE CENTER
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius
.circular(
10),
side:
const BorderSide(
// BORDER ADDED
color: Color(
0xFF00999E),
width: 1,
),
),
),
child: Icon(
Icons.file_upload_outlined,
Icons
.file_upload_outlined,
size: 22,
color: Color(0xFF00999E),
)
),
color: Color(
0xFF00999E),
)),
),
),
SizedBox(height: 12),
Text('Upload Your Documents',
style: GoogleFonts.poppins(
Text(
'Upload Your Documents',
style:
GoogleFonts.poppins(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF000000)
),
fontWeight:
FontWeight
.w600,
color: Color(
0xFF000000)),
),
SizedBox(height: 8),
Text(
'(Supported Format: XLSX)',
style: GoogleFonts.poppins(
style:
GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w400,
color: Color(0xFF707070)
),
fontWeight:
FontWeight
.w400,
color: Color(
0xFF707070)),
),
],
),
@ -1023,28 +1209,21 @@ class _excelVerifyState extends State<preFileUpload> {
),
SizedBox(height: 20),
Row(
mainAxisAlignment:
MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment:
MainAxisAlignment
.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Please download the sample file to review the format.',
textAlign:
TextAlign.center,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight:
FontWeight.w400,
color: Color(0xFF707070)
)),
fontWeight: FontWeight.w400,
color: Color(0xFF707070))),
MouseRegion(
cursor: SystemMouseCursors
.click,
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
downloadSampleFile();
@ -1063,20 +1242,17 @@ class _excelVerifyState extends State<preFileUpload> {
],
),
SizedBox(height: 20),
Row(
children: [
Expanded(
child: Column(
Column(
children: [
_buildFileUploadedGrid(),
const SizedBox(height: 16),
_buildPagination(context),
],
),
)
],
),
),
),
],
),
);
@ -1084,20 +1260,18 @@ class _excelVerifyState extends State<preFileUpload> {
Widget _buildFileUploadedGrid() {
if (filteredData.isEmpty) {
return const SizedBox(
height: 120,
child: Center(child: Text('No uploaded files')),
);
return const Center(child: Text('No uploaded files'));
}
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, // IMPORTANT
physics: const NeverScrollableScrollPhysics(), // Disable inner scroll
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 👈 2 cards per row
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 10, // 👈 card height
childAspectRatio: 10,
),
itemCount: _paginatedData.length,
itemBuilder: (context, index) {
@ -1107,6 +1281,31 @@ class _excelVerifyState extends State<preFileUpload> {
);
}
// Widget _buildFileUploadedGrid() {
// if (filteredData.isEmpty) {
// return const SizedBox(
// height: 120,
// child: Center(child: Text('No uploaded files')),
// );
// }
//
// return GridView.builder(
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 2, // 👈 2 cards per row
// crossAxisSpacing: 16,
// mainAxisSpacing: 16,
// childAspectRatio: 10, // 👈 card height
// ),
// itemCount: _paginatedData.length,
// itemBuilder: (context, index) {
// final item = _paginatedData[index];
// return _buildFileCard(item);
// },
// );
// }
Widget _buildFileCard(Map<String, dynamic> item) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
@ -1189,9 +1388,7 @@ class _excelVerifyState extends State<preFileUpload> {
children: [
/// 🔴 Error + Status
Row(
children: [
],
children: [],
),
const SizedBox(height: 8),
@ -1205,36 +1402,40 @@ class _excelVerifyState extends State<preFileUpload> {
onTap: () async {
print(item);
// return;
final String? token = await tokenService.getCurrentToken();
final String? enrollmentClient_id = await tokenService.readValue('enrollmentClient_id');
final String? enrollmentEmpClientBranchId = await tokenService.readValue('enrollmentEmpClientBranchId');
final String? token =
await tokenService.getCurrentToken();
final String? enrollmentClient_id = await tokenService
.readValue('enrollmentClient_id');
final String? enrollmentEmpClientBranchId =
await tokenService
.readValue('enrollmentEmpClientBranchId');
print(item);
print(enrollmentClient_id);
print(widget.policyTypeId);
print(localPolicyTypeId);
print(enrollmentEmpClientBranchId);
print(token);
print('post');
print(widget.cardType);
print(widget.cardPolicyNo);
print(widget.cardInsurer_name);
print(widget.cardPolicy_name);
print(widget.cardPolicy_ExpDate);
print(localCardType);
print(localCardPolicyNo);
print(localCardInsurerName);
print(localCardPolicyName);
print(localCardPolicyExpDate);
print(item['id']);
// SAFETY CHECK
if (token == null ||
enrollmentClient_id == null ||
enrollmentEmpClientBranchId == null) {
debugPrint('❌ Missing required data for navigation ${token}');
debugPrint(
'❌ Missing required data for navigation ${token}');
return;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
excelErrorScreen(
builder: (context) => excelErrorScreen(
ClientId: enrollmentClient_id,
policy_no: item['policy_no'],
action: item['file_action'],
@ -1242,8 +1443,7 @@ class _excelVerifyState extends State<preFileUpload> {
clientBranchId: enrollmentEmpClientBranchId,
Token: token,
TokenType: 'pre',
id: item['id']
),
id: item['id']),
),
);
},
@ -1281,8 +1481,8 @@ class _excelVerifyState extends State<preFileUpload> {
),
],
),
/// Download
/// Download
],
),
],
@ -1325,12 +1525,13 @@ class _excelVerifyState extends State<preFileUpload> {
Widget _buildPagination(BuildContext context) {
// 1. Calculate the range of entries being shown
final totalItems = filteredData.length;
final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1;
final int startEntry =
totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1;
int endEntry = _currentPage * _rowsPerPage;
if (endEntry > totalItems) endEntry = totalItems;
final totalPages = (filteredData.length / _rowsPerPage).ceil();
const visiblePageCount = 5;
const visiblePageCount = 6;
List<int> getVisiblePages() {
if (totalPages <= visiblePageCount) {
@ -1356,7 +1557,6 @@ class _excelVerifyState extends State<preFileUpload> {
_currentPage + 1,
_currentPage + 2,
];
}
List<int> visiblePages = getVisiblePages();
@ -1365,7 +1565,8 @@ class _excelVerifyState extends State<preFileUpload> {
// Match this horizontal padding (16) to your Table Header padding for perfect alignment
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Pushes text to left, buttons to right
mainAxisAlignment: MainAxisAlignment
.spaceBetween, // Pushes text to left, buttons to right
children: [
// --- LEFT SIDE: Showing Text ---
Text(
@ -1384,7 +1585,7 @@ class _excelVerifyState extends State<preFileUpload> {
DropdownButton<int>(
value: _rowsPerPage,
// focusColor: Colors.transparent, // Fix: Removes the grey/blue highlight on change
items: [5, 10, 15, 20, 50].map((int value) {
items: [6, 10, 15, 20, 50].map((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(' $value ',
@ -1475,7 +1676,6 @@ class _excelVerifyState extends State<preFileUpload> {
return '-';
}
}
}
Widget _dateField({
@ -1533,7 +1733,6 @@ Widget _dateField({
);
}
class Data {
final dynamic value;
final int row;

View File

@ -12,6 +12,8 @@ class ApiService {
String? _token;
String? _hrtoken;
bool _isSessionOutToastShown = false; // Flag to track toast message
bool isTpaDashboardEnabled = false;
bool isTpaSelected = false; // track which dashboard is active
ApiService(this.context) {
_initializeToken();
@ -676,9 +678,32 @@ class ApiService {
}
}
Future<Map<String, dynamic>> getClaimPoliciesToApi(String token) async {
Future<Map<String, dynamic>> postHrTpaDashboard(params, token) async {
final url = Uri.parse('${Environment.apiUrlPost}getHrTpaDashboard');
final headers = {
'Authorization': 'Bearer ${token ?? ''}',
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
};
final response = await http.post(
url,
headers: headers,
body: jsonEncode(params),
);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception(
'Failed to load HR TPA dashboard: ${response.statusCode}');
}
}
Future<Map<String, dynamic>> getClaimPoliciesToApi(String token,empClientId) async {
print("getgetClaimPoliciesToApii1");
final url = Uri.parse('${Environment.apiUrlPost}claimsSearch');
final url = Uri.parse('${Environment.apiUrlPost}claimsSearch?client_id=$empClientId');
final headers = {
'Authorization': 'Bearer $token' ?? '',
@ -993,6 +1018,10 @@ class ApiService {
}
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
// 'throttle' => 429,
// 'soft' => 429,
// 'medium' => 403,
// 'hard' => 451,
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else if (response.statusCode == 401) {
@ -1001,6 +1030,17 @@ class ApiService {
await _clearLocalStorageAndRedirect();
}
return {};
} else if (response.statusCode == 403) {
if (!_isSessionOutToastShown) {
_isSessionOutToastShown = true;
await _clearLocalStorageAndRedirect();
}
return {};
}else if (response.statusCode == 451) {
final body = jsonDecode(response.body);
final message = body['message'];
ToastHelper.showWarningToast(context, message);
return {};
} else if (response.statusCode == 429) {
final body = jsonDecode(response.body);
final message = body['message'];

View File

@ -11,6 +11,7 @@ class MultiFileUploadWidget extends StatefulWidget {
State<MultiFileUploadWidget> createState() => _MultiFileUploadWidgetState();
static bool hasFiles = false;
static bool showValidation = false;
}
class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
@ -44,6 +45,7 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
setState(() {
errorMessage = null;
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty;
MultiFileUploadWidget.showValidation = false;
});
}
}
@ -52,6 +54,9 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
fileService.removeFileAt(index);
setState(() {
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty;
if (fileService.files.isEmpty) {
MultiFileUploadWidget.showValidation = true;
}
});
}
@ -173,7 +178,9 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
// ),
],
if (fileService.files.isEmpty && errorMessage == null) ...[
if (MultiFileUploadWidget.showValidation &&
fileService.files.isEmpty &&
errorMessage == null) ...[
const SizedBox(height: 4),
const Text(
"Required",

View File

@ -241,6 +241,9 @@ class TokenStorageService {
await _secureStorage.write(key: key, value: value);
}
Future<void> removeValue(String key) async {
await _secureStorage.delete(key: key);
}
Future<void> clearBranchSession() async {
final keysToRemove = [

View File

@ -33,6 +33,26 @@
<link rel="manifest" href="manifest.json">
<style>
/* Hide card three-dot menu */
[data-testid="dashcard-action-panel"] {
display: none !important;
}
/* Fallback selector */
.DashCard-actions {
display: none !important;
}
/* Hide powered by footer */
.MetabasePoweredBy {
display: none !important;
}
/* Fallback */
a[href*="metabase.com"] {
display: none !important;
}
.content {
width: 10%;
height: 10vh;