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(), }).toList(),
if(postModules.isNotEmpty) if(postModules.isNotEmpty && postModules.contains(5))
_SideItem( _SideItem(
// icon: Icons.dashboard, // icon: Icons.dashboard,
icon: SvgPicture.string( icon: SvgPicture.string(

View File

@ -69,6 +69,7 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
late String _verificationId; late String _verificationId;
dynamic empMobileNo; dynamic empMobileNo;
dynamic empEmailid; dynamic empEmailid;
final tokenService = TokenStorageService();
@override @override
void initState() { void initState() {
@ -225,12 +226,53 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
// // Show a Snackbar if the OTP is invalid // // Show a Snackbar if the OTP is invalid
// print('Invalid OTP. Please try again'); // 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 { } else {
setState(() { setState(() {
_isLoading = false; _isLoading = false;
}); });
ToastHelper.showWarningToast(context, 'Something went wrong'); throw Exception('Failed to load data');
throw Exception('Failed to verify OTP');
} }
} catch (e) { } catch (e) {
setState(() { setState(() {

View File

@ -1,6 +1,7 @@
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:http/http.dart' as http; 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:google_fonts/google_fonts.dart';
import 'package:flutter_animated_button/flutter_animated_button.dart'; import 'package:flutter_animated_button/flutter_animated_button.dart';
import 'package:nhancepolicy/service/token_storage_service.dart'; import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:url_launcher/url_launcher.dart';
import 'config/environment.dart'; import 'config/environment.dart';
import 'email_verify.dart'; import 'email_verify.dart';
@ -517,28 +519,34 @@ class _MyPhoneState extends State<MyHrLogin> {
), ),
SizedBox(height: 20), SizedBox(height: 20),
Container( Container(
width: double width: double.infinity,
.infinity, // Make the footer full width
child: Container(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
padding: padding: const EdgeInsets.symmetric(vertical: 8),
EdgeInsets.symmetric(vertical: 8),
child: RichText( child: RichText(
textAlign: TextAlign.center, textAlign: TextAlign.center,
text: TextSpan( text: TextSpan(
text: text: 'By continuing, you agree with our ',
'By continuing, you agree with our ',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: Colors.black, color: Colors.black,
fontSize: 9, fontSize: 9,
), ),
children: <TextSpan>[ children: [
TextSpan( TextSpan(
text: 'privacy policy ', text: 'privacy policy ',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: Color(0xFFE26828), color: const Color(0xFFE26828),
fontSize: 9, 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( TextSpan(
text: 'and ', text: 'and ',
@ -550,15 +558,67 @@ class _MyPhoneState extends State<MyHrLogin> {
TextSpan( TextSpan(
text: 'terms of use', text: 'terms of use',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: Color(0xFFE26828), color: const Color(0xFFE26828),
fontSize: 9, 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')); projectId: 'nhance-ee8d1'));
// await dotenv.load(fileName: Environment.fileName); // 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', title: 'Nhance HR',
onGenerateTitle: (context) => "Nhance HR",
initialRoute: 'hrLogin',
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
initialRoute: (initialToken == null || initialToken!.isEmpty)
? 'hrLogin'
: 'hrHome',
theme: ThemeData( theme: ThemeData(
primaryColor: Color(0xFF00999E), // Primary theme color primaryColor: const Color(0xFF00999E),
scaffoldBackgroundColor: Colors.white, scaffoldBackgroundColor: Colors.white,
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: Color(0xFF00999E), seedColor: const Color(0xFF00999E),
), ),
textTheme: GoogleFonts.poppinsTextTheme(), textTheme: GoogleFonts.poppinsTextTheme(),
elevatedButtonTheme: ElevatedButtonThemeData( elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF00999E), // Button background backgroundColor: const Color(0xFF00999E),
), ),
), ),
), ),
routes: { routes: appRoutes,
);
}
}
final Map<String, WidgetBuilder> appRoutes = {
'phone': (context) => MyPhone(), 'phone': (context) => MyPhone(),
'mailVerify': (context) => MyEmailVerify( 'mailVerify': (context) => MyEmailVerify(
type: '', type: '',
@ -173,6 +306,38 @@ Future<void> startApp() async {
cd_ac_pk: '', cd_ac_pk: '',
empClientId: '', 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 BuildContext parentContext;
final VoidCallback onSuccess; final VoidCallback onSuccess;
const RaiseClaimDialog({Key? key, required this.parentContext, required this.onSuccess,}) const RaiseClaimDialog({
: super(key: key); Key? key,
required this.parentContext,
required this.onSuccess,
}) : super(key: key);
@override @override
State<RaiseClaimDialog> createState() => _RaiseClaimDialogState(); State<RaiseClaimDialog> createState() => _RaiseClaimDialogState();
@ -32,6 +35,7 @@ class RaiseClaimDialog extends StatefulWidget {
class _RaiseClaimDialogState extends State<RaiseClaimDialog> { class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
// const RaiseClaimDialog({super.key}); // const RaiseClaimDialog({super.key});
bool hasSubmitted = false;
late ApiService apiService; late ApiService apiService;
bool isLoading = false; bool isLoading = false;
dynamic empPrimaryId; dynamic empPrimaryId;
@ -86,6 +90,11 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
Map<String, dynamic>? selectedMemberObject; Map<String, dynamic>? selectedMemberObject;
TextEditingController searchController = TextEditingController(); TextEditingController searchController = TextEditingController();
Map<String, dynamic> claimTypeMap = {};
List<Map<String, dynamic>> claimTypeList = [];
int? claimTypeId;
bool isClaimTypeValid = true;
// Declare subjectController and bodyController as instance variables // Declare subjectController and bodyController as instance variables
late TextEditingController subjectController; late TextEditingController subjectController;
late TextEditingController messageController; late TextEditingController messageController;
@ -145,6 +154,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
bool isAdmitDateValid = true; bool isAdmitDateValid = true;
bool isDischargeDateValid = true; bool isDischargeDateValid = true;
bool isAccidentService = false; bool isAccidentService = false;
bool isDeathDateValid = true;
@override @override
void initState() { void initState() {
@ -188,6 +198,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
admitDateController.dispose(); admitDateController.dispose();
dischargeDateController.dispose(); dischargeDateController.dispose();
serviceId = null; serviceId = null;
claimTypeId = null;
departmentList.clear(); departmentList.clear();
super.dispose(); super.dispose();
fileService.clearAll(); fileService.clearAll();
@ -210,7 +221,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
}); });
try { try {
print('10'); print('10');
final response = await apiService.getClaimPoliciesToApi(_postPreToken!); final response = await apiService.getClaimPoliciesToApi(_postPreToken!,empClientId);
if (response['status'] == 'success') { if (response['status'] == 'success') {
setState(() { setState(() {
isLoading = false; isLoading = false;
@ -223,6 +234,9 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
'name': item['type_name'].toString(), 'name': item['type_name'].toString(),
}) })
.toList(); .toList();
claimTypeMap = Map<String, dynamic>.from(getClaimPoliciesApi['claim_type']);
print('claimTypeMap $claimTypeMap');
}); });
} else { } else {
setState(() { setState(() {
@ -319,11 +333,40 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
}; };
}).toList(); }).toList();
isPolicyValid = true; 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 { Future<void> getCDPoliciesDetails() async {
print('9'); print('9');
setState(() { setState(() {
@ -333,7 +376,10 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
print('10'); print('10');
final response = await apiService.getEmployeeAndDependenceToApi( final response = await apiService.getEmployeeAndDependenceToApi(
empClientId, selectedClientPolicyId, empClientBranchId, _postPreToken!); empClientId,
selectedClientPolicyId,
empClientBranchId,
_postPreToken!);
if (response['status'] == 'success') { if (response['status'] == 'success') {
final List<Map<String, dynamic>> members = final List<Map<String, dynamic>> members =
@ -353,9 +399,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
'relationship': m['relationship'], 'relationship': m['relationship'],
'gender': m['gender'], 'gender': m['gender'],
'dob': m['dob'], 'dob': m['dob'],
'policy_name': serviceId == 1 'policy_name': serviceId == 1 ? null : m['policy_name'],
? null
: m['policy_name'],
}; };
}).toList(); }).toList();
}); });
@ -373,60 +417,144 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
} }
Future<void> sendFormDataToApi() async { Future<void> sendFormDataToApi() async {
setState(() { setState(() {
hasSubmitted = serviceId != null ? true : false;
isServiceValid = serviceId != null; isServiceValid = serviceId != null;
isPolicyValid = selectedClientPolicyId != null; isPolicyValid = selectedClientPolicyId != null;
isMemberValid = selectedMemberId != null; isMemberValid = selectedMemberId != null;
MultiFileUploadWidget.showValidation = serviceId != null && FileUploadService().files.isEmpty;
// MultiFileUploadWidget.showValidation = FileUploadService().files.isEmpty;
// isClaimTypeValid = claimTypeId != null;
// isSubjectValid = subjectController.text.trim().isNotEmpty; // isSubjectValid = subjectController.text.trim().isNotEmpty;
// 🏥 GMC / Topup // 🏥 GMC / Topup
final isGmc = serviceId == 1 || serviceId == 4; final isGmc = serviceId == 1 || serviceId == 72;
isHospitalNameValid = !isGmc || hospitalNameController.text.trim().isNotEmpty;
isHospitalAddressValid = !isGmc || hospitalAddressController.text.trim().isNotEmpty; isHospitalNameValid =
isHospitalCityValid = !isGmc || hospitalCityController.text.trim().isNotEmpty; !isGmc || hospitalNameController.text.trim().isNotEmpty;
isHospitalStateValid = !isGmc || hospitalStateController.text.trim().isNotEmpty; isHospitalAddressValid =
isHospitalPincodeValid = !isGmc || hospitalPinCodeController.text.trim().isNotEmpty; !isGmc || hospitalAddressController.text.trim().isNotEmpty;
isHospitalPhoneNoValid = !isGmc || hospitalPhoneNoController.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; isAdmitDateValid = !isGmc || admitDate != null;
isDischargeDateValid = !isGmc || dischargeDate != null; isDischargeDateValid = !isGmc || dischargeDate != null;
isClaimTypeValid = !isGmc || claimTypeId != null;
isClaimAmountValid = !isGmc || claimAmountController.text.trim().isNotEmpty; isClaimAmountValid =
!isGmc || claimAmountController.text.trim().isNotEmpty;
// Accident / Death // Accident / Death
final isAccident = [2, 3, 4].contains(serviceId); // final isAccident = [2, 3, 4].contains(serviceId);
//
isAccidentService = isAccident ? true : false; // isAccidentService = isAccident ? true : false;
//
isAccidentDateValid = !isAccident || accidentDate != null; // isAccidentDateValid = !isAccident || accidentDate != null;
isIntimationDateValid = !isAccident || intimationDate != 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 && if (isServiceValid &&
isPolicyValid && isPolicyValid &&
isMemberValid && isMemberValid &&
// isSubjectValid &&
isHospitalNameValid && isHospitalNameValid &&
isHospitalAddressValid && isHospitalAddressValid &&
isHospitalStateValid && isHospitalStateValid &&
isHospitalCityValid && isHospitalCityValid &&
isHospitalPincodeValid && isHospitalPincodeValid &&
isHospitalPhoneNoValid &&
isAdmitDateValid && isAdmitDateValid &&
isHospitalPhoneNoValid &&
isDischargeDateValid && isDischargeDateValid &&
isClaimAmountValid && isClaimAmountValid &&
isAccidentDateValid && isAccidentDateValid &&
isDeathDateValid &&
isIntimationDateValid) { isIntimationDateValid) {
if (FileUploadService().files.isEmpty) {
setState(() { // Phone validation AFTER all required fields pass
MultiFileUploadWidget.hasFiles = false; // Exact 10 digit validation AFTER all required fields filled
}); if ((serviceId == 1 || serviceId == 72) &&
ToastHelper.showErrorToast(context, 'Please upload at least one document'); hospitalPhoneNoController.text.trim().isNotEmpty &&
!RegExp(r'^\d{10}$').hasMatch(hospitalPhoneNoController.text.trim())) {
ToastHelper.showErrorToast(
context,
'Hospital phone number must be exactly 10 digits',
);
return; return;
} }
// Proceed to submit
if (FileUploadService().files.isEmpty) {
ToastHelper.showErrorToast(
context,
'Please upload at least one document',
);
return;
}
} else { } else {
ToastHelper.showErrorToast(context, 'Please Fill Required Fields'); ToastHelper.showErrorToast(
context,
'Please Fill Required Fields',
);
return; return;
} }
setState(() => isSubmitting = true); // 🔥 start loader setState(() => isSubmitting = true); // 🔥 start loader
@ -453,16 +581,15 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
'relationship': selectedMemberObject?['relationship'], 'relationship': selectedMemberObject?['relationship'],
'gender': selectedMemberObject?['gender'], 'gender': selectedMemberObject?['gender'],
'age': selectedMemberObject?['dob'], 'age': selectedMemberObject?['dob'],
'policy_name': serviceId == 1 'policy_name':
? null serviceId == 1 ? null : selectedMemberObject?['policy_name'],
: selectedMemberObject?['policy_name'],
}; };
if (serviceId == 1 || serviceId == 72) { if (serviceId == 1 || serviceId == 72) {
String formattedAdmitDate = DateFormat('yyyy-MM-dd').format(admitDate!); 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_name'] = hospitalNameController.text;
fields['hospital_address'] = hospitalAddressController.text; fields['hospital_address'] = hospitalAddressController.text;
fields['hospital_city'] = hospitalCityController.text; fields['hospital_city'] = hospitalCityController.text;
@ -476,39 +603,54 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
fields['member_name'] = selectedMemberObject?['name']; fields['member_name'] = selectedMemberObject?['name'];
fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id']; fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id'];
} else { } else {
String formattedAccidentDate = if (accidentDate != null) {
fields['date_of_accident'] =
DateFormat('yyyy-MM-dd').format(accidentDate!); 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!); 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['si_amt'] = sumInsuredController.text;
fields['policyholder_name'] = employeePolicyList[0]['name']; fields['policyholder_name'] = employeePolicyList[0]['name'];
fields['member_name'] = selectedMemberName; fields['member_name'] = selectedMemberName;
fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id']; fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id'];
} }
final request = http.MultipartRequest(
final request = http.MultipartRequest('POST', Uri.parse('${Environment.apiUrlPost}initiateClaim')); 'POST', Uri.parse('${Environment.apiUrlPost}initiateClaim'));
request.headers['Authorization'] = 'Bearer $_postPreToken'; request.headers['Authorization'] = 'Bearer $_postPreToken';
request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; request.headers['APP-SIGNATURE'] =
final stringFields = fields.map((key, value) => MapEntry(key, value?.toString() ?? '')); 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
final stringFields =
fields.map((key, value) => MapEntry(key, value?.toString() ?? ''));
print("📁 stringFields: ${stringFields}");
request.fields.addAll(stringFields); request.fields.addAll(stringFields);
// get uploaded files // get uploaded files
final uploadedFiles = FileUploadService().files; final uploadedFiles = FileUploadService().files;
print("📁 Total uploaded files: ${uploadedFiles.length}"); 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) // Validate all labels present (optional but recommended)
for (final uf in uploadedFiles) { 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) { 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); setState(() => isLoading = false);
return; return;
} }
@ -581,7 +723,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
} }
} }
// Combine all names into a JSON array string // Combine all names into a JSON array string
final claimDocNames = uploadedFiles.map((uf) => uf.label.trim()).toList(); final claimDocNames = uploadedFiles.map((uf) => uf.label.trim()).toList();
final encodedNames = jsonEncode(claimDocNames); final encodedNames = jsonEncode(claimDocNames);
@ -592,12 +733,12 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
print("Files: ${uploadedFiles.map((f) => f.file.name).toList()}"); print("Files: ${uploadedFiles.map((f) => f.file.name).toList()}");
print("Names (JSON): $encodedNames"); print("Names (JSON): $encodedNames");
final response = await request.send(); final response = await request.send();
final responseBody = await response.stream.bytesToString(); final responseBody = await response.stream.bytesToString();
final decoded = jsonDecode(responseBody); final decoded = jsonDecode(responseBody);
if (decoded['status'] == true) { if (decoded['status'] == true) {
resetFormOnServiceChange();
// 1 Close dialog first // 1 Close dialog first
Navigator.pop(context); Navigator.pop(context);
@ -620,8 +761,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
Navigator.pop(context); Navigator.pop(context);
ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}"); ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}");
} }
} catch (e) { } catch (e) {
setState(() { setState(() {
isLoading = false; isLoading = false;
@ -713,9 +852,73 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
return age; return age;
} }
void resetFormOnServiceChange() {
hasSubmitted = false;
MultiFileUploadWidget.showValidation = false;
// Reset dropdown selections
selectedClientPolicyId = null;
selectedPolicyTypeId = null;
policyNumberId = null;
selectedMemberId = null;
selectedMemberName = null;
selectedMemberObject = null;
claimTypeId = null;
// Clear lists
employeePolicyList.clear();
policyNumberList.clear();
// Clear all text controllers
messageController.clear();
accidentDetailsController.clear();
hospitalNameController.clear();
hospitalAddressController.clear();
hospitalCityController.clear();
hospitalStateController.clear();
hospitalPinCodeController.clear();
hospitalPhoneNoController.clear();
claimAmountController.clear();
sumInsuredController.clear();
admitDateController.clear();
dischargeDateController.clear();
// Reset dates
accidentDate = null;
deathDate = null;
intimationDate = null;
birthDate = null;
admitDate = null;
dischargeDate = null;
// Reset validation flags
isPolicyValid = true;
isMemberValid = true;
isHospitalNameValid = true;
isHospitalAddressValid = true;
isHospitalCityValid = true;
isHospitalStateValid = true;
isHospitalPincodeValid = true;
isHospitalPhoneNoValid = true;
isAdmitDateValid = true;
isDischargeDateValid = true;
isClaimAmountValid = true;
isAccidentDateValid = true;
isIntimationDateValid = true;
isDeathDateValid = true;
// Clear uploaded files
fileService.clearAll();
MultiFileUploadWidget.hasFiles = false;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Dialog( return WillPopScope(
onWillPop: () async {
resetFormOnServiceChange();
return true;
},
child: Dialog(
backgroundColor: Colors.white, // PURE WHITE popup backgroundColor: Colors.white, // PURE WHITE popup
insetPadding: const EdgeInsets.all(20), insetPadding: const EdgeInsets.all(20),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -724,7 +927,8 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: SizedBox( 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, // height: MediaQuery.of(context).size.height * 0.85,
child: Stack( child: Stack(
children: [ children: [
@ -748,7 +952,12 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
), ),
IconButton( IconButton(
icon: const Icon(Icons.close), 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( buildDropdownField(
'Service', 'Service',
(value) { (value) {
final selectedItem = departmentList.firstWhere( final selectedItem =
departmentList.firstWhere(
(item) => item['id'] == value, (item) => item['id'] == value,
orElse: () => {}, orElse: () => {},
); );
setState(() { setState(() {
resetFormOnServiceChange();
serviceId = value; serviceId = value;
serviceName = selectedItem['name']; serviceName = selectedItem['name'];
policyNumberId = null; policyNumberId = null;
@ -775,6 +986,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
}); });
print('🔥 serviceId set to $serviceId'); print('🔥 serviceId set to $serviceId');
filterPoliciesByService(value!); filterPoliciesByService(value!);
loadClaimTypes();
}, },
departmentList, departmentList,
'name', 'name',
@ -785,15 +997,17 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
buildDropdownField( buildDropdownField(
'Select Policy', 'Select Policy',
(value) { (value) {
final selectedPolicy = final selectedPolicy = policyNumberList
policyNumberList.firstWhere((p) => p['id'] == value); .firstWhere((p) => p['id'] == value);
setState(() { setState(() {
// THIS is what you send to API // THIS is what you send to API
selectedClientPolicyId = selectedPolicy['id']; selectedClientPolicyId =
selectedPolicy['id'];
// optional // optional
selectedPolicyTypeId = selectedPolicy['policy_type_id']; selectedPolicyTypeId =
selectedPolicy['policy_type_id'];
policyNumberId = value; policyNumberId = value;
isPolicyValid = true; isPolicyValid = true;
@ -807,20 +1021,37 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
required: true, required: true,
isValid: isPolicyValid, 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( buildDropdownFieldSearch(
'Member Name', 'Member Name',
(value) { (value) {
final member = employeePolicyList.firstWhere( final member =
employeePolicyList.firstWhere(
(m) => m['id'] == value, (m) => m['id'] == value,
); );
setState(() { setState(() {
selectedMemberId = value; selectedMemberId = value;
selectedMemberObject = member; // FULL OBJECT selectedMemberObject =
member; // FULL OBJECT
selectedMemberName = member['name']; selectedMemberName = member['name'];
isMemberValid = true; isMemberValid = true;
print('selectedMemberObject $selectedMemberObject'); print(
'selectedMemberObject $selectedMemberObject');
}); });
}, },
employeePolicyList, employeePolicyList,
@ -829,42 +1060,63 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
required: true, required: true,
isValid: isMemberValid, isValid: isMemberValid,
), ),
]), ]),
_row([ _row([
buildTextField('Message', messageController), buildTextField('Message', messageController),
if (serviceId == 1 || serviceId == 72)...[
buildTextField('Hospital Name', hospitalNameController,required: true, isValid: isHospitalNameValid), if (serviceId == 1 || serviceId == 72) ...[
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) if (serviceId == 1 || serviceId == 72)
_row([ _row([
buildTextField('Hospital City', hospitalCityController,required: true, isValid: isHospitalCityValid),
buildTextField('Hospital State', hospitalStateController,required: true, isValid: isHospitalStateValid),
buildTextField( 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, hospitalPinCodeController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly, FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(6), LengthLimitingTextInputFormatter(6),
], ],
required: true, isValid: isHospitalPincodeValid required: true,
), isValid: isHospitalPincodeValid),
]), ]),
if (serviceId == 1 || serviceId == 72) if (serviceId == 1 || serviceId == 72)
_row([ _row([
buildTextField( buildTextField('Hospital Phone No',
'Hospital Phone No',
hospitalPhoneNoController, hospitalPhoneNoController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly, FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(10), 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( buildDatePickerField(
label: 'Admit Date', label: 'Admit Date',
@ -876,32 +1128,35 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
dischargeDate = null; dischargeDate = null;
}); });
}, },
required: true, isValid: isAdmitDateValid required: true,
), isValid: isAdmitDateValid),
buildDatePickerField( buildDatePickerField(
label: 'Discharge Date', label: 'Discharge Date',
selectedDate: dischargeDate, selectedDate: dischargeDate,
allowFuture: true, allowFuture: true,
minDate: admitDate?.add(const Duration(days: 1)), minDate: admitDate
onDateSelected: (d) => setState(() => dischargeDate = d), ?.add(const Duration(days: 1)),
required: true, isValid: isDischargeDateValid onDateSelected: (d) =>
), setState(() => dischargeDate = d),
required: true,
isValid: isDischargeDateValid),
buildTextField( buildTextField(
'Claims Amount', 'Claims Amount', claimAmountController,
claimAmountController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [
required: true, isValid: isClaimAmountValid FilteringTextInputFormatter.digitsOnly
), ],
required: true,
isValid: isClaimAmountValid),
]), ]),
if ([2, 3, 4].contains(serviceId)) if ([2, 3, 4].contains(serviceId))
_row([ _row([
buildDatePickerField( buildDatePickerField(
label: 'Date of Birth', label: 'Date of Birth',
selectedDate: birthDate, selectedDate: birthDate,
allowFuture: false, allowFuture: false,
onDateSelected: (d) => setState(() => birthDate = d), onDateSelected: (d) =>
setState(() => birthDate = d),
), ),
buildDatePickerField( buildDatePickerField(
label: 'Accident Date', label: 'Accident Date',
@ -914,13 +1169,16 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
intimationDate = null; intimationDate = null;
}); });
}, },
required: true, isValid: isAccidentDateValid required: serviceId == 2,
), isValid: isAccidentDateValid),
buildDatePickerField( buildDatePickerField(
label: 'Date of Death', label: 'Date of Death',
selectedDate: deathDate, selectedDate: deathDate,
allowFuture: false, 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)) if ([2, 3, 4].contains(serviceId))
@ -929,18 +1187,18 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
label: 'Date of Intimation', label: 'Date of Intimation',
selectedDate: intimationDate, selectedDate: intimationDate,
allowFuture: false, allowFuture: false,
onDateSelected: (d) => setState(() => intimationDate = d), onDateSelected: (d) =>
required: true, isValid: isIntimationDateValid setState(() => intimationDate = d)),
),
buildTextField( buildTextField(
'Sum Insured', 'Sum Insured',
sumInsuredController, sumInsuredController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [
FilteringTextInputFormatter.digitsOnly
],
), ),
const SizedBox(), const SizedBox(),
]), ]),
_row([ _row([
MultiFileUploadWidget(), MultiFileUploadWidget(),
]), ]),
@ -950,7 +1208,9 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
width: 120, width: 120,
height: 42, height: 42,
child: ElevatedButton( child: ElevatedButton(
onPressed: isSubmitting ? null : sendFormDataToApi, onPressed: (isSubmitting || serviceId == null)
? null
: sendFormDataToApi,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE26728), backgroundColor: const Color(0xFFE26728),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -968,7 +1228,8 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
) )
: const Text( : const Text(
'Send', 'Send',
style: TextStyle(color: Colors.white, style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600), fontWeight: FontWeight.w600),
), ),
), ),
@ -982,10 +1243,14 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
), ),
), ),
/// 🔄 LOADER OVERLAY (UNCHANGED) /// 🔥 LOADER OVERLAY
if (isLoading) if (isLoading)
Container( Positioned.fill(
color: const Color(0x98FFFCE5), child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.6),
borderRadius: BorderRadius.circular(20),
),
child: Center( child: Center(
child: Image.asset( child: Image.asset(
'assets/nhance-loader.gif', 'assets/nhance-loader.gif',
@ -994,12 +1259,11 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
), ),
), ),
), ),
),
], ],
), ),
), ),
) )));
);
} }
/// ---------- HELPERS ---------- /// ---------- HELPERS ----------
@ -1058,7 +1322,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
); );
} }
Widget buildTextField( Widget buildTextField(
String label, String label,
TextEditingController controller, { TextEditingController controller, {
@ -1087,7 +1350,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
); );
} }
Widget buildTextAreaField(String label, TextEditingController controller) { Widget buildTextAreaField(String label, TextEditingController controller) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -1168,9 +1430,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
); );
} }
Widget buildDropdownFieldSearch( Widget buildDropdownFieldSearch(
String label, String label,
void Function(int?) onChanged, void Function(int?) onChanged,
@ -1180,7 +1439,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
bool required = false, bool required = false,
bool isValid = true, bool isValid = true,
}) { }) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -1245,10 +1503,8 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
orElse: () => {}, orElse: () => {},
); );
final text = matchedItem[displayField] final text =
?.toString() matchedItem[displayField]?.toString().toLowerCase() ?? '';
.toLowerCase() ??
'';
return text.contains(searchValue.toLowerCase()); return text.contains(searchValue.toLowerCase());
}, },
@ -1279,7 +1535,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
); );
} }
Widget buildDatePickerField({ Widget buildDatePickerField({
required String label, required String label,
required DateTime? selectedDate, required DateTime? selectedDate,
@ -1294,7 +1549,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
fieldLabel(label, required: required), fieldLabel(label, required: required),
Container( Container(
height: 42, height: 42,
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(horizontal: 12),
@ -1320,6 +1574,7 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
initialDate: initialDate, initialDate: initialDate,
firstDate: first, firstDate: first,
lastDate: last, lastDate: last,
initialEntryMode: DatePickerEntryMode.calendarOnly,
); );
if (picked != null) { if (picked != null) {
@ -1335,18 +1590,29 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
: 'Select', : 'Select',
style: const TextStyle(color: Colors.black), 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), const Icon(Icons.calendar_today, size: 18),
], ],
), ),
), ),
), ),
errorText(isValid), errorText(isValid),
], ],
); );
} }
// Widget uploadBox() { // Widget uploadBox() {
// return Column( // return Column(
// crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
@ -1380,7 +1646,6 @@ class _RaiseClaimDialogState extends State<RaiseClaimDialog> {
child: Center(child: child), child: Center(child: child),
); );
} }
} }
// class RaiseClaimDialog extends StatelessWidget { // class RaiseClaimDialog extends StatelessWidget {

