diff --git a/gatsby-browser.js b/gatsby-browser.js index 8f75b786..828aea0c 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -1,11 +1,11 @@ import { createMuiTheme, MuiThemeProvider } from '@material-ui/core'; +import 'animate.css'; import 'firebase/analytics'; import 'firebase/auth'; import 'firebase/database'; import 'firebase/storage'; import React from 'react'; import { DatabaseProvider } from './src/contexts/DatabaseContext'; -import { MetadataProvider } from './src/contexts/MetadataContext'; import { ModalProvider } from './src/contexts/ModalContext'; import { ResumeProvider } from './src/contexts/ResumeContext'; import { StorageProvider } from './src/contexts/StorageContext'; @@ -32,9 +32,7 @@ export const wrapRootElement = ({ element }) => ( - - {element} - + {element} diff --git a/package-lock.json b/package-lock.json index fdb5ef51..fa762c1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2700,6 +2700,11 @@ "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" }, + "animate.css": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.0.tgz", + "integrity": "sha512-0aVcfWDeU9ykV6vjn1P67ZSs01jxoUQZCGaYbkk0SIIelIG8kUdLrIkua1+VabHfTtsSivDRMMn0ILPvZum2gw==" + }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -8225,9 +8230,9 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, "gatsby": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-2.24.0.tgz", - "integrity": "sha512-l/9ueKdnHNB3+nbzZRaj2mYWJMAay7PwMN+M0VfdIgA2K6d/lz3xI8W5BRNmNJyuk8itykB64r7NFNxsnTChRw==", + "version": "2.24.1", + "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-2.24.1.tgz", + "integrity": "sha512-aqFfx+Vj3kBhS17tgL1LrkMzip2Xctd3lCj+pogCGV9GCkg6wOqW2uOEqWeoiCNq09sPuwv3GNB8sbEFoQ/2DA==", "requires": { "@babel/code-frame": "^7.10.3", "@babel/core": "^7.10.3", @@ -8543,11 +8548,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -8556,49 +8556,11 @@ "yallist": "^3.0.2" } }, - "mini-css-extract-plugin": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz", - "integrity": "sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw==", - "requires": { - "loader-utils": "^1.1.0", - "normalize-url": "1.9.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - } - }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - }, - "dependencies": { - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - } - } - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -8612,29 +8574,11 @@ "glob": "^7.1.3" } }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "requires": { - "is-plain-obj": "^1.0.0" - } - }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", @@ -12667,10 +12611,9 @@ } }, "mini-css-extract-plugin": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", - "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", - "dev": true, + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz", + "integrity": "sha512-a3Y4of27Wz+mqK3qrcd3VhYz6cU0iW5x3Sgvqzbj+XmlrSizmvu8QQMl5oMYJjgHOC4iyt+w7l4umP+dQeW3bw==", "requires": { "loader-utils": "^1.1.0", "normalize-url": "1.9.1", @@ -12681,14 +12624,12 @@ "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" }, "normalize-url": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, "requires": { "object-assign": "^4.0.1", "prepend-http": "^1.0.0", @@ -12699,14 +12640,12 @@ "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, "query-string": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, "requires": { "object-assign": "^4.1.0", "strict-uri-encode": "^1.0.0" @@ -12716,7 +12655,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, "requires": { "ajv": "^6.1.0", "ajv-errors": "^1.0.0", @@ -12727,7 +12665,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, "requires": { "is-plain-obj": "^1.0.0" } diff --git a/package.json b/package.json index cc7e6ffa..6f0bd3d5 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,13 @@ "dependencies": { "@material-ui/core": "^4.11.0", "@reach/router": "^1.3.4", + "animate.css": "^4.1.0", "array-move": "^2.2.2", "classnames": "^2.2.6", "dotenv": "^8.2.0", "firebase": "^7.15.5", "formik": "^2.1.4", - "gatsby": "^2.24.0", + "gatsby": "^2.24.1", "gatsby-image": "^2.4.13", "gatsby-plugin-create-client-paths": "^2.3.10", "gatsby-plugin-firebase": "^0.2.0-beta.4", diff --git a/src/components/builder/center/Artboard.js b/src/components/builder/center/Artboard.js index 71a4d21f..3480dd2b 100644 --- a/src/components/builder/center/Artboard.js +++ b/src/components/builder/center/Artboard.js @@ -1,14 +1,13 @@ import React, { memo } from 'react'; import { Helmet } from 'react-helmet'; -import { useSelector as useMetadataSelector } from '../../../contexts/MetadataContext'; -import { useSelector as useResumeSelector } from '../../../contexts/ResumeContext'; +import { useSelector } from '../../../contexts/ResumeContext'; import Onyx from '../../../templates/Onyx'; import styles from './Artboard.module.css'; const Artboard = () => { - const { template, layout, colors } = useMetadataSelector((store) => store); - const state = useResumeSelector((store) => store); - const { id, name } = state; + const state = useSelector(); + const { id, name, metadata } = state; + const { template } = metadata; return (
@@ -18,9 +17,7 @@ const Artboard = () => {
- {template === 'Onyx' && ( - - )} + {template === 'onyx' && }
); diff --git a/src/components/builder/left/LeftNavbar.js b/src/components/builder/left/LeftNavbar.js index 7b396fd4..1194e393 100644 --- a/src/components/builder/left/LeftNavbar.js +++ b/src/components/builder/left/LeftNavbar.js @@ -14,7 +14,7 @@ const LeftNavbar = () => (
-
+
{sections.map((x) => ( { const path = `${id}.items`; diff --git a/src/components/builder/sections/Certifications.js b/src/components/builder/left/sections/Certifications.js similarity index 82% rename from src/components/builder/sections/Certifications.js rename to src/components/builder/left/sections/Certifications.js index f8284085..ce3bbff1 100644 --- a/src/components/builder/sections/Certifications.js +++ b/src/components/builder/left/sections/Certifications.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const Certifications = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/sections/Education.js b/src/components/builder/left/sections/Education.js similarity index 81% rename from src/components/builder/sections/Education.js rename to src/components/builder/left/sections/Education.js index 2ab0e329..535ca3c3 100644 --- a/src/components/builder/sections/Education.js +++ b/src/components/builder/left/sections/Education.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const Education = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/sections/Hobbies.js b/src/components/builder/left/sections/Hobbies.js similarity index 77% rename from src/components/builder/sections/Hobbies.js rename to src/components/builder/left/sections/Hobbies.js index b3d9352e..10f2675e 100644 --- a/src/components/builder/sections/Hobbies.js +++ b/src/components/builder/left/sections/Hobbies.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const Hobbies = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/sections/Languages.js b/src/components/builder/left/sections/Languages.js similarity index 78% rename from src/components/builder/sections/Languages.js rename to src/components/builder/left/sections/Languages.js index 9777b28c..8b02e505 100644 --- a/src/components/builder/sections/Languages.js +++ b/src/components/builder/left/sections/Languages.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const Languages = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/sections/Objective.js b/src/components/builder/left/sections/Objective.js similarity index 73% rename from src/components/builder/sections/Objective.js rename to src/components/builder/left/sections/Objective.js index af92699c..f42ae5d5 100644 --- a/src/components/builder/sections/Objective.js +++ b/src/components/builder/left/sections/Objective.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import Input from '../../shared/Input'; +import Heading from '../../../shared/Heading'; +import Input from '../../../shared/Input'; const Objective = () => { return ( diff --git a/src/components/builder/sections/Profile.js b/src/components/builder/left/sections/Profile.js similarity index 89% rename from src/components/builder/sections/Profile.js rename to src/components/builder/left/sections/Profile.js index d61ff3b9..93682815 100644 --- a/src/components/builder/sections/Profile.js +++ b/src/components/builder/left/sections/Profile.js @@ -1,7 +1,7 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import Input from '../../shared/Input'; -import PhotoUpload from '../../shared/PhotoUpload'; +import Heading from '../../../shared/Heading'; +import Input from '../../../shared/Input'; +import PhotoUpload from '../../../shared/PhotoUpload'; const Profile = () => { return ( diff --git a/src/components/builder/sections/References.js b/src/components/builder/left/sections/References.js similarity index 81% rename from src/components/builder/sections/References.js rename to src/components/builder/left/sections/References.js index 533d98ee..dcaa98fa 100644 --- a/src/components/builder/sections/References.js +++ b/src/components/builder/left/sections/References.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const References = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/sections/Skills.js b/src/components/builder/left/sections/Skills.js similarity index 78% rename from src/components/builder/sections/Skills.js rename to src/components/builder/left/sections/Skills.js index 1ed5b58f..66851955 100644 --- a/src/components/builder/sections/Skills.js +++ b/src/components/builder/left/sections/Skills.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const Skills = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/sections/Social.js b/src/components/builder/left/sections/Social.js similarity index 80% rename from src/components/builder/sections/Social.js rename to src/components/builder/left/sections/Social.js index 5ddf11fa..271caee5 100644 --- a/src/components/builder/sections/Social.js +++ b/src/components/builder/left/sections/Social.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const Social = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/sections/Work.js b/src/components/builder/left/sections/Work.js similarity index 80% rename from src/components/builder/sections/Work.js rename to src/components/builder/left/sections/Work.js index a7c9c89d..11637c35 100644 --- a/src/components/builder/sections/Work.js +++ b/src/components/builder/left/sections/Work.js @@ -1,6 +1,6 @@ import React, { memo } from 'react'; -import Heading from '../../shared/Heading'; -import List from '../lists/List'; +import Heading from '../../../shared/Heading'; +import List from '../../lists/List'; const Work = ({ id, name, event }) => { const path = `${id}.items`; diff --git a/src/components/builder/lists/List.js b/src/components/builder/lists/List.js index 9ec8be33..24f92c3b 100644 --- a/src/components/builder/lists/List.js +++ b/src/components/builder/lists/List.js @@ -20,7 +20,7 @@ const List = ({ hasDate, event, }) => { - const items = useSelector((state) => get(state, path, [])); + const items = useSelector(path, []); const { emitter } = useContext(ModalContext); const handleAdd = () => emitter.emit(event); diff --git a/src/components/builder/lists/ListItem.js b/src/components/builder/lists/ListItem.js index 058dd010..33976585 100644 --- a/src/components/builder/lists/ListItem.js +++ b/src/components/builder/lists/ListItem.js @@ -82,7 +82,7 @@ const ListItem = ({ size="18px" aria-haspopup="true" onClick={handleClick} - className="cursor-pointer" + className="cursor-context-menu" /> { return (
-
+
{sections.map((x) => ( { switch (id) { + case 'templates': + return Templates; case 'layout': return Layout; default: diff --git a/src/components/builder/right/RightSidebar.module.css b/src/components/builder/right/RightSidebar.module.css index fbd0bea5..cab6e4ad 100644 --- a/src/components/builder/right/RightSidebar.module.css +++ b/src/components/builder/right/RightSidebar.module.css @@ -4,7 +4,7 @@ -ms-overflow-style: none; scrollbar-width: none; @apply w-full h-screen overflow-scroll p-8; - @apply grid gap-6; + @apply grid gap-8; } .container::-webkit-scrollbar { diff --git a/src/components/builder/right/sections/Colors.js b/src/components/builder/right/sections/Colors.js new file mode 100644 index 00000000..59b9504a --- /dev/null +++ b/src/components/builder/right/sections/Colors.js @@ -0,0 +1,47 @@ +import React, { memo } from 'react'; +import { useDispatch } from '../../../../contexts/ResumeContext'; +import Heading from '../../../shared/Heading'; +import Input from '../../../shared/Input'; + +const Colors = () => { + const dispatch = useDispatch(); + + const handleClick = (value) => { + dispatch({ + type: 'on_input', + payload: { + path: 'metadata.colors.primary', + value, + }, + }); + }; + + return ( +
+ Colors + + + + + + +
+ ); +}; + +export default memo(Colors); diff --git a/src/components/builder/right/sections/Colors.module.css b/src/components/builder/right/sections/Colors.module.css new file mode 100644 index 00000000..e69de29b diff --git a/src/components/builder/sections/Layout.js b/src/components/builder/right/sections/Layout.js similarity index 83% rename from src/components/builder/sections/Layout.js rename to src/components/builder/right/sections/Layout.js index 938657ec..d9f39d22 100644 --- a/src/components/builder/sections/Layout.js +++ b/src/components/builder/right/sections/Layout.js @@ -1,12 +1,12 @@ import React, { memo } from 'react'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; -import { useDispatch, useSelector } from '../../../contexts/MetadataContext'; -import { move, reorder } from '../../../utils'; -import Heading from '../../shared/Heading'; +import { useDispatch, useSelector } from '../../../../contexts/ResumeContext'; +import { move, reorder } from '../../../../utils'; +import Heading from '../../../shared/Heading'; import styles from './Layout.module.css'; const Layout = () => { - const blocks = useSelector((state) => state.layout); + const blocks = useSelector('metadata.layout'); const dispatch = useDispatch(); const onDragEnd = (result) => { @@ -22,13 +22,25 @@ const Layout = () => { const items = reorder(blocks[sInd], source.index, destination.index); const newState = [...blocks]; newState[sInd] = items; - dispatch({ type: 'set_layout', payload: newState }); + dispatch({ + type: 'on_input', + payload: { + path: 'metadata.layout', + value: newState, + }, + }); } else { const newResult = move(blocks[sInd], blocks[dInd], source, destination); const newState = [...blocks]; newState[sInd] = newResult[sInd]; newState[dInd] = newResult[dInd]; - dispatch({ type: 'set_layout', payload: newState }); + dispatch({ + type: 'on_input', + payload: { + path: 'layout', + value: newState, + }, + }); } }; @@ -58,8 +70,8 @@ const Layout = () => { {el.map((item, index) => ( {(dragProvided) => (
{ + const dispatch = useDispatch(); + const template = useSelector('metadata.template'); + + const handleClick = (value) => { + dispatch({ + type: 'on_input', + payload: { + path: 'metadata.template', + value, + }, + }); + }; + + return ( +
+ Templates + +
+ {templates.map((x) => ( +
handleKeyUp(e, () => handleClick(x.id))} + onClick={() => handleClick(x.id)} + className={cx(styles.template, { + [styles.selected]: template === x.id, + })} + > + {x.name} + {x.name} +
+ ))} +
+
+ ); +}; + +export default memo(Templates); diff --git a/src/components/builder/right/sections/Templates.module.css b/src/components/builder/right/sections/Templates.module.css new file mode 100644 index 00000000..e1cd3f7c --- /dev/null +++ b/src/components/builder/right/sections/Templates.module.css @@ -0,0 +1,26 @@ +.template { + @apply w-full rounded flex flex-col items-center; +} + +.template:focus { + @apply outline-none; +} + +.template img { + height: 240px; + @apply w-full object-cover border-2 border-transparent rounded; + @apply transition-opacity duration-200 ease-in-out; +} + +.template.selected img { + @apply border-2 border-primary; +} + +.template:hover img { + @apply cursor-pointer opacity-75; + @apply transition-opacity duration-200 ease-in-out; +} + +.template span { + @apply mt-1 text-center text-sm font-semibold; +} \ No newline at end of file diff --git a/src/components/shared/Input.js b/src/components/shared/Input.js index fd25cc3f..8b89e449 100644 --- a/src/components/shared/Input.js +++ b/src/components/shared/Input.js @@ -1,5 +1,5 @@ import cx from 'classnames'; -import { get, isFunction } from 'lodash'; +import { isFunction } from 'lodash'; import React, { memo, useEffect, useState } from 'react'; import { FaAngleDown } from 'react-icons/fa'; import { MdClose } from 'react-icons/md'; @@ -26,14 +26,14 @@ const Input = ({ type = 'text', }) => { const [uuid, setUuid] = useState(null); - const stateValue = useSelector((state) => get(state, path)); + const stateValue = useSelector(path, ''); const dispatch = useDispatch(); useEffect(() => { setUuid(uuidv4()); }, []); - value = isFunction(onChange) ? value : stateValue; + value = path ? stateValue : value; onChange = isFunction(onChange) ? onChange : (e) => { diff --git a/src/components/shared/Input.module.css b/src/components/shared/Input.module.css index 548efec1..6848fc79 100644 --- a/src/components/shared/Input.module.css +++ b/src/components/shared/Input.module.css @@ -14,10 +14,16 @@ @apply text-primary opacity-50; } +.container label input:hover, +.container label textarea:hover, +.container label select:hover { + @apply outline-none border-secondary-dark; +} + .container label input:focus, .container label textarea:focus, .container label select:focus { - @apply outline-none border-gray-500; + @apply outline-none border-primary; } .container label > p { diff --git a/src/components/shared/PhotoUpload.module.css b/src/components/shared/PhotoUpload.module.css index 40d90b5e..7845c5ac 100644 --- a/src/components/shared/PhotoUpload.module.css +++ b/src/components/shared/PhotoUpload.module.css @@ -3,4 +3,14 @@ height: 60px; flex: 0 0 60px; @apply flex items-center justify-center cursor-pointer bg-secondary text-secondary-dark rounded-full; + @apply transition-opacity duration-200 ease-in-out; +} + +.circle:focus { + @apply outline-none; +} + +.circle:hover { + @apply opacity-75; + @apply transition-opacity duration-200 ease-in-out; } diff --git a/src/components/shared/SectionIcon.js b/src/components/shared/SectionIcon.js index b51eb0fc..6c717d3c 100644 --- a/src/components/shared/SectionIcon.js +++ b/src/components/shared/SectionIcon.js @@ -19,9 +19,9 @@ const SectionIcon = ({ section, containerId, tooltipPlacement }) => { duration={500} containerId={containerId} activeClass="text-primary" - className="py-2 cursor-pointer focus:outline-none focus:text-primary hover:text-primary" + className="py-2 cursor-pointer focus:outline-none focus:text-primary hover:text-primary animate__animated animate__fadeIn" > - + ); diff --git a/src/contexts/MetadataContext.js b/src/contexts/MetadataContext.js deleted file mode 100644 index 1d838b29..00000000 --- a/src/contexts/MetadataContext.js +++ /dev/null @@ -1,70 +0,0 @@ -import { clone, setWith } from 'lodash'; -import React, { - createContext, - memo, - useCallback, - useContext, - useReducer, -} from 'react'; -import leftSections from '../data/leftSections'; -import DatabaseContext from './DatabaseContext'; -import { useSelector as useResumeSelector } from './ResumeContext'; - -const initialState = { - template: 'Onyx', - font: 'Montserrat', - layout: [leftSections.map(({ id, name }) => ({ id, name }))], - colors: { - textColor: '#444444', - primaryColor: '#5875DB', - backgroundColor: '#FFFFFF', - }, -}; - -const MetadataContext = createContext({}); - -const MetadataProvider = ({ children }) => { - const id = useResumeSelector((state) => state.id); - const { debouncedUpdateMetadata } = useContext(DatabaseContext); - - const memoizedReducer = useCallback( - (state, { type, payload }) => { - let newState; - - switch (type) { - case 'set_layout': - newState = setWith(clone(state), 'layout', payload, clone); - debouncedUpdateMetadata(id, newState); - return newState; - - default: - throw new Error(); - } - }, - [id, debouncedUpdateMetadata], - ); - - const [state, dispatch] = useReducer(memoizedReducer, initialState); - - const selectValue = (callback) => callback(state); - - return ( - - {children} - - ); -}; - -const useSelector = (callback) => { - const { selectValue } = useContext(MetadataContext); - return selectValue(callback); -}; - -const useDispatch = () => { - const { dispatch } = useContext(MetadataContext); - return dispatch; -}; - -const memoizedProvider = memo(MetadataProvider); - -export { memoizedProvider as MetadataProvider, useSelector, useDispatch }; diff --git a/src/contexts/ResumeContext.js b/src/contexts/ResumeContext.js index fd4e4be4..208d24a9 100644 --- a/src/contexts/ResumeContext.js +++ b/src/contexts/ResumeContext.js @@ -1,5 +1,5 @@ import arrayMove from 'array-move'; -import { clone, findIndex, get, setWith } from 'lodash'; +import { clone, findIndex, get, isUndefined, setWith } from 'lodash'; import React, { createContext, memo, @@ -7,9 +7,22 @@ import React, { useContext, useReducer, } from 'react'; +import leftSections from '../data/leftSections'; import DatabaseContext from './DatabaseContext'; -const initialState = {}; +const initialState = { + name: '', + metadata: { + template: 'onyx', + font: 'Montserrat', + layout: [leftSections.map(({ id, name }) => ({ id, name }))], + colors: { + text: '#444444', + primary: '#5875DB', + background: '#FFFFFF', + }, + }, +}; const ResumeContext = createContext({}); @@ -89,18 +102,22 @@ const ResumeProvider = ({ children }) => { const [state, dispatch] = useReducer(memoizedReducer, initialState); - const selectValue = (callback) => callback(state); - return ( - + {children} ); }; -const useSelector = (callback) => { - const { selectValue } = useContext(ResumeContext); - return selectValue(callback); +const useSelector = (path, fallback) => { + const { state } = useContext(ResumeContext); + let value = get(state, path); + + if (isUndefined(value)) { + value = isUndefined(fallback) ? state : fallback; + } + + return value; }; const useDispatch = () => { diff --git a/src/contexts/StorageContext.js b/src/contexts/StorageContext.js index 628f985e..454c92fc 100644 --- a/src/contexts/StorageContext.js +++ b/src/contexts/StorageContext.js @@ -16,7 +16,7 @@ const StorageProvider = ({ children }) => { const { user } = useContext(UserContext); - const id = useSelector((state) => state.id); + const id = useSelector('id'); const dispatch = useDispatch(); const uploadPhotograph = async (file) => { diff --git a/src/data/rightSections.js b/src/data/rightSections.js index fc266834..aa5ccd53 100644 --- a/src/data/rightSections.js +++ b/src/data/rightSections.js @@ -1,6 +1,11 @@ -import { MdDashboard } from 'react-icons/md'; +import { MdDashboard, MdStyle } from 'react-icons/md'; export default [ + { + id: 'templates', + name: 'Templates', + icon: MdStyle, + }, { id: 'layout', name: 'Layout', diff --git a/src/data/templates.js b/src/data/templates.js new file mode 100644 index 00000000..a70bbb75 --- /dev/null +++ b/src/data/templates.js @@ -0,0 +1,14 @@ +const templates = [ + { + id: 'onyx', + name: 'Onyx', + preview: 'https://source.unsplash.com/random/300x500', + }, + { + id: 'pikachu', + name: 'Pikachu', + preview: 'https://source.unsplash.com/random/301x501', + }, +]; + +export default templates; diff --git a/src/modals/ResumeModal.js b/src/modals/ResumeModal.js index 1ab083cf..7b6ffef8 100644 --- a/src/modals/ResumeModal.js +++ b/src/modals/ResumeModal.js @@ -6,9 +6,20 @@ import ModalEvents from '../constants/ModalEvents'; import DatabaseContext from '../contexts/DatabaseContext'; import { getFieldProps } from '../utils'; import DataModal from './DataModal'; +import leftSections from '../data/leftSections'; const initialValues = { name: '', + metadata: { + template: 'onyx', + font: 'Montserrat', + layout: [leftSections.map(({ id, name }) => ({ id, name }))], + colors: { + text: '#444444', + primary: '#5875DB', + background: '#FFFFFF', + }, + }, }; const schema = Yup.object().shape({ diff --git a/src/pages/app/dashboard.js b/src/pages/app/dashboard.js index 0883b279..6ec5c941 100644 --- a/src/pages/app/dashboard.js +++ b/src/pages/app/dashboard.js @@ -1,5 +1,5 @@ import firebase from 'gatsby-plugin-firebase'; -import React, { memo, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Helmet } from 'react-helmet'; import CreateResume from '../../components/dashboard/CreateResume'; import ResumePreview from '../../components/dashboard/ResumePreview'; @@ -27,6 +27,15 @@ const Dashboard = ({ user }) => { setLoading(false); }); + firebase + .database() + .ref(ref) + .on('child_removed', (snapshot) => { + if (snapshot.val()) { + setResumes(resumes.filter((x) => x.id === snapshot.val().id)); + } + }); + return () => { firebase.database().ref(ref).off(); }; @@ -58,4 +67,4 @@ const Dashboard = ({ user }) => { ); }; -export default memo(Dashboard); +export default Dashboard; diff --git a/src/templates/Onyx.js b/src/templates/Onyx.js index e29f90c2..bfabd829 100644 --- a/src/templates/Onyx.js +++ b/src/templates/Onyx.js @@ -2,13 +2,16 @@ import React, { memo } from 'react'; import { FaGlobeAmericas, FaPhone } from 'react-icons/fa'; import { MdEmail } from 'react-icons/md'; -const Onyx = ({ data, layout, colors }) => { +const Onyx = ({ data }) => { + const { profile, metadata } = data; + const { colors, layout } = metadata; + return (
{ alt="Photograph" />
-

- {data.profile.firstName} {data.profile.lastName} +

+ {profile.firstName} {profile.lastName}

Customer Sales Representative @@ -33,17 +33,17 @@ const Onyx = ({ data, layout, colors }) => {
- + +1 661-808-4188
- + nancyontheweb.com
- + nancyjack43@gmail.com diff --git a/tailwind.config.js b/tailwind.config.js index bf656fa0..08c50214 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,6 +5,9 @@ module.exports = { center: true, }, extend: { + cursor: { + 'context-menu': 'context-menu', + }, colors: { primary: 'var(--color-primary)', 'primary-dark': 'var(--color-primary-dark)',