#7 Introduce proper error handling on sign-up

This commit is contained in:
Christoph Lienhard 2020-12-27 21:01:37 +01:00
parent b6f6adac22
commit 2979f1b1af
Signed by: christoph.lienhard
GPG key ID: 6B98870DDC270884
2 changed files with 112 additions and 10 deletions

View file

@ -1,8 +1,8 @@
import React, {useState} from 'react';
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} from 'react-router-dom';
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';
@ -12,6 +12,7 @@ import Container from '@material-ui/core/Container';
import {Copyright} from "./Copyright";
import {gql, useMutation} from "@apollo/client";
import ButtonWithSpinner from "./ButtonWithSpinner";
import {errorHandler, SignUpError} from "./SignUpErrorHandler";
const useStyles = makeStyles((theme) => ({
@ -45,14 +46,49 @@ export default function SignUp() {
const [password, setPassword] = useState("");
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [createAccount, {loading, error, data}] = useMutation<RegisterMutationResponse, RegisterMutationVariables>(
registerMutation
const [error, setError] = useState<SignUpError | undefined>(undefined)
const history = useHistory();
const [createAccount, {loading, data}] = useMutation<RegisterMutationResponse, RegisterMutationVariables>(
registerMutation,
{
onCompleted: (data) => {history.push("/login")},
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}}).catch(error => console.log(error));
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 (
@ -78,7 +114,8 @@ export default function SignUp() {
label="First Name"
autoFocus
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
onChange={onFirstNameChange}
error={error?.firstNameInvalid}
/>
</Grid>
<Grid item xs={12} sm={6}>
@ -91,7 +128,8 @@ export default function SignUp() {
name="lastName"
autoComplete="lname"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
onChange={onLastNameChange}
error={error?.lastNameInvalid}
/>
</Grid>
<Grid item xs={12}>
@ -104,7 +142,8 @@ export default function SignUp() {
name="email"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onChange={onEmailChange}
error={error?.emailInvalid}
/>
</Grid>
<Grid item xs={12}>
@ -118,7 +157,8 @@ export default function SignUp() {
id="password"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
onChange={onPasswordChange}
error={error?.passwordInvalid}
/>
</Grid>
</Grid>
@ -131,7 +171,7 @@ export default function SignUp() {
</ButtonWithSpinner>
{
error
? <p className={classes.error}> `Error while trying to create a new account: ${error.message}`}</p>
? <p className={classes.error}>{error.message}</p>
: data && data.registerPerson.person.nodeId
? <p className={classes.success}>`Created new account successfully.
<Link to={"/login"}>Return to login page</Link></p>

View file

@ -0,0 +1,62 @@
import {ApolloError} from "@apollo/client";
export interface SignUpError {
message: string,
emailInvalid: boolean,
firstNameInvalid: boolean,
lastNameInvalid: boolean,
passwordInvalid: boolean
}
const parseErrorMessage = (error: ApolloError): string => {
let result = "Sign-up failed because of the following reason(s): ";
if (isEmailAlreadyUsed(error)) {
result += "The E-Mail is already in use. "
}
if (isFirstNameInvalid(error)) {
result += "The provided 'First Name' is invalid. "
}
if (isLastNameInvalid(error)) {
result += "The provided 'Last Name' is invalid. "
}
if (isPasswordInvalid(error)) {
result += "The provided password is invalid. "
}
return result
}
const isEmailAlreadyUsed = (error: ApolloError): boolean => {
const errorMessage = error.message.toLowerCase();
return errorMessage.includes("unique-constraint") && errorMessage.includes("email");
}
const isFirstNameInvalid = (error: ApolloError): boolean => {
const errorMessage = error.message.toLowerCase();
return errorMessage.includes("invalid") && errorMessage.includes("first name");
}
const isLastNameInvalid = (error: ApolloError): boolean => {
const errorMessage = error.message.toLowerCase();
return errorMessage.includes("invalid") && errorMessage.includes("last name");
}
const isPasswordInvalid = (error: ApolloError): boolean => {
const errorMessage = error.message.toLowerCase();
return errorMessage.includes("invalid") && errorMessage.includes("password");
}
export const errorHandler = (error: undefined | ApolloError): undefined | SignUpError => {
return error ? {
message: parseErrorMessage(error),
emailInvalid: isEmailAlreadyUsed(error),
firstNameInvalid: isFirstNameInvalid(error),
lastNameInvalid: isLastNameInvalid(error),
passwordInvalid: isPasswordInvalid(error)
} : undefined
}