Added reset password modal and confirm reset password page

This commit is contained in:
Keannu Bernasol 2023-11-20 00:00:30 +08:00
parent 742a1af9f8
commit a8de8476e5
8 changed files with 335 additions and 18 deletions

View file

@ -10,6 +10,7 @@ import ErrorPage from "./Pages/ErrorPage/ErrorPage";
import DashboardPage from "./Pages/DashboardPage/DashboardPage";
import Revalidator from "./Components/Revalidator/Revalidator";
import ActivationPage from "./Pages/ActivationPage/ActivationPage";
import ResetPasswordPage from "./Pages/ResetPasswordPage/ResetPasswordPage";
const queryClient = new QueryClient();
const router = createHashRouter([
@ -42,6 +43,15 @@ const router = createHashRouter([
),
errorElement: <ErrorPage />,
},
{
path: "/reset_password_confirm/:uid/:token",
element: (
<>
<ResetPasswordPage />
</>
),
errorElement: <ErrorPage />,
},
]);
export default function App() {

View file

@ -1,6 +1,11 @@
/* eslint-disable react-refresh/only-export-components */
import axios from "axios";
import { ActivationType, LoginType, RegisterType } from "../Types/Types";
import {
ActivationType,
LoginType,
RegisterType,
ResetPasswordConfirmType,
} from "../Types/Types";
const instance = axios.create({
baseURL: "http://localhost:8000/",
@ -130,3 +135,28 @@ export function ActivationAPI(activation: ActivationType) {
return false;
});
}
export function ResetPasswordAPI(email: string) {
return instance
.post("api/v1/accounts/users/reset_password/", { email: email })
.then(() => {
console.log("Activation Success");
return true;
})
.catch(() => {
console.log("Activation failed");
return false;
});
}
export function ResetPasswordConfirmAPI(info: ResetPasswordConfirmType) {
return instance
.post("api/v1/accounts/users/reset_password_confirm/", info)
.then(() => {
console.log("Reset Success");
return true;
})
.catch(() => {
console.log("Reset failed");
return false;
});
}

View file

@ -106,16 +106,6 @@ export default function LoginModal() {
/>
<p style={{ ...styles.text_dark, ...styles.text_S }}>Remember me</p>
</div>
<div style={{ flex: 1 }}>
<p
style={{
...styles.text_dark,
...styles.text_S,
}}
>
Forgot password?
</p>
</div>
</div>
</div>
<div

View file

@ -0,0 +1,91 @@
import { useState } from "react";
import styles from "../../styles";
import { colors } from "../../styles";
import TextField from "@mui/material/TextField";
import NewReleasesIcon from "@mui/icons-material/NewReleases";
import Button from "../Button/Button";
import { useNavigate } from "react-router-dom";
import { ResetPasswordAPI } from "../API/API";
import { useDispatch } from "react-redux";
import { auth_toggle } from "../Plugins/Redux/Slices/AuthSlice/AuthSlice";
import { toast } from "react-toastify";
export default function ResetPasswordModal() {
const navigate = useNavigate();
const [error, setError] = useState("");
const [email, setEmail] = useState("");
const dispatch = useDispatch();
return (
<>
<div
style={{
...styles.flex_row,
...{
alignItems: "center",
justifyContent: "center",
overflowY: "scroll",
},
}}
>
<NewReleasesIcon
style={{
height: 64,
width: 64,
fill: colors.font_dark,
}}
/>
<p style={{ ...styles.text_dark, ...styles.text_L }}>Forgot Password</p>
</div>
<p style={{ ...styles.text_dark, ...styles.text_S }}>
Enter your email to request a password reset
</p>
<div style={styles.flex_column}>
<TextField
id="outlined-helperText"
label="Email"
style={styles.input_form}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setEmail(e.target.value);
setError("");
}}
value={email}
placeholder={"Enter email associated with account"}
/>
<p style={{ ...styles.text_dark, ...styles.text_S }}>{error}</p>
<div
style={{
backgroundColor: colors.button_border,
width: "100%",
height: "2px",
marginBottom: 8,
}}
/>
<Button
type={"dark"}
label={"Send Reset Request"}
onClick={async () => {
const status = await ResetPasswordAPI(email);
if (status === true) {
await dispatch(auth_toggle());
navigate("/");
toast("Reset request sent", {
position: "top-right",
autoClose: 6000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
});
setError(
"Password reset request sent. Please follow your email for further instructions"
);
} else {
setError("Invalid email specified");
}
}}
/>
</div>
</>
);
}

View file

@ -16,6 +16,12 @@ export type ActivationType = {
token: string;
};
export type ResetPasswordConfirmType = {
uid: string;
token: string;
new_password: string;
};
export type AddEquipmentType = {
name: string;
remarks: string;

View file

@ -27,6 +27,16 @@ export default function ActivationPage() {
progress: undefined,
theme: "light",
});
toast("Please login to continue", {
position: "top-right",
autoClose: 6000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
});
setTimeout(() => {
navigate("/dashboard");
});

View file

