diff --git a/lib/branch/branch_selection_page.dart b/lib/branch/branch_selection_page.dart index 21830a2..e1745e8 100755 --- a/lib/branch/branch_selection_page.dart +++ b/lib/branch/branch_selection_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../customAppBar/base_layout.dart'; import '../customAppBar/toastHelper.dart'; import '../service/api_service.dart'; +import '../service/secure_pop_scope.dart'; import '../service/token_storage_service.dart'; import 'branch_card_widget.dart'; import 'dart:html' as html; @@ -105,8 +106,7 @@ class _BranchSelectionPageState extends State { @override Widget build(BuildContext context) { return BaseLayout( - child: PopScope( - canPop: false, + child: SecurePopScope( child: _buildContent(context), ), ); diff --git a/lib/presentation/RaiseClaimForm.dart b/lib/presentation/RaiseClaimForm.dart index 1ac19e9..e75febb 100644 --- a/lib/presentation/RaiseClaimForm.dart +++ b/lib/presentation/RaiseClaimForm.dart @@ -726,7 +726,7 @@ class _RaiseClaimDialogState extends State { borderRadius: BorderRadius.circular(16), child: SizedBox( width: MediaQuery.of(context).size.width * 0.75, // Desktop popup width - // height: MediaQuery.of(context).size.height * 0.85, + height: MediaQuery.of(context).size.height * 0.85, // 🔥 ADD THIS child: Stack( children: [ /// MAIN CONTENT (YOUR EXISTING UI) @@ -1052,6 +1052,7 @@ class _RaiseClaimDialogState extends State { return Padding( padding: const EdgeInsets.only(bottom: 14), child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: children .map((e) => Expanded( child: Padding( diff --git a/lib/presentation/cdList.dart b/lib/presentation/cdList.dart index cf44a34..de9603e 100644 --- a/lib/presentation/cdList.dart +++ b/lib/presentation/cdList.dart @@ -10,6 +10,7 @@ import 'package:http/http.dart' as http; import 'package:nhancepolicy/presentation/policies.dart'; import 'package:universal_html/html.dart' as html; +import '../service/secure_pop_scope.dart'; import 'cdTransactionDetails.dart'; import 'package:collection/collection.dart'; @@ -63,21 +64,6 @@ class _CdPoliciesListState extends State { void initState() { super.initState(); apiService = ApiService(context); // Initialize ApiService here - html.window.onPopState.listen((event) async { - final shouldLogout = await _showLogoutDialog(); - if (shouldLogout) { - await apiService.logout(); - if (!mounted) return; - Navigator.pushNamedAndRemoveUntil( - context, - 'hrLogin', - (route) => false, - ); - } else { - // Push state back to prevent browser navigation - html.window.history.pushState(null, '', html.window.location.href); - } - }); checkIds(); } @@ -249,8 +235,7 @@ class _CdPoliciesListState extends State { @override Widget build(BuildContext context) { return BaseLayout( - child: PopScope( - canPop: false, + child: SecurePopScope( child: _buildContent(context), ), ); diff --git a/lib/presentation/claims.dart b/lib/presentation/claims.dart index e867902..b7df781 100755 --- a/lib/presentation/claims.dart +++ b/lib/presentation/claims.dart @@ -13,6 +13,7 @@ import 'package:collection/collection.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:universal_html/html.dart' as html; +import '../service/secure_pop_scope.dart'; import 'claimshistory.dart'; import '../customAppBar/base_layout.dart'; import '../service/api_service.dart'; @@ -109,22 +110,6 @@ class _ClaimsPolicieState extends State { super.initState(); apiService = ApiService(context); - html.window.onPopState.listen((event) async { - final shouldLogout = await _showLogoutDialog(); - if (shouldLogout) { - await apiService.logout(); - if (!mounted) return; - Navigator.pushNamedAndRemoveUntil( - context, - 'hrLogin', - (route) => false, - ); - } else { - // Push state back to prevent browser navigation - html.window.history.pushState(null, '', html.window.location.href); - } - }); - for (String field in tabHeader) { controllers[field] = TextEditingController(); } @@ -475,8 +460,7 @@ class _ClaimsPolicieState extends State { @override Widget build(BuildContext context) { return BaseLayout( - child: PopScope( - canPop: false, + child: SecurePopScope( child: _buildContent(context), ), ); diff --git a/lib/presentation/hrDashboard.dart b/lib/presentation/hrDashboard.dart index 3436a49..5d4fa3a 100755 --- a/lib/presentation/hrDashboard.dart +++ b/lib/presentation/hrDashboard.dart @@ -9,6 +9,7 @@ import 'package:universal_html/html.dart' as html; import '../config/environment.dart'; import '../customAppBar/base_layout.dart'; +import '../service/secure_pop_scope.dart'; class hrDashboard extends StatefulWidget { hrDashboard({Key? key}) : super(key: key); @@ -45,21 +46,6 @@ class _hrDashboardState extends State with SingleTickerProviderStat void initState() { super.initState(); apiService = ApiService(context); - html.window.onPopState.listen((event) async { - final shouldLogout = await _showLogoutDialog(); - if (shouldLogout) { - await apiService.logout(); - if (!mounted) return; - Navigator.pushNamedAndRemoveUntil( - context, - 'hrLogin', - (route) => false, - ); - } else { - // Push state back to prevent browser navigation - html.window.history.pushState(null, '', html.window.location.href); - } - }); _loadToken(); } @@ -181,8 +167,7 @@ class _hrDashboardState extends State with SingleTickerProviderStat } Widget _buildContent(BuildContext context) { - return PopScope( - canPop: false, + return SecurePopScope( child: Scaffold( // ✅ REMOVED 'Expanded' from directly inside body. body: Container( diff --git a/lib/presentation/policies.dart b/lib/presentation/policies.dart index 448b417..a7674d5 100644 --- a/lib/presentation/policies.dart +++ b/lib/presentation/policies.dart @@ -20,6 +20,7 @@ import 'package:intl/intl.dart'; import '../config/environment.dart'; import '../customAppBar/base_layout.dart'; import '../customAppBar/toastHelper.dart'; +import '../service/secure_pop_scope.dart'; import 'hrPolicyDetails.dart'; class policies extends StatefulWidget { @@ -331,23 +332,7 @@ class _policiesState extends State @override Widget build(BuildContext context) { return BaseLayout( - child: WillPopScope( - onWillPop: () async { - - final shouldLogout = await _showLogoutDialog(); - - if (shouldLogout) { - await apiService.logout(); - if (!mounted) return false; - - Navigator.pushNamedAndRemoveUntil( - context, - 'hrLogin', - (route) => false, - ); - } - return false; - }, + child: SecurePopScope( child: buildPoliciesBody( openEnrollment: openForEnrollmentList, activePolicies: activePoliciesList, diff --git a/lib/service/secure_pop_scope.dart b/lib/service/secure_pop_scope.dart new file mode 100644 index 0000000..27b62cf --- /dev/null +++ b/lib/service/secure_pop_scope.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:universal_html/html.dart' as html; + +import '../service/token_storage_service.dart'; +import '../service/api_service.dart'; + +class SecurePopScope extends StatefulWidget { + final Widget child; + + const SecurePopScope({Key? key, required this.child}) : super(key: key); + + @override + State createState() => _SecurePopScopeState(); +} + +class _SecurePopScopeState extends State { + final TokenStorageService _tokenService = TokenStorageService(); + late ApiService _apiService; + + @override + void initState() { + super.initState(); + _apiService = ApiService(context); + + // 🔥 Push dummy state (IMPORTANT for Firefox) + html.window.history.pushState(null, '', html.window.location.href); + + html.window.onPopState.listen((event) async { + await _handleBack(); + }); + } + + Future _handleBack() async { + final token = await _tokenService.getCurrentToken(); + + // If no token → force login + if (token == null || token.isEmpty) { + _redirectToLogin(); + return; + } + + final shouldLogout = await _showLogoutDialog(); + + if (shouldLogout) { + await _tokenService.clearAll(); + await _apiService.logout(); + _redirectToLogin(); + } else { + // 🔥 Re-push state (VERY IMPORTANT for Firefox) + html.window.history.pushState(null, '', html.window.location.href); + } + } + + void _redirectToLogin() { + if (!mounted) return; + + Navigator.pushNamedAndRemoveUntil( + context, + 'hrLogin', + (route) => false, + ); + } + + Future _showLogoutDialog() async { + return await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: const Text("Confirm Logout"), + content: const Text("Do you want to logout?"), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text("Cancel"), + ), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: const Text("Logout"), + ), + ], + ), + ) ?? + false; + } + + @override + Widget build(BuildContext context) { + return widget.child; // ❌ Don't rely on PopScope for web + } +}