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 ( + +
Password Reset
+ + +

+ Enter your email and we will send you a password reset link. +

+ + +
+
+
+ ); +}; + +export function ConfirmReset() { + return ( + +
Password Reset
+ + +

+ Choose a new password. +

+ + +
+
+
+ ); +};