mirror of
https://github.com/lemeow125/EquipmentTracker-Frontend.git
synced 2024-11-17 06:09:25 +08:00
Added reset password modal and confirm reset password page
This commit is contained in:
parent
742a1af9f8
commit
a8de8476e5
8 changed files with 335 additions and 18 deletions
10
src/App.tsx
10
src/App.tsx
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
91
src/Components/ResetPasswordModal/ResetPasswordModal.tsx
Normal file
91
src/Components/ResetPasswordModal/ResetPasswordModal.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
148
src/Pages/ResetPasswordPage/ResetPasswordPage.tsx
Normal file
148
src/Pages/ResetPasswordPage/ResetPasswordPage.tsx
Normal 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>
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue