Skip to content

Commit

Permalink
Forget password restructured (#140)
Browse files Browse the repository at this point in the history
* Moved changes frorm forget-password branch to a new branch to deal with restructured files - frontend

* Moved changes frorm forget-password branch to a new branch to deal with restructured files - backend

* modified users.controller to plan to send email from within the try and use a Response Type for cleaner error handling as per James

* removed some old commented out bits

* cleared form after submit takes place

* button changes while loading

* added type to resestPassword in users.controller and set error as nullin ForgetPassword.tsx set error as null in EmailInput to ease changes when error handeling is set inside EmailInput entirely
  • Loading branch information
kevhines authored Feb 25, 2022
1 parent 805e80a commit 342b3ed
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 4 deletions.
5 changes: 5 additions & 0 deletions client/src/features/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ function Main() {

{/* users */}
<Route exact path={routes.Login.path} component={routes.Login.component} />
<Route
exact
path={routes.ForgotPassword.path}
component={routes.ForgotPassword.component}
/>
<Route exact path={routes.Signup.path} component={routes.Signup.component} />
<Route exact path={routes.SignupCitizen.path} component={routes.SignupCitizen.component} />
<Route
Expand Down
6 changes: 4 additions & 2 deletions client/src/features/action/assets/SimpleSnackbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import * as React from 'react';
import Snackbar from '@material-ui/core/Snackbar';
import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
type Props = { message?: string };

export default function SimpleSnackbar() {
export default function SimpleSnackbar(props: Props) {
let message = props.message || 'You Claimed This!';
const [isOpen, isSetOpen] = React.useState<boolean>(true);
const handleClose = (event?: React.SyntheticEvent, reason?: string) => {
isSetOpen(false);
Expand All @@ -20,7 +22,7 @@ export default function SimpleSnackbar() {
<CloseIcon />
</IconButton>,
]}
message={<span id="message-id">You claimed this! </span>}
message={<span id="message-id">{message}</span>}
/>
</div>
);
Expand Down
130 changes: 130 additions & 0 deletions client/src/features/users/Auth/ForgotPassword.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as React from 'react';
import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import SimpleSnackbar from '../../action/assets/SimpleSnackbar';

import type { Theme } from '@material-ui/core/styles';

import EmailInput from './EmailInput';

const useStyles = makeStyles((theme: Theme) => {
const xPadding = 12;
const yPadding = 6;
const yMargin = 8;

return {
paper: {
maxWidth: 821 - theme.spacing(xPadding),
maxHeight: 732 - theme.spacing(yPadding),
borderRadius: 10,
marginTop: theme.spacing(yMargin),
marginBottom: theme.spacing(yMargin),
paddingTop: theme.spacing(yPadding),
paddingBottom: theme.spacing(yPadding),
paddingLeft: theme.spacing(xPadding),
paddingRight: theme.spacing(xPadding),
margin: 'auto',
},
header: { fontWeight: 'bold', marginBottom: 68 },
button: {
borderRadius: 0,
height: 62,
textTransform: 'none',
},
};
});

interface UserLoginData {
email: string;
}

const initialFormData: UserLoginData = {
email: '',
};

function ForgotPassword() {
const classes = useStyles();
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const [showSnackbar, setShowSnackbar] = React.useState<boolean>(false);
const [formData, setFormData] = React.useState(initialFormData);
const instructions =
"Enter your email and we'll send you a link to reset you password if you have an account.";
const emailResponse =
'If this email exists as a user you will be sent an email to reset your password.';

const generateForm = () => {
return (
<Grid container item xs={12}>
<form onSubmit={handleSubmit} style={{ width: '100%' }}>
<EmailInput
value={formData.email}
placeholder="[email protected]"
onChange={handleChange}
error={null}
/>
<Button
className={classes.button}
style={{ backgroundColor: '#C4C4C4', color: 'white' }}
fullWidth
type="submit"
disabled={isLoading}
>
{isLoading ? 'sending...' : 'Send Email To Set New Password'}
</Button>
</form>
</Grid>
);
};

const handleChange = (evt: React.ChangeEvent<HTMLInputElement>): void => {
const { name, value }: { name: string; value: string } = evt.target;
setFormData((fData) => ({
...fData,
[name]: value,
}));
};

const handleSubmit = async (evt: React.FormEvent): Promise<void> => {
evt.preventDefault();
setIsLoading(true);

await fetch('http://localhost:3001/api/users/reset_password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});

setIsLoading(false);
setShowSnackbar(true);
setFormData((fData) => ({
...fData,
email: '',
}));
};

return (
<div className="Login" style={{ justifyContent: 'center', alignItems: 'center' }}>
<Paper elevation={3} className={classes.paper}>
<Grid container justifyContent="center" direction="column" spacing={2}>
<Grid item xs={12}>
<Typography className={classes.header} variant="h3" component="h1" align="center">
Forgot Your Password?
</Typography>
<Typography component="p" align="left" gutterBottom>
{showSnackbar ? emailResponse : instructions}
</Typography>
{showSnackbar ? null : generateForm()}
</Grid>
</Grid>
</Paper>
{showSnackbar ? <SimpleSnackbar message="Email Has Sent!" /> : null}
</div>
);
}

export default ForgotPassword;
3 changes: 2 additions & 1 deletion client/src/features/users/Auth/PasswordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import FormHelperText from '@material-ui/core/FormHelperText';
import type { Theme } from '@material-ui/core/styles';

import StyledLink from '../../../assets/sharedComponents/StyledLink';
import routes from '../../../routes';

const useStyles = makeStyles((theme: Theme) => {
return {
Expand Down Expand Up @@ -62,7 +63,7 @@ function PasswordInput({
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
Password
{/* to prop to be updated to use routes once page is set up */}
{showForgot && <StyledLink to="/forgot-password">Forgot Password?</StyledLink>}
{showForgot && <StyledLink to={routes.ForgotPassword.path}>Forgot Password?</StyledLink>}
</div>
</label>
{error && <FormHelperText error>{error}</FormHelperText>}
Expand Down
5 changes: 5 additions & 0 deletions client/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import OfferFormGoods from './features/action/offers/OfferFormGoods';
import OfferFormSkills from './features/action/offers/OfferFormSkills';
import ContactUs from './features/support/ContactUs';
import Help from './features/support/Help';
import ForgotPassword from './features/users/Auth/ForgotPassword';

const routes = {
Home: {
Expand Down Expand Up @@ -52,6 +53,10 @@ const routes = {
component: PrivacyPolicy,
path: '/privacy-policy',
},
ForgotPassword: {
component: ForgotPassword,
path: '/forgot-password',
},
Login: {
component: Login,
path: '/login',
Expand Down
27 changes: 26 additions & 1 deletion server/src/users/users.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe } from '@nestjs/common';
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Request,
Response,
ParseIntPipe,
} from '@nestjs/common';
import type { Response as ResponseT } from 'express';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
Expand All @@ -17,6 +29,19 @@ export class UsersController {
return user;
}

@Post('reset_password')
async resetPassword(
@Request() req,
@Response({ passthrough: true }) response: ResponseT,
): Promise<void> {
try {
const user = await this.usersService.findByEmail(req.body.email);
//TODO send email the user
} catch (e) {
response.status(200).send();
}
}

@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findOne(id);
Expand Down

0 comments on commit 342b3ed

Please sign in to comment.