From 9e98da038c26d0361d90dea10730659656fbc277 Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Thu, 9 Jul 2020 10:41:16 +0530 Subject: [PATCH] - implementing hobby, language and reference sections - dynamic template selection --- package-lock.json | 15 +++- package.json | 2 + postcss.config.js | 3 +- src/components/builder/center/Artboard.js | 8 +- src/components/builder/left/LeftNavbar.js | 2 +- src/components/builder/left/LeftSidebar.js | 9 ++- src/components/builder/lists/List.js | 15 ++-- src/components/builder/lists/List.module.css | 18 ----- src/components/builder/lists/ListItem.js | 2 +- .../builder/lists/ListItem.module.css | 6 +- src/components/builder/sections/Education.js | 1 + src/components/builder/sections/Hobbies.js | 17 +++++ src/components/builder/sections/Languages.js | 17 +++++ src/components/builder/sections/References.js | 23 ++++++ src/components/builder/sections/Work.js | 8 +- src/components/dashboard/CreateResume.js | 4 +- src/components/shared/Button.js | 4 +- src/components/shared/Input.js | 43 ++++++++--- src/components/shared/Input.module.css | 11 ++- src/components/shared/PhotoUpload.js | 4 +- src/components/shared/SectionIcon.js | 25 +++++- src/constants/ModalEvents.js | 3 + src/contexts/TemplateContext.js | 2 +- src/data/leftSections.js | 37 ++++++++- src/modals/BaseModal.js | 4 +- src/modals/DataModal.js | 2 +- src/modals/ModalRegistrar.js | 6 ++ src/modals/ResumeModal.js | 15 +--- src/modals/sections/AwardModal.js | 21 ++--- src/modals/sections/CertificateModal.js | 21 ++--- src/modals/sections/EducationModal.js | 34 ++++----- src/modals/sections/HobbyModal.js | 44 +++++++++++ src/modals/sections/LanguageModal.js | 51 +++++++++++++ src/modals/sections/ReferenceModal.js | 76 +++++++++++++++++++ src/modals/sections/SkillModal.js | 34 +++++---- src/modals/sections/SocialModal.js | 19 ++--- src/modals/sections/WorkModal.js | 34 ++++----- src/utils/index.js | 17 ++++- 38 files changed, 470 insertions(+), 187 deletions(-) create mode 100644 src/components/builder/sections/Hobbies.js create mode 100644 src/components/builder/sections/Languages.js create mode 100644 src/components/builder/sections/References.js create mode 100644 src/modals/sections/HobbyModal.js create mode 100644 src/modals/sections/LanguageModal.js create mode 100644 src/modals/sections/ReferenceModal.js diff --git a/package-lock.json b/package-lock.json index 9623ba03..00303fdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5496,9 +5496,9 @@ } }, "colorette": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.0.tgz", - "integrity": "sha512-soRSroY+OF/8OdA3PTQXwaDJeMc7TfknKKrxeSCencL2a4+Tx5zhxmmv7hdpCjhKBjehzp8+bwe/T68K0hpIjw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==" }, "colors": { "version": "1.4.0", @@ -16229,6 +16229,15 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.7.2.tgz", "integrity": "sha512-u5l7fhAJXecWUJzVxzMRU2Zvw8m4QmDNHlTrT5uo3KBlYBhmChd7syAakBoay1yIiVhx/8Fi7a6v6kQZfsw81Q==" }, + "react-scroll": { + "version": "1.7.16", + "resolved": "https://registry.npmjs.org/react-scroll/-/react-scroll-1.7.16.tgz", + "integrity": "sha512-f4M5AdL+3cw3MJ7c/T0hPMY2iHCeQLDXV13lRanAFQ6JIt9xyAdHCpTH9mLUQt9SQh4pRarD+Qc7KhU6qMx3Yg==", + "requires": { + "lodash.throttle": "^4.1.1", + "prop-types": "^15.5.8" + } + }, "react-side-effect": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.0.tgz", diff --git a/package.json b/package.json index eb28f9a5..f1cfcb13 100644 --- a/package.json +++ b/package.json @@ -45,11 +45,13 @@ "react-dom": "^16.13.1", "react-helmet": "^6.1.0", "react-icons": "^3.10.0", + "react-scroll": "^1.7.16", "react-toastify": "^6.0.8", "uuid": "^8.2.0", "yup": "^0.29.1" }, "devDependencies": { + "autoprefixer": "^9.8.4", "eslint": "^7.4.0", "eslint-config-airbnb": "^18.2.0", "eslint-config-prettier": "^6.11.0", diff --git a/postcss.config.js b/postcss.config.js index 3cea32ff..d1e5bbae 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,5 +1,6 @@ const tailwindcss = require('tailwindcss'); +const autoprefixer = require('autoprefixer'); module.exports = () => ({ - plugins: [tailwindcss], + plugins: [tailwindcss, autoprefixer], }); diff --git a/src/components/builder/center/Artboard.js b/src/components/builder/center/Artboard.js index 47e58150..15a52cf4 100644 --- a/src/components/builder/center/Artboard.js +++ b/src/components/builder/center/Artboard.js @@ -6,8 +6,8 @@ import Onyx from '../../../templates/Onyx'; import styles from './Artboard.module.css'; const Artboard = () => { - const { blocks, colors } = useContext(TemplateContext); - const state = useSelector((s) => s); + const { selected, blocks, colors } = useContext(TemplateContext); + const state = useSelector((store) => store); const { id, name } = state; return ( @@ -18,7 +18,9 @@ const Artboard = () => {
- + {selected === 'Onyx' && ( + + )}
); diff --git a/src/components/builder/left/LeftNavbar.js b/src/components/builder/left/LeftNavbar.js index b54932f7..24f3255e 100644 --- a/src/components/builder/left/LeftNavbar.js +++ b/src/components/builder/left/LeftNavbar.js @@ -16,7 +16,7 @@ const LeftNavbar = () => (
{sections.map((x) => ( - + ))}
diff --git a/src/components/builder/left/LeftSidebar.js b/src/components/builder/left/LeftSidebar.js index 829bcbb9..dadf8b92 100644 --- a/src/components/builder/left/LeftSidebar.js +++ b/src/components/builder/left/LeftSidebar.js @@ -1,4 +1,5 @@ -import React, { Fragment } from 'react'; +import React from 'react'; +import { Element } from 'react-scroll'; import sections from '../../../data/leftSections'; import LeftNavbar from './LeftNavbar'; import styles from './LeftSidebar.module.css'; @@ -7,12 +8,12 @@ const LeftSidebar = () => (
-
+
diff --git a/src/components/builder/lists/List.js b/src/components/builder/lists/List.js index aa2bfe21..4cafb471 100644 --- a/src/components/builder/lists/List.js +++ b/src/components/builder/lists/List.js @@ -1,9 +1,9 @@ import { get, isEmpty } from 'lodash'; -import moment from 'moment'; import React, { useContext } from 'react'; import { MdAdd } from 'react-icons/md'; import ModalContext from '../../../contexts/ModalContext'; import { useSelector } from '../../../contexts/ResumeContext'; +import { formatDateRange } from '../../../utils'; import Button from '../../shared/Button'; import EmptyList from './EmptyList'; import styles from './List.module.css'; @@ -17,6 +17,7 @@ const List = ({ subtitlePath, text, textPath, + hasDate, event, }) => { const items = useSelector((state) => get(state, path, [])); @@ -26,13 +27,6 @@ const List = ({ const handleEdit = (data) => emitter.emit(event, data); - const formatDateRange = (x) => - `${moment(x.startDate).format('MMMM Y')} — ${ - moment(x.endDate).isValid() - ? moment(x.endDate).format('MMMM Y') - : 'Present' - }`; - return (
@@ -46,10 +40,11 @@ const List = ({ path={path} title={title || get(x, titlePath, '')} subtitle={ - subtitle || get(x, subtitlePath, '') || formatDateRange(x) + subtitle || + get(x, subtitlePath, '') || + (hasDate && formatDateRange(x)) } text={text || get(x, textPath, '')} - className={styles.listItem} onEdit={() => handleEdit(x)} isFirst={i === 0} isLast={i === items.length - 1} diff --git a/src/components/builder/lists/List.module.css b/src/components/builder/lists/List.module.css index 71a6e278..99b9b9aa 100644 --- a/src/components/builder/lists/List.module.css +++ b/src/components/builder/lists/List.module.css @@ -1,21 +1,3 @@ .list { @apply flex flex-col border border-secondary rounded; } - -.list-item { - @apply flex items-center justify-between border-t border-secondary px-8 py-5; -} - -.list-item:first-child { - @apply border-t-0; -} - -.menu { - @apply opacity-0; - @apply transition-opacity duration-200 ease-in-out; -} - -.list-item:hover .menu { - @apply opacity-100; - @apply transition-opacity duration-200 ease-in-out; -} diff --git a/src/components/builder/lists/ListItem.js b/src/components/builder/lists/ListItem.js index c78ca46e..c85e2005 100644 --- a/src/components/builder/lists/ListItem.js +++ b/src/components/builder/lists/ListItem.js @@ -64,7 +64,7 @@ const ListItem = ({ }; return ( -
+
{title} diff --git a/src/components/builder/lists/ListItem.module.css b/src/components/builder/lists/ListItem.module.css index 85034a75..bd0ad0a8 100644 --- a/src/components/builder/lists/ListItem.module.css +++ b/src/components/builder/lists/ListItem.module.css @@ -1,8 +1,8 @@ -.container { +.listItem { @apply flex items-center justify-between border-t border-secondary px-6 py-5; } -.container:first-child { +.listItem:first-child { @apply border-t-0; } @@ -11,7 +11,7 @@ @apply transition-opacity duration-200 ease-in-out; } -.container:hover .menu { +.listItem:hover .menu { @apply opacity-100; @apply transition-opacity duration-200 ease-in-out; } diff --git a/src/components/builder/sections/Education.js b/src/components/builder/sections/Education.js index 362634b8..7c3ab476 100644 --- a/src/components/builder/sections/Education.js +++ b/src/components/builder/sections/Education.js @@ -10,6 +10,7 @@ const Education = ({ id, name, event }) => { {name} { + const path = `${id}.items`; + + return ( +
+ {name} + + +
+ ); +}; + +export default Hobbies; diff --git a/src/components/builder/sections/Languages.js b/src/components/builder/sections/Languages.js new file mode 100644 index 00000000..6d80d9df --- /dev/null +++ b/src/components/builder/sections/Languages.js @@ -0,0 +1,17 @@ +import React from 'react'; +import Heading from '../../shared/Heading'; +import List from '../lists/List'; + +const Languages = ({ id, name, event }) => { + const path = `${id}.items`; + + return ( +
+ {name} + + +
+ ); +}; + +export default Languages; diff --git a/src/components/builder/sections/References.js b/src/components/builder/sections/References.js new file mode 100644 index 00000000..8b91a5a2 --- /dev/null +++ b/src/components/builder/sections/References.js @@ -0,0 +1,23 @@ +import React from 'react'; +import Heading from '../../shared/Heading'; +import List from '../lists/List'; + +const References = ({ id, name, event }) => { + const path = `${id}.items`; + + return ( +
+ {name} + + +
+ ); +}; + +export default References; diff --git a/src/components/builder/sections/Work.js b/src/components/builder/sections/Work.js index c310217b..eb79d6d4 100644 --- a/src/components/builder/sections/Work.js +++ b/src/components/builder/sections/Work.js @@ -9,7 +9,13 @@ const Work = ({ id, name, event }) => {
{name} - +
); }; diff --git a/src/components/dashboard/CreateResume.js b/src/components/dashboard/CreateResume.js index cb3ed10e..4de0029f 100644 --- a/src/components/dashboard/CreateResume.js +++ b/src/components/dashboard/CreateResume.js @@ -1,7 +1,7 @@ import React, { useContext } from 'react'; import { MdAdd } from 'react-icons/md'; import ModalContext from '../../contexts/ModalContext'; -import { handleKeyDown } from '../../utils'; +import { handleKeyUp } from '../../utils'; import styles from './CreateResume.module.css'; const CreateResume = () => { @@ -19,7 +19,7 @@ const CreateResume = () => { role="button" className={styles.page} onClick={handleClick} - onKeyDown={(e) => handleKeyDown(e, handleClick)} + onKeyUp={(e) => handleKeyUp(e, handleClick)} >
diff --git a/src/components/shared/Button.js b/src/components/shared/Button.js index 1afcdefe..05a149cc 100644 --- a/src/components/shared/Button.js +++ b/src/components/shared/Button.js @@ -1,6 +1,6 @@ import classNames from 'classnames'; import React from 'react'; -import { handleKeyDown } from '../../utils'; +import { handleKeyUp } from '../../utils'; import styles from './Button.module.css'; const Button = ({ icon, title, onClick, outline, className, isLoading }) => { @@ -12,7 +12,7 @@ const Button = ({ icon, title, onClick, outline, className, isLoading }) => { return (
diff --git a/src/components/shared/Input.module.css b/src/components/shared/Input.module.css index f3db2072..548efec1 100644 --- a/src/components/shared/Input.module.css +++ b/src/components/shared/Input.module.css @@ -3,17 +3,20 @@ } .container label input, -.container label textarea { - @apply py-3 px-4 rounded bg-secondary text-primary border border-secondary; +.container label textarea, +.container label select { + @apply py-3 px-4 rounded bg-secondary text-primary border border-secondary appearance-none; } .container label input::placeholder, -.container label textarea::placeholder { +.container label textarea::placeholder, +.container label select::placeholder { @apply text-primary opacity-50; } .container label input:focus, -.container label textarea:focus { +.container label textarea:focus, +.container label select:focus { @apply outline-none border-gray-500; } diff --git a/src/components/shared/PhotoUpload.js b/src/components/shared/PhotoUpload.js index 4b828654..cc919340 100644 --- a/src/components/shared/PhotoUpload.js +++ b/src/components/shared/PhotoUpload.js @@ -1,7 +1,7 @@ import React, { useContext, useRef } from 'react'; import { MdFileUpload } from 'react-icons/md'; import StorageContext from '../../contexts/StorageContext'; -import { handleKeyDown } from '../../utils'; +import { handleKeyUp } from '../../utils'; import Input from './Input'; import styles from './PhotoUpload.module.css'; @@ -25,7 +25,7 @@ const PhotoUpload = () => { tabIndex="0" className={styles.circle} onClick={handleIconClick} - onKeyDown={(e) => handleKeyDown(e, handleIconClick)} + onKeyUp={(e) => handleKeyUp(e, handleIconClick)} > { - const { icon: Icon, name } = section; +const SectionIcon = ({ section, containerId, placement = 'right' }) => { + const { id, name, icon: Icon } = section; + + const handleClick = () => { + scroller.scrollTo(id, { + duration: 500, + smooth: true, + containerId, + offset: -10, + }); + }; return ( -
- +
handleKeyUp(e, handleClick)} + onClick={handleClick} + > +
); diff --git a/src/constants/ModalEvents.js b/src/constants/ModalEvents.js index 8ed6920f..c491d19f 100644 --- a/src/constants/ModalEvents.js +++ b/src/constants/ModalEvents.js @@ -7,6 +7,9 @@ const ModalEvents = { AWARD_MODAL: 'award_modal', CERTIFICATION_MODAL: 'certification_modal', SKILL_MODAL: 'skill_modal', + HOBBY_MODAL: 'hobby_modal', + LANGUAGE_MODAL: 'language_modal', + REFERENCE_MODAL: 'reference_modal', }; export default ModalEvents; diff --git a/src/contexts/TemplateContext.js b/src/contexts/TemplateContext.js index 83840ffd..f2a5d0af 100644 --- a/src/contexts/TemplateContext.js +++ b/src/contexts/TemplateContext.js @@ -3,7 +3,7 @@ import React, { createContext, useState } from 'react'; import leftSections from '../data/leftSections'; const defaultState = { - selected: 'Pikachu', + selected: 'Onyx', setSelected: () => {}, colors: { textColor: '#212121', diff --git a/src/data/leftSections.js b/src/data/leftSections.js index 23b450ad..f4dd80e6 100644 --- a/src/data/leftSections.js +++ b/src/data/leftSections.js @@ -1,12 +1,20 @@ import { AiFillSafetyCertificate, AiOutlineTwitter } from 'react-icons/ai'; -import { FaAward, FaTools } from 'react-icons/fa'; -import { IoMdBriefcase, IoMdDocument } from 'react-icons/io'; -import { MdPerson, MdSchool } from 'react-icons/md'; +import { BsTools } from 'react-icons/bs'; +import { FaAward, FaUserFriends } from 'react-icons/fa'; +import { + IoLogoGameControllerB, + IoMdBriefcase, + IoMdDocument, +} from 'react-icons/io'; +import { MdPerson, MdSchool, MdTranslate } from 'react-icons/md'; import Awards from '../components/builder/sections/Awards'; import Certifications from '../components/builder/sections/Certifications'; import Education from '../components/builder/sections/Education'; +import Hobbies from '../components/builder/sections/Hobbies'; +import Languages from '../components/builder/sections/Languages'; import Objective from '../components/builder/sections/Objective'; import Profile from '../components/builder/sections/Profile'; +import References from '../components/builder/sections/References'; import Skills from '../components/builder/sections/Skills'; import Social from '../components/builder/sections/Social'; import Work from '../components/builder/sections/Work'; @@ -63,8 +71,29 @@ export default [ { id: 'skills', name: 'Skills', - icon: FaTools, + icon: BsTools, component: Skills, event: ModalEvents.SKILL_MODAL, }, + { + id: 'hobbies', + name: 'Hobbies', + icon: IoLogoGameControllerB, + component: Hobbies, + event: ModalEvents.HOBBY_MODAL, + }, + { + id: 'languages', + name: 'Languages', + icon: MdTranslate, + component: Languages, + event: ModalEvents.LANGUAGE_MODAL, + }, + { + id: 'references', + name: 'References', + icon: FaUserFriends, + component: References, + event: ModalEvents.REFERENCE_MODAL, + }, ]; diff --git a/src/modals/BaseModal.js b/src/modals/BaseModal.js index cdfffe28..a7555f61 100644 --- a/src/modals/BaseModal.js +++ b/src/modals/BaseModal.js @@ -5,7 +5,7 @@ import { isFunction } from 'lodash'; import React, { forwardRef, useImperativeHandle } from 'react'; import { MdClose } from 'react-icons/md'; import Button from '../components/shared/Button'; -import { handleKeyDown } from '../utils'; +import { handleKeyUp } from '../utils'; import styles from './BaseModal.module.css'; const BaseModal = forwardRef( @@ -38,7 +38,7 @@ const BaseModal = forwardRef( size="18" tabIndex="0" onClick={handleClose} - onKeyDown={(e) => handleKeyDown(e, handleClose)} + onKeyUp={(e) => handleKeyUp(e, handleClose)} />
diff --git a/src/modals/DataModal.js b/src/modals/DataModal.js index de099145..a07bf182 100644 --- a/src/modals/DataModal.js +++ b/src/modals/DataModal.js @@ -41,7 +41,7 @@ const DataModal = ({ }, [data]); const onSubmit = async (newData) => { - if (isEmpty(await validateForm(newData))) { + if (isEmpty(await validateForm())) { if (isEditMode) { if (data !== newData) { isFunction(onEdit) diff --git a/src/modals/ModalRegistrar.js b/src/modals/ModalRegistrar.js index aaf49fd2..d405be83 100644 --- a/src/modals/ModalRegistrar.js +++ b/src/modals/ModalRegistrar.js @@ -4,6 +4,9 @@ import ResumeModal from './ResumeModal'; import AwardModal from './sections/AwardModal'; import CertificateModal from './sections/CertificateModal'; import EducationModal from './sections/EducationModal'; +import HobbyModal from './sections/HobbyModal'; +import LanguageModal from './sections/LanguageModal'; +import ReferenceModal from './sections/ReferenceModal'; import SkillModal from './sections/SkillModal'; import SocialModal from './sections/SocialModal'; import WorkModal from './sections/WorkModal'; @@ -19,6 +22,9 @@ const ModalRegistrar = () => { + + + ); }; diff --git a/src/modals/ResumeModal.js b/src/modals/ResumeModal.js index 3c94fdab..89017384 100644 --- a/src/modals/ResumeModal.js +++ b/src/modals/ResumeModal.js @@ -1,17 +1,17 @@ import { Formik } from 'formik'; -import { get } from 'lodash'; import React, { useContext } from 'react'; import * as Yup from 'yup'; import Input from '../components/shared/Input'; import ModalEvents from '../constants/ModalEvents'; import DatabaseContext from '../contexts/DatabaseContext'; +import { getFieldProps } from '../utils'; import DataModal from './DataModal'; const initialValues = { name: '', }; -const validationSchema = Yup.object().shape({ +const schema = Yup.object().shape({ name: Yup.string() .min(5, 'Please enter at least 5 characters.') .required('This is a required field.'), @@ -20,18 +20,11 @@ const validationSchema = Yup.object().shape({ const ResumeModal = () => { const { createResume, updateResume } = useContext(DatabaseContext); - const getFieldProps = (formik, name) => ({ - touched: get(formik, `touched.${name}`, false), - error: get(formik, `errors.${name}`, ''), - isRequired: get(validationSchema, `fields.${name}._exclusive.required`), - ...formik.getFieldProps(name), - }); - return ( {(formik) => ( { label="Name" className="mb-8" placeholder="Full Stack Web Developer" - {...getFieldProps(formik, 'name')} + {...getFieldProps(formik, schema, 'name')} />

diff --git a/src/modals/sections/AwardModal.js b/src/modals/sections/AwardModal.js index a863af54..7372f7a7 100644 --- a/src/modals/sections/AwardModal.js +++ b/src/modals/sections/AwardModal.js @@ -1,9 +1,9 @@ import { Formik } from 'formik'; -import { get } from 'lodash'; import React from 'react'; import * as Yup from 'yup'; import Input from '../../components/shared/Input'; import ModalEvents from '../../constants/ModalEvents'; +import { getFieldProps } from '../../utils'; import DataModal from '../DataModal'; const initialValues = { @@ -13,7 +13,7 @@ const initialValues = { summary: '', }; -const validationSchema = Yup.object().shape({ +const schema = Yup.object().shape({ title: Yup.string().required('This is a required field.'), awarder: Yup.string().required('This is a required field.'), date: Yup.date().max(new Date()), @@ -21,18 +21,11 @@ const validationSchema = Yup.object().shape({ }); const AwardModal = () => { - const getFieldProps = (formik, name) => ({ - touched: get(formik, `touched.${name}`, false), - error: get(formik, `errors.${name}`, ''), - isRequired: get(validationSchema, `fields.${name}._exclusive.required`), - ...formik.getFieldProps(name), - }); - return ( {(formik) => ( { label="Title" className="col-span-2" placeholder="Intl. Flutter Hackathon '19" - {...getFieldProps(formik, 'title')} + {...getFieldProps(formik, schema, 'title')} />

diff --git a/src/modals/sections/CertificateModal.js b/src/modals/sections/CertificateModal.js index 2cd1f2fc..3b7e13d1 100644 --- a/src/modals/sections/CertificateModal.js +++ b/src/modals/sections/CertificateModal.js @@ -1,9 +1,9 @@ import { Formik } from 'formik'; -import { get } from 'lodash'; import React from 'react'; import * as Yup from 'yup'; import Input from '../../components/shared/Input'; import ModalEvents from '../../constants/ModalEvents'; +import { getFieldProps } from '../../utils'; import DataModal from '../DataModal'; const initialValues = { @@ -13,7 +13,7 @@ const initialValues = { summary: '', }; -const validationSchema = Yup.object().shape({ +const schema = Yup.object().shape({ title: Yup.string().required('This is a required field.'), issuer: Yup.string().required('This is a required field.'), date: Yup.date().max(new Date()), @@ -21,18 +21,11 @@ const validationSchema = Yup.object().shape({ }); const CertificateModal = () => { - const getFieldProps = (formik, name) => ({ - touched: get(formik, `touched.${name}`, false), - error: get(formik, `errors.${name}`, ''), - isRequired: get(validationSchema, `fields.${name}._exclusive.required`), - ...formik.getFieldProps(name), - }); - return ( {(formik) => ( { label="Title" className="col-span-2" placeholder="CCNP" - {...getFieldProps(formik, 'title')} + {...getFieldProps(formik, schema, 'title')} />
diff --git a/src/modals/sections/EducationModal.js b/src/modals/sections/EducationModal.js index 8e750ead..d7f4db30 100644 --- a/src/modals/sections/EducationModal.js +++ b/src/modals/sections/EducationModal.js @@ -1,11 +1,10 @@ import { Field, FieldArray, Formik } from 'formik'; -import { get } from 'lodash'; import React from 'react'; import { MdAdd } from 'react-icons/md'; import * as Yup from 'yup'; import Input from '../../components/shared/Input'; import ModalEvents from '../../constants/ModalEvents'; -import { handleKeyDown } from '../../utils'; +import { getFieldProps, handleKeyUp } from '../../utils'; import DataModal from '../DataModal'; const initialValues = { @@ -19,7 +18,7 @@ const initialValues = { temp: '', }; -const validationSchema = Yup.object().shape({ +const schema = Yup.object().shape({ institution: Yup.string().required('This is a required field.'), field: Yup.string().required('This is a required field.'), degree: Yup.string(), @@ -27,27 +26,20 @@ const validationSchema = Yup.object().shape({ startDate: Yup.date().required('This is a required field.'), endDate: Yup.date().when( 'startDate', - (startDate, schema) => + (startDate, yupSchema) => startDate && - schema.min(startDate, 'End Date must be later than Start Date'), + yupSchema.min(startDate, 'End Date must be later than Start Date'), ), courses: Yup.array().of(Yup.string().required('This is a required field.')), temp: Yup.string().ensure(), }); const EducationModal = () => { - const getFieldProps = (formik, name) => ({ - touched: get(formik, `touched.${name}`, false), - error: get(formik, `errors.${name}`, ''), - isRequired: get(validationSchema, `fields.${name}._exclusive.required`), - ...formik.getFieldProps(name), - }); - return ( {(formik) => ( { label="Institution" className="col-span-2" placeholder="Dayananda Sagar College of Engineering" - {...getFieldProps(formik, 'institution')} + {...getFieldProps(formik, schema, 'institution')} /> {
handleKeyDown(e, handleClickAdd)} + onKeyUp={(e) => handleKeyUp(e, handleClickAdd)} onClick={handleClickAdd} />
diff --git a/src/modals/sections/HobbyModal.js b/src/modals/sections/HobbyModal.js new file mode 100644 index 00000000..3446836d --- /dev/null +++ b/src/modals/sections/HobbyModal.js @@ -0,0 +1,44 @@ +import { Formik } from 'formik'; +import React from 'react'; +import * as Yup from 'yup'; +import Input from '../../components/shared/Input'; +import ModalEvents from '../../constants/ModalEvents'; +import { getFieldProps } from '../../utils'; +import DataModal from '../DataModal'; + +const initialValues = { + name: '', +}; + +const schema = Yup.object().shape({ + name: Yup.string().required('This is a required field.'), +}); + +const HobbyModal = () => { + return ( + + {(formik) => ( + +
+ +
+
+ )} +
+ ); +}; + +export default HobbyModal; diff --git a/src/modals/sections/LanguageModal.js b/src/modals/sections/LanguageModal.js new file mode 100644 index 00000000..144583be --- /dev/null +++ b/src/modals/sections/LanguageModal.js @@ -0,0 +1,51 @@ +import { Formik } from 'formik'; +import React from 'react'; +import * as Yup from 'yup'; +import Input from '../../components/shared/Input'; +import ModalEvents from '../../constants/ModalEvents'; +import { getFieldProps } from '../../utils'; +import DataModal from '../DataModal'; + +const initialValues = { + name: '', + fluency: '', +}; + +const schema = Yup.object().shape({ + name: Yup.string().required('This is a required field.'), + fluency: Yup.string().required('This is a required field.'), +}); + +const LanguageModal = () => { + return ( + + {(formik) => ( + +
+ + + +
+
+ )} +
+ ); +}; + +export default LanguageModal; diff --git a/src/modals/sections/ReferenceModal.js b/src/modals/sections/ReferenceModal.js new file mode 100644 index 00000000..c650d133 --- /dev/null +++ b/src/modals/sections/ReferenceModal.js @@ -0,0 +1,76 @@ +import { Formik } from 'formik'; +import React from 'react'; +import * as Yup from 'yup'; +import Input from '../../components/shared/Input'; +import ModalEvents from '../../constants/ModalEvents'; +import { getFieldProps } from '../../utils'; +import DataModal from '../DataModal'; + +const initialValues = { + name: '', + position: '', + contact: '', + email: '', + summary: '', +}; + +const schema = Yup.object().shape({ + name: Yup.string().required('This is a required field.'), + position: Yup.string().required('This is a required field.'), + contact: Yup.string(), + email: Yup.string().email('Must be a valid email address.'), + summary: Yup.string(), +}); + +const ReferenceModal = () => { + return ( + + {(formik) => ( + +
+ + + + + + + + + +
+
+ )} +
+ ); +}; + +export default ReferenceModal; diff --git a/src/modals/sections/SkillModal.js b/src/modals/sections/SkillModal.js index 99a7cdba..dca4976a 100644 --- a/src/modals/sections/SkillModal.js +++ b/src/modals/sections/SkillModal.js @@ -1,34 +1,37 @@ import { Formik } from 'formik'; -import { get } from 'lodash'; import React from 'react'; import * as Yup from 'yup'; import Input from '../../components/shared/Input'; import ModalEvents from '../../constants/ModalEvents'; +import { getFieldProps } from '../../utils'; import DataModal from '../DataModal'; +const SKILL_LEVELS = [ + 'Fundamental Awareness', + 'Novice', + 'Intermediate', + 'Advanced', + 'Expert', +]; + const initialValues = { name: '', - level: '', + level: SKILL_LEVELS[0], }; -const validationSchema = Yup.object().shape({ +const schema = Yup.object().shape({ name: Yup.string().required('This is a required field.'), - level: Yup.string().required('This is a required field.'), + level: Yup.string() + .oneOf(SKILL_LEVELS, 'Must be one of the options above.') + .required('This is a required field.'), }); const SkillModal = () => { - const getFieldProps = (formik, name) => ({ - touched: get(formik, `touched.${name}`, false), - error: get(formik, `errors.${name}`, ''), - isRequired: get(validationSchema, `fields.${name}._exclusive.required`), - ...formik.getFieldProps(name), - }); - return ( {(formik) => ( {
diff --git a/src/modals/sections/SocialModal.js b/src/modals/sections/SocialModal.js index be34dc96..1ed43626 100644 --- a/src/modals/sections/SocialModal.js +++ b/src/modals/sections/SocialModal.js @@ -1,9 +1,9 @@ import { Formik } from 'formik'; -import { get } from 'lodash'; import React from 'react'; import * as Yup from 'yup'; import Input from '../../components/shared/Input'; import ModalEvents from '../../constants/ModalEvents'; +import { getFieldProps } from '../../utils'; import DataModal from '../DataModal'; const initialValues = { @@ -12,7 +12,7 @@ const initialValues = { username: '', }; -const validationSchema = Yup.object().shape({ +const schema = Yup.object().shape({ network: Yup.string() .min(5, 'Please enter at least 5 characters.') .required('This is a required field.'), @@ -24,18 +24,11 @@ const validationSchema = Yup.object().shape({ }); const SocialModal = () => { - const getFieldProps = (formik, name) => ({ - touched: get(formik, `touched.${name}`, false), - error: get(formik, `errors.${name}`, ''), - isRequired: get(validationSchema, `fields.${name}._exclusive.required`), - ...formik.getFieldProps(name), - }); - return ( {(formik) => ( {
diff --git a/src/modals/sections/WorkModal.js b/src/modals/sections/WorkModal.js index c11363d7..7a1b3afc 100644 --- a/src/modals/sections/WorkModal.js +++ b/src/modals/sections/WorkModal.js @@ -1,11 +1,10 @@ import { Field, FieldArray, Formik } from 'formik'; -import { get } from 'lodash'; import React from 'react'; import { MdAdd } from 'react-icons/md'; import * as Yup from 'yup'; import Input from '../../components/shared/Input'; import ModalEvents from '../../constants/ModalEvents'; -import { handleKeyDown } from '../../utils'; +import { getFieldProps, handleKeyUp } from '../../utils'; import DataModal from '../DataModal'; const initialValues = { @@ -19,16 +18,16 @@ const initialValues = { temp: '', }; -const validationSchema = Yup.object().shape({ +const schema = Yup.object().shape({ company: Yup.string().required('This is a required field.'), position: Yup.string().required('This is a required field.'), website: Yup.string().url('Must be a valid URL'), startDate: Yup.date().required('This is a required field.'), endDate: Yup.date().when( 'startDate', - (startDate, schema) => + (startDate, yupSchema) => startDate && - schema.min(startDate, 'End Date must be later than Start Date'), + yupSchema.min(startDate, 'End Date must be later than Start Date'), ), summary: Yup.string().min(10, 'Please enter at least 10 characters.'), highlights: Yup.array().of( @@ -38,18 +37,11 @@ const validationSchema = Yup.object().shape({ }); const WorkModal = () => { - const getFieldProps = (formik, name) => ({ - touched: get(formik, `touched.${name}`, false), - error: get(formik, `errors.${name}`, ''), - isRequired: get(validationSchema, `fields.${name}._exclusive.required`), - ...formik.getFieldProps(name), - }); - return ( {(formik) => ( { label="Company" className="col-span-2" placeholder="Postdot Technologies Pvt. Ltd." - {...getFieldProps(formik, 'company')} + {...getFieldProps(formik, schema, 'company')} /> {
handleKeyDown(e, handleClickAdd)} + onKeyUp={(e) => handleKeyUp(e, handleClickAdd)} onClick={handleClickAdd} />
diff --git a/src/utils/index.js b/src/utils/index.js index a2325842..154d6dd1 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,3 +1,6 @@ +import { get } from 'lodash'; +import moment from 'moment'; + export const getModalText = (isEditMode, type) => { return isEditMode ? `Edit ${type}` : `Add ${type}`; }; @@ -8,7 +11,7 @@ export const transformCollectionSnapshot = (snapshot, setData) => { setData(data); }; -export const handleKeyDown = (event, action) => { +export const handleKeyUp = (event, action) => { (event.which === 13 || event.which === 32) && action(); }; @@ -16,3 +19,15 @@ export const isFileImage = (file) => { const acceptedImageTypes = ['image/jpeg', 'image/png']; return file && acceptedImageTypes.includes(file.type); }; + +export const formatDateRange = (x) => + `${moment(x.startDate).format('MMMM Y')} — ${ + moment(x.endDate).isValid() ? moment(x.endDate).format('MMMM Y') : 'Present' + }`; + +export const getFieldProps = (formik, schema, name) => ({ + touched: get(formik, `touched.${name}`, false), + error: get(formik, `errors.${name}`, ''), + isRequired: get(schema, `fields.${name}._exclusive.required`), + ...formik.getFieldProps(name), +});