import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:nhancepolicy/responsive.dart'; import 'package:nhancepolicy/service/api_service.dart'; import 'package:nhancepolicy/service/token_storage_service.dart'; import 'package:pinput/pinput.dart'; import 'dart:async'; import 'package:flutter/gestures.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:jwt_decode/jwt_decode.dart'; import '../customAppBar/toastHelper.dart'; import '../models/platform_helper_mobile.dart' if (dart.library.html) '../models/platform_helper_other.dart'; import 'branch/branch_selection_page.dart'; import 'config/environment.dart'; import 'package:nhancepolicy/logger.dart'; // import 'dart:html' as html; class MyEmailVerify extends StatefulWidget { final String type; // 'email' or 'mobile' final String value; const MyEmailVerify({super.key, required this.type, required this.value}); @override State createState() => _MyEmailVerifyState(); } class _MyEmailVerifyState extends State { late ApiService apiService; TextEditingController _otpController = TextEditingController(); final _formKey = GlobalKey(); late Timer _timer; int _secondsRemaining = 60; bool _isTimerRunning = false; dynamic enrollmentEmpClientBranchId; dynamic enrollmentEmpCodeString; dynamic enrollmentEmpPrimaryId; dynamic enrollmentGpaEmpName; dynamic enrollmentClient_id; dynamic enrollmentHrId; List enrollmentAllowed_modules = []; dynamic enrollmentEmp_status; dynamic empCodeString; dynamic empClientBranchId; dynamic empPrimaryId; dynamic gpaEmpName; dynamic empClientId; dynamic empHrId; List empAllowed_modules = []; dynamic _token; dynamic _postToken; dynamic clientName; dynamic clientLogo; dynamic emp_status; dynamic fCMToken; late String verificationId; bool _isLoading = false; late String _verificationId; dynamic empMobileNo; dynamic empEmailid; final tokenService = TokenStorageService(); @override void initState() { super.initState(); apiService = ApiService(context); startTimer(); } @override void dispose() { _timer.cancel(); super.dispose(); } void startTimer() { _isTimerRunning = true; _timer = Timer.periodic(Duration(seconds: 1), (Timer timer) { setState(() { if (_secondsRemaining > 0) { _secondsRemaining--; } else { _isTimerRunning = false; _timer.cancel(); } }); }); } void verifyOTP(String otp) async { try { if(otp.isEmpty){ ToastHelper.showErrorToast(context, 'Please enter OTP'); return; } final Map payload = (widget.type == 'mobile') ? {'otp': _otpController.text, 'mobile_no': widget.value} : {'otp': _otpController.text, 'email': widget.value}; final response = await http.post( Uri.parse(Environment.apiUrl + 'getVerifiedHrData'), body: json.encode(payload), headers: { 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', HttpHeaders.contentTypeHeader: 'application/json', }, ); if (response.statusCode == 200) { setState(() { _isLoading = false; }); Map data = json.decode(response.body); logDebug('Response data: $data'); final String mainStatus = data['status'] ?? 'failed'; final Map postEnrollment1 = data['post_enrollment'] ?? {}; final String postStatus = postEnrollment1['status'] ?? 'failed'; /// ❌ BLOCK if BOTH failed if (mainStatus != 'success' && postStatus != 'success') { ToastHelper.showErrorToast( context, postEnrollment1['message'], ); return; } // ✅ Safely extract preEnrollmentData List preEnrollmentData = []; if (data['data'] is List) { preEnrollmentData = data['data']; } // ✅ Safely extract postEnrollmentData final postEnrollment = data['post_enrollment'] ?? {}; List postEnrollmentData = []; if (postEnrollment['data'] is List) { postEnrollmentData = postEnrollment['data']; } // Extract enrollment data // List preEnrollmentData = data['data'] ?? []; // Map postEnrollment = data['post_enrollment'] ?? {}; // List postEnrollmentData = postEnrollment['data'] ?? []; // Save to global storage final tokenStorage = TokenStorageService(); await tokenStorage.saveEnrollmentData( preEnrollmentData, postEnrollmentData, ); // Navigate to branch selection // 🔥 Get all valid branches final branches = tokenStorage.getCombinedBranches(); if (branches.length == 1) { // ✅ ONLY ONE BRANCH → AUTO SELECT final singleBranch = branches.first; // Save selected branch await tokenStorage.saveSelectedBranch(singleBranch); final decodedToken = tokenStorage.getDecodedToken(); final token = tokenStorage.getCurrentToken(); if (decodedToken == null || token == null) { ToastHelper.showErrorToast(context, 'Invalid token data'); return; } // Save decoded session values await tokenStorage.saveDecodedSessionData(decodedToken, token); if (!mounted) return; // 🚀 DIRECTLY GO TO POLICIES Navigator.pushReplacementNamed(context, 'policies'); } else { // 🧭 MULTIPLE BRANCHES → SHOW SELECTION PAGE if (!mounted) return; Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => const BranchSelectionPage(), ), ); } // logDebug('data: $data'); // _token = data['data']; // String status = data['status']; // // // Directly access the post_enrollment data // Map post = data['post_enrollment']; // logDebug('post: $post'); // // _postToken = post['data']; // String postStatus = post['status']; // // if (postStatus == 'success') { // setState(() { // _isLoading = false; // }); // postSuccessData(post, data); // } else if (status == 'success') { // setState(() { // _isLoading = false; // }); // enrollmentSuccessData(data); // } else { // setState(() { // _isLoading = false; // }); // ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again'); // // Show a Snackbar if the OTP is invalid // logDebug('Invalid OTP. Please try again'); // } } else if (response.statusCode == 401) { setState(() { _isLoading = false; }); await tokenService.clearAll(); // 🔐 clears flutter_secure_storage ToastHelper.showErrorToast(context, 'Session Out'); if (!context.mounted) return; Navigator.pushNamedAndRemoveUntil( context, 'hrLogin', (route) => false, ); } else if (response.statusCode == 429) { setState(() { _isLoading = false; }); Map data = json.decode(response.body); if (data.containsKey('error')) { final message = data['error']['message']; ToastHelper.showErrorToast(context, message); } else { final message = data['message']; ToastHelper.showErrorToast(context, message); } } else if (response.statusCode == 403) { setState(() { _isLoading = false; }); Map data = json.decode(response.body); if (data.containsKey('error')) { final message = data['error']['message']; ToastHelper.showErrorToast(context, message); } else { final message = data['message']; ToastHelper.showErrorToast(context, message); } } else if (response.statusCode == 451) { setState(() { _isLoading = false; }); Map data = json.decode(response.body); if (data.containsKey('error')) { final message = data['error']['message']; ToastHelper.showErrorToast(context, message); } else { final message = data['message']; ToastHelper.showErrorToast(context, message); } } else { setState(() { _isLoading = false; }); throw Exception('Failed to load data'); } } catch (e) { setState(() { _isLoading = false; }); logDebug('Error: $e'); ToastHelper.showWarningToast(context, 'Something went wrong'); // Show a Snackbar if there's an error while verifying OTP logDebug('Failed to verify OTP. Please try again.'); } } 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']); // Decode the JWT token received from the API response Map? decodedToken = Jwt.parseJwt(post['data']); logDebug('postdecodedToken : $decodedToken'); empClientBranchId = decodedToken['ref_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); empClientId = decodedToken['client_id']; prefs.setString('empClientId', empClientId); empHrId = decodedToken['id']; prefs.setString('empHrId', empHrId); empAllowed_modules = decodedToken['allowed_modules']; prefs.setString('empAllowed_modules', jsonEncode(empAllowed_modules)); // emp_status = decodedToken['emp_status']; // prefs.setString('emp_status', emp_status); // getClientLogoAndDetails(); logDebug('Successfully Login'); // Redirect to another page } if (status == 'success') { 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']); logDebug('postenrolldecodedToken : $decodedToken'); enrollmentEmpClientBranchId = decodedToken['ref_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); enrollmentHrId = decodedToken['id']; prefs.setString('enrollmentHrId', enrollmentHrId); enrollmentAllowed_modules = decodedToken['allowed_modules']; prefs.setString( 'enrollmentAllowed_modules', jsonEncode(enrollmentAllowed_modules)); // enrollmentEmp_status = decodedToken['emp_status']; // prefs.setString('enrollmentEmp_status', enrollmentEmp_status); } getClientLogoAndDetails(empClientId, empClientBranchId, enrollmentClient_id, enrollmentEmpClientBranchId, post['data']); final SharedPreferences prefs = await SharedPreferences.getInstance(); final _postToken = prefs.getString('_postToken'); if (_postToken != null && _postToken.isNotEmpty) { ToastHelper.showSuccessToast(context, 'Successfully Logged In'); // if (emp_status == 'enrolled' || emp_status == 'active') { Navigator.pushReplacementNamed(context, 'policies'); // } else { // Navigator.pushReplacementNamed(context, 'empDetails'); // } } } void enrollmentSuccessData(data) async { 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']); logDebug('enrolldecodedToken : $decodedToken'); enrollmentEmpClientBranchId = decodedToken['ref_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); enrollmentHrId = decodedToken['id']; logDebug("enrollmentHrId- $enrollmentHrId"); prefs.setString('enrollmentHrId', enrollmentHrId); enrollmentAllowed_modules = decodedToken['allowed_modules']; prefs.setString( 'enrollmentAllowed_modules', jsonEncode(enrollmentAllowed_modules)); // enrollmentEmp_status = decodedToken['emp_status']; // prefs.setString('enrollmentEmp_status', enrollmentEmp_status); getClientLogoAndDetails(empClientId, empClientBranchId, enrollmentClient_id, enrollmentEmpClientBranchId, data['data']); logDebug('Successfully Login'); // Redirect to another page final enrollToken = prefs.getString('enrollToken'); if (enrollToken != null && enrollToken.isNotEmpty) { ToastHelper.showSuccessToast(context, 'Successfully Logged In'); // if (emp_status == 'enrolled' || emp_status == 'active') { // Navigator.pushReplacementNamed(context, 'home'); // } else { Navigator.pushReplacementNamed(context, 'policies'); // } } } void _resendOTP() { setState(() { _secondsRemaining = 60; _isTimerRunning = true; }); startTimer(); verifyMobileAndEmailNumber(); } Future verifyMobileAndEmailNumber() async { try { // var enteredMobileNumber = mobileController.text; final Map payload = (widget.type == 'mobile') ? {'mobile_number': widget.value} : {'email': widget.value}; String apiEndpoint = (widget.type == 'mobile') ? Environment.apiUrl + 'verifyHrWithMobileNumber' : Environment.apiUrl + 'verifyHrWithEmail'; final response = await http.post( Uri.parse(apiEndpoint), body: json.encode(payload), headers: { 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', HttpHeaders.contentTypeHeader: 'application/json', }, ); if (response.statusCode == 200) { Map data = json.decode(response.body); bool userVerification = data['data']['user_verification']; String message = data['data']['message']; if (userVerification) { ToastHelper.showSuccessToast( context, 'Verification code sent to ${widget.value}'); } else { ToastHelper.showErrorToast(context, message); logDebug('Invalid mobile number'); } } else if (response.statusCode == 403) { setState(() { _isLoading = false; }); Map data = json.decode(response.body); final message = data['message']; ToastHelper.showErrorToast(context, message); } else if (response.statusCode == 451) { setState(() { _isLoading = false; }); Map data = json.decode(response.body); final message = data['error']['message']; ToastHelper.showErrorToast(context, message); } else { ToastHelper.showErrorToast(context, 'Something went wrong'); throw Exception('Failed to verify mobile number'); } } catch (e) { ToastHelper.showErrorToast(context, 'Something went wrong'); logDebug('Error: $e'); } } Future getClientLogoAndDetails(empClientId, empClientBranchId, enrollmentClientId, enrollmentClientBranchId, token) async { var url = Uri.parse(Environment.apiUrl + 'getClientDetails?post_client_id=$empClientId&post_branch_id=$empClientBranchId&pre_client_id=$enrollmentClientId&pre_branch_id=$enrollmentClientBranchId'); try { var response = await http.get( url, headers: { 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', 'Authorization': 'Bearer $token', // Add token to the Authorization header }, ); if (response.statusCode == 200) { // logDebug('response.statusCode == 200'); Map data = json.decode(response.body); // logDebug(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(() { // dynamic clientDetails = data['data']; // logDebug(clientDetails); clientName = clientDetails['client']['client_name']; logDebug(clientName); clientLogo = clientDetails['client']['client_logo']; logDebug(clientLogo); }); } else { // Handle other status messages if needed // ToastHelper.showErrorToast( // context, 'API request failed with status: ${data['status']}'); logDebug('API request failed with status: ${data['status']}'); } } else { // Handle other status codes // ToastHelper.showErrorToast( // context, 'Request failed with status: ${response.statusCode}'); logDebug('Request failed with status: ${response.statusCode}'); } } catch (e) { // Handle exceptions logDebug('Exception occurred: $e'); } } @override Widget build(BuildContext context) { final Size _size = MediaQuery.of(context).size; final defaultPinTheme = PinTheme( width: 56, height: 56, textStyle: TextStyle( fontSize: 20, color: Color.fromRGBO(30, 60, 87, 1), fontWeight: FontWeight.w600, ), decoration: BoxDecoration( border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)), borderRadius: BorderRadius.circular(20), ), ); final focusedPinTheme = defaultPinTheme.copyDecorationWith( border: Border.all(color: Color.fromRGBO(114, 178, 238, 1)), borderRadius: BorderRadius.circular(8), ); final submittedPinTheme = defaultPinTheme.copyWith( decoration: defaultPinTheme.decoration?.copyWith( color: Color.fromRGBO(234, 239, 243, 1), ), ); return Scaffold( body: Container( width: double.infinity, height: _size.height, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ Color(0xFF00B6AC), Color(0xFF83E0DE), Color(0xFF01B4A8), ], ), ), child: Center( child: Container( width: double.infinity, // ✅ fixed web width height: _size.height, // ✅ fixed web height (IMPORTANT) margin: const EdgeInsets.all(60), clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(40)), ), child: Row( children: [ // ================= LEFT IMAGE ================= Expanded( flex: 5, child: ClipRRect( borderRadius: const BorderRadius.only( topLeft: Radius.circular(40), bottomLeft: Radius.circular(40), ), child: Image.asset( 'assets/hrLogin.png', width: double.infinity, height: double.infinity, fit: BoxFit.cover, ), ), ), // ================= RIGHT FORM ================= Expanded( flex: 7, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( 'assets/Nhance-Logo-Final 1.png', width: 300, height: 100, ), SizedBox(height: 15), Container( margin: Responsive.isDesktop(context) ? EdgeInsets.symmetric(horizontal: 150) : EdgeInsets.symmetric(horizontal: 0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "Welcome to Nhance", style: GoogleFonts.poppins( fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), ), SizedBox(height: 15), Container( margin: Responsive.isDesktop(context) ? EdgeInsets.symmetric(horizontal: 150) : EdgeInsets.symmetric(horizontal: 0), child: RichText( textAlign: TextAlign.center, text: TextSpan( text: "Please enter the one-time usage code sent to your ", style: TextStyle( fontSize: 12, height: 1.5, color: Color(0xFF000000)), children: [ TextSpan( text: widget.type == 'mobile' ? 'mobile number ${widget.value}' : 'Email Id ${widget.value}', ), TextSpan( text: widget.type == 'mobile' ? ' (Change Mobile)' : ' (Change Email)', style: TextStyle( fontSize: 12, color: Color( 0xFFE26728)), // Change color as desired recognizer: TapGestureRecognizer() ..onTap = () { // Navigate to the page where the user can change the phone number Navigator.pushNamed( context, 'hrLogin'); }, ), ], ), ), ), SizedBox(height: 15), Container( margin: Responsive.isDesktop(context) ? EdgeInsets.symmetric(horizontal: 150) : EdgeInsets.symmetric(horizontal: 0), child: Pinput( length: 6, // defaultPinTheme: defaultPinTheme, // focusedPinTheme: focusedPinTheme, // submittedPinTheme: submittedPinTheme, showCursor: true, controller: _otpController, ), ), SizedBox(height: 15), Container( margin: Responsive.isDesktop(context) ? EdgeInsets.symmetric(horizontal: 150) : EdgeInsets.symmetric(horizontal: 0), child: Row( mainAxisAlignment: MainAxisAlignment .end, // Align text to the right children: [ _isTimerRunning ? Text( "Resend OTP in $_secondsRemaining seconds", style: GoogleFonts.poppins( color: Colors.black), ) : InkWell( onTap: () { _resendOTP(); }, child: Text( "Resend OTP", style: GoogleFonts.poppins( color: Colors.blue), ), ), ], ), ), SizedBox(height: 15), Container( margin: Responsive.isDesktop(context) ? EdgeInsets.symmetric(horizontal: 150) : EdgeInsets.symmetric(horizontal: 0), child: SizedBox( width: double.infinity, height: 45, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF00989E), shape: RoundedRectangleBorder( borderRadius: 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)), ), ), ), ), SizedBox(height: 15), Container( alignment: Alignment.bottomCenter, padding: EdgeInsets.symmetric(vertical: 8), child: RichText( textAlign: TextAlign.center, text: TextSpan( text: 'By continuing, you agree with our ', style: GoogleFonts.poppins( color: Colors.black, fontSize: 9, ), children: [ TextSpan( text: 'privacy policy ', style: GoogleFonts.poppins( color: Color(0xFF00989E), fontSize: 9, ), ), TextSpan( text: 'and ', style: GoogleFonts.poppins( color: Colors.black, fontSize: 9, ), ), TextSpan( text: 'terms of use', style: GoogleFonts.poppins( color: Color(0xFF00989E), fontSize: 9, ), ), ], ), ), ), ], ), ), ), ), ], ), ), ))); } }