208 lines
5.8 KiB
TypeScript
208 lines
5.8 KiB
TypeScript
import React, { ChangeEvent, useState } from "react";
|
|
import Avatar from "@material-ui/core/Avatar";
|
|
import CssBaseline from "@material-ui/core/CssBaseline";
|
|
import TextField from "@material-ui/core/TextField";
|
|
import { Link, useHistory } from "react-router-dom";
|
|
import Grid from "@material-ui/core/Grid";
|
|
import Box from "@material-ui/core/Box";
|
|
import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
|
|
import Typography from "@material-ui/core/Typography";
|
|
import { makeStyles } from "@material-ui/core/styles";
|
|
import Container from "@material-ui/core/Container";
|
|
import { Copyright } from "./Copyright";
|
|
import { useMutation } from "@apollo/client";
|
|
import ButtonWithSpinner from "./ButtonWithSpinner";
|
|
import { errorHandler, SignUpError } from "./SignUpErrorHandler";
|
|
import { Alert } from "@material-ui/lab";
|
|
import {
|
|
SIGN_UP,
|
|
SignUpResponse,
|
|
SignUpVariables,
|
|
} from "../backend/mutations/signUp";
|
|
|
|
const useStyles = makeStyles((theme) => ({
|
|
paper: {
|
|
marginTop: theme.spacing(8),
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
},
|
|
avatar: {
|
|
margin: theme.spacing(1),
|
|
backgroundColor: theme.palette.secondary.main,
|
|
},
|
|
form: {
|
|
width: "100%", // Fix IE 11 issue.
|
|
marginTop: theme.spacing(3),
|
|
},
|
|
submit: {
|
|
margin: theme.spacing(3, 0, 2),
|
|
},
|
|
error: {
|
|
color: "red",
|
|
},
|
|
alert: {
|
|
margin: theme.spacing(1),
|
|
},
|
|
}));
|
|
|
|
export default function SignUp(): React.ReactElement {
|
|
const [email, setEmail] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [firstName, setFirstName] = useState("");
|
|
const [lastName, setLastName] = useState("");
|
|
const [error, setError] = useState<SignUpError | undefined>(undefined);
|
|
const history = useHistory();
|
|
const [createAccount, { loading }] = useMutation<
|
|
SignUpResponse,
|
|
SignUpVariables
|
|
>(SIGN_UP, {
|
|
onCompleted() {
|
|
history.push("/login?recent-sign-up-success=true");
|
|
},
|
|
onError(e) {
|
|
console.error(e);
|
|
setPassword("");
|
|
setError(errorHandler(e));
|
|
},
|
|
});
|
|
const classes = useStyles();
|
|
|
|
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
event.preventDefault();
|
|
createAccount({ variables: { firstName, lastName, email, password } });
|
|
};
|
|
|
|
const onFirstNameChange = (
|
|
e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
|
) => {
|
|
setFirstName(e.target.value);
|
|
if (error?.firstNameInvalid) {
|
|
setError(undefined);
|
|
}
|
|
};
|
|
|
|
const onLastNameChange = (
|
|
e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
|
) => {
|
|
setLastName(e.target.value);
|
|
if (error?.lastNameInvalid) {
|
|
setError(undefined);
|
|
}
|
|
};
|
|
|
|
const onEmailChange = (
|
|
e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
|
) => {
|
|
setEmail(e.target.value);
|
|
if (error?.emailInvalid) {
|
|
setError(undefined);
|
|
}
|
|
};
|
|
|
|
const onPasswordChange = (
|
|
e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
|
) => {
|
|
setPassword(e.target.value);
|
|
if (error?.passwordInvalid) {
|
|
setError(undefined);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Container component="main" maxWidth="xs">
|
|
<CssBaseline />
|
|
<div className={classes.paper}>
|
|
<Avatar className={classes.avatar}>
|
|
<LockOutlinedIcon />
|
|
</Avatar>
|
|
<Typography component="h1" variant="h5">
|
|
Sign up
|
|
</Typography>
|
|
<form className={classes.form} noValidate onSubmit={onSubmit}>
|
|
<Grid container spacing={2}>
|
|
<Grid item xs={12} sm={6}>
|
|
<TextField
|
|
autoComplete="fname"
|
|
name="firstName"
|
|
variant="outlined"
|
|
required
|
|
fullWidth
|
|
id="firstName"
|
|
label="First Name"
|
|
autoFocus
|
|
value={firstName}
|
|
onChange={onFirstNameChange}
|
|
error={error?.firstNameInvalid}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6}>
|
|
<TextField
|
|
variant="outlined"
|
|
required
|
|
fullWidth
|
|
id="lastName"
|
|
label="Last Name"
|
|
name="lastName"
|
|
autoComplete="lname"
|
|
value={lastName}
|
|
onChange={onLastNameChange}
|
|
error={error?.lastNameInvalid}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
variant="outlined"
|
|
required
|
|
fullWidth
|
|
id="email"
|
|
label="Email Address"
|
|
name="email"
|
|
autoComplete="email"
|
|
value={email}
|
|
onChange={onEmailChange}
|
|
error={error?.emailInvalid}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
variant="outlined"
|
|
required
|
|
fullWidth
|
|
name="password"
|
|
label="Password"
|
|
type="password"
|
|
id="password"
|
|
autoComplete="current-password"
|
|
value={password}
|
|
onChange={onPasswordChange}
|
|
error={error?.passwordInvalid}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
<ButtonWithSpinner loading={loading} type="submit" fullWidth>
|
|
Sign Up
|
|
</ButtonWithSpinner>
|
|
<Grid container justify="flex-end">
|
|
<Grid item>
|
|
<Link to="/login">Already have an account? Sign in</Link>
|
|
</Grid>
|
|
</Grid>
|
|
{error ? (
|
|
<Alert
|
|
className={classes.alert}
|
|
severity="error"
|
|
onClose={() => setError(undefined)}
|
|
>
|
|
{error.message}
|
|
</Alert>
|
|
) : null}
|
|
</form>
|
|
</div>
|
|
<Box mt={5}>
|
|
<Copyright />
|
|
</Box>
|
|
</Container>
|
|
);
|
|
}
|