updated forget password functionality with backend

This commit is contained in:
2025-08-23 14:29:25 +05:30
parent c86509e812
commit 1018c42500
10 changed files with 519 additions and 247 deletions

View File

@@ -38,11 +38,13 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
const handleCreateNewProject = async () => {
const token = localStorage.getItem("token");
const refreshToken = localStorage.getItem("refreshToken")
console.log('refreshToken: ', refreshToken);
const refreshToken = localStorage.getItem("refreshToken");
console.log("refreshToken: ", refreshToken);
try {
const projectId = generateProjectId();
useSocketStore.getState().initializeSocket(email, organization, token, refreshToken);
useSocketStore
.getState()
.initializeSocket(email, organization, token, refreshToken);
//API for creating new Project
// const project = await createProject(
@@ -59,12 +61,12 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
projectUuid: projectId,
};
console.log('projectSocket: ', projectSocket);
console.log("projectSocket: ", projectSocket);
if (projectSocket) {
const handleResponse = (data: any) => {
if (data.message === "Project created successfully") {
setLoadingProgress(1)
navigate(`/${data.data.projectId}`);
setLoadingProgress(1);
navigate(`/projects/${data.data.projectId}`);
}
projectSocket.off("v1-project:response:add", handleResponse); // Clean up
};
@@ -88,7 +90,8 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
</div>
<div className="user-name">
{userName
? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()
? userName.charAt(0).toUpperCase() +
userName.slice(1).toLowerCase()
: "Anonymous"}
</div>
</div>
@@ -162,10 +165,14 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
<SettingsIcon />
Settings
</div>
<div className="option-list" style={{ cursor: "pointer" }} onClick={() => {
localStorage.clear();
navigate("/");
}}>
<div
className="option-list"
style={{ cursor: "pointer" }}
onClick={() => {
localStorage.clear();
navigate("/");
}}
>
<LogoutIcon />
Log out
</div>
@@ -179,4 +186,4 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
);
};
export default SidePannel;
export default SidePannel;

View File

@@ -1,30 +1,39 @@
import React from 'react';
import React from "react";
interface Props {
email: string;
setEmail: (value: string) => void;
onSubmit: () => void;
email: string;
setEmail: (value: string) => void;
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
}
const EmailInput: React.FC<Props> = ({ email, setEmail, onSubmit }) => {
return (
<div className='request-container'>
<h1 className='header'>Forgot password</h1>
<p className='sub-header'>
Enter your email for the verification process, we will send a 4-digit code to your email.
</p>
<form className='auth-form' onSubmit={(e) => { e.preventDefault(); onSubmit(); }}>
<input
type='email'
placeholder='Email'
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<button type='submit' className='continue-button'>Continue</button>
</form>
</div>
);
return (
<div className="request-container">
<h1 className="header">Forgot password</h1>
<p className="sub-header">
Enter your email for the verification process, we will send a 4-digit
code to your email.
</p>
<form
className="auth-form"
onSubmit={(e) => {
e.preventDefault();
onSubmit(e);
}}
>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<button type="submit" className="continue-button">
Continue
</button>
</form>
</div>
);
};
export default EmailInput;

View File

