🔥 Reset password now working
This commit is contained in:
@@ -20,42 +20,46 @@ import EditEventsPage from "./pages/EditEventsPage";
|
||||
import DefaultLayout from "./layouts/default";
|
||||
import AdministratorLayout from "./layouts/administrator";
|
||||
import UsersManagement from "./pages/UsersManagement";
|
||||
import ResetPasswordPage from "./pages/ResetPasswordPage";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Routes>
|
||||
{/* User Routes */}
|
||||
<Route path="/" element={<DefaultLayout />}>
|
||||
{/* General Routes */}
|
||||
<Route index element={<HomePage />} />
|
||||
<Route element={<SignUpPage />} path="signup" />
|
||||
<Route element={<SignInPage />} path="signin" />
|
||||
<Route element={<SpringboardPage />} path="springboard" />
|
||||
<Route element={<ManageUserAccountPage />} path="manage-account" />
|
||||
<Route path="/">
|
||||
<Route element={<DefaultLayout />}>
|
||||
{/* General Routes */}
|
||||
<Route index element={<HomePage />} />
|
||||
<Route element={<SignUpPage />} path="signup" />
|
||||
<Route element={<SignInPage />} path="signin" />
|
||||
<Route element={<SpringboardPage />} path="springboard" />
|
||||
<Route element={<ManageUserAccountPage />} path="manage-account" />
|
||||
|
||||
{/* Events Route */}
|
||||
<Route path="events">
|
||||
<Route index element={<EventsPage />} />
|
||||
</Route>
|
||||
{/* Events Route */}
|
||||
<Route path="events">
|
||||
<Route index element={<EventsPage />} />
|
||||
</Route>
|
||||
|
||||
{/* Karang Guni Schedules Route */}
|
||||
<Route path="karang-guni-schedules">
|
||||
<Route index element={<SchedulePage />} />
|
||||
</Route>
|
||||
{/* Karang Guni Schedules Route */}
|
||||
<Route path="karang-guni-schedules">
|
||||
<Route index element={<SchedulePage />} />
|
||||
</Route>
|
||||
|
||||
{/* Home Bill Contest Route */}
|
||||
<Route path="home-bill-contest">
|
||||
<Route index element={<HBContestPage />} />
|
||||
<Route element={<HBFormPage />} path="new-submission" />
|
||||
</Route>
|
||||
{/* Home Bill Contest Route */}
|
||||
<Route path="home-bill-contest">
|
||||
<Route index element={<HBContestPage />} />
|
||||
<Route element={<HBFormPage />} path="new-submission" />
|
||||
</Route>
|
||||
|
||||
{/* Community Posts Route */}
|
||||
<Route path="community-posts">
|
||||
<Route index element={<CommunityPage />} />
|
||||
<Route element={<CreatePostPage />} path="create" />
|
||||
<Route element={<PostPage />} path="post/:id" />
|
||||
<Route element={<EditPostPage />} path="edit/:id" />
|
||||
{/* Community Posts Route */}
|
||||
<Route path="community-posts">
|
||||
<Route index element={<CommunityPage />} />
|
||||
<Route element={<CreatePostPage />} path="create" />
|
||||
<Route element={<PostPage />} path="post/:id" />
|
||||
<Route element={<EditPostPage />} path="edit/:id" />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route element={<ResetPasswordPage />} path="reset-password/:token" />
|
||||
</Route>
|
||||
|
||||
{/* Admin Routes */}
|
||||
|
||||
@@ -36,9 +36,6 @@ export default function NavigationBar() {
|
||||
);
|
||||
setUserInformation(value);
|
||||
})
|
||||
.catch(() => {
|
||||
navigate("/signin");
|
||||
})
|
||||
.finally(() => {
|
||||
setDoneLoading(true);
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function SignInModule() {
|
||||
if (value.accountType == 2) {
|
||||
navigate("/admin");
|
||||
} else {
|
||||
navigate("/springboard");
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Form, Formik } from "formik";
|
||||
import NextUIFormikInput from "./NextUIFormikInput";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import UserProfilePicture from "./UserProfilePicture";
|
||||
import { popErrorToast } from "../utilities";
|
||||
import { popErrorToast, popToast } from "../utilities";
|
||||
import instance from "../security/http";
|
||||
|
||||
export default function UpdateAccountModule() {
|
||||
@@ -105,6 +105,25 @@ export default function UpdateAccountModule() {
|
||||
});
|
||||
};
|
||||
|
||||
const sendResetPasswordEmail = () => {
|
||||
instance
|
||||
.put(
|
||||
`${
|
||||
config.serverAddress
|
||||
}/users/request-reset-password/${encodeURIComponent(
|
||||
userInformation.email
|
||||
)}`
|
||||
)
|
||||
.then(() => {
|
||||
console.log("Email sent successfully");
|
||||
popToast("Email sent to your mailbox!", 1);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to send email:", error);
|
||||
popErrorToast("Failed to send email: " + error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{userInformation && (
|
||||
@@ -221,16 +240,7 @@ export default function UpdateAccountModule() {
|
||||
color="danger"
|
||||
variant="light"
|
||||
onPress={() => {
|
||||
instance
|
||||
.put(
|
||||
`${config.serverAddress}/connections/send-reset-password-email/${userInformation.id}`
|
||||
)
|
||||
.then(() => {
|
||||
console.log("Email sent successfully");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to send email:", error);
|
||||
});
|
||||
sendResetPasswordEmail();
|
||||
}}
|
||||
>
|
||||
Reset your password
|
||||
|
||||
142
client/src/pages/ResetPasswordPage.tsx
Normal file
142
client/src/pages/ResetPasswordPage.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import instance from "../security/http";
|
||||
import config from "../config";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button, Card, CircularProgress } from "@nextui-org/react";
|
||||
import EcoconnectFullLogo from "../components/EcoconnectFullLogo";
|
||||
import NextUIFormikInput from "../components/NextUIFormikInput";
|
||||
import { Formik, Form } from "formik";
|
||||
import * as Yup from "yup";
|
||||
import axios from "axios";
|
||||
import { popErrorToast, popToast } from "../utilities";
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
password: Yup.string()
|
||||
.trim()
|
||||
.max(69, "Password must be at most 69 characters")
|
||||
.matches(
|
||||
/^(?=.*[a-zA-Z])(?=.*[0-9]).{1,}$/,
|
||||
"Password must contain both letters and numbers"
|
||||
)
|
||||
.required("Password is required"),
|
||||
confirmPassword: Yup.string()
|
||||
.oneOf([Yup.ref("password"), undefined], "Passwords must match")
|
||||
.required("Confirm Password is required"),
|
||||
});
|
||||
|
||||
export default function ResetPasswordPage() {
|
||||
const { token } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [validationResult, setValidationResult] = useState<boolean>(false);
|
||||
const [pageLoading, setPageLoading] = useState<boolean>(true);
|
||||
|
||||
const validateToken = () => {
|
||||
instance
|
||||
.get(`${config.serverAddress}/users/reset-password/${token}`)
|
||||
.then(() => {
|
||||
setValidationResult(true);
|
||||
})
|
||||
.catch(() => {
|
||||
setValidationResult(false);
|
||||
})
|
||||
.finally(() => {
|
||||
setPageLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
validateToken();
|
||||
}, []);
|
||||
|
||||
const handleSubmit = (values: any): void => {
|
||||
console.log("submitting");
|
||||
instance
|
||||
.post(`${config.serverAddress}/users/reset-password`, {
|
||||
token,
|
||||
password: values.password,
|
||||
})
|
||||
.then(() => {
|
||||
popToast("Success!", 1);
|
||||
navigate("/signin");
|
||||
})
|
||||
.catch((error) => {
|
||||
popErrorToast(error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 p-8 w-full h-full flex flex-col justify-center">
|
||||
<div className="flex flex-row justify-center">
|
||||
{pageLoading && (
|
||||
<div>
|
||||
<CircularProgress label="Loading..." />
|
||||
</div>
|
||||
)}
|
||||
{!pageLoading && (
|
||||
<div className="flex flex-col gap-8 *:mx-auto">
|
||||
<Card className="max-w-[600px] w-full mx-auto">
|
||||
{validationResult && (
|
||||
<div className="flex flex-col gap-8 p-12 text-left">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-3xl font-bold">Password Reset</p>
|
||||
<p>Enter a new password below.</p>
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={{ password: "", confirmPassword: "" }}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ isValid, dirty }) => (
|
||||
<Form className="flex flex-col gap-8">
|
||||
<NextUIFormikInput
|
||||
label="New Password"
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="Enter new password"
|
||||
labelPlacement="outside"
|
||||
/>
|
||||
<NextUIFormikInput
|
||||
label="Confirm Password"
|
||||
name="confirmPassword"
|
||||
type="password"
|
||||
placeholder="Confirm new password"
|
||||
labelPlacement="outside"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
size="lg"
|
||||
isDisabled={!isValid || !dirty}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
)}
|
||||
{!validationResult && (
|
||||
<div className="flex flex-col gap-8 p-12 *:mr-auto text-left">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-3xl font-bold">
|
||||
Reset portal has been closed.
|
||||
</p>
|
||||
<p>Please request for a password reset again.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
<div className="flex flex-row gap-4">
|
||||
<EcoconnectFullLogo />
|
||||
<p>·</p>
|
||||
<p className="opacity-50">
|
||||
© Copyright {new Date().getFullYear()}. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user