Hire+Plus! For Employees Here's how I built it (UI - Modals)
Tags: react, webdev, programming, tutorial
Published: 2022-08-06 08:50:14

## Project Modal Component
**inside `components > modal > project-modal.component.tsx`**
`ProjectPopupModalProps`, defines the types of props being passed into this component. `defaultFormFields` fields of the modal to edit. Used `defaultFormFields` as the default state for `projectFields`. 

```javascript
import React, { ChangeEvent, useState } from 'react';
import { setProjects } from '../../app/features/profile/profileSlice';
import { useAppDispatch, useAppSelector } from '../../app/hooks';

interface ProjectPopupModalProps {
	isProjOpen: boolean;
	closeProjModal: () => void;
}
const defaultFormFields = {
	date: '',
	title: '',
	summary: '',
	github: '',
	projectUrl: '',
};
```

### Functionality
`onHandleChange`, `onTextareaChange` - handle state changes for the form fields.

`resetFormFields` - resets form fields after submission.

`addProject` - set projects for the `profile.projects` state within `profileSlice`, resets form, and close modal afterward.

```javascript
const ProjectPopupModal: React.FC<ProjectPopupModalProps> = ({
	isProjOpen,
	closeProjModal,
}) => {
	const dispatch = useAppDispatch();

	const { profile } = useAppSelector((state) => state.profile);

	const [projectFields, setProjectFields] = useState(defaultFormFields);

	const onHandleChange = (e: ChangeEvent<HTMLInputElement>) => {
		const { name, value } = e.target;
		setProjectFields({ ...projectFields, [name]: value });
	};
	const onTextareaChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
		const { value } = e.target;
		setProjectFields({ ...projectFields, summary: value });
	};
	const resetFormFields = () => {
		setProjectFields(defaultFormFields);
	};
	const addProject = (e: ChangeEvent<HTMLFormElement>) => {
		e.preventDefault();
		dispatch(
			setProjects([
				{
					date: projectFields.date,
					title: projectFields.title,
					summary: projectFields.summary,
					github: projectFields.github,
					projectUrl: projectFields.projectUrl,
				},
				...profile.projects,
			])
		);
		resetFormFields();
		setProjectFields(projectFields);
		closeProjModal();
	};
	return ( {/* removed for simplicity */} );
};

export default ProjectPopupModal;

```

### UI
Renders the pop-up modal if the `isProjOpen` is set to true, and all the fields to edit.