@@ -1,10 +1,95 @@
import React, { useState, useRef, useEffect } from 'react';
// import React, { useState, useRef, useEffect } from "react";
const OTPInput: React.FC<{ length?: number; onComplete: (otp: string) => void }> = ({ length = 4, onComplete }) => {
const [otpValues, setOtpValues] = useState<string[]>(Array(length).fill(''));
// const OTPInput: React.FC<{
// length?: number;
// onComplete: (otp: string) => void;
// code: string;
// }> = ({ length = 4, onComplete, code }) => {
// const [otpValues, setOtpValues] = useState<string[]>(Array(length).fill(""));
// const inputsRef = useRef<(HTMLInputElement | null)[]>([]);
// useEffect(() => {
// if (code) {
// console.log("code: ", code);
// const codeString = String(code); // convert number → string
// setOtpValues(codeString.split(""));
// onComplete(codeString);
// }
// }, [code, length]);
// // Auto focus first input on mount
// useEffect(() => {
// inputsRef.current[0]?.focus();
// }, []);
// const handleChange = (value: string, index: number) => {
// if (/^[0-9]?$/.test(value)) {
// const newOtp = [...otpValues];
// newOtp[index] = value;
// setOtpValues(newOtp);
// if (value && index < length - 1) {
// inputsRef.current[index + 1]?.focus();
// }
// if (newOtp.every((digit) => digit !== "")) {
// console.log('newOtp.join(""): ', newOtp.join(""));
// onComplete(newOtp.join(""));
// }
// }
// };
// const handleKeyDown = (
// e: React.KeyboardEvent<HTMLInputElement>,
// index: number
// ) => {
// if (e.key === "Backspace" && !otpValues[index] && index > 0) {
// inputsRef.current[index - 1]?.focus();
// }
// };
// return (
// <div className="otp-container">
// {otpValues.map((value, index) => (
// <input
// key={index}
// type="text"
// className="otp-input"
// maxLength={1}
// value={value}
// onChange={(e) => handleChange(e.target.value, index)}
// onKeyDown={(e) => handleKeyDown(e, index)}
// ref={(el) => (inputsRef.current[index] = el)}
// />
// ))}
// </div>
// );
// };
// export default OTPInput;
import React, { useState, useRef, useEffect } from "react";
const OTPInput: React.FC<{
length?: number;
onComplete: (otp: string) => void;
code: string | number;
}> = ({ length = 4, onComplete, code }) => {
const [otpValues, setOtpValues] = useState<string[]>(Array(length).fill(""));
const inputsRef = useRef<(HTMLInputElement | null)[]>([]);
// Auto focus first input on mount
// ✅ Pre-fill inputs if code is passed
useEffect(() => {
if (code) {
const codeString = String(code);
const filled = codeString.split("").slice(0, length);
const padded = filled.concat(Array(length - filled.length).fill(""));
setOtpValues(padded);
if (filled.length === length) {
onComplete(filled.join(""));
}
}
}, [code, length, onComplete]);
// ✅ Focus first input on mount
useEffect(() => {
inputsRef.current[0]?.focus();
}, []);
@@ -19,14 +104,18 @@ const OTPInput: React.FC<{ length?: number; onComplete: (otp: string) => void }>
inputsRef.current[index + 1]?.focus();
}
if (newOtp.every((digit) => digit !== '')) {
onComplete(newOtp.join(''));
// ✅ Only trigger onComplete when all digits are filled
if (newOtp.every((digit) => digit !== "")) {
onComplete(newOtp.join(""));
}
}
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
if (e.key === 'Backspace' && !otpValues[index] && index > 0) {
const handleKeyDown = (
e: React.KeyboardEvent<HTMLInputElement>,
index: number
) => {
if (e.key === "Backspace" && !otpValues[index] && index > 0) {
inputsRef.current[index - 1]?.focus();
}
};
@@ -39,7 +128,7 @@ const OTPInput: React.FC<{ length?: number; onComplete: (otp: string) => void }>
type="text"
className="otp-input"
maxLength={1}
value={value}
value={value ?? ""}
onChange={(e) => handleChange(e.target.value, index)}
onKeyDown={(e) => handleKeyDown(e, index)}
ref={(el) => (inputsRef.current[index] = el)}

View File

@@ -1,57 +1,77 @@
import React, { useState } from 'react';
import OTPInput from './OTPInput';
import React, { FormEvent, useState } from "react";
import OTPInput from "./OTPInput";
interface Props {
email: string;
timer: number;
setCode: (value: string) => void;
onSubmit: () => void;
resendCode: () => void;
email: string;
code: string;
timer: number;
setCode: (value: string) => void;
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
resendCode: () => void;
}
const OTPVerification: React.FC<Props> = ({ email, timer, setCode, onSubmit, resendCode }) => {
const [otp, setOtp] = useState('');
const OTPVerification: React.FC<Props> = ({
email,
timer,
setCode,
onSubmit,
resendCode,
code,
}) => {
const [otp, setOtp] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log('otp.length: ', otp.length);
if (otp.length === 4) {
onSubmit();
} else {
alert('Please enter the 4-digit code');
}
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
return (
<div className='request-container'>
<h1 className='header'>Verification</h1>
<p className='sub-header'>
Enter the 4-digit code sent to <strong>{email}</strong>.
</p>
<form className='auth-form' onSubmit={handleSubmit}>
<OTPInput length={4} onComplete={(code) => { setOtp(code); setCode(code); }} />
<div className="timing">
{timer > 0
? `${String(Math.floor(timer / 60)).padStart(2, '0')}:${String(timer % 60).padStart(2, '0')}`
: ''}
</div>
<button
type='submit'
className='continue-button'
disabled={otp.length < 4} // prevent clicking if not complete
>
Verify
</button>
</form>
<div
className={`resend ${timer > 0 ? 'disabled' : ''}`}
onClick={timer === 0 ? resendCode : undefined}
style={{ cursor: timer === 0 ? 'pointer' : 'not-allowed', opacity: timer === 0 ? 1 : 0.5 }}
>
If you didnt receive a code, <span>Resend</span>
</div>
if (otp.length === 4) {
onSubmit(e);
} else {
alert("Please enter the 4-digit code");
}
};
return (
<div className="request-container">
<h1 className="header">Verification</h1>
<p className="sub-header">
Enter the 4-digit code sent to <strong>{email}</strong>.
</p>
<form className="auth-form" onSubmit={handleSubmit}>
<OTPInput
length={4}
onComplete={(codes) => {
setOtp(code);
setCode(codes);
}}
code={code}
/>
<div className="timing">
{timer > 0
? `${String(Math.floor(timer / 60)).padStart(2, "0")}:${String(
timer % 60
).padStart(2, "0")}`
: ""}
</div>
);
<button
type="submit"
className="continue-button"
// disabled={otp.length < 4} // prevent clicking if not complete
>
Verify
</button>
</form>
<div
className={`resend ${timer > 0 ? "disabled" : ""}`}
onClick={timer === 0 ? resendCode : undefined}
style={{
cursor: timer === 0 ? "pointer" : "not-allowed",
opacity: timer === 0 ? 1 : 0.5,
}}
>
If you didnt receive a code, <span>Resend</span>
</div>
</div>
);
};
export default OTPVerification;

View File

@@ -1,77 +1,82 @@
import React, { useState } from 'react';
import { EyeIcon } from '../icons/ExportCommonIcons';
import React, { useState } from "react";
import { EyeIcon } from "../icons/ExportCommonIcons";
interface Props {
newPassword: string;
confirmPassword: string;
setNewPassword: (value: string) => void;
setConfirmPassword: (value: string) => void;
onSubmit: () => void;
newPassword: string;
confirmPassword: string;
setNewPassword: (value: string) => void;
setConfirmPassword: (value: string) => void;
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
}
const PasswordSetup: React.FC<Props> = ({
newPassword,
confirmPassword,
setNewPassword,
setConfirmPassword,
onSubmit
newPassword,
confirmPassword,
setNewPassword,
setConfirmPassword,
onSubmit,
}) => {
const [showNewPassword, setShowNewPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [showNewPassword, setShowNewPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
return (
<div className='request-container'>
<h1 className='header'>New Password</h1>
<p className='sub-header'>Set the new password for your account so you can login and access all features.</p>
<form
className='auth-form'
onSubmit={(e) => {
e.preventDefault();
if (newPassword !== confirmPassword) {
alert('Passwords do not match');
return;
}
onSubmit();
}}
>
<div className="password-container">
<input
type={showNewPassword ? 'text' : 'password'}
placeholder='Enter new password'
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowNewPassword(prev => !prev)}
>
<EyeIcon isClosed={!showNewPassword} />
</button>
</div>
<div className="password-container">
<input
type={showConfirmPassword ? 'text' : 'password'}
placeholder='Confirm password'
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowConfirmPassword(prev => !prev)}
>
<EyeIcon isClosed={!showConfirmPassword} />
</button>
</div>
<button type='submit' className='continue-button'>Update password</button>
</form>
return (
<div className="request-container">
<h1 className="header">New Password</h1>
<p className="sub-header">
Set the new password for your account so you can login and access all
features.
</p>
<form
className="auth-form"
onSubmit={(e) => {
e.preventDefault();
if (newPassword !== confirmPassword) {
alert("Passwords do not match");
return;
}
onSubmit(e);
}}
>
<div className="password-container">
<input
type={showNewPassword ? "text" : "password"}
placeholder="Enter new password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowNewPassword((prev) => !prev)}
>
<EyeIcon isClosed={!showNewPassword} />
</button>
</div>
);
<div className="password-container">
<input
type={showConfirmPassword ? "text" : "password"}
placeholder="Confirm password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowConfirmPassword((prev) => !prev)}
>
<EyeIcon isClosed={!showConfirmPassword} />
</button>
</div>
<button type="submit" className="continue-button">
Update password
</button>
</form>
</div>
);
};
export default PasswordSetup;