@ -9,9 +9,11 @@ import RegisterModal from "../../Components/RegisterModal/RegisterModal";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { RootState } from "../../Components/Plugins/Redux/Store/Store";
import ResetPasswordModal from "../../Components/ResetPasswordModal/ResetPasswordModal";
export default function LandingPage() {
const [LoginModalOpen, SetLoginModalOpen] = useState(false);
const [RegisterModalOpen, SetRegisterModalOpen] = useState(false);
const [loginmodalOpen, SetloginmodalOpen] = useState(false);
const [registermodalOpen, SetRegisterModalOpen] = useState(false);
const [resetmodalOpen, SetResetModalOpen] = useState(false);
const authenticated = useSelector((state: RootState) => state.auth.value);
const navigate = useNavigate();
@ -68,8 +70,9 @@ export default function LandingPage() {
type={"light"}
label={"Login"}
onClick={() => {
SetLoginModalOpen(true);
SetloginmodalOpen(true);
SetRegisterModalOpen(false);
SetResetModalOpen(false);
}}
/>
<Button
@ -77,12 +80,22 @@ export default function LandingPage() {
label={"Register"}
onClick={() => {
SetRegisterModalOpen(true);
SetLoginModalOpen(false);
SetloginmodalOpen(false);
SetResetModalOpen(false);
}}
/>
<Button
type={"light"}
label={"Forgot Password"}
onClick={() => {
SetResetModalOpen(true);
SetRegisterModalOpen(false);
SetloginmodalOpen(false);
}}
/>
<Popup
open={LoginModalOpen}
onClose={() => SetLoginModalOpen(false)}
open={loginmodalOpen}
onClose={() => SetloginmodalOpen(false)}
modal
position={"top center"}
contentStyle={{
@ -100,7 +113,7 @@ export default function LandingPage() {
<LoginModal />
</Popup>
<Popup
open={RegisterModalOpen}
open={registermodalOpen}
onClose={() => SetRegisterModalOpen(false)}
modal
position={"top center"}
@ -118,6 +131,25 @@ export default function LandingPage() {
>
<RegisterModal />
</Popup>
<Popup
open={resetmodalOpen}
onClose={() => SetResetModalOpen(false)}
modal
position={"top center"}
contentStyle={{
width: "512px",
borderRadius: 16,
borderColor: "grey",
borderStyle: "solid",
borderWidth: 1,
padding: 16,
alignContent: "center",
justifyContent: "center",
textAlign: "center",
}}
>
<ResetPasswordModal />
</Popup>
</div>
</div>
</div>

View file

@ -0,0 +1,148 @@
import { useNavigate, useParams } from "react-router-dom";
import styles, { colors } from "../../styles";
import { ResetPasswordConfirmAPI } from "../../Components/API/API";
import { useState } from "react";
import { toast } from "react-toastify";
import { VisibilityOff, Visibility } from "@mui/icons-material";
import { TextField, InputAdornment, IconButton } from "@mui/material";
import Button from "../../Components/Button/Button";
export default function ResetPasswordPage() {
const { uid, token } = useParams();
const [feedback, setFeedback] = useState("");
const [user, setUser] = useState({
password: "",
confirm_password: "",
});
const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate();
return (
<div style={styles.background}>
<div
style={{
...styles.flex_column,
...{
justifyContent: "center",
verticalAlign: "center",
height: "100%",
},
}}
>
<p style={{ ...styles.text_dark, ...styles.text_L }}>
Confirm Password Reset
</p>
<TextField
id="outlined-helperText"
type={showPassword ? "text" : "password"}
style={styles.input_form}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={() => {
setShowPassword(!showPassword);
setFeedback("");
}}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
label="New Password"
placeholder={"Enter new password"}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setUser({ ...user, password: e.target.value })
}
value={user.password}
/>
<TextField
id="outlined-helperText"
type={showPassword ? "text" : "password"}
style={styles.input_form}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={() => {
setShowPassword(!showPassword);
setFeedback("");
}}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
label="Confirm Password"
placeholder={"Re-enter password"}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setUser({ ...user, password: e.target.value })
}
value={user.password}
/>
<div style={{ justifyContent: "center", display: "flex" }}>
<div
style={{
backgroundColor: colors.header_color,
marginTop: "16px",
width: "80%",
height: "4px",
marginBottom: 8,
}}
/>{" "}
</div>
<p style={{ ...styles.text_dark, ...styles.text_M }}>{feedback}</p>
<Button
type={"dark"}
label={"Confirm"}
onClick={() => {
if (uid && token && feedback == "") {
ResetPasswordConfirmAPI({
uid,
token,
new_password: user.password,
}).then((response) => {
if (response) {
setFeedback("Reset successful");
toast("Reset successful", {
position: "top-right",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
});
toast("Please login to continue", {
position: "top-right",
autoClose: 6000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
});
setTimeout(() => {
navigate("/");
});
} else {
setFeedback("Invalid token specified for password reset");
}
});
}
if (!uid || !token) {
setFeedback("Missing token for password reset");
}
}}
/>
</div>
</div>
);
}