```javascript
const ProjectPopupModal: React.FC<ProjectPopupModalProps> = ({
	isProjOpen,
	closeProjModal,
}) => {
	{/* removed for simplicity */}
	return (
		<div
			className="py-12 bg-gray-700 transition duration-150 ease-in-out z-10 absolute right-0 bottom-0 left-0 h-screen"
			id="modal"
			style={{ display: `${isProjOpen ? 'block' : 'none'}`, top: '850px' }}
		>
			<div role="alert" className="container mx-auto w-11/12 md:w-2/3 max-w-lg">
				<div className="relative py-8 px-5 md:px-10 bg-white shadow-md rounded border border-gray-400">
					<div className="w-full flex justify-center text-gray-600 mb-3">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							className="icon icon-tabler icon-tabler-wallet"
							width="52"
							height="52"
							viewBox="0 0 24 24"
							strokeWidth="1"
							stroke="currentColor"
							fill="none"
							strokeLinecap="round"
							strokeLinejoin="round"
						>
							<path stroke="none" d="M0 0h24v24H0z" />
							<path d="M17 8v-3a1 1 0 0 0 -1 -1h-10a2 2 0 0 0 0 4h12a1 1 0 0 1 1 1v3m0 4v3a1 1 0 0 1 -1 1h-12a2 2 0 0 1 -2 -2v-12" />
							<path d="M20 12v4h-4a2 2 0 0 1 0 -4h4" />
						</svg>
					</div>
					<h1 className="text-gray-800 font-lg font-bold tracking-normal leading-tight mb-4 text-center text-lg">
						Add Project
					</h1>
					<form onSubmit={addProject}>
						<label
							htmlFor="name"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Project Name
						</label>
						<input
							type="text"
							required
							name="title"
							onChange={onHandleChange}
							value={projectFields.title}
							id="name"
							className="mb-5 mt-2 text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full h-10 flex items-center pl-3 text-sm border-gray-300 rounded border"
							placeholder="Full stack react"
						/>
						{/* Project Date */}
						<label
							htmlFor="date"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Date
						</label>
						<div className="relative mb-5 mt-2">
							<div className="absolute right-0 text-gray-600 flex items-center pr-3 h-full cursor-pointer">
								<svg
									xmlns="http://www.w3.org/2000/svg"
									className="icon icon-tabler icon-tabler-calendar-event"
									width="20"
									height="20"
									viewBox="0 0 24 24"
									strokeWidth="1.5"
									stroke="currentColor"
									fill="none"
									strokeLinecap="round"
									strokeLinejoin="round"
								>
									<path stroke="none" d="M0 0h24v24H0z" />
									<rect x="4" y="5" width="16" height="16" rx="2" />
									<line x1="16" y1="3" x2="16" y2="7" />
									<line x1="8" y1="3" x2="8" y2="7" />
									<line x1="4" y1="11" x2="20" y2="11" />
									<rect x="8" y="15" width="2" height="2" />
								</svg>
							</div>
							<input
								name="date"
								onChange={onHandleChange}
								value={projectFields.date}
								id="date"
								className="text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full h-10 flex items-center pl-3 text-sm border-gray-300 rounded border"
								placeholder="MM/YY"
							/>
						</div>
						{/* project github */}
						<label
							htmlFor="github"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Project Github
						</label>
						<input
							type="text"
							name="github"
							onChange={onHandleChange}
							value={projectFields.github}
							id="github"
							className="mb-5 mt-2 text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full h-10 flex items-center pl-3 text-sm border-gray-300 rounded border"
							placeholder="www.projectOne@github.com"
							required
						/>
						<label
							htmlFor="projectUrl"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Project URL
						</label>
						<input
							name="projectUrl"
							onChange={onHandleChange}
							value={projectFields.projectUrl}
							id="projectUrl"
							className="mb-5 mt-2 text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full h-10 flex items-center pl-3 text-sm border-gray-300 rounded border"
							placeholder="www.coolwebapp.com"
							required
						/>
						<label
							htmlFor="description"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Project Summary
						</label>
						<textarea
							name="summary"
							onChange={onTextareaChange}
							value={projectFields.summary}
							maxLength={300}
							id="description"
							className="mb-5 mt-2 text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full flex items-center px-3 py-3 text-sm border-gray-300 rounded border"
							placeholder="Authentication, testing, etc."
							required
						/>
						<div className="flex items-center justify-start w-full">
							<button
								type="submit"
								className="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 transition duration-150 ease-in-out hover:bg-indigo-600 bg-indigo-700 rounded text-white px-8 py-2 text-sm"
							>
								Submit
							</button>
							<button
								type="button"
								onClick={closeProjModal}
								aria-label="close modal"
								className="focus:outline-none focus:ring-2 focus:ring-offset-2  focus:ring-gray-400 ml-3 bg-gray-100 transition duration-150 text-gray-600 ease-in-out hover:border-gray-400 hover:bg-gray-300 border rounded px-8 py-2 text-sm"
							>
								Cancel
							</button>
						</div>
					</form>
					<button
						className="cursor-pointer absolute top-0 right-0 mt-4 mr-5 text-gray-400 hover:text-gray-600 transition duration-150 ease-in-out rounded focus:ring-2 focus:outline-none focus:ring-gray-600"
						aria-label="close modal"
						onClick={closeProjModal}
					>
						<svg
							xmlns="http://www.w3.org/2000/svg"
							className="icon icon-tabler icon-tabler-x"
							width="20"
							height="20"
							viewBox="0 0 24 24"
							strokeWidth="2.5"
							stroke="currentColor"
							fill="none"
							strokeLinecap="round"
							strokeLinejoin="round"
						>
							<path stroke="none" d="M0 0h24v24H0z" />
							<line x1="18" y1="6" x2="6" y2="18" />
							<line x1="6" y1="6" x2="18" y2="18" />
						</svg>
					</button>
				</div>
			</div>
		</div>
	);
};

export default ProjectPopupModal;

```