View File

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

View File

@ -1,7 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:csv/csv.dart'; import 'package:csv/csv.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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:nhancepolicy/service/token_storage_service.dart';
import 'package:universal_html/html.dart' as html; import 'package:universal_html/html.dart' as html;
import 'package:collection/collection.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../config/environment.dart';
import '../customAppBar/base_layout.dart'; import '../customAppBar/base_layout.dart';
import '../customAppBar/customAppBar.dart'; import 'cdList.dart';
import '../customAppBar/customFooter.dart'; import 'claims.dart';
class cdTransactionDetails extends StatefulWidget { class cdTransactionDetails extends StatefulWidget {
final String insurerName; final String insurerName;
@ -42,6 +41,14 @@ class cdTransactionDetails extends StatefulWidget {
} }
class _cdTransactionDetailsState extends State<cdTransactionDetails> { class _cdTransactionDetailsState extends State<cdTransactionDetails> {
String? localInsurerId;
String? localCdAcPk;
String? localEmpClientId;
String? localInsurerName;
String? localCdMasterAccountNo;
final tokenService = TokenStorageService(); final tokenService = TokenStorageService();
Uint8List? fileBytes; Uint8List? fileBytes;
List<Map<String, dynamic>> getCDTransData = []; List<Map<String, dynamic>> getCDTransData = [];
@ -85,7 +92,9 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
void initState() { void initState() {
super.initState(); super.initState();
apiService = ApiService(context); // Initialize ApiService here apiService = ApiService(context); // Initialize ApiService here
getCdTransactionDetails();
restoreTransactionData();
} }
@override @override
@ -93,9 +102,51 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
super.dispose(); super.dispose();
} }
// downloadPolicyFiles?file_id=13 // downloadPolicyFiles?file_id=13
// getPolicyAndEndorsementFiles?cd_ac_pk=12 // 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 { Future<void> getCdTransactionDetails() async {
print('9'); print('9');
setState(() { setState(() {
@ -104,8 +155,8 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
try { try {
print('10'); print('10');
final _postPreToken = await tokenService.getCurrentToken(); final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getCdTransactionData(widget.empClientId, final response = await apiService.getCdTransactionData(localEmpClientId!,
widget.insurerId, widget.cd_ac_pk, _postPreToken!); localInsurerId!, localCdAcPk!, _postPreToken!);
if (response['status'] == 'success') { if (response['status'] == 'success') {
setState(() { setState(() {
isLoading = false; isLoading = false;
@ -115,11 +166,16 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
List<Map<String, dynamic>>.from(response['data']['deposit_data']); List<Map<String, dynamic>>.from(response['data']['deposit_data']);
originalData = getCDTransData; originalData = getCDTransData;
filteredData = List.from(originalData); filteredData = List.from(originalData);
total_deposit = formatAmount(response['data']['total_deposit']); total_deposit = response['data']['total_deposit'];
total_consumed = formatAmount(response['data']['total_consumed']); // total_deposit = formatAmount(response['data']['total_deposit']);
total_refund = formatAmount(response['data']['total_refund']); total_consumed = response['data']['total_consumed'];
currect_balance = formatAmount(response['data']['currect_balance']); // total_consumed = formatAmount(response['data']['total_consumed']);
insurer_short_name = formatAmount(response['data']['insurer_short_name']); 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');
print(filteredData); print(filteredData);
}); });
@ -144,25 +200,53 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
} }
} }
Future<void> _openEndorsementFile(id) async { Future<void> _openEndorsementFile(id, file_name) async {
print('9'); // final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token);
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken(); final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getOpenEndorsementFileData(id, _postPreToken!); print("**********-------*****");
if (response['status'] == false) { final apiurl = Environment.apiUrlPost;
ToastHelper.showErrorToast(context, response['message']); final String url = '$apiurl/downloadPolicyFiles?file_id=$id';
} else {
// ToastHelper.showWarningToast( final response = await http.get(
// context, 'Request failed with status: ${response.statusCode}'); Uri.parse(url),
print('Request failed with status: ${response['code']}'); 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) { } 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 { Future<void> getCdEndorsementDetails(id) async {
print('9'); print('9');
setState(() { setState(() {
@ -243,7 +327,7 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
), ),
onTap: () { onTap: () {
// Navigator.pop(context); // close popup // 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['endorsement_no'] ?? '',
item['sub_type_text'] ?? '', item['sub_type_text'] ?? '',
item['transaction_type'] == 'Credit' ? '${formatAmount(item['amount'])}' : '-', item['transaction_type'] == 'Credit' ? '${item['amount']}' : '-',
item['transaction_type'] == 'Debit' ? '${formatAmount(item['amount'])}' : '-', // item['transaction_type'] == 'Credit' ? '${formatAmount(item['amount'])}' : '-',
'${formatAmount(item['balance']) ?? '0'}', item['transaction_type'] == 'Debit' ? '${item['amount']}' : '-',
// item['transaction_type'] == 'Debit' ? '${formatAmount(item['amount'])}' : '-',
'${item['balance'] ?? '0'}',
// '${formatAmount(item['balance']) ?? '0'}',
item['description'] ?? '', item['description'] ?? '',
item['username'] ?? '', item['username'] ?? '',
]); ]);
@ -541,7 +628,21 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
children: [ children: [
IconButton( IconButton(
tooltip: 'Previous Page', 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( icon: const Icon(
Icons.arrow_back_ios, Icons.arrow_back_ios,
size: 18, size: 18,
@ -552,7 +653,7 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
'Transaction Details - ${insurer_short_name} (${widget.cdMasterAccountNo})', 'Transaction Details - ${insurer_short_name} (${localCdMasterAccountNo ?? widget.cdMasterAccountNo})',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -762,21 +863,36 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
SizedBox(width: 10), SizedBox(width: 10),
_cell( _cell(
item['transaction_type'] == 'Credit' item['transaction_type'] == 'Credit'
? formatAmount(item['amount']) ? item['amount']
: '-', : '-',
2, 2,
alignRight: true, alignRight: true,
), ),
// _cell(
// item['transaction_type'] == 'Credit'
// ? formatAmount(item['amount'])
// : '-',
// 2,
// alignRight: true,
// ),
SizedBox(width: 10), SizedBox(width: 10),
_cell( _cell(
item['transaction_type'] == 'Debit' item['transaction_type'] == 'Debit'
? formatAmount(item['amount']) ? item['amount']
: '-', : '-',
2, 2,
alignRight: true, alignRight: true,
), ),
// _cell(
// item['transaction_type'] == 'Debit'
// ? formatAmount(item['amount'])
// : '-',
// 2,
// alignRight: true,
// ),
SizedBox(width: 10), 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), SizedBox(width: 10),
_cell(item['description'], 3), _cell(item['description'], 3),
SizedBox(width: 10), SizedBox(width: 10),
@ -784,25 +900,72 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
SizedBox(width: 10), SizedBox(width: 10),
Expanded( Expanded(
flex: 2, flex: 2,
child: SizedBox(
height: 36,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ 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, icon: Icons.picture_as_pdf_outlined,
toolTip: 'View Endorsement PDF', toolTip: 'View Endorsement PDF',
onTap: () => getCdEndorsementDetails(item['id']), onTap: () => getCdEndorsementDetails(item['id']),
), ),
),
),
const SizedBox(width: 8), 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, icon: Icons.folder_open_outlined,
toolTip: 'View Files', toolTip: 'View Files',
onTap: () => _launchURL(item['split_up_url']), 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); return filteredData.sublist(startIndex, endIndex);
} }
int? appliedClaimStatus;
Color getStatusColor(String status) { Color getStatusColor(String status) {
switch (status.toLowerCase()) { switch (status.toLowerCase()) {
case 'claim received': case 'claim received':
@ -182,7 +184,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
}); });
try { try {
print('10'); print('10');
final response = await apiService.getClaimPoliciesToApi(_postPreToken!); final response = await apiService.getClaimPoliciesToApi(_postPreToken!,'');
if (response['status'] == 'success') { if (response['status'] == 'success') {
setState(() { setState(() {
isLoading = false; isLoading = false;
@ -272,6 +274,9 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
final data = claim_Detials(); final data = claim_Detials();
print("data -- $data"); print("data -- $data");
getClaimList(); getClaimList();
setState(() {
appliedClaimStatus = selectedClaimStatus; // apply only after API call
});
} }
Future<void> reset() async { Future<void> reset() async {
@ -285,6 +290,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
selectedClaimStatus = null; selectedClaimStatus = null;
selectedClaimStatusName = null; selectedClaimStatusName = null;
_currentPage = 1; _currentPage = 1;
appliedClaimStatus = null;
getClaimList(); getClaimList();
}); });
@ -482,16 +488,19 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
/// 🔙 Back + Title (LEFT) /// 🔙 Back + Title (LEFT)
Row( Row(
children: [ children: [
// IconButton( if(widget.empCode.isNotEmpty)
// onPressed: () => {}, IconButton(
// icon: const Icon( onPressed: () => {
// Icons.arrow_back_ios, Navigator.of(context).pop(),
// size: 18, },
// color: Colors.black, icon: const Icon(
// ), Icons.arrow_back_ios,
// padding: EdgeInsets.zero, size: 18,
// constraints: const BoxConstraints(), color: Colors.black,
// ), ),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
'Claims', 'Claims',
@ -707,11 +716,6 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
); );
} }
Widget _buildDataRow(Map<String, dynamic> item) { Widget _buildDataRow(Map<String, dynamic> item) {
return Container( return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
@ -866,21 +870,28 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
if (statusMaster.isEmpty) return const SizedBox(); 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( return SizedBox(
height: 30, height: 35,
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: statusMaster.length, itemCount: displayStatuses.length,
separatorBuilder: (_, __) => const SizedBox(width: 10), separatorBuilder: (_, __) => const SizedBox(width: 10),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final statusName = final statusName =
statusMaster[index]['claim_status']?.toString() ?? ''; displayStatuses[index]['claim_status']?.toString() ?? '';
final count = getStatusCount(statusName); final count = getStatusCount(statusName);
final color = getStatusColor(statusName); final color = getStatusColor(statusName);
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color, color: color,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),

View File

@ -34,6 +34,9 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
final tokenService = TokenStorageService(); final tokenService = TokenStorageService();
final FocusNode _policyFocusNode = FocusNode(); final FocusNode _policyFocusNode = FocusNode();
bool isTpaDashboardEnabled = false;
bool isTpaSelected = false;
// Variables for API data // Variables for API data
dynamic empClientBranchId; dynamic empClientBranchId;
dynamic empHrId; dynamic empHrId;
@ -41,6 +44,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
String? _postPreToken = ''; String? _postPreToken = '';
int stausVal = 1; int stausVal = 1;
bool _isPolicyDropdownOpen = false; bool _isPolicyDropdownOpen = false;
html.IFrameElement? _currentIframe;
@override @override
void initState() { void initState() {
@ -114,17 +118,32 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
_metabaseLoaded = false; _metabaseLoaded = false;
}); });
// REMOVE OLD IFRAME COMPLETELY
_currentIframe?.remove();
_currentIframe = null;
// CLEAR OLD VIEW TYPES
_registeredViewTypes.clear();
final response = await apiService.postHrDashboard({ final response = await apiService.postHrDashboard({
"client_id": empClientId, "client_id": empClientId,
"client_policy_id": clientPolicyId, "client_policy_id": clientPolicyId,
}, _postPreToken); }, _postPreToken);
if (response['status'] == 'success') { if (response['status'] == 'success') {
// ADD THIS LINE
isTpaDashboardEnabled = response['is_tpa_dashboard_enable'] == true;
setState(() {
isTpaSelected = false;
});
_registerMetabaseIframe( _registerMetabaseIframe(
token: response['data']['metabaseToken'], token: response['data']['metabaseToken'],
url: response['data']['metabaseUrl'], url: response['data']['metabaseUrl'],
clientPolicyId: clientPolicyId, clientPolicyId: clientPolicyId,
); );
setState(() => _metabaseLoaded = true); setState(() => _metabaseLoaded = true);
} else { } else {
ToastHelper.showErrorToast(context, response['message']); 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({ void _registerMetabaseIframe({
required String token, required String token,
required String url, required String url,
required String clientPolicyId, required String clientPolicyId,
}) { }) {
final viewType = 'metabase-dashboard-$clientPolicyId'; // 🔥 ALWAYS CREATE UNIQUE VIEW TYPE
final viewType =
'metabase-dashboard-${clientPolicyId}-${DateTime.now().millisecondsSinceEpoch}';
_dashboardViewType = viewType; _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"; final iframe = html.IFrameElement()
ui.platformViewRegistry.registerViewFactory(
viewType,
(int viewId) => html.IFrameElement()
..src = embedUrl ..src = embedUrl
..style.border = 'none' ..style.border = 'none'
..style.width = '100%' ..style.width = '100%'
..style.height = '100%' ..style.height = '100%'
..allowFullscreen = true, ..allowFullscreen = true;
_currentIframe = iframe;
ui.platformViewRegistry.registerViewFactory(
viewType,
(int viewId) => iframe,
); );
_registeredViewTypes.add(viewType); _registeredViewTypes.add(viewType);
@ -185,7 +252,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
Widget _buildDashboardView() { Widget _buildDashboardView() {
if (postModules.isNotEmpty && activePoliciesList.isEmpty) { 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( return Padding(
@ -339,6 +406,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
color: Colors.white, color: Colors.white,
child: Row( child: Row(
children: [ children: [
if (!isTpaSelected)...[
const Text( const Text(
'Select Policy', 'Select Policy',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13), 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 { // Future<void> _loadToken() async {
final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]" // final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]"
final postRaw = await tokenService.readValue('empAllowed_modules'); // "[2,3,4]" // 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 enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
? List<int>.from(jsonDecode(enrollmentRaw)) ? List<int>.from(jsonDecode(enrollmentRaw))
: []; : [];
@ -119,30 +164,56 @@ class _policiesState extends State<policies>
? List<int>.from(jsonDecode(postRaw)) ? List<int>.from(jsonDecode(postRaw))
: []; : [];
print('enrollmentModules $enrollmentModules');
print('postModules $postModules');
_postPreToken = await tokenService.getCurrentToken(); _postPreToken = await tokenService.getCurrentToken();
print(_postPreToken);
List<Future> apiCalls = [];
/// 👇 Add APIs dynamically
if (enrollmentModules.contains(1)) { if (enrollmentModules.contains(1)) {
enrollmentClient_id = await tokenService.readValue('enrollmentClient_id'); enrollmentClient_id =
await tokenService.readValue('enrollmentClient_id');
enrollmentEmpClientBranchId = enrollmentEmpClientBranchId =
await tokenService.readValue('enrollmentEmpClientBranchId'); await tokenService.readValue('enrollmentEmpClientBranchId');
enrollmentHrId = await tokenService.readValue('enrollmentHrId'); enrollmentHrId =
await tokenService.readValue('enrollmentHrId');
await getPreCashDepositDetails(enrollmentEmpClientBranchId, apiCalls.add(
enrollmentClient_id, enrollmentHrId, _postPreToken); getPreCashDepositDetails(
enrollmentEmpClientBranchId,
enrollmentClient_id,
enrollmentHrId,
_postPreToken,
),
);
} }
if (postModules.contains(2)) { if (postModules.contains(2)) {
empClientId = await tokenService.readValue('empClientId'); empClientId = await tokenService.readValue('empClientId');
empClientBranchId = await tokenService.readValue('empClientBranchId'); empClientBranchId =
await tokenService.readValue('empClientBranchId');
empHrId = await tokenService.readValue('empHrId'); 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, Future<void> getPreCashDepositDetails(enrollmentEmpClientBranchId,
@ -153,7 +224,6 @@ class _policiesState extends State<policies>
print("hr_id -$enrollmentHrId"); print("hr_id -$enrollmentHrId");
print("token -$_postPreToken"); print("token -$_postPreToken");
isLoading = true;
// setState(() { // setState(() {
// _isLoading = true; // _isLoading = true;
// }); // });
@ -171,7 +241,6 @@ class _policiesState extends State<policies>
// clintID!, clintBranchId!, hr_id, token); // clintID!, clintBranchId!, hr_id, token);
print('IN1'); print('IN1');
if (response['status'] == 'success') { if (response['status'] == 'success') {
isLoading = false;
setState(() { setState(() {
print('response'); print('response');
print(response['data']); print(response['data']);
@ -198,7 +267,6 @@ class _policiesState extends State<policies>
print("hr_id -$empHrId"); print("hr_id -$empHrId");
print("token -$_postPreToken"); print("token -$_postPreToken");
isLoading = true;
// setState(() { // setState(() {
// _isLoading = true; // _isLoading = true;
// }); // });
@ -213,7 +281,6 @@ class _policiesState extends State<policies>
// clintID!, clintBranchId!, hr_id, token); // clintID!, clintBranchId!, hr_id, token);
print('IN1'); print('IN1');
if (response['status'] == 'success') { if (response['status'] == 'success') {
isLoading = false;
setState(() { setState(() {
print('response'); print('response');
print(response['data']); print(response['data']);
@ -226,7 +293,6 @@ class _policiesState extends State<policies>
print('IN2'); print('IN2');
} else { } else {
isLoading = false;
print('API request failed with status'); print('API request failed with status');
setState(() { setState(() {
activePoliciesList = []; activePoliciesList = [];
@ -345,7 +411,16 @@ class _policiesState extends State<policies>
required List<Map<String, dynamic>> openEnrollment, required List<Map<String, dynamic>> openEnrollment,
required List<Map<String, dynamic>> activePolicies, 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( body: SingleChildScrollView(
// padding: const EdgeInsets.all(20), // padding: const EdgeInsets.all(20),
@ -385,12 +460,15 @@ class _policiesState extends State<policies>
SizedBox(height: 15), SizedBox(height: 15),
Container( Container(
width: double.infinity, width: double.infinity,
height: 400, // constraints: const BoxConstraints(
// minHeight: 180,
// ),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: IntrinsicHeight(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -401,23 +479,26 @@ class _policiesState extends State<policies>
SizedBox(height: 14), SizedBox(height: 14),
/// SCROLLABLE AREA /// SCROLLABLE AREA
Expanded( openEnrollment.isEmpty
child: openEnrollment.isEmpty
? _EmptyBox('No policies open for enrollment') ? _EmptyBox('No policies open for enrollment')
: _PolicyGrid( : _PolicyGrid(
policies: openEnrollment, policies: openEnrollment,
isEnrollment: true, isEnrollment: true,
), ),
),
], ],
), ),
)
), ),
], ],
if(postModules.contains(2))...[ if(postModules.contains(2))...[
SizedBox(height: 20), SizedBox(height: 20),
Container( Container(
width: double.infinity, width: double.infinity,
height: 400, // constraints: const BoxConstraints(
// minHeight: 180,
// ),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
@ -454,15 +535,14 @@ class _policiesState extends State<policies>
const SizedBox(height: 14), const SizedBox(height: 14),
/// SCROLLABLE GRID /// SCROLLABLE GRID
Expanded( activePolicies.isEmpty
child: activePolicies.isEmpty
? stausVal == 0 ? _EmptyBox('You dont have any expired policies at the moment.') : _EmptyBox('No active policies found') ? stausVal == 0 ? _EmptyBox('You dont have any expired policies at the moment.') : _EmptyBox('No active policies found')
: _PolicyGrid( : _PolicyGrid(
policies: activePolicies, policies: activePolicies,
isEnrollment: false, isEnrollment: false,
onBulkDownload: getEcardBulkDownload, onBulkDownload: getEcardBulkDownload,
), ),
),
const SizedBox(height: 8), const SizedBox(height: 8),
Align( Align(
@ -513,31 +593,62 @@ class _PolicyGrid extends StatelessWidget {
) { ) {
final width = MediaQuery.of(context).size.width; final width = MediaQuery.of(context).size.width;
if (width < 600) { // if (width < 600) {
return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15); // return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15);
} else if (width < 900) { // } else if (width < 900) {
return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45); // return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45);
} else if (width < 1400) { // } else if (width < 1400) {
return ResponsiveGridConfig(3, isEnrollment ? 3.1 : 2.5); // 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 { } else {
return ResponsiveGridConfig(4, isEnrollment ? 3.1 : 2.4); return const ResponsiveGridConfig(1, 1.8); // Mobile
} }
} }
@override
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final tokenService = TokenStorageService(); final tokenService = TokenStorageService();
final config = _getGridConfig(context, isEnrollment); 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(), physics: const BouncingScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: config.crossAxisCount, crossAxisCount: config.crossAxisCount,
childAspectRatio: config.childAspectRatio, childAspectRatio: config.childAspectRatio,
crossAxisSpacing: 16, crossAxisSpacing: 16,
mainAxisSpacing: 16, mainAxisSpacing: 16,
mainAxisExtent: isEnrollment ? 120 : 150, // fixed card height
), ),
itemCount: policies.length, itemCount: policies.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@ -590,12 +701,15 @@ class _PolicyGrid extends StatelessWidget {
onBulkDownload: onBulkDownload, onBulkDownload: onBulkDownload,
onTap: () async { onTap: () async {
final token = await tokenService.getCurrentToken(); final token = await tokenService.getCurrentToken();
final clientId = final clientId = await tokenService.readValue('empClientId');
await tokenService.readValue('empClientId'); final branchId = await tokenService.readValue('empClientBranchId');
final branchId =
await tokenService.readValue('empClientBranchId'); print("token: $token");
print("clientId: $clientId");
print("branchId: $branchId");
if (token == null || clientId == null || branchId == null) { if (token == null || clientId == null || branchId == null) {
print("Missing required values");
return; 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.cardInsurer_name,
required this.cardPolicy_name, required this.cardPolicy_name,
required this.cardPolicy_ExpDate, required this.cardPolicy_ExpDate,
required this.total_premium required this.total_premium})
})
: super(key: key); : super(key: key);
@override @override
@ -61,6 +60,23 @@ class postFileUpload extends StatefulWidget {
class _postFileUploadState extends State<postFileUpload> { class _postFileUploadState extends State<postFileUpload> {
final tokenService = TokenStorageService(); 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? fileBytes;
Uint8List? fileBytes2; Uint8List? fileBytes2;
late String _token; late String _token;
@ -101,9 +117,8 @@ class _postFileUploadState extends State<postFileUpload> {
bool showSampleButton = false; bool showSampleButton = false;
String? currentApiValue; // To store the 'value' for the 2nd param String? currentApiValue; // To store the 'value' for the 2nd param
int _currentPage = 1; int _currentPage = 1;
int _rowsPerPage = 5; int _rowsPerPage = 6;
List<dynamic> get _paginatedData { List<dynamic> get _paginatedData {
final startIndex = (_currentPage - 1) * _rowsPerPage; final startIndex = (_currentPage - 1) * _rowsPerPage;
@ -125,9 +140,12 @@ class _postFileUploadState extends State<postFileUpload> {
void initState() { void initState() {
super.initState(); super.initState();
apiService = ApiService(context); apiService = ApiService(context);
restoreUploadData().then((_) {
_loadToken(); _loadToken();
getFileUploadMasterDetails(); getFileUploadMasterDetails();
getFileListDetails(); getFileListDetails();
});
} }
@override @override
@ -137,22 +155,85 @@ class _postFileUploadState extends State<postFileUpload> {
} }
Future<void> _loadToken() async { Future<void> _loadToken() async {
// final token = prefs.getString('hrtoken'); final token = localToken.isNotEmpty
final token = widget.Token; ? localToken
: await tokenService.readValue('upload_Token');
if (token != null && token.isNotEmpty) { if (token != null && token.isNotEmpty) {
setState(() { setState(() {
_token = token; _token = token;
}); });
Map<String, dynamic>? decodedToken = Jwt.parseJwt(token);
print('decodedToken $decodedToken');
} else { } 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'); ToastHelper.showErrorToast(context, 'Session Out');
Navigator.pushReplacementNamed(context, 'hrLogin'); 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 { // Future<void> getPolicyDetails() async {
// setState(() { // setState(() {
// clientPolicyId = argumentsData['client_policy_id']; // clientPolicyId = argumentsData['client_policy_id'];
@ -184,7 +265,7 @@ class _postFileUploadState extends State<postFileUpload> {
Future<void> getFileUploadMasterDetails() async { Future<void> getFileUploadMasterDetails() async {
print('9'); print('9');
try { try {
final response = await apiService.getFileUploadMastersToApi(widget.Token); final response = await apiService.getFileUploadMastersToApi(localToken);
if (response['status'] == true) { if (response['status'] == true) {
print('getFileUploadMasterList1'); print('getFileUploadMasterList1');
@ -220,8 +301,8 @@ class _postFileUploadState extends State<postFileUpload> {
print('9'); print('9');
try { try {
final response = await apiService.getFileListToApi( final response = await apiService.getFileListToApi(empPrimaryId,
empPrimaryId, widget.cardPolicyNo, empClientId,widget.Token,widget.TokenType); localCardPolicyNo, empClientId, localToken, localTokenType);
if (response['status'] == 'success') { if (response['status'] == 'success') {
print('getThrFileList'); print('getThrFileList');
@ -248,18 +329,20 @@ class _postFileUploadState extends State<postFileUpload> {
} }
Future<void> getHrFileDownload(id, file_name) async { 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("**********-------*****"); print("**********-------*****");
final encryptClientId = widget.ClientId; final encryptClientId = localClientId;
print(encryptClientId); print(encryptClientId);
final apiurl = Environment.apiUrlPost; final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId'; final String url =
final token = widget.Token; '$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId';
final token = localToken;
final response = await http.get( final response = await http.get(
Uri.parse(url), Uri.parse(url),
headers: { headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', 'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token', 'Authorization': 'Bearer $token',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456', // 'app-signature': 'ts-traveltool-2025-signature-123456',
@ -297,16 +380,17 @@ class _postFileUploadState extends State<postFileUpload> {
Future<void> downloadPostSampleFile(String apiParam) async { Future<void> downloadPostSampleFile(String apiParam) async {
print("fun Sam f - in"); print("fun Sam f - in");
final post_file_name = apiParam+'_sample_file.xlsx'; final post_file_name = apiParam + '_sample_file.xlsx';
print("fun Sam f - name $post_file_name" ); print("fun Sam f - name $post_file_name");
final apiurl = Environment.apiUrlPost; final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/downloadSampleExcel/$apiParam'; final String url = '$apiurl/downloadSampleExcel/$apiParam';
final token = widget.Token; final token = localToken;
final response = await http.get( final response = await http.get(
Uri.parse(url), Uri.parse(url),
headers: { headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', 'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token', 'Authorization': 'Bearer $token',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456', // 'app-signature': 'ts-traveltool-2025-signature-123456',
@ -315,7 +399,7 @@ class _postFileUploadState extends State<postFileUpload> {
if (response.statusCode == 200) { if (response.statusCode == 200) {
try { try {
print("fun sam f - ${response.statusCode}" ); print("fun sam f - ${response.statusCode}");
// Create a blob from the response body bytes // Create a blob from the response body bytes
final blob = html.Blob([response.bodyBytes]); final blob = html.Blob([response.bodyBytes]);
@ -333,7 +417,7 @@ class _postFileUploadState extends State<postFileUpload> {
ToastHelper.showSuccessToast(context, 'File Downloaded Successfully'); ToastHelper.showSuccessToast(context, 'File Downloaded Successfully');
} catch (e) { } catch (e) {
print("fun sam f - fail" ); print("fun sam f - fail");
throw Exception('Error parsing response: $e'); throw Exception('Error parsing response: $e');
} }
} else { } else {
@ -475,7 +559,8 @@ class _postFileUploadState extends State<postFileUpload> {
print('else'); print('else');
// Attach the file to the request // Attach the file to the request
// Set authorization token in headers // 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.headers['Authorization'] = 'Bearer $_token';
// request.files.add(http.MultipartFile.fromBytes('file', fileBytes, // request.files.add(http.MultipartFile.fromBytes('file', fileBytes,
// filename: fileName)); // filename: fileName));
@ -492,13 +577,13 @@ class _postFileUploadState extends State<postFileUpload> {
)); ));
print('clintID: $clintID'); print('clintID: $clintID');
request.fields['client_id'] = widget.ClientId; request.fields['client_id'] = localClientId;
request.fields['policy_no'] = widget.cardPolicyNo; request.fields['policy_no'] = localCardPolicyNo;
request.fields['client_branch_id'] = widget.clientBranchId; request.fields['client_branch_id'] = localClientBranchId;
request.fields['file_action'] = selectedKey!; request.fields['file_action'] = selectedKey!;
// request.fields['status'] = selectedKey!; // request.fields['status'] = selectedKey!;
request.fields['created_by'] = empPrimaryId; request.fields['created_by'] = empPrimaryId;
request.fields['policy_id'] = widget.ClientPoliyId; request.fields['policy_id'] = localClientBranchId;
// "client_id": 1, // "client_id": 1,
// "client_branch_id": 2, // "client_branch_id": 2,
// "policy_no": "POL123456", // "policy_no": "POL123456",
@ -520,15 +605,15 @@ class _postFileUploadState extends State<postFileUpload> {
isLoading = false; isLoading = false;
}); });
ToastHelper.showSuccessToast(context, data['message']); ToastHelper.showSuccessToast(context, data['message']);
getFileListDetails();
setState(() { setState(() {
selectedValue = null; selectedValue = null;
selectedKey = null; selectedKey = null;
resetErrorCount(); resetErrorCount();
getFileListDetails();
}); });
} else { } else {
getFileListDetails();
setState(() { setState(() {
getFileListDetails();
isLoading = false; isLoading = false;
selectedValue = null; selectedValue = null;
selectedKey = null; selectedKey = null;
@ -587,7 +672,8 @@ class _postFileUploadState extends State<postFileUpload> {
} }
Widget _buildContent(BuildContext context) { Widget _buildContent(BuildContext context) {
return isLoading ? Container( return isLoading
? Container(
color: Colors.transparent, // Semi-transparent background color: Colors.transparent, // Semi-transparent background
child: Center( child: Center(
child: // Your GIF loader widget child: // Your GIF loader widget
@ -596,21 +682,61 @@ class _postFileUploadState extends State<postFileUpload> {
width: 60, width: 60,
'assets/nhance-loader.gif'), // Adjust path to your GIF loader 'assets/nhance-loader.gif'), // Adjust path to your GIF loader
), ),
): Container( )
: Container(
child: Column( child: Column(
children: [ children: [
Row( Row(
children: [ children: [
IconButton( IconButton(
tooltip: 'Previous Page', 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( icon: const Icon(
Icons.arrow_back_ios, Icons.arrow_back_ios,
size: 18, size: 18,
color: Colors.black, color: Colors.black,
), ),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
Expanded( Expanded(
@ -619,7 +745,7 @@ class _postFileUploadState extends State<postFileUpload> {
// mainAxisAlignment: MainAxisAlignment.start, // mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
"${widget.cardType} - ${widget.cardPolicyNo} " ?? "${localCardType} - ${localCardPolicyNo} " ??
'', '',
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
color: Colors.black, color: Colors.black,
@ -628,10 +754,13 @@ class _postFileUploadState extends State<postFileUpload> {
), ),
), ),
Text( Text(
widget.TokenType == 'pre' localTokenType == 'pre'
? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})" ? "${localCardPolicyName} (${localCardPolicyExpDate})"
: "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})", : "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})",
style: GoogleFonts.poppins(color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w400), style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 12,
fontWeight: FontWeight.w400),
), ),
], ],
), ),
@ -650,11 +779,15 @@ class _postFileUploadState extends State<postFileUpload> {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE26728), backgroundColor: const Color(0xFFE26728),
elevation: 0, elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
), ),
child: Text( child: Text(
'Sample Excel', 'Sample Excel',
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white), style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w700,
color: Colors.white),
), ),
), ),
), ),
@ -662,7 +795,12 @@ class _postFileUploadState extends State<postFileUpload> {
), ),
], ],
), ),
SizedBox(height:20), SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -676,7 +814,8 @@ class _postFileUploadState extends State<postFileUpload> {
onChanged: (val) { onChanged: (val) {
setState(() { setState(() {
selectedKey = val; selectedKey = val;
final selectedItem = getFileUploadMasterList.firstWhere((e) => e['key'] == val); final selectedItem = getFileUploadMasterList
.firstWhere((e) => e['key'] == val);
selectedValue = selectedItem['value']; selectedValue = selectedItem['value'];
currentApiValue = selectedItem['key']; currentApiValue = selectedItem['key'];
showSampleButton = true; showSampleButton = true;
@ -725,7 +864,8 @@ class _postFileUploadState extends State<postFileUpload> {
}); });
_dragAndDropFile(droppedFile); _dragAndDropFile(droppedFile);
}, },
builder: (context, candidateData, rejectedData) { builder:
(context, candidateData, rejectedData) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
if (selectedValue != null) { if (selectedValue != null) {
@ -738,11 +878,13 @@ class _postFileUploadState extends State<postFileUpload> {
} }
}, },
child: Container( child: Container(
height: 40 , height: 40,
padding: const EdgeInsets.symmetric(horizontal: 12), padding: const EdgeInsets.symmetric(
horizontal: 12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(8), borderRadius:
BorderRadius.circular(8),
border: Border.all( border: Border.all(
color: const Color(0xFF00A6A6), color: const Color(0xFF00A6A6),
width: 1, width: 1,
@ -752,8 +894,10 @@ class _postFileUploadState extends State<postFileUpload> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
fileName ?? 'Upload Your Documents', fileName ??
overflow: TextOverflow.ellipsis, 'Upload Your Documents',
overflow:
TextOverflow.ellipsis,
style: GoogleFonts.poppins( style: GoogleFonts.poppins(
fontSize: 13, fontSize: 13,
color: fileName == null color: fileName == null
@ -769,8 +913,7 @@ class _postFileUploadState extends State<postFileUpload> {
), ),
], ],
), ),
) ));
);
}, },
), ),
], ],
@ -779,25 +922,17 @@ class _postFileUploadState extends State<postFileUpload> {
], ],
), ),
SizedBox(height: 20), SizedBox(height: 20),
Row( Column(
children: [
Expanded(
child: Column(
children: [ children: [
_buildFileUploadedGrid(), _buildFileUploadedGrid(),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildPagination(context), _buildPagination(context),
], ],
), ),
) ])))
],
),
], ],
), ),
); );
} }
Widget buildUploadBox({ Widget buildUploadBox({
@ -852,8 +987,6 @@ class _postFileUploadState extends State<postFileUpload> {
); );
} }
Widget buildStyledDropdown({ Widget buildStyledDropdown({
required String label, required String label,
required String? value, required String? value,
@ -906,7 +1039,6 @@ class _postFileUploadState extends State<postFileUpload> {
); );
} }
Widget _buildFileUploadedGrid() { Widget _buildFileUploadedGrid() {
if (filteredData.isEmpty) { if (filteredData.isEmpty) {
return const SizedBox( return const SizedBox(
@ -916,13 +1048,14 @@ class _postFileUploadState extends State<postFileUpload> {
} }
return GridView.builder( return GridView.builder(
shrinkWrap: true, shrinkWrap: true, // IMPORTANT
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(), // Disable inner scroll
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 👈 2 cards per row crossAxisCount: 2,
crossAxisSpacing: 16, crossAxisSpacing: 16,
mainAxisSpacing: 16, mainAxisSpacing: 16,
childAspectRatio: 10, // 👈 card height childAspectRatio: 10,
), ),
itemCount: _paginatedData.length, itemCount: _paginatedData.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@ -930,6 +1063,8 @@ class _postFileUploadState extends State<postFileUpload> {
return _buildFileCard(item); return _buildFileCard(item);
}, },
); );
} }
Widget _buildFileCard(Map<String, dynamic> item) { Widget _buildFileCard(Map<String, dynamic> item) {
@ -1014,9 +1149,7 @@ class _postFileUploadState extends State<postFileUpload> {
children: [ children: [
/// 🔴 Error + Status /// 🔴 Error + Status
Row( Row(
children: [ children: [],
],
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@ -1028,36 +1161,39 @@ class _postFileUploadState extends State<postFileUpload> {
onTap: () async { onTap: () async {
print(item); print(item);
// return; // return;
final String? token = await tokenService.getCurrentToken(); final String? token =
final String? empClientId = await tokenService.readValue('empClientId'); await tokenService.getCurrentToken();
final String? empBranchId = await tokenService.readValue('empClientBranchId'); final String? empClientId =
await tokenService.readValue('empClientId');
final String? empBranchId =
await tokenService.readValue('empClientBranchId');
print(item); print(item);
print(empClientId); print(empClientId);
print(widget.policyTypeId); print(localPolicyTypeId);
print(empBranchId); print(empBranchId);
print(token); print(token);
print('post'); print('post');
print(widget.cardType); print(localCardType);
print(widget.cardPolicyNo); print(localCardPolicyNo);
print(widget.cardInsurer_name); print(localCardInsurerName);
print(widget.cardPolicy_name); print(localCardPolicyName);
print(widget.cardPolicy_ExpDate); print(localCardPolicyExpDate);
print(item['id']); print(item['id']);
// SAFETY CHECK // SAFETY CHECK
if (token == null || if (token == null ||
empClientId == null || empClientId == null ||
empBranchId == null) { empBranchId == null) {
debugPrint('❌ Missing required data for navigation ${token}'); debugPrint(
'❌ Missing required data for navigation ${token}');
return; return;
} }
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => excelErrorScreen(
excelErrorScreen(
ClientId: empClientId, ClientId: empClientId,
policy_no: item['policy_no'], policy_no: item['policy_no'],
action: item['file_action'], action: item['file_action'],
@ -1065,8 +1201,7 @@ class _postFileUploadState extends State<postFileUpload> {
clientBranchId: empBranchId, clientBranchId: empBranchId,
Token: token, Token: token,
TokenType: 'post', 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) { Widget _buildStatusChip(String status) {
final s = status.toLowerCase(); final s = status.toLowerCase();
@ -1142,7 +1276,6 @@ class _postFileUploadState extends State<postFileUpload> {
); );
} }
static final _dataBold = GoogleFonts.poppins( static final _dataBold = GoogleFonts.poppins(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@ -1161,14 +1294,14 @@ class _postFileUploadState extends State<postFileUpload> {
); );
Widget _buildPagination(BuildContext context) { Widget _buildPagination(BuildContext context) {
final totalItems = filteredData.length; 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; int endEntry = _currentPage * _rowsPerPage;
if (endEntry > totalItems) endEntry = totalItems; if (endEntry > totalItems) endEntry = totalItems;
final totalPages = (filteredData.length / _rowsPerPage).ceil(); final totalPages = (filteredData.length / _rowsPerPage).ceil();
const visiblePageCount = 5; const visiblePageCount = 6;
List<int> getVisiblePages() { List<int> getVisiblePages() {
if (totalPages <= visiblePageCount) { if (totalPages <= visiblePageCount) {
@ -1177,7 +1310,8 @@ class _postFileUploadState extends State<postFileUpload> {
if (_currentPage <= 3) { if (_currentPage <= 3) {
return [1, 2, 3, 4, 5]; return [1, 2, 3, 4, 5];
} if (_currentPage >= totalPages - 2) { }
if (_currentPage >= totalPages - 2) {
return [ return [
totalPages - 4, totalPages - 4,
totalPages - 3, totalPages - 3,
@ -1193,7 +1327,6 @@ class _postFileUploadState extends State<postFileUpload> {
_currentPage + 1, _currentPage + 1,
_currentPage + 2, _currentPage + 2,
]; ];
} }
List<int> visiblePages = getVisiblePages(); List<int> visiblePages = getVisiblePages();
@ -1208,7 +1341,7 @@ class _postFileUploadState extends State<postFileUpload> {
// Dropdown for rows per page // Dropdown for rows per page
DropdownButton<int>( DropdownButton<int>(
value: _rowsPerPage, value: _rowsPerPage,
items: [5, 10, 15, 20, 50].map((int value) { items: [6, 10, 15, 20, 50].map((int value) {
return DropdownMenuItem<int>( return DropdownMenuItem<int>(
value: value, value: value,
child: Text(' $value ', child: Text(' $value ',

View File

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

View File

@ -12,6 +12,8 @@ class ApiService {
String? _token; String? _token;
String? _hrtoken; String? _hrtoken;
bool _isSessionOutToastShown = false; // Flag to track toast message bool _isSessionOutToastShown = false; // Flag to track toast message
bool isTpaDashboardEnabled = false;
bool isTpaSelected = false; // track which dashboard is active
ApiService(this.context) { ApiService(this.context) {
_initializeToken(); _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"); print("getgetClaimPoliciesToApii1");
final url = Uri.parse('${Environment.apiUrlPost}claimsSearch'); final url = Uri.parse('${Environment.apiUrlPost}claimsSearch?client_id=$empClientId');
final headers = { final headers = {
'Authorization': 'Bearer $token' ?? '', 'Authorization': 'Bearer $token' ?? '',
@ -993,6 +1018,10 @@ class ApiService {
} }
Future<Map<String, dynamic>> _handleResponse(http.Response response) async { Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
// 'throttle' => 429,
// 'soft' => 429,
// 'medium' => 403,
// 'hard' => 451,
if (response.statusCode == 200) { if (response.statusCode == 200) {
return jsonDecode(response.body); return jsonDecode(response.body);
} else if (response.statusCode == 401) { } else if (response.statusCode == 401) {
@ -1001,6 +1030,17 @@ class ApiService {
await _clearLocalStorageAndRedirect(); await _clearLocalStorageAndRedirect();
} }
return {}; 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) { } else if (response.statusCode == 429) {
final body = jsonDecode(response.body); final body = jsonDecode(response.body);
final message = body['message']; final message = body['message'];

View File

@ -11,6 +11,7 @@ class MultiFileUploadWidget extends StatefulWidget {
State<MultiFileUploadWidget> createState() => _MultiFileUploadWidgetState(); State<MultiFileUploadWidget> createState() => _MultiFileUploadWidgetState();
static bool hasFiles = false; static bool hasFiles = false;
static bool showValidation = false;
} }
class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> { class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
@ -44,6 +45,7 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
setState(() { setState(() {
errorMessage = null; errorMessage = null;
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty; MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty;
MultiFileUploadWidget.showValidation = false;
}); });
} }
} }
@ -52,6 +54,9 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
fileService.removeFileAt(index); fileService.removeFileAt(index);
setState(() { setState(() {
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty; 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 SizedBox(height: 4),
const Text( const Text(
"Required", "Required",

View File

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

View File

@ -33,6 +33,26 @@
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<style> <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 { .content {
width: 10%; width: 10%;
height: 10vh; height: 10vh;