Hire+Plus! For Employees Here's how I built it (UI - Auth)
Tags: react, webdev, programming, tutorial
Published: 2022-08-05 16:18:00

## Overview: All views and functionality related to authentication, all funcs called are coming from the `authSlice` reducer. 

---

## Auth Route Page
**inside `routes > auth > auth-page.tsx`**
Outlet will render whatever is nested inside the `auth/employees` route.

```javascript
import { Outlet } from 'react-router';

const AuthPage = () => {
	return <Outlet />;
};

export default AuthPage;
```

---

## Auth Components

### Sign-in 
**inside `components > sign-in > sign-in.component.tsx`**
I imported `ChangeEvent`, `FormEvent` type interfaces from react to define my funcs with typescript. `defaultFormFields` is default state of form fields.

```javascript
import { ChangeEvent, FormEvent, useState } from 'react';
import BeatLoader from 'react-spinners/BeatLoader';
import { useAppDispatch, useAppSelector } from '../../app/hooks';
import { signInWithGoogle, signInWithEmailAndPassword } from '../../app/features/auth/authSlice';
import { resetError } from '../../app/features/auth/authSlice';
import { useNavigate } from 'react-router-dom';

const defaultFormFields = {
	email: '',
	password: '',
};
```

#### Functionality
**At the top:** I'll render any possible sign-in errors or loading states with `signInError` and `isLoading` props from `auth` state. I'll navigate to new routes on success using `navigate` func from `react-router-dom`. I handle the form states with `formFields` and `setFormFields`. 

**At the bottom:** 
`handleChange` - handle form field changes

`resetFormFields` - resets form after submission

`handleSubmit` - sends form data to redux action, resets the form, redirects to the main app.

`signInGooglePopup` - calls redux action to login using google, resets form, and redirects to the main app.

```javascript
const SignIn = () => {
	const { signInError, isLoading } = useAppSelector((state) => state.auth);
	const navigate = useNavigate();
	const dispatch = useAppDispatch();
	const [formFields, setFormFields] = useState(defaultFormFields);
	const { email, password } = formFields;

	const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
		const { name, value } = event.target;
		setFormFields({ ...formFields, [name]: value });
	};
	const resetFormFields = () => {
		setFormFields(defaultFormFields);
	};

	const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
		event.preventDefault();

		dispatch(signInWithEmailAndPassword(formFields))
			.unwrap()
			.then(() => {
				resetFormFields();
				navigate('/app');
			})
			.catch((error) => {
				resetFormFields();
			});
	};

	const signInGooglePopup = async () => {
		dispatch(signInWithGoogle())
			.unwrap()
			.then(() => {
				resetFormFields();
				navigate('/app');
			})
			.catch((error) => {
				dispatch(resetError());
			});
	};
	return (// removed for simplicity);
};

export default SignIn;

```

#### UI

```javascript
const SignIn = () => {
	// removed for simplicity
	return (
		<div className="items-center px-5 mt-10">
			<div className="flex flex-col w-full max-w-md p-6 mx-auto my-6 transition duration-500 ease-in-out transform rounded-lg md:mt-0 secondary-bg-color">
				<div>
					<div className="mb-8 mt-4">
						<h1 className="text-2xl lg:text-3xl text-center">
							Already have an account?
						</h1>
						<p className="text-center font-normal my-2 font-color">
							Sign in with your email and password
						</p>
					</div>
					{/* Error Handling */}
					{signInError && (
						<div className="text-center text-red-600 mb-5 text-lg">
							{signInError}
						</div>
					)}
					<div>
						{/* Form Starts */}
						<form className="space-y-6" onSubmit={handleSubmit}>
							{/* Email Field */}
							<div>
								<label
									htmlFor="email"
									className="block text-sm font-medium font-color"
								>
									{' '}
									Email address{' '}
								</label>
								<div className="mt-2">
									<input
										id="email"
										onChange={handleChange}
										value={email}
										name="email"
										type="email"
										required
										placeholder="e.g example@yahoo.com"
										className="font-color primary-bg-color block w-full px-5 py-3 text-base text-neutral-600 placeholder-gray-500 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-500"
									/>
								</div>
							</div>
							{/* Password Field */}
							<div className="space-y-1">
								<label
									htmlFor="pass"
									className="block text-sm font-medium font-color mb-2"
								>
									{' '}
									Password{' '}
								</label>
								<div>
									<input
										id="password"
										minLength={6}
										onChange={handleChange}
										value={password}
										name="password"
										type="password"
										data-testid="password"
										required
										placeholder="********"
										className="font-color primary-bg-color block w-full px-5 py-3 text-base text-neutral-600 placeholder-gray-500 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-bg-indigo-700 focus:ring-offset-2 focus:ring-offset-gray-300"
									/>
								</div>
							</div>
							{/* Submit Button */}
							<div>
								<button
									type="submit"
									className="flex items-center justify-center w-full px-10 py-4 text-base font-bold text-center text-white transition duration-500 ease-in-out transform rounded-xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 bg-indigo-700"
								>
									{isLoading ? (
										<div className="text-center z-index">
											<BeatLoader color={'white'} loading={true} />
										</div>
									) : (
										<p>Sign in</p>
									)}
								</button>
							</div>
						</form>
						{/* Google Sign-in */}
						<div className="relative my-4">
							<div className="absolute inset-0 flex items-center">
								<div className="w-full border-t border-gray-500"></div>
							</div>
							<div className="relative flex justify-center text-sm">
								<span className="px-2 text-neutral-800 bg-white">
									Or continue with
								</span>
							</div>
						</div>
						<div>
							<button
								onClick={signInGooglePopup}
								className="w-full items-center block px-10 py-3.5 text-base font-medium text-center text-blue-600 transition duration-500 ease-in-out transform border-2 border-gray-300 shadow-md rounded-xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
							>
								<div className="flex items-center justify-center">
									<svg
										xmlns="http://www.w3.org/2000/svg"
										viewBox="0 0 48 48"
										width="24px"
										height="24px"
									>
										<path
											fill="#fbc02d"
											d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12	s5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24s8.955,20,20,20	s20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
										/>
										<path
											fill="#e53935"
											d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039	l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
										/>
										<path
											fill="#4caf50"
											d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36	c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
										/>
										<path
											fill="#1565c0"
											d="M43.611,20.083L43.595,20L42,20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571	c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
										/>
									</svg>
									<span className="ml-4 text-white"> Log in with Google</span>
								</div>
							</button>
						</div>
					</div>
				</div>
			</div>
		</div>
	);
};

export default SignIn;

```