### Screenshot

![project modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qbdkfy0xo8h6fclx1u7.png)

---

## Experience Modal Component
**inside `components > modal > experience-modal.component.tsx`**
`ExperiencePopupModalProps`, defines the types of props being passed into this component. `defaultFormFields` fields of the modal to edit. Used `defaultFormFields` as the default state for `experienceFields`. 

```javascript
import { ChangeEvent, useState } from 'react';
import { setExperiences } from '../../app/features/profile/profileSlice';
import { useAppDispatch, useAppSelector } from '../../app/hooks';

interface ExperiencePopupModalProps {
	isOpen: boolean;
	closeModal: () => void;
}
const defaultFormFields = {
	date: '',
	position: '',
	positionSummary: '',
};
```

### Functionality
`onHandleChange`, `onTextareaChange` - handle state changes for the form fields.

`resetFormFields` - resets form fields after submission.

`addExperience` - set experiences for the `profile.experience` state within `profileSlice`, resets form, and close modal afterward.

```javascript
const ExperiencePopupModal: React.FC<ExperiencePopupModalProps> = ({
	isOpen,
	closeModal,
}) => {
	const dispatch = useAppDispatch();
	const { profile } = useAppSelector((state) => state.profile);
	const [experienceFields, setExperienceFields] = useState(defaultFormFields);

	const onHandleChange = (e: ChangeEvent<HTMLInputElement>) => {
		const { name, value } = e.target;
		setExperienceFields({ ...experienceFields, [name]: value });
	};
	const onTextareaChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
		const { value } = e.target;
		setExperienceFields({ ...experienceFields, positionSummary: value });
	};
	const resetFormFields = () => {
		setExperienceFields(defaultFormFields);
	};

	const addExperience = (e: ChangeEvent<HTMLFormElement>) => {
		e.preventDefault();
		dispatch(
			setExperiences([
				{
					position: experienceFields.position,
					positionSummary: experienceFields.positionSummary,
					date: experienceFields.date,
				},
				...profile.experience,
			])
		);
		resetFormFields();
		closeModal();
	};
	return ( {/* removed for simplicity */} );
};

export default ExperiencePopupModal;

```

### UI
Renders the pop-up modal and all the fields to edit.

