Added Forgot Password UI and Functionality
This commit is contained in:
parent
21313da24a
commit
13b506960b
|
|
@ -27,6 +27,7 @@ import CompletionScreen from "./components/client/CompletionScreen";
|
||||||
import SessionExpired from "./components/client/SessionExpired";
|
import SessionExpired from "./components/client/SessionExpired";
|
||||||
|
|
||||||
import ExpiredPage from "./components/ExpiredPage";
|
import ExpiredPage from "./components/ExpiredPage";
|
||||||
|
import ForgotPassword from "./components/forgotPassword/ForgotPassword";
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -103,12 +104,16 @@ const App = () => {
|
||||||
} />
|
} />
|
||||||
|
|
||||||
<Route path="/completion" element={
|
<Route path="/completion" element={
|
||||||
<CompletionScreen />
|
<CompletionScreen />
|
||||||
} />
|
} />
|
||||||
|
|
||||||
{/* Links work wothout Login */}
|
{/* Links work wothout Login */}
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/register" element={<Register />} />
|
<Route path="/register" element={<Register />} />
|
||||||
|
<Route path="/forgot-password" element={<ForgotPassword />} />
|
||||||
|
<Route path="/forgot-password/:token" element={<ForgotPassword />} />
|
||||||
|
|
||||||
|
|
||||||
<Route path="/sign/:recipientId/:signType" element={<SignDocument />} />
|
<Route path="/sign/:recipientId/:signType" element={<SignDocument />} />
|
||||||
<Route path="/sign/:recipientId/" element={<SignDocument />} />
|
<Route path="/sign/:recipientId/" element={<SignDocument />} />
|
||||||
<Route path="/view" element={<DocView />} />
|
<Route path="/view" element={<DocView />} />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,319 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import { Link, useNavigate, useParams, } from "react-router-dom";
|
||||||
|
import { FiEyeOff } from "react-icons/fi";
|
||||||
|
import { BsEye } from "react-icons/bs";
|
||||||
|
|
||||||
|
interface ForgotPasswordFormProps {
|
||||||
|
onSwitchToLogin: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ForgotPasswordForm = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const [password, setPassword] = useState<string>();
|
||||||
|
const [confirmPassword, setConfirmPassword] = useState<string>();
|
||||||
|
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
|
||||||
|
const [countdown, setCountdown] = useState(3);
|
||||||
|
|
||||||
|
const { token } = useParams<{ token: string }>();
|
||||||
|
|
||||||
|
const validateEmail = (email: string) => {
|
||||||
|
if (!email.trim()) {
|
||||||
|
return "Email is required";
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
||||||
|
if (!emailRegex.test(email)) {
|
||||||
|
return "Please enter a valid email";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatePassword = () => {
|
||||||
|
if (!password?.trim()) {
|
||||||
|
return "Password is required";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < 8) {
|
||||||
|
return "Password must be at least 8 characters";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[A-Z]/.test(password)) {
|
||||||
|
return "Password must contain at least one uppercase letter";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/[a-z]/.test(password)) {
|
||||||
|
return "Password must contain at least one lowercase letter";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/\d/.test(password)) {
|
||||||
|
return "Password must contain at least one number";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
return "Passwords do not match";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const emailError = validateEmail(email);
|
||||||
|
|
||||||
|
if (emailError) {
|
||||||
|
setError(emailError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError("");
|
||||||
|
setMessage("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(
|
||||||
|
`${import.meta.env.VITE_BASE_URI}/api/v1/auth/forgot-password`,
|
||||||
|
{ email }
|
||||||
|
);
|
||||||
|
|
||||||
|
setMessage(
|
||||||
|
"If an account exists with this email, password reset instructions have been sent."
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
setError("Unable to process your request. Please try again.");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdatePassword = async () => {
|
||||||
|
const validationError = validatePassword();
|
||||||
|
|
||||||
|
if (validationError) {
|
||||||
|
setError(validationError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError("");
|
||||||
|
setMessage("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(
|
||||||
|
`${import.meta.env.VITE_BASE_URI}/api/v1/auth/reset-password`,
|
||||||
|
{
|
||||||
|
token,
|
||||||
|
password,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
setMessage(
|
||||||
|
"🎉 Password updated successfully! Redirecting to login in 3 seconds..."
|
||||||
|
);
|
||||||
|
|
||||||
|
let seconds = 3;
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
seconds -= 1;
|
||||||
|
setCountdown(seconds);
|
||||||
|
|
||||||
|
if (seconds <= 0) {
|
||||||
|
clearInterval(timer);
|
||||||
|
navigate("/login");
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} catch (err: any) {
|
||||||
|
setError(
|
||||||
|
err?.response?.data?.message ||
|
||||||
|
"Unable to update password. Please try again."
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white p-8 rounded-lg w-full max-w-md">
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<span className="text-orange-500 text-sm">
|
||||||
|
PASSWORD RECOVERY 🔐
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h2 className="text-2xl font-bold text-gray-800 mt-2">
|
||||||
|
{!token && <>Forgot Password</>}
|
||||||
|
{token && <>Change Password</>}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p className="text-sm text-gray-500 mt-2">
|
||||||
|
{!token && <>Enter your email address and we'll send you instructions to reset your password.</>}
|
||||||
|
{token && <>Create a new password for your account. Enter your new password below and confirm it to continue.</>}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!token && <>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Email Address
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => {
|
||||||
|
setEmail(e.target.value);
|
||||||
|
setError("");
|
||||||
|
}}
|
||||||
|
placeholder="Enter your email"
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<p className="text-red-500 text-sm mt-2">{error}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{message && (
|
||||||
|
<p className="text-green-600 text-sm mt-2">{message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
disabled={loading}
|
||||||
|
className={`w-full mt-5 py-2 rounded-lg text-white font-medium ${loading
|
||||||
|
? "bg-gray-400 cursor-not-allowed"
|
||||||
|
: "bg-blue-500 hover:bg-blue-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{loading ? "Sending..." : "Send Reset Link"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="text-center mt-5">
|
||||||
|
<Link
|
||||||
|
to="/login"
|
||||||
|
className="text-sm text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
Back to Login
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>}
|
||||||
|
|
||||||
|
{token && (
|
||||||
|
<>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
New Password
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
placeholder="Enter new password"
|
||||||
|
className="w-full px-4 py-2 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword((prev) => !prev)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500"
|
||||||
|
>
|
||||||
|
{showPassword ? <FiEyeOff size={18} /> : <BsEye size={18} />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Confirm Password
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type={showConfirmPassword ? "text" : "password"}
|
||||||
|
value={confirmPassword}
|
||||||
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
|
placeholder="Confirm new password"
|
||||||
|
className="w-full px-4 py-2 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowConfirmPassword((prev) => !prev)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500"
|
||||||
|
>
|
||||||
|
{showConfirmPassword ? <FiEyeOff size={18} /> : <BsEye size={18} />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gray-50 rounded-lg p-3">
|
||||||
|
<p className="text-xs font-semibold text-gray-700 mb-2">
|
||||||
|
Password Requirements
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul className="text-xs text-gray-500 space-y-1">
|
||||||
|
<li>• Minimum 8 characters</li>
|
||||||
|
<li>• One uppercase letter</li>
|
||||||
|
<li>• One lowercase letter</li>
|
||||||
|
<li>• One number</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
||||||
|
<p className="text-red-600 text-sm">
|
||||||
|
{error}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{message && (
|
||||||
|
<div className="bg-green-50 border border-green-200 rounded-lg p-3">
|
||||||
|
<p className="text-green-700 text-sm font-medium">
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-green-600 text-xs mt-1">
|
||||||
|
Redirecting in {countdown}s...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleUpdatePassword}
|
||||||
|
disabled={loading}
|
||||||
|
className={`w-full py-3 rounded-lg text-white font-medium transition-all duration-200 ${loading
|
||||||
|
? "bg-gray-400 cursor-not-allowed"
|
||||||
|
: "bg-blue-500 hover:bg-blue-600 active:scale-[0.98]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||||
|
Updating...
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
"Update Password"
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ForgotPasswordForm;
|
||||||
|
|
@ -16,9 +16,11 @@ interface FormDataState {
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoginFormProps {
|
||||||
|
onSwitchToRegister: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoginForm = ({ onSwitchToRegister }: LoginFormProps) => {
|
||||||
const LoginForm = ({ onSwitchToRegister }) => {
|
|
||||||
const [formData, setForm] = useState({ email: "", password: "" });
|
const [formData, setForm] = useState({ email: "", password: "" });
|
||||||
const [cacheKey, setCacheKey] = useState("");
|
const [cacheKey, setCacheKey] = useState("");
|
||||||
const [nextClick, setNextClick] = useState(false);
|
const [nextClick, setNextClick] = useState(false);
|
||||||
|
|
@ -123,7 +125,7 @@ const LoginForm = ({ onSwitchToRegister }) => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.response?.status == 400 || err.response?.status == 401) {
|
if (err.response?.status == 400 || err.response?.status == 401) {
|
||||||
formErrorRef.current = { message: "The email or password you entered is invalid.", error: true }
|
formErrorRef.current = { message: "The email or password you entered is invalid.", error: true }
|
||||||
} else if(err.response?.status == 404) {
|
} else if (err.response?.status == 404) {
|
||||||
formErrorRef.current = { message: "Email not found. Please sign up to continue.", error: true }
|
formErrorRef.current = { message: "Email not found. Please sign up to continue.", error: true }
|
||||||
} else {
|
} else {
|
||||||
formErrorRef.current = { message: "Something went wrong. Please refresh and try again.", error: true }
|
formErrorRef.current = { message: "Something went wrong. Please refresh and try again.", error: true }
|
||||||
|
|
@ -206,15 +208,15 @@ const LoginForm = ({ onSwitchToRegister }) => {
|
||||||
{errors.password && (
|
{errors.password && (
|
||||||
<p className="text-red-500 text-sm mt-1">{errors.password}</p>
|
<p className="text-red-500 text-sm mt-1">{errors.password}</p>
|
||||||
)}
|
)}
|
||||||
{/* <div className="text-right mt-2">
|
<div className="text-right mt-2">
|
||||||
<a href="#" className="text-blue-500 text-sm hover:underline">
|
<Link to="/forgot-password" className="text-blue-500 text-sm hover:underline">
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</a>
|
</Link>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* )} */}
|
{/* )} */}
|
||||||
|
|
||||||
<p className={`${formErrorRef.current.error ? 'text-red-500 text-center' : 'text-green-500 text-center'} `}>{formErrorRef.current.message}</p>
|
<p className={`${formErrorRef.current.error ? 'text-red-500 text-center' : 'text-green-500 text-center'} `}>{formErrorRef.current.message}</p>
|
||||||
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
@ -238,6 +240,7 @@ const LoginForm = ({ onSwitchToRegister }) => {
|
||||||
Register
|
Register
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@ interface FormDataState {
|
||||||
lastName: string;
|
lastName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegisterForm = ({ onSwitchToLogin }) => {
|
interface RegisterFormProps {
|
||||||
|
onSwitchToLogin: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const RegisterForm = ({ onSwitchToLogin }: RegisterFormProps) => {
|
||||||
const formErrorRef = useRef<{ message: string; error: boolean }>({ message: "", error: false });
|
const formErrorRef = useRef<{ message: string; error: boolean }>({ message: "", error: false });
|
||||||
const [formData, setForm] = useState<FormDataState>({
|
const [formData, setForm] = useState<FormDataState>({
|
||||||
email: "",
|
email: "",
|
||||||
|
|
@ -76,7 +80,7 @@ const RegisterForm = ({ onSwitchToLogin }) => {
|
||||||
if (!password) return "Password is required";
|
if (!password) return "Password is required";
|
||||||
if (password.length < 8) return "Password must be at least 8 characters long";
|
if (password.length < 8) return "Password must be at least 8 characters long";
|
||||||
if (password.length > 60) return "Password is too long";
|
if (password.length > 60) return "Password is too long";
|
||||||
if (/\s/.test(password)) return "Password cannot contain spaces";
|
if (/\s/.test(password)) return "Password cannot contain spaces";
|
||||||
|
|
||||||
|
|
||||||
const hasUpperCase = /[A-Z]/.test(password);
|
const hasUpperCase = /[A-Z]/.test(password);
|
||||||
|
|
|
||||||
|
|
@ -9,37 +9,43 @@ import { fetchUserDetails } from "../../store/slices/userSlice";
|
||||||
import LoginRegisterImg from "../../assets/illustration_1_.png";
|
import LoginRegisterImg from "../../assets/illustration_1_.png";
|
||||||
import SMELogo from "../../assets/SMELogo.png";
|
import SMELogo from "../../assets/SMELogo.png";
|
||||||
import api from "../../config/api";
|
import api from "../../config/api";
|
||||||
|
import ForgotPasswordForm from "./ForgotPasswordForm";
|
||||||
|
|
||||||
|
enum PAGE_TYPE_ENUM {
|
||||||
|
LOGIN,
|
||||||
|
REGISTER,
|
||||||
|
FORGOT_PASSWORD,
|
||||||
|
EMPTY
|
||||||
|
}
|
||||||
function UserAuthUI() {
|
function UserAuthUI() {
|
||||||
const [isLogin, setIsLogin] = useState(true);
|
const [pageType, setPageType] = useState<PAGE_TYPE_ENUM>(PAGE_TYPE_ENUM.EMPTY);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation(); // Hook to get the current URL path
|
const location = useLocation(); // Hook to get the current URL path
|
||||||
|
|
||||||
// Sync isLogin state with the current route
|
// Sync isLogin state with the current route
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const path = location.pathname;
|
const path = location.pathname;
|
||||||
if (path == "/login") {
|
if (path.includes("/login")) {
|
||||||
setIsLogin(true);
|
setPageType(PAGE_TYPE_ENUM.LOGIN);
|
||||||
} else if (path == "/register") {
|
} else if (path.includes("/register")) {
|
||||||
setIsLogin(false);
|
setPageType(PAGE_TYPE_ENUM.REGISTER);
|
||||||
|
} else if (path.includes("/forgot-password")) {
|
||||||
|
setPageType(PAGE_TYPE_ENUM.FORGOT_PASSWORD);
|
||||||
}
|
}
|
||||||
}, [location.pathname]);
|
}, [location.pathname]);
|
||||||
|
|
||||||
const handleLogin = (loginData) => {
|
// const handleLogin = (loginData) => {
|
||||||
console.log("Login data:", loginData);
|
// };
|
||||||
// Your login API call logic here
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRegister = (registerData) => {
|
// const handleRegister = (registerData) => {
|
||||||
console.log("Register data:", registerData);
|
// };
|
||||||
// Your register API call logic here
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (pageType !== PAGE_TYPE_ENUM.LOGIN) return
|
||||||
(async () => {
|
(async () => {
|
||||||
await api.get(`/api/v1/user/details`);
|
await api.get(`/api/v1/user/details`);
|
||||||
})()
|
})()
|
||||||
}, []);
|
}, [pageType]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-h-screen bg-white flex">
|
<div className="max-h-screen bg-white flex">
|
||||||
|
|
@ -70,17 +76,9 @@ function UserAuthUI() {
|
||||||
|
|
||||||
{/* Right Side - Form */}
|
{/* Right Side - Form */}
|
||||||
<div className="flex-1 flex items-center justify-center p-8 lg:p-12">
|
<div className="flex-1 flex items-center justify-center p-8 lg:p-12">
|
||||||
{isLogin ? (
|
{pageType === PAGE_TYPE_ENUM.LOGIN && <LoginForm onSwitchToRegister={() => navigate("/register")} />}
|
||||||
<LoginForm
|
{pageType === PAGE_TYPE_ENUM.REGISTER && <RegisterForm onSwitchToLogin={() => navigate("/login")} />}
|
||||||
onLogin={handleLogin}
|
{pageType === PAGE_TYPE_ENUM.FORGOT_PASSWORD && <ForgotPasswordForm />}
|
||||||
onSwitchToRegister={() => navigate("/register")} // Navigate to /register
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<RegisterForm
|
|
||||||
onRegister={handleRegister}
|
|
||||||
onSwitchToLogin={() => navigate("/login")} // Navigate to /login
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import UserAuthUI from "../auth/UsertAuthUI"
|
||||||
|
|
||||||
|
function ForgotPassword() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="">
|
||||||
|
<UserAuthUI pg={"forgot-password"} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ForgotPassword
|
||||||
Loading…
Reference in New Issue