import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:nhancepolicy/addons.dart'; import 'package:nhancepolicy/branch/branch_selection_page.dart'; import 'package:nhancepolicy/empReview.dart'; import 'package:nhancepolicy/empDetails.dart'; import 'package:nhancepolicy/presentation/preFileUpload.dart'; import 'package:nhancepolicy/hrHome.dart'; import 'package:nhancepolicy/hrVerify.dart'; import 'package:nhancepolicy/presentation/excelVerification.dart'; import 'package:nhancepolicy/presentation/postFileUpload.dart'; import 'package:nhancepolicy/presentation/cdList.dart'; import 'package:nhancepolicy/presentation/claims.dart'; import 'package:nhancepolicy/presentation/nonEBClaimsList.dart'; import 'package:nhancepolicy/presentation/pendingDependentApproval.dart'; import 'package:nhancepolicy/presentation/policies.dart'; import 'package:nhancepolicy/service/session/session_service.dart'; import 'package:nhancepolicy/service/token_storage_service.dart'; import 'package:nhancepolicy/home.dart'; import 'package:nhancepolicy/presentation/claims_overview/claims_overview_dashboard.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:nhancepolicy/hrLogin.dart'; import 'package:nhancepolicy/presentation/hrPolicyDetails.dart'; import 'package:nhancepolicy/oldPolicy.dart'; // import 'package:firebase_core/firebase_core.dart'; import 'presentation/cdTransactionDetails.dart'; import 'config/environment.dart'; import 'email_verify.dart'; import 'package:nhancepolicy/logger.dart'; Future startApp() async { // html.window.onBeforeUnload.listen((event) { // logDebug('Local Storage cleared by window'); // html.window.localStorage.clear(); // }); // Ask confirmation (browser may or may not show it again) // html.window.onBeforeUnload.listen((event) { // final e = event as html.BeforeUnloadEvent; // e.preventDefault(); // e.returnValue = ''; // }); // Always clear when user leaves (refresh/close) // html.window.onUnload.listen((event) { // html.window.localStorage.clear(); // }); // await dotenv.load(fileName: Environment.fileName); WidgetsFlutterBinding.ensureInitialized(); // final sessionService = SessionService(); // await sessionService.restoreSession(); // ⭐ IMPORTANT // Initialize token storage await TokenStorageService().initialize(); // await Firebase.initializeApp( // options: const FirebaseOptions( // apiKey: 'AIzaSyCSvDM5fG2blDBE69Cae3S-iYRwwNBy7xo', // appId: '1:1084115316849:web:ce28f12a426f285d6c6bd0', // messagingSenderId: '1084115316849', // projectId: 'nhance-ee8d1')); // await dotenv.load(fileName: Environment.fileName); // runApp(MaterialApp( // title: 'Nhance HR', // onGenerateTitle: (context) => "Nhance HR", // initialRoute: 'hrLogin', // debugShowCheckedModeBanner: false, // theme: ThemeData( // primaryColor: Color(0xFF00999E), // Primary theme color // scaffoldBackgroundColor: Colors.white, // colorScheme: ColorScheme.fromSeed( // seedColor: Color(0xFF00999E), // ), // textTheme: GoogleFonts.poppinsTextTheme(), // elevatedButtonTheme: ElevatedButtonThemeData( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF00999E), // Button background // ), // ), // ), // routes: { // 'phone': (context) => MyPhone(), // 'mailVerify': (context) => MyEmailVerify( // type: '', // value: '', // // ), // 'verify': (context) => MyVerify( // verificationId: '', // mobileNumber: '', // resendToken: null, // onResendCode: (String, int) {}, // ), // 'home': (context) => MyApp(), // 'hrLogin': (context) => MyHrLogin(), // // 'hrVerify': (context) => MyHrVerify( // // verificationId: '', // // mobileNumber: '', // // resendToken: null, // // onResendCode: (String, int) {}, // // ), // 'hrHome': (context) => MyHrHome(), // 'preFileUpload': (context) => const preFileUpload( // ClientId: '', // policyTypeId: '', // ClientPoliyId: '', // clientBranchId: '', // Token: '', // TokenType: '', // cardType: '', // cardPolicyNo: '', // cardInsurer_name: '', // cardPolicy_name: '', // cardPolicy_ExpDate: '', // total_premium: '', // ), // 'postFileUpload': (context) => const postFileUpload( // ClientId: '', // policyTypeId: '', // ClientPoliyId: '', // clientBranchId: '', // Token: '', // TokenType: '', // cardType: '', // cardPolicyNo: '', // cardInsurer_name: '', // cardPolicy_name: '', // cardPolicy_ExpDate: '', // total_premium: '', // ), // 'excelErrorScreen': (context) => const excelErrorScreen( // ClientId: '', // policy_no: '', // action: '', // created_at: '', // clientBranchId: '', // Token: '', // TokenType: '', // id: '' // ), // 'empDetails': (context) => empDetails(), // 'addOnsDetails': (context) => addOnsDetails(), // 'empReviewDetails': (context) => empReviewDetails(), // 'hrDashboard': (context) => hrDashboard(), // 'hrPolicyDetails': (context) => hrPolicyDetails( // ClientId: '', // policyTypeId: '', // ClientPoliyId: '', // clientBranchId: '', // Token: '', // TokenType: '', // cardType: '', // cardPolicyNo: '', // cardInsurer_name: '', // cardPolicy_name: '', // cardPolicy_ExpDate: '', // total_premium: '', // is_ecard_bulk_download_for_employee: 0, // ), // 'oldPolicy': (context) => oldPolicy(), // 'branchSelection': (context) => BranchSelectionPage(), // 'policies': (context) => policies(), // 'CdPoliciesList': (context) => CdPoliciesList(), // 'ClaimsPolicies': (context) => ClaimsPolicies( // empCode:'', // ), // 'cdTransactionDetails': (context) => cdTransactionDetails( // insurerName: '', // cdMasterAccountNo: '', // insurerId: '', // cd_ac_pk: '', // empClientId: '', // ), // }, // )); final tokenService = TokenStorageService(); final token = await tokenService.getCurrentToken(); runApp(MyApp(initialToken: token)); } /// Routes that require an authenticated HR session. /// Public auth-flow routes (hrLogin, mailVerify, branchSelection) stay excluded. const Set _authProtectedRoutes = { 'hrHome', 'hrDashboard', 'policies', 'pendingDependentApproval', 'claimsOverviewDashboard', 'ClaimsPolicies', 'nonEBClaimsList', 'CdPoliciesList', 'cdTransactionDetails', 'hrPolicyDetails', 'oldPolicy', 'preFileUpload', 'postFileUpload', 'excelErrorScreen', 'empDetails', 'addOnsDetails', 'empReviewDetails', }; String _normalizeRouteName(String routeName) { var name = routeName.trim(); if (name.startsWith('#')) { name = name.substring(1); } if (name.startsWith('/')) { name = name.substring(1); } // Flutter web may include query/hash fragments; keep only path segment. final hashIndex = name.indexOf('#'); if (hashIndex != -1) { name = name.substring(hashIndex + 1); } final queryIndex = name.indexOf('?'); if (queryIndex != -1) { name = name.substring(0, queryIndex); } return name; } bool _hasValidSession() { final token = TokenStorageService().getCurrentToken(); return token != null && token.trim().isNotEmpty; } class MyApp extends StatelessWidget { final String? initialToken; const MyApp({super.key, this.initialToken}); @override Widget build(BuildContext context) { final fallbackRoute = (initialToken == null || initialToken!.isEmpty) ? 'hrLogin' : 'hrHome'; return MaterialApp( title: 'Nhance HR', debugShowCheckedModeBanner: false, initialRoute: fallbackRoute, onGenerateInitialRoutes: (String initialRouteName) { final requested = _normalizeRouteName(initialRouteName); final resolved = (requested.isEmpty || requested == '/') ? fallbackRoute : requested; // Deep-link guard: unauthenticated access to protected URLs → HR login final targetRoute = (!_hasValidSession() && _authProtectedRoutes.contains(resolved)) ? 'hrLogin' : resolved; final builder = appRoutes[targetRoute] ?? appRoutes['hrLogin']!; return [ MaterialPageRoute( builder: builder, settings: RouteSettings(name: targetRoute), ), ]; }, theme: ThemeData( primaryColor: const Color(0xFF00999E), scaffoldBackgroundColor: Colors.white, colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF00999E), ), textTheme: GoogleFonts.poppinsTextTheme(), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF00999E), ), ), ), routes: appRoutes, ); } } final Map appRoutes = { 'mailVerify': (context) => MyEmailVerify( type: '', value: '', ), 'home': (context) => MyApp(), 'hrLogin': (context) => MyHrLogin(), // 'hrVerify': (context) => MyHrVerify( // verificationId: '', // mobileNumber: '', // resendToken: null, // onResendCode: (String, int) {}, // ), 'hrHome': (context) => AuthWrapper( builder: (_) => MyHrHome(), ), 'preFileUpload': (context) => AuthWrapper( builder: (_) => const preFileUpload( ClientId: '', policyTypeId: '', ClientPoliyId: '', clientBranchId: '', Token: '', TokenType: '', cardType: '', cardPolicyNo: '', cardInsurer_name: '', cardPolicy_name: '', cardPolicy_ExpDate: '', total_premium: '', ), ), 'postFileUpload': (context) => AuthWrapper( builder: (_) => const postFileUpload( ClientId: '', policyTypeId: '', ClientPoliyId: '', clientBranchId: '', Token: '', TokenType: '', cardType: '', cardPolicyNo: '', cardInsurer_name: '', cardPolicy_name: '', cardPolicy_ExpDate: '', total_premium: '', ), ), 'excelErrorScreen': (context) => AuthWrapper( builder: (_) => const excelErrorScreen( ClientId: '', policy_no: '', action: '', created_at: '', clientBranchId: '', Token: '', TokenType: '', id: ''), ), 'empDetails': (context) => AuthWrapper( builder: (_) => empDetails(), ), 'addOnsDetails': (context) => AuthWrapper( builder: (_) => addOnsDetails(), ), 'empReviewDetails': (context) => AuthWrapper( builder: (_) => empReviewDetails(), ), 'hrDashboard': (context) => AuthWrapper( builder: (_) => const policies(), ), 'claimsOverviewDashboard': (context) => AuthWrapper( builder: (_) => const ClaimsOverviewDashboard(), ), 'hrPolicyDetails': (context) => AuthWrapper( builder: (_) => hrPolicyDetails( ClientId: '', policyTypeId: '', ClientPoliyId: '', clientBranchId: '', Token: '', TokenType: '', cardType: '', cardPolicyNo: '', cardInsurer_name: '', cardPolicy_name: '', cardPolicy_ExpDate: '', total_premium: '', is_ecard_bulk_download_for_employee: 0, ), ), 'oldPolicy': (context) => AuthWrapper( builder: (_) => oldPolicy(), ), // Branch selection happens after login but before a branch token is set. 'branchSelection': (context) => BranchSelectionPage(), 'policies': (context) => AuthWrapper( builder: (_) => const policies(), ), 'pendingDependentApproval': (context) => AuthWrapper( builder: (_) => const PendingDependentApproval(), ), 'CdPoliciesList': (context) => AuthWrapper( builder: (_) => CdPoliciesList(), ), 'ClaimsPolicies': (context) => AuthWrapper( builder: (_) => const ClaimsPolicies( empCode: '', ), ), 'nonEBClaimsList': (context) => AuthWrapper( builder: (_) => const NonEBClaimsList( empCode: '', ), ), 'cdTransactionDetails': (context) => AuthWrapper( builder: (_) => cdTransactionDetails( insurerName: '', cdMasterAccountNo: '', insurerId: '', cd_ac_pk: '', empClientId: '', ), ), }; /// 🔐 Auth Wrapper — blocks protected pages until a valid session exists. /// Uses a builder so protected widgets are not constructed without a token. class AuthWrapper extends StatefulWidget { final WidgetBuilder builder; const AuthWrapper({super.key, required this.builder}); @override State createState() => _AuthWrapperState(); } class _AuthWrapperState extends State { late final bool _isAuthenticated; @override void initState() { super.initState(); _isAuthenticated = _hasValidSession(); if (!_isAuthenticated) { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; Navigator.pushNamedAndRemoveUntil( context, 'hrLogin', (route) => false, ); }); } } @override Widget build(BuildContext context) { if (!_isAuthenticated) { // Do not render protected UI while redirecting to login. return const Scaffold( body: Center( child: CircularProgressIndicator( color: Color(0xFF00999E), ), ), ); } return widget.builder(context); } }