```javascript
const ExperiencePopupModal: React.FC<ExperiencePopupModalProps> = ({
	isOpen,
	closeModal,
}) => {
	{/* removed for simplicity */}
	return (
		<div
			className="py-12 bg-gray-700 transition duration-150 ease-in-out z-10 absolute right-0 bottom-0 left-0 h-screen"
			id="modal"
			style={{ display: `${isOpen ? 'block' : 'none'}`, top: '880px' }}
		>
			<div role="alert" className="container mx-auto w-11/12 md:w-2/3 max-w-lg">
				<div className="relative py-8 px-5 md:px-10 bg-white shadow-md rounded border border-gray-400">
					<div className="w-full flex justify-center text-gray-600 mb-3">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							className="icon icon-tabler icon-tabler-wallet"
							width="52"
							height="52"
							viewBox="0 0 24 24"
							strokeWidth="1"
							stroke="currentColor"
							fill="none"
							strokeLinecap="round"
							strokeLinejoin="round"
						>
							<path stroke="none" d="M0 0h24v24H0z" />
							<path d="M17 8v-3a1 1 0 0 0 -1 -1h-10a2 2 0 0 0 0 4h12a1 1 0 0 1 1 1v3m0 4v3a1 1 0 0 1 -1 1h-12a2 2 0 0 1 -2 -2v-12" />
							<path d="M20 12v4h-4a2 2 0 0 1 0 -4h4" />
						</svg>
					</div>
					<h1 className="text-gray-800 font-lg font-bold tracking-normal leading-tight mb-4 text-center text-lg">
						Add Job Experience
					</h1>
					<form onSubmit={addExperience}>
						<label
							htmlFor="title"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Position Title
						</label>
						<input
							required
							name="position"
							onChange={onHandleChange}
							value={experienceFields.position}
							id="title"
							className="mb-5 mt-2 text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full h-10 flex items-center pl-3 text-sm border-gray-300 rounded border"
							placeholder="Front-End Developer"
						/>
						<label
							htmlFor="description"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Position Summary
						</label>
						<textarea
							required
							name="description"
							onChange={onTextareaChange}
							value={experienceFields.positionSummary}
							maxLength={1000}
							id="description"
							className="mb-5 mt-2 text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full flex items-center px-3 py-3 text-sm border-gray-300 rounded border"
							placeholder="Job details..."
						/>
						<label
							htmlFor="date"
							className="text-gray-800 text-sm font-bold leading-tight tracking-normal"
						>
							Date
						</label>
						<div className="relative mb-5 mt-2">
							<div className="absolute right-0 text-gray-600 flex items-center pr-3 h-full cursor-pointer">
								<svg
									xmlns="http://www.w3.org/2000/svg"
									className="icon icon-tabler icon-tabler-calendar-event"
									width="20"
									height="20"
									viewBox="0 0 24 24"
									strokeWidth="1.5"
									stroke="currentColor"
									fill="none"
									strokeLinecap="round"
									strokeLinejoin="round"
								>
									<path stroke="none" d="M0 0h24v24H0z" />
									<rect x="4" y="5" width="16" height="16" rx="2" />
									<line x1="16" y1="3" x2="16" y2="7" />
									<line x1="8" y1="3" x2="8" y2="7" />
									<line x1="4" y1="11" x2="20" y2="11" />
									<rect x="8" y="15" width="2" height="2" />
								</svg>
							</div>
							<input
								required
								name="date"
								onChange={onHandleChange}
								value={experienceFields.date}
								id="date"
								className="text-gray-600 focus:outline-none focus:border focus:border-indigo-700 font-normal w-full h-10 flex items-center pl-3 text-sm border-gray-300 rounded border"
								placeholder="MM/YY"
							/>
						</div>

						<div className="flex items-center justify-start w-full">
							<button
								type="submit"
								className="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 transition duration-150 ease-in-out hover:bg-indigo-600 bg-indigo-700 rounded text-white px-8 py-2 text-sm"
							>
								Submit
							</button>
							<button
								aria-label="close modal"
								type="button"
								onClick={closeModal}
								className="focus:outline-none focus:ring-2 focus:ring-offset-2  focus:ring-gray-400 ml-3 bg-gray-100 transition duration-150 text-gray-600 ease-in-out hover:border-gray-400 hover:bg-gray-300 border rounded px-8 py-2 text-sm"
							>
								Cancel
							</button>
						</div>
					</form>
					<button
						className="cursor-pointer absolute top-0 right-0 mt-4 mr-5 text-gray-400 hover:text-gray-600 transition duration-150 ease-in-out rounded focus:ring-2 focus:outline-none focus:ring-gray-600"
						aria-label="close modal"
						onClick={closeModal}
					>
						<svg
							xmlns="http://www.w3.org/2000/svg"
							className="icon icon-tabler icon-tabler-x"
							width="20"
							height="20"
							viewBox="0 0 24 24"
							strokeWidth="2.5"
							stroke="currentColor"
							fill="none"
							strokeLinecap="round"
							strokeLinejoin="round"
						>
							<path stroke="none" d="M0 0h24v24H0z" />
							<line x1="18" y1="6" x2="6" y2="18" />
							<line x1="6" y1="6" x2="18" y2="18" />
						</svg>
					</button>
				</div>
			</div>
		</div>
	);
};

export default ExperiencePopupModal;

```

### Screenshot

![experience modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbhob0kawjaq7wetsbdr.png)

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