diff --git a/gatsby-browser.js b/gatsby-browser.js
index b6aa7cd0..c57786c5 100644
--- a/gatsby-browser.js
+++ b/gatsby-browser.js
@@ -2,10 +2,12 @@ import { createMuiTheme, MuiThemeProvider } from "@material-ui/core";
import "firebase/analytics";
import "firebase/auth";
import "firebase/database";
+import "firebase/storage";
import React from "react";
import { DatabaseProvider } from "./src/contexts/DatabaseContext";
import { ModalProvider } from "./src/contexts/ModalContext";
import { ResumeProvider } from "./src/contexts/ResumeContext";
+import { StorageProvider } from "./src/contexts/StorageContext";
import { TemplateProvider } from "./src/contexts/TemplateContext";
import { ThemeProvider } from "./src/contexts/ThemeContext";
import { UserProvider } from "./src/contexts/UserContext";
@@ -29,7 +31,9 @@ export const wrapRootElement = ({ element }) => (
- {element}
+
+ {element}
+
diff --git a/gatsby-config.js b/gatsby-config.js
index 1fd56f8c..fbdcf92a 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -2,6 +2,7 @@ require("dotenv").config();
module.exports = {
plugins: [
+ `gatsby-plugin-react-helmet`,
{
resolve: `gatsby-plugin-prefetch-google-fonts`,
options: {
@@ -24,7 +25,6 @@ module.exports = {
},
},
`gatsby-plugin-lodash`,
- `gatsby-plugin-react-helmet`,
{
resolve: `gatsby-plugin-manifest`,
options: {
diff --git a/gatsby-ssr.js b/gatsby-ssr.js
index d6039e77..b29ef852 100644
--- a/gatsby-ssr.js
+++ b/gatsby-ssr.js
@@ -1,3 +1,4 @@
import "firebase/analytics";
import "firebase/auth";
import "firebase/database";
+import "firebase/storage";
diff --git a/package-lock.json b/package-lock.json
index 256982b2..90c935ce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3552,6 +3552,11 @@
"resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
"integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI="
},
+ "array-move": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/array-move/-/array-move-2.2.2.tgz",
+ "integrity": "sha512-lKc6C+nsOSA1o7eHSP/HshlGDYUI7QKyaus5kPDm2zEEPQID9xlspnraLR8l+rDlqg9mGo8ziE7F8TEnF6D3Tw=="
+ },
"array-reduce": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
@@ -9469,6 +9474,11 @@
}
}
},
+ "gatsby-source-gravatar": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gatsby-source-gravatar/-/gatsby-source-gravatar-1.0.0.tgz",
+ "integrity": "sha512-pN48dBm1oinhhrtypdOOgRu0nO3ZPARdZYKdTwIm8Pyfql1BbHUHwLfYf8E14SFkthHkUnXLh63q/qiQIGvqbw=="
+ },
"gatsby-telemetry": {
"version": "1.3.18",
"resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.3.18.tgz",
diff --git a/package.json b/package.json
index 8a0bb08d..285bb821 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
},
"dependencies": {
"@material-ui/core": "^4.11.0",
+ "array-move": "^2.2.2",
"classnames": "^2.2.6",
"firebase": "^7.15.5",
"formik": "^2.1.4",
@@ -30,6 +31,7 @@
"gatsby-plugin-react-helmet": "^3.3.9",
"gatsby-plugin-sharp": "^2.6.18",
"gatsby-source-filesystem": "^2.3.18",
+ "gatsby-source-gravatar": "^1.0.0",
"gatsby-transformer-sharp": "^2.5.10",
"lodash": "^4.17.15",
"moment": "^2.27.0",
diff --git a/src/components/builder/center/Artboard.js b/src/components/builder/center/Artboard.js
index 937b4806..3033804c 100644
--- a/src/components/builder/center/Artboard.js
+++ b/src/components/builder/center/Artboard.js
@@ -1,4 +1,5 @@
import React, { useContext } from "react";
+import { Helmet } from "react-helmet";
import ResumeContext from "../../../contexts/ResumeContext";
import TemplateContext from "../../../contexts/TemplateContext";
import Onyx from "../../../templates/Onyx";
@@ -9,8 +10,18 @@ const Artboard = () => {
const { state } = useContext(ResumeContext);
return (
-
-
+
+
+ {state.name} | Reactive Resume
+
+
+
+
+
+
);
};
diff --git a/src/components/builder/left/LeftSidebar.js b/src/components/builder/left/LeftSidebar.js
index 8be1a5a5..bc7c7f7b 100644
--- a/src/components/builder/left/LeftSidebar.js
+++ b/src/components/builder/left/LeftSidebar.js
@@ -1,9 +1,12 @@
-import React, { Fragment } from "react";
+import React, { Fragment, useContext } from "react";
+import ResumeContext from "../../../contexts/ResumeContext";
import sections from "../../../data/leftSections";
import LeftNavbar from "./LeftNavbar";
import styles from "./LeftSidebar.module.css";
const LeftSidebar = () => {
+ const { state } = useContext(ResumeContext);
+
return (
@@ -11,7 +14,7 @@ const LeftSidebar = () => {
{sections.map(({ id, component: Component }) => (
-
+
))}
diff --git a/src/components/builder/lists/EmptyList.js b/src/components/builder/lists/EmptyList.js
new file mode 100644
index 00000000..05752dfd
--- /dev/null
+++ b/src/components/builder/lists/EmptyList.js
@@ -0,0 +1,9 @@
+import React from "react";
+
+const EmptyList = () => (
+
+ This list is empty.
+
+);
+
+export default EmptyList;
diff --git a/src/components/builder/lists/List.js b/src/components/builder/lists/List.js
new file mode 100644
index 00000000..962740c8
--- /dev/null
+++ b/src/components/builder/lists/List.js
@@ -0,0 +1,26 @@
+import { isEmpty } from "lodash";
+import React from "react";
+import { MdAdd } from "react-icons/md";
+import Button from "../../shared/Button";
+import EmptyList from "./EmptyList";
+import styles from "./List.module.css";
+
+const List = ({ items, onAdd, children }) => {
+ return (
+
+
+ {isEmpty(items) ? : children}
+
+
+
+
+ );
+};
+
+export default List;
diff --git a/src/components/builder/lists/List.module.css b/src/components/builder/lists/List.module.css
new file mode 100644
index 00000000..363aab2d
--- /dev/null
+++ b/src/components/builder/lists/List.module.css
@@ -0,0 +1,3 @@
+.container {
+ @apply flex flex-col border border-secondary rounded;
+}
\ No newline at end of file
diff --git a/src/components/builder/lists/small/SmallListItem.js b/src/components/builder/lists/small/SmallListItem.js
new file mode 100644
index 00000000..e9baf44a
--- /dev/null
+++ b/src/components/builder/lists/small/SmallListItem.js
@@ -0,0 +1,103 @@
+import { Menu, MenuItem } from "@material-ui/core";
+import React, { useContext, useState } from "react";
+import { IoIosArrowDown, IoIosArrowUp } from "react-icons/io";
+import { MdMoreVert } from "react-icons/md";
+import ResumeContext from "../../../../contexts/ResumeContext";
+import styles from "./SmallListItem.module.css";
+
+const SmallListItem = ({
+ title,
+ subtitle,
+ path,
+ data,
+ isFirst,
+ isLast,
+ onEdit,
+}) => {
+ const [anchorEl, setAnchorEl] = useState(null);
+ const { dispatch } = useContext(ResumeContext);
+
+ const handleClick = (event) => setAnchorEl(event.currentTarget);
+
+ const handleClose = () => setAnchorEl(null);
+
+ const handleEdit = () => {
+ onEdit();
+ handleClose();
+ };
+
+ const handleMoveUp = () => {
+ dispatch({
+ type: "on_move_item_up",
+ payload: {
+ path,
+ value: data,
+ },
+ });
+
+ handleClose();
+ };
+
+ const handleMoveDown = () => {
+ dispatch({
+ type: "on_move_item_down",
+ payload: {
+ path,
+ value: data,
+ },
+ });
+
+ handleClose();
+ };
+
+ const handleDelete = () => {
+ dispatch({
+ type: "on_delete_item",
+ payload: {
+ path,
+ value: data,
+ },
+ });
+
+ handleClose();
+ };
+
+ return (
+
+
+ {title}
+ {subtitle}
+
+
+
+
+
+
+
+ );
+};
+
+export default SmallListItem;
diff --git a/src/components/builder/lists/small/SmallListItem.module.css b/src/components/builder/lists/small/SmallListItem.module.css
new file mode 100644
index 00000000..699169ec
--- /dev/null
+++ b/src/components/builder/lists/small/SmallListItem.module.css
@@ -0,0 +1,17 @@
+.container {
+ @apply flex items-center justify-between border-t border-secondary px-8 py-5;
+}
+
+.container:first-child {
+ @apply border-t-0;
+}
+
+.menu {
+ @apply opacity-0;
+ @apply transition-opacity duration-200 ease-in-out;
+}
+
+.container:hover .menu {
+ @apply opacity-100;
+ @apply transition-opacity duration-200 ease-in-out;
+}
diff --git a/src/components/builder/sections/Profile.js b/src/components/builder/sections/Profile.js
index c3333b5c..79aa8da3 100644
--- a/src/components/builder/sections/Profile.js
+++ b/src/components/builder/sections/Profile.js
@@ -1,19 +1,45 @@
-import React from "react";
+import React, { useContext, useRef } from "react";
import { MdFileUpload } from "react-icons/md";
+import StorageContext from "../../../contexts/StorageContext";
+import { handleKeyDown } from "../../../utils";
import Heading from "../../shared/Heading";
import Input from "../../shared/Input";
import styles from "./Profile.module.css";
const Profile = () => {
+ const fileInputRef = useRef(null);
+ const { uploadPhotograph } = useContext(StorageContext);
+
+ const handleIconClick = () => {
+ fileInputRef.current.click();
+ };
+
+ const handleImageUpload = (e) => {
+ const file = e.target.files[0];
+ uploadPhotograph(file);
+ };
+
return (
Profile
-
diff --git a/src/components/builder/sections/Profile.module.css b/src/components/builder/sections/Profile.module.css
index d2ac4b0c..40d90b5e 100644
--- a/src/components/builder/sections/Profile.module.css
+++ b/src/components/builder/sections/Profile.module.css
@@ -2,5 +2,5 @@
width: 60px;
height: 60px;
flex: 0 0 60px;
- @apply flex items-center justify-center bg-secondary text-secondary-dark rounded-full;
+ @apply flex items-center justify-center cursor-pointer bg-secondary text-secondary-dark rounded-full;
}
diff --git a/src/components/builder/sections/Social.js b/src/components/builder/sections/Social.js
new file mode 100644
index 00000000..ed8b5c86
--- /dev/null
+++ b/src/components/builder/sections/Social.js
@@ -0,0 +1,40 @@
+import { get } from "lodash";
+import React, { useContext } from "react";
+import ModalContext from "../../../contexts/ModalContext";
+import Heading from "../../shared/Heading";
+import List from "../lists/List";
+import SmallListItem from "../lists/small/SmallListItem";
+
+const Social = ({ state }) => {
+ const { emitter, events } = useContext(ModalContext);
+
+ const path = "social.items";
+ const items = get(state, path, []);
+
+ const handleAdd = () => emitter.emit(events.SOCIAL_MODAL);
+
+ const handleEdit = (data) => emitter.emit(events.SOCIAL_MODAL, data);
+
+ return (
+
+ Social Network
+
+
+ {items.map((x, i) => (
+ handleEdit(x)}
+ isLast={i === items.length - 1}
+ />
+ ))}
+
+
+ );
+};
+
+export default Social;
diff --git a/src/components/builder/sections/SocialNetwork.js b/src/components/builder/sections/SocialNetwork.js
deleted file mode 100644
index 0488b7f2..00000000
--- a/src/components/builder/sections/SocialNetwork.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from "react";
-import Heading from "../../shared/Heading";
-
-const SocialNetwork = () => {
- return (
-
- );
-};
-
-export default SocialNetwork;
diff --git a/src/components/dashboard/CreateResume.js b/src/components/dashboard/CreateResume.js
index 23d6cc3d..73526461 100644
--- a/src/components/dashboard/CreateResume.js
+++ b/src/components/dashboard/CreateResume.js
@@ -1,6 +1,7 @@
import React, { useContext } from "react";
import { MdAdd } from "react-icons/md";
import ModalContext from "../../contexts/ModalContext";
+import { handleKeyDown } from "../../utils";
import styles from "./CreateResume.module.css";
const CreateResume = () => {
@@ -18,12 +19,12 @@ const CreateResume = () => {
role="button"
className={styles.page}
onClick={handleClick}
- onKeyDown={() => {}}
+ onKeyDown={(e) => handleKeyDown(e, handleClick)}
>
-
Create New Resume
+
Create Resume
);
diff --git a/src/components/dashboard/ResumePreview.js b/src/components/dashboard/ResumePreview.js
index 56bb0d4e..749bdee9 100644
--- a/src/components/dashboard/ResumePreview.js
+++ b/src/components/dashboard/ResumePreview.js
@@ -64,7 +64,7 @@ const ResumePreview = ({ resume }) => {
>
diff --git a/src/components/router/PrivateRoute.js b/src/components/router/PrivateRoute.js
index a1e06458..1df958dc 100644
--- a/src/components/router/PrivateRoute.js
+++ b/src/components/router/PrivateRoute.js
@@ -7,7 +7,7 @@ const PrivateRoute = ({ component: Component, location, ...props }) => {
const { user, loading } = useContext(UserContext);
if (loading) {
- return
;
+ return
;
}
if (!user) {
diff --git a/src/components/shared/Avatar.js b/src/components/shared/Avatar.js
index 61f07cc8..e88418d2 100644
--- a/src/components/shared/Avatar.js
+++ b/src/components/shared/Avatar.js
@@ -1,15 +1,18 @@
import cx from "classnames";
-import React, { useContext } from "react";
+import { toUrl } from "gatsby-source-gravatar";
+import React, { useContext, useMemo } from "react";
import UserContext from "../../contexts/UserContext";
import styles from "./Avatar.module.css";
const Avatar = ({ className }) => {
const { user } = useContext(UserContext);
+ const photoURL = useMemo(() => toUrl(user.email, "size=128"), [user.email]);
+
return (

);
diff --git a/src/components/shared/Button.js b/src/components/shared/Button.js
index 6ee91653..840f3782 100644
--- a/src/components/shared/Button.js
+++ b/src/components/shared/Button.js
@@ -1,5 +1,6 @@
import classNames from "classnames";
import React from "react";
+import { handleKeyDown } from "../../utils";
import styles from "./Button.module.css";
const Button = ({
@@ -16,13 +17,11 @@ const Button = ({
[styles.outline]: outline,
});
- const handleKeyDown = () => {};
-
return (
);
diff --git a/src/contexts/DatabaseContext.js b/src/contexts/DatabaseContext.js
index 57347a5f..0f45717b 100644
--- a/src/contexts/DatabaseContext.js
+++ b/src/contexts/DatabaseContext.js
@@ -1,7 +1,6 @@
import firebase from "gatsby-plugin-firebase";
import { debounce } from "lodash";
import React, { createContext, useContext, useEffect, useState } from "react";
-import { v4 as uuidv4 } from "uuid";
import UserContext from "./UserContext";
const defaultState = {
@@ -38,27 +37,22 @@ const DatabaseProvider = ({ children }) => {
};
const createResume = (resume) => {
- const id = uuidv4();
+ const { id } = resume;
const createdAt = firebase.database.ServerValue.TIMESTAMP;
- let firstName = "",
- lastName = "",
- photograph = "";
+ let firstName, lastName;
if (!user.isAnonymous) {
[firstName, lastName] = user.displayName.split(" ");
- photograph = user.photoURL;
}
firebase
.database()
.ref(`users/${user.uid}/resumes/${id}`)
.set({
- id,
profile: {
- firstName,
- lastName,
- photograph,
+ firstName: firstName || "",
+ lastName: lastName || "",
},
...resume,
createdAt,
diff --git a/src/contexts/ModalContext.js b/src/contexts/ModalContext.js
index a7c6d3a1..a15c1b78 100644
--- a/src/contexts/ModalContext.js
+++ b/src/contexts/ModalContext.js
@@ -4,6 +4,7 @@ import React, { createContext } from "react";
const events = {
AUTH_MODAL: "auth_modal",
CREATE_RESUME_MODAL: "create_resume_modal",
+ SOCIAL_MODAL: "social_modal",
};
const emitter = createNanoEvents();
diff --git a/src/contexts/ResumeContext.js b/src/contexts/ResumeContext.js
index a953f829..83bc4870 100644
--- a/src/contexts/ResumeContext.js
+++ b/src/contexts/ResumeContext.js
@@ -1,26 +1,88 @@
-import { set } from "lodash";
-import React, { createContext, useContext, useReducer } from "react";
+import arrayMove from "array-move";
+import { clone, findIndex, get, setWith } from "lodash";
+import React, {
+ createContext,
+ useCallback,
+ useContext,
+ useReducer,
+} from "react";
import DatabaseContext from "./DatabaseContext";
-const ResumeContext = createContext({});
+const initialState = {};
+
+const ResumeContext = createContext(initialState);
const ResumeProvider = ({ children }) => {
const { debouncedUpdate } = useContext(DatabaseContext);
- const [state, dispatch] = useReducer((state, { type, payload }) => {
- let newState;
+ const memoizedReducer = useCallback(
+ (state, { type, payload }) => {
+ let newState, index, items;
- switch (type) {
- case "on_input":
- newState = set({ ...state }, payload.path, payload.value);
- debouncedUpdate(newState);
- return newState;
- case "set_data":
- return payload;
- default:
- throw new Error();
- }
- }, {});
+ switch (type) {
+ case "on_add_item":
+ items = get(state, payload.path, []);
+ newState = setWith(
+ clone(state),
+ payload.path,
+ [...items, payload.value],
+ clone
+ );
+ debouncedUpdate(newState);
+ return newState;
+
+ case "on_edit_item":
+ items = get(state, payload.path);
+ index = findIndex(items, ["id", payload.value.id]);
+ newState = setWith(
+ clone(state),
+ `${payload.path}[${index}]`,
+ payload.value,
+ clone
+ );
+ debouncedUpdate(newState);
+ return newState;
+
+ case "on_delete_item":
+ items = get(state, payload.path);
+ index = findIndex(items, ["id", payload.value.id]);
+ items.splice(index, 1);
+ newState = setWith(clone(state), payload.path, items, clone);
+ debouncedUpdate(newState);
+ return newState;
+
+ case "on_move_item_up":
+ items = get(state, payload.path);
+ index = findIndex(items, ["id", payload.value.id]);
+ items = arrayMove(items, index, index - 1);
+ newState = setWith(clone(state), payload.path, items, clone);
+ debouncedUpdate(newState);
+ return newState;
+
+ case "on_move_item_down":
+ items = get(state, payload.path);
+ index = findIndex(items, ["id", payload.value.id]);
+ items = arrayMove(items, index, index + 1);
+ newState = setWith(clone(state), payload.path, items, clone);
+ debouncedUpdate(newState);
+ return newState;
+
+ case "on_input":
+ newState = setWith(clone(state), payload.path, payload.value, clone);
+ debouncedUpdate(newState);
+ return newState;
+
+ case "set_data":
+ return payload;
+
+ default:
+ throw new Error();
+ }
+ },
+ [debouncedUpdate]
+ );
+
+ const [state, dispatch] = useReducer(memoizedReducer, initialState);
return (
diff --git a/src/contexts/StorageContext.js b/src/contexts/StorageContext.js
new file mode 100644
index 00000000..02d97454
--- /dev/null
+++ b/src/contexts/StorageContext.js
@@ -0,0 +1,85 @@
+import firebase from "gatsby-plugin-firebase";
+import React, { createContext, useContext, useRef } from "react";
+import { toast } from "react-toastify";
+import { isFileImage } from "../utils";
+import ResumeContext from "./ResumeContext";
+import UserContext from "./UserContext";
+
+const defaultState = {
+ uploadPhotograph: async () => {},
+};
+
+const StorageContext = createContext(defaultState);
+
+const StorageProvider = ({ children }) => {
+ const toastId = useRef(null);
+
+ const { user } = useContext(UserContext);
+ const { state, dispatch } = useContext(ResumeContext);
+
+ const uploadPhotograph = async (file) => {
+ if (!isFileImage(file)) {
+ toast.error(
+ "You tried to upload a file that was not an image. That won't look good on your resume. Please try again."
+ );
+ return null;
+ }
+
+ const uploadTask = firebase
+ .storage()
+ .ref(`/users/${user.uid}/photographs/${state.id}`)
+ .put(file);
+
+ let progress = 0;
+ toastId.current = toast("Firing up engines...", {
+ progress,
+ });
+
+ uploadTask.on(
+ "state_changed",
+ (snapshot) => {
+ progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
+ toast.update(toastId.current, {
+ render: "Uploading...",
+ progress,
+ hideProgressBar: false,
+ });
+ },
+ (error) => toast.error(error),
+ async () => {
+ const downloadURL = await uploadTask.snapshot.ref.getDownloadURL();
+ dispatch({
+ type: "on_input",
+ payload: {
+ path: "profile.photograph",
+ value: downloadURL,
+ },
+ });
+
+ toast.update(toastId.current, {
+ render:
+ "Your photograph was uploaded successfully... and you look great!",
+ progress,
+ autoClose: 5000,
+ hideProgressBar: true,
+ });
+
+ toastId.current = null;
+ }
+ );
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default StorageContext;
+
+export { StorageProvider };
diff --git a/src/contexts/UserContext.js b/src/contexts/UserContext.js
index dbae2ef1..179ca73e 100644
--- a/src/contexts/UserContext.js
+++ b/src/contexts/UserContext.js
@@ -6,13 +6,13 @@ import useAuthState from "../hooks/useAuthState";
const defaultUser = {
uid: null,
- displayName: null,
email: null,
- photoURL: null,
+ displayName: null,
isAnonymous: false,
};
const defaultState = {
+ loading: false,
user: defaultUser,
logout: async () => {},
loginWithGoogle: async () => {},
@@ -32,8 +32,8 @@ const UserProvider = ({ children }) => {
useEffect(() => {
if (firebaseUser) {
const user = pick(firebaseUser, Object.keys(defaultUser));
- setUser(user);
localStorage.setItem("user", JSON.stringify(user));
+ setUser(user);
const addUserToDatabase = async () => {
const userRef = firebase.database().ref(`users/${user.uid}`);
diff --git a/src/data/leftSections.js b/src/data/leftSections.js
index d06a677e..ef522d55 100644
--- a/src/data/leftSections.js
+++ b/src/data/leftSections.js
@@ -1,7 +1,7 @@
import { AiOutlineTwitter } from "react-icons/ai";
import { MdPerson } from "react-icons/md";
import Profile from "../components/builder/sections/Profile";
-import SocialNetwork from "../components/builder/sections/SocialNetwork";
+import Social from "../components/builder/sections/Social";
export default [
{
@@ -14,6 +14,6 @@ export default [
id: "social",
name: "Social Network",
icon: AiOutlineTwitter,
- component: SocialNetwork,
+ component: Social,
},
];
diff --git a/src/modals/CreateResumeModal.js b/src/modals/CreateResumeModal.js
deleted file mode 100644
index eba8089d..00000000
--- a/src/modals/CreateResumeModal.js
+++ /dev/null
@@ -1,103 +0,0 @@
-import { useFormik } from "formik";
-import React, { useContext, useEffect, useRef, useState } from "react";
-import * as Yup from "yup";
-import Button from "../components/shared/Button";
-import Input from "../components/shared/Input";
-import DatabaseContext from "../contexts/DatabaseContext";
-import ModalContext from "../contexts/ModalContext";
-import { getModalText } from "../utils";
-import BaseModal from "./BaseModal";
-
-const CreateResumeSchema = Yup.object().shape({
- name: Yup.string()
- .min(5, "Please enter at least 5 characters.")
- .required("This is a required field."),
-});
-
-const CreateResumeModal = () => {
- const modalRef = useRef(null);
-
- const [data, setData] = useState(null);
- const [open, setOpen] = useState(false);
- const [isEditMode, setEditMode] = useState(false);
-
- const { emitter, events } = useContext(ModalContext);
- const { createResume, updateResume } = useContext(DatabaseContext);
-
- useEffect(() => {
- const unbind = emitter.on(events.CREATE_RESUME_MODAL, (data) => {
- setOpen(true);
- setData(data);
- });
-
- return () => unbind();
- }, [emitter, events]);
-
- const formik = useFormik({
- initialValues: {
- name: "",
- },
- validationSchema: CreateResumeSchema,
- onSubmit: (newData) => {
- if (isEditMode) {
- if (data !== newData) {
- updateResume(newData);
- }
- } else {
- createResume(newData);
- }
-
- modalRef.current.handleClose();
- },
- });
-
- useEffect(() => {
- data && formik.setValues(data) && setEditMode(true);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [data]);
-
- const submitAction = (
-