updated forget password functionality with backend
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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 didn’t 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 didn’t receive a code, <span>Resend</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OTPVerification;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user