From 511a671c067c18e4b3f06a8e3a43e21f200a0981 Mon Sep 17 00:00:00 2001 From: SurendarSuri30 Date: Fri, 13 Feb 2026 10:25:27 +0530 Subject: [PATCH] bug fix --- android/app/build.gradle | 4 +- lib/customAppBar/enrollmentAppBar.dart | 97 +- lib/pages/email_verify.dart | 100 +- lib/pages/enrollment/addons.dart | 40 +- lib/pages/enrollment/empDetails.dart | 71 +- lib/pages/enrollment/empReview.dart | 73 +- lib/pages/enrollment/service/api_service.dart | 5 + lib/pages/login.dart | 9 +- lib/pages/postEnrollment/claimshistory.dart | 2 - .../generalexclusionsdeductibles.dart | 947 +++------- lib/pages/postEnrollment/help.dart | 152 +- lib/pages/postEnrollment/home.dart | 2 +- lib/pages/postEnrollment/planclaimsform.dart | 20 +- lib/pages/postEnrollment/policies.dart | 6 +- lib/pages/postEnrollment/profile.dart | 30 +- .../postEnrollment/service/api_service.dart | 21 +- lib/pages/session/SetPinBiometric.dart | 1597 +++++++++-------- lib/pages/session/changePin.dart | 43 +- .../session/settingUpPinAndBiometric.dart | 59 +- pubspec.yaml | 4 +- 20 files changed, 1482 insertions(+), 1800 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 88414d0..a11a274 100755 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -19,12 +19,12 @@ if (project.hasProperty('google-services.json')) { def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { - flutterVersionCode = '53' + flutterVersionCode = '54' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { - flutterVersionName = '2.0.15' + flutterVersionName = '2.0.16' } def keystoreProperties = new Properties() diff --git a/lib/customAppBar/enrollmentAppBar.dart b/lib/customAppBar/enrollmentAppBar.dart index ca16974..4aa4da0 100755 --- a/lib/customAppBar/enrollmentAppBar.dart +++ b/lib/customAppBar/enrollmentAppBar.dart @@ -9,7 +9,6 @@ import 'package:nhance_app_pwa/customAppBar/responsive.dart'; import 'package:nhance_app_pwa/customAppBar/toastHelper.dart'; import 'package:shared_preferences/shared_preferences.dart'; - import '../config/environment.dart'; import '../models/platform_helper_mobile.dart' if (dart.library.html) '../models/platform_helper_other.dart'; @@ -29,7 +28,7 @@ class _CustomAppBarState extends State { bool showBackToHR = true; bool hideInactiveStatus = true; dynamic empStatus; - dynamic _postToken; + // dynamic _postToken; bool isTokenAvailable = false; late ApiService apiService; final session = SessionManager(); @@ -44,16 +43,28 @@ class _CustomAppBarState extends State { Future checkEnrollToken() async { SharedPreferences prefs = await SharedPreferences.getInstance(); setState(() { - isTokenAvailable = prefs.getString('enrollToken') != null; + isTokenAvailable = prefs.getString('pre_token') != null; }); } Future getEmpStatus() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); + + // final empStatusValue = prefs.getString('enrollmentEmp_status'); + final postTokenValue = prefs.getString('post_token'); + setState(() { - empStatus = prefs.getString('enrollmentEmp_status'); - // _postToken = prefs.getString('_postToken'); - isTokenAvailable = prefs.getString('_postToken') != null; + // // empStatus: null-safe + empty-safe + // empStatus = (empStatusValue != null && empStatusValue.isNotEmpty) + // ? empStatusValue + // : null; + + // token availability: null + empty check + isTokenAvailable = + postTokenValue != null && postTokenValue.trim().isNotEmpty; + + debugPrint('empStatus: $empStatus'); + debugPrint('isTokenAvailable: $isTokenAvailable'); }); } @@ -80,7 +91,8 @@ class _CustomAppBarState extends State { body: json.encode(params), headers: { HttpHeaders.contentTypeHeader: 'application/json', - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + 'APP-SIGNATURE': + 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', }, ); @@ -95,7 +107,8 @@ class _CustomAppBarState extends State { prefs.setString('mpin', data['Mpin']); } if (data['is_biometric_enabled'] != null) { - prefs.setString('is_biometric_enabled', data['is_biometric_enabled']); + prefs.setString( + 'is_biometric_enabled', data['is_biometric_enabled']); } if (data['is_mpin_skipped'] != null) { prefs.setString('is_mpin_skipped', data['is_mpin_skipped']); @@ -169,8 +182,6 @@ class _CustomAppBarState extends State { } } - - @override Widget build(BuildContext context) { final sw = MediaQuery.of(context).size.width; @@ -207,19 +218,19 @@ class _CustomAppBarState extends State { ), // AdaptiveNavBar Column if (!isTokenAvailable) - Row( - mainAxisSize: MainAxisSize.min, - children: [ - NavBarItem( - text: "Logout", - onTap: () async { - logout(context); - }, - ), - ], - ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + NavBarItem( + text: "Logout", + onTap: () async { + logout(context); + }, + ), + ], + ), // if (empStatus == 'enrolled') - if (isTokenAvailable) + if (isTokenAvailable && Responsive.isDesktop(context)) Row( mainAxisSize: MainAxisSize.min, children: [ @@ -236,21 +247,9 @@ class _CustomAppBarState extends State { }, ), NavBarItem( - text: "Help", + text: "FAQs", onTap: () { - context.push('/help'); - }, - ), - NavBarItem( - text: "Wellness", - onTap: () { - // context.push('/wellness'); - // Wellness tab clicked → show popup - PopupHelper.showRedirectPopup( - context: context, - apiService: apiService, - empPrimaryId: session.empPrimaryId, - ); + context.push('/faqs'); }, ), NavBarItem( @@ -259,6 +258,29 @@ class _CustomAppBarState extends State { context.push('/profile'); }, ), + NavBarItem( + text: "Help", + onTap: () { + context.push('/help'); + }, + ), + + // NavBarItem( + // text: "Wellness", + // onTap: () => handleMenuTap(() { + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // }), + // ), + NavBarItem( + text: "Logout", + onTap: () { + logout(context); + }, + ), ], ), ], @@ -269,6 +291,7 @@ class _CustomAppBarState extends State { ); } } + class NavBarItem extends StatelessWidget { final String text; final VoidCallback? onTap; @@ -298,4 +321,4 @@ class NavBarItem extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/pages/email_verify.dart b/lib/pages/email_verify.dart index 380480e..6ed1791 100755 --- a/lib/pages/email_verify.dart +++ b/lib/pages/email_verify.dart @@ -71,6 +71,8 @@ class _MyEmailVerifyState extends State { // final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; // final FirebaseAuth _auth = FirebaseAuth.instance; bool _isLoading = false; + bool _isOtpVerified = false; + late String _verificationId; dynamic empMobileNo; @@ -204,7 +206,13 @@ class _MyEmailVerifyState extends State { } void verifyOTP(String otp) async { + if (_isLoading) return; // prevent double tap + + setState(() { + _isLoading = true; + }); try { + // print('EMAIL PARAMS : ${widget.email} - OTP : $otp'); final Map payload = (widget.type == 'mobile') ? {'otp': _otpController.text, 'mobile_number': widget.value} @@ -220,9 +228,6 @@ class _MyEmailVerifyState extends State { ); print('response : ${response.statusCode}'); if (response.statusCode == 200) { - setState(() { - _isLoading = false; - }); Map data = json.decode(response.body); print('data: $data'); @@ -271,43 +276,40 @@ class _MyEmailVerifyState extends State { if (postStatus == 'success') { setState(() { - _isLoading = false; + _isOtpVerified = true; // ✅ LOCK }); postSuccessData(post, data); } else if (status == 'success') { setState(() { - _isLoading = false; + _isOtpVerified = true; // ✅ LOCK }); enrollmentSuccessData(data); } else { - setState(() { - _isLoading = false; - }); final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.clear(); - ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again'); + ToastHelper.showErrorToast(context, 'OTP verification failed. Please try again.'); // Show a Snackbar if the OTP is invalid - print('Invalid OTP. Please try again'); + print('OTP verification failed. Please try again.'); } } else { - setState(() { - _isLoading = false; - }); final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.clear(); ToastHelper.showWarningToast(context, 'Something went wrong'); throw Exception('Failed to verify OTP'); } } catch (e) { - setState(() { - _isLoading = false; - }); final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.clear(); print('Error: $e'); ToastHelper.showWarningToast(context, 'Something went wrong'); // Show a Snackbar if there's an error while verifying OTP print('Failed to verify OTP. Please try again.'); + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } } } @@ -339,13 +341,13 @@ class _MyEmailVerifyState extends State { await SessionManager().initializeFromPostToken(post['data']); session = await SessionManager(); - if (session.client_id != null && session.client_id!.isNotEmpty) { - await dataManager.loadSelfEmployeeProfile( - clientId: session.client_id!, - empCode: session.empCodeString!, - clientBranchId: session.empClientBranchId!, - ); - } + // if (session.client_id != null && session.client_id!.isNotEmpty) { + // await dataManager.loadSelfEmployeeProfile( + // clientId: session.client_id!, + // empCode: session.empCodeString!, + // clientBranchId: session.empClientBranchId!, + // ); + // } // // Decode the JWT token received from the API response // Map? decodedToken = Jwt.parseJwt(post['data']); @@ -417,7 +419,7 @@ class _MyEmailVerifyState extends State { // prefs.setString('enrollmentClient_id', enrollmentClient_id); // enrollmentEmp_status = decodedToken['emp_status']; // prefs.setString('enrollmentEmp_status', enrollmentEmp_status); - getClientLogoAndDetails(); + // getClientLogoAndDetails(); } // // if(kIsWeb) { @@ -505,7 +507,7 @@ class _MyEmailVerifyState extends State { // prefs.setString('enrollmentClient_id', enrollmentClient_id); // enrollmentEmp_status = decodedToken['emp_status']; // prefs.setString('enrollmentEmp_status', enrollmentEmp_status); - getClientLogoAndDetails(); + // getClientLogoAndDetails(); print('Successfully Login'); @@ -513,7 +515,7 @@ class _MyEmailVerifyState extends State { // final enrollToken = prefs.getString('enrollToken'); // int skipStatus = prefs.getInt('skipStatus') ?? 1; // final mpinText = prefs.getString('mpinText'); - print(isMobilePlatform()); + // print(isMobilePlatform()); if (isMobilePlatform()) { // if (prefs.containsKey('mpin') && mpinText == 'Mpin - exist') { if (_preToken != null && _preToken.isNotEmpty) { @@ -546,6 +548,7 @@ class _MyEmailVerifyState extends State { _otpController.text = ''; _secondsRemaining = 60; _isTimerRunning = true; + _isLoading = false; }); startTimer(); verifyMobileAndEmailNumber(); @@ -1063,6 +1066,15 @@ class _MyEmailVerifyState extends State { keyboardType: TextInputType.number, showCursor: true, controller: _otpController, + validator: (value) { + if (value == null || value.isEmpty) { + return 'OTP is required'; + } + if (value.length < 6) { + return 'Please enter a valid 6-digit OTP'; + } + return null; + }, ), ), SizedBox(height: 10), @@ -1118,20 +1130,28 @@ class _MyEmailVerifyState extends State { BorderRadius.circular(10), ), ), - onPressed: () { - if (_formKey.currentState! - .validate()) { - _formKey.currentState! - .save(); // Save form fields before calling verifyOTP - verifyOTP( - _otpController.text); - } - }, - child: Text( - "Submit", - style: GoogleFonts.poppins( - color: Color(0xFFFFFFFF)), - ), + onPressed: (_isLoading || _isOtpVerified) + ? null + : () { + if (_formKey.currentState!.validate()) { + verifyOTP(_otpController.text.trim()); + } + }, + child: _isLoading + ? CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation< + Color>( + Color(0xFF00989E), + ), + ) + : Text( "Submit", + style: GoogleFonts + .poppins( + color: Color( + 0xFFFFFFFF), + ), + ) ), ), ), diff --git a/lib/pages/enrollment/addons.dart b/lib/pages/enrollment/addons.dart index 1da83f4..0805024 100755 --- a/lib/pages/enrollment/addons.dart +++ b/lib/pages/enrollment/addons.dart @@ -4590,40 +4590,38 @@ class _addOnsDetailsState extends State { ? CustomBottomNavigationBar( onTabChanged: (index) { // Add your navigation logic here - // For example: + // repositionBotman(); if (index == 0) { - context.go('/home'); - // Navigator.pushNamed(context, 'home'); + context.push('/home'); } else if (index == 1) { - context.go('/claims'); - // Navigator.pushNamed(context, 'claims'); + context.push('/claims'); } else if (index == 2) { - context.go('/profile'); - // Navigator.pushNamed(context, 'profile'); + context.push('/faqs'); } else if (index == 3) { - context.go('/help'); - // Navigator.pushNamed(context, 'help'); + context.push('/profile'); } else if (index == 4) { - context.go('/wellness'); - // Navigator.pushNamed(context, 'wellness'); + context.push('/help'); } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } }, icons: [ Icons.home_outlined, Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, Icons.person_outline_outlined, Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "wellness", - ], - initialIndex: - 0, // Initial index of the bottom navigation bar + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 0, // Initial index of the bottom navigation bar ) : null, ) ); } diff --git a/lib/pages/enrollment/empDetails.dart b/lib/pages/enrollment/empDetails.dart index f875bbf..a3f42db 100755 --- a/lib/pages/enrollment/empDetails.dart +++ b/lib/pages/enrollment/empDetails.dart @@ -1044,42 +1044,41 @@ class _empDetailsState extends State { ? null : isTokenAvailable ? CustomBottomNavigationBar( - onTabChanged: (index) { - // For example: - if (index == 0) { - context.go('/home'); - // Navigator.pushNamed(context, 'home'); - } else if (index == 1) { - context.go('/claims'); - // Navigator.pushNamed(context, 'claims'); - } else if (index == 2) { - context.go('/profile'); - // Navigator.pushNamed(context, 'profile'); - } else if (index == 3) { - context.go('/help'); - // Navigator.pushNamed(context, 'help'); - } else if (index == 4) { - context.go('/wellness'); - // Navigator.pushNamed(context, 'wellness'); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "Wellness", - ], - initialIndex: - 0, // Initial index of the bottom navigation bar - ) + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/faqs'); + } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ) : null, )); } diff --git a/lib/pages/enrollment/empReview.dart b/lib/pages/enrollment/empReview.dart index 27f7afd..292f477 100755 --- a/lib/pages/enrollment/empReview.dart +++ b/lib/pages/enrollment/empReview.dart @@ -586,6 +586,7 @@ class _empReviewDetailsState extends State { // setState(() { topUpClientPolicyId = topUpSiPolicies['gmc_si_topup']['client_policy_id']; + print('topUpClientPolicyId12345 $topUpClientPolicyId'); topUpSumInsured = topUpSiPolicies['gmc_si_topup'] ['family_floaters_of_only_si_value']; @@ -3309,43 +3310,41 @@ setState(() { ? null : isTokenAvailable ? CustomBottomNavigationBar( - onTabChanged: (index) { - // Add your navigation logic here - // For example: - if (index == 0) { - context.go('/home'); - // Navigator.pushNamed(context, 'home'); - } else if (index == 1) { - context.go('/claims'); - // Navigator.pushNamed(context, 'claims'); - } else if (index == 2) { - context.go('/profile'); - // Navigator.pushNamed(context, 'profile'); - } else if (index == 3) { - context.go('/help'); - // Navigator.pushNamed(context, 'help'); - } else if (index == 4) { - context.go('/wellness'); - // Navigator.pushNamed(context, 'wellness'); - } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - Icons.health_and_safety_outlined, - ], - labels: [ - "Home", - "Claims", - "Profile", - "Help", - "wellness", - ], - initialIndex: - 0, // Initial index of the bottom navigation bar - ) + onTabChanged: (index) { + // Add your navigation logic here + // repositionBotman(); + if (index == 0) { + context.push('/home'); + } else if (index == 1) { + context.push('/claims'); + } else if (index == 2) { + context.push('/faqs'); + } else if (index == 3) { + context.push('/profile'); + } else if (index == 4) { + context.push('/help'); + } + // else if (index == 4) { + // // context.push('/wellness'); + // // Wellness tab clicked → show popup + // if(!isRetailLoggedIn) + // PopupHelper.showRedirectPopup( + // context: context, + // apiService: apiService, + // empPrimaryId: session.empPrimaryId, + // ); + // } + }, + icons: [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + labels: ["Home", "Claims", "FAQs", "Profile","Help"], + initialIndex: 0, // Initial index of the bottom navigation bar + ) : null, )); } diff --git a/lib/pages/enrollment/service/api_service.dart b/lib/pages/enrollment/service/api_service.dart index 5c21bf1..5fb12a1 100755 --- a/lib/pages/enrollment/service/api_service.dart +++ b/lib/pages/enrollment/service/api_service.dart @@ -508,6 +508,11 @@ class ApiService { await _clearLocalStorageAndRedirect(); } return {}; + } else if (response.statusCode == 429) { + final body = jsonDecode(response.body); + final message = body['message']; + ToastHelper.showWarningToast(context, message); + return {}; } else { throw Exception('Failed to load data'); } diff --git a/lib/pages/login.dart b/lib/pages/login.dart index 75435f0..4d794a1 100755 --- a/lib/pages/login.dart +++ b/lib/pages/login.dart @@ -280,7 +280,14 @@ class _loginState extends State { ToastHelper.showErrorToast(context, message); print('Invalid mobile number'); } - } else { + } else if (response.statusCode == 429) { + setState(() { + _isLoading = false; + }); + Map data = json.decode(response.body); + final message = data['message']; + ToastHelper.showErrorToast(context, message); + } else { setState(() { _isLoading = false; }); diff --git a/lib/pages/postEnrollment/claimshistory.dart b/lib/pages/postEnrollment/claimshistory.dart index 04ef615..676718c 100755 --- a/lib/pages/postEnrollment/claimshistory.dart +++ b/lib/pages/postEnrollment/claimshistory.dart @@ -129,8 +129,6 @@ class _ClaimHistoryPopupState extends State }); } - - // ------------------------- // Remove assignment // ------------------------- diff --git a/lib/pages/postEnrollment/generalexclusionsdeductibles.dart b/lib/pages/postEnrollment/generalexclusionsdeductibles.dart index f1ba9df..536d15f 100755 --- a/lib/pages/postEnrollment/generalexclusionsdeductibles.dart +++ b/lib/pages/postEnrollment/generalexclusionsdeductibles.dart @@ -29,6 +29,7 @@ class _generalExclusionsDeductiblesState late ApiService apiService; int _currentIndex = 0; bool isActive = true; + bool isLoading = false; dynamic _token; dynamic empCodeString; dynamic empPrimaryId; @@ -40,30 +41,19 @@ class _generalExclusionsDeductiblesState late Map generalContent; late Map generalNotes; - dynamic type3; - dynamic type3SectionName; - dynamic type3Heading; - late Map type3Content; + // Headings + String? type3SectionName, type3Heading; + String? type4SectionName, type4Heading; + String? type5Heading; + String? type6Heading; + String? type7Heading; - dynamic type4; - dynamic type4SectionName; - dynamic type4Heading; - late Map type4Content; - - dynamic type5; - dynamic type5SectionName; - dynamic type5Heading; - late Map type5Content; - - dynamic type6; - dynamic type6SectionName; - dynamic type6Heading; - late Map type6Content; - - dynamic type7; - dynamic type7SectionName; - dynamic type7Heading; - late Map type7Content; + // Content lists + List type3Content = []; + List type4Content = []; + List type5Content = []; + List type6Content = []; + List type7Content = []; final session = SessionManager(); @@ -95,68 +85,81 @@ class _generalExclusionsDeductiblesState } } + /// 🔥 Extract
  • text from HTML + List extractListItems(String html) { + final regex = RegExp(r'
  • (.*?)<\/li>', dotAll: true); + return regex + .allMatches(html) + .map((m) => m.group(1)!.replaceAll(RegExp(r'<[^>]*>'), '').trim()) + .toList(); + } + Future getCashlessAndReimbursement() async { - final response = await apiService.getCashlessAndReimbursementToApi(); - print('check 1'); - if (response['status'] == 'success') { - print(response['data']); + setState(() => isLoading = true); + + try { + final response = await apiService.getCashlessAndReimbursementToApi(); + + if (response['status'] != 'success') { + setState(() => isLoading = false); + return; + } + + final List data = response['data']; + + final filtered = + data.where((e) => e['content_section'] != 'Video').toList(); + + Map? findType(String type) { + try { + return filtered.firstWhere((e) => e['type'] == type); + } catch (_) { + return null; + } + } + + final t3 = findType('3'); + final t4 = findType('4'); + final t5 = findType('5'); + final t6 = findType('6'); + final t7 = findType('7'); + setState(() { - generalExclusionsDetails = response['data'][3]; - type3 = response['data'][2]; - print(type3); - type3SectionName = type3['content_section']; - type3Heading = type3['heading']; - Map type3ContentjsonData = - jsonDecode(type3['content']); - type3Content = Map.from(type3ContentjsonData); - print('type3Content'); - print(type3Content); + if (t3 != null) { + type3SectionName = t3['content_section']; + type3Heading = t3['heading']; + type3Content = extractListItems(t3['content']); + } - type4 = response['data'][3]; - print(type4); - type4SectionName = type4['content_section']; - type4Heading = type4['heading']; - Map type4ContentjsonData = - jsonDecode(type4['content']); - type4Content = Map.from(type4ContentjsonData); - print('type4Content'); - print(type4Content); + if (t4 != null) { + type4Heading = t4['heading']; + type4Content = extractListItems(t4['content']); + } - type5 = response['data'][4]; - print(type5); - type5SectionName = type5['content_section']; - type5Heading = type5['heading']; - Map type5ContentjsonData = - jsonDecode(type5['content']); - type5Content = Map.from(type5ContentjsonData); - print('type5Content'); - print(type5Content); + if (t5 != null) { + type5Heading = t5['heading']; + type5Content = extractListItems(t5['content']); + } - type6 = response['data'][5]; - print(type6); - type6SectionName = type6['content_section']; - type6Heading = type6['heading']; - Map type6ContentjsonData = - jsonDecode(type6['content']); - type6Content = Map.from(type6ContentjsonData); - print('type6Content'); - print(type6Content); + if (t6 != null) { + type6Heading = t6['heading']; + type6Content = extractListItems(t6['content']); + } - type7 = response['data'][6]; - print(type7); - type7SectionName = type7['content_section']; - type7Heading = type7['heading']; - Map type7ContentjsonData = - jsonDecode(type7['content']); - type7Content = Map.from(type7ContentjsonData); - print('type7Content'); - print(type7Content); + if (t7 != null) { + type7Heading = t7['heading']; + type7Content = extractListItems(t7['content']); + } + + isLoading = false; }); - } else { - print('API request failed with status: ${response['status']}'); + } catch (e) { + debugPrint('API ERROR: $e'); + setState(() => isLoading = false); } } + @override Widget build(BuildContext context) { return PopScope( @@ -166,682 +169,140 @@ class _generalExclusionsDeductiblesState context.pop(); }, child: Scaffold( - backgroundColor: Colors.white, appBar: CustomAppBar(), - body: Stack( - children: [ - SingleChildScrollView( - child: Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width * 0.2, - vertical: MediaQuery.of(context).size.height * 0.03, - ) - : EdgeInsets.all(10), - color: Colors.white, + backgroundColor: Colors.white, + body: isLoading + ? Container( + color: Color(0x98FFFCE5), // Semi-transparent background + child: Center( + child: // Your GIF loader widget + Image.asset( + height: 60, + width: 60, + 'assets/nhance-loader.gif'), // Adjust path to your GIF loader + ), + ) + : SingleChildScrollView( + padding: Responsive.isDesktop(context) + ? EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.2, + vertical: MediaQuery.of(context).size.height * 0.03, + ) + : EdgeInsets.all(10), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: InkWell( - onTap: () { - context.pop(); - }, - child: Row( - mainAxisAlignment: - MainAxisAlignment.start, - children: [ - // if (!Responsive.isDesktop(context)) - Icon( - Icons.chevron_left, - color: Color(0xFF000000), - size: 30, - ), - SizedBox( - width: Responsive.isDesktop(context) - ? 0 - : 5), - Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - 'General Exclusions & Deductibles', - textAlign: TextAlign.start, - style: GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Color(0xFF000000), - ), - ), - ], - ), - ], - ), - ), - ), - ], - ), - ], - ), + Text( + 'General Exclusions & Deductibles', + style: GoogleFonts.poppins( + fontSize: 18, fontWeight: FontWeight.w600), ), - SizedBox(height: 15), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(bottom: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - type3SectionName ?? '', - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - ], - ), - ), - if (generalExclusionsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 50; // Adjust item height based on content - double listViewHeight = - type3Content.length * itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: type3Content.length, - itemBuilder: (context, index) { - String key = type3Content.keys - .elementAt(index); - String value = - type3Content[key]!; - return Padding( - padding: const EdgeInsets - .symmetric(vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - ]), - ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(bottom: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - type4SectionName ?? '', - style: GoogleFonts.poppins( - fontSize: Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - ], - ), - ), - if (generalExclusionsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - type4Heading ?? '', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 40; // Adjust item height based on content - double listViewHeight = - type4Content.length * itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: type4Content.length, - itemBuilder: (context, index) { - String key = type4Content.keys - .elementAt(index); - String value = - type4Content[key]!; - return Padding( - padding: const EdgeInsets - .symmetric(vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - ]), - ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (generalExclusionsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - type5Heading ?? '', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 40; // Adjust item height based on content - double listViewHeight = - type5Content.length * itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: type5Content.length, - itemBuilder: (context, index) { - String key = type5Content.keys - .elementAt(index); - String value = - type5Content[key]!; - return Padding( - padding: const EdgeInsets - .symmetric(vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - ]), - ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (generalExclusionsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - type6Heading ?? '', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 40; // Adjust item height based on content - double listViewHeight = - type6Content.length * itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: type6Content.length, - itemBuilder: (context, index) { - String key = type6Content.keys - .elementAt(index); - String value = - type6Content[key]!; - return Padding( - padding: const EdgeInsets - .symmetric(vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - ]), - ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - ), - padding: Responsive.isDesktop(context) - ? EdgeInsets.symmetric(vertical: 15, horizontal: 25) - : EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (generalExclusionsDetails != null) - Padding( - padding: - EdgeInsets.only(bottom: 20, left: 15), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - type7Heading ?? '', - style: GoogleFonts.poppins( - fontSize: - Responsive.isDesktop(context) - ? 18 - : 16, - fontWeight: FontWeight.w500, - color: Color(0xFF000000), - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - double itemHeight = - 40; // Adjust item height based on content - double listViewHeight = - type7Content.length * itemHeight; - double maxHeight = - constraints.maxHeight; - - if (listViewHeight > maxHeight) { - listViewHeight = maxHeight; - } - - return Container( - height: listViewHeight, - child: ListView.builder( - itemCount: type7Content.length, - itemBuilder: (context, index) { - String key = type7Content.keys - .elementAt(index); - String value = - type7Content[key]!; - return Padding( - padding: const EdgeInsets - .symmetric(vertical: 8.0), - child: Row( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 20.0), - child: Text( - "• ", - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFFE26728), - ), - ), - ), - Expanded( - child: Text( - value, - style: GoogleFonts - .poppins( - fontSize: 16, - fontWeight: - FontWeight.w400, - color: Color( - 0xFF000000), - ), - ), - ), - ], - ), - ); - }, - ), - ); - }, - ), - ], - ), - ), - ]), - ), - SizedBox(height: Responsive.isDesktop(context) ? 40 : 10), + const SizedBox(height: 20), + if (type3Content.isNotEmpty) ...[ + Text(type3SectionName ?? '', + style: GoogleFonts.poppins( + fontSize: 18, fontWeight: FontWeight.w500)), + const SizedBox(height: 10), + buildBulletList(type3Content), + ], + if (type4Content.isNotEmpty) ...[ + const SizedBox(height: 20), + Text(type4Heading ?? '', + style: GoogleFonts.poppins( + fontSize: 18, fontWeight: FontWeight.w500)), + buildBulletList(type4Content), + ], + if (type5Content.isNotEmpty) ...[ + const SizedBox(height: 20), + Text(type5Heading ?? '', + style: GoogleFonts.poppins( + fontSize: 18, fontWeight: FontWeight.w500)), + buildBulletList(type5Content), + ], + if (type6Content.isNotEmpty) ...[ + const SizedBox(height: 20), + Text(type6Heading ?? '', + style: GoogleFonts.poppins( + fontSize: 18, fontWeight: FontWeight.w500)), + buildBulletList(type6Content), + ], + if (type7Content.isNotEmpty) ...[ + const SizedBox(height: 20), + Text(type7Heading ?? '', + style: GoogleFonts.poppins( + fontSize: 18, fontWeight: FontWeight.w500)), + buildBulletList(type7Content), + ], ], ), ), - ), - if (Responsive.isDesktop(context)) - Align( - alignment: Alignment.bottomCenter, - child: Container( - width: double.infinity, - child: CustomFooter(), - ), - ), - ], - ), - // floatingActionButton: Responsive.isDesktop(context) - // ? null - // : FloatingActionButton( - // onPressed: () { - // Navigator.pushNamed(context, 'chatbot'); - // }, - // child: Icon(Icons.chat), - // ), - floatingActionButtonLocation: Responsive.isDesktop(context) - ? null - : FloatingActionButtonLocation.miniEndFloat, + bottomNavigationBar: Responsive.isDesktop(context) - ? null - : CustomBottomNavigationBar( - onTabChanged: (index) { - // Add your navigation logic here - // repositionBotman(); - if (index == 0) { - context.push('/home'); - } else if (index == 1) { - context.push('/claims'); - } else if (index == 2) { - context.push('/faqs'); - } else if (index == 3) { - context.push('/profile'); - } else if (index == 4) { - context.push('/help'); - } - // else if (index == 4) { - // // context.push('/wellness'); - // // Wellness tab clicked → show popup - // if(!isRetailLoggedIn) - // PopupHelper.showRedirectPopup( - // context: context, - // apiService: apiService, - // empPrimaryId: session.empPrimaryId, - // ); - // } - }, - icons: [ - Icons.home_outlined, - Icons.sticky_note_2_outlined, - Icons.question_answer_outlined, - Icons.person_outline_outlined, - Icons.headset_mic_outlined, - ], - labels: ["Home", "Claims", "FAQs", "Profile","Help"], - initialIndex: 0, // Initial index of the bottom navigation bar - ), + ? SizedBox( + height: 40, // 👈 FIXED HEIGHT (adjust if needed) + width: double.infinity, + child: CustomFooter(), + ) + : CustomBottomNavigationBar( + initialIndex: 0, + labels: const ["Home", "Claims", "FAQs", "Profile", "Help"], + icons: const [ + Icons.home_outlined, + Icons.sticky_note_2_outlined, + Icons.question_answer_outlined, + Icons.person_outline_outlined, + Icons.headset_mic_outlined, + ], + onTabChanged: (index) { + if (index == 0) context.push('/home'); + if (index == 1) context.push('/claims'); + if (index == 2) context.push('/faqs'); + if (index == 3) context.push('/profile'); + if (index == 4) context.push('/help'); + }, + ), )); } + + Widget buildBulletList(List items) { + if (items.isEmpty) return const SizedBox.shrink(); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: items.map((text) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(top: 2), + child: Text( + "•", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFFE26728), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + text, + style: GoogleFonts.poppins( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ), + ), + ), + ], + ), + ); + }).toList(), + ); + } + + } diff --git a/lib/pages/postEnrollment/help.dart b/lib/pages/postEnrollment/help.dart index 8df68db..b5a8d45 100755 --- a/lib/pages/postEnrollment/help.dart +++ b/lib/pages/postEnrollment/help.dart @@ -506,82 +506,82 @@ class _helpState extends State { ]), ), - Container( - width: Responsive.isDesktop(context) ? 500 : double.infinity, - height: 75, - // padding: Responsive.isDesktop(context) - // ? EdgeInsets.only(top: 20, bottom: 20, left: 30, right: 30) - // : EdgeInsets.only(top: 5, bottom: 5,left: 10,right: 10), - padding: Responsive.isDesktop(context) - ? EdgeInsets.only( - top: 20, bottom: 20, left: 30, right: 30) - : EdgeInsets.symmetric(vertical: 5, horizontal: 10), - child: Row( - children: [ - // Expanded( - // flex: 6, - // child: ElevatedButton( - // onPressed: () { - // var details = { - // 'claimsDetails': '', - // 'fromClaimPage': 1, - // }; - // Navigator.pushNamed(context, 'planclaimsform', - // arguments: details); - // }, - // style: ElevatedButton.styleFrom( - // backgroundColor: Color(0xFFE26728), - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(5), - // ), - // ), - // child: Text( - // 'Raise Claims', - // style: GoogleFonts.poppins(color: Colors.white), - // ), - // ), - // ), - // SizedBox(width: 10), - Expanded( - flex: 4, - child: ElevatedButton( - onPressed: () { - context.go('/tickets'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFFE26728), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5), - ), - ), - child: Text( - 'Raise a Query', - style: GoogleFonts.poppins(color: Colors.white), - ), - ), - ), - SizedBox(width: 10), - Expanded( - flex: 4, - child: ElevatedButton( - onPressed: () { - context.go('/raisedTicketHistory'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFFE26728), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5), - ), - ), - child: Text( - 'Track Queries', - style: GoogleFonts.poppins(color: Colors.white), - ), - ), - ), - ], - ), - ), + // Container( + // width: Responsive.isDesktop(context) ? 500 : double.infinity, + // height: 75, + // // padding: Responsive.isDesktop(context) + // // ? EdgeInsets.only(top: 20, bottom: 20, left: 30, right: 30) + // // : EdgeInsets.only(top: 5, bottom: 5,left: 10,right: 10), + // padding: Responsive.isDesktop(context) + // ? EdgeInsets.only( + // top: 20, bottom: 20, left: 30, right: 30) + // : EdgeInsets.symmetric(vertical: 5, horizontal: 10), + // child: Row( + // children: [ + // // Expanded( + // // flex: 6, + // // child: ElevatedButton( + // // onPressed: () { + // // var details = { + // // 'claimsDetails': '', + // // 'fromClaimPage': 1, + // // }; + // // Navigator.pushNamed(context, 'planclaimsform', + // // arguments: details); + // // }, + // // style: ElevatedButton.styleFrom( + // // backgroundColor: Color(0xFFE26728), + // // shape: RoundedRectangleBorder( + // // borderRadius: BorderRadius.circular(5), + // // ), + // // ), + // // child: Text( + // // 'Raise Claims', + // // style: GoogleFonts.poppins(color: Colors.white), + // // ), + // // ), + // // ), + // // SizedBox(width: 10), + // Expanded( + // flex: 4, + // child: ElevatedButton( + // onPressed: () { + // context.go('/tickets'); + // }, + // style: ElevatedButton.styleFrom( + // backgroundColor: Color(0xFFE26728), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(5), + // ), + // ), + // child: Text( + // 'Raise a Query', + // style: GoogleFonts.poppins(color: Colors.white), + // ), + // ), + // ), + // SizedBox(width: 10), + // Expanded( + // flex: 4, + // child: ElevatedButton( + // onPressed: () { + // context.go('/raisedTicketHistory'); + // }, + // style: ElevatedButton.styleFrom( + // backgroundColor: Color(0xFFE26728), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(5), + // ), + // ), + // child: Text( + // 'Track Queries', + // style: GoogleFonts.poppins(color: Colors.white), + // ), + // ), + // ), + // ], + // ), + // ), SizedBox(height: 5), diff --git a/lib/pages/postEnrollment/home.dart b/lib/pages/postEnrollment/home.dart index 87d145b..1951e76 100755 --- a/lib/pages/postEnrollment/home.dart +++ b/lib/pages/postEnrollment/home.dart @@ -1800,7 +1800,7 @@ class _HomeState extends State { return GestureDetector( onTap: () { - context.go('/policies', extra: item); + context.push('/policies', extra: item); }, child: MouseRegion( cursor: SystemMouseCursors.click, diff --git a/lib/pages/postEnrollment/planclaimsform.dart b/lib/pages/postEnrollment/planclaimsform.dart index a50fd89..f82d69b 100755 --- a/lib/pages/postEnrollment/planclaimsform.dart +++ b/lib/pages/postEnrollment/planclaimsform.dart @@ -78,6 +78,7 @@ class _planclaimsformState extends State { // List uploadedFiles = []; List uploadedFiles = []; final fileService = FileUploadService(); + bool isSubmitting = false; // Declare subjectController and bodyController as instance variables @@ -177,7 +178,10 @@ class _planclaimsformState extends State { sumInsuredController.dispose(); admitDateController.dispose(); dischargeDateController.dispose(); + serviceId = null; + departmentList.clear(); super.dispose(); + fileService.clearAll(); } // void repositionBotman() { @@ -537,6 +541,7 @@ class _planclaimsformState extends State { } Future sendFormDataToApi() async { + setState(() => isSubmitting = true); // 🔥 start loader setState(() { isServiceValid = serviceId != null; isPolicyValid = policyNumberId != null; @@ -653,7 +658,7 @@ class _planclaimsformState extends State { _token = await TokenService.getPostToken(); - final request = http.MultipartRequest('POST', Uri.parse('${Environment.apiUrl}/initiateClaim')); + final request = http.MultipartRequest('POST', Uri.parse('${Environment.apiUrl}initiateClaim')); request.headers['Authorization'] = 'Bearer $_token'; request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y'; final stringFields = fields.map((key, value) => MapEntry(key, value?.toString() ?? '')); @@ -780,6 +785,8 @@ class _planclaimsformState extends State { isLoading = false; }); print('Error submitting form data: $e'); + } finally { + setState(() => isSubmitting = false); // 🔥 stop loader } } @@ -1412,7 +1419,16 @@ SizedBox(height: 15), onPressed: () { sendFormDataToApi(); }, - child: Text( + child: isSubmitting + ? SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : Text( 'Send', style: GoogleFonts.poppins( color: Colors.white), diff --git a/lib/pages/postEnrollment/policies.dart b/lib/pages/postEnrollment/policies.dart index 3f5f762..b940500 100755 --- a/lib/pages/postEnrollment/policies.dart +++ b/lib/pages/postEnrollment/policies.dart @@ -291,7 +291,8 @@ class _policiesState extends State { canPop: false, onPopInvokedWithResult: (didPop, result) { if (didPop) return; - context.go('/home'); + // context.go('/home'); + context.pop(); }, child: Scaffold( backgroundColor: Colors.white, @@ -330,7 +331,8 @@ class _policiesState extends State { flex: 1, child: InkWell( onTap: () { - context.go('/home'); + // context.go('/home'); + context.pop(); }, child: Icon( Icons diff --git a/lib/pages/postEnrollment/profile.dart b/lib/pages/postEnrollment/profile.dart index b6664a9..586738a 100755 --- a/lib/pages/postEnrollment/profile.dart +++ b/lib/pages/postEnrollment/profile.dart @@ -69,6 +69,7 @@ class _profileState extends State { final session = SessionManager(); String installedVersion = ""; String? storeVersion = ""; + bool showSetMpin = false; void _onTabChanged(int index) { setState(() { @@ -81,7 +82,9 @@ class _profileState extends State { super.initState(); apiService = ApiService(context); // Initialize ApiService here _loadToken(); + // _checkMpinStatus(); if(!kIsWeb){ + _checkMpinStatus(); loadVersionInfo(); } } @@ -91,6 +94,22 @@ class _profileState extends State { super.dispose(); } + Future _checkMpinStatus() async { + print('check Mpin Status'); + // if (kIsWeb) return; // ❌ skip web + + final prefs = await SharedPreferences.getInstance(); + final String? isMpinSkipped = prefs.getString('is_mpin_skipped'); + print('check Mpin Status'); + print(isMpinSkipped); + + setState(() { + showSetMpin = isMpinSkipped == "1"; + print('check Mpin Status'); + print(showSetMpin); + }); + } + Future loadVersionInfo() async { print('App version:}'); installedVersion = await AppVersionService.getInstalledVersion(); @@ -762,15 +781,20 @@ class _profileState extends State { alignment: Alignment.center, child: ElevatedButton( onPressed: () { - print('changePin'); - context.go('/changePin'); + if (showSetMpin) { + print('pinSettingPage'); + context.go('/pinSettingPage'); // ✅ Set MPIN page + } else { + print('changePin'); + context.go('/changePin'); // ✅ Change PIN page + } }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // Adjust space between icon and text Text( - 'Change Pin', + showSetMpin ? 'Set MPIN' : 'Change PIN', style: GoogleFonts.poppins( color: Color(0xFFE26728)), ), diff --git a/lib/pages/postEnrollment/service/api_service.dart b/lib/pages/postEnrollment/service/api_service.dart index 9415962..d4a4d47 100755 --- a/lib/pages/postEnrollment/service/api_service.dart +++ b/lib/pages/postEnrollment/service/api_service.dart @@ -42,6 +42,10 @@ class ApiService { Future> getClaimsHistoryToApi( String ticket_type_id) async { print("getCashDepositDetailsToApi1"); + print(_postToken); + if (_postToken == null) { + await _initializeToken(); + } final url = Uri.parse('${Environment.apiUrl}claimView?ticket_id=$ticket_type_id'); @@ -112,7 +116,7 @@ class ApiService { await _initializeToken(); } final url = Uri.parse( - '${Environment.apiUrl}/get_ticket_data?emp_id=$empPrimaryId&mobile_number=$mobileNo&email_id=$emailID'); + '${Environment.apiUrl}get_ticket_data?emp_id=$empPrimaryId&mobile_number=$mobileNo&email_id=$emailID'); final headers = { 'Authorization': 'Bearer $_postToken' ?? '', }; @@ -242,7 +246,7 @@ class ApiService { if (_postToken == null) { await _initializeToken(); } - final url = Uri.parse('${Environment.apiUrl}/get_ticket_type?client_id=$clientId&emp_code=$empCode'); + final url = Uri.parse('${Environment.apiUrl}get_ticket_type?client_id=$clientId&emp_code=$empCode'); final headers = { 'Authorization': 'Bearer $_postToken' ?? '', }; @@ -255,7 +259,7 @@ class ApiService { if (_postToken == null) { await _initializeToken(); } - final url = Uri.parse('${Environment.apiUrl}/getClaimTypeMaster'); + final url = Uri.parse('${Environment.apiUrl}getClaimTypeMaster'); final headers = { 'Authorization': 'Bearer $_postToken' ?? '', }; @@ -286,7 +290,7 @@ class ApiService { if (_postToken == null) { await _initializeToken(); } - final url = Uri.parse('${Environment.apiUrl}/initiateClaim'); + final url = Uri.parse('${Environment.apiUrl}initiateClaim'); // Convert formData to Map Map stringFormData = formData.map((key, value) => MapEntry(key, value.toString())); @@ -299,6 +303,10 @@ class ApiService { Future> sendClaimsMessageToApi( Map formData) async { + print(_postToken); + if (_postToken == null) { + await _initializeToken(); + } print("sendClaimsMessageToApi"); final url = Uri.parse('${Environment.apiUrl}ticketConversationSave'); // final url = Uri.parse('${Environment.apiUrlTicket}/messages/create'); @@ -398,6 +406,11 @@ class ApiService { } else if (response.statusCode == 401) { await _clearLocalStorageAndRedirect(); return {}; + } else if (response.statusCode == 429) { + final body = jsonDecode(response.body); + final message = body['message']; + ToastHelper.showWarningToast(context, message); + return {}; } else { throw Exception('Failed to load data'); } diff --git a/lib/pages/session/SetPinBiometric.dart b/lib/pages/session/SetPinBiometric.dart index 8219a11..212cf86 100755 --- a/lib/pages/session/SetPinBiometric.dart +++ b/lib/pages/session/SetPinBiometric.dart @@ -1,260 +1,313 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:flutter_svg/svg.dart'; -import 'package:go_router/go_router.dart'; -import 'package:http/http.dart' as http; -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:jwt_decode/jwt_decode.dart'; -import 'package:nhance_app_pwa/customAppBar/toastHelper.dart'; -import 'package:pinput/pinput.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import '../../config/environment.dart'; -import '../../customAppBar/responsive.dart'; -import '../postEnrollment/service/svg_service.dart'; -import '../service/SessionManager.dart'; -import '../service/TokenService.dart'; -import 'authenticationService.dart'; + import 'dart:convert'; + import 'dart:io'; + import 'package:flutter_svg/svg.dart'; + import 'package:go_router/go_router.dart'; + import 'package:http/http.dart' as http; + import 'package:flutter/material.dart'; + import 'package:google_fonts/google_fonts.dart'; + import 'package:jwt_decode/jwt_decode.dart'; + import 'package:nhance_app_pwa/customAppBar/toastHelper.dart'; + import 'package:pinput/pinput.dart'; + import 'package:shared_preferences/shared_preferences.dart'; + import '../../config/environment.dart'; + import '../../customAppBar/responsive.dart'; + import '../postEnrollment/service/svg_service.dart'; + import '../service/SessionManager.dart'; + import '../service/TokenService.dart'; + import 'authenticationService.dart'; -class pinPage extends StatefulWidget { - const pinPage({Key? key}) : super(key: key); + class pinPage extends StatefulWidget { + const pinPage({Key? key}) : super(key: key); - @override - State createState() => _pinPageState(); -} - -class _pinPageState extends State { - final AuthService _authService = AuthService(); - final TextEditingController _pinController = TextEditingController(); - final _formKey = GlobalKey(); - late final FocusNode focusNode; - String? savedPin; - dynamic empMobileNo; - dynamic empEmailid; - dynamic _token; - dynamic _postToken; - dynamic empCodeString; - dynamic empClientBranchId; - dynamic empPrimaryId; - dynamic gpaEmpName; - dynamic client_id; - dynamic emp_status; - dynamic enrollmentEmpCodeString; - dynamic enrollmentEmpClientBranchId; - dynamic enrollmentEmpPrimaryId; - dynamic enrollmentGpaEmpName; - dynamic enrollmentClient_id; - dynamic clientName; - dynamic clientLogo; - dynamic enrollmentEmp_status; - dynamic bioMetricMpin; - dynamic biometricStatus; - dynamic isMpinSkippedStatus; - late SessionManager session; - - @override - void initState() { - super.initState(); - _checkBiometricAuthentication(); - // _loadSavedPin(); - focusNode = FocusNode(); + @override + State createState() => _pinPageState(); } - @override - void dispose() { - focusNode.dispose(); - super.dispose(); - } + class _pinPageState extends State { + final AuthService _authService = AuthService(); + final TextEditingController _pinController = TextEditingController(); + final _formKey = GlobalKey(); + late final FocusNode focusNode; + String? savedPin; + dynamic empMobileNo; + dynamic empEmailid; + dynamic _token; + dynamic _postToken; + dynamic empCodeString; + dynamic empClientBranchId; + dynamic empPrimaryId; + dynamic gpaEmpName; + dynamic client_id; + dynamic emp_status; + dynamic enrollmentEmpCodeString; + dynamic enrollmentEmpClientBranchId; + dynamic enrollmentEmpPrimaryId; + dynamic enrollmentGpaEmpName; + dynamic enrollmentClient_id; + dynamic clientName; + dynamic clientLogo; + dynamic enrollmentEmp_status; + dynamic bioMetricMpin; + dynamic biometricStatus; + dynamic isMpinSkippedStatus; + late SessionManager session; + bool _isVerifyingForgotPin = false; + bool _isPinForgotVerified = false; + bool _isVerifyingPin = false; + bool _isPinVerified = false; // hard success lock - Future checkLoginPin(BuildContext context) async { - print('checkLoginPin'); - try { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - empMobileNo = prefs.getString('empMobileNo'); - empEmailid = prefs.getString('empEmailid'); - print('empMobileNo $empMobileNo'); - print('empEmailid $empEmailid'); - // _preToken = prefs.getString('token'); - var params = {}; - if (empMobileNo != null && empMobileNo.isNotEmpty) { - params = {'mobile_number': empMobileNo}; - } else if (empEmailid != null && empEmailid.isNotEmpty) { - params = {'email_id': empEmailid}; - } + @override + void initState() { + super.initState(); + _checkBiometricAuthentication(); + // _loadSavedPin(); + focusNode = FocusNode(); + } - final response = await http.post( - Uri.parse(Environment.apiUrlEnrollment + 'checkMpin'), - body: json.encode(params), - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', - }, - ); + @override + void dispose() { + focusNode.dispose(); + super.dispose(); + } - if (response.statusCode == 200) { - print('checkLoginPin response'); - Map data = json.decode(response.body); - if (data['status'] == 'success') { - print('checkLoginPin success'); - if (data['data'] != null) { - prefs.setString('mpinText', data['data']); - } - - if (data['Mpin'] != null) { - prefs.setString('mpin', data['Mpin']); - } - - if (data['is_biometric_enabled'] != null) { - prefs.setString( - 'is_biometric_enabled', data['is_biometric_enabled']); - } - - if (data['is_mpin_skipped'] != null) { - prefs.setString('is_mpin_skipped', data['is_mpin_skipped']); - } + Future checkLoginPin(BuildContext context) async { + print('checkLoginPin'); + try { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + empMobileNo = prefs.getString('empMobileNo'); + empEmailid = prefs.getString('empEmailid'); + print('empMobileNo $empMobileNo'); + print('empEmailid $empEmailid'); + // _preToken = prefs.getString('token'); + var params = {}; + if (empMobileNo != null && empMobileNo.isNotEmpty) { + params = {'mobile_number': empMobileNo}; + } else if (empEmailid != null && empEmailid.isNotEmpty) { + params = {'email_id': empEmailid}; } - } else { - print('checkLoginPin Something went wrong'); - ToastHelper.showErrorToast(context, 'Something went wrong'); - throw Exception('Failed to verify pin number'); - } - } catch (e) { - ToastHelper.showErrorToast(context, 'Something went wrong'); - print('Error: $e'); - } - } - Future _checkBiometricAuthentication() async { - await checkLoginPin(context); - final SharedPreferences prefs = await SharedPreferences.getInstance(); - bioMetricMpin = prefs.getString('mpin'); - biometricStatus = prefs.getString('is_biometric_enabled'); - isMpinSkippedStatus = prefs.getString('is_mpin_skipped'); - print('biometricStatus'); - print(biometricStatus); + final response = await http.post( + Uri.parse(Environment.apiUrlEnrollment + 'checkMpin'), + body: json.encode(params), + headers: { + HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + }, + ); - if(biometricStatus == '1') { - bool authenticated = await _authService.authenticateWithBiometrics(); - if (authenticated) { - enterPinApi(bioMetricMpin); - } - } - } + if (response.statusCode == 200) { + print('checkLoginPin response'); + Map data = json.decode(response.body); + if (data['status'] == 'success') { + print('checkLoginPin success'); + if (data['data'] != null) { + prefs.setString('mpinText', data['data']); + } - Future enterPinApi(mpin) async { - print('mpin $mpin'); - try { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - empMobileNo = prefs.getString('empMobileNo'); - empEmailid = prefs.getString('empEmailid'); - var params = {}; - if(mpin != null && mpin.isNotEmpty){ + if (data['Mpin'] != null) { + prefs.setString('mpin', data['Mpin']); + } - if (empMobileNo != null && empMobileNo.isNotEmpty) { - print('mpin empMobileNo'); - params = {'mobile_number': empMobileNo, 'mpin': mpin}; - } else if(empEmailid != null && empEmailid.isNotEmpty){ - print('mpin empEmailid'); - params = {'email_id': empEmailid, 'mpin': mpin}; - } - } else { - if (empMobileNo != null && empMobileNo.isNotEmpty) { - params = {'mobile_number': empMobileNo, 'mpin': _pinController.text}; - } else if(empEmailid != null && empEmailid.isNotEmpty){ - params = {'email_id': empEmailid, 'mpin': _pinController.text}; - } - // _token = prefs.getString('token'); - } + if (data['is_biometric_enabled'] != null) { + prefs.setString( + 'is_biometric_enabled', data['is_biometric_enabled']); + } - - final response = await http.post( - Uri.parse(Environment.apiUrlEnrollment + 'verifyMpin'), - body: json.encode(params), - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', - }, - ); - - if (response.statusCode == 200) { - Map data = json.decode(response.body); - print('data: $data'); - _token = data['data']; - String status = data['status']; - - // Directly access the post_enrollment data - Map post = data['post_enrollment']; - print('post: $post'); - _postToken = post['data']; - String postStatus = post['status']; - - // Save tokens - await TokenService.saveTokens(preToken: _token, postToken: _postToken); - print('Tokens saved → preToken: $_token, postToken: $_postToken'); - - if (postStatus == 'success') { - postSuccessData(post, data); - } else if (status == 'success') { - enrollmentSuccessData(data); + if (data['is_mpin_skipped'] != null) { + prefs.setString('is_mpin_skipped', data['is_mpin_skipped']); + } + } } else { - ToastHelper.showErrorToast(context, 'Invalid Pin Number'); - print('Invalid Pin Number'); + print('checkLoginPin Something went wrong'); + ToastHelper.showErrorToast(context, 'Something went wrong'); + throw Exception('Failed to verify pin number'); } - } else { + } catch (e) { + ToastHelper.showErrorToast(context, 'Something went wrong'); + print('Error: $e'); + } + } + + Future _checkBiometricAuthentication() async { + await checkLoginPin(context); + final SharedPreferences prefs = await SharedPreferences.getInstance(); + bioMetricMpin = prefs.getString('mpin'); + biometricStatus = prefs.getString('is_biometric_enabled'); + isMpinSkippedStatus = prefs.getString('is_mpin_skipped'); + print('biometricStatus'); + print(biometricStatus); + + if(biometricStatus == '1' && !_isVerifyingPin && !_isPinVerified) { + bool authenticated = await _authService.authenticateWithBiometrics(); + if (authenticated) { + setState(() { + _isVerifyingPin = true; + }); + enterPinApi(bioMetricMpin); + } + } + } + + Future enterPinApi(mpin) async { + print('mpin $mpin'); + try { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + empMobileNo = prefs.getString('empMobileNo'); + empEmailid = prefs.getString('empEmailid'); + var params = {}; + if(mpin != null && mpin.isNotEmpty){ + + if (empMobileNo != null && empMobileNo.isNotEmpty) { + print('mpin empMobileNo'); + params = {'mobile_number': empMobileNo, 'mpin': mpin}; + } else if(empEmailid != null && empEmailid.isNotEmpty){ + print('mpin empEmailid'); + params = {'email_id': empEmailid, 'mpin': mpin}; + } + } else { + if (empMobileNo != null && empMobileNo.isNotEmpty) { + params = {'mobile_number': empMobileNo, 'mpin': _pinController.text}; + } else if(empEmailid != null && empEmailid.isNotEmpty){ + params = {'email_id': empEmailid, 'mpin': _pinController.text}; + } + // _token = prefs.getString('token'); + } + + + final response = await http.post( + Uri.parse(Environment.apiUrlEnrollment + 'verifyMpin'), + body: json.encode(params), + headers: { + HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + }, + ); + + if (response.statusCode == 200) { + Map data = json.decode(response.body); + print('data: $data'); + _token = data['data']; + String status = data['status']; + + // Directly access the post_enrollment data + Map post = data['post_enrollment']; + print('post: $post'); + _postToken = post['data']; + String postStatus = post['status']; + + // Save tokens + await TokenService.saveTokens(preToken: _token, postToken: _postToken); + print('Tokens saved → preToken: $_token, postToken: $_postToken'); + + if (postStatus == 'success') { + postSuccessData(post, data); + } else if (status == 'success') { + enrollmentSuccessData(data); + } else { + ToastHelper.showErrorToast(context, 'Invalid Pin Number'); + print('Invalid Pin Number'); + } + } else { + ToastHelper.showErrorToast(context, 'Something went wrong'); + context.go('/login'); + throw Exception('Failed to verify pin number'); + } + } catch (e) { ToastHelper.showErrorToast(context, 'Something went wrong'); context.go('/login'); - throw Exception('Failed to verify pin number'); + print('Error: $e'); } - } catch (e) { - ToastHelper.showErrorToast(context, 'Something went wrong'); - context.go('/login'); - print('Error: $e'); } - } - void postSuccessData(post, data) async { - String status = data['status']; - String postStatus = post['status']; + void postSuccessData(post, data) async { + String status = data['status']; + String postStatus = post['status']; + + if (postStatus == 'success') { + // final SharedPreferences prefs = await SharedPreferences.getInstance(); + // prefs.setString('_postToken', post['data']); + + await SessionManager().initializeFromPostToken(post['data']); + + session = await SessionManager(); + // Decode the JWT token received from the API response + // Map? decodedToken = Jwt.parseJwt(post['data']); + // print('postdecodedToken : $decodedToken'); + // empClientBranchId = decodedToken['client_branch_id']; + // prefs.setString('empClientBranchId', empClientBranchId); + // empCodeString = decodedToken['emp_code'].toString(); + // prefs.setString('empCode', empCodeString); + // empPrimaryId = decodedToken['id']; + // prefs.setString('empPrimaryId', empPrimaryId); + // gpaEmpName = decodedToken['name'].toString(); + // prefs.setString('gpaEmpName', gpaEmpName); + // client_id = decodedToken['client_id']; + // prefs.setString('client_id', client_id); + // emp_status = decodedToken['emp_status']; + // prefs.setString('emp_status', emp_status); + // getClientLogoAndDetails(); + + print('Successfully Login'); + + // Redirect to another page + } + + if (status == 'success') { + // final SharedPreferences prefs = await SharedPreferences.getInstance(); + // prefs.setString('enrollToken', data['data']); + await SessionManager().initializeFromPreToken(data['data']); + // // Decode the JWT token received from the API response + // Map? decodedToken = Jwt.parseJwt(data['data']); + // print('enrolldecodedToken : $decodedToken'); + // enrollmentEmpClientBranchId = decodedToken['client_branch_id']; + // prefs.setString( + // 'enrollmentEmpClientBranchId', enrollmentEmpClientBranchId); + // enrollmentEmpCodeString = decodedToken['emp_code'].toString(); + // prefs.setString('enrollmentEmpCodeString', enrollmentEmpCodeString); + // enrollmentEmpPrimaryId = decodedToken['id']; + // prefs.setString('enrollmentEmpPrimaryId', enrollmentEmpPrimaryId); + // enrollmentGpaEmpName = decodedToken['name'].toString(); + // prefs.setString('enrollmentGpaEmpName', enrollmentGpaEmpName); + // enrollmentClient_id = decodedToken['client_id']; + // prefs.setString('enrollmentClient_id', enrollmentClient_id); + // enrollmentEmp_status = decodedToken['emp_status']; + // prefs.setString('enrollmentEmp_status', enrollmentEmp_status); + getClientLogoAndDetails(); + } - if (postStatus == 'success') { // final SharedPreferences prefs = await SharedPreferences.getInstance(); - // prefs.setString('_postToken', post['data']); - await SessionManager().initializeFromPostToken(post['data']); + print(_postToken); + if (_postToken != null && _postToken.isNotEmpty) { + setState(() { + _isPinVerified = true; // 🔒 HARD LOCK + }); - session = await SessionManager(); - // Decode the JWT token received from the API response - // Map? decodedToken = Jwt.parseJwt(post['data']); - // print('postdecodedToken : $decodedToken'); - // empClientBranchId = decodedToken['client_branch_id']; - // prefs.setString('empClientBranchId', empClientBranchId); - // empCodeString = decodedToken['emp_code'].toString(); - // prefs.setString('empCode', empCodeString); - // empPrimaryId = decodedToken['id']; - // prefs.setString('empPrimaryId', empPrimaryId); - // gpaEmpName = decodedToken['name'].toString(); - // prefs.setString('gpaEmpName', gpaEmpName); - // client_id = decodedToken['client_id']; - // prefs.setString('client_id', client_id); - // emp_status = decodedToken['emp_status']; - // prefs.setString('emp_status', emp_status); - // getClientLogoAndDetails(); - - print('Successfully Login'); - - // Redirect to another page + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); + // if (enrollmentEmp_status == 'enrolled' || + // enrollmentEmp_status == 'active') { + print('Token12345: ${await TokenService.getPostToken()}'); + print('Token00000'); + // if (context.mounted) { + context.go('/home'); + // } + // Navigator.pushReplacementNamed(context, 'home'); + // } else { + // Navigator.pushReplacementNamed(context, 'empDetails'); + // } + } } - if (status == 'success') { + void enrollmentSuccessData(data) async { + await SessionManager().initializeFromPreToken(data['data']); // final SharedPreferences prefs = await SharedPreferences.getInstance(); // prefs.setString('enrollToken', data['data']); - await SessionManager().initializeFromPreToken(data['data']); + // // Decode the JWT token received from the API response // Map? decodedToken = Jwt.parseJwt(data['data']); - // print('enrolldecodedToken : $decodedToken'); + // print('decodedToken : $decodedToken'); // enrollmentEmpClientBranchId = decodedToken['client_branch_id']; - // prefs.setString( - // 'enrollmentEmpClientBranchId', enrollmentEmpClientBranchId); + // prefs.setString('enrollmentEmpClientBranchId', enrollmentEmpClientBranchId); // enrollmentEmpCodeString = decodedToken['emp_code'].toString(); // prefs.setString('enrollmentEmpCodeString', enrollmentEmpCodeString); // enrollmentEmpPrimaryId = decodedToken['id']; @@ -266,602 +319,608 @@ class _pinPageState extends State { // enrollmentEmp_status = decodedToken['emp_status']; // prefs.setString('enrollmentEmp_status', enrollmentEmp_status); getClientLogoAndDetails(); - } - // final SharedPreferences prefs = await SharedPreferences.getInstance(); + print('Successfully Login'); - print(_postToken); - if (_postToken != null && _postToken.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Logged In'); - // if (enrollmentEmp_status == 'enrolled' || - // enrollmentEmp_status == 'active') { - print('Token12345: ${await TokenService.getPostToken()}'); - print('Token00000'); - // if (context.mounted) { - context.go('/home'); - // } - // Navigator.pushReplacementNamed(context, 'home'); - // } else { - // Navigator.pushReplacementNamed(context, 'empDetails'); - // } - } - } - - void enrollmentSuccessData(data) async { - await SessionManager().initializeFromPreToken(data['data']); - // final SharedPreferences prefs = await SharedPreferences.getInstance(); - // prefs.setString('enrollToken', data['data']); - - // // Decode the JWT token received from the API response - // Map? decodedToken = Jwt.parseJwt(data['data']); - // print('decodedToken : $decodedToken'); - // enrollmentEmpClientBranchId = decodedToken['client_branch_id']; - // prefs.setString('enrollmentEmpClientBranchId', enrollmentEmpClientBranchId); - // enrollmentEmpCodeString = decodedToken['emp_code'].toString(); - // prefs.setString('enrollmentEmpCodeString', enrollmentEmpCodeString); - // enrollmentEmpPrimaryId = decodedToken['id']; - // prefs.setString('enrollmentEmpPrimaryId', enrollmentEmpPrimaryId); - // enrollmentGpaEmpName = decodedToken['name'].toString(); - // prefs.setString('enrollmentGpaEmpName', enrollmentGpaEmpName); - // enrollmentClient_id = decodedToken['client_id']; - // prefs.setString('enrollmentClient_id', enrollmentClient_id); - // enrollmentEmp_status = decodedToken['emp_status']; - // prefs.setString('enrollmentEmp_status', enrollmentEmp_status); - getClientLogoAndDetails(); - - print('Successfully Login'); - - // Redirect to another page - final token = await TokenService.getPreToken(); - if (token != null && token.isNotEmpty) { - ToastHelper.showSuccessToast(context, 'Successfully Logged In'); - // if (enrollmentEmp_status == 'enrolled' || - // enrollmentEmp_status == 'active') { - // Navigator.pushReplacementNamed(context, 'home'); - // } else { - context.go('/empDetails'); - // Navigator.pushReplacementNamed(context, 'empDetails'); - // } - } - } - - Future enterLoginPin() async { - try { - if (_formKey.currentState!.validate()) { - enterPinApi(_pinController.text); + // Redirect to another page + final token = await TokenService.getPreToken(); + if (token != null && token.isNotEmpty) { + setState(() { + _isPinVerified = true; // 🔒 HARD LOCK + }); + ToastHelper.showSuccessToast(context, 'Successfully Logged In'); + // if (enrollmentEmp_status == 'enrolled' || + // enrollmentEmp_status == 'active') { + // Navigator.pushReplacementNamed(context, 'home'); + // } else { + context.go('/empDetails'); + // Navigator.pushReplacementNamed(context, 'empDetails'); + // } } - } catch (e) { - ToastHelper.showErrorToast(context, 'Something went wrong'); - print('Error: $e'); } - } - Future _loadSavedPin() async { - savedPin = await _authService.getPin(); - } + Future enterLoginPin() async { + if (_isVerifyingPin || _isPinVerified) return; - String? _validatePin(String? value) { - if (value == null || value.isEmpty) { - return 'Please enter a pin'; - } - if (value.length != 4) { - return 'Pin must be 4 digits'; - } - return null; - } + if (!_formKey.currentState!.validate()) return; - Future forgotPin() async { - try { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - empMobileNo = prefs.getString('empMobileNo'); - empEmailid = prefs.getString('empEmailid'); - print('empMobileNo $empMobileNo' ); - print('empEmailid $empEmailid'); - // _token = prefs.getString('token'); - var params = {}; - if (empMobileNo != null && empMobileNo.isNotEmpty) { - params = {'mobile_number': empMobileNo}; - } else if(empEmailid != null && empEmailid.isNotEmpty){ - params = {'email_id': empEmailid}; - } - print(params); - final response = await http.post( - Uri.parse(Environment.apiUrlEnrollment + 'forgotMPIN'), - body: json.encode(params), - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', - }, - ); - - if (response.statusCode == 200) { - Map data = json.decode(response.body); - if (data['status'] == 'success') { - final prefs = await SharedPreferences.getInstance(); - await prefs.clear(); - await SessionManager().clear(); - // await SessionManager().mobileSessionClear(); - context.go('/login'); - // Navigator.pushNamed(context, 'login'); - // print('data'); - // prefs.setString('mpinText', data['data']); - // prefs.setString('mpin', data['Mpin']); - // prefs.setString('is_biometric_enabled', data['is_biometric_enabled']); - // prefs.setString('is_mpin_skipped', data['is_mpin_skipped']); - // if(data['is_mpin_skipped'] == '1'){ - // Navigator.pushReplacementNamed(context, 'pinPage'); - // } - - } - } else { + setState(() { + _isVerifyingPin = true; + }); + try { + await enterPinApi(_pinController.text); + } catch (e) { ToastHelper.showErrorToast(context, 'Something went wrong'); - throw Exception('Failed to verify pin number'); - } - } catch (e) { - // ToastHelper.showErrorToast(context, 'Something went wrong'); - print('Error: $e'); - } - - } - - - Future getClientLogoAndDetails() async { - session = await SessionManager(); - var url = Uri.parse(Environment.apiUrlEnrollment + - 'getClientDetails?post_client_id=${session.client_id}&post_branch_id=${session.empClientBranchId}&pre_client_id=${session.enrollmentClient_id}&pre_branch_id=${session.enrollmentEmpClientBranchId}'); - try { - var response = await http.get( - url, - headers: { - 'Authorization': - 'Bearer $_token', // Add token to the Authorization header - 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', - }, - ); - if (response.statusCode == 200) { - // print('response.statusCode == 200'); - Map data = json.decode(response.body); - // print(data); - - if (data.containsKey('data')) { - dynamic clientDetails = data['data']; - final SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.setString('clientLogo', clientDetails['client']['client_logo']); - prefs.setString('clientName', clientDetails['client']['client_name']); + print('Error: $e'); + } finally { + if (mounted) { setState(() { - clientName = clientDetails['client']['client_name']; - print(clientName); - clientLogo = clientDetails['client']['client_logo']; - print(clientLogo); + _isVerifyingPin = false; }); - } else { - print('API request failed with status: ${data['status']}'); } - } else { - print('Request failed with status: ${response.statusCode}'); } - } catch (e) { - print('Exception occurred: $e'); - } - } - - @override - Widget build(BuildContext context) { - const focusedBorderColor = Color.fromRGBO(23, 171, 144, 1); - const fillColor = Color.fromRGBO(243, 246, 249, 0); - const borderColor = Color.fromRGBO(23, 171, 144, 0.4); - - Size _size = MediaQuery.of(context).size; - EdgeInsets marginInsets = EdgeInsets.zero; - if (Responsive.isDesktop(context)) { - marginInsets = const EdgeInsets.only( - left: 0, - right: 0, - bottom: 0, - top: 0, - ); - } else if (Responsive.isMobile(context)) { - marginInsets = const EdgeInsets.only( - left: 25, // Example value for mobile - right: 25, // Example value for mobile - bottom: 0, // Example value for mobile - top: 0, // Example value for mobile - ); - } else if (Responsive.isTablet(context)) { - marginInsets = const EdgeInsets.only( - left: 25, // Example value for mobile - right: 25, // Example value for mobile - bottom: 0, // Example value for mobile - top: 0, // Example value for mobile - ); } - final defaultPinTheme = PinTheme( - width: 56, - height: 56, - textStyle: const TextStyle( - fontSize: 20, - color: Color.fromRGBO(30, 60, 87, 1), - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(19), - border: Border.all(color: borderColor), - ), - ); + Future _loadSavedPin() async { + savedPin = await _authService.getPin(); + } - return WillPopScope( - onWillPop: () async { - return false; - }, - child: Scaffold( - resizeToAvoidBottomInset: true, // IMPORTANT - bottomNavigationBar: SafeArea( - child: Container( - color: Colors.white, // 👈 set your color here - padding: const EdgeInsets.only(bottom: 8, top: 4), - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - text: 'By continuing, you agree with our ', - style: GoogleFonts.poppins( - color: Colors.black, - fontSize: 9, + String? _validatePin(String? value) { + if (value == null || value.isEmpty) { + return 'Please enter a pin'; + } + if (value.length != 4) { + return 'Pin must be 4 digits'; + } + return null; + } + + Future forgotPin() async { + if (_isVerifyingForgotPin || _isPinForgotVerified) return; + setState(() { + _isVerifyingForgotPin = true; + }); + try { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + empMobileNo = prefs.getString('empMobileNo'); + empEmailid = prefs.getString('empEmailid'); + print('empMobileNo $empMobileNo' ); + print('empEmailid $empEmailid'); + // _token = prefs.getString('token'); + var params = {}; + if (empMobileNo != null && empMobileNo.isNotEmpty) { + params = {'mobile_number': empMobileNo}; + } else if(empEmailid != null && empEmailid.isNotEmpty){ + params = {'email_id': empEmailid}; + } + print(params); + final response = await http.post( + Uri.parse(Environment.apiUrlEnrollment + 'forgotMPIN'), + body: json.encode(params), + headers: { + HttpHeaders.contentTypeHeader: 'application/json', + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + }, + ); + + if (response.statusCode == 200) { + Map data = json.decode(response.body); + if (data['status'] == 'success') { + setState(() { + _isPinForgotVerified = true; // 🔒 HARD LOCK + }); + final prefs = await SharedPreferences.getInstance(); + await prefs.clear(); + await SessionManager().clear(); + // await SessionManager().mobileSessionClear(); + context.go('/login'); + // Navigator.pushNamed(context, 'login'); + // print('data'); + // prefs.setString('mpinText', data['data']); + // prefs.setString('mpin', data['Mpin']); + // prefs.setString('is_biometric_enabled', data['is_biometric_enabled']); + // prefs.setString('is_mpin_skipped', data['is_mpin_skipped']); + // if(data['is_mpin_skipped'] == '1'){ + // Navigator.pushReplacementNamed(context, 'pinPage'); + // } + + } + } else { + ToastHelper.showErrorToast(context, 'Something went wrong'); + throw Exception('Failed to verify pin number'); + } + } catch (e) { + // ToastHelper.showErrorToast(context, 'Something went wrong'); + print('Error: $e'); + } finally { + if (mounted) { + setState(() { + _isVerifyingForgotPin = false; + }); + } + } + + } + + + Future getClientLogoAndDetails() async { + session = await SessionManager(); + var url = Uri.parse(Environment.apiUrlEnrollment + + 'getClientDetails?post_client_id=${session.client_id}&post_branch_id=${session.empClientBranchId}&pre_client_id=${session.enrollmentClient_id}&pre_branch_id=${session.enrollmentEmpClientBranchId}'); + try { + var response = await http.get( + url, + headers: { + 'Authorization': + 'Bearer $_token', // Add token to the Authorization header + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + }, + ); + if (response.statusCode == 200) { + // print('response.statusCode == 200'); + Map data = json.decode(response.body); + // print(data); + + if (data.containsKey('data')) { + dynamic clientDetails = data['data']; + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setString('clientLogo', clientDetails['client']['client_logo']); + prefs.setString('clientName', clientDetails['client']['client_name']); + setState(() { + clientName = clientDetails['client']['client_name']; + print(clientName); + clientLogo = clientDetails['client']['client_logo']; + print(clientLogo); + }); + } else { + print('API request failed with status: ${data['status']}'); + } + } else { + print('Request failed with status: ${response.statusCode}'); + } + } catch (e) { + print('Exception occurred: $e'); + } + } + + @override + Widget build(BuildContext context) { + const focusedBorderColor = Color.fromRGBO(23, 171, 144, 1); + const fillColor = Color.fromRGBO(243, 246, 249, 0); + const borderColor = Color.fromRGBO(23, 171, 144, 0.4); + + Size _size = MediaQuery.of(context).size; + EdgeInsets marginInsets = EdgeInsets.zero; + if (Responsive.isDesktop(context)) { + marginInsets = const EdgeInsets.only( + left: 0, + right: 0, + bottom: 0, + top: 0, + ); + } else if (Responsive.isMobile(context)) { + marginInsets = const EdgeInsets.only( + left: 25, // Example value for mobile + right: 25, // Example value for mobile + bottom: 0, // Example value for mobile + top: 0, // Example value for mobile + ); + } else if (Responsive.isTablet(context)) { + marginInsets = const EdgeInsets.only( + left: 25, // Example value for mobile + right: 25, // Example value for mobile + bottom: 0, // Example value for mobile + top: 0, // Example value for mobile + ); + } + + final defaultPinTheme = PinTheme( + width: 56, + height: 56, + textStyle: const TextStyle( + fontSize: 20, + color: Color.fromRGBO(30, 60, 87, 1), + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(19), + border: Border.all(color: borderColor), + ), + ); + + return WillPopScope( + onWillPop: () async { + return false; + }, + child: Scaffold( + resizeToAvoidBottomInset: true, // IMPORTANT + bottomNavigationBar: SafeArea( + child: Container( + color: Colors.white, // 👈 set your color here + padding: const EdgeInsets.only(bottom: 8, top: 4), + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + text: 'By continuing, you agree with our ', + style: GoogleFonts.poppins( + color: Colors.black, + fontSize: 9, + ), + children: const [ + TextSpan( + text: 'privacy policy ', + style: TextStyle(color: Color(0xFF00989E)), + ), + TextSpan(text: 'and '), + TextSpan( + text: 'terms of use', + style: TextStyle(color: Color(0xFF00989E)), + ), + ], ), - children: const [ - TextSpan( - text: 'privacy policy ', - style: TextStyle(color: Color(0xFF00989E)), - ), - TextSpan(text: 'and '), - TextSpan( - text: 'terms of use', - style: TextStyle(color: Color(0xFF00989E)), - ), - ], ), ), ), - ), - body: Container( - color: Colors.white, // Set the color for the body - child: SizedBox.expand( - child: Stack(children: [ - SingleChildScrollView( - child: Container( - padding: EdgeInsets.all(0), - color: Color(0xFFFFFCE5), - child: Column(children: [ - Container( - decoration: BoxDecoration( - color: Color( - 0xFFFFFCE5), // Set background color for the container - borderRadius: BorderRadius.circular( - 10), // Set border radius for the container - ), - // padding: EdgeInsets.only(top: 0, bottom: 0, left: 10, right: 10), - child: Column( - // crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - flex: 12, - child: Align( - alignment: Alignment.topLeft, - child: Padding( - padding: const EdgeInsets.only( - left: 16.0), // Add left margin - child: Image.asset( - 'assets/nhance_app_logo.png', - width: 150, - height: 100, + body: Container( + color: Colors.white, // Set the color for the body + child: SizedBox.expand( + child: Stack(children: [ + SingleChildScrollView( + child: Container( + padding: EdgeInsets.all(0), + color: Color(0xFFFFFCE5), + child: Column(children: [ + Container( + decoration: BoxDecoration( + color: Color( + 0xFFFFFCE5), // Set background color for the container + borderRadius: BorderRadius.circular( + 10), // Set border radius for the container + ), + // padding: EdgeInsets.only(top: 0, bottom: 0, left: 10, right: 10), + child: Column( + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + flex: 12, + child: Align( + alignment: Alignment.topLeft, + child: Padding( + padding: const EdgeInsets.only( + left: 16.0), // Add left margin + child: Image.asset( + 'assets/nhance_app_logo.png', + width: 150, + height: 100, + ), ), ), ), - ), - ], - ), - SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - flex: 12, - child: Align( - alignment: Alignment.center, - child: Padding( - padding: const EdgeInsets.only( - left: 16.0), // Add left margin - child: SvgPicture.string( - SvgService.getSvg('mpin'), - width: 150, - height: 150, + ], + ), + SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 12, + child: Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.only( + left: 16.0), // Add left margin + child: SvgPicture.string( + SvgService.getSvg('mpin'), + width: 150, + height: 150, + ), ), ), ), - ), - ], - ), - SizedBox(height: 30), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - flex: 12, - child: Text( - 'Enter PIN to login', - textAlign: TextAlign.center, - style: GoogleFonts.poppins( - fontSize: 16, - color: Color(0xFF404040), - fontWeight: FontWeight.w500, + ], + ), + SizedBox(height: 30), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 12, + child: Text( + 'Enter PIN to login', + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: Color(0xFF404040), + fontWeight: FontWeight.w500, + ), ), ), - ), - ], - ), - SizedBox(height: 10), - ], + ], + ), + SizedBox(height: 10), + ], + ), ), - ), - Container( - decoration: BoxDecoration( - color: Colors - .white, // Set background color for the container - borderRadius: BorderRadius.only( - topLeft: Radius.circular(150.0), - topRight: Radius.circular(0), - ), // Set border radius for the container - ), - padding: EdgeInsets.only( - top: 20, bottom: 10, left: 10, right: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: marginInsets, - alignment: Alignment.bottomCenter, - child: SingleChildScrollView( - child: Form( - key: _formKey, - child: Column( - children: [ - Row( - children: [ - Expanded( - flex: 12, - child: Column( - children: [ - SizedBox(height: 40), - Container( - margin: - EdgeInsets.symmetric( - horizontal: 30), - child: Row( + Container( + decoration: BoxDecoration( + color: Colors + .white, // Set background color for the container + borderRadius: BorderRadius.only( + topLeft: Radius.circular(150.0), + topRight: Radius.circular(0), + ), // Set border radius for the container + ), + padding: EdgeInsets.only( + top: 20, bottom: 10, left: 10, right: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: marginInsets, + alignment: Alignment.bottomCenter, + child: SingleChildScrollView( + child: Form( + key: _formKey, + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 12, + child: Column( + children: [ + SizedBox(height: 40), + Container( + margin: + EdgeInsets.symmetric( + horizontal: 30), + child: Row( + mainAxisAlignment: + MainAxisAlignment + .start, // Align text to the right + children: [ + Align( + // alignment: Alignment.centerLeft, + child: Text( + 'Enter the 4 Digit pin', + style: GoogleFonts + .poppins( + fontSize: 16, + color: Color( + 0xFF909090), + fontWeight: + FontWeight + .w400, + ), + ), + ), + ]), + ), + SizedBox(height: 5), + Container( + margin: + EdgeInsets.symmetric( + horizontal: 0), + child: Pinput( + enabled: !_isVerifyingPin && !_isPinVerified, + length: 4, + defaultPinTheme: + defaultPinTheme, + separatorBuilder: + (index) => + const SizedBox( + width: 8), + showCursor: true, + controller: + _pinController, + validator: _validatePin, + errorPinTheme: + defaultPinTheme + .copyBorderWith( + border: Border.all( + color: Colors + .redAccent), + ), + hapticFeedbackType: + HapticFeedbackType + .lightImpact, + onCompleted: (pin) { + debugPrint( + 'onCompleted: $pin'); + }, + onChanged: (value) { + debugPrint( + 'onChanged: $value'); + }, + cursor: Column( + mainAxisAlignment: + MainAxisAlignment + .end, + children: [ + Container( + margin: + const EdgeInsets + .only( + bottom: 9), + width: 22, + height: 1, + color: + focusedBorderColor, + ), + ], + ), + focusedPinTheme: + defaultPinTheme + .copyWith( + decoration: + defaultPinTheme + .decoration! + .copyWith( + borderRadius: + BorderRadius + .circular(8), + border: Border.all( + color: + focusedBorderColor), + ), + ), + obscureText: true, + obscuringWidget: const Text( + '●', + style: TextStyle(fontSize: 12,color: Colors.black87), + ), + submittedPinTheme: + defaultPinTheme + .copyWith( + decoration: + defaultPinTheme + .decoration! + .copyWith( + color: fillColor, + borderRadius: + BorderRadius + .circular(19), + border: Border.all( + color: + focusedBorderColor), + ), + ), + ), + ), + SizedBox(height: 15), + Container( + margin: + EdgeInsets.symmetric( + horizontal: 30), + child: SizedBox( + width: double.infinity, + height: 45, + child: ElevatedButton( + style: ElevatedButton + .styleFrom( + backgroundColor: + Color(0xFF00989E), + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .circular( + 10), + ), + ), + onPressed: (_isVerifyingPin || _isPinVerified) + ? null + : () { + enterLoginPin(); + }, + child: _isVerifyingPin + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ) + : Text( + "Submit", + style: GoogleFonts.poppins(color: Colors.white), + ), + ), + ), + ), + SizedBox(height: 15), + Container( + child: Row( mainAxisAlignment: MainAxisAlignment - .start, // Align text to the right + .center, children: [ - Align( - // alignment: Alignment.centerLeft, + GestureDetector( + onTap: (_isVerifyingForgotPin || _isPinForgotVerified) + ? null + : () { + forgotPin(); + }, child: Text( - 'Enter the 4 Digit pin', + 'Forgot PIN?', style: GoogleFonts .poppins( - fontSize: 16, + fontSize: 12, color: Color( - 0xFF909090), + 0xFF929292), fontWeight: FontWeight .w400, ), ), ), - ]), - ), - SizedBox(height: 5), - Container( - margin: - EdgeInsets.symmetric( - horizontal: 0), - child: Pinput( - length: 4, - defaultPinTheme: - defaultPinTheme, - separatorBuilder: - (index) => - const SizedBox( - width: 8), - showCursor: true, - controller: - _pinController, - validator: _validatePin, - errorPinTheme: - defaultPinTheme - .copyBorderWith( - border: Border.all( - color: Colors - .redAccent), - ), - hapticFeedbackType: - HapticFeedbackType - .lightImpact, - onCompleted: (pin) { - debugPrint( - 'onCompleted: $pin'); - }, - onChanged: (value) { - debugPrint( - 'onChanged: $value'); - }, - cursor: Column( - mainAxisAlignment: - MainAxisAlignment - .end, - children: [ - Container( - margin: - const EdgeInsets - .only( - bottom: 9), - width: 22, - height: 1, - color: - focusedBorderColor, - ), ], ), - focusedPinTheme: - defaultPinTheme - .copyWith( - decoration: - defaultPinTheme - .decoration! - .copyWith( - borderRadius: - BorderRadius - .circular(8), - border: Border.all( - color: - focusedBorderColor), - ), - ), - obscureText: true, - obscuringWidget: const Text( - '●', - style: TextStyle(fontSize: 12,color: Colors.black87), - ), - submittedPinTheme: - defaultPinTheme - .copyWith( - decoration: - defaultPinTheme - .decoration! - .copyWith( - color: fillColor, - borderRadius: - BorderRadius - .circular(19), - border: Border.all( - color: - focusedBorderColor), - ), - ), ), - ), - SizedBox(height: 15), - Container( - margin: - EdgeInsets.symmetric( - horizontal: 30), - child: SizedBox( - width: double.infinity, - height: 45, - child: ElevatedButton( - style: ElevatedButton - .styleFrom( - backgroundColor: - Color(0xFF00989E), - shape: - RoundedRectangleBorder( - borderRadius: - BorderRadius - .circular( - 10), - ), - ), - onPressed: () async { - enterLoginPin(); - }, - child: Text( - "Submit", - style: GoogleFonts - .poppins( - color: Color( - 0xFFFFFFFF)), - ), - ), - ), - ), - SizedBox(height: 15), - Container( - child: Row( - mainAxisAlignment: - MainAxisAlignment - .center, - children: [ - GestureDetector( - onTap: () { - forgotPin(); - }, - child: Text( - 'Forgot PIN?', - style: GoogleFonts - .poppins( - fontSize: 12, - color: Color( - 0xFF929292), - fontWeight: - FontWeight - .w400, + SizedBox(height: 15), + Container( + margin: + EdgeInsets.symmetric( + horizontal: 30), + child: SizedBox( + width: double.infinity, + height: 45, + child: ElevatedButton( + style: ElevatedButton + .styleFrom( + backgroundColor: + Color(0xFF00989E), + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .circular( + 10), ), ), - ), - ], - ), - ), - SizedBox(height: 15), - Container( - margin: - EdgeInsets.symmetric( - horizontal: 30), - child: SizedBox( - width: double.infinity, - height: 45, - child: ElevatedButton( - style: ElevatedButton - .styleFrom( - backgroundColor: - Color(0xFF00989E), - shape: - RoundedRectangleBorder( - borderRadius: - BorderRadius - .circular( - 10), + onPressed: (_isVerifyingForgotPin || _isPinForgotVerified) + ? null + : () { + forgotPin(); + }, + child: _isVerifyingForgotPin + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ) + : Text( + "Login with OTP", + style: GoogleFonts + .poppins( + color: Color( + 0xFFFFFFFF)), ), ), - onPressed: () async { - forgotPin(); - // context.go('/login'); - }, - child: Text( - "Login with OTP", - style: GoogleFonts - .poppins( - color: Color( - 0xFFFFFFFF)), - ), ), ), - ), - ], + ], + ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), ), - ), - ], + ], + ), ), - ), - ]), - )), - ]), - )))); + ]), + )), + ]), + )))); + } } -} diff --git a/lib/pages/session/changePin.dart b/lib/pages/session/changePin.dart index 88cfe44..e659017 100755 --- a/lib/pages/session/changePin.dart +++ b/lib/pages/session/changePin.dart @@ -32,6 +32,9 @@ class _changePinState extends State { dynamic empEmailid; dynamic _token; dynamic emp_status; + bool _isSaving = false; + bool _isPinChanged = false; // hard success lock + @override void initState(){ @@ -53,6 +56,14 @@ class _changePinState extends State { } Future setChangePin() async { + if (_isSaving || _isPinChanged) return; + + if (!_formKey.currentState!.validate()) return; + + setState(() { + _isSaving = true; + }); + try { if (_formKey.currentState!.validate()) { final SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -87,6 +98,9 @@ class _changePinState extends State { bool pinVerification = data['data']['mpin_verification']; String message = data['data']['message']; if (pinVerification) { + setState(() { + _isPinChanged = true; + }); ToastHelper.showSuccessToast(context, message); context.go('/profile'); } else { @@ -103,7 +117,14 @@ class _changePinState extends State { } catch (e) { ToastHelper.showErrorToast(context, 'Something went wrong'); print('Error: $e'); + } finally { + if (mounted) { + setState(() { + _isSaving = false; + }); + } } + } String? _validatePin(String? value) { @@ -343,6 +364,7 @@ class _changePinState extends State { margin: EdgeInsets.symmetric( horizontal: 0), child: Pinput( + enabled: !_isSaving && !_isPinChanged, length: 4, defaultPinTheme: defaultPinTheme, @@ -445,6 +467,7 @@ class _changePinState extends State { : EdgeInsets.symmetric( horizontal: 0), child: Pinput( + enabled: !_isSaving && !_isPinChanged, length: 4, defaultPinTheme: defaultPinTheme, @@ -533,15 +556,23 @@ class _changePinState extends State { .circular(10), ), ), - onPressed: () async { + onPressed: (_isSaving || _isPinChanged) + ? null + : () { setChangePin(); }, - child: Text( + child: _isSaving + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ) + : Text( "Save", - style: - GoogleFonts.poppins( - color: Color( - 0xFFFFFFFF)), + style: GoogleFonts.poppins(color: Colors.white), ), ), ), diff --git a/lib/pages/session/settingUpPinAndBiometric.dart b/lib/pages/session/settingUpPinAndBiometric.dart index 7727556..d41e6a9 100755 --- a/lib/pages/session/settingUpPinAndBiometric.dart +++ b/lib/pages/session/settingUpPinAndBiometric.dart @@ -35,6 +35,8 @@ class _pinSettingPageState extends State { dynamic _postToken; dynamic emp_status; final session = SessionManager(); + bool _isSavingPin = false; + bool _isPinSaved = false; // hard success lock @override void initState() { @@ -49,6 +51,11 @@ class _pinSettingPageState extends State { } Future setLoginPin(skipOrNot) async { + if (_isSavingPin || _isPinSaved) return; + + setState(() { + _isSavingPin = true; + }); try { print('setLoginPin'); // ✅ Only validate if not skipping @@ -63,7 +70,7 @@ class _pinSettingPageState extends State { print('setLoginPin112233 $skipOrNot'); prefs.setString('is_mpin_skipped', skipOrNot.toString()); print('setLoginPin112233'); - emp_status = session.emp_status; + // emp_status = session.emp_status; enrollToken = await TokenService.getPreToken(); _postToken = await TokenService.getPostToken(); String pin = _pinController.text; @@ -126,6 +133,10 @@ class _pinSettingPageState extends State { bool userVerification = data['data']['user_verification']; String message = data['data']['message']; if (userVerification) { + setState(() { + _isPinSaved = true; // 🔒 HARD LOCK + }); + if (_enableBiometric == true) { await _authService.saveBiometricEnableKey(_enableBiometric); bool biometricEnabled = @@ -163,7 +174,15 @@ class _pinSettingPageState extends State { } catch (e) { ToastHelper.showErrorToast(context, 'Something went wrong'); print('Error: $e'); + } finally { + if (mounted) { + setState(() { + _isSavingPin = false; + }); + } } + + } String? _validatePin(String? value) { @@ -424,6 +443,7 @@ class _pinSettingPageState extends State { EdgeInsets.symmetric( horizontal: 0), child: Pinput( + enabled: !_isSavingPin && !_isPinSaved, length: 4, defaultPinTheme: defaultPinTheme, @@ -539,6 +559,7 @@ class _pinSettingPageState extends State { : EdgeInsets.symmetric( horizontal: 0), child: Pinput( + enabled: !_isSavingPin && !_isPinSaved, length: 4, defaultPinTheme: defaultPinTheme, @@ -687,15 +708,23 @@ class _pinSettingPageState extends State { 10), ), ), - onPressed: () async { + onPressed: (_isSavingPin || _isPinSaved) + ? null + : () { setLoginPin(0); }, - child: Text( + child: _isSavingPin + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ) + : Text( "Save", - style: GoogleFonts - .poppins( - color: Color( - 0xFFFFFFFF)), + style: GoogleFonts.poppins(color: Colors.white), ), ), ), @@ -708,20 +737,18 @@ class _pinSettingPageState extends State { .center, children: [ GestureDetector( - onTap: () { - // setSkipStatus(); + onTap: (_isSavingPin || _isPinSaved) + ? null + : () { setLoginPin(1); }, child: Text( 'Skip ?', - style: GoogleFonts - .poppins( + style: GoogleFonts.poppins( fontSize: 12, - color: Color( - 0xFF929292), - fontWeight: - FontWeight - .w400, + color: (_isSavingPin || _isPinSaved) + ? Colors.grey + : const Color(0xFF929292), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index bba65eb..4ed2211 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,8 +17,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. #version: 1.0.32+32 -version: 1.0.28+33 -#version: 2.0.15+53 +version: 1.0.29+34 +#version: 2.0.16+54 environment: sdk: '>=3.3.3 <4.0.0'