The frontend part is very easy compared to the backend part. All I need to do is create a modal, and use it to send data twice.
To create the modal, I copied some code, the classNames for the encapsulation of a modal, from the MessageModal component in my earlier project Chat-Nat.
I'll add a "Forgot Password?" button on the login page, and set the onClick handler to open the modal
I need to use a boolean state to denote whether the OTP has been sent to the user's email before asking for it. I'm naming the state isOTPSent
Here are a few components and hooks that I'm reusing from the existing frontend of this project:
Here's the entire code for the modal:
// src/components/PasswordResetModal.tsx import React, { useState } from "react" import AuthForm from "./AuthForm"; import FormInput from "./FormInput"; import Box from "./Box"; import { useAxios } from "../hooks/useAxios"; interface FormData { email: string, new_password: string, otp: string, } interface Props { isVisible: boolean, onClose: () => void, } const PasswordResetModal: React.FC<Props> = ({ isVisible, onClose }) => { const [formData, setFormData] = useState<FormData>({ email: "", new_password: "", otp: "" }); const [isLoading, setLoading] = useState<boolean>(false); const [isOTPSent, setOTPSent] = useState<boolean>(false); const { apiReq } = useAxios(); const handleClose = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => { if ((e.target as HTMLElement).id === "wrapper") { onClose(); // could have setOTPSent(false), but avoiding it in case user misclicks outside } }; const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value, }); }; const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); setLoading(true); if (!isOTPSent) { // first request for sending otp, const response = await apiReq<unknown, FormData>("post", "/api/reset-password", formData) if (response) { alert("OTP has been sent to your email"); setOTPSent(true); } } else { // then using otp to change password const response = await apiReq<unknown, FormData>("put", "/api/reset-password", formData) if (response) { alert("Password has been successfully reset\nPlease log in again"); // clear the form setFormData({ email: "", otp: "", new_password: "", }) // close modal onClose(); } } setLoading(false); }; if (!isVisible) return null; return ( <div id="wrapper" className="fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm flex justify-center items-center" onClick={handleClose}> <Box title="Password Reset"> <AuthForm submitHandler={handleSubmit} isLoading={isLoading} buttonText={isOTPSent ? "Change Password" : "Send OTP"}> <FormInput id="email" label="Your email" type="email" value={formData.email} changeHandler={handleChange} isRequired /> {isOTPSent && (<> <FormInput id="otp" label="OTP" type="text" value={formData.otp} changeHandler={handleChange} isRequired /> <FormInput id="new_password" label="New Password" type="password" value={formData.new_password} changeHandler={handleChange} isRequired /> </>)} </AuthForm> </Box> </div> ) } export default PasswordResetModal
And here's how the conditional rendering of the modal is handled in the Login form
// src/pages/auth/Login.tsx import PasswordResetModal from "../../components/PasswordResetModal"; const Login: React.FC = () => { const [showModal, setShowModal] = useState<boolean>(false); return ( <Section> <Box title="Login"> <div className="grid grid-flow-col"> {/* link to the register page here */} <button type="button" onClick={() => setShowModal(true)} className="text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-2 text-center me-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800"> Forgot Password? </button> <PasswordResetModal isVisible={showModal} onClose={() => setShowModal(false)} /> </div> </Box> </Section> )
We're done! Or so I thought.
While running the app in my development environment, I discovered a bug where the emails wouldn't go through if the backend has been running since a long time.
We'll fix this bug in the next post
The above is the detailed content of Password Reset Feature: Frontend. For more information, please follow other related articles on the PHP Chinese website!