#### Screenshot

![Login component screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdv6a9tdiwirhk7jhi47.png)

---

### Sign-up
**inside `components > sign-up > sign-up.component.tsx`**
`defaultFormFields` is the default state of form fields

```javascript
import { ChangeEvent, FormEvent, useState } from 'react';
import { useNavigate } from 'react-router';
import { BeatLoader } from 'react-spinners';
import { signUpUserEmailAndPassword, setSignupError } from '../../app/features/auth/authSlice';
import { useAppDispatch, useAppSelector } from '../../app/hooks';

const defaultFormFields = {
	displayName: '',
	email: '',
	password: '',
	confirmPassword: '',
};
```

#### Functionality
Does majority of the same thing as sign-in form, handle form change and submission, redirect to main app. However, if passwords don't match `setSignupError` will dispatch with an error.

```javascript
const Signup = () => {
	const dispatch = useAppDispatch();

	const { isLoading, signUpError } = useAppSelector((state) => state.auth);
	const navigate = useNavigate();

	const [formFields, setFormFields] = useState(defaultFormFields);
	const { email, password, displayName, confirmPassword } = formFields;

	const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
		const { name, value } = event.target;
		setFormFields({ ...formFields, [name]: value });
	};
	const resetFormFields = () => {
		setFormFields(defaultFormFields);
	};
	const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
		event.preventDefault();
		if (password !== confirmPassword) {
			dispatch(setSignupError('Passwords must match'));
			return;
		}
		dispatch(
			signUpUserEmailAndPassword({
				email,
				password,
				displayName,
			})
		)
			.unwrap()
			.then(() => {
				resetFormFields();
				navigate('/app');
			})
			.catch((error) => {
				resetFormFields();
			});
	};
    return ({/* removed for simplicity */})
}
```

#### UI

```javascript
const Signup = () => {
	{/* removed for simplicity */}
	return (
		<div className="items-center px-5 mt-5">
			<div className="flex flex-col w-full max-w-md p-6 mx-auto transition duration-500 ease-in-out transform rounded-lg md:mt-0 secondary-bg-color">
				<div>
					<div className="mb-8 mt-4">
						<h1 className="text-2xl lg:text-3xl text-center">
							Dont have an account?
						</h1>
						<p className="text-center font-normal my-2 font-color">
							Sign up with your email and password
						</p>
					</div>
					{signUpError && (
						<div className="text-center text-red-600 mb-5 text-lg">
							{signUpError}
						</div>
					)}
					<div>
						<form onSubmit={handleSubmit} className="space-y-6">
							<div>
								<label
									htmlFor="name"
									className="block text-sm font-medium font-color"
								>
									Name
								</label>
								<div className="mt-2">
									<input
										value={displayName}
										onChange={handleChange}
										id="name"
										name="displayName"
										type="text"
										autoComplete="current-name"
										required
										placeholder="Enter your name"
										className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-400 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
									/>
								</div>
							</div>
							<div>
								<label
									htmlFor="email"
									className="block text-sm font-medium font-color"
								>
								
									Email address
								</label>
								<div className="mt-2">
									<input
										value={email}
										onChange={handleChange}
										id="email"
										name="email"
										type="email"
										autoComplete="current-email"
										required
										placeholder="Enter your email"
										className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-400 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
									/>
								</div>
							</div>
							<div className="space-y-1">
								<label
									htmlFor="password"
									className="block text-sm font-medium font-color"
								>
								
									Password
								</label>
								<div className="mt-2">
									<input
										id="password"
										name="password"
										type="password"
										onChange={handleChange}
										value={password}
										minLength={6}
										required
										data-testid="pass"
										placeholder="********"
										className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-300 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
									/>
								</div>
							</div>
							<div className="space-y-1">
								<label
									htmlFor="confirmPassword"
									className="block text-sm font-medium font-color"
								>
								
									Confirm Password
								</label>
								<div className="mt-1">
									<input
										id="confirmPassword"
										name="confirmPassword"
										type="password"
										onChange={handleChange}
										value={confirmPassword}
										data-testid="confirmPass"
										minLength={6}
										required
										placeholder="********"
										className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-300 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
									/>
								</div>
							</div>
							<div>
								<button
									type="submit"
									className="bg-indigo-700 flex items-center justify-center w-full px-10 py-4 text-base font-medium text-center text-white transition duration-500 ease-in-out transform rounded-xl hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
								>
									{isLoading ? (
										<div className="text-center z-index">
											<BeatLoader color={'white'} loading={true} />
										</div>
									) : (
										<p>Sign up</p>
									)}
								</button>
							</div>
						</form>
					</div>
				</div>
			</div>
		</div>
	);
};

export default Signup;

```

#### Screenshot

![Sign up component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o950j1zneygi13n21cql.png)


That's all for the UI/Auth portion of the project, stay tuned!