mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-13 00:03:27 +10:00
- implementing hobby, language and reference sections
- dynamic template selection
This commit is contained in:
15
package-lock.json
generated
15
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
module.exports = () => ({
|
||||
plugins: [tailwindcss],
|
||||
plugins: [tailwindcss, autoprefixer],
|
||||
});
|
||||
|
||||
@ -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 = () => {
|
||||
</Helmet>
|
||||
|
||||
<div id="artboard" className={styles.container}>
|
||||
<Onyx data={state} layout={blocks} colors={colors} />
|
||||
{selected === 'Onyx' && (
|
||||
<Onyx data={state} layout={blocks} colors={colors} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -16,7 +16,7 @@ const LeftNavbar = () => (
|
||||
|
||||
<div className="grid grid-cols-1 gap-8">
|
||||
{sections.map((x) => (
|
||||
<SectionIcon key={x.id} section={x} />
|
||||
<SectionIcon key={x.id} section={x} containerId="LeftSidebar" />
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@ -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 = () => (
|
||||
<div className="flex">
|
||||
<LeftNavbar />
|
||||
|
||||
<div className={styles.container}>
|
||||
<div id="LeftSidebar" className={styles.container}>
|
||||
{sections.map(({ id, name, event, component: Component }) => (
|
||||
<Fragment key={id}>
|
||||
<Element key={id} name={id}>
|
||||
<Component id={id} name={name} event={event} />
|
||||
<hr />
|
||||
</Fragment>
|
||||
</Element>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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 (
|
||||
<div className="flex flex-col">
|
||||
<div className={styles.list}>
|
||||
@ -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}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ const ListItem = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.listItem}>
|
||||
<div className="grid">
|
||||
<span className="font-medium truncate">{title}</span>
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ const Education = ({ id, name, event }) => {
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<List
|
||||
hasDate
|
||||
path={path}
|
||||
event={event}
|
||||
titlePath="institution"
|
||||
|
||||
17
src/components/builder/sections/Hobbies.js
Normal file
17
src/components/builder/sections/Hobbies.js
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import Heading from '../../shared/Heading';
|
||||
import List from '../lists/List';
|
||||
|
||||
const Hobbies = ({ id, name, event }) => {
|
||||
const path = `${id}.items`;
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<List path={path} event={event} titlePath="name" />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hobbies;
|
||||
17
src/components/builder/sections/Languages.js
Normal file
17
src/components/builder/sections/Languages.js
Normal file
@ -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 (
|
||||
<section>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<List path={path} event={event} titlePath="name" subtitlePath="fluency" />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Languages;
|
||||
23
src/components/builder/sections/References.js
Normal file
23
src/components/builder/sections/References.js
Normal file
@ -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 (
|
||||
<section>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<List
|
||||
path={path}
|
||||
event={event}
|
||||
titlePath="name"
|
||||
subtitlePath="position"
|
||||
textPath="summary"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default References;
|
||||
@ -9,7 +9,13 @@ const Work = ({ id, name, event }) => {
|
||||
<section>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<List path={path} event={event} titlePath="company" textPath="summary" />
|
||||
<List
|
||||
hasDate
|
||||
path={path}
|
||||
event={event}
|
||||
titlePath="company"
|
||||
textPath="summary"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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)}
|
||||
>
|
||||
<MdAdd size="48" />
|
||||
</div>
|
||||
|
||||
@ -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 (
|
||||
<button
|
||||
className={classes}
|
||||
onKeyDown={(e) => handleKeyDown(e, onClick)}
|
||||
onKeyUp={(e) => handleKeyUp(e, onClick)}
|
||||
onClick={isLoading ? undefined : onClick}
|
||||
>
|
||||
{icon && <Icon size="14" className="mr-2" />}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import cx from 'classnames';
|
||||
import { get, isFunction } from 'lodash';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FaAngleDown } from 'react-icons/fa';
|
||||
import { MdClose } from 'react-icons/md';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useDispatch, useSelector } from '../../contexts/ResumeContext';
|
||||
import { handleKeyDown } from '../../utils';
|
||||
import { handleKeyUp } from '../../utils';
|
||||
import styles from './Input.module.css';
|
||||
|
||||
const Input = ({
|
||||
@ -14,8 +15,8 @@ const Input = ({
|
||||
error,
|
||||
value,
|
||||
onBlur,
|
||||
options,
|
||||
touched,
|
||||
checked,
|
||||
onChange,
|
||||
className,
|
||||
isRequired,
|
||||
@ -24,11 +25,15 @@ const Input = ({
|
||||
showDeleteItemButton,
|
||||
type = 'text',
|
||||
}) => {
|
||||
const uuid = uuidv4();
|
||||
const [uuid, setUuid] = useState(null);
|
||||
const stateValue = useSelector((state) => get(state, path));
|
||||
const dispatch = useDispatch();
|
||||
|
||||
value = value || stateValue;
|
||||
useEffect(() => {
|
||||
setUuid(uuidv4());
|
||||
}, []);
|
||||
|
||||
value = isFunction(onChange) ? value : stateValue;
|
||||
onChange = isFunction(onChange)
|
||||
? onChange
|
||||
: (e) => {
|
||||
@ -51,7 +56,7 @@ const Input = ({
|
||||
)}
|
||||
</span>
|
||||
|
||||
{type === 'text' && (
|
||||
{(type === 'text' || type === 'date') && (
|
||||
<div className="relative grid items-center">
|
||||
<input
|
||||
id={uuid}
|
||||
@ -59,7 +64,6 @@ const Input = ({
|
||||
type={type}
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
@ -69,7 +73,7 @@ const Input = ({
|
||||
size="16px"
|
||||
tabIndex="0"
|
||||
onClick={onDeleteItem}
|
||||
onKeyDown={(e) => handleKeyDown(e, onDeleteItem)}
|
||||
onKeyUp={(e) => handleKeyUp(e, onDeleteItem)}
|
||||
className="absolute right-0 cursor-pointer opacity-50 hover:opacity-75 mx-4"
|
||||
/>
|
||||
)}
|
||||
@ -82,10 +86,8 @@ const Input = ({
|
||||
id={uuid}
|
||||
rows="4"
|
||||
name={name}
|
||||
type={type}
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
@ -104,6 +106,27 @@ const Input = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type === 'dropdown' && (
|
||||
<div className="relative grid items-center">
|
||||
<select
|
||||
id={uuid}
|
||||
name={name}
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
>
|
||||
{options.map((x) => (
|
||||
<option key={x}>{x}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<FaAngleDown
|
||||
size="16px"
|
||||
className="absolute right-0 opacity-50 hover:opacity-75 mx-4"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && touched && <p>{error}</p>}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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)}
|
||||
>
|
||||
<MdFileUpload size="22px" />
|
||||
<input
|
||||
|
||||
@ -1,13 +1,30 @@
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { scroller } from 'react-scroll';
|
||||
import { handleKeyUp } from '../../utils';
|
||||
|
||||
const SectionIcon = ({ section, placement = 'right' }) => {
|
||||
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 (
|
||||
<Tooltip title={name} placement={placement} arrow>
|
||||
<div className="cursor-pointer">
|
||||
<Icon className="text-secondary-dark hover:text-primary" size="20px" />
|
||||
<div
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
className="cursor-pointer focus:outline-none"
|
||||
onKeyUp={(e) => handleKeyUp(e, handleClick)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Icon className="text-secondary-dark hover:text-primary" size="16px" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
@ -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)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 = () => {
|
||||
<AwardModal />
|
||||
<CertificateModal />
|
||||
<SkillModal />
|
||||
<HobbyModal />
|
||||
<LanguageModal />
|
||||
<ReferenceModal />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
@ -48,7 +41,7 @@ const ResumeModal = () => {
|
||||
label="Name"
|
||||
className="mb-8"
|
||||
placeholder="Full Stack Web Developer"
|
||||
{...getFieldProps(formik, 'name')}
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<p>
|
||||
|
||||
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
@ -45,26 +38,26 @@ const AwardModal = () => {
|
||||
label="Title"
|
||||
className="col-span-2"
|
||||
placeholder="Intl. Flutter Hackathon '19"
|
||||
{...getFieldProps(formik, 'title')}
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Awarder"
|
||||
placeholder="Google"
|
||||
{...getFieldProps(formik, 'awarder')}
|
||||
{...getFieldProps(formik, schema, 'awarder')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
{...getFieldProps(formik, 'date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, 'summary')}
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
</div>
|
||||
</DataModal>
|
||||
|
||||
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
@ -45,26 +38,26 @@ const CertificateModal = () => {
|
||||
label="Title"
|
||||
className="col-span-2"
|
||||
placeholder="CCNP"
|
||||
{...getFieldProps(formik, 'title')}
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Issuer"
|
||||
placeholder="Cisco Systems"
|
||||
{...getFieldProps(formik, 'issuer')}
|
||||
{...getFieldProps(formik, schema, 'issuer')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
{...getFieldProps(formik, 'date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, 'summary')}
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
</div>
|
||||
</DataModal>
|
||||
|
||||
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
@ -60,40 +52,40 @@ const EducationModal = () => {
|
||||
label="Institution"
|
||||
className="col-span-2"
|
||||
placeholder="Dayananda Sagar College of Engineering"
|
||||
{...getFieldProps(formik, 'institution')}
|
||||
{...getFieldProps(formik, schema, 'institution')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Field of Study"
|
||||
className="col-span-2"
|
||||
placeholder="Computer Science & Engineering"
|
||||
{...getFieldProps(formik, 'field')}
|
||||
{...getFieldProps(formik, schema, 'field')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Degree Type"
|
||||
placeholder="Bachelor's Degree"
|
||||
{...getFieldProps(formik, 'degree')}
|
||||
{...getFieldProps(formik, schema, 'degree')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="GPA"
|
||||
placeholder="8.8"
|
||||
{...getFieldProps(formik, 'gpa')}
|
||||
{...getFieldProps(formik, schema, 'gpa')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Start Date"
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, 'startDate')}
|
||||
{...getFieldProps(formik, schema, 'startDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="End Date"
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, 'endDate')}
|
||||
{...getFieldProps(formik, schema, 'endDate')}
|
||||
/>
|
||||
|
||||
<FieldArray
|
||||
@ -127,13 +119,13 @@ const EducationModal = () => {
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
placeholder="Algorithms & Data Structures"
|
||||
{...getFieldProps(formik, 'temp')}
|
||||
{...getFieldProps(formik, schema, 'temp')}
|
||||
/>
|
||||
<MdAdd
|
||||
size="18px"
|
||||
tabIndex="0"
|
||||
className="mx-4 cursor-pointer opacity-50 hover:opacity-75"
|
||||
onKeyDown={(e) => handleKeyDown(e, handleClickAdd)}
|
||||
onKeyUp={(e) => handleKeyUp(e, handleClickAdd)}
|
||||
onClick={handleClickAdd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
44
src/modals/sections/HobbyModal.js
Normal file
44
src/modals/sections/HobbyModal.js
Normal file
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Hobby"
|
||||
path="hobbies.items"
|
||||
event={ModalEvents.HOBBY_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
placeholder="Fishing"
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
</div>
|
||||
</DataModal>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
||||
export default HobbyModal;
|
||||
51
src/modals/sections/LanguageModal.js
Normal file
51
src/modals/sections/LanguageModal.js
Normal file
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Language"
|
||||
path="languages.items"
|
||||
event={ModalEvents.LANGUAGE_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
placeholder="German"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Fluency"
|
||||
placeholder="Native/B1"
|
||||
{...getFieldProps(formik, schema, 'fluency')}
|
||||
/>
|
||||
</div>
|
||||
</DataModal>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguageModal;
|
||||
76
src/modals/sections/ReferenceModal.js
Normal file
76
src/modals/sections/ReferenceModal.js
Normal file
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Reference"
|
||||
path="references.items"
|
||||
event={ModalEvents.REFERENCE_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
placeholder="Jane Doe"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="position"
|
||||
placeholder="Assistant Manager"
|
||||
{...getFieldProps(formik, schema, 'position')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Phone Number"
|
||||
placeholder="+1 (708) 756-6065"
|
||||
{...getFieldProps(formik, schema, 'contact')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Email Address"
|
||||
placeholder="janedoe@example.com"
|
||||
{...getFieldProps(formik, schema, 'email')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
</div>
|
||||
</DataModal>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferenceModal;
|
||||
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
@ -40,13 +43,14 @@ const SkillModal = () => {
|
||||
<Input
|
||||
label="Name"
|
||||
placeholder="ReactJS"
|
||||
{...getFieldProps(formik, 'name')}
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="select"
|
||||
label="Level"
|
||||
{...getFieldProps(formik, 'level')}
|
||||
type="dropdown"
|
||||
options={SKILL_LEVELS}
|
||||
{...getFieldProps(formik, schema, 'level')}
|
||||
/>
|
||||
</div>
|
||||
</DataModal>
|
||||
|
||||
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
@ -47,20 +40,20 @@ const SocialModal = () => {
|
||||
<Input
|
||||
label="Network"
|
||||
placeholder="Twitter"
|
||||
{...getFieldProps(formik, 'network')}
|
||||
{...getFieldProps(formik, schema, 'network')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Username"
|
||||
placeholder="KingOKings"
|
||||
{...getFieldProps(formik, 'username')}
|
||||
{...getFieldProps(formik, schema, 'username')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="URL"
|
||||
className="col-span-2"
|
||||
placeholder="https://twitter.com/KingOKings"
|
||||
{...getFieldProps(formik, 'url')}
|
||||
{...getFieldProps(formik, schema, 'url')}
|
||||
/>
|
||||
</div>
|
||||
</DataModal>
|
||||
|
||||
@ -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
|
||||
validateOnBlur
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
validationSchema={schema}
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
@ -62,40 +54,40 @@ const WorkModal = () => {
|
||||
label="Company"
|
||||
className="col-span-2"
|
||||
placeholder="Postdot Technologies Pvt. Ltd."
|
||||
{...getFieldProps(formik, 'company')}
|
||||
{...getFieldProps(formik, schema, 'company')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Position"
|
||||
placeholder="Full Stack Web Developer"
|
||||
{...getFieldProps(formik, 'position')}
|
||||
{...getFieldProps(formik, schema, 'position')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Website"
|
||||
placeholder="https://example.com/"
|
||||
{...getFieldProps(formik, 'website')}
|
||||
{...getFieldProps(formik, schema, 'website')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Start Date"
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, 'startDate')}
|
||||
{...getFieldProps(formik, schema, 'startDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="End Date"
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, 'endDate')}
|
||||
{...getFieldProps(formik, schema, 'endDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, 'summary')}
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
||||
<FieldArray
|
||||
@ -129,13 +121,13 @@ const WorkModal = () => {
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
placeholder="Worked passionately in customer service in a high volume restaurant."
|
||||
{...getFieldProps(formik, 'temp')}
|
||||
{...getFieldProps(formik, schema, 'temp')}
|
||||
/>
|
||||
<MdAdd
|
||||
size="18px"
|
||||
tabIndex="0"
|
||||
className="mx-4 cursor-pointer opacity-50 hover:opacity-75"
|
||||
onKeyDown={(e) => handleKeyDown(e, handleClickAdd)}
|
||||
onKeyUp={(e) => handleKeyUp(e, handleClickAdd)}
|
||||
onClick={handleClickAdd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -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),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user