Search code examples
javascriptreactjsauthenticationone-time-password

React JS Passing The Props From Mail Verify Page to OTP input field page does not work


So that I tried to integrate the API for my mail verify page, and OTP code input field page. So that when I pass the props of email of user from the mail verify page, with this <VerifyAccount email={email} profileId={profileId} /> to OTP input field page, the props works finely and we can see the email as I am putting it in the text

<p>Privacy and security is our top priority. Kindly confirm your account by entering the verification code sent to your email 
<p className='text-blue-500'> {email}</p>
</p>

but the problem is, the OTP page is showing in my mail input verify page, as it shown in the picture:it shows in my mail verify page

which I don't want this happen.

And if I make a ternary logic {showVerifyAccount && <VerifyAccount email={email} profileId={profileId} />} * the props does not show, I can't see the email and also can not integrate the API for finding the profileId which should be match to the OTP code that is being input by the user.

Here is my mail verify page code :

import React, { useState } from 'react'
import Logo from '../../asset/bb1.png';
import { FcGoogle } from 'react-icons/fc';
import { AiFillApple, AiOutlineCheckCircle, AiOutlineCloseCircle } from 'react-icons/ai'; 
import { RiEyeOffFill } from 'react-icons/ri'; 
import { RiEyeFill  } from 'react-icons/ri'; 
import { useNavigate } from 'react-router-dom';
import { TiTick } from 'react-icons/ti';
import VerifyAccount from './VerifyAccount';
import { Link } from 'react-router-dom';


