diff --git a/webclient/src/App.js b/webclient/src/App.js
index 2cdb843..e05976d 100644
--- a/webclient/src/App.js
+++ b/webclient/src/App.js
@@ -19,6 +19,7 @@ import { Courses, CourseDetail } from './Courses.js';
import { Classes, ClassDetail } from './Classes.js';
import { Members, MemberDetail } from './Members.js';
import { Charts } from './Charts.js';
+import { PasswordReset, ConfirmReset } from './PasswordReset.js';
import { NotFound, PleaseLogin } from './Misc.js';
import { Footer } from './Footer.js';
@@ -193,6 +194,13 @@ function App() {
+
+
+
+
+
+
+
diff --git a/webclient/src/LoginSignup.js b/webclient/src/LoginSignup.js
index 7226b9f..eb4c8ae 100644
--- a/webclient/src/LoginSignup.js
+++ b/webclient/src/LoginSignup.js
@@ -62,6 +62,11 @@ export function LoginForm(props) {
Log In
+
+
+ Forgot your password?
+ Click here to reset it.
+
);
};
diff --git a/webclient/src/PasswordReset.js b/webclient/src/PasswordReset.js
new file mode 100644
index 0000000..7ca2e66
--- /dev/null
+++ b/webclient/src/PasswordReset.js
@@ -0,0 +1,148 @@
+import React, { useState, useEffect, useReducer, useContext } from 'react';
+import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom';
+import { Button, Container, Checkbox, Dimmer, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
+import { apiUrl, statusColor, BasicTable, staticUrl, requester } from './utils.js';
+import { NotFound } from './Misc.js';
+
+function ResetForm() {
+ const [input, setInput] = useState({});
+ const [error, setError] = useState({});
+ const [loading, setLoading] = useState(false);
+ const [success, setSuccess] = useState(false);
+
+ const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value });
+ const handleChange = (e) => handleValues(e, e.currentTarget);
+
+ const handleSubmit = (e) => {
+ if (loading) return;
+ setLoading(true);
+ requester('/password/reset/', 'POST', '', input)
+ .then(res => {
+ setLoading(false);
+ setSuccess(true);
+ setError({});
+ })
+ .catch(err => {
+ setLoading(false);
+ console.log(err);
+ setError(err.data);
+ });
+ };
+
+ const makeProps = (name) => ({
+ name: name,
+ onChange: handleChange,
+ value: input[name] || '',
+ error: error[name],
+ });
+
+ return (
+
+
+
+ Submit
+
+ {success && Success!
}
+
+ );
+};
+
+function ConfirmForm() {
+ const { uid, token } = useParams();
+ const [input, setInput] = useState({ uid: uid, token: token });
+ const [error, setError] = useState({});
+ const [loading, setLoading] = useState(false);
+ const [success, setSuccess] = useState(false);
+ const history = useHistory();
+
+ const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value });
+ const handleChange = (e) => handleValues(e, e.currentTarget);
+
+ const handleSubmit = (e) => {
+ if (loading) return;
+ setLoading(true);
+ requester('/rest-auth/password/reset/confirm/', 'POST', '', input)
+ .then(res => {
+ setLoading(false);
+ setSuccess(true);
+ setError({});
+ history.push('/');
+ window.scrollTo(0, 0);
+ })
+ .catch(err => {
+ setLoading(false);
+ console.log(err);
+ setError(err.data);
+ });
+ };
+
+ const makeProps = (name) => ({
+ name: name,
+ onChange: handleChange,
+ value: input[name] || '',
+ error: error[name],
+ });
+
+ return (
+
+
+
+ {(error.token || error.uid) && Error: Invalid password reset URL! Try doing another reset.
}
+
+
+ Submit
+
+ {success && Success!
}
+
+ );
+};
+
+
+export function PasswordReset() {
+ return (
+
+
+
+
+
+ Enter your email and we will send you a password reset link.
+
+
+
+
+
+
+ );
+};
+
+export function ConfirmReset() {
+ return (
+
+
+
+
+
+ Choose a new password.
+
+
+
+
+
+
+ );
+};