From 72b253918a4599309d91396f4853cf237016520f Mon Sep 17 00:00:00 2001 From: SurendarSuri30 Date: Tue, 17 Mar 2026 10:55:37 +0530 Subject: [PATCH] bug fix --- lib/customAppBar/side_bar.dart | 2 +- lib/email_verify.dart | 46 +- lib/hrLogin.dart | 136 +- lib/main.dart | 387 +++-- lib/presentation/RaiseClaimForm.dart | 745 ++++++--- lib/presentation/cdList.dart | 32 +- lib/presentation/cdTransactionDetails.dart | 263 ++- lib/presentation/claims.dart | 51 +- lib/presentation/hrDashboard.dart | 295 ++-- lib/presentation/hrPolicyDetails.dart | 1692 ++++++++++++-------- lib/presentation/policies.dart | 445 +++-- lib/presentation/postFileUpload.dart | 759 +++++---- lib/presentation/preFileUpload.dart | 1189 ++++++++------ lib/service/api_service.dart | 44 +- lib/service/multi_file_upload_widget.dart | 9 +- lib/service/token_storage_service.dart | 3 + web/index.html | 20 + 17 files changed, 3949 insertions(+), 2169 deletions(-) diff --git a/lib/customAppBar/side_bar.dart b/lib/customAppBar/side_bar.dart index 73c55ae..4bbcd1a 100644 --- a/lib/customAppBar/side_bar.dart +++ b/lib/customAppBar/side_bar.dart @@ -220,7 +220,7 @@ class _NhanceSideBarState extends State { ); }).toList(), - if(postModules.isNotEmpty) + if(postModules.isNotEmpty && postModules.contains(5)) _SideItem( // icon: Icons.dashboard, icon: SvgPicture.string( diff --git a/lib/email_verify.dart b/lib/email_verify.dart index c9e7592..812a16f 100755 --- a/lib/email_verify.dart +++ b/lib/email_verify.dart @@ -69,6 +69,7 @@ class _MyEmailVerifyState extends State { late String _verificationId; dynamic empMobileNo; dynamic empEmailid; + final tokenService = TokenStorageService(); @override void initState() { @@ -225,12 +226,53 @@ class _MyEmailVerifyState extends State { // // Show a Snackbar if the OTP is invalid // print('Invalid OTP. Please try again'); // } + } else if (response.statusCode == 401) { + setState(() { + _isLoading = false; + }); + await tokenService.clearAll(); // 🔐 clears flutter_secure_storage + + ToastHelper.showErrorToast(context, 'Session Out'); + if (!context.mounted) return; + Navigator.pushNamedAndRemoveUntil( + context, + 'hrLogin', + (route) => false, + ); + + } else if (response.statusCode == 403) { + setState(() { + _isLoading = false; + }); + await tokenService.clearAll(); // 🔐 clears flutter_secure_storage + + ToastHelper.showErrorToast(context, 'Session Out'); + if (!context.mounted) return; + Navigator.pushNamedAndRemoveUntil( + context, + 'hrLogin', + (route) => false, + ); + + } else if (response.statusCode == 451) { + setState(() { + _isLoading = false; + }); + final body = jsonDecode(response.body); + final message = body['message']; + ToastHelper.showWarningToast(context, message); + } else if (response.statusCode == 429) { + setState(() { + _isLoading = false; + }); + final body = jsonDecode(response.body); + final message = body['message']; + ToastHelper.showWarningToast(context, message); } else { setState(() { _isLoading = false; }); - ToastHelper.showWarningToast(context, 'Something went wrong'); - throw Exception('Failed to verify OTP'); + throw Exception('Failed to load data'); } } catch (e) { setState(() { diff --git a/lib/hrLogin.dart b/lib/hrLogin.dart index a0c70af..9ba9013 100755 --- a/lib/hrLogin.dart +++ b/lib/hrLogin.dart @@ -1,6 +1,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; @@ -12,6 +13,7 @@ import 'package:nhancepolicy/responsive.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:flutter_animated_button/flutter_animated_button.dart'; import 'package:nhancepolicy/service/token_storage_service.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'config/environment.dart'; import 'email_verify.dart'; @@ -517,48 +519,106 @@ class _MyPhoneState extends State { ), SizedBox(height: 20), 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( - 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, - ), - ), - ], + width: double.infinity, + alignment: Alignment.bottomCenter, + padding: const 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( + text: 'privacy policy ', + style: GoogleFonts.poppins( + color: const Color(0xFFE26828), + fontSize: 9, + // decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () async { + final url = Uri.parse( + 'https://nhanceindia.in/privacy-policy/'); + if (await canLaunchUrl(url)) { + await launchUrl(url, + mode: LaunchMode.externalApplication); + } + }, + ), + TextSpan( + text: 'and ', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 9, + ), + ), + TextSpan( + text: 'terms of use', + style: GoogleFonts.poppins( + color: const Color(0xFFE26828), + fontSize: 9, + // decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () async { + final url = Uri.parse( + 'https://nhanceindia.in/privacy-policy/'); + if (await canLaunchUrl(url)) { + await launchUrl(url, + mode: LaunchMode.externalApplication); + } + }, + ), + ], ), ), ), + // Container( + // width: double + // .infinity, // Make the footer full width + // child: Container( + // alignment: Alignment.bottomCenter, + // padding: + // EdgeInsets.symmetric(vertical: 8), + // child: RichText( + // textAlign: TextAlign.center, + // text: TextSpan( + // text: + // 'By continuing, you agree with our ', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontSize: 9, + // ), + // children: [ + // TextSpan( + // 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, + // ), + // ), + // ], + // ), + // ), + // ), + // ), ], ), ), diff --git a/lib/main.dart b/lib/main.dart index c711cec..9047dc4 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -62,117 +62,282 @@ Future startApp() async { projectId: 'nhance-ee8d1')); // await dotenv.load(fileName: Environment.fileName); - 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 + // 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', + debugShowCheckedModeBanner: false, + initialRoute: (initialToken == null || initialToken!.isEmpty) + ? 'hrLogin' + : 'hrHome', + theme: ThemeData( + primaryColor: const Color(0xFF00999E), + scaffoldBackgroundColor: Colors.white, + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF00999E), + ), + textTheme: GoogleFonts.poppinsTextTheme(), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF00999E), + ), ), ), - ), - 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: '', - ), - }, - )); + routes: appRoutes, + ); + } +} + +final Map appRoutes = { + '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: '', + ), +}; + +/// 🔐 Global Auth Wrapper (Protects All Pages) +class AuthWrapper extends StatefulWidget { + final Widget child; + + const AuthWrapper({super.key, required this.child}); + + @override + State createState() => _AuthWrapperState(); +} + +class _AuthWrapperState extends State { + @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; + } } diff --git a/lib/presentation/RaiseClaimForm.dart b/lib/presentation/RaiseClaimForm.dart index 27a3eb4..fa41f61 100644 --- a/lib/presentation/RaiseClaimForm.dart +++ b/lib/presentation/RaiseClaimForm.dart @@ -23,8 +23,11 @@ class RaiseClaimDialog extends StatefulWidget { final BuildContext parentContext; final VoidCallback onSuccess; - const RaiseClaimDialog({Key? key, required this.parentContext, required this.onSuccess,}) - : super(key: key); + const RaiseClaimDialog({ + Key? key, + required this.parentContext, + required this.onSuccess, + }) : super(key: key); @override State createState() => _RaiseClaimDialogState(); @@ -32,6 +35,7 @@ class RaiseClaimDialog extends StatefulWidget { class _RaiseClaimDialogState extends State { // const RaiseClaimDialog({super.key}); + bool hasSubmitted = false; late ApiService apiService; bool isLoading = false; dynamic empPrimaryId; @@ -86,6 +90,11 @@ class _RaiseClaimDialogState extends State { Map? selectedMemberObject; TextEditingController searchController = TextEditingController(); + Map claimTypeMap = {}; + List> claimTypeList = []; + int? claimTypeId; + bool isClaimTypeValid = true; + // Declare subjectController and bodyController as instance variables late TextEditingController subjectController; late TextEditingController messageController; @@ -145,6 +154,7 @@ class _RaiseClaimDialogState extends State { bool isAdmitDateValid = true; bool isDischargeDateValid = true; bool isAccidentService = false; + bool isDeathDateValid = true; @override void initState() { @@ -188,6 +198,7 @@ class _RaiseClaimDialogState extends State { admitDateController.dispose(); dischargeDateController.dispose(); serviceId = null; + claimTypeId = null; departmentList.clear(); super.dispose(); fileService.clearAll(); @@ -210,7 +221,7 @@ class _RaiseClaimDialogState extends State { }); try { print('10'); - final response = await apiService.getClaimPoliciesToApi(_postPreToken!); + final response = await apiService.getClaimPoliciesToApi(_postPreToken!,empClientId); if (response['status'] == 'success') { setState(() { isLoading = false; @@ -219,10 +230,13 @@ class _RaiseClaimDialogState extends State { getClaimPoliciesApi = Map.from(response['data']); departmentList = (getClaimPoliciesApi['ticket_type'] as List) .map>((item) => { - 'id': int.parse(item['ticket_type'].toString()), - 'name': item['type_name'].toString(), - }) + 'id': int.parse(item['ticket_type'].toString()), + 'name': item['type_name'].toString(), + }) .toList(); + + claimTypeMap = Map.from(getClaimPoliciesApi['claim_type']); + print('claimTypeMap $claimTypeMap'); }); } else { setState(() { @@ -319,11 +333,40 @@ class _RaiseClaimDialogState extends State { }; }).toList(); - isPolicyValid = true; }); } + void loadClaimTypes() { + print('serviceId $serviceId'); + if (serviceId == null) return; + + String key = serviceId.toString(); + + if (claimTypeMap.containsKey(key)) { + Map types = Map.from(claimTypeMap[key]); + + claimTypeList = types.entries.map((entry) { + return { + 'id': int.parse(entry.key), + 'name': entry.value, + }; + }).toList(); + + print('claimTypeList $claimTypeList'); + + // // ✅ AUTO SELECT FIRST CLAIM TYPE + // if (claimTypeList.isNotEmpty) { + // claimTypeId = claimTypeList.first['id']; + // } + + } else { + claimTypeList = []; + claimTypeId = null; + setState(() {}); + } + } + Future getCDPoliciesDetails() async { print('9'); setState(() { @@ -333,11 +376,14 @@ class _RaiseClaimDialogState extends State { print('10'); final response = await apiService.getEmployeeAndDependenceToApi( - empClientId, selectedClientPolicyId, empClientBranchId, _postPreToken!); + empClientId, + selectedClientPolicyId, + empClientBranchId, + _postPreToken!); if (response['status'] == 'success') { final List> members = - List>.from(response['data']); + List>.from(response['data']); setState(() { employeePolicyList = members.map((m) { @@ -353,9 +399,7 @@ class _RaiseClaimDialogState extends State { 'relationship': m['relationship'], 'gender': m['gender'], 'dob': m['dob'], - 'policy_name': serviceId == 1 - ? null - : m['policy_name'], + 'policy_name': serviceId == 1 ? null : m['policy_name'], }; }).toList(); }); @@ -373,60 +417,144 @@ class _RaiseClaimDialogState extends State { } Future sendFormDataToApi() async { - setState(() { + hasSubmitted = serviceId != null ? true : false; isServiceValid = serviceId != null; isPolicyValid = selectedClientPolicyId != null; isMemberValid = selectedMemberId != null; + MultiFileUploadWidget.showValidation = serviceId != null && FileUploadService().files.isEmpty; + // MultiFileUploadWidget.showValidation = FileUploadService().files.isEmpty; + // isClaimTypeValid = claimTypeId != null; // isSubjectValid = subjectController.text.trim().isNotEmpty; // 🏥 GMC / Topup - final isGmc = serviceId == 1 || serviceId == 4; - isHospitalNameValid = !isGmc || hospitalNameController.text.trim().isNotEmpty; - isHospitalAddressValid = !isGmc || hospitalAddressController.text.trim().isNotEmpty; - isHospitalCityValid = !isGmc || hospitalCityController.text.trim().isNotEmpty; - isHospitalStateValid = !isGmc || hospitalStateController.text.trim().isNotEmpty; - isHospitalPincodeValid = !isGmc || hospitalPinCodeController.text.trim().isNotEmpty; - isHospitalPhoneNoValid = !isGmc || hospitalPhoneNoController.text.trim().isNotEmpty; + final isGmc = serviceId == 1 || serviceId == 72; - isAdmitDateValid = !isGmc || admitDate != null; - isDischargeDateValid = !isGmc || dischargeDate != null; + isHospitalNameValid = + !isGmc || hospitalNameController.text.trim().isNotEmpty; + isHospitalAddressValid = + !isGmc || hospitalAddressController.text.trim().isNotEmpty; + isHospitalCityValid = + !isGmc || hospitalCityController.text.trim().isNotEmpty; + isHospitalStateValid = + !isGmc || hospitalStateController.text.trim().isNotEmpty; + isHospitalPincodeValid = + !isGmc || hospitalPinCodeController.text.trim().isNotEmpty; + // isHospitalPhoneNoValid = !isGmc || hospitalPhoneNoController.text.trim().isNotEmpty; + // isHospitalPhoneNoValid = !isGmc || (hospitalPhoneNoController.text.trim().length == 10); + String phone = hospitalPhoneNoController.text.trim(); + // isHospitalPhoneNoValid = !isGmc || (RegExp(r'^\d{10}$').hasMatch(phone)); + isHospitalPhoneNoValid = !isGmc || phone.isNotEmpty; - isClaimAmountValid = !isGmc || claimAmountController.text.trim().isNotEmpty; + + isAdmitDateValid = !isGmc || admitDate != null; + isDischargeDateValid = !isGmc || dischargeDate != null; + isClaimTypeValid = !isGmc || claimTypeId != null; + + isClaimAmountValid = + !isGmc || claimAmountController.text.trim().isNotEmpty; // ☠ Accident / Death - final isAccident = [2, 3, 4].contains(serviceId); - - isAccidentService = isAccident ? true : false; - - isAccidentDateValid = !isAccident || accidentDate != null; - isIntimationDateValid = !isAccident || intimationDate != null; + // final isAccident = [2, 3, 4].contains(serviceId); + // + // isAccidentService = isAccident ? true : false; + // + // isAccidentDateValid = !isAccident || accidentDate != null; + // isIntimationDateValid = !isAccident || intimationDate != null; + // 🎯 GPA / GTLI / EDLI validation + // 🎯 GPA / GTLI / EDLI dynamic validation + // if (serviceId == 2) { + // print('serviceId == 2'); + // isAccidentDateValid = accidentDate != null; + // isDeathDateValid = true; + // } + // else if (serviceId == 3 || serviceId == 4) { + // print('serviceId == 3 || serviceId == 4'); + // isDeathDateValid = deathDate != null; + // isAccidentDateValid = true; + // } + // else { + // print('ELSE'); + // isAccidentDateValid = true; + // isDeathDateValid = true; + // } + if (serviceId == 2) { + // GPA + isAccidentDateValid = accidentDate != null; + isDeathDateValid = true; + isIntimationDateValid = true; + } + else if (serviceId == 3) { + // GTLI + isDeathDateValid = deathDate != null; + isAccidentDateValid = true; + isIntimationDateValid = true; + } + else if (serviceId == 4) { + // EDLI + isDeathDateValid = deathDate != null; + isAccidentDateValid = true; + isIntimationDateValid = true; + } + else { + isAccidentDateValid = true; + isDeathDateValid = true; + isIntimationDateValid = true; + } }); + // if (!isDeathDateValid) { + // ToastHelper.showErrorToast( + // context, 'Date of Death is required'); + // return; + // } + // + // if (!isAccidentDateValid) { + // ToastHelper.showErrorToast( + // context, 'Accident Date is required'); + // return; + // } + if (isServiceValid && isPolicyValid && isMemberValid && - // isSubjectValid && isHospitalNameValid && isHospitalAddressValid && isHospitalStateValid && isHospitalCityValid && isHospitalPincodeValid && - isHospitalPhoneNoValid && isAdmitDateValid && + isHospitalPhoneNoValid && isDischargeDateValid && isClaimAmountValid && isAccidentDateValid && + isDeathDateValid && isIntimationDateValid) { - if (FileUploadService().files.isEmpty) { - setState(() { - MultiFileUploadWidget.hasFiles = false; - }); - ToastHelper.showErrorToast(context, 'Please upload at least one document'); + + // ✅ Phone validation AFTER all required fields pass + // ✅ Exact 10 digit validation AFTER all required fields filled + if ((serviceId == 1 || serviceId == 72) && + hospitalPhoneNoController.text.trim().isNotEmpty && + !RegExp(r'^\d{10}$').hasMatch(hospitalPhoneNoController.text.trim())) { + ToastHelper.showErrorToast( + context, + 'Hospital phone number must be exactly 10 digits', + ); return; } - // Proceed to submit + + if (FileUploadService().files.isEmpty) { + ToastHelper.showErrorToast( + context, + 'Please upload at least one document', + ); + return; + } + } else { - ToastHelper.showErrorToast(context, 'Please Fill Required Fields'); + ToastHelper.showErrorToast( + context, + 'Please Fill Required Fields', + ); return; } setState(() => isSubmitting = true); // 🔥 start loader @@ -453,16 +581,15 @@ class _RaiseClaimDialogState extends State { 'relationship': selectedMemberObject?['relationship'], 'gender': selectedMemberObject?['gender'], 'age': selectedMemberObject?['dob'], - 'policy_name': serviceId == 1 - ? null - : selectedMemberObject?['policy_name'], - + 'policy_name': + serviceId == 1 ? null : selectedMemberObject?['policy_name'], }; - if (serviceId == 1 || serviceId == 72) { String formattedAdmitDate = DateFormat('yyyy-MM-dd').format(admitDate!); - String formattedDischargeDate = DateFormat('yyyy-MM-dd').format(dischargeDate!); + String formattedDischargeDate = + DateFormat('yyyy-MM-dd').format(dischargeDate!); + fields['claim_type'] = claimTypeId; fields['hospital_name'] = hospitalNameController.text; fields['hospital_address'] = hospitalAddressController.text; fields['hospital_city'] = hospitalCityController.text; @@ -476,39 +603,54 @@ class _RaiseClaimDialogState extends State { fields['member_name'] = selectedMemberObject?['name']; fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id']; } else { - String formattedAccidentDate = - DateFormat('yyyy-MM-dd').format(accidentDate!); - String formattedDeathDate = DateFormat('yyyy-MM-dd').format(deathDate!); - String formattedBirthDate = DateFormat('yyyy-MM-dd').format(birthDate!); - String formattedIntimationDate = - DateFormat('yyyy-MM-dd').format(intimationDate!); - fields['date_of_accident'] = formattedAccidentDate; - fields['dob'] = formattedBirthDate; - fields['date_of_intimat'] = formattedIntimationDate; - fields['date_of_death'] = formattedDeathDate; + if (accidentDate != null) { + fields['date_of_accident'] = + DateFormat('yyyy-MM-dd').format(accidentDate!); + } + + if (deathDate != null) { + fields['date_of_death'] = + DateFormat('yyyy-MM-dd').format(deathDate!); + } + + if (birthDate != null) { + fields['dob'] = + DateFormat('yyyy-MM-dd').format(birthDate!); + } + + if (intimationDate != null) { + fields['date_of_intimat'] = + DateFormat('yyyy-MM-dd').format(intimationDate!); + } fields['si_amt'] = sumInsuredController.text; fields['policyholder_name'] = employeePolicyList[0]['name']; fields['member_name'] = selectedMemberName; fields['insured_emp_id'] = selectedMemberObject?['insured_emp_id']; } - - final request = http.MultipartRequest('POST', Uri.parse('${Environment.apiUrlPost}initiateClaim')); + final request = http.MultipartRequest( + 'POST', Uri.parse('${Environment.apiUrlPost}initiateClaim')); request.headers['Authorization'] = 'Bearer $_postPreToken'; - request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; - final stringFields = fields.map((key, value) => MapEntry(key, value?.toString() ?? '')); + request.headers['APP-SIGNATURE'] = + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; + final stringFields = + fields.map((key, value) => MapEntry(key, value?.toString() ?? '')); + print("📁 stringFields: ${stringFields}"); request.fields.addAll(stringFields); // get uploaded files final uploadedFiles = FileUploadService().files; print("📁 Total uploaded files: ${uploadedFiles.length}"); - print("📂 File list (original names): ${uploadedFiles.map((f) => f.file.name).toList()}"); + print( + "📂 File list (original names): ${uploadedFiles.map((f) => f.file.name).toList()}"); // Validate all labels present (optional but recommended) for (final uf in uploadedFiles) { - print("📝 Checking label for file: ${uf.file.name}, current label: '${uf.label}'"); + print( + "📝 Checking label for file: ${uf.file.name}, current label: '${uf.label}'"); if ((uf.label ?? '').trim().isEmpty) { - ToastHelper.showErrorToast(context, 'Please enter name for all uploaded documents'); + ToastHelper.showErrorToast( + context, 'Please enter name for all uploaded documents'); setState(() => isLoading = false); return; } @@ -581,7 +723,6 @@ class _RaiseClaimDialogState extends State { } } - // ✅ Combine all names into a JSON array string final claimDocNames = uploadedFiles.map((uf) => uf.label.trim()).toList(); final encodedNames = jsonEncode(claimDocNames); @@ -592,12 +733,12 @@ class _RaiseClaimDialogState extends State { print("Files: ${uploadedFiles.map((f) => f.file.name).toList()}"); print("Names (JSON): $encodedNames"); - final response = await request.send(); final responseBody = await response.stream.bytesToString(); final decoded = jsonDecode(responseBody); if (decoded['status'] == true) { + resetFormOnServiceChange(); // 1️⃣ Close dialog first Navigator.pop(context); @@ -620,8 +761,6 @@ class _RaiseClaimDialogState extends State { Navigator.pop(context); ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}"); } - - } catch (e) { setState(() { isLoading = false; @@ -650,9 +789,9 @@ class _RaiseClaimDialogState extends State { Future _pickFromToDate(BuildContext context) async { // Define the policy date range final DateTime policyStartDate = - DateFormat('dd-MMM-yyyy').parse(rawPolicyStartDate); + DateFormat('dd-MMM-yyyy').parse(rawPolicyStartDate); final DateTime policyEndDate = - DateFormat('dd-MMM-yyyy').parse(rawPolicyEndDate); + DateFormat('dd-MMM-yyyy').parse(rawPolicyEndDate); // Show the date range picker within the policy date limits final DateTimeRange? pickedDateRange = await showDateRangePicker( @@ -713,9 +852,73 @@ class _RaiseClaimDialogState extends State { return age; } + void resetFormOnServiceChange() { + hasSubmitted = false; + MultiFileUploadWidget.showValidation = false; + // Reset dropdown selections + selectedClientPolicyId = null; + selectedPolicyTypeId = null; + policyNumberId = null; + selectedMemberId = null; + selectedMemberName = null; + selectedMemberObject = null; + claimTypeId = null; + + // Clear lists + employeePolicyList.clear(); + policyNumberList.clear(); + + // Clear all text controllers + messageController.clear(); + accidentDetailsController.clear(); + hospitalNameController.clear(); + hospitalAddressController.clear(); + hospitalCityController.clear(); + hospitalStateController.clear(); + hospitalPinCodeController.clear(); + hospitalPhoneNoController.clear(); + claimAmountController.clear(); + sumInsuredController.clear(); + admitDateController.clear(); + dischargeDateController.clear(); + + // Reset dates + accidentDate = null; + deathDate = null; + intimationDate = null; + birthDate = null; + admitDate = null; + dischargeDate = null; + + // Reset validation flags + isPolicyValid = true; + isMemberValid = true; + isHospitalNameValid = true; + isHospitalAddressValid = true; + isHospitalCityValid = true; + isHospitalStateValid = true; + isHospitalPincodeValid = true; + isHospitalPhoneNoValid = true; + isAdmitDateValid = true; + isDischargeDateValid = true; + isClaimAmountValid = true; + isAccidentDateValid = true; + isIntimationDateValid = true; + isDeathDateValid = true; + + // Clear uploaded files + fileService.clearAll(); + MultiFileUploadWidget.hasFiles = false; + } + @override Widget build(BuildContext context) { - return Dialog( + return WillPopScope( + onWillPop: () async { + resetFormOnServiceChange(); + return true; + }, + child: Dialog( backgroundColor: Colors.white, // ✅ PURE WHITE popup insetPadding: const EdgeInsets.all(20), shape: RoundedRectangleBorder( @@ -723,8 +926,9 @@ class _RaiseClaimDialogState extends State { ), child: ClipRRect( borderRadius: BorderRadius.circular(16), - child: SizedBox( - width: MediaQuery.of(context).size.width * 0.75, // Desktop popup width + child: SizedBox( + width: + MediaQuery.of(context).size.width * 0.75, // Desktop popup width // height: MediaQuery.of(context).size.height * 0.85, child: Stack( children: [ @@ -748,7 +952,12 @@ class _RaiseClaimDialogState extends State { ), IconButton( icon: const Icon(Icons.close), - onPressed: () => Navigator.pop(context), + onPressed: () => { + setState(() { + resetFormOnServiceChange(); + }), + Navigator.pop(context) + }, ), ], ), @@ -762,12 +971,14 @@ class _RaiseClaimDialogState extends State { _row([ buildDropdownField( 'Service', - (value) { - final selectedItem = departmentList.firstWhere( - (item) => item['id'] == value, + (value) { + final selectedItem = + departmentList.firstWhere( + (item) => item['id'] == value, orElse: () => {}, ); setState(() { + resetFormOnServiceChange(); serviceId = value; serviceName = selectedItem['name']; policyNumberId = null; @@ -775,6 +986,7 @@ class _RaiseClaimDialogState extends State { }); print('🔥 serviceId set to $serviceId'); filterPoliciesByService(value!); + loadClaimTypes(); }, departmentList, 'name', @@ -784,16 +996,18 @@ class _RaiseClaimDialogState extends State { ), buildDropdownField( 'Select Policy', - (value) { - final selectedPolicy = - policyNumberList.firstWhere((p) => p['id'] == value); + (value) { + final selectedPolicy = policyNumberList + .firstWhere((p) => p['id'] == value); setState(() { // ✅ THIS is what you send to API - selectedClientPolicyId = selectedPolicy['id']; + selectedClientPolicyId = + selectedPolicy['id']; // optional - selectedPolicyTypeId = selectedPolicy['policy_type_id']; + selectedPolicyTypeId = + selectedPolicy['policy_type_id']; policyNumberId = value; isPolicyValid = true; @@ -807,20 +1021,37 @@ class _RaiseClaimDialogState extends State { required: true, isValid: isPolicyValid, ), - + if (serviceId == 1 || serviceId == 72) + buildDropdownField( + 'Select Claim Type', + (value) { + setState(() { + claimTypeId = value; + isClaimTypeValid = true; + }); + }, + claimTypeList, + 'name', + claimTypeId, + required: true, + isValid: isClaimTypeValid, + ), buildDropdownFieldSearch( 'Member Name', (value) { - final member = employeePolicyList.firstWhere( + final member = + employeePolicyList.firstWhere( (m) => m['id'] == value, ); setState(() { selectedMemberId = value; - selectedMemberObject = member; // ✅ FULL OBJECT + selectedMemberObject = + member; // ✅ FULL OBJECT selectedMemberName = member['name']; isMemberValid = true; - print('selectedMemberObject $selectedMemberObject'); + print( + 'selectedMemberObject $selectedMemberObject'); }); }, employeePolicyList, @@ -829,118 +1060,145 @@ class _RaiseClaimDialogState extends State { required: true, isValid: isMemberValid, ), - ]), _row([ buildTextField('Message', messageController), - if (serviceId == 1 || serviceId == 72)...[ - buildTextField('Hospital Name', hospitalNameController,required: true, isValid: isHospitalNameValid), - buildTextField('Hospital Address', hospitalAddressController,required: true, isValid: isHospitalAddressValid), + + if (serviceId == 1 || serviceId == 72) ...[ + buildTextField( + 'Hospital Name', hospitalNameController, + required: true, + isValid: isHospitalNameValid), + buildTextField('Hospital Address', + hospitalAddressController, + required: true, + isValid: isHospitalAddressValid), ] ]), - - if (serviceId == 1 || serviceId == 72) - _row([ - buildTextField('Hospital City', hospitalCityController,required: true, isValid: isHospitalCityValid), - buildTextField('Hospital State', hospitalStateController,required: true, isValid: isHospitalStateValid), - buildTextField( - 'Hospital Pincode', - hospitalPinCodeController, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(6), - ], - required: true, isValid: isHospitalPincodeValid - ), - ]), if (serviceId == 1 || serviceId == 72) _row([ buildTextField( - 'Hospital Phone No', - hospitalPhoneNoController, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(10), - ], - required: true, isValid: isHospitalPhoneNoValid - ), - buildDatePickerField( - label: 'Admit Date', - selectedDate: admitDate, - allowFuture: false, - onDateSelected: (d) { - setState(() { - admitDate = d; - dischargeDate = null; - }); - }, - required: true, isValid: isAdmitDateValid - ), - buildDatePickerField( - label: 'Discharge Date', - selectedDate: dischargeDate, - allowFuture: true, - minDate: admitDate?.add(const Duration(days: 1)), - onDateSelected: (d) => setState(() => dischargeDate = d), - required: true, isValid: isDischargeDateValid - ), + 'Hospital City', hospitalCityController, + required: true, + isValid: isHospitalCityValid, + inputFormatters: [ + FilteringTextInputFormatter.allow( + RegExp(r"[a-zA-Z\s]")), + ]), buildTextField( - 'Claims Amount', - claimAmountController, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - required: true, isValid: isClaimAmountValid - ), + 'Hospital State', hospitalStateController, + required: true, + isValid: isHospitalStateValid, + inputFormatters: [ + FilteringTextInputFormatter.allow( + RegExp(r"[a-zA-Z\s]")), + ]), + buildTextField('Hospital Pincode', + hospitalPinCodeController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(6), + ], + required: true, + isValid: isHospitalPincodeValid), + ]), + if (serviceId == 1 || serviceId == 72) + _row([ + buildTextField('Hospital Phone No', + hospitalPhoneNoController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(10), + ], + required: true, + isValid: !hasSubmitted || + serviceId == null || + (serviceId != 1 && serviceId != 72) || + RegExp(r'^\d{10}$').hasMatch(hospitalPhoneNoController.text.trim()), + ), + buildDatePickerField( + label: 'Admit Date', + selectedDate: admitDate, + allowFuture: false, + onDateSelected: (d) { + setState(() { + admitDate = d; + dischargeDate = null; + }); + }, + required: true, + isValid: isAdmitDateValid), + buildDatePickerField( + label: 'Discharge Date', + selectedDate: dischargeDate, + allowFuture: true, + minDate: admitDate + ?.add(const Duration(days: 1)), + onDateSelected: (d) => + setState(() => dischargeDate = d), + required: true, + isValid: isDischargeDateValid), + buildTextField( + 'Claims Amount', claimAmountController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + required: true, + isValid: isClaimAmountValid), ]), - if ([2, 3, 4].contains(serviceId)) _row([ buildDatePickerField( label: 'Date of Birth', selectedDate: birthDate, allowFuture: false, - onDateSelected: (d) => setState(() => birthDate = d), + onDateSelected: (d) => + setState(() => birthDate = d), ), buildDatePickerField( - label: 'Accident Date', - selectedDate: accidentDate, - allowFuture: false, - onDateSelected: (d) { - setState(() { - accidentDate = d; - deathDate = null; - intimationDate = null; - }); - }, - required: true, isValid: isAccidentDateValid - ), + label: 'Accident Date', + selectedDate: accidentDate, + allowFuture: false, + onDateSelected: (d) { + setState(() { + accidentDate = d; + deathDate = null; + intimationDate = null; + }); + }, + required: serviceId == 2, + isValid: isAccidentDateValid), buildDatePickerField( label: 'Date of Death', selectedDate: deathDate, allowFuture: false, - onDateSelected: (d) => setState(() => deathDate = d), + onDateSelected: (d) => + setState(() => deathDate = d), + required: (serviceId == 3 || serviceId == 4) ? true : false, // 🔥 GTLI & EDLI + isValid: isDeathDateValid, ), ]), if ([2, 3, 4].contains(serviceId)) _row([ buildDatePickerField( - label: 'Date of Intimation', - selectedDate: intimationDate, - allowFuture: false, - onDateSelected: (d) => setState(() => intimationDate = d), - required: true, isValid: isIntimationDateValid - ), + label: 'Date of Intimation', + selectedDate: intimationDate, + allowFuture: false, + onDateSelected: (d) => + setState(() => intimationDate = d)), buildTextField( 'Sum Insured', sumInsuredController, keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], ), const SizedBox(), ]), - _row([ MultiFileUploadWidget(), ]), @@ -950,7 +1208,9 @@ class _RaiseClaimDialogState extends State { width: 120, height: 42, child: ElevatedButton( - onPressed: isSubmitting ? null : sendFormDataToApi, + onPressed: (isSubmitting || serviceId == null) + ? null + : sendFormDataToApi, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFE26728), shape: RoundedRectangleBorder( @@ -959,18 +1219,19 @@ class _RaiseClaimDialogState extends State { ), child: isSubmitting ? const SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - strokeWidth: 2, - color: Colors.white, - ), - ) + height: 18, + width: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) : const Text( - 'Send', - style: TextStyle(color: Colors.white, - fontWeight: FontWeight.w600), - ), + 'Send', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600), + ), ), ), ), @@ -982,24 +1243,27 @@ class _RaiseClaimDialogState extends State { ), ), - /// 🔄 LOADER OVERLAY (UNCHANGED) + /// 🔥 LOADER OVERLAY if (isLoading) - Container( - color: const Color(0x98FFFCE5), - child: Center( - child: Image.asset( - 'assets/nhance-loader.gif', - height: 60, - width: 60, + Positioned.fill( + child: Container( + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.6), + borderRadius: BorderRadius.circular(20), + ), + child: Center( + child: Image.asset( + 'assets/nhance-loader.gif', + height: 60, + width: 60, + ), ), ), ), ], ), ), - ) - - ); + ))); } /// ---------- HELPERS ---------- @@ -1010,10 +1274,10 @@ class _RaiseClaimDialogState extends State { child: Row( children: children .map((e) => Expanded( - child: Padding( - padding: const EdgeInsets.only(right: 12), - child: e, - ))) + child: Padding( + padding: const EdgeInsets.only(right: 12), + child: e, + ))) .toList(), ), ); @@ -1032,11 +1296,11 @@ class _RaiseClaimDialogState extends State { ), children: required ? const [ - TextSpan( - text: ' *', - style: TextStyle(color: Colors.red), - ) - ] + TextSpan( + text: ' *', + style: TextStyle(color: Colors.red), + ) + ] : [], ), ), @@ -1049,24 +1313,23 @@ class _RaiseClaimDialogState extends State { child: isValid ? null : const Text( - "Required", - style: TextStyle( - color: Colors.red, - fontSize: 11, - ), - ), + "Required", + style: TextStyle( + color: Colors.red, + fontSize: 11, + ), + ), ); } - Widget buildTextField( - String label, - TextEditingController controller, { - bool required = false, - bool isValid = true, - TextInputType keyboardType = TextInputType.text, - List? inputFormatters, - }) { + String label, + TextEditingController controller, { + bool required = false, + bool isValid = true, + TextInputType keyboardType = TextInputType.text, + List? inputFormatters, + }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1087,7 +1350,6 @@ class _RaiseClaimDialogState extends State { ); } - Widget buildTextAreaField(String label, TextEditingController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1113,14 +1375,14 @@ class _RaiseClaimDialogState extends State { } Widget buildDropdownField( - String label, - void Function(int?) onChanged, - List> itemsList, - String displayField, - int? selectedValue, { - bool required = false, - bool isValid = true, - }) { + String label, + void Function(int?) onChanged, + List> itemsList, + String displayField, + int? selectedValue, { + bool required = false, + bool isValid = true, + }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1168,19 +1430,15 @@ class _RaiseClaimDialogState extends State { ); } - - - Widget buildDropdownFieldSearch( - String label, - void Function(int?) onChanged, - List> itemsList, - String displayField, - int? selectedValue, { - bool required = false, - bool isValid = true, - }) { - + String label, + void Function(int?) onChanged, + List> itemsList, + String displayField, + int? selectedValue, { + bool required = false, + bool isValid = true, + }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1241,14 +1499,12 @@ class _RaiseClaimDialogState extends State { ), searchMatchFn: (item, searchValue) { final matchedItem = itemsList.firstWhere( - (e) => e['id'] == item.value, + (e) => e['id'] == item.value, orElse: () => {}, ); - final text = matchedItem[displayField] - ?.toString() - .toLowerCase() ?? - ''; + final text = + matchedItem[displayField]?.toString().toLowerCase() ?? ''; return text.contains(searchValue.toLowerCase()); }, @@ -1279,7 +1535,6 @@ class _RaiseClaimDialogState extends State { ); } - Widget buildDatePickerField({ required String label, required DateTime? selectedDate, @@ -1294,7 +1549,6 @@ class _RaiseClaimDialogState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ fieldLabel(label, required: required), - Container( height: 42, padding: const EdgeInsets.symmetric(horizontal: 12), @@ -1310,7 +1564,7 @@ class _RaiseClaimDialogState extends State { final DateTime now = DateTime.now(); final DateTime first = minDate ?? DateTime(1980); final DateTime last = - allowFuture ? (maxDate ?? DateTime(2100)) : now; + allowFuture ? (maxDate ?? DateTime(2100)) : now; final DateTime initialDate = selectedDate ?? (first.isAfter(now) ? first : now); @@ -1320,6 +1574,7 @@ class _RaiseClaimDialogState extends State { initialDate: initialDate, firstDate: first, lastDate: last, + initialEntryMode: DatePickerEntryMode.calendarOnly, ); if (picked != null) { @@ -1335,18 +1590,29 @@ class _RaiseClaimDialogState extends State { : 'Select', style: const TextStyle(color: Colors.black), ), - const Icon(Icons.calendar_today, size: 18), + // ✅ Show clear button only if date selected + if (selectedDate != null) + GestureDetector( + onTap: () { + onDateSelected(null); // 🔥 Clear date + }, + child: const Icon( + Icons.close, + size: 18, + color: Colors.grey, + ), + ) + else + const Icon(Icons.calendar_today, size: 18), ], ), ), ), - errorText(isValid), ], ); } - // Widget uploadBox() { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, @@ -1380,7 +1646,6 @@ class _RaiseClaimDialogState extends State { child: Center(child: child), ); } - } // class RaiseClaimDialog extends StatelessWidget { diff --git a/lib/presentation/cdList.dart b/lib/presentation/cdList.dart index de9603e..b056b64 100644 --- a/lib/presentation/cdList.dart +++ b/lib/presentation/cdList.dart @@ -73,11 +73,12 @@ class _CdPoliciesListState extends State { } Future checkIds() async { + print('checkIds'); _postPreToken = await tokenService.getCurrentToken(); empClientId = await tokenService.readValue('empClientId'); // empClientBranchId = await tokenService.readValue('empClientBranchId'); empHrId = await tokenService.readValue('empHrId'); - + print('$_postPreToken - $empClientId - $empHrId'); await getCDPoliciesDetails(empClientId, empHrId, _postPreToken); } @@ -165,7 +166,7 @@ class _CdPoliciesListState extends State { rows.add([ item['insurer_name'] ?? '', item['cd_master_account_no'] ?? '', - '₹${item['balance'] ?? '0'}', + '${item['balance'] ?? '0'}', ]); } @@ -393,6 +394,30 @@ class _CdPoliciesListState extends State { return InkWell( borderRadius: BorderRadius.circular(12), onTap: () async { + await tokenService.writeValue( + 'hr_cd_insurer_name', + filteredData[index]['insurer_name'].toString(), + ); + + await tokenService.writeValue( + 'hr_cd_master_account_no', + filteredData[index]['cd_master_account_no'].toString(), + ); + + await tokenService.writeValue( + 'hr_cd_insurer_id', + filteredData[index]['insurer_id'].toString(), + ); + + await tokenService.writeValue( + 'hr_cd_ac_pk', + filteredData[index]['cd_ac_pk'].toString(), + ); + + await tokenService.writeValue( + 'hr_empClientId', + empClientId.toString(), + ); Navigator.push( context, @@ -462,7 +487,8 @@ class _CDPolicyCard extends StatelessWidget { Expanded( flex: 3, child: Text( - "₹${balance.toStringAsFixed(0)}", + data['balance'] ?? '', + // "₹${balance.toStringAsFixed(0)}", textAlign: TextAlign.right, style: GoogleFonts.poppins( fontSize: 14, diff --git a/lib/presentation/cdTransactionDetails.dart b/lib/presentation/cdTransactionDetails.dart index e45c9c6..f218fa5 100755 --- a/lib/presentation/cdTransactionDetails.dart +++ b/lib/presentation/cdTransactionDetails.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:csv/csv.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -13,12 +12,12 @@ import 'package:nhancepolicy/service/api_service.dart'; import 'package:nhancepolicy/service/token_storage_service.dart'; import 'package:universal_html/html.dart' as html; -import 'package:collection/collection.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../config/environment.dart'; import '../customAppBar/base_layout.dart'; -import '../customAppBar/customAppBar.dart'; -import '../customAppBar/customFooter.dart'; +import 'cdList.dart'; +import 'claims.dart'; class cdTransactionDetails extends StatefulWidget { final String insurerName; @@ -42,6 +41,14 @@ class cdTransactionDetails extends StatefulWidget { } class _cdTransactionDetailsState extends State { + + String? localInsurerId; + String? localCdAcPk; + String? localEmpClientId; + String? localInsurerName; + String? localCdMasterAccountNo; + + final tokenService = TokenStorageService(); Uint8List? fileBytes; List> getCDTransData = []; @@ -85,7 +92,9 @@ class _cdTransactionDetailsState extends State { void initState() { super.initState(); apiService = ApiService(context); // Initialize ApiService here - getCdTransactionDetails(); + + restoreTransactionData(); + } @override @@ -93,9 +102,51 @@ class _cdTransactionDetailsState extends State { super.dispose(); } + + // downloadPolicyFiles?file_id=13 // getPolicyAndEndorsementFiles?cd_ac_pk=12 + Future 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 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 getCdTransactionDetails() async { print('9'); setState(() { @@ -104,8 +155,8 @@ class _cdTransactionDetailsState extends State { try { print('10'); final _postPreToken = await tokenService.getCurrentToken(); - final response = await apiService.getCdTransactionData(widget.empClientId, - widget.insurerId, widget.cd_ac_pk, _postPreToken!); + final response = await apiService.getCdTransactionData(localEmpClientId!, + localInsurerId!, localCdAcPk!, _postPreToken!); if (response['status'] == 'success') { setState(() { isLoading = false; @@ -115,11 +166,16 @@ class _cdTransactionDetailsState extends State { List>.from(response['data']['deposit_data']); originalData = getCDTransData; filteredData = List.from(originalData); - total_deposit = formatAmount(response['data']['total_deposit']); - total_consumed = formatAmount(response['data']['total_consumed']); - total_refund = formatAmount(response['data']['total_refund']); - currect_balance = formatAmount(response['data']['currect_balance']); - insurer_short_name = formatAmount(response['data']['insurer_short_name']); + total_deposit = response['data']['total_deposit']; + // total_deposit = formatAmount(response['data']['total_deposit']); + total_consumed = response['data']['total_consumed']; + // total_consumed = formatAmount(response['data']['total_consumed']); + total_refund = response['data']['total_refund']; + // total_refund = formatAmount(response['data']['total_refund']); + currect_balance = response['data']['currect_balance']; + // currect_balance = formatAmount(response['data']['currect_balance']); + insurer_short_name = response['data']['insurer_short_name']; + // insurer_short_name = formatAmount(response['data']['insurer_short_name']); print('filteredData'); print(filteredData); }); @@ -144,25 +200,53 @@ class _cdTransactionDetailsState extends State { } } - Future _openEndorsementFile(id) async { - print('9'); - try { - print('10'); - final _postPreToken = await tokenService.getCurrentToken(); - final response = await apiService.getOpenEndorsementFileData(id, _postPreToken!); - if (response['status'] == false) { - ToastHelper.showErrorToast(context, response['message']); - } else { + Future _openEndorsementFile(id, file_name) async { + // final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token); + final _postPreToken = await tokenService.getCurrentToken(); + print("**********-------*****"); + final apiurl = Environment.apiUrlPost; + final String url = '$apiurl/downloadPolicyFiles?file_id=$id'; - // ToastHelper.showWarningToast( - // context, 'Request failed with status: ${response.statusCode}'); - print('Request failed with status: ${response['code']}'); + final response = await http.get( + Uri.parse(url), + headers: { + 'APP-SIGNATURE': + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + 'Authorization': 'Bearer $_postPreToken', + 'Content-Type': 'application/json', + // 'app-signature': 'ts-traveltool-2025-signature-123456', + }, + ); + + if (response.statusCode == 200) { + try { + print("PDF Downloaded"); + + // ✅ Create a blob from the response body bytes + final blob = html.Blob([response.bodyBytes]); + + // ✅ Generate a download URL + final url = html.Url.createObjectUrlFromBlob(blob); + + // ✅ Trigger file download automatically + final anchor = html.AnchorElement(href: url) + ..setAttribute('download', '$file_name') + ..click(); + + // ✅ Revoke the URL to free memory + html.Url.revokeObjectUrl(url); + + ToastHelper.showSuccessToast(context, 'File Downloaded Successfully'); + } catch (e) { + throw Exception('Error parsing response: $e'); } - } catch (e) { - print('Exception occurred: $e'); + } else { + ToastHelper.showErrorToast(context, 'Failed to download'); + print("Download failed with status: ${response.statusCode}"); } } + Future getCdEndorsementDetails(id) async { print('9'); setState(() { @@ -243,7 +327,7 @@ class _cdTransactionDetailsState extends State { ), onTap: () { // Navigator.pop(context); // close popup - _openEndorsementFile(file['id']); + _openEndorsementFile(file['id'],file['file_name']); }, ); }, @@ -387,9 +471,12 @@ class _cdTransactionDetailsState extends State { : '-', item['endorsement_no'] ?? '', item['sub_type_text'] ?? '', - item['transaction_type'] == 'Credit' ? '₹${formatAmount(item['amount'])}' : '-', - item['transaction_type'] == 'Debit' ? '₹${formatAmount(item['amount'])}' : '-', - '₹${formatAmount(item['balance']) ?? '0'}', + item['transaction_type'] == 'Credit' ? '${item['amount']}' : '-', + // item['transaction_type'] == 'Credit' ? '₹${formatAmount(item['amount'])}' : '-', + item['transaction_type'] == 'Debit' ? '${item['amount']}' : '-', + // item['transaction_type'] == 'Debit' ? '₹${formatAmount(item['amount'])}' : '-', + '${item['balance'] ?? '0'}', + // '₹${formatAmount(item['balance']) ?? '0'}', item['description'] ?? '', item['username'] ?? '', ]); @@ -541,7 +628,21 @@ class _cdTransactionDetailsState extends State { children: [ IconButton( tooltip: 'Previous Page', - onPressed: () => Navigator.pop(context), + onPressed: () async { + await clearPolicyStorage(); + + if (Navigator.canPop(context)) { + Navigator.pop(context); + } else { + Navigator.pushReplacement( + context, + MaterialPageRoute( + settings: const RouteSettings(name: 'cdPoliciesList'), + builder: (_) => CdPoliciesList(), + ), + ); + } + }, icon: const Icon( Icons.arrow_back_ios, size: 18, @@ -552,7 +653,7 @@ class _cdTransactionDetailsState extends State { ), const SizedBox(width: 6), Text( - 'Transaction Details - ${insurer_short_name} (${widget.cdMasterAccountNo})', + 'Transaction Details - ${insurer_short_name} (${localCdMasterAccountNo ?? widget.cdMasterAccountNo})', style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.w500, @@ -762,21 +863,36 @@ class _cdTransactionDetailsState extends State { SizedBox(width: 10), _cell( item['transaction_type'] == 'Credit' - ? formatAmount(item['amount']) + ? item['amount'] : '-', 2, alignRight: true, ), + // _cell( + // item['transaction_type'] == 'Credit' + // ? formatAmount(item['amount']) + // : '-', + // 2, + // alignRight: true, + // ), SizedBox(width: 10), _cell( item['transaction_type'] == 'Debit' - ? formatAmount(item['amount']) + ? item['amount'] : '-', 2, alignRight: true, ), + // _cell( + // item['transaction_type'] == 'Debit' + // ? formatAmount(item['amount']) + // : '-', + // 2, + // alignRight: true, + // ), SizedBox(width: 10), - _cell(formatAmount(item['balance']), 2, alignRight: true), + _cell(item['balance'], 2, alignRight: true), + // _cell(formatAmount(item['balance']), 2, alignRight: true), SizedBox(width: 10), _cell(item['description'], 3), SizedBox(width: 10), @@ -784,25 +900,72 @@ class _cdTransactionDetailsState extends State { SizedBox(width: 10), 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']), + child: SizedBox( + height: 36, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + + /// --- PDF ICON SLOT --- + SizedBox( + width: 36, + height: 36, + child: Visibility( + visible: isAllowedSubType, + maintainSize: true, + maintainAnimation: true, + maintainState: true, + child: _ActionIconButton( + icon: Icons.picture_as_pdf_outlined, + toolTip: 'View Endorsement PDF', + onTap: () => getCdEndorsementDetails(item['id']), + ), + ), ), - const SizedBox(width: 8), - if (isAllowedSubType && hasSplitUpFile) - _ActionIconButton( - icon: Icons.folder_open_outlined, - toolTip: 'View Files', - onTap: () => _launchURL(item['split_up_url']), + + const SizedBox(width: 8), + + /// --- FOLDER ICON SLOT --- + SizedBox( + width: 36, + height: 36, + child: Visibility( + visible: isAllowedSubType && hasSplitUpFile, + maintainSize: true, + maintainAnimation: true, + maintainState: true, + child: _ActionIconButton( + icon: Icons.folder_open_outlined, + toolTip: 'View Files', + onTap: () => _launchURL(item['split_up_url']), + ), + ), ), - ], + ], + ), ), ), + // Expanded( + // flex: 2, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // if (isAllowedSubType) + // _ActionIconButton( + // icon: Icons.picture_as_pdf_outlined, + // toolTip: 'View Endorsement PDF', + // onTap: () => getCdEndorsementDetails(item['id']), + // ), + // const SizedBox(width: 8), + // if (isAllowedSubType && hasSplitUpFile) + // _ActionIconButton( + // icon: Icons.folder_open_outlined, + // toolTip: 'View Files', + // onTap: () => _launchURL(item['split_up_url']), + // ), + // ], + // ), + // ), ], ), ); diff --git a/lib/presentation/claims.dart b/lib/presentation/claims.dart index 5114d91..b25d8fe 100755 --- a/lib/presentation/claims.dart +++ b/lib/presentation/claims.dart @@ -66,6 +66,8 @@ class _ClaimsPolicieState extends State { return filteredData.sublist(startIndex, endIndex); } + int? appliedClaimStatus; + Color getStatusColor(String status) { switch (status.toLowerCase()) { case 'claim received': @@ -182,7 +184,7 @@ class _ClaimsPolicieState extends State { }); try { print('10'); - final response = await apiService.getClaimPoliciesToApi(_postPreToken!); + final response = await apiService.getClaimPoliciesToApi(_postPreToken!,''); if (response['status'] == 'success') { setState(() { isLoading = false; @@ -272,6 +274,9 @@ class _ClaimsPolicieState extends State { final data = claim_Detials(); print("data -- $data"); getClaimList(); + setState(() { + appliedClaimStatus = selectedClaimStatus; // ✅ apply only after API call + }); } Future reset() async { @@ -285,6 +290,7 @@ class _ClaimsPolicieState extends State { selectedClaimStatus = null; selectedClaimStatusName = null; _currentPage = 1; + appliedClaimStatus = null; getClaimList(); }); @@ -482,16 +488,19 @@ class _ClaimsPolicieState extends State { /// 🔙 Back + Title (LEFT) Row( children: [ - // IconButton( - // onPressed: () => {}, - // icon: const Icon( - // Icons.arrow_back_ios, - // size: 18, - // color: Colors.black, - // ), - // padding: EdgeInsets.zero, - // constraints: const BoxConstraints(), - // ), + if(widget.empCode.isNotEmpty) + IconButton( + onPressed: () => { + Navigator.of(context).pop(), + }, + icon: const Icon( + Icons.arrow_back_ios, + size: 18, + color: Colors.black, + ), + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), const SizedBox(width: 6), Text( 'Claims', @@ -707,11 +716,6 @@ class _ClaimsPolicieState extends State { ); } - - - - - Widget _buildDataRow(Map item) { return Container( padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), @@ -866,21 +870,28 @@ class _ClaimsPolicieState extends State { if (statusMaster.isEmpty) return const SizedBox(); + final List displayStatuses = appliedClaimStatus != null + ? statusMaster.where((status) => + int.parse(status['id'].toString()) == appliedClaimStatus) + .toList() + : statusMaster; + return SizedBox( - height: 30, + height: 35, child: ListView.separated( scrollDirection: Axis.horizontal, - itemCount: statusMaster.length, + itemCount: displayStatuses.length, separatorBuilder: (_, __) => const SizedBox(width: 10), itemBuilder: (context, index) { final statusName = - statusMaster[index]['claim_status']?.toString() ?? ''; + displayStatuses[index]['claim_status']?.toString() ?? ''; final count = getStatusCount(statusName); final color = getStatusColor(statusName); return Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(6), diff --git a/lib/presentation/hrDashboard.dart b/lib/presentation/hrDashboard.dart index 5d4fa3a..84fb0ac 100755 --- a/lib/presentation/hrDashboard.dart +++ b/lib/presentation/hrDashboard.dart @@ -34,6 +34,9 @@ class _hrDashboardState extends State with SingleTickerProviderStat final tokenService = TokenStorageService(); final FocusNode _policyFocusNode = FocusNode(); + bool isTpaDashboardEnabled = false; + bool isTpaSelected = false; + // Variables for API data dynamic empClientBranchId; dynamic empHrId; @@ -41,6 +44,7 @@ class _hrDashboardState extends State with SingleTickerProviderStat String? _postPreToken = ''; int stausVal = 1; bool _isPolicyDropdownOpen = false; + html.IFrameElement? _currentIframe; @override void initState() { @@ -114,17 +118,32 @@ class _hrDashboardState extends State with SingleTickerProviderStat _metabaseLoaded = false; }); + // ✅ REMOVE OLD IFRAME COMPLETELY + _currentIframe?.remove(); + _currentIframe = null; + + // ✅ CLEAR OLD VIEW TYPES + _registeredViewTypes.clear(); + final response = await apiService.postHrDashboard({ "client_id": empClientId, "client_policy_id": clientPolicyId, }, _postPreToken); if (response['status'] == 'success') { + // ✅ ADD THIS LINE + isTpaDashboardEnabled = response['is_tpa_dashboard_enable'] == true; + setState(() { + isTpaSelected = false; + }); + + _registerMetabaseIframe( token: response['data']['metabaseToken'], url: response['data']['metabaseUrl'], clientPolicyId: clientPolicyId, ); + setState(() => _metabaseLoaded = true); } else { ToastHelper.showErrorToast(context, response['message']); @@ -136,26 +155,74 @@ class _hrDashboardState extends State with SingleTickerProviderStat } } + Future _loadTpaDashboard(String clientPolicyId) async { + try { + setState(() { + isDashboardLoading = true; + _metabaseLoaded = false; + }); + + // ✅ REMOVE OLD IFRAME + _currentIframe?.remove(); + _currentIframe = null; + _registeredViewTypes.clear(); + + final response = await apiService.postHrTpaDashboard({ + "client_id": empClientId, + "client_policy_id": clientPolicyId, + }, _postPreToken); + + if (response['status'] == 'success') { +setState(() { + isTpaSelected = true; +}); + + _registerMetabaseIframe( + token: response['data']['metabaseToken'], + url: response['data']['metabaseUrl'], + clientPolicyId: clientPolicyId, + ); + + setState(() => _metabaseLoaded = true); + + } else { + ToastHelper.showErrorToast(context, response['message']); + } + } catch (e) { + ToastHelper.showErrorToast(context, 'TPA Dashboard loading failed'); + } finally { + setState(() => isDashboardLoading = false); + } + } + void _registerMetabaseIframe({ required String token, required String url, required String clientPolicyId, }) { - final viewType = 'metabase-dashboard-$clientPolicyId'; + // 🔥 ALWAYS CREATE UNIQUE VIEW TYPE + final viewType = + 'metabase-dashboard-${clientPolicyId}-${DateTime.now().millisecondsSinceEpoch}'; + _dashboardViewType = viewType; - if (_registeredViewTypes.contains(viewType)) return; + final embedUrl = + "$url/embed/dashboard/$token" + "#theme=light&bordered=true&titled=true" + "&v=${DateTime.now().millisecondsSinceEpoch}"; // 🔥 cache buster - final embedUrl = "$url/embed/dashboard/$token#theme=light&bordered=true&titled=true"; + final iframe = html.IFrameElement() + ..src = embedUrl + ..style.border = 'none' + ..style.width = '100%' + ..style.height = '100%' + ..allowFullscreen = true; + + _currentIframe = iframe; ui.platformViewRegistry.registerViewFactory( viewType, - (int viewId) => html.IFrameElement() - ..src = embedUrl - ..style.border = 'none' - ..style.width = '100%' - ..style.height = '100%' - ..allowFullscreen = true, + (int viewId) => iframe, ); _registeredViewTypes.add(viewType); @@ -185,7 +252,7 @@ class _hrDashboardState extends State with SingleTickerProviderStat Widget _buildDashboardView() { if (postModules.isNotEmpty && activePoliciesList.isEmpty) { - return const Center(child: Text('No dashboard data available for your account')); + return const Center(child: Text('No active policy found.')); } return Padding( @@ -339,95 +406,127 @@ class _hrDashboardState extends State with SingleTickerProviderStat color: Colors.white, child: Row( children: [ - const Text( - 'Select Policy', - style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13), - ), - const SizedBox(width: 12), - SizedBox( - width: 420, - height: 40, - child: SearchAnchor( - viewBackgroundColor: Colors.white, - viewConstraints: const BoxConstraints(maxHeight: 220), - - builder: (BuildContext context, SearchController controller) { - String displayText = "Select Policy"; - - if (selectedPolicyId != null) { - final policy = activePoliciesList.firstWhere( - (p) => p['client_policy_id'].toString() == selectedPolicyId, - orElse: () => {}, - ); - if (policy.isNotEmpty) { - displayText = "${policy['type']} - ${policy['policy_no']}"; - } - } - - return InkWell( - onTap: () { - setState(() => _isPolicyDropdownOpen = true); // 🔥 OPEN - controller.openView(); - }, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12), - decoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade300), - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - displayText, - style: const TextStyle(fontSize: 12), - overflow: TextOverflow.ellipsis, - ), - ), - const Icon(Icons.arrow_drop_down, color: Colors.grey), - ], - ), - ), - ); - }, - - suggestionsBuilder: - (BuildContext context, SearchController controller) { - final input = controller.text.toLowerCase(); - - return activePoliciesList - .where((policy) => - policy['type'] - .toString() - .toLowerCase() - .contains(input) || - policy['policy_no'] - .toString() - .toLowerCase() - .contains(input)) - .map((policy) { - final label = - "${policy['type']} - ${policy['policy_no']}"; - - return ListTile( - dense: true, - title: Text(label, style: const TextStyle(fontSize: 13)), - onTap: () { - setState(() { - selectedPolicyId = - policy['client_policy_id'].toString(); - _isPolicyDropdownOpen = false; // 🔥 CLOSE - }); - - controller.closeView(label); - _loadDashboardByPolicy(selectedPolicyId!); - }, - ); - }).toList(); - }, + if (!isTpaSelected)...[ + const Text( + 'Select Policy', + style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13), + ), + const SizedBox(width: 12), + SizedBox( + width: 420, + height: 40, + child: SearchAnchor( + viewBackgroundColor: Colors.white, + viewConstraints: const BoxConstraints(maxHeight: 220), + + builder: (BuildContext context, SearchController controller) { + String displayText = "Select Policy"; + + if (selectedPolicyId != null) { + final policy = activePoliciesList.firstWhere( + (p) => p['client_policy_id'].toString() == selectedPolicyId, + orElse: () => {}, + ); + if (policy.isNotEmpty) { + displayText = "${policy['type']} - ${policy['policy_no']}"; + } + } + + return InkWell( + onTap: () { + setState(() => _isPolicyDropdownOpen = true); // 🔥 OPEN + controller.openView(); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + displayText, + style: const TextStyle(fontSize: 12), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon(Icons.arrow_drop_down, color: Colors.grey), + ], + ), + ), + ); + }, + + suggestionsBuilder: + (BuildContext context, SearchController controller) { + final input = controller.text.toLowerCase(); + + return activePoliciesList + .where((policy) => + policy['type'] + .toString() + .toLowerCase() + .contains(input) || + policy['policy_no'] + .toString() + .toLowerCase() + .contains(input)) + .map((policy) { + final label = + "${policy['type']} - ${policy['policy_no']}"; + + return ListTile( + dense: true, + title: Text(label, style: const TextStyle(fontSize: 13)), + onTap: () { + setState(() { + selectedPolicyId = + policy['client_policy_id'].toString(); + _isPolicyDropdownOpen = false; // 🔥 CLOSE + }); + + controller.closeView(label); + _loadDashboardByPolicy(selectedPolicyId!); + }, + ); + }).toList(); + }, + ), + ), + ], + + + 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 ", + ), ), - ), ], ), ); diff --git a/lib/presentation/hrPolicyDetails.dart b/lib/presentation/hrPolicyDetails.dart index 072019c..c702f11 100755 --- a/lib/presentation/hrPolicyDetails.dart +++ b/lib/presentation/hrPolicyDetails.dart @@ -25,6 +25,7 @@ import 'package:collection/collection.dart'; import 'package:url_launcher/url_launcher.dart'; import '../customAppBar/base_layout.dart'; import '../customAppBar/customFooter.dart'; +import '../service/secure_pop_scope.dart'; import 'hrDashboard.dart'; class hrPolicyDetails extends StatefulWidget { @@ -67,6 +68,21 @@ class hrPolicyDetails extends StatefulWidget { class _HrPolicyDetailsState extends State with TickerProviderStateMixin { + + String? localClientId; + String? localPolicyTypeId; + String? localClientPolicyId; + String? localClientBranchId; + String? localToken; + String? localTokenType; + String? localCardType; + String? localCardPolicyNo; + String? localCardInsurerName; + String? localCardPolicyName; + String? localCardPolicyExpDate; + String? localTotalPremium; + int localIsEcardBulkDownload = 0; + final tokenService = TokenStorageService(); dynamic empPrimaryId; dynamic empClientId; @@ -156,16 +172,93 @@ class _HrPolicyDetailsState extends State apiService = ApiService(context); // If you are using TabBar, you MUST initialize this: _tabController = TabController(length: 2, vsync: this); + restorePolicyData(); + } + + Future restorePolicyData() async { + localClientId = widget.ClientId.isNotEmpty + ? widget.ClientId + : await tokenService.readValue('hr_ClientId'); + + localPolicyTypeId = widget.policyTypeId.isNotEmpty + ? widget.policyTypeId + : await tokenService.readValue('hr_policyTypeId'); + + localClientPolicyId = widget.ClientPoliyId.isNotEmpty + ? widget.ClientPoliyId + : await tokenService.readValue('hr_ClientPoliyId'); + + localClientBranchId = widget.clientBranchId.isNotEmpty + ? widget.clientBranchId + : await tokenService.readValue('hr_clientBranchId'); + + localToken = widget.Token.isNotEmpty + ? widget.Token + : await tokenService.readValue('hr_Token'); + + localTokenType = widget.TokenType.isNotEmpty + ? widget.TokenType + : await tokenService.readValue('hr_TokenType'); + + localCardType = widget.cardType.isNotEmpty + ? widget.cardType + : await tokenService.readValue('hr_cardType'); + + localCardPolicyNo = widget.cardPolicyNo.isNotEmpty + ? widget.cardPolicyNo + : await tokenService.readValue('hr_cardPolicyNo'); + + localCardInsurerName = widget.cardInsurer_name.isNotEmpty + ? widget.cardInsurer_name + : await tokenService.readValue('hr_cardInsurer_name'); + + localCardPolicyName = widget.cardPolicy_name.isNotEmpty + ? widget.cardPolicy_name + : await tokenService.readValue('hr_cardPolicy_name'); + + localCardPolicyExpDate = widget.cardPolicy_ExpDate.isNotEmpty + ? widget.cardPolicy_ExpDate + : await tokenService.readValue('hr_cardPolicy_ExpDate'); + + localTotalPremium = widget.total_premium.isNotEmpty + ? widget.total_premium + : await tokenService.readValue('hr_total_premium'); + + final savedBulk = await tokenService.readValue( + 'hr_is_ecard_bulk_download_for_employee'); + + localIsEcardBulkDownload = + widget.is_ecard_bulk_download_for_employee != 0 + ? widget.is_ecard_bulk_download_for_employee + : int.tryParse(savedBulk ?? '0') ?? 0; + getCDPoliciesDetails(); } + + Future clearPolicyStorage() async { + await tokenService.removeValue('hr_ClientId'); + await tokenService.removeValue('hr_policyTypeId'); + await tokenService.removeValue('hr_ClientPoliyId'); + await tokenService.removeValue('hr_clientBranchId'); + await tokenService.removeValue('hr_Token'); + await tokenService.removeValue('hr_TokenType'); + await tokenService.removeValue('hr_cardType'); + await tokenService.removeValue('hr_cardPolicyNo'); + await tokenService.removeValue('hr_cardInsurer_name'); + await tokenService.removeValue('hr_cardPolicy_name'); + await tokenService.removeValue('hr_cardPolicy_ExpDate'); + await tokenService.removeValue('hr_total_premium'); + await tokenService.removeValue('hr_is_ecard_bulk_download_for_employee'); + } + // Future _loadToken() async { // _postPreToken = tokenService.getCurrentToken(); - // if(widget.TokenType == "post") { + // if(localTokenType == "post") { // empClientId = await tokenService.readValue('empClientId'); // empClientBranchId = await tokenService.readValue('empClientBranchId'); // empHrId = await tokenService.readValue('empHrId'); // } - // if(widget.TokenType == "pre") { + // if(localTokenType == "pre") { // enrollmentClient_id = await tokenService.readValue('enrollmentClient_id'); // enrollmentEmpClientBranchId = await tokenService.readValue('enrollmentEmpClientBranchId'); // enrollmentHrId = await tokenService.readValue('enrollmentHrId'); @@ -199,11 +292,11 @@ class _HrPolicyDetailsState extends State hasModule = moduleList!.contains(storeModuleId); } - final response = widget.TokenType == "post" - ? await apiService.getEmployeeAndDependenceToApi(widget.ClientId, - widget.ClientPoliyId, widget.clientBranchId, widget.Token) - : await apiService.getEmployeeAndDependenceToApiPre(widget.ClientId, - widget.ClientPoliyId, widget.clientBranchId, widget.Token!); + final response = localTokenType == "post" + ? await apiService.getEmployeeAndDependenceToApi(localClientId ?? widget.ClientId, + localClientPolicyId ?? widget.ClientPoliyId, localClientBranchId ?? widget.clientBranchId, localToken ?? widget.Token) + : await apiService.getEmployeeAndDependenceToApiPre(localClientId ?? widget.ClientId, + localClientPolicyId ?? widget.ClientPoliyId, localClientBranchId ?? widget.clientBranchId, localToken ?? widget.Token!); if (response['status'] == 'success') { setState(() { @@ -260,15 +353,15 @@ class _HrPolicyDetailsState extends State hasModule = moduleList?.contains(storeModuleId) ?? false; } - print('Calling API with TokenType: ${widget.TokenType}'); + print('Calling API with TokenType: ${localTokenType}'); print( - 'ClientId: ${widget.ClientId}, ClientPoliyId: ${widget.ClientPoliyId}'); + 'ClientId: ${localClientId}, ClientPoliyId: ${localClientPolicyId}'); - final response = widget.TokenType == "post" - ? await apiService.getEmployeeAndDependenceToApi(widget.ClientId, - widget.ClientPoliyId, widget.clientBranchId, widget.Token) - : await apiService.getEmployeeAndDependenceToApiPre(widget.ClientId, - widget.ClientPoliyId, widget.clientBranchId, widget.Token!); + final response = localTokenType == "post" + ? await apiService.getEmployeeAndDependenceToApi(localClientId ?? widget.ClientId, + localClientPolicyId ?? widget.ClientPoliyId, localClientBranchId ?? widget.clientBranchId, localToken ?? widget.Token) + : await apiService.getEmployeeAndDependenceToApiPre(localClientId ?? widget.ClientId, + localClientPolicyId ?? widget.ClientPoliyId, localClientBranchId ?? widget.clientBranchId, localToken ?? widget.Token!); print('API Response: ${response.toString()}'); @@ -323,7 +416,7 @@ class _HrPolicyDetailsState extends State 'policy_no': policyNo }; final response = - await apiService.getEcardRequest(eCarDParams, widget.Token); + await apiService.getEcardRequest(eCarDParams, localToken!); print('check 1'); final ecardDownloadUrl = response['data']['eCardDownload']; final message = response['data']['message']; @@ -445,7 +538,7 @@ class _HrPolicyDetailsState extends State final bytes = utf8.encode(csvData); final blob = html.Blob([bytes]); final url = html.Url.createObjectUrlFromBlob(blob); - final String csvFileName = "policies(${widget.cardPolicyNo}).csv"; + final String csvFileName = "policies(${localCardPolicyNo}).csv"; final anchor = html.AnchorElement(href: url) ..setAttribute("download", csvFileName) ..click(); @@ -471,12 +564,12 @@ class _HrPolicyDetailsState extends State try { print('10'); - if (widget.TokenType == 'pre') { + if (localTokenType == 'pre') { response = await apiService.getPreLogHrActivity( - postId!, preId!, widget.Token, activityPre); - } else if (widget.TokenType == 'post') { + postId!, preId!, localToken!, activityPre); + } else if (localTokenType == 'post') { response = await apiService.getPostLogHrActivity( - postId!, preId!, widget.Token, activity); + postId!, preId!, localToken!, activity); } if (response['status'] == 'success') { @@ -502,7 +595,7 @@ class _HrPolicyDetailsState extends State print('10 $emp_policy_ids'); empHrId = await tokenService.readValue('empHrId'); final response = await apiService.getEcardBulkDownloadApi( - '', empHrId, emp_policy_ids, widget.Token); + '', empHrId, emp_policy_ids, localToken!); if (response['status'] == true) { print('Request success'); _showBulkDownloadSuccessPopup(response['message']); @@ -598,7 +691,15 @@ class _HrPolicyDetailsState extends State children: [ IconButton( tooltip: 'Previous Page', - onPressed: () => {Navigator.pop(context)}, + onPressed: () async => { + await clearPolicyStorage(), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => policies(), + ), + ) + }, // splashRadius: 20, icon: const Icon( Icons.arrow_back_ios, @@ -616,7 +717,7 @@ class _HrPolicyDetailsState extends State mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - "${widget.cardType} - ${widget.cardPolicyNo} " ?? + "${localCardType} - ${localCardPolicyNo} " ?? '', style: GoogleFonts.poppins( color: Colors.black, @@ -625,9 +726,9 @@ class _HrPolicyDetailsState extends State ), ), Text( - widget.TokenType == 'pre' - ? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})" - : "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})", + localTokenType == 'pre' + ? "${localCardPolicyName} (${localCardPolicyExpDate})" + : "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 12, @@ -665,7 +766,7 @@ class _HrPolicyDetailsState extends State ), ), - if (widget.is_ecard_bulk_download_for_employee == + if (localIsEcardBulkDownload == 1) ...[ const SizedBox(width: 12), SizedBox( @@ -698,12 +799,25 @@ class _HrPolicyDetailsState extends State width: 116, height: 37, child: ElevatedButton( - onPressed: () { + onPressed: () async { + + await tokenService.writeValue('upload_ClientId', widget.ClientId.toString()); + await tokenService.writeValue('upload_policyTypeId', widget.policyTypeId.toString()); + await tokenService.writeValue('upload_ClientPoliyId', widget.ClientPoliyId.toString()); + await tokenService.writeValue('upload_clientBranchId', widget.clientBranchId.toString()); + await tokenService.writeValue('upload_Token', widget.Token.toString()); + await tokenService.writeValue('upload_TokenType', widget.TokenType.toString()); + await tokenService.writeValue('upload_cardType', widget.cardType.toString()); + await tokenService.writeValue('upload_cardPolicyNo', widget.cardPolicyNo.toString()); + await tokenService.writeValue('upload_cardInsurer_name', widget.cardInsurer_name.toString()); + await tokenService.writeValue('upload_cardPolicy_name', widget.cardPolicy_name.toString()); + await tokenService.writeValue('upload_cardPolicy_ExpDate', widget.cardPolicy_ExpDate.toString()); + await tokenService.writeValue('upload_total_premium', widget.total_premium.toString()); Navigator.push( context, MaterialPageRoute( - settings: widget.TokenType != "post" ? RouteSettings(name: 'preFileUpload') : RouteSettings(name: 'postFileUpload'), - builder: (context) => widget.TokenType != + settings: localTokenType != "post" ? RouteSettings(name: 'preFileUpload') : RouteSettings(name: 'postFileUpload'), + builder: (context) => localTokenType != "post" ? preFileUpload( ClientId: @@ -751,7 +865,7 @@ class _HrPolicyDetailsState extends State widget.cardPolicy_ExpDate, total_premium: widget.total_premium, - // Token: widget.Token, + // Token: localToken, // ClientId: widget.ClientId, // ClientPolicyId : widget.ClientPoliyId, // PolicyName: widget.cardPolicy_name, @@ -823,13 +937,13 @@ class _HrPolicyDetailsState extends State ), // ---------------- PREMIUM / STATUS ---------------- - if (widget.TokenType == "pre") + if (localTokenType == "pre") Padding( padding: const EdgeInsets.only(left: 15), child: _buildStatusSummary(), ), - if (widget.TokenType == "post") + if (localTokenType == "post") Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( @@ -842,7 +956,7 @@ class _HrPolicyDetailsState extends State borderRadius: BorderRadius.circular(6), ), child: Text( - 'Premium - ₹${widget.total_premium}', + 'Premium - ₹${localTotalPremium}', style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, @@ -872,9 +986,11 @@ class _HrPolicyDetailsState extends State 'assets/nhance-loader.gif'), // Adjust path to your GIF loader ), ) - : SingleChildScrollView( - child: _buildCDDataTable(context), - ), + : Column( + children: [ + _buildCDDataTable(context), // ← DO NOT wrap again in Expanded + ], + ) ), ), @@ -939,7 +1055,7 @@ class _HrPolicyDetailsState extends State // ), // ), // Text( - // widget.TokenType == 'pre' + // localTokenType == 'pre' // ? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})" // : "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})", // style: GoogleFonts.poppins( @@ -979,7 +1095,7 @@ class _HrPolicyDetailsState extends State // ), // ), // - // if (widget.is_ecard_bulk_download_for_employee == 1) ...[ + // if (localIsEcardBulkDownload == 1) ...[ // const SizedBox(width: 12), // SizedBox( // width: 40, @@ -1015,15 +1131,15 @@ class _HrPolicyDetailsState extends State // Navigator.push( // context, // MaterialPageRoute( - // builder: (context) => widget.TokenType != "post" + // builder: (context) => localTokenType != "post" // ? preFileUpload( // ClientId: - // widget.ClientId, // <-- from map - // policyTypeId: widget.policyTypeId, - // ClientPoliyId: widget.ClientPoliyId, + // localClientId, // <-- from map + // policyTypeId: localPolicyTypeId, + // ClientPoliyId: localClientPolicyId, // clientBranchId: widget.clientBranchId, // Token: widget.Token, - // TokenType: widget.TokenType, + // TokenType: localTokenType, // cardType: widget.cardType, // cardPolicyNo: widget.cardPolicyNo, // cardInsurer_name: @@ -1034,8 +1150,8 @@ class _HrPolicyDetailsState extends State // total_premium: widget.total_premium, // // // Token: widget.Token, - // // ClientId: widget.ClientId, - // // ClientPolicyId : widget.ClientPoliyId, + // // ClientId: localClientId, + // // ClientPolicyId : localClientPolicyId, // // PolicyName: widget.cardPolicy_name, // // PolicyNo: widget.cardPolicyNo, // // ClientBranchId: widget.HrId, @@ -1043,12 +1159,12 @@ class _HrPolicyDetailsState extends State // ) // : postFileUpload( // ClientId: - // widget.ClientId, // <-- from map - // policyTypeId: widget.policyTypeId, - // ClientPoliyId: widget.ClientPoliyId, + // localClientId, // <-- from map + // policyTypeId: localPolicyTypeId, + // ClientPoliyId: localClientPolicyId, // clientBranchId: widget.clientBranchId, // Token: widget.Token, - // TokenType: widget.TokenType, + // TokenType: localTokenType, // cardType: widget.cardType, // cardPolicyNo: widget.cardPolicyNo, // cardInsurer_name: @@ -1059,8 +1175,8 @@ class _HrPolicyDetailsState extends State // total_premium: widget.total_premium, // // // Token: widget.Token, - // // ClientId: widget.ClientId, - // // ClientPolicyId : widget.ClientPoliyId, + // // ClientId: localClientId, + // // ClientPolicyId : localClientPolicyId, // // PolicyName: widget.cardPolicy_name, // // PolicyNo: widget.cardPolicyNo, // // ClientBranchId: widget.HrId, @@ -1117,8 +1233,8 @@ class _HrPolicyDetailsState extends State // ], // ), // SizedBox(height: 20), - // if (widget.TokenType == "pre") ...[_buildStatusSummary()], - // if (widget.TokenType == "post") ...[ + // if (localTokenType == "pre") ...[_buildStatusSummary()], + // if (localTokenType == "post") ...[ // Row( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ @@ -1247,595 +1363,829 @@ class _HrPolicyDetailsState extends State } Widget _buildCDDataTable(BuildContext context) { - if (_paginatedData.isNotEmpty) { - hasAnyEcardLink = _paginatedData.any( - (item) => item['ecard_download_link'] != null, - ); - - if (widget.TokenType == "post" && hasModule) { - print("paginatTtt - $_paginatedData"); - - print("📥 Any e-card link present: $hasAnyEcardLink"); - } - } - - print('widgetpolicyTypeId'); - print(widget.policyTypeId); - if (filteredData.isEmpty) { - return SizedBox( - // height: 50, - child: Center( - child: Text( - 'No data is available for the selected policy', - style: GoogleFonts.poppins( - color: Colors.grey, - fontWeight: FontWeight.w400, - ), - )), - ); + return const Center(child: Text('No data available')); } - final currentPageIds = _paginatedData - .map((e) => e['id']?.toString()) - .whereType() - .toList(); + return Expanded( // ✅ VERY IMPORTANT + child: CustomScrollView( + slivers: [ - return Container( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Header row - Container( - decoration: BoxDecoration( - color: Color(0xFFD7E9EB), - borderRadius: BorderRadius.circular(6), - ), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - child: Row( - children: [ - if (widget.TokenType == 'post' && - widget.is_ecard_bulk_download_for_employee == 1) - SizedBox( - width: 40, - child: Checkbox( - value: currentPageIds.isNotEmpty && - currentPageIds.every(selectedEmployeeIds.contains), - onChanged: (checked) { - setState(() { - if (checked == true) { - selectedEmployeeIds.addAll(currentPageIds); - } else { - selectedEmployeeIds.removeAll(currentPageIds); - } - }); - }, - ), - ), - Expanded( - flex: 3, - child: Text( - 'Name', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - if (widget.policyTypeId != '6' && widget.policyTypeId != '7') - Expanded( - flex: 2, - child: Text( - 'UHID', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - Expanded( - flex: 2, - child: Text( - 'Relationship', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - Expanded( - flex: 2, - child: Text( - 'Date Of Birth', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - Expanded( - flex: 2, - child: Text( - 'Gender', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - Expanded( - flex: 2, - child: Text( - 'Mobile', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - Expanded( - flex: 5, - child: Text( - 'Email', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - Expanded( - flex: 2, - child: Text( - 'Status', - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - if (widget.TokenType != "pre" && (hasAnyEcardLink || hasModule)) - Expanded( - flex: 3, - child: Text( - 'Action', - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - color: Colors.black, - fontWeight: FontWeight.w600, - ), - ), - ), - ], + /// 🔒 Sticky Header + SliverPersistentHeader( + pinned: true, + delegate: _CDHeaderDelegate( + showUHID: localPolicyTypeId != '6' && + localPolicyTypeId != '7', + showAction: + localTokenType != "pre" && + (hasAnyEcardLink || hasModule), ), ), - const SizedBox(height: 6), - - SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _paginatedData.mapIndexed((index, item) { - return Container( - // margin: const EdgeInsets.only(bottom: 8), - padding: - const EdgeInsets.symmetric(vertical: 5, horizontal: 16), - decoration: BoxDecoration( - // color: Colors.white, - border: Border( - bottom: BorderSide( - // color: Color(0xFFA1A1A1), - color: Color(0xFFA9D9DE), - width: 1, - ), - ), - // color: index % 2 == 0 ? Color(0xFFE6FAFB) : Colors.white, - // borderRadius: BorderRadius.circular(6), - ), - child: Row( - children: [ - if (widget.TokenType == 'post' && - widget.is_ecard_bulk_download_for_employee == 1) - SizedBox( - width: 40, - child: Checkbox( - value: selectedEmployeeIds - .contains(item['id']?.toString()), - onChanged: (checked) { - setState(() { - final id = item['id']?.toString(); - if (id == null) return; - - if (checked == true) { - selectedEmployeeIds.add(id); - } else { - selectedEmployeeIds.remove(id); - } - }); - }, - ), - ), - - Expanded( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item['name'] ?? '-', - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: 12), - ), - Text( - item['emp_code'] ?? '-', - style: GoogleFonts.poppins( - color: Color(0xFF585757), - fontWeight: FontWeight.w300, - fontSize: 10), - ), - ], - ), - ), - if (widget.policyTypeId != '6' && - widget.policyTypeId != '7') - Expanded( - flex: 2, - child: Text( - item['uhid'] ?? '-', - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: 12), - ), - ), - Expanded( - flex: 2, - child: Text( - "${item['relationship'] ?? ''}", - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: 12), - ), - ), - Expanded( - flex: 2, - child: Text( - "${item['formatted_dob'].replaceAll("/", "-") ?? ''}", - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: 12), - ), - ), - Expanded( - flex: 2, - child: Text( - "${item['gender'] ?? ''}", - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: 12), - ), - ), - Expanded( - flex: 2, - child: Text( - "${item['mobile'] ?? ''}", - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: 12), - ), - ), - Expanded( - flex: 5, - child: Text( - "${item['email_corporate'] ?? ''}", - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w400, - fontSize: 12), - ), - ), - Expanded( - flex: 2, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 4, vertical: 4), - decoration: BoxDecoration( - color: getStatusColor(item['status'] ?? ''), - // color: (item['emp_is_active'] == "1") - // ? Color(0xFF7BD9B6) - // : Color(0xFFFFA6A6), - borderRadius: BorderRadius.circular(10), - ), - child: Align( - alignment: Alignment.center, - child: Text( - _capitalize(item['status']), - style: GoogleFonts.poppins( - color: Color(0xFF000000), - fontWeight: FontWeight.w500, - fontSize: 12, - ), - ), - ), - ), - ), - - if (widget.TokenType != "pre" && - (hasAnyEcardLink || hasModule)) - Expanded( - flex: 3, - child: Builder( - builder: (context) { - final isSelf = item['relationship'] == 'Self'; - final hasEcard = item['ecard_download_link'] != null; - final showEcard = isSelf && hasEcard; - final showClaim = widget.TokenType == "post" && hasModule; - - if (!showEcard && !showClaim) { - return SizedBox(); // No icon to show - } - - return SizedBox( - height: 40, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - - /// --- eCard Button (Fixed Space) --- - SizedBox( - width: 40, - height: 40, - child: Visibility( - visible: showEcard, - maintainSize: true, - maintainAnimation: true, - maintainState: true, - child: Tooltip( - message: 'Download e-Card', - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - getEcardDownload( - item['emp_code'], - item['employee_id'], - item['client_policy_id'], - item['policy_no'], - ); - }, - child: Container( - decoration: BoxDecoration( - color: const Color(0xFFE6F5F6), - borderRadius: BorderRadius.circular(8), - ), - padding: const EdgeInsets.all(6), - child: Image.asset( - 'assets/credit_card.png', - fit: BoxFit.contain, - ), - ), - ), - ), - ), - ), - ), - - const SizedBox(width: 8), - - /// --- Claim Button (Fixed Space) --- - SizedBox( - width: 40, - height: 40, - child: Visibility( - visible: showClaim, - maintainSize: true, - maintainAnimation: true, - maintainState: true, - child: Tooltip( - message: 'View Claims', - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ClaimsPolicies( - empCode: item['emp_code']!, - ), - ), - ); - }, - child: Container( - decoration: BoxDecoration( - color: const Color(0xFFE6F5F6), - borderRadius: BorderRadius.circular(8), - ), - padding: const EdgeInsets.all(6), - child: Image.asset( - 'assets/claim.png', - fit: BoxFit.contain, - ), - ), - ), - ), - ), - ), - ), - ], - ), - ); - }, - ), - ), - - // (hasAnyEcardLink || hasModule)) - // Expanded( - // flex: 3, - // child: Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // if (item['ecard_download_link'] != null) - // GestureDetector( - // onTap: () { - // _launchURL(item['ecard_download_link']); - // }, - // child: Container( - // height: 35, - // width: 35, - // decoration: BoxDecoration( - // color: Color(0xFFE6F5F6), - // borderRadius: BorderRadius.circular(8), - // ), - // child: Padding( - // padding: EdgeInsets.all( - // 6), // You can adjust this value - // child: Image.asset( - // 'assets/credit_card.png', - // fit: BoxFit.contain, - // ), - // ), - // ), - // ), - // SizedBox( - // width: 10, - // ), - // if (widget.TokenType == "post" && hasModule) - // GestureDetector( - // onTap: () { - // setState(() { - // print( - // "policytabdata - ${item['emp_code']!}"); - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => hrDashboard( - // selectedIndex: 3, - // empCodeFromHrPolicy: - // item['emp_code']!, - // isHrcode: 1, - // ), - // ), - // ); - // }); - // }, - // child: Container( - // height: 35, - // width: 35, - // decoration: BoxDecoration( - // color: Color(0xFFE6F5F6), - // borderRadius: BorderRadius.circular(8), - // ), - // child: Padding( - // padding: EdgeInsets.all( - // 6), // You can adjust this value - // child: Image.asset( - // 'assets/claim.png', - // fit: BoxFit.contain, - // ), - // ), - // ), - // ), - // ], - // ), - // ), - ], - ), - ); - }).toList(), + /// 📄 Rows + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final item = _paginatedData[index]; + return _buildCDRow(item); + }, + childCount: _paginatedData.length, ), ), - // ), - _buildPagination(context) - // Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // Padding( - // padding: const EdgeInsets.symmetric(vertical: 12), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // DropdownButton( - // value: _rowsPerPage, - // items: [5, 10, 15, 20, 50].map((int value) { - // return DropdownMenuItem( - // value: value, - // child: Text( - // ' $value ', - // style: GoogleFonts.poppins(fontSize: 15), - // ), - // ); - // }).toList(), - // onChanged: (newValue) { - // setState(() { - // _rowsPerPage = newValue!; - // _currentPage = - // 1; // Reset to first page when rows per page changes - // }); - // }, - // ), - // IconButton( - // onPressed: _currentPage > 1 - // ? () { - // setState(() { - // _currentPage--; - // }); - // } - // : null, - // icon: Icon(Icons.chevron_left), - // ), - // for (int i = 1; - // i <= (filteredData.length / _rowsPerPage).ceil(); - // i++) - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 4), - // child: ElevatedButton( - // style: ElevatedButton.styleFrom( - // backgroundColor: _currentPage == i - // ? Color(0xFF00A6A6) - // : Colors.grey[300], - // foregroundColor: - // _currentPage == i ? Colors.white : Colors.black, - // minimumSize: Size(36, 36), - // padding: EdgeInsets.zero, - // ), - // onPressed: () { - // setState(() { - // _currentPage = i; - // }); - // }, - // child: Text(i.toString()), - // ), - // ), - // IconButton( - // onPressed: _currentPage < - // (filteredData.length / _rowsPerPage).ceil() - // ? () { - // setState(() { - // _currentPage++; - // }); - // } - // : null, - // icon: Icon(Icons.chevron_right), - // ), - // ], - // ), - // ), - // ], - // ), + + /// 📌 Pagination + SliverToBoxAdapter( + child: _buildPagination(context), + ), ], ), ); } + Widget _buildCDRow(Map item) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + margin: const EdgeInsets.only(top: 6), + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(color: Color(0xFFA9D9DE)), + ), + ), + child: Row( + children: [ + + /// NAME + Expanded( + flex: 3, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item['name'] ?? '-', style: _dataBold), + Text(item['emp_code'] ?? '-', style: _dataSub), + ], + ), + ), + + /// UHID + if (localPolicyTypeId != '6' && + localPolicyTypeId != '7') + Expanded( + flex: 2, + child: Text(item['uhid'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 2, + child: Text(item['relationship'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 2, + child: Text( + item['formatted_dob']?.replaceAll("/", "-") ?? '-', + style: _dataBold), + ), + + Expanded( + flex: 2, + child: Text(item['gender'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 2, + child: Text(item['mobile'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 5, + child: Text(item['email_corporate'] ?? '-', style: _dataBold), + ), + + /// STATUS + Expanded( + flex: 2, + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: getStatusColor(item['status'] ?? ''), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + _capitalize(item['status']), + textAlign: TextAlign.center, + style: _dataBold, + ), + ), + ), + + /// ACTION + + if (localTokenType != "pre" && + (hasAnyEcardLink || hasModule)) + Expanded( + flex: 3, + child: Builder( + builder: (context) { + final isSelf = item['relationship'] == 'Self'; + final hasEcard = item['ecard_download_link'] != null; + final showEcard = isSelf && hasEcard; + final showClaim = localTokenType == "post" && hasModule; + + if (!showEcard && !showClaim) { + return SizedBox(); // No icon to show + } + + return SizedBox( + height: 40, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + + /// --- eCard Button (Fixed Space) --- + SizedBox( + width: 40, + height: 40, + child: Visibility( + visible: showEcard, + maintainSize: true, + maintainAnimation: true, + maintainState: true, + child: Tooltip( + message: 'Download e-Card', + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + getEcardDownload( + item['emp_code'], + item['employee_id'], + item['client_policy_id'], + item['policy_no'], + ); + }, + child: Container( + decoration: BoxDecoration( + color: const Color(0xFFE6F5F6), + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.all(6), + child: Image.asset( + 'assets/credit_card.png', + fit: BoxFit.contain, + ), + ), + ), + ), + ), + ), + ), + + const SizedBox(width: 8), + + /// --- Claim Button (Fixed Space) --- + SizedBox( + width: 40, + height: 40, + child: Visibility( + visible: showClaim, + maintainSize: true, + maintainAnimation: true, + maintainState: true, + child: Tooltip( + message: 'View Claims', + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ClaimsPolicies( + empCode: item['emp_code']!, + ), + ), + ); + }, + child: Container( + decoration: BoxDecoration( + color: const Color(0xFFE6F5F6), + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.all(6), + child: Image.asset( + 'assets/claim.png', + fit: BoxFit.contain, + ), + ), + ), + ), + ), + ), + ), + ], + ), + ); + }, + ), + ), + ], + ), + ); + } + + // Widget _buildCDDataTable(BuildContext context) { + // if (_paginatedData.isNotEmpty) { + // hasAnyEcardLink = _paginatedData.any( + // (item) => item['ecard_download_link'] != null, + // ); + // + // if (localTokenType == "post" && hasModule) { + // print("paginatTtt - $_paginatedData"); + // + // print("📥 Any e-card link present: $hasAnyEcardLink"); + // } + // } + // + // print('widgetpolicyTypeId'); + // print(localPolicyTypeId); + // + // if (filteredData.isEmpty) { + // return SizedBox( + // // height: 50, + // child: Center( + // child: Text( + // 'No data is available for the selected policy', + // style: GoogleFonts.poppins( + // color: Colors.grey, + // fontWeight: FontWeight.w400, + // ), + // )), + // ); + // } + // + // final currentPageIds = _paginatedData + // .map((e) => e['id']?.toString()) + // .whereType() + // .toList(); + // + // return Container( + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // // Header row + // Container( + // decoration: BoxDecoration( + // color: Color(0xFFD7E9EB), + // borderRadius: BorderRadius.circular(6), + // ), + // padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + // child: Row( + // children: [ + // if (localTokenType == 'post' && + // localIsEcardBulkDownload == 1) + // SizedBox( + // width: 40, + // child: Checkbox( + // value: currentPageIds.isNotEmpty && + // currentPageIds.every(selectedEmployeeIds.contains), + // onChanged: (checked) { + // setState(() { + // if (checked == true) { + // selectedEmployeeIds.addAll(currentPageIds); + // } else { + // selectedEmployeeIds.removeAll(currentPageIds); + // } + // }); + // }, + // ), + // ), + // Expanded( + // flex: 3, + // child: Text( + // 'Name', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // if (localPolicyTypeId != '6' && localPolicyTypeId != '7') + // Expanded( + // flex: 2, + // child: Text( + // 'UHID', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // 'Relationship', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // 'Date Of Birth', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // 'Gender', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // 'Mobile', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Expanded( + // flex: 5, + // child: Text( + // 'Email', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // 'Status', + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // if (localTokenType != "pre" && (hasAnyEcardLink || hasModule)) + // Expanded( + // flex: 3, + // child: Text( + // 'Action', + // textAlign: TextAlign.center, + // style: GoogleFonts.poppins( + // color: Colors.black, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // ], + // ), + // ), + // + // const SizedBox(height: 6), + // + // SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: _paginatedData.mapIndexed((index, item) { + // return Container( + // // margin: const EdgeInsets.only(bottom: 8), + // padding: + // const EdgeInsets.symmetric(vertical: 5, horizontal: 16), + // decoration: BoxDecoration( + // // color: Colors.white, + // border: Border( + // bottom: BorderSide( + // // color: Color(0xFFA1A1A1), + // color: Color(0xFFA9D9DE), + // width: 1, + // ), + // ), + // // color: index % 2 == 0 ? Color(0xFFE6FAFB) : Colors.white, + // // borderRadius: BorderRadius.circular(6), + // ), + // child: Row( + // children: [ + // if (localTokenType == 'post' && + // localIsEcardBulkDownload == 1) + // SizedBox( + // width: 40, + // child: Checkbox( + // value: selectedEmployeeIds + // .contains(item['id']?.toString()), + // onChanged: (checked) { + // setState(() { + // final id = item['id']?.toString(); + // if (id == null) return; + // + // if (checked == true) { + // selectedEmployeeIds.add(id); + // } else { + // selectedEmployeeIds.remove(id); + // } + // }); + // }, + // ), + // ), + // + // Expanded( + // flex: 3, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // item['name'] ?? '-', + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w400, + // fontSize: 12), + // ), + // Text( + // item['emp_code'] ?? '-', + // style: GoogleFonts.poppins( + // color: Color(0xFF585757), + // fontWeight: FontWeight.w300, + // fontSize: 10), + // ), + // ], + // ), + // ), + // if (localPolicyTypeId != '6' && + // localPolicyTypeId != '7') + // Expanded( + // flex: 2, + // child: Text( + // item['uhid'] ?? '-', + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w400, + // fontSize: 12), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // "${item['relationship'] ?? ''}", + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w400, + // fontSize: 12), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // "${item['formatted_dob'].replaceAll("/", "-") ?? ''}", + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w400, + // fontSize: 12), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // "${item['gender'] ?? ''}", + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w400, + // fontSize: 12), + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // "${item['mobile'] ?? ''}", + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w400, + // fontSize: 12), + // ), + // ), + // Expanded( + // flex: 5, + // child: Text( + // "${item['email_corporate'] ?? ''}", + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w400, + // fontSize: 12), + // ), + // ), + // Expanded( + // flex: 2, + // child: Container( + // padding: const EdgeInsets.symmetric( + // horizontal: 4, vertical: 4), + // decoration: BoxDecoration( + // color: getStatusColor(item['status'] ?? ''), + // // color: (item['emp_is_active'] == "1") + // // ? Color(0xFF7BD9B6) + // // : Color(0xFFFFA6A6), + // borderRadius: BorderRadius.circular(10), + // ), + // child: Align( + // alignment: Alignment.center, + // child: Text( + // _capitalize(item['status']), + // style: GoogleFonts.poppins( + // color: Color(0xFF000000), + // fontWeight: FontWeight.w500, + // fontSize: 12, + // ), + // ), + // ), + // ), + // ), + // + // if (localTokenType != "pre" && + // (hasAnyEcardLink || hasModule)) + // Expanded( + // flex: 3, + // child: Builder( + // builder: (context) { + // final isSelf = item['relationship'] == 'Self'; + // final hasEcard = item['ecard_download_link'] != null; + // final showEcard = isSelf && hasEcard; + // final showClaim = localTokenType == "post" && hasModule; + // + // if (!showEcard && !showClaim) { + // return SizedBox(); // No icon to show + // } + // + // return SizedBox( + // height: 40, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // + // /// --- eCard Button (Fixed Space) --- + // SizedBox( + // width: 40, + // height: 40, + // child: Visibility( + // visible: showEcard, + // maintainSize: true, + // maintainAnimation: true, + // maintainState: true, + // child: Tooltip( + // message: 'Download e-Card', + // child: MouseRegion( + // cursor: SystemMouseCursors.click, + // child: GestureDetector( + // onTap: () { + // getEcardDownload( + // item['emp_code'], + // item['employee_id'], + // item['client_policy_id'], + // item['policy_no'], + // ); + // }, + // child: Container( + // decoration: BoxDecoration( + // color: const Color(0xFFE6F5F6), + // borderRadius: BorderRadius.circular(8), + // ), + // padding: const EdgeInsets.all(6), + // child: Image.asset( + // 'assets/credit_card.png', + // fit: BoxFit.contain, + // ), + // ), + // ), + // ), + // ), + // ), + // ), + // + // const SizedBox(width: 8), + // + // /// --- Claim Button (Fixed Space) --- + // SizedBox( + // width: 40, + // height: 40, + // child: Visibility( + // visible: showClaim, + // maintainSize: true, + // maintainAnimation: true, + // maintainState: true, + // child: Tooltip( + // message: 'View Claims', + // child: MouseRegion( + // cursor: SystemMouseCursors.click, + // child: GestureDetector( + // onTap: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => ClaimsPolicies( + // empCode: item['emp_code']!, + // ), + // ), + // ); + // }, + // child: Container( + // decoration: BoxDecoration( + // color: const Color(0xFFE6F5F6), + // borderRadius: BorderRadius.circular(8), + // ), + // padding: const EdgeInsets.all(6), + // child: Image.asset( + // 'assets/claim.png', + // fit: BoxFit.contain, + // ), + // ), + // ), + // ), + // ), + // ), + // ), + // ], + // ), + // ); + // }, + // ), + // ), + // + // // (hasAnyEcardLink || hasModule)) + // // Expanded( + // // flex: 3, + // // child: Row( + // // mainAxisAlignment: MainAxisAlignment.center, + // // children: [ + // // if (item['ecard_download_link'] != null) + // // GestureDetector( + // // onTap: () { + // // _launchURL(item['ecard_download_link']); + // // }, + // // child: Container( + // // height: 35, + // // width: 35, + // // decoration: BoxDecoration( + // // color: Color(0xFFE6F5F6), + // // borderRadius: BorderRadius.circular(8), + // // ), + // // child: Padding( + // // padding: EdgeInsets.all( + // // 6), // You can adjust this value + // // child: Image.asset( + // // 'assets/credit_card.png', + // // fit: BoxFit.contain, + // // ), + // // ), + // // ), + // // ), + // // SizedBox( + // // width: 10, + // // ), + // // if (localTokenType == "post" && hasModule) + // // GestureDetector( + // // onTap: () { + // // setState(() { + // // print( + // // "policytabdata - ${item['emp_code']!}"); + // // Navigator.push( + // // context, + // // MaterialPageRoute( + // // builder: (context) => hrDashboard( + // // selectedIndex: 3, + // // empCodeFromHrPolicy: + // // item['emp_code']!, + // // isHrcode: 1, + // // ), + // // ), + // // ); + // // }); + // // }, + // // child: Container( + // // height: 35, + // // width: 35, + // // decoration: BoxDecoration( + // // color: Color(0xFFE6F5F6), + // // borderRadius: BorderRadius.circular(8), + // // ), + // // child: Padding( + // // padding: EdgeInsets.all( + // // 6), // You can adjust this value + // // child: Image.asset( + // // 'assets/claim.png', + // // fit: BoxFit.contain, + // // ), + // // ), + // // ), + // // ), + // // ], + // // ), + // // ), + // ], + // ), + // ); + // }).toList(), + // ), + // ), + // // ), + // _buildPagination(context) + // // Row( + // // mainAxisAlignment: MainAxisAlignment.end, + // // children: [ + // // Padding( + // // padding: const EdgeInsets.symmetric(vertical: 12), + // // child: Row( + // // mainAxisAlignment: MainAxisAlignment.center, + // // children: [ + // // DropdownButton( + // // value: _rowsPerPage, + // // items: [5, 10, 15, 20, 50].map((int value) { + // // return DropdownMenuItem( + // // value: value, + // // child: Text( + // // ' $value ', + // // style: GoogleFonts.poppins(fontSize: 15), + // // ), + // // ); + // // }).toList(), + // // onChanged: (newValue) { + // // setState(() { + // // _rowsPerPage = newValue!; + // // _currentPage = + // // 1; // Reset to first page when rows per page changes + // // }); + // // }, + // // ), + // // IconButton( + // // onPressed: _currentPage > 1 + // // ? () { + // // setState(() { + // // _currentPage--; + // // }); + // // } + // // : null, + // // icon: Icon(Icons.chevron_left), + // // ), + // // for (int i = 1; + // // i <= (filteredData.length / _rowsPerPage).ceil(); + // // i++) + // // Padding( + // // padding: const EdgeInsets.symmetric(horizontal: 4), + // // child: ElevatedButton( + // // style: ElevatedButton.styleFrom( + // // backgroundColor: _currentPage == i + // // ? Color(0xFF00A6A6) + // // : Colors.grey[300], + // // foregroundColor: + // // _currentPage == i ? Colors.white : Colors.black, + // // minimumSize: Size(36, 36), + // // padding: EdgeInsets.zero, + // // ), + // // onPressed: () { + // // setState(() { + // // _currentPage = i; + // // }); + // // }, + // // child: Text(i.toString()), + // // ), + // // ), + // // IconButton( + // // onPressed: _currentPage < + // // (filteredData.length / _rowsPerPage).ceil() + // // ? () { + // // setState(() { + // // _currentPage++; + // // }); + // // } + // // : null, + // // icon: Icon(Icons.chevron_right), + // // ), + // // ], + // // ), + // // ), + // // ], + // // ), + // ], + // ), + // ); + // } + Widget _buildPagination(BuildContext context) { // 1. Calculate the range of entries being shown final totalItems = filteredData.length; @@ -1897,7 +2247,7 @@ class _HrPolicyDetailsState extends State // Dropdown for rows per page DropdownButton( 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) { return DropdownMenuItem( value: value, @@ -1981,4 +2331,86 @@ class _HrPolicyDetailsState extends State ), ); } + + static final _dataBold = GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataBoldStatus = GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w400, + color: Colors.white, + ); + + static final _dataSub = GoogleFonts.poppins( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static final _dataColorSub = GoogleFonts.poppins( + fontSize: 10, + fontWeight: FontWeight.w400, + color: Color(0xFFFF731C), + ); } + + +class _CDHeaderDelegate extends SliverPersistentHeaderDelegate { + final bool showUHID; + final bool showAction; + + _CDHeaderDelegate({ + required this.showUHID, + required this.showAction, + }); + + @override + double get minExtent => 55; + + @override + double get maxExtent => 55; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return Container( + color: const Color(0xFFD7E9EB), + padding: const EdgeInsets.symmetric(horizontal: 16), + alignment: Alignment.centerLeft, + child: Row( + children: [ + _headerCell('Name', 3), + if (showUHID) _headerCell('UHID', 2), + _headerCell('Relationship', 2), + _headerCell('Date Of Birth', 2), + _headerCell('Gender', 2), + _headerCell('Mobile', 2), + _headerCell('Email', 5), + _headerCell('Status', 2), + if (showAction) _headerCell('Action', 3, center: true), + ], + ), + ); + } + + Widget _headerCell(String text, int flex, + {bool center = false}) { + return Expanded( + flex: flex, + child: Text( + text, + textAlign: center ? TextAlign.center : TextAlign.left, + style: GoogleFonts.poppins( + fontWeight: FontWeight.w600, + ), + ), + ); + } + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => + true; +} \ No newline at end of file diff --git a/lib/presentation/policies.dart b/lib/presentation/policies.dart index a7674d5..aa85ae0 100644 --- a/lib/presentation/policies.dart +++ b/lib/presentation/policies.dart @@ -106,43 +106,114 @@ class _policiesState extends State } } + // Future _loadToken() async { + // final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]" + // final postRaw = await tokenService.readValue('empAllowed_modules'); // "[2,3,4]" + // + // // ✅ Decode safely + // enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty + // ? List.from(jsonDecode(enrollmentRaw)) + // : []; + // + // postModules = postRaw != null && postRaw.isNotEmpty + // ? List.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 _loadToken() async { - final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]" - final postRaw = await tokenService.readValue('empAllowed_modules'); // "[2,3,4]" + setState(() { + isLoading = true; // 🔥 START LOADER HERE + }); - // ✅ Decode safely - enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty - ? List.from(jsonDecode(enrollmentRaw)) - : []; + try { + final enrollmentRaw = + await tokenService.readValue('enrollmentAllowed_modules'); + final postRaw = + await tokenService.readValue('empAllowed_modules'); - postModules = postRaw != null && postRaw.isNotEmpty - ? List.from(jsonDecode(postRaw)) - : []; + enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty + ? List.from(jsonDecode(enrollmentRaw)) + : []; - print('enrollmentModules $enrollmentModules'); - print('postModules $postModules'); + postModules = postRaw != null && postRaw.isNotEmpty + ? List.from(jsonDecode(postRaw)) + : []; - _postPreToken = await tokenService.getCurrentToken(); - print(_postPreToken); + _postPreToken = await tokenService.getCurrentToken(); - if (enrollmentModules.contains(1)) { - enrollmentClient_id = await tokenService.readValue('enrollmentClient_id'); - enrollmentEmpClientBranchId = - await tokenService.readValue('enrollmentEmpClientBranchId'); - enrollmentHrId = await tokenService.readValue('enrollmentHrId'); + List apiCalls = []; - await getPreCashDepositDetails(enrollmentEmpClientBranchId, - enrollmentClient_id, enrollmentHrId, _postPreToken); + /// 👇 Add APIs dynamically + if (enrollmentModules.contains(1)) { + enrollmentClient_id = + await tokenService.readValue('enrollmentClient_id'); + enrollmentEmpClientBranchId = + await tokenService.readValue('enrollmentEmpClientBranchId'); + enrollmentHrId = + await tokenService.readValue('enrollmentHrId'); + + apiCalls.add( + 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'); + + 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 + }); + } } - - 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 getPreCashDepositDetails(enrollmentEmpClientBranchId, @@ -153,7 +224,6 @@ class _policiesState extends State print("hr_id -$enrollmentHrId"); print("token -$_postPreToken"); - isLoading = true; // setState(() { // _isLoading = true; // }); @@ -171,7 +241,6 @@ class _policiesState extends State // clintID!, clintBranchId!, hr_id, token); print('IN1'); if (response['status'] == 'success') { - isLoading = false; setState(() { print('response'); print(response['data']); @@ -198,7 +267,6 @@ class _policiesState extends State print("hr_id -$empHrId"); print("token -$_postPreToken"); - isLoading = true; // setState(() { // _isLoading = true; // }); @@ -213,7 +281,6 @@ class _policiesState extends State // clintID!, clintBranchId!, hr_id, token); print('IN1'); if (response['status'] == 'success') { - isLoading = false; setState(() { print('response'); print(response['data']); @@ -226,7 +293,6 @@ class _policiesState extends State print('IN2'); } else { - isLoading = false; print('API request failed with status'); setState(() { activePoliciesList = []; @@ -345,7 +411,16 @@ class _policiesState extends State required List> openEnrollment, required List> activePolicies, }) { - return Scaffold( + return isLoading ? Container( + color: Colors.transparent, // Semi-transparent background + child: Center( + child: // Your GIF loader widget + Image.asset( + height: 60, + width: 60, + 'assets/nhance-loader.gif'), // Adjust path to your GIF loader + ), + ) : Scaffold( body: SingleChildScrollView( // padding: const EdgeInsets.all(20), @@ -385,39 +460,45 @@ class _policiesState extends State SizedBox(height: 15), Container( width: double.infinity, - height: 400, + // constraints: const BoxConstraints( + // minHeight: 180, + // ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(6), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Open for Enrollment', - style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600), - ), - SizedBox(height: 14), + child: IntrinsicHeight( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Open for Enrollment', + style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600), + ), + SizedBox(height: 14), + + /// ✅ SCROLLABLE AREA + openEnrollment.isEmpty + ? _EmptyBox('No policies open for enrollment') + : _PolicyGrid( + policies: openEnrollment, + isEnrollment: true, + ), + + ], + ), + ) - /// ✅ SCROLLABLE AREA - Expanded( - child: openEnrollment.isEmpty - ? _EmptyBox('No policies open for enrollment') - : _PolicyGrid( - policies: openEnrollment, - isEnrollment: true, - ), - ), - ], - ), ), ], if(postModules.contains(2))...[ SizedBox(height: 20), Container( width: double.infinity, - height: 400, + // constraints: const BoxConstraints( + // minHeight: 180, + // ), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, @@ -454,15 +535,14 @@ class _policiesState extends State const SizedBox(height: 14), /// ✅ SCROLLABLE GRID - Expanded( - child: activePolicies.isEmpty + activePolicies.isEmpty ? stausVal == 0 ? _EmptyBox('You don’t have any expired policies at the moment.') : _EmptyBox('No active policies found') : _PolicyGrid( policies: activePolicies, isEnrollment: false, onBulkDownload: getEcardBulkDownload, ), - ), + const SizedBox(height: 8), Align( @@ -513,123 +593,158 @@ class _PolicyGrid extends StatelessWidget { ) { final width = MediaQuery.of(context).size.width; - if (width < 600) { - return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15); - } else if (width < 900) { - return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45); - } else if (width < 1400) { - return ResponsiveGridConfig(3, isEnrollment ? 3.1 : 2.5); + // if (width < 600) { + // return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15); + // } else if (width < 900) { + // return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45); + // } else if (width < 1400) { + // return ResponsiveGridConfig(3, isEnrollment ? 3.1 : 2.5); + // } else { + // return ResponsiveGridConfig(4, isEnrollment ? 3.1 : 2.4); + // } + + + + if (width >= 1400) { + return const ResponsiveGridConfig(4, 2.6); // Big screen + } else if (width >= 1000) { + return const ResponsiveGridConfig(3, 2.3); // Small desktop + } else if (width >= 600) { + return const ResponsiveGridConfig(2, 2.0); // Tablet } else { - return ResponsiveGridConfig(4, isEnrollment ? 3.1 : 2.4); + return const ResponsiveGridConfig(1, 1.8); // Mobile } } + @override @override Widget build(BuildContext context) { final tokenService = TokenStorageService(); final config = _getGridConfig(context, isEnrollment); - return GridView.builder( - physics: const BouncingScrollPhysics(), - padding: EdgeInsets.zero, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: config.crossAxisCount, - childAspectRatio: config.childAspectRatio, - crossAxisSpacing: 16, - mainAxisSpacing: 16, + final int rowCount = (policies.length / config.crossAxisCount).ceil(); + + double cardHeight; + + if (isEnrollment) { + cardHeight = 140; + } else { + cardHeight = 170; + } + + final double totalHeight = + rowCount * cardHeight + ((rowCount - 1) * 16); + + + return SizedBox( + height: totalHeight, + child: GridView.builder( + physics: const BouncingScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsets.zero, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: config.crossAxisCount, + childAspectRatio: config.childAspectRatio, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + mainAxisExtent: isEnrollment ? 120 : 150, // fixed card height + ), + itemCount: policies.length, + itemBuilder: (context, index) { + final data = policies[index]; + + return isEnrollment + ? _EnrollmentPolicyCardNew( + data: data, + onTap: () async { + final token = await tokenService.getCurrentToken(); + final clientId = + await tokenService.readValue('enrollmentClient_id'); + final branchId = await tokenService + .readValue('enrollmentEmpClientBranchId'); + + if (token == null || clientId == null || branchId == null) { + return; + } + + Navigator.push( + context, + MaterialPageRoute( + settings: const RouteSettings(name: 'hrPolicyDetails'), + builder: (_) => hrPolicyDetails( + ClientId: clientId, + policyTypeId: + data['policy_type_id'].toString(), + ClientPoliyId: + data['client_policy_id'].toString(), + clientBranchId: branchId, + Token: token, + TokenType: "pre", + cardType: data['type'].toString(), + cardPolicyNo: data['policy_no'].toString(), + cardInsurer_name: + data['insurer_short_name'].toString(), + cardPolicy_name: + data['policy_name'].toString(), + cardPolicy_ExpDate: + data['policy_expiry_date'].toString(), + total_premium: '', + is_ecard_bulk_download_for_employee: 0, + ), + ), + ); + }, + ) + : _ActivePolicyCardNew( + data: data, + onBulkDownload: onBulkDownload, + onTap: () async { + final token = await tokenService.getCurrentToken(); + final clientId = await tokenService.readValue('empClientId'); + final branchId = await tokenService.readValue('empClientBranchId'); + + print("token: $token"); + print("clientId: $clientId"); + print("branchId: $branchId"); + + if (token == null || clientId == null || branchId == null) { + print("Missing required values"); + return; + } + + Navigator.push( + context, + MaterialPageRoute( + settings: const RouteSettings(name: 'hrPolicyDetails'), + builder: (_) => hrPolicyDetails( + ClientId: clientId, + policyTypeId: + data['policy_type_id'].toString(), + ClientPoliyId: + data['client_policy_id'].toString(), + clientBranchId: branchId, + Token: token, + TokenType: 'post', + cardType: data['type'].toString(), + cardPolicyNo: data['policy_no'].toString(), + cardInsurer_name: + data['insurer_short_name'].toString(), + cardPolicy_name: + data['policy_name'].toString(), + cardPolicy_ExpDate: + data['policy_expiry_date'].toString(), + total_premium: + data['total_premium'].toString(), + is_ecard_bulk_download_for_employee: + data['is_ecard_bulk_download_for_employee'], + ), + ), + ); + }, + ); + }, ), - itemCount: policies.length, - itemBuilder: (context, index) { - final data = policies[index]; - - return isEnrollment - ? _EnrollmentPolicyCardNew( - data: data, - onTap: () async { - final token = await tokenService.getCurrentToken(); - final clientId = - await tokenService.readValue('enrollmentClient_id'); - final branchId = await tokenService - .readValue('enrollmentEmpClientBranchId'); - - if (token == null || clientId == null || branchId == null) { - return; - } - - Navigator.push( - context, - MaterialPageRoute( - settings: const RouteSettings(name: 'hrPolicyDetails'), - builder: (_) => hrPolicyDetails( - ClientId: clientId, - policyTypeId: - data['policy_type_id'].toString(), - ClientPoliyId: - data['client_policy_id'].toString(), - clientBranchId: branchId, - Token: token, - TokenType: "pre", - cardType: data['type'].toString(), - cardPolicyNo: data['policy_no'].toString(), - cardInsurer_name: - data['insurer_short_name'].toString(), - cardPolicy_name: - data['policy_name'].toString(), - cardPolicy_ExpDate: - data['policy_expiry_date'].toString(), - total_premium: '', - is_ecard_bulk_download_for_employee: 0, - ), - ), - ); - }, - ) - : _ActivePolicyCardNew( - data: data, - onBulkDownload: onBulkDownload, - onTap: () async { - final token = await tokenService.getCurrentToken(); - final clientId = - await tokenService.readValue('empClientId'); - final branchId = - await tokenService.readValue('empClientBranchId'); - - if (token == null || clientId == null || branchId == null) { - return; - } - - Navigator.push( - context, - MaterialPageRoute( - settings: const RouteSettings(name: 'hrPolicyDetails'), - builder: (_) => hrPolicyDetails( - ClientId: clientId, - policyTypeId: - data['policy_type_id'].toString(), - ClientPoliyId: - data['client_policy_id'].toString(), - clientBranchId: branchId, - Token: token, - TokenType: 'post', - cardType: data['type'].toString(), - cardPolicyNo: data['policy_no'].toString(), - cardInsurer_name: - data['insurer_short_name'].toString(), - cardPolicy_name: - data['policy_name'].toString(), - cardPolicy_ExpDate: - data['policy_expiry_date'].toString(), - total_premium: - data['total_premium'].toString(), - is_ecard_bulk_download_for_employee: - data['is_ecard_bulk_download_for_employee'], - ), - ), - ); - }, - ); - }, ); } } diff --git a/lib/presentation/postFileUpload.dart b/lib/presentation/postFileUpload.dart index dfcf815..c58cb74 100755 --- a/lib/presentation/postFileUpload.dart +++ b/lib/presentation/postFileUpload.dart @@ -51,8 +51,7 @@ class postFileUpload extends StatefulWidget { required this.cardInsurer_name, required this.cardPolicy_name, required this.cardPolicy_ExpDate, - required this.total_premium - }) + required this.total_premium}) : super(key: key); @override @@ -61,6 +60,23 @@ class postFileUpload extends StatefulWidget { class _postFileUploadState extends State { final tokenService = TokenStorageService(); + + + String localClientId = ''; + String localPolicyTypeId = ''; + String localClientPolicyId = ''; + String localClientBranchId = ''; + String localToken = ''; + String localTokenType = ''; + String localCardType = ''; + String localCardPolicyNo = ''; + String localCardInsurerName = ''; + String localCardPolicyName = ''; + String localCardPolicyExpDate = ''; + String localTotalPremium = ''; + + + Uint8List? fileBytes; Uint8List? fileBytes2; late String _token; @@ -101,9 +117,8 @@ class _postFileUploadState extends State { bool showSampleButton = false; String? currentApiValue; // To store the 'value' for the 2nd param - int _currentPage = 1; - int _rowsPerPage = 5; + int _rowsPerPage = 6; List get _paginatedData { final startIndex = (_currentPage - 1) * _rowsPerPage; @@ -125,9 +140,12 @@ class _postFileUploadState extends State { void initState() { super.initState(); apiService = ApiService(context); - _loadToken(); - getFileUploadMasterDetails(); - getFileListDetails(); + restoreUploadData().then((_) { + _loadToken(); + getFileUploadMasterDetails(); + getFileListDetails(); + }); + } @override @@ -137,22 +155,85 @@ class _postFileUploadState extends State { } Future _loadToken() async { - // final token = prefs.getString('hrtoken'); - final token = widget.Token; + final token = localToken.isNotEmpty + ? localToken + : await tokenService.readValue('upload_Token'); + if (token != null && token.isNotEmpty) { setState(() { _token = token; }); - Map? decodedToken = Jwt.parseJwt(token); - print('decodedToken $decodedToken'); } else { - // Token is empty or null, handle accordingly (e.g., navigate to login screen) - // For now, let's navigate to the login screen ToastHelper.showErrorToast(context, 'Session Out'); Navigator.pushReplacementNamed(context, 'hrLogin'); } } + Future 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 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 getPolicyDetails() async { // setState(() { // clientPolicyId = argumentsData['client_policy_id']; @@ -184,7 +265,7 @@ class _postFileUploadState extends State { Future getFileUploadMasterDetails() async { print('9'); try { - final response = await apiService.getFileUploadMastersToApi(widget.Token); + final response = await apiService.getFileUploadMastersToApi(localToken); if (response['status'] == true) { print('getFileUploadMasterList1'); @@ -220,8 +301,8 @@ class _postFileUploadState extends State { print('9'); try { - final response = await apiService.getFileListToApi( - empPrimaryId, widget.cardPolicyNo, empClientId,widget.Token,widget.TokenType); + final response = await apiService.getFileListToApi(empPrimaryId, + localCardPolicyNo, empClientId, localToken, localTokenType); if (response['status'] == 'success') { print('getThrFileList'); @@ -248,18 +329,20 @@ class _postFileUploadState extends State { } Future getHrFileDownload(id, file_name) async { - // final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token); + // final http.Response response = await apiService.getHrFileDownloadToApi(id, localToken); print("**********-------*****"); - final encryptClientId = widget.ClientId; + final encryptClientId = localClientId; print(encryptClientId); final apiurl = Environment.apiUrlPost; - final String url = '$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId'; - final token = widget.Token; + final String url = + '$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId'; + final token = localToken; final response = await http.get( Uri.parse(url), headers: { - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + 'APP-SIGNATURE': + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', // 'app-signature': 'ts-traveltool-2025-signature-123456', @@ -297,16 +380,17 @@ class _postFileUploadState extends State { Future downloadPostSampleFile(String apiParam) async { print("fun Sam f - in"); - final post_file_name = apiParam+'_sample_file.xlsx'; - print("fun Sam f - name $post_file_name" ); + final post_file_name = apiParam + '_sample_file.xlsx'; + print("fun Sam f - name $post_file_name"); final apiurl = Environment.apiUrlPost; final String url = '$apiurl/downloadSampleExcel/$apiParam'; - final token = widget.Token; + final token = localToken; final response = await http.get( Uri.parse(url), headers: { - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + 'APP-SIGNATURE': + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', // 'app-signature': 'ts-traveltool-2025-signature-123456', @@ -315,7 +399,7 @@ class _postFileUploadState extends State { if (response.statusCode == 200) { try { - print("fun sam f - ${response.statusCode}" ); + print("fun sam f - ${response.statusCode}"); // ✅ Create a blob from the response body bytes final blob = html.Blob([response.bodyBytes]); @@ -333,7 +417,7 @@ class _postFileUploadState extends State { ToastHelper.showSuccessToast(context, 'File Downloaded Successfully'); } catch (e) { - print("fun sam f - fail" ); + print("fun sam f - fail"); throw Exception('Error parsing response: $e'); } } else { @@ -475,7 +559,8 @@ class _postFileUploadState extends State { print('else'); // Attach the file to the request // Set authorization token in headers - request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; + request.headers['APP-SIGNATURE'] = + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; request.headers['Authorization'] = 'Bearer $_token'; // request.files.add(http.MultipartFile.fromBytes('file', fileBytes, // filename: fileName)); @@ -492,13 +577,13 @@ class _postFileUploadState extends State { )); print('clintID: $clintID'); - request.fields['client_id'] = widget.ClientId; - request.fields['policy_no'] = widget.cardPolicyNo; - request.fields['client_branch_id'] = widget.clientBranchId; + request.fields['client_id'] = localClientId; + request.fields['policy_no'] = localCardPolicyNo; + request.fields['client_branch_id'] = localClientBranchId; request.fields['file_action'] = selectedKey!; // request.fields['status'] = selectedKey!; request.fields['created_by'] = empPrimaryId; - request.fields['policy_id'] = widget.ClientPoliyId; + request.fields['policy_id'] = localClientBranchId; // "client_id": 1, // "client_branch_id": 2, // "policy_no": "POL123456", @@ -520,15 +605,15 @@ class _postFileUploadState extends State { isLoading = false; }); ToastHelper.showSuccessToast(context, data['message']); - getFileListDetails(); setState(() { selectedValue = null; selectedKey = null; resetErrorCount(); + getFileListDetails(); }); } else { - getFileListDetails(); setState(() { + getFileListDetails(); isLoading = false; selectedValue = null; selectedKey = null; @@ -587,217 +672,267 @@ class _postFileUploadState extends State { } Widget _buildContent(BuildContext context) { - 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 - ), - ): Container( - child: Column( - children: [ - Row( - children: [ - IconButton( - tooltip: 'Previous Page', - onPressed: () => {Navigator.pop(context)}, - icon: const Icon( - Icons.arrow_back_ios, - size: 18, - color: Colors.black, - ), - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - ), - const SizedBox(width: 6), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.start, + 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 + ), + ) + : Container( + child: Column( + children: [ + Row( children: [ - Text( - "${widget.cardType} - ${widget.cardPolicyNo} " ?? - '', - style: GoogleFonts.poppins( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - Text( - widget.TokenType == 'pre' - ? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})" - : "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})", - style: GoogleFonts.poppins(color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w400), - ), - ], - ), - ), - // Visibility toggles based on dropdown selection - Visibility( - visible: showSampleButton, - child: Padding( - padding: const EdgeInsets.only(left: 10), - child: SizedBox( - child: ElevatedButton( - onPressed: () { - // Pass the dynamic value to the function - downloadPostSampleFile(currentApiValue ?? ''); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFE26728), - elevation: 0, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - ), - child: Text( - 'Sample Excel', - style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white), - ), - ), - ), - ), - ), - ], - ), - SizedBox(height:20), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - /// Select File Action - Expanded( - flex: 5, - child: buildStyledDropdown( - label: 'Select File Action', - value: selectedKey, - items: getFileUploadMasterList, - onChanged: (val) { - setState(() { - selectedKey = val; - final selectedItem = getFileUploadMasterList.firstWhere((e) => e['key'] == val); - selectedValue = selectedItem['value']; - currentApiValue = selectedItem['key']; - showSampleButton = true; - }); - }, - ), - ), + IconButton( + tooltip: 'Previous Page', + onPressed: () async { + if (Navigator.canPop(context)) { + Navigator.pop(context); + return; + } - const SizedBox(width: 16), + 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'; - /// Upload Box - Expanded( - flex: 5, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - /// ✅ LABEL - RichText( - text: TextSpan( - text: 'Upload File', - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - children: const [ - TextSpan( - text: '(Supported Formats: XLSX)', - style: TextStyle( - fontSize: 11, - color: Colors.grey, - fontWeight: FontWeight.w400, + Navigator.pushReplacement( + context, + MaterialPageRoute( + settings: const RouteSettings(name: 'hrPolicyDetails'), + builder: (_) => hrPolicyDetails( + ClientId: clientId, + policyTypeId: policyTypeId, + ClientPoliyId: clientPolicyId, + clientBranchId: clientBranchId, + Token: token, + TokenType: tokenType, + cardType: cardType, + cardPolicyNo: policyNo, + cardInsurer_name: insurer, + cardPolicy_name: policyName, + cardPolicy_ExpDate: expDate, + total_premium: totalPremium, + is_ecard_bulk_download_for_employee: int.tryParse(bulkDownload) ?? 0, ), ), + ); + }, + icon: const Icon( + Icons.arrow_back_ios, + size: 18, + color: Colors.black, + ), + ), + const SizedBox(width: 6), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "${localCardType} - ${localCardPolicyNo} " ?? + '', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + Text( + localTokenType == 'pre' + ? "${localCardPolicyName} (${localCardPolicyExpDate})" + : "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 12, + fontWeight: FontWeight.w400), + ), ], ), ), - - const SizedBox(height: 6), - - /// ✅ DOTTED UPLOAD BOX - DragTarget( - onAccept: (html.File droppedFile) { - setState(() { - fileName = droppedFile.name; - }); - _dragAndDropFile(droppedFile); - }, - builder: (context, candidateData, rejectedData) { - return GestureDetector( - onTap: () { - if (selectedValue != null) { - _uploadFile(); - } else { - ToastHelper.showErrorToast( - context, - 'Please select file action', - ); - } + // Visibility toggles based on dropdown selection + Visibility( + visible: showSampleButton, + child: Padding( + padding: const EdgeInsets.only(left: 10), + child: SizedBox( + child: ElevatedButton( + onPressed: () { + // Pass the dynamic value to the function + downloadPostSampleFile(currentApiValue ?? ''); }, - child: Container( - height: 40 , - padding: const EdgeInsets.symmetric(horizontal: 12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: const Color(0xFF00A6A6), - width: 1, - ), - ), - child: Row( - children: [ - Expanded( - child: Text( - fileName ?? 'Upload Your Documents', - overflow: TextOverflow.ellipsis, - style: GoogleFonts.poppins( - fontSize: 13, - color: fileName == null - ? Colors.grey - : Colors.black, - ), - ), - ), - const Icon( - Icons.file_upload_outlined, - size: 18, - color: Colors.black, - ), - ], - ), - ) - ); - }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26728), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + ), + child: Text( + 'Sample Excel', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w700, + color: Colors.white), + ), + ), + ), + ), ), ], ), - ), - ], - ), - SizedBox(height: 20), - Row( - children: [ - Expanded( - child: Column( - children: [ - _buildFileUploadedGrid(), - const SizedBox(height: 16), - _buildPagination(context), - ], - ), - ) + SizedBox(height: 20), + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// Select File Action + Expanded( + flex: 5, + child: buildStyledDropdown( + label: 'Select File Action', + value: selectedKey, + items: getFileUploadMasterList, + onChanged: (val) { + setState(() { + selectedKey = val; + final selectedItem = getFileUploadMasterList + .firstWhere((e) => e['key'] == val); + selectedValue = selectedItem['value']; + currentApiValue = selectedItem['key']; + showSampleButton = true; + }); + }, + ), + ), - ], - ), - ], - ), - ); + const SizedBox(width: 16), + /// Upload Box + Expanded( + flex: 5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// ✅ LABEL + RichText( + text: TextSpan( + text: 'Upload File', + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + children: const [ + TextSpan( + text: '(Supported Formats: XLSX)', + style: TextStyle( + fontSize: 11, + color: Colors.grey, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + const SizedBox(height: 6), + + /// ✅ DOTTED UPLOAD BOX + DragTarget( + onAccept: (html.File droppedFile) { + setState(() { + fileName = droppedFile.name; + }); + _dragAndDropFile(droppedFile); + }, + builder: + (context, candidateData, rejectedData) { + return GestureDetector( + onTap: () { + if (selectedValue != null) { + _uploadFile(); + } else { + ToastHelper.showErrorToast( + context, + 'Please select file action', + ); + } + }, + child: Container( + height: 40, + padding: const EdgeInsets.symmetric( + horizontal: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius.circular(8), + border: Border.all( + color: const Color(0xFF00A6A6), + width: 1, + ), + ), + child: Row( + children: [ + Expanded( + child: Text( + fileName ?? + 'Upload Your Documents', + overflow: + TextOverflow.ellipsis, + style: GoogleFonts.poppins( + fontSize: 13, + color: fileName == null + ? Colors.grey + : Colors.black, + ), + ), + ), + const Icon( + Icons.file_upload_outlined, + size: 18, + color: Colors.black, + ), + ], + ), + )); + }, + ), + ], + ), + ), + ], + ), + SizedBox(height: 20), + Column( + children: [ + _buildFileUploadedGrid(), + const SizedBox(height: 16), + _buildPagination(context), + ], + ), + ]))) + ], + ), + ); } Widget buildUploadBox({ @@ -852,8 +987,6 @@ class _postFileUploadState extends State { ); } - - Widget buildStyledDropdown({ required String label, required String? value, @@ -906,7 +1039,6 @@ class _postFileUploadState extends State { ); } - Widget _buildFileUploadedGrid() { if (filteredData.isEmpty) { return const SizedBox( @@ -916,20 +1048,23 @@ class _postFileUploadState extends State { } return GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, // ✅ IMPORTANT + physics: const NeverScrollableScrollPhysics(), // ✅ Disable inner scroll + padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, // 👈 2 cards per row - crossAxisSpacing: 16, - mainAxisSpacing: 16, - childAspectRatio: 10, // 👈 card height - ), - itemCount: _paginatedData.length, - itemBuilder: (context, index) { - final item = _paginatedData[index]; - return _buildFileCard(item); - }, - ); + crossAxisCount: 2, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + childAspectRatio: 10, + ), + itemCount: _paginatedData.length, + itemBuilder: (context, index) { + final item = _paginatedData[index]; + return _buildFileCard(item); + }, + ); + + } Widget _buildFileCard(Map item) { @@ -1014,9 +1149,7 @@ class _postFileUploadState extends State { children: [ /// 🔴 Error + Status Row( - children: [ - - ], + children: [], ), const SizedBox(height: 8), @@ -1024,58 +1157,60 @@ class _postFileUploadState extends State { Row( children: [ if (item['file_error_status'] == '1') - InkWell( - onTap: () async { - print(item); - // return; - final String? token = await tokenService.getCurrentToken(); - final String? empClientId = await tokenService.readValue('empClientId'); - final String? empBranchId = await tokenService.readValue('empClientBranchId'); + InkWell( + onTap: () async { + print(item); + // return; + final String? token = + await tokenService.getCurrentToken(); + final String? empClientId = + await tokenService.readValue('empClientId'); + final String? empBranchId = + await tokenService.readValue('empClientBranchId'); - print(item); - print(empClientId); - print(widget.policyTypeId); - print(empBranchId); - print(token); - print('post'); - print(widget.cardType); - print(widget.cardPolicyNo); - print(widget.cardInsurer_name); - print(widget.cardPolicy_name); - print(widget.cardPolicy_ExpDate); - print(item['id']); + print(item); + print(empClientId); + print(localPolicyTypeId); + print(empBranchId); + print(token); + print('post'); + print(localCardType); + print(localCardPolicyNo); + print(localCardInsurerName); + print(localCardPolicyName); + print(localCardPolicyExpDate); + print(item['id']); - // ✅ SAFETY CHECK - if (token == null || - empClientId == null || - empBranchId == null) { - debugPrint('❌ Missing required data for navigation ${token}'); - return; - } + // ✅ SAFETY CHECK + if (token == null || + empClientId == null || + empBranchId == null) { + debugPrint( + '❌ Missing required data for navigation ${token}'); + return; + } - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - excelErrorScreen( - ClientId: empClientId, - policy_no: item['policy_no'], - action: item['file_action'], - created_at: item['created_at'], - clientBranchId: empBranchId, - Token: token, - TokenType: 'post', - id: item['id'] - ), - ), - ); - }, - child: Icon( - Icons.error, - size: 16, - color: Colors.red, + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => excelErrorScreen( + ClientId: empClientId, + policy_no: item['policy_no'], + action: item['file_action'], + created_at: item['created_at'], + clientBranchId: empBranchId, + Token: token, + TokenType: 'post', + id: item['id']), + ), + ); + }, + child: Icon( + Icons.error, + size: 16, + color: Colors.red, + ), ), - ), SizedBox(width: 10), _buildStatusChip(item['status']), SizedBox(width: 10), @@ -1100,8 +1235,8 @@ class _postFileUploadState extends State { ), ], ), - /// ⬇ Download + /// ⬇ Download ], ), ], @@ -1109,7 +1244,6 @@ class _postFileUploadState extends State { ); } - Widget _buildStatusChip(String status) { final s = status.toLowerCase(); @@ -1142,7 +1276,6 @@ class _postFileUploadState extends State { ); } - static final _dataBold = GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, @@ -1161,14 +1294,14 @@ class _postFileUploadState extends State { ); Widget _buildPagination(BuildContext context) { - final totalItems = filteredData.length; - final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + final int startEntry = + totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; int endEntry = _currentPage * _rowsPerPage; if (endEntry > totalItems) endEntry = totalItems; final totalPages = (filteredData.length / _rowsPerPage).ceil(); - const visiblePageCount = 5; + const visiblePageCount = 6; List getVisiblePages() { if (totalPages <= visiblePageCount) { @@ -1177,7 +1310,8 @@ class _postFileUploadState extends State { if (_currentPage <= 3) { return [1, 2, 3, 4, 5]; - } if (_currentPage >= totalPages - 2) { + } + if (_currentPage >= totalPages - 2) { return [ totalPages - 4, totalPages - 3, @@ -1186,14 +1320,13 @@ class _postFileUploadState extends State { totalPages ]; } - return [ - _currentPage - 2, - _currentPage - 1, - _currentPage, - _currentPage + 1, - _currentPage + 2, - ]; - + return [ + _currentPage - 2, + _currentPage - 1, + _currentPage, + _currentPage + 1, + _currentPage + 2, + ]; } List visiblePages = getVisiblePages(); @@ -1208,7 +1341,7 @@ class _postFileUploadState extends State { // Dropdown for rows per page DropdownButton( value: _rowsPerPage, - items: [5, 10, 15, 20, 50].map((int value) { + items: [6, 10, 15, 20, 50].map((int value) { return DropdownMenuItem( value: value, child: Text(' $value ', diff --git a/lib/presentation/preFileUpload.dart b/lib/presentation/preFileUpload.dart index 665eaf1..c9cf4d7 100755 --- a/lib/presentation/preFileUpload.dart +++ b/lib/presentation/preFileUpload.dart @@ -11,13 +11,14 @@ import 'package:nhancepolicy/service/token_storage_service.dart'; import 'package:universal_html/html.dart' as html; import 'package:flutter/foundation.dart' show kIsWeb; // 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 'package:intl/intl.dart'; import 'package:csv/csv.dart'; import '../config/environment.dart'; import '../customAppBar/base_layout.dart'; import 'excelVerification.dart'; +import 'hrPolicyDetails.dart'; class preFileUpload extends StatefulWidget { final String ClientId; @@ -32,23 +33,21 @@ class preFileUpload extends StatefulWidget { final String cardPolicy_name; final String cardPolicy_ExpDate; final String total_premium; - const preFileUpload( - {Key? key, - required this.ClientId, - required this.policyTypeId, - required this.ClientPoliyId, - required this.clientBranchId, - required this.Token, - required this.TokenType, - required this.cardType, - required this.cardPolicyNo, - required this.cardInsurer_name, - required this.cardPolicy_name, - required this.cardPolicy_ExpDate, - required this.total_premium, - - }) - : super(key: key); + const preFileUpload({ + Key? key, + required this.ClientId, + required this.policyTypeId, + required this.ClientPoliyId, + required this.clientBranchId, + required this.Token, + required this.TokenType, + required this.cardType, + required this.cardPolicyNo, + required this.cardInsurer_name, + required this.cardPolicy_name, + required this.cardPolicy_ExpDate, + required this.total_premium, + }) : super(key: key); @override State createState() => _excelVerifyState(); @@ -56,6 +55,20 @@ class preFileUpload extends StatefulWidget { class _excelVerifyState extends State { final tokenService = TokenStorageService(); + + String localClientId = ''; + String localPolicyTypeId = ''; + String localClientPolicyId = ''; + String localClientBranchId = ''; + String localToken = ''; + String localTokenType = ''; + String localCardType = ''; + String localCardPolicyNo = ''; + String localCardInsurerName = ''; + String localCardPolicyName = ''; + String localCardPolicyExpDate = ''; + String localTotalPremium = ''; + Uint8List? fileBytes; Uint8List? fileBytes2; late String _token; @@ -79,8 +92,8 @@ class _excelVerifyState extends State { dynamic invalidRelationships = 0; dynamic dobAgeCheckCount = 0; dynamic empRefId; - List excelHeader = []; - List>> excelData = []; + List excelHeader = []; + List>> excelData = []; late int excelValidationStaus = 1; bool isSuccess = false; String successContent = ''; @@ -92,12 +105,12 @@ class _excelVerifyState extends State { final List _allowedExtensions = ['xlsx', 'xls']; int _currentPage = 1; - int _rowsPerPage = 5; + int _rowsPerPage = 6; List get _paginatedData { final startIndex = (_currentPage - 1) * _rowsPerPage; final endIndex = - (_currentPage * _rowsPerPage).clamp(0, filteredData.length); + (_currentPage * _rowsPerPage).clamp(0, filteredData.length); return filteredData.sublist(startIndex, endIndex); } @@ -105,8 +118,10 @@ class _excelVerifyState extends State { void initState() { super.initState(); apiService = ApiService(context); - getFileListDetails(); - _loadToken(); + restoreUploadData().then((_) { + _loadToken(); + getFileListDetails(); + }); } @override @@ -116,21 +131,85 @@ class _excelVerifyState extends State { } Future _loadToken() async { - final token = widget.Token; + final token = localToken.isNotEmpty + ? localToken + : await tokenService.readValue('upload_Token'); + if (token != null && token.isNotEmpty) { setState(() { _token = token; }); - Map? decodedToken = Jwt.parseJwt(token); - print('decodedToken $decodedToken'); } else { - // Token is empty or null, handle accordingly (e.g., navigate to login screen) - // For now, let's navigate to the login screen ToastHelper.showErrorToast(context, 'Session Out'); Navigator.pushReplacementNamed(context, 'hrLogin'); } } + Future 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 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 getPolicyDetails() async { // setState(() { // clientPolicyId = argumentsData['client_policy_id']; @@ -159,7 +238,7 @@ class _excelVerifyState extends State { // } // } - void _uploadFile(importPolicyName) async { + void _uploadFile(importPolicyName) async { print('Test'); if (kIsWeb) { print('kIsWeb'); @@ -210,15 +289,14 @@ class _excelVerifyState extends State { } bool _validateDatesBeforeUpload() { - if (openDateController.text.isEmpty || - closeDateController.text.isEmpty) { - ToastHelper.showErrorToast2(context,'','Please select both Enrolment Open Date and Close Date'); + if (openDateController.text.isEmpty || closeDateController.text.isEmpty) { + ToastHelper.showErrorToast2( + context, '', 'Please select both Enrolment Open Date and Close Date'); return false; } return true; } - void _processExcelData(Uint8List fileBytes, fileName) { List> dataArray; if (fileName.endsWith('.xlsx')) { @@ -461,7 +539,6 @@ class _excelVerifyState extends State { } Future sendExcelFIleTOAPI(Uint8List fileBytes, fileName) async { - // Future.delayed(Duration(seconds: 3), () { // setState(() { isLoading = true; @@ -486,7 +563,8 @@ class _excelVerifyState extends State { print('else'); // Attach the file to the request // Set authorization token in headers - request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; + request.headers['APP-SIGNATURE'] = + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; request.headers['Authorization'] = 'Bearer $_token'; // request.files.add(http.MultipartFile.fromBytes('file', fileBytes, // filename: fileName)); @@ -497,10 +575,10 @@ class _excelVerifyState extends State { filename: fileName ?? 'default_filename.xlsx', )); - request.fields['client_id'] = widget.ClientId; + request.fields['client_id'] = localClientId; // if (policyFirstPart == 'GPA') { - request.fields['policy_id'] = widget.ClientPoliyId; - request.fields['client_branch_id'] = widget.clientBranchId; + request.fields['policy_id'] = localClientPolicyId; + request.fields['client_branch_id'] = localClientBranchId; request.fields['enrollment_open_date'] = openDateController.text; request.fields['enrollment_close_date'] = closeDateController.text; request.fields['created_by'] = enrollmentHrId!; @@ -527,18 +605,21 @@ class _excelVerifyState extends State { isSuccess = true; successContent = data['message']; excelValidationStaus = 0; + resetErrorCount(); + handleImportAction(); + getFileListDetails(); }); - resetErrorCount(); - handleImportAction(); - getFileListDetails(); } else { setState(() { isLoading = false; + handleImportAction(); + }); + + ToastHelper.showErrorToast2(context, "", data['message']); + setState(() { + resetErrorCount(); + getFileListDetails(); }); - handleImportAction(); - ToastHelper.showErrorToast2(context,"",data['message']); - resetErrorCount(); - getFileListDetails(); // ToastHelper.showErrorToast(context, data['message']); print('Table'); } @@ -570,7 +651,7 @@ class _excelVerifyState extends State { print('10'); response = await apiService.getImportLogHrActivity( - postId!, preId!, widget.Token, activity); + postId!, preId!, localToken, activity); if (response['status'] == 'success') { print('Request success'); @@ -585,13 +666,15 @@ class _excelVerifyState extends State { } Future getFileListDetails() async { - final enrollmentPrimaryId = await tokenService.readValue('enrollmentEmpPrimaryId'); - final enrollmentClientId = await tokenService.readValue('enrollmentClient_id'); + final enrollmentPrimaryId = + await tokenService.readValue('enrollmentEmpPrimaryId'); + final enrollmentClientId = + await tokenService.readValue('enrollmentClient_id'); print('9'); try { - final response = await apiService.getFileListToApi( - enrollmentPrimaryId, widget.cardPolicyNo, enrollmentClientId,widget.Token,widget.TokenType); + final response = await apiService.getFileListToApi(enrollmentPrimaryId, + localCardPolicyNo, enrollmentClientId, localToken, localTokenType); if (response['status'] == true) { print('getThrFileList'); @@ -640,7 +723,7 @@ class _excelVerifyState extends State { // Future downloadSampleFile() async { // - // final response = await apiService.getSampleFileDownload(widget.Token); + // final response = await apiService.getSampleFileDownload(localToken); // print('check 1'); // if (response['status'] == 'success') { // final url = response['data']; @@ -669,15 +752,16 @@ class _excelVerifyState extends State { } Future getHrFileDownload(id, file_name) async { - // final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token); + // final http.Response response = await apiService.getHrFileDownloadToApi(id, localToken); final apiurl = Environment.apiUrl; final String url = '$apiurl/hrFileDownload?id=$id'; - final token = widget.Token; + final token = localToken; final response = await http.get( Uri.parse(url), headers: { - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + 'APP-SIGNATURE': + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', // 'app-signature': 'ts-traveltool-2025-signature-123456', @@ -717,387 +801,477 @@ class _excelVerifyState extends State { } Widget _buildContent(BuildContext context) { - 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 - ), - ) : Container( - // padding: const EdgeInsets.all(20), - // color: Color(0xFFEFF3F6), - child: Column( - children: [ - Row( - children: [ - IconButton( - tooltip: 'Previous Page', - onPressed: () => {Navigator.pop(context)}, - icon: const Icon( - Icons.arrow_back_ios, - size: 18, - color: Colors.black, - ), - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - ), - const SizedBox(width: 6), - Container( - // color: Colors.redAccent.shade100, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + 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 + ), + ) + : Container( + // padding: const EdgeInsets.all(20), + // color: Color(0xFFEFF3F6), + child: Column( + children: [ + Row( children: [ - Text( - "${widget.cardType} - ${widget.cardPolicyNo} " ?? - '', - style: GoogleFonts.poppins( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - Text( - widget.TokenType == 'pre' - ? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})" - : "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})", - style: GoogleFonts.poppins( - color: Colors.grey, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ], - ), - SizedBox(height: 20), - Row( - children: [ - SizedBox( - width: 260, // 👈 set your required width - child: _dateField( - label: 'Enrolment Open Date', - controller: openDateController, - onTap: () async { - final picked = await showDatePicker( - context: context, - firstDate: DateTime(2000), - lastDate: DateTime.now(), - initialDate: DateTime.now(), - ); - if (picked != null) { - final formatted = - DateFormat('dd-MM-yyyy').format(picked); + IconButton( + tooltip: 'Previous Page', + onPressed: () async { + if (Navigator.canPop(context)) { + Navigator.pop(context); + return; + } - // ✅ If open date changed, clear close date - if (openDateController.text != formatted) { - closeDateController.clear(); - } + 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'; - openDateController.text = formatted; - } - - }, - ), - ), - const SizedBox(width: 16), - SizedBox( - width: 260, // 👈 same width - child: _dateField( - label: 'Enrolment Close Date', - controller: closeDateController, - onTap: () async { - - if (openDateController.text.isEmpty) { - ToastHelper.showErrorToast(context, 'Please select Enrolment Open Date first'); - return; - } - - final openDate = DateFormat('dd-MM-yyyy') - .parse(openDateController.text); - - final picked = await showDatePicker( - context: context, - firstDate: openDate, // ✅ Cannot select before open date - lastDate: DateTime(2100), - initialDate: openDate, - ); - - if (picked != null) { - closeDateController.text = - DateFormat('dd-MM-yyyy').format(picked); - } - }, - ), - ), - ], - ), - SizedBox(height: 20), - Row( - children: [ - Text( - 'Upload File', - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - ), - ], - ), - SizedBox(height: 5), - Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Expanded( - child: Container( - alignment: Alignment.center, - height: 125, - decoration: BoxDecoration( - color: Color(0xFFF7F5F6), // ✅ moved here - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: const Color(0xFF00A6A6), - width: 1, - ), - ), - child: GestureDetector( - onTap: () { - if (!_validateDatesBeforeUpload()) return; - if (fileName == null) { - _uploadFile('Policy Name'); // ✅ same function - } - }, - child: DragTarget( - onAccept: (html.File droppedFile) { - if (!_validateDatesBeforeUpload()) return; - - setState(() { - fileName = droppedFile.name; - }); - _dragAndDropFile(droppedFile); - }, - builder: ( - BuildContext context, - List candidateData, - List rejectedData, - ) { - return Container( - alignment: Alignment.center, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - fileName != null - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: 40, - height: 40 , - child: Tooltip( - message: 'Upload', // The text that appears on hover - child: ElevatedButton( - onPressed: () => null, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD4F1F2), - elevation: 0, - padding: EdgeInsets.zero, // ✅ IMPORTANT - alignment: Alignment.center, // ✅ FORCE CENTER - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - side: const BorderSide( // ✅ BORDER ADDED - color: Color(0xFF00999E), - width: 1, - ), - ), - ), - child: Icon( - Icons.file_upload_outlined, - size: 22, - color: Color(0xFF00999E), - ) - ), - ), - ), - const SizedBox(height: 15), - Text( - fileName!, - style: const TextStyle(fontSize: 16), - ), - const SizedBox(height: 15), - MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: resetErrorCount, - child: const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.delete_forever, - size: 20, - color: Colors.red, - ), - SizedBox(width: 4), - Text( - 'Remove', - style: TextStyle( - fontSize: 13, - color: Color(0xFF727272), - ), - ), - ], - ), - ), - ), - ], - ) - : Column( - children: [ - SizedBox( - width: 40, - height: 40 , - child: Tooltip( - message: 'Upload', // The text that appears on hover - child: ElevatedButton( - onPressed: () { - if (!_validateDatesBeforeUpload()) return; - if (fileName == null) { - _uploadFile('Policy Name'); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD4F1F2), - elevation: 0, - padding: EdgeInsets.zero, // ✅ IMPORTANT - alignment: Alignment.center, // ✅ FORCE CENTER - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - side: const BorderSide( // ✅ BORDER ADDED - color: Color(0xFF00999E), - width: 1, - ), - ), - ), - child: Icon( - Icons.file_upload_outlined, - size: 22, - color: Color(0xFF00999E), - ) - ), - ), - ), - SizedBox(height: 12), - Text('Upload Your Documents', - style: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Color(0xFF000000) - ), - ), - SizedBox(height: 8), - Text( - '(Supported Format: XLSX)', - style: GoogleFonts.poppins( - fontSize: 10, - fontWeight: FontWeight.w400, - color: Color(0xFF707070) - ), - ), - ], - ), - ], + Navigator.pushReplacement( + context, + MaterialPageRoute( + settings: const RouteSettings(name: 'hrPolicyDetails'), + builder: (_) => hrPolicyDetails( + ClientId: clientId, + policyTypeId: policyTypeId, + ClientPoliyId: clientPolicyId, + clientBranchId: clientBranchId, + Token: token, + TokenType: tokenType, + cardType: cardType, + cardPolicyNo: policyNo, + cardInsurer_name: insurer, + cardPolicy_name: policyName, + cardPolicy_ExpDate: expDate, + total_premium: totalPremium, + is_ecard_bulk_download_for_employee: int.tryParse(bulkDownload) ?? 0, + ), ), ); }, + icon: const Icon( + Icons.arrow_back_ios, + size: 18, + color: Colors.black, + ), + ), + const SizedBox(width: 6), + Container( + // color: Colors.redAccent.shade100, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "${localCardType} - ${localCardPolicyNo} " ?? '', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + Text( + localTokenType == 'pre' + ? "${localCardPolicyName} (${localCardPolicyExpDate})" + : "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ], + ), + SizedBox(height: 20), + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SizedBox( + width: 260, // 👈 set your required width + child: _dateField( + label: 'Enrolment Open Date', + controller: openDateController, + onTap: () async { + final picked = await showDatePicker( + context: context, + firstDate: DateTime(2000), + lastDate: DateTime.now(), + initialDate: DateTime.now(), + ); + if (picked != null) { + final formatted = + DateFormat('dd-MM-yyyy').format(picked); + + // ✅ If open date changed, clear close date + if (openDateController.text != formatted) { + closeDateController.clear(); + } + + openDateController.text = formatted; + } + }, + ), + ), + const SizedBox(width: 16), + SizedBox( + width: 260, // 👈 same width + child: _dateField( + label: 'Enrolment Close Date', + controller: closeDateController, + onTap: () async { + if (openDateController.text.isEmpty) { + ToastHelper.showErrorToast(context, + 'Please select Enrolment Open Date first'); + return; + } + + final openDate = DateFormat('dd-MM-yyyy') + .parse(openDateController.text); + + final picked = await showDatePicker( + context: context, + firstDate: + openDate, // ✅ Cannot select before open date + lastDate: DateTime(2100), + initialDate: openDate, + ); + + if (picked != null) { + closeDateController.text = + DateFormat('dd-MM-yyyy').format(picked); + } + }, + ), + ), + ], + ), + SizedBox(height: 20), + Row( + children: [ + Text( + 'Upload File', + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ], + ), + SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: Container( + alignment: Alignment.center, + height: 125, + decoration: BoxDecoration( + color: Color(0xFFF7F5F6), // ✅ moved here + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: const Color(0xFF00A6A6), + width: 1, + ), + ), + child: GestureDetector( + onTap: () { + if (!_validateDatesBeforeUpload()) return; + if (fileName == null) { + _uploadFile( + 'Policy Name'); // ✅ same function + } + }, + child: DragTarget( + onAccept: (html.File droppedFile) { + if (!_validateDatesBeforeUpload()) return; + + setState(() { + fileName = droppedFile.name; + }); + _dragAndDropFile(droppedFile); + }, + builder: ( + BuildContext context, + List candidateData, + List rejectedData, + ) { + return Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + fileName != null + ? Column( + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + SizedBox( + width: 40, + height: 40, + child: Tooltip( + message: + 'Upload', // The text that appears on hover + child: ElevatedButton( + onPressed: () => + null, + style: + ElevatedButton + .styleFrom( + backgroundColor: + const Color( + 0xFFD4F1F2), + elevation: 0, + padding: EdgeInsets + .zero, // ✅ IMPORTANT + alignment: Alignment + .center, // ✅ FORCE CENTER + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .circular( + 10), + side: + const BorderSide( + // ✅ BORDER ADDED + color: Color( + 0xFF00999E), + width: 1, + ), + ), + ), + child: Icon( + Icons + .file_upload_outlined, + size: 22, + color: Color( + 0xFF00999E), + )), + ), + ), + const SizedBox( + height: 15), + Text( + fileName!, + style: const TextStyle( + fontSize: 16), + ), + const SizedBox( + height: 15), + MouseRegion( + cursor: + SystemMouseCursors + .click, + child: GestureDetector( + onTap: + resetErrorCount, + child: const Row( + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + Icon( + Icons + .delete_forever, + size: 20, + color: + Colors.red, + ), + SizedBox( + width: 4), + Text( + 'Remove', + style: + TextStyle( + fontSize: 13, + color: Color( + 0xFF727272), + ), + ), + ], + ), + ), + ), + ], + ) + : Column( + children: [ + SizedBox( + width: 40, + height: 40, + child: Tooltip( + message: + 'Upload', // The text that appears on hover + child: ElevatedButton( + onPressed: () { + if (!_validateDatesBeforeUpload()) + return; + if (fileName == + null) { + _uploadFile( + 'Policy Name'); + } + }, + style: + ElevatedButton + .styleFrom( + backgroundColor: + const Color( + 0xFFD4F1F2), + elevation: 0, + padding: EdgeInsets + .zero, // ✅ IMPORTANT + alignment: Alignment + .center, // ✅ FORCE CENTER + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .circular( + 10), + side: + const BorderSide( + // ✅ BORDER ADDED + color: Color( + 0xFF00999E), + width: 1, + ), + ), + ), + child: Icon( + Icons + .file_upload_outlined, + size: 22, + color: Color( + 0xFF00999E), + )), + ), + ), + SizedBox(height: 12), + Text( + 'Upload Your Documents', + style: + GoogleFonts.poppins( + fontSize: 16, + fontWeight: + FontWeight + .w600, + color: Color( + 0xFF000000)), + ), + SizedBox(height: 8), + Text( + '(Supported Format: XLSX)', + style: + GoogleFonts.poppins( + fontSize: 10, + fontWeight: + FontWeight + .w400, + color: Color( + 0xFF707070)), + ), + ], + ), + ], + ), + ); + }, + ), + ), + ), + ), + ], + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Please download the sample file to review the format.', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + color: Color(0xFF707070))), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + downloadSampleFile(); + }, + child: Text( + 'Template File', + style: TextStyle( + fontSize: 15, + color: Color( + 0xFF00999E), // Add underline decoration + ), + ), + ), + ) + ])) + ], + ), + SizedBox(height: 20), + Column( + children: [ + _buildFileUploadedGrid(), + const SizedBox(height: 16), + _buildPagination(context), + ], + ), + ], ), ), ), - ), - ], - ), - SizedBox(height: 20), - Row( - mainAxisAlignment: - MainAxisAlignment.start, - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment - .start, - children: [ - Text( - 'Please download the sample file to review the format.', - textAlign: - TextAlign.center, - style: TextStyle( - fontSize: 12, - fontWeight: - FontWeight.w400, - color: Color(0xFF707070) - )), - MouseRegion( - cursor: SystemMouseCursors - .click, - child: GestureDetector( - onTap: () { - downloadSampleFile(); - }, - child: Text( - 'Template File', - style: TextStyle( - fontSize: 15, - color: Color( - 0xFF00999E), // Add underline decoration - ), - ), - ), - ) - ])) - ], - ), - SizedBox(height: 20), - Row( - children: [ - Expanded( - child: Column( - children: [ - _buildFileUploadedGrid(), - const SizedBox(height: 16), - _buildPagination(context), - ], - ), - ) - - ], - ), - ], - ), - ); + ], + ), + ); } Widget _buildFileUploadedGrid() { if (filteredData.isEmpty) { - return const SizedBox( - height: 120, - child: Center(child: Text('No uploaded files')), - ); + return const Center(child: Text('No uploaded files')); } return GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, // ✅ IMPORTANT + physics: const NeverScrollableScrollPhysics(), // ✅ Disable inner scroll + padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, // 👈 2 cards per row + crossAxisCount: 2, crossAxisSpacing: 16, mainAxisSpacing: 16, - childAspectRatio: 10, // 👈 card height + childAspectRatio: 10, ), itemCount: _paginatedData.length, itemBuilder: (context, index) { @@ -1107,6 +1281,31 @@ class _excelVerifyState extends State { ); } + // 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 item) { return Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), @@ -1189,9 +1388,7 @@ class _excelVerifyState extends State { children: [ /// 🔴 Error + Status Row( - children: [ - - ], + children: [], ), const SizedBox(height: 8), @@ -1201,88 +1398,91 @@ class _excelVerifyState extends State { if (item['file_error_status'] == '1') Tooltip( message: 'Info', // Added tooltip name - child:InkWell( - onTap: () async { - print(item); - // return; - final String? token = await tokenService.getCurrentToken(); - final String? enrollmentClient_id = await tokenService.readValue('enrollmentClient_id'); - final String? enrollmentEmpClientBranchId = await tokenService.readValue('enrollmentEmpClientBranchId'); + child: InkWell( + onTap: () async { + print(item); + // return; + final String? token = + await tokenService.getCurrentToken(); + final String? enrollmentClient_id = await tokenService + .readValue('enrollmentClient_id'); + final String? enrollmentEmpClientBranchId = + await tokenService + .readValue('enrollmentEmpClientBranchId'); - print(item); - print(enrollmentClient_id); - print(widget.policyTypeId); - print(enrollmentEmpClientBranchId); - print(token); - print('post'); - print(widget.cardType); - print(widget.cardPolicyNo); - print(widget.cardInsurer_name); - print(widget.cardPolicy_name); - print(widget.cardPolicy_ExpDate); - print(item['id']); + print(item); + print(enrollmentClient_id); + print(localPolicyTypeId); + print(enrollmentEmpClientBranchId); + print(token); + print('post'); + print(localCardType); + print(localCardPolicyNo); + print(localCardInsurerName); + print(localCardPolicyName); + print(localCardPolicyExpDate); + print(item['id']); - // ✅ SAFETY CHECK - if (token == null || - enrollmentClient_id == null || - enrollmentEmpClientBranchId == null) { - debugPrint('❌ Missing required data for navigation ${token}'); - return; - } + // ✅ SAFETY CHECK + if (token == null || + enrollmentClient_id == null || + enrollmentEmpClientBranchId == null) { + debugPrint( + '❌ Missing required data for navigation ${token}'); + return; + } - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - excelErrorScreen( - ClientId: enrollmentClient_id, - policy_no: item['policy_no'], - action: item['file_action'], - created_at: item['created_at'], - clientBranchId: enrollmentEmpClientBranchId, - Token: token, - TokenType: 'pre', - id: item['id'] - ), - ), - ); - }, - child: Icon( - Icons.error, - size: 16, - color: Colors.red, + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => excelErrorScreen( + ClientId: enrollmentClient_id, + policy_no: item['policy_no'], + action: item['file_action'], + created_at: item['created_at'], + clientBranchId: enrollmentEmpClientBranchId, + Token: token, + TokenType: 'pre', + id: item['id']), + ), + ); + }, + child: Icon( + Icons.error, + size: 16, + color: Colors.red, + ), ), ), - ), SizedBox(width: 10), _buildStatusChip(item['status']), SizedBox(width: 10), Tooltip( message: 'Download', // Added tooltip name - child:InkWell( - onTap: () { - getHrFileDownload(item['id'], item['file_name']); - }, - child: Container( - height: 30, - width: 30, - decoration: BoxDecoration( - color: Color(0xFFC5F2F4), - borderRadius: BorderRadius.circular(10), - border: Border.all(color: const Color(0xFF76CED2)), - ), - child: Icon( - Icons.file_download_outlined, - color: Color(0xFF1D1B20), - size: 22, + child: InkWell( + onTap: () { + getHrFileDownload(item['id'], item['file_name']); + }, + child: Container( + height: 30, + width: 30, + decoration: BoxDecoration( + color: Color(0xFFC5F2F4), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: const Color(0xFF76CED2)), + ), + child: Icon( + Icons.file_download_outlined, + color: Color(0xFF1D1B20), + size: 22, + ), ), ), ), - ), ], ), - /// ⬇ Download + /// ⬇ Download ], ), ], @@ -1325,12 +1525,13 @@ class _excelVerifyState extends State { Widget _buildPagination(BuildContext context) { // 1. Calculate the range of entries being shown final totalItems = filteredData.length; - final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + final int startEntry = + totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; int endEntry = _currentPage * _rowsPerPage; if (endEntry > totalItems) endEntry = totalItems; final totalPages = (filteredData.length / _rowsPerPage).ceil(); - const visiblePageCount = 5; + const visiblePageCount = 6; List getVisiblePages() { if (totalPages <= visiblePageCount) { @@ -1356,7 +1557,6 @@ class _excelVerifyState extends State { _currentPage + 1, _currentPage + 2, ]; - } List visiblePages = getVisiblePages(); @@ -1365,7 +1565,8 @@ class _excelVerifyState extends State { // Match this horizontal padding (16) to your Table Header padding for perfect alignment padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, // Pushes text to left, buttons to right + mainAxisAlignment: MainAxisAlignment + .spaceBetween, // Pushes text to left, buttons to right children: [ // --- LEFT SIDE: Showing Text --- Text( @@ -1384,7 +1585,7 @@ class _excelVerifyState extends State { DropdownButton( value: _rowsPerPage, // focusColor: Colors.transparent, // Fix: Removes the grey/blue highlight on change - items: [5, 10, 15, 20, 50].map((int value) { + items: [6, 10, 15, 20, 50].map((int value) { return DropdownMenuItem( value: value, child: Text(' $value ', @@ -1450,7 +1651,7 @@ class _excelVerifyState extends State { child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: - _currentPage == page ? const Color(0xFF00A6A6) : Colors.grey[300], + _currentPage == page ? const Color(0xFF00A6A6) : Colors.grey[300], foregroundColor: _currentPage == page ? Colors.white : Colors.black, minimumSize: const Size(36, 36), padding: EdgeInsets.zero, @@ -1475,7 +1676,6 @@ class _excelVerifyState extends State { return '-'; } } - } Widget _dateField({ @@ -1511,7 +1711,7 @@ Widget _dateField({ color: Color(0xFF00999E), ), contentPadding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + const EdgeInsets.symmetric(horizontal: 12, vertical: 10), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: const BorderSide( @@ -1533,7 +1733,6 @@ Widget _dateField({ ); } - class Data { final dynamic value; final int row; diff --git a/lib/service/api_service.dart b/lib/service/api_service.dart index 0d4f43f..f198a7f 100755 --- a/lib/service/api_service.dart +++ b/lib/service/api_service.dart @@ -12,6 +12,8 @@ class ApiService { String? _token; String? _hrtoken; bool _isSessionOutToastShown = false; // Flag to track toast message + bool isTpaDashboardEnabled = false; + bool isTpaSelected = false; // track which dashboard is active ApiService(this.context) { _initializeToken(); @@ -676,9 +678,32 @@ class ApiService { } } - Future> getClaimPoliciesToApi(String token) async { + Future> 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> getClaimPoliciesToApi(String token,empClientId) async { print("getgetClaimPoliciesToApii1"); - final url = Uri.parse('${Environment.apiUrlPost}claimsSearch'); + final url = Uri.parse('${Environment.apiUrlPost}claimsSearch?client_id=$empClientId'); final headers = { 'Authorization': 'Bearer $token' ?? '', @@ -993,6 +1018,10 @@ class ApiService { } Future> _handleResponse(http.Response response) async { + // 'throttle' => 429, + // 'soft' => 429, + // 'medium' => 403, + // 'hard' => 451, if (response.statusCode == 200) { return jsonDecode(response.body); } else if (response.statusCode == 401) { @@ -1001,6 +1030,17 @@ class ApiService { await _clearLocalStorageAndRedirect(); } return {}; + } else if (response.statusCode == 403) { + if (!_isSessionOutToastShown) { + _isSessionOutToastShown = true; + await _clearLocalStorageAndRedirect(); + } + return {}; + }else if (response.statusCode == 451) { + final body = jsonDecode(response.body); + final message = body['message']; + ToastHelper.showWarningToast(context, message); + return {}; } else if (response.statusCode == 429) { final body = jsonDecode(response.body); final message = body['message']; diff --git a/lib/service/multi_file_upload_widget.dart b/lib/service/multi_file_upload_widget.dart index a58487f..c7defb4 100755 --- a/lib/service/multi_file_upload_widget.dart +++ b/lib/service/multi_file_upload_widget.dart @@ -11,6 +11,7 @@ class MultiFileUploadWidget extends StatefulWidget { State createState() => _MultiFileUploadWidgetState(); static bool hasFiles = false; + static bool showValidation = false; } class _MultiFileUploadWidgetState extends State { @@ -44,6 +45,7 @@ class _MultiFileUploadWidgetState extends State { setState(() { errorMessage = null; MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty; + MultiFileUploadWidget.showValidation = false; }); } } @@ -52,6 +54,9 @@ class _MultiFileUploadWidgetState extends State { fileService.removeFileAt(index); setState(() { MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty; + if (fileService.files.isEmpty) { + MultiFileUploadWidget.showValidation = true; + } }); } @@ -173,7 +178,9 @@ class _MultiFileUploadWidgetState extends State { // ), ], - if (fileService.files.isEmpty && errorMessage == null) ...[ + if (MultiFileUploadWidget.showValidation && + fileService.files.isEmpty && + errorMessage == null) ...[ const SizedBox(height: 4), const Text( "Required", diff --git a/lib/service/token_storage_service.dart b/lib/service/token_storage_service.dart index 32da29e..30e2785 100755 --- a/lib/service/token_storage_service.dart +++ b/lib/service/token_storage_service.dart @@ -241,6 +241,9 @@ class TokenStorageService { await _secureStorage.write(key: key, value: value); } + Future removeValue(String key) async { + await _secureStorage.delete(key: key); + } Future clearBranchSession() async { final keysToRemove = [ diff --git a/web/index.html b/web/index.html index fa17c60..bd9eccc 100755 --- a/web/index.html +++ b/web/index.html @@ -33,6 +33,26 @@