const Register = () => {

  const navigate = useNavigate();
  const [showPassword, setShowPassword] = useState(false);
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [emailError, setEmailError] = useState('');
  const [passwordError, setPasswordError] = useState('');
  const [confirmPasswordError, setConfirmPasswordError] = useState('');
  const [fillOutError, setFillOutError] = useState('');
  const [hasCapitalLetter, setHasCapitalLetter] = useState(false);
  const [hasNumber, setHasNumber] = useState(false);
  const [hasSymbol, setHasSymbol] = useState(false);
  const [isMinLength, setIsMinLength] = useState(false);
  const [passwordErrorVisible, setPasswordErrorVisible] = useState(true);
  const [profileId, setProfileId] = useState(null);
  const [showVerifyAccount, setShowVerifyAccount] = useState(false);



  const validateEmail = () => {
    const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
    if (!email.match(emailRegex)) {
      setEmailError(
        <div className='flex flex-row items-center mr-1'>
        <AiOutlineCloseCircle /> Invalid email format
      </div>
        );
    } else {
      setEmailError('');
    }
  };

  const validatePassword = () => {
    const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

    if (!password.match(passwordRegex)) {
      setPasswordError('At least 8 characters, one uppercase letter, one lowercase letter, one number, and one special character.');
      setIsMinLength(false);
      setHasCapitalLetter(false);
      setHasNumber(false);
      setHasSymbol(false);
      setPasswordErrorVisible(true); // Show the error message

    } else {
      setPasswordError('');
      setIsMinLength(true);
      setPasswordErrorVisible(false); // Hide the error message

    }

    if (/[A-Z]/.test(password)) {
      setHasCapitalLetter(true);
    } else {
      setHasCapitalLetter(false);
    }

    if (/\d/.test(password)) {
      setHasNumber(true);
    } else {
      setHasNumber(false);
    }

    if (/[!@#$%^&*]/.test(password)) {
      setHasSymbol(true);
    } else {
      setHasSymbol(false);
    }

    setTimeout(() => {
      setPasswordErrorVisible(false);
    }, 300000);
  };

  const validateConfirmPassword = () => {
    if (password !== confirmPassword) {
      setConfirmPasswordError(
        <div className='flex flex-row items-center mr-1'>
        <AiOutlineCloseCircle /> Password does not match
      </div>
      );
    } else {
      setConfirmPasswordError('');
    }
  };

  const handleCreateAccount = (e) => {
    e.preventDefault();
    if (!email || !password || !confirmPassword) {
      setFillOutError('You should fill out all of the registration form.');
      return;
    }
    
    // If any error messages are set, return early
    if (emailError || passwordError || confirmPasswordError) {
      return;
    }


    let objMailVerify = {email, password}
    console.log(objMailVerify)
    fetch('https://api.backend.ca/api/v1/auth/verify-email', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(objMailVerify),
      }).then((res)=> {
         alert('succes') 
         navigate('/verify');
      }).catch((err)=>{
          alert('failed'+ err.message)
      })  
      

  
}

const togglePasswordVisibility = () => {
  setShowPassword(!showPassword);
};
  return (
    
      <div className='w-full relative h-screen  flex items-start bg-[#0066f6] '>
          <div className='relative w-1/2 h-full flex flex-col'>
            <div className='absolute top-[116.06px] left-[110px] flex flex-col'>
            <img src={Logo} alt='logo ini' className='w-[539px] h-[311.87px] '/>  
            </div>          
          </div>
          {/* <div className='relative  md:w-1/2 h-auto  flex flex-col '> */}
      <div className='md:w-[498px] w-80 h-auto md:left-[720px] bg-white flex flex-col p-3 md:p-10 
      rounded-md md:absolute md:mr-4 my-3 mr-10 
        items-center'>
        <h1 className='text-[21px] md:-mt-5 mt-3 text-[#001533] font-semibold'>Welcome!</h1>
        <h1 className='text-[21px] -mt-4 text-[#001533] font-semibold
        mb-1'>Time to create an account</h1>
        <div className='border-gray-400 border rounded-lg 
        py-1 px-4 w-full h-[35px] mb-2 items-center
        justify-center cursor-pointer
        flex flex-row'>
        <FcGoogle className='w-[30px] h-[30px]'/>         
        <p className='text-sm md:text-[13px] font-bold ml-9'> Sign Up With Google</p>
        </div>
        <div className='border-gray-400 border rounded-lg 
        py-1 px-4 w-full h-[35px] mb-2 items-center
        justify-center
        flex flex-row cursor-pointer'>
        <AiFillApple className='w-[30px] h-[30px]'/>
        <p className='text-sm md:text-[13px] font-bold ml-9'>Sign Up With Apple</p>
        </div>
        <div className="mt-1 w-full flex items-center">
            <hr className="flex-grow md:w-[115px] w-[115px] border-gray-400" />
            <span className="text-[#41618E] md:text-[15px] 
            text-center mx-2">Sign Up With Email</span>
            <hr className="flex-grow md:w-[115px] w-[115px] border-gray-400" />
          </div>
    <form onSubmit={handleCreateAccount}>
          <div className='w-full  flex flex-col mb-3 text-left'>
      <label htmlFor="email" className="text-black">Email Address</label>
            <input 
              type='email'
              id="email"
              placeholder='[email protected]'
              className='w-full text-[#666666] text-[16.26px] bg-gray-200 p-3 my-2 border-none outline-none focus:outline-none '
              value={email}
              onChange={(e) =>{ 
                setEmail(e.target.value)
                setFillOutError('');
                setEmailError('');
                }}
              onBlur={validateEmail}
            />
                    {emailError && <p className='text-red-500 text-sm'>{emailError}</p>}
      

                    <label htmlFor='password' className='text-black'>
  Password
</label>
<div className='relative'>
  <div className='relative w-full'>
    <input
      type={showPassword ? 'text' : 'password'}
      id='password'
      placeholder='******'
      className='w-full text-black text-sm bg-gray-200 p-3 my-2 border-none outline-none focus:outline-none'
      value={password}
      onChange={(e) => { setPassword(e.target.value)
        // setFillOutError('')
        validatePassword(e.target.value); 
        // setIsMinLength(false);
        // setIsMinLength(false);
        //     setHasCapitalLetter(false);
        //     setHasNumber(false);
        //     setHasSymbol(false);
        }}
        
      onBlur={validatePassword}
    />
    <span
      className='absolute right-4 top-1/2 transform -translate-y-1/2 cursor-pointer'
      onClick={togglePasswordVisibility}
    >
      {showPassword ? <RiEyeOffFill /> : <RiEyeFill />}
    </span>
  </div>
  <div className='text-sm text-red-500'>
  {passwordErrorVisible && passwordError && (
          <p className='flex flex-row items-center'>
            {isMinLength ? <span className='text-green-500 text-[8px] flex flex-row'> <TiTick /> 8 characters, </span> : <span className='text-red-500 text-[10px] flex flex-row items-center'> <AiOutlineCloseCircle/> 8 characters,</span>}{' '}
            {hasCapitalLetter ? <span className='text-green-500 text-[8px] flex flex-row items-center'> <AiOutlineCheckCircle/> 1 uppercase letter, </span> : <span className='text-red-500 text-[10px] flex flex-row items-center'> <AiOutlineCloseCircle/> 1 uppercase letter,</span>}{' '}
            {hasNumber ? <span className='text-green-500 flex text-[8px] flex-row items-center'><AiOutlineCheckCircle/> 1 number, </span> : <span className='text-red-500 text-[10px] flex flex-row items-center'> <AiOutlineCloseCircle/> 1 number,</span>}{' '}
            {hasSymbol ? <span className='text-green-500 flex text-[8px] flex-row items-center'><AiOutlineCheckCircle/> 1 symbol </span> : <span className='text-red-500 text-[10px] flex flex-row items-center'> <AiOutlineCloseCircle/> 1 symbol</span>}
          </p>
        )}
      </div>
</div>

<label htmlFor='confirmPassword' className='text-black'>
  Confirm Password
</label>
<div className='relative'>
  <div className='relative w-full'>
    <input
      type={showPassword ? 'text' : 'password'}
      id='confirmPassword'
      placeholder='******'
      className='w-full text-black text-sm bg-gray-200 p-3 my-2 border-none outline-none focus:outline-none'
      value={confirmPassword}
      onChange={(e) =>  {
      setConfirmPassword(e.target.value)
        setFillOutError('')
        setConfirmPasswordError('')
        }}
      onBlur={validateConfirmPassword}
    />
    <span
      className='absolute right-4 top-1/2 transform -translate-y-1/2 cursor-pointer'
      onClick={togglePasswordVisibility}
    >
      {showPassword ? <RiEyeOffFill /> : <RiEyeFill />}
    </span>
  </div>
  {confirmPasswordError && (
    <p className='text-red-500 text-sm'>{confirmPasswordError}</p>
  )}
  {fillOutError && (
  <p className='text-red-500 text-sm'>
    {fillOutError}
  </p>
)}

</div>


      </div>
      <div className='w-full flex flex-col '>
      <button type='submit'
        className='w-full hover:bg-blue-800 text-white bg-blue-950 rounded-md p-2 text-center flex items-center justify-center'
        onClick={() => setShowVerifyAccount(true)}
>
        Create Your Account
      </button>
        {/* {showVerifyAccount && <VerifyAccount email={email} profileId={profileId} />} */}
        <VerifyAccount email={email} profileId={profileId} />
      </div>
      <div className='w-full flex items-center justify-center
      mt-2'>
        <p className='text-sm font-normal text-black'>
        Alread have an account here? 
        <span className='font-semibold
        underline underline-offset-2
        hover:cursor-pointer'> Login Here</span>
        </p>
      </div>
      </form>
        </div>


      </div>
  )
}

