diff --git a/gatsby-browser.js b/gatsby-browser.js index 828aea0c..e3886aff 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -11,11 +11,10 @@ import { ResumeProvider } from './src/contexts/ResumeContext'; import { StorageProvider } from './src/contexts/StorageContext'; import { ThemeProvider } from './src/contexts/ThemeContext'; import { UserProvider } from './src/contexts/UserContext'; -import './src/styles/colors.css'; -import './src/styles/global.css'; import './src/styles/shadows.css'; import './src/styles/tailwind.css'; import './src/styles/toastify.css'; +import './src/styles/global.css'; const theme = createMuiTheme({ typography: { diff --git a/gatsby-config.js b/gatsby-config.js index 99468e38..ea94f850 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -86,6 +86,14 @@ module.exports = { }, }, 'gatsby-plugin-lodash', + { + resolve: `gatsby-plugin-material-ui`, + options: { + stylesProvider: { + injectFirst: true, + }, + }, + }, 'gatsby-plugin-postcss', { resolve: 'gatsby-source-filesystem', diff --git a/package-lock.json b/package-lock.json index cf1b33d1..d6e5e17a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8715,6 +8715,16 @@ } } }, + "gatsby-plugin-material-ui": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/gatsby-plugin-material-ui/-/gatsby-plugin-material-ui-2.1.9.tgz", + "integrity": "sha512-bvfAEOugCyDgVY9ZNadnC+RP20qA/G2mMDnoj25hRf98fcTtP128s1DshoeYZF61w46Mqp6ACB+uZtrG6iWDfQ==", + "requires": { + "autoprefixer": "^9.6.1", + "clean-css": "^4.2.1", + "postcss": "^7.0.17" + } + }, "gatsby-plugin-offline": { "version": "3.2.17", "resolved": "https://registry.npmjs.org/gatsby-plugin-offline/-/gatsby-plugin-offline-3.2.17.tgz", @@ -10043,6 +10053,53 @@ "resolved": "https://registry.npmjs.org/html-tag-names/-/html-tag-names-1.1.5.tgz", "integrity": "sha512-aI5tKwNTBzOZApHIynaAwecLBv8TlZTEy/P4Sj2SzzAhBrGuI8yGZ0UIXVPQzOHGS+to2mjb04iy6VWt/8+d8A==" }, + "html-to-react": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/html-to-react/-/html-to-react-1.4.3.tgz", + "integrity": "sha512-txe09A3vxW8yEZGJXJ1is5gGDfBEVACmZDSgwDyH5EsfRdOubBwBCg63ZThZP0xBn0UE4FyvMXZXmohusCxDcg==", + "requires": { + "domhandler": "^3.0", + "htmlparser2": "^4.1.0", + "lodash.camelcase": "^4.3.0", + "ramda": "^0.27" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + }, + "domhandler": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz", + "integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==", + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.1.0.tgz", + "integrity": "sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg==", + "requires": { + "dom-serializer": "^0.2.1", + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0" + } + }, + "htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + } + } + }, "html-void-elements": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", @@ -12330,6 +12387,21 @@ "safe-buffer": "^5.1.2" } }, + "mdast-add-list-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz", + "integrity": "sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA==", + "requires": { + "unist-util-visit-parents": "1.1.2" + }, + "dependencies": { + "unist-util-visit-parents": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz", + "integrity": "sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q==" + } + } + }, "mdast-squeeze-paragraphs": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", @@ -15051,6 +15123,11 @@ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz", "integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==" }, + "ramda": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz", + "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -15451,6 +15528,129 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "react-markdown": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-4.3.1.tgz", + "integrity": "sha512-HQlWFTbDxTtNY6bjgp3C3uv1h2xcjCSi1zAEzfBW9OwJJvENSYiLXWNXN5hHLsoqai7RnZiiHzcnWdXk2Splzw==", + "requires": { + "html-to-react": "^1.3.4", + "mdast-add-list-metadata": "1.0.1", + "prop-types": "^15.7.2", + "react-is": "^16.8.6", + "remark-parse": "^5.0.0", + "unified": "^6.1.5", + "unist-util-visit": "^1.3.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "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=" + }, + "parse-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "remark-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", + "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "requires": { + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^1.1.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^1.0.0", + "vfile-location": "^2.0.0", + "xtend": "^4.0.1" + } + }, + "unified": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", + "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^2.0.0", + "x-is-string": "^0.1.0" + } + }, + "unist-util-remove-position": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", + "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "requires": { + "unist-util-is": "^3.0.0" + } + }, + "vfile": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", + "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "requires": { + "is-buffer": "^1.1.4", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "vfile-location": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", + "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==" + }, + "vfile-message": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + } + } + }, "react-reconciler": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.25.1.tgz", @@ -19866,6 +20066,11 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" + }, "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", diff --git a/package.json b/package.json index 553257ee..3fb24e5c 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "gatsby-plugin-firebase": "^0.2.0-beta.4", "gatsby-plugin-lodash": "^3.3.10", "gatsby-plugin-manifest": "^2.4.18", + "gatsby-plugin-material-ui": "^2.1.9", "gatsby-plugin-offline": "^3.2.17", "gatsby-plugin-postcss": "^2.3.11", "gatsby-plugin-prefetch-google-fonts": "^1.4.3", @@ -46,6 +47,7 @@ "react-dom": "^16.13.1", "react-helmet": "^6.1.0", "react-icons": "^3.10.0", + "react-markdown": "^4.3.1", "react-scroll": "^1.7.16", "react-toastify": "^6.0.8", "uuid": "^8.2.0", diff --git a/src/components/builder/left/sections/Objective.js b/src/components/builder/left/sections/Objective.js index f42ae5d5..e931ebc0 100644 --- a/src/components/builder/left/sections/Objective.js +++ b/src/components/builder/left/sections/Objective.js @@ -7,7 +7,7 @@ const Objective = () => {
Objective - +
); }; diff --git a/src/components/builder/right/sections/Actions.js b/src/components/builder/right/sections/Actions.js index 8ed9a5cd..2512c757 100644 --- a/src/components/builder/right/sections/Actions.js +++ b/src/components/builder/right/sections/Actions.js @@ -38,7 +38,7 @@ const Actions = () => {
Import Your Resume
-

+

You can import your information from various sources like JSON Resume or your LinkedIn to autofill most of the data for your resume.

@@ -53,7 +53,7 @@ const Actions = () => {
Export Your Resume
-

+

Export your resume as a PDF to share with recruiters or a JSON that you will be able to import back onto this app on another computer.

@@ -73,7 +73,7 @@ const Actions = () => {
Share Your Resume
-

+

The link below will be accessible publicly if you choose to share it, and viewers would see the latest version of your resume at any time.

@@ -86,7 +86,7 @@ const Actions = () => {
Load Demo Data
-

+

Unclear on what to do with a fresh blank page? Load some demo data to see how a resume should look and you can start editing from there.

@@ -99,7 +99,7 @@ const Actions = () => {
Delete Account
-

+

If you would like to delete your account and erase all your resumes, it’s just one button away. Please be weary as this is an irreversible process. diff --git a/src/components/builder/right/sections/Layout.js b/src/components/builder/right/sections/Layout.js index d9f39d22..20a20a66 100644 --- a/src/components/builder/right/sections/Layout.js +++ b/src/components/builder/right/sections/Layout.js @@ -1,4 +1,4 @@ -import React, { memo } from 'react'; +import React from 'react'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import { useDispatch, useSelector } from '../../../../contexts/ResumeContext'; import { move, reorder } from '../../../../utils'; @@ -37,7 +37,7 @@ const Layout = () => { dispatch({ type: 'on_input', payload: { - path: 'layout', + path: 'metadata.layout', value: newState, }, }); @@ -68,11 +68,7 @@ const Layout = () => { Block {ind + 1} {el.map((item, index) => ( - + {(dragProvided) => (

{ {...dragProvided.draggableProps} {...dragProvided.dragHandleProps} > - {item.name} + {item}
)} @@ -97,4 +93,4 @@ const Layout = () => { ); }; -export default memo(Layout); +export default Layout; diff --git a/src/components/dashboard/ResumePreview.js b/src/components/dashboard/ResumePreview.js index 377aeb2a..691ff6fe 100644 --- a/src/components/dashboard/ResumePreview.js +++ b/src/components/dashboard/ResumePreview.js @@ -1,7 +1,7 @@ import { Menu, MenuItem } from '@material-ui/core'; import { navigate } from 'gatsby'; import moment from 'moment'; -import React, { memo, useContext, useState } from 'react'; +import React, { useContext, useState } from 'react'; import { MdMoreHoriz, MdOpenInNew } from 'react-icons/md'; import { toast } from 'react-toastify'; import DatabaseContext from '../../contexts/DatabaseContext'; @@ -78,4 +78,4 @@ const ResumePreview = ({ resume }) => { ); }; -export default memo(ResumePreview); +export default ResumePreview; diff --git a/src/contexts/DatabaseContext.js b/src/contexts/DatabaseContext.js index 32940f93..dc9c68a9 100644 --- a/src/contexts/DatabaseContext.js +++ b/src/contexts/DatabaseContext.js @@ -8,6 +8,7 @@ import React, { useState, } from 'react'; import UserContext from './UserContext'; +import initialState from '../data/initialState'; const DEBOUNCE_WAIT_TIME = 4000; @@ -46,8 +47,7 @@ const DatabaseProvider = ({ children }) => { return snapshot.val(); }; - const createResume = (resume) => { - const { id } = resume; + const createResume = ({ id, name }) => { const createdAt = firebase.database.ServerValue.TIMESTAMP; let firstName; @@ -61,11 +61,14 @@ const DatabaseProvider = ({ children }) => { .database() .ref(`users/${user.uid}/resumes/${id}`) .set({ + ...initialState, + id, + name, profile: { + ...initialState.profile, firstName: firstName || '', lastName: lastName || '', }, - ...resume, createdAt, updatedAt: createdAt, }); diff --git a/src/contexts/PageContext.js b/src/contexts/PageContext.js new file mode 100644 index 00000000..da70323a --- /dev/null +++ b/src/contexts/PageContext.js @@ -0,0 +1,7 @@ +import { createContext } from 'react'; + +const defaultState = {}; + +const PageContext = createContext(defaultState); + +export default PageContext; diff --git a/src/contexts/ResumeContext.js b/src/contexts/ResumeContext.js index a9669f75..9d599712 100644 --- a/src/contexts/ResumeContext.js +++ b/src/contexts/ResumeContext.js @@ -1,5 +1,14 @@ import arrayMove from 'array-move'; -import { clone, findIndex, get, isUndefined, setWith } from 'lodash'; +import { + clone, + findIndex, + get, + isUndefined, + setWith, + flatten, + concat, + times, +} from 'lodash'; import React, { createContext, memo, @@ -7,22 +16,8 @@ import React, { useContext, useReducer, } from 'react'; -import leftSections from '../data/leftSections'; import DatabaseContext from './DatabaseContext'; - -const initialState = { - name: '', - metadata: { - template: 'onyx', - font: 'Montserrat', - layout: [leftSections.map(({ id, name }) => ({ id, name }))], - colors: { - text: '#444444', - primary: '#5875DB', - background: '#FFFFFF', - }, - }, -}; +import initialState from '../data/initialState'; const ResumeContext = createContext({}); @@ -34,6 +29,8 @@ const ResumeProvider = ({ children }) => { let newState; let index; let items; + let diff; + let temp; switch (type) { case 'on_add_item': @@ -85,6 +82,30 @@ const ResumeProvider = ({ children }) => { debouncedUpdateResume(newState); return newState; + case 'set_block_count': + items = get(state, 'metadata.layout'); + + if (items.length === payload) return state; + + if (payload === 1) { + items = flatten(items); + } + + if (items.length > payload) { + diff = items.length - payload; + temp = items.splice(Math.max(items.length - diff, 1)); + items[0] = concat(items[0], flatten(temp)); + } + + if (items.length < payload) { + diff = payload - items.length; + times(diff, () => items.push([])); + } + + newState = setWith(clone(state), 'metadata.layout', items, clone); + debouncedUpdateResume(newState); + return newState; + case 'on_input': newState = setWith(clone(state), payload.path, payload.value, clone); debouncedUpdateResume(newState); @@ -132,4 +153,9 @@ const useDispatch = () => { const memoizedProvider = memo(ResumeProvider); -export { memoizedProvider as ResumeProvider, useSelector, useDispatch }; +export { + ResumeContext, + memoizedProvider as ResumeProvider, + useSelector, + useDispatch, +}; diff --git a/src/contexts/StorageContext.js b/src/contexts/StorageContext.js index 454c92fc..247fc35f 100644 --- a/src/contexts/StorageContext.js +++ b/src/contexts/StorageContext.js @@ -62,11 +62,9 @@ const StorageProvider = ({ children }) => { render: 'Your photograph was uploaded successfully... and you look great!', progress, - autoClose: 5000, + autoClose: 2000, hideProgressBar: true, }); - - toastId.current = null; }, ); }; diff --git a/src/data/initialState.js b/src/data/initialState.js new file mode 100644 index 00000000..d79f3c64 --- /dev/null +++ b/src/data/initialState.js @@ -0,0 +1,86 @@ +import leftSections from './leftSections'; + +const initialState = { + id: '', + profile: { + heading: 'Profile', + photograph: '', + firstName: '', + lastName: '', + subtitle: '', + address: { + line1: '', + line2: '', + city: '', + pincode: '', + }, + profile: '', + website: '', + email: '', + }, + social: { + heading: 'Social', + visible: true, + items: [], + }, + objective: { + heading: 'Objective', + visible: true, + body: '', + }, + work: { + heading: 'Work Experience', + visible: true, + items: [], + }, + education: { + heading: 'Education', + visible: true, + items: [], + }, + awards: { + heading: 'Awards', + visible: true, + items: [], + }, + certifications: { + heading: 'Certifications', + visible: true, + items: [], + }, + skills: { + heading: 'Skills', + visible: true, + items: [], + }, + hobbies: { + heading: 'Hobbies', + visible: true, + items: [], + }, + languages: { + heading: 'Languages', + visible: true, + items: [], + }, + references: { + heading: 'References', + visible: true, + items: [], + }, + name: '', + metadata: { + template: 'onyx', + font: 'Montserrat', + layout: [leftSections.filter((x) => !x.fixed).map((x) => x.id)], + colors: { + text: '#444444', + primary: '#5875DB', + background: '#FFFFFF', + }, + }, + createdAt: new Date(), + updatedAt: new Date(), +}; + +export default initialState; diff --git a/src/data/leftSections.js b/src/data/leftSections.js index f149adb3..670f2983 100644 --- a/src/data/leftSections.js +++ b/src/data/leftSections.js @@ -14,12 +14,14 @@ export default [ id: 'profile', name: 'Profile', icon: MdPerson, + fixed: true, }, { id: 'social', name: 'Social Network', icon: AiOutlineTwitter, event: ModalEvents.SOCIAL_MODAL, + fixed: true, }, { id: 'objective', diff --git a/src/modals/sections/ReferenceModal.js b/src/modals/sections/ReferenceModal.js index ff54744e..01090dc4 100644 --- a/src/modals/sections/ReferenceModal.js +++ b/src/modals/sections/ReferenceModal.js @@ -9,7 +9,7 @@ import DataModal from '../DataModal'; const initialValues = { name: '', position: '', - contact: '', + phone: '', email: '', summary: '', }; @@ -17,7 +17,7 @@ const initialValues = { 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(), + phone: Yup.string(), email: Yup.string().email('Must be a valid email address.'), summary: Yup.string(), }); @@ -51,7 +51,7 @@ const ReferenceModal = () => { { ); }; -export default Dashboard; +export default memo(Dashboard); diff --git a/src/styles/colors.css b/src/styles/colors.css deleted file mode 100644 index df0cd48d..00000000 --- a/src/styles/colors.css +++ /dev/null @@ -1,12 +0,0 @@ -:root { - --color-primary-50: #FFFFFF; - --color-primary-100: #FAFAFA; - --color-primary-200: #F1F0F0; - --color-primary-300: #D8D2CD; - --color-primary-400: #CDC4BA; - --color-primary-500: #ABA59D; - --color-primary-600: #8A8680; - --color-primary-700: #686663; - --color-primary-800: #484745; - --color-primary-900: #282727; -} \ No newline at end of file diff --git a/src/styles/global.css b/src/styles/global.css index d35d5d97..6c6e8ea2 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -6,14 +6,6 @@ body { @apply transition-colors duration-200 ease-in-out; } -p { - @apply leading-loose; -} - -a { - @apply text-blue-600 font-medium; -} - a:hover { @apply underline; } @@ -22,6 +14,10 @@ hr { @apply w-full border-primary-200 h-1; } +ul { + @apply list-disc list-inside; +} + section { @apply grid grid-cols-1 gap-8; } @@ -35,11 +31,11 @@ label > span:first-child { } .MuiTooltip-tooltip { - font-size: 10px !important; + font-size: 10px; } .MuiMenuItem-root { - justify-content: center !important; + justify-content: center; } .spin { diff --git a/src/templates/Onyx.js b/src/templates/Onyx.js index 3301796d..2e62be70 100644 --- a/src/templates/Onyx.js +++ b/src/templates/Onyx.js @@ -1,60 +1,107 @@ -import React, { memo } from 'react'; -import { FaGlobeAmericas, FaPhone } from 'react-icons/fa'; -import { MdEmail } from 'react-icons/md'; +import React, { memo, useEffect } from 'react'; +import PageContext from '../contexts/PageContext'; +import { useDispatch } from '../contexts/ResumeContext'; +import AwardsA from './blocks/Awards/AwardsA'; +import CertificationsA from './blocks/Certifications/CertificationsA'; +import Contact from './blocks/Contact/ContactA'; +import EducationA from './blocks/Education/EducationA'; +import Heading from './blocks/Heading/HeadingA'; +import HobbiesA from './blocks/Hobbies/HobbiesA'; +import LanguagesA from './blocks/Languages/LanguagesA'; +import ObjectiveA from './blocks/Objective/ObjectiveA'; +import ReferencesA from './blocks/References/ReferencesA'; +import SkillsA from './blocks/Skills/SkillsA'; +import WorkA from './blocks/Work/WorkA'; + +const Blocks = { + objective: ObjectiveA, + work: WorkA, + education: EducationA, + awards: AwardsA, + certifications: CertificationsA, + skills: SkillsA, + hobbies: HobbiesA, + languages: LanguagesA, + references: ReferencesA, +}; const Onyx = ({ data }) => { - const { profile, metadata } = data; - const { font, colors, layout } = metadata; + const dispatch = useDispatch(); + + useEffect(() => { + dispatch({ type: 'set_block_count', payload: 3 }); + }, []); return ( -
- Photograph -
-

- {profile.firstName} {profile.lastName} -

- Customer Sales Representative + +
+
+
+ Resume Photograph -
- 3879 Gateway Avenue, - Bakersfield, - California, USA +
+

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

+
{data.profile.subtitle}
+ +
+ {data.profile.address.line1} + {data.profile.address.line2} + + {data.profile.address.city} {data.profile.address.pincode} + +
+
+
+ + +
+ +
+ +
+ {data.metadata.layout[0] && + data.metadata.layout[0].map((x) => { + const Component = Blocks[x]; + return Component && ; + })} + +
+ {data.metadata.layout[1] && + data.metadata.layout[1].map((x) => { + const Component = Blocks[x]; + return Component && ; + })} +
+ + {data.metadata.layout[2] && + data.metadata.layout[2].map((x) => { + const Component = Blocks[x]; + return Component && ; + })}
-
-
- - +1 661-808-4188 -
- -
- - nancyontheweb.com -
- -
- - - nancyjack43@gmail.com - -
-
- -
- - {JSON.stringify(layout)} -
+
); }; diff --git a/src/templates/blocks/Awards/AwardsA.js b/src/templates/blocks/Awards/AwardsA.js new file mode 100644 index 00000000..ac89d026 --- /dev/null +++ b/src/templates/blocks/Awards/AwardsA.js @@ -0,0 +1,33 @@ +import moment from 'moment'; +import React, { memo, useContext } from 'react'; +import ReactMarkdown from 'react-markdown'; +import PageContext from '../../../contexts/PageContext'; + +const AwardItem = (x) => ( +
+
+
+
{x.title}
+

{x.awarder}

+
+ +
+ {moment(x.date).format('MMMM YYYY')} +
+
+ +
+); + +const AwardsA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.awards.visible && data.awards.items ? ( +
+ {data.awards.heading} + {data.awards.items.map(AwardItem)} +
+ ) : null; +}; + +export default memo(AwardsA); diff --git a/src/templates/blocks/Certifications/CertificationsA.js b/src/templates/blocks/Certifications/CertificationsA.js new file mode 100644 index 00000000..0ea07251 --- /dev/null +++ b/src/templates/blocks/Certifications/CertificationsA.js @@ -0,0 +1,33 @@ +import moment from 'moment'; +import React, { memo, useContext } from 'react'; +import ReactMarkdown from 'react-markdown'; +import PageContext from '../../../contexts/PageContext'; + +const CertificationItem = (x) => ( +
+
+
+
{x.title}
+

{x.issuer}

+
+ +
+ {moment(x.date).format('MMMM YYYY')} +
+
+ +
+); + +const CertificationsA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.certifications.visible && data.certifications.items ? ( +
+ {data.certifications.heading} + {data.certifications.items.map(CertificationItem)} +
+ ) : null; +}; + +export default memo(CertificationsA); diff --git a/src/templates/blocks/Contact/ContactA.js b/src/templates/blocks/Contact/ContactA.js new file mode 100644 index 00000000..a86eb67a --- /dev/null +++ b/src/templates/blocks/Contact/ContactA.js @@ -0,0 +1,66 @@ +import { get } from 'lodash'; +import React, { memo, useContext } from 'react'; +import { FaCaretRight } from 'react-icons/fa'; +import PageContext from '../../../contexts/PageContext'; +import Icons from '../Icons'; + +const ContactItem = ({ value, icon, link }) => { + const { data } = useContext(PageContext); + const Icon = get(Icons, icon.toLowerCase(), FaCaretRight); + + return value ? ( +
+ + {link ? ( + + {value} + + ) : ( + {value} + )} +
+ ) : null; +}; + +const ContactA = () => { + const { data } = useContext(PageContext); + + return ( +
+ + + + + {data.social.visible && data.social.items ? ( +
+ {data.social.items.map((x) => ( + + ))} +
+ ) : null} +
+ ); +}; + +export default memo(ContactA); diff --git a/src/templates/blocks/Education/EducationA.js b/src/templates/blocks/Education/EducationA.js new file mode 100644 index 00000000..0a087ae3 --- /dev/null +++ b/src/templates/blocks/Education/EducationA.js @@ -0,0 +1,42 @@ +import React, { memo, useContext } from 'react'; +import PageContext from '../../../contexts/PageContext'; +import { formatDateRange } from '../../../utils'; + +const EducationItem = (x) => ( +
+
+
+
{x.institution}
+ + {x.degree} {x.field} + +
+
+ + ({formatDateRange({ startDate: x.startDate, endDate: x.endDate })}) + +
{x.gpa}
+
+
+ {x.courses && ( +
    + {x.courses.map((y) => ( +
  • {y}
  • + ))} +
+ )} +
+); + +const EducationA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.education.visible && data.education.items ? ( +
+ {data.education.heading} + {data.education.items.map(EducationItem)} +
+ ) : null; +}; + +export default memo(EducationA); diff --git a/src/templates/blocks/Heading/HeadingA.js b/src/templates/blocks/Heading/HeadingA.js new file mode 100644 index 00000000..6e1f58ce --- /dev/null +++ b/src/templates/blocks/Heading/HeadingA.js @@ -0,0 +1,17 @@ +import React, { useContext, memo } from 'react'; +import PageContext from '../../../contexts/PageContext'; + +const HeadingA = ({ children }) => { + const { data } = useContext(PageContext); + + return ( +
+ {children} +
+ ); +}; + +export default memo(HeadingA); diff --git a/src/templates/blocks/Hobbies/HobbiesA.js b/src/templates/blocks/Hobbies/HobbiesA.js new file mode 100644 index 00000000..7c64e139 --- /dev/null +++ b/src/templates/blocks/Hobbies/HobbiesA.js @@ -0,0 +1,21 @@ +import React, { memo, useContext } from 'react'; +import PageContext from '../../../contexts/PageContext'; + +const HobbyA = (x) => ( +
+
{x.name}
+
+); + +const HobbiesA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.hobbies.visible && data.hobbies.items ? ( +
+ {data.hobbies.heading} + {data.hobbies.items.map(HobbyA)} +
+ ) : null; +}; + +export default memo(HobbiesA); diff --git a/src/templates/blocks/Icons.js b/src/templates/blocks/Icons.js new file mode 100644 index 00000000..4832c0f9 --- /dev/null +++ b/src/templates/blocks/Icons.js @@ -0,0 +1,24 @@ +import { + FaGlobeAmericas, + FaFacebookF, + FaTwitter, + FaLinkedinIn, + FaGithub, + FaDribbble, + FaInstagram, +} from 'react-icons/fa'; +import { MdPhone, MdEmail } from 'react-icons/md'; + +const Icons = { + phone: MdPhone, + website: FaGlobeAmericas, + email: MdEmail, + facebook: FaFacebookF, + twitter: FaTwitter, + linkedin: FaLinkedinIn, + github: FaGithub, + dribbble: FaDribbble, + instagram: FaInstagram, +}; + +export default Icons; diff --git a/src/templates/blocks/Languages/LanguagesA.js b/src/templates/blocks/Languages/LanguagesA.js new file mode 100644 index 00000000..809686eb --- /dev/null +++ b/src/templates/blocks/Languages/LanguagesA.js @@ -0,0 +1,22 @@ +import React, { memo, useContext } from 'react'; +import PageContext from '../../../contexts/PageContext'; + +const LanguageItem = (x) => ( +
+
{x.name}
+

{x.fluency}

+
+); + +const LanguagesA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.languages.visible && data.languages.items ? ( +
+ {data.languages.heading} + {data.languages.items.map(LanguageItem)} +
+ ) : null; +}; + +export default memo(LanguagesA); diff --git a/src/templates/blocks/Objective/ObjectiveA.js b/src/templates/blocks/Objective/ObjectiveA.js new file mode 100644 index 00000000..1d0f6491 --- /dev/null +++ b/src/templates/blocks/Objective/ObjectiveA.js @@ -0,0 +1,16 @@ +import React, { useContext, memo } from 'react'; +import ReactMarkdown from 'react-markdown'; +import PageContext from '../../../contexts/PageContext'; + +const ObjectiveA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return ( +
+ {data.objective.heading} + +
+ ); +}; + +export default memo(ObjectiveA); diff --git a/src/templates/blocks/References/ReferencesA.js b/src/templates/blocks/References/ReferencesA.js new file mode 100644 index 00000000..37f0017d --- /dev/null +++ b/src/templates/blocks/References/ReferencesA.js @@ -0,0 +1,28 @@ +import React, { memo, useContext } from 'react'; +import ReactMarkdown from 'react-markdown'; +import PageContext from '../../../contexts/PageContext'; + +const ReferenceItem = (x) => ( +
+
{x.name}
+

{x.position}

+

{x.phone}

+

{x.email}

+ +
+); + +const ReferencesA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.references.visible && data.references.items ? ( +
+ {data.references.heading} +
+ {data.references.items.map(ReferenceItem)} +
+
+ ) : null; +}; + +export default memo(ReferencesA); diff --git a/src/templates/blocks/Skills/SkillsA.js b/src/templates/blocks/Skills/SkillsA.js new file mode 100644 index 00000000..992b1315 --- /dev/null +++ b/src/templates/blocks/Skills/SkillsA.js @@ -0,0 +1,22 @@ +import React, { memo, useContext } from 'react'; +import PageContext from '../../../contexts/PageContext'; + +const SkillItem = (x) => ( +
+
{x.name}
+

{x.level}

+
+); + +const SkillsA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.skills.visible && data.skills.items ? ( +
+ {data.skills.heading} + {data.skills.items.map(SkillItem)} +
+ ) : null; +}; + +export default memo(SkillsA); diff --git a/src/templates/blocks/Work/WorkA.js b/src/templates/blocks/Work/WorkA.js new file mode 100644 index 00000000..b9ea1c46 --- /dev/null +++ b/src/templates/blocks/Work/WorkA.js @@ -0,0 +1,39 @@ +import React, { useContext, memo } from 'react'; +import ReactMarkdown from 'react-markdown'; +import PageContext from '../../../contexts/PageContext'; +import { formatDateRange } from '../../../utils'; + +const WorkItem = (x) => ( +
+
+
+
{x.company}
+ {x.position} +
+ + ({formatDateRange({ startDate: x.startDate, endDate: x.endDate })}) + +
+ + {x.highlights && ( +
    + {x.highlights.map((y) => ( +
  • {y}
  • + ))} +
+ )} +
+); + +const WorkA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return data.work.visible && data.work.items ? ( +
+ {data.work.heading} +
{data.work.items.map(WorkItem)}
+
+ ) : null; +}; + +export default memo(WorkA); diff --git a/src/utils/index.js b/src/utils/index.js index 46bc4015..9e0f486b 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -20,9 +20,9 @@ export const isFileImage = (file) => { 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 formatDateRange = ({ startDate, endDate }) => + `${moment(startDate).format('MMMM Y')} — ${ + moment(endDate).isValid() ? moment(endDate).format('MMMM Y') : 'Present' }`; export const getFieldProps = (formik, schema, name) => ({