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
.
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.
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.
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
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
.
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.
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.
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
That's all for the UI/Modals portion of the project, stay tuned!
Top comments (0)