export default Register

and below is my OTP page code:

import React, { useState, useRef } from 'react';
import Logo from '../../asset/bb1.png';
import { FcGoogle } from 'react-icons/fc';
import { AiFillApple } from 'react-icons/ai'; 
import { RiEyeOffFill } from 'react-icons/ri'; 
import { RiEyeFill } from 'react-icons/ri'; 
import { useNavigate } from "react-router-dom";
import { useLocation } from 'react-router-dom';

const VerifyAccount = ({email, profileId }) => {
  const { state } = useLocation();
  // const email = state ? state.email : '';
  const navigate = useNavigate();
  const [otp, setOtp] = useState(['', '', '', '']);
  const [loading, setLoading] = useState(false);
  const otpInputs = useRef([]);


  const handleInputChange = (index, value) => {
    const newOtp = [...otp];
    newOtp[index] = value;
    setOtp(newOtp);

    if (index < 3 && value !== '') {
      otpInputs.current[index + 1].focus();
    }
  };

  const isOtpIncomplete = otp.some((digit) => digit === '');

  const handleCreateAccount = async () => {
    if (isOtpIncomplete || !profileId) {
      alert('Please enter the complete OTP code and ensure you have a valid profileId.');
      return;
    }
  
    try {
      setLoading(true);
  
      const response = await fetch('https://api.borrow4less.ca/api/v1/auth/verify-email-otp'+profileId, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          profileId, 
          otp: otp.join(''),
        }),
      });
  
      if (response.ok) {
        // API call was successful, navigate to the next page
        navigate('/details-register');
      } else {
        // Handle error cases
        console.error('OTP verification failed:', response.status, response.statusText);
        const responseBody = await response.json();
        console.error('Error details:', responseBody);
      }
    } catch (error) {
      // Handle network or other errors
      console.error('Error verifying OTP:', error);
      // You might want to show an error message to the user
      // For example: setError('An error occurred. Please try again.');
    } finally {
      setLoading(false);
    }
  };
  
  
  
  
  return (
    <div className='w-full relative h-screen flex items-start bg-[#0066f6] '>
      <div className='relative md:w-1/2 md:h-full flex md:flex-col'>
        <div className='absolute top-[30%] left-[20%] flex flex-col'>
          <img src={Logo} alt='logo ini' className='w-[500px] h-[311.87px] '/>  
        </div>          
      </div>
      <div className='relative w-1/2 h-auto flex flex-col mt-2'>
        <div className='md:w-3/4 md:h-auto bg-white md:flex md:flex-col p-10 md:p-10 rounded absolute md:right-[30%] ml-12 mt-8 items-center'>
          <h1 className='mb-4 text-xl text-black font-semibold'>Verify your account!</h1>
          <p>
            Privacy and security is our top priority. Kindly confirm your account by entering the verification code sent to your email 
            <p className='text-blue-500'> {email}</p>
          </p>
          <div className='flex flex-row items-center mt-20 mb-20'>
            {otp.map((digit, index) => (
              <input
                key={index}
                ref={(input) => (otpInputs.current[index] = input)}
                className='border border-black w-12 h-16 p-4 mx-2 rounded-sm text-center'
                type='text'
                maxLength='1'
                value={digit}
                onChange={(e) => handleInputChange(index, e.target.value)}
              />
            ))}
          </div>
          <div className='w-full flex flex-col'>
            <div className='w-full'>
              <button
                className='w-full hover:bg-blue-800 text-white bg-blue-950 rounded-md p-3 text-center flex items-center justify-center'
                onClick={handleCreateAccount}
                disabled={loading}
              >
                {loading ? 'Verifying...' : 'Create Your Account'}
              </button>
            </div>
          </div>
          <div className='w-full flex items-center justify-center mt-2'>
            <p className='text-sm font-normal text-black'>
              Didn’t get a code?  
              <span className='font-semibold text-blue-500 hover:cursor-pointer'> Resend</span>
            </p>
          </div>
        </div>
      </div>
    </div>
  );
}

export default VerifyAccount;

Any help here could be really appreciated, about how to make the OTP input field page does not show in my mail verify page, but the prop is working finely when user moved from mail verify to the OTP input field page.


Solution

  • There is solution for this, to pass the props for displaying the email and make a request body to get the profileId properties, we can use localStorage.