diff --git a/src/components/builder/center/Artboard.js b/src/components/builder/center/Artboard.js index 3480dd2b..20e7c576 100644 --- a/src/components/builder/center/Artboard.js +++ b/src/components/builder/center/Artboard.js @@ -2,7 +2,9 @@ import React, { memo } from 'react'; import { Helmet } from 'react-helmet'; import { useSelector } from '../../../contexts/ResumeContext'; import Onyx from '../../../templates/Onyx'; +import Pikachu from '../../../templates/Pikachu'; import styles from './Artboard.module.css'; +import Gengar from '../../../templates/Gengar'; const Artboard = () => { const state = useSelector(); @@ -18,6 +20,8 @@ const Artboard = () => {
{template === 'onyx' && } + {template === 'pikachu' && } + {template === 'gengar' && }
); diff --git a/src/components/builder/right/sections/Layout.js b/src/components/builder/right/sections/Layout.js index f92264bc..d2873528 100644 --- a/src/components/builder/right/sections/Layout.js +++ b/src/components/builder/right/sections/Layout.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { memo, useState } from 'react'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import { useDispatch, useSelector } from '../../../../contexts/ResumeContext'; import { move, reorder } from '../../../../utils'; @@ -9,7 +9,8 @@ import styles from './Layout.module.css'; const Layout = () => { const [resetLayoutText, setResetLayoutText] = useState('Reset Layout'); - const blocks = useSelector('metadata.layout'); + const template = useSelector('metadata.template'); + const blocks = useSelector(`metadata.layout.${template}`, [[]]); const dispatch = useDispatch(); const onDragEnd = (result) => { @@ -28,7 +29,7 @@ const Layout = () => { dispatch({ type: 'on_input', payload: { - path: 'metadata.layout', + path: `metadata.layout.${template}`, value: newState, }, }); @@ -40,7 +41,7 @@ const Layout = () => { dispatch({ type: 'on_input', payload: { - path: 'metadata.layout', + path: `metadata.layout.${template}`, value: newState, }, }); @@ -110,4 +111,4 @@ const Layout = () => { ); }; -export default Layout; +export default memo(Layout); diff --git a/src/contexts/DatabaseContext.js b/src/contexts/DatabaseContext.js index 862df98e..fbb8aefe 100644 --- a/src/contexts/DatabaseContext.js +++ b/src/contexts/DatabaseContext.js @@ -2,8 +2,8 @@ import firebase from 'gatsby-plugin-firebase'; import { debounce } from 'lodash'; import ShortUniqueId from 'short-unique-id'; import React, { createContext, memo, useContext, useState } from 'react'; -import initialState from '../data/initialState'; import UserContext from './UserContext'; +import initialState from '../data/initialState.json'; const DEBOUNCE_WAIT_TIME = 4000; @@ -68,6 +68,8 @@ const DatabaseProvider = ({ children }) => { }; const duplicateResume = (originalResume) => { + console.trace(); + const id = uuid(); const createdAt = firebase.database.ServerValue.TIMESTAMP; diff --git a/src/contexts/ResumeContext.js b/src/contexts/ResumeContext.js index b22632b8..da34aea0 100644 --- a/src/contexts/ResumeContext.js +++ b/src/contexts/ResumeContext.js @@ -1,15 +1,5 @@ import arrayMove from 'array-move'; -import { - clone, - concat, - findIndex, - flatten, - get, - isUndefined, - merge, - setWith, - times, -} from 'lodash'; +import { clone, findIndex, get, isUndefined, merge, setWith } from 'lodash'; import React, { createContext, memo, @@ -18,9 +8,8 @@ import React, { useReducer, } from 'react'; import demoState from '../data/demoState.json'; -import initialState from '../data/initialState'; +import initialState from '../data/initialState.json'; import DatabaseContext from './DatabaseContext'; -import leftSections from '../data/leftSections'; const ResumeContext = createContext({}); @@ -32,7 +21,6 @@ const ResumeProvider = ({ children }) => { let newState; let index; let items; - let diff; let temp; switch (type) { @@ -85,46 +73,15 @@ 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 'set_fixed_sections': - items = get(state, 'metadata.layout'); - - items = items.map((x) => { - return x.filter((y) => { - return !payload.includes(y); - }); - }); - - newState = setWith(clone(state), 'metadata.layout', items, clone); - debouncedUpdateResume(newState); - return newState; - case 'reset_layout': - items = [leftSections.filter((x) => !x.fixed).map((x) => x.id)]; - newState = setWith(clone(state), 'metadata.layout', items, clone); + temp = get(state, 'metadata.template'); + items = get(initialState, `metadata.layout.${temp}`); + newState = setWith( + clone(state), + `metadata.layout.${temp}`, + items, + clone, + ); debouncedUpdateResume(newState); return newState; @@ -144,7 +101,13 @@ const ResumeProvider = ({ children }) => { return newState; case 'reset_data': - newState = merge(clone(state), initialState); + temp = clone(state); + newState = initialState; + newState.id = temp.id; + newState.user = temp.user; + newState.name = temp.name; + newState.createdAt = temp.createdAt; + newState.updatedAt = temp.updatedAt; debouncedUpdateResume(newState); return newState; diff --git a/src/data/demoState.json b/src/data/demoState.json index a3670114..bc6a4e0e 100644 --- a/src/data/demoState.json +++ b/src/data/demoState.json @@ -7,14 +7,21 @@ "date": "2019-04-01", "id": "6f857f2b-6312-4a0d-907d-2e17991954eb", "summary": "", - "title": "International Flutter Hackathon '19" + "title": "International Flutter Hackathon" }, { "awarder": "Venturesity", "date": "2016-06-01", "id": "f6efa3f9-9741-4e36-a538-ba0d9779bc61", "summary": "", - "title": "Venturesity Banyan Hack '16" + "title": "Venturesity Banyan Hack" + }, + { + "title": "Smart India Hackathon", + "awarder": "Govt. of India", + "date": "2017-04-01", + "summary": "", + "id": "89c0171a-eae9-403e-9f4c-a757fb535c2b" } ], "visible": true @@ -25,16 +32,23 @@ { "date": "2018-02-01", "id": "d2ec12bc-7876-46bc-afd4-11ae06faf3bd", - "issuer": "Google", + "issuer": "Cisco Systems", "summary": "", - "title": "Applied CS with Android" + "title": "CCNP" }, { "date": "2019-06-01", "id": "f8312288-53ae-4504-a768-4b67aea95926", - "issuer": "Udemy", + "issuer": "VMWare", "summary": "", - "title": "Data Science & Machine Learning using Python" + "title": "VCP6-DCV" + }, + { + "title": "DCUCI 642-999", + "issuer": "Cisco Systems", + "date": "2014-04-01", + "summary": "", + "id": "11107df6-5f3c-49ae-bcd4-62b8baa181a1" } ], "visible": true @@ -43,23 +57,23 @@ "heading": "Education", "items": [ { - "degree": "Bachelor's Degree", - "endDate": "2018-04-01", - "field": "Computer Science & Engineering", - "gpa": "9.2", + "degree": "Masters", + "endDate": "2002-08-01", + "field": "Computer Science", + "gpa": "7.2 CGPA", "id": "c42e2a5a-3f0d-497e-838b-ac2019dcf045", - "institution": "Dayananda Sagar College of Engineering", - "startDate": "2015-04-01", + "institution": "The City College of New York, NYC, NY", + "startDate": "2001-09-01", "summary": "" }, { - "degree": "Diploma", - "endDate": "2015-04-01", + "degree": "Bachelors", + "endDate": "2001-08-01", "field": "Computer Science", - "gpa": "9.8", + "gpa": "8.4 CGPA", "id": "278490a2-c327-4e83-8be8-adf913a9b36c", - "institution": "Dayananda Sagar Institute of Technology", - "startDate": "2012-04-01", + "institution": "University of California, Berkeley, CA", + "startDate": "1997-09-01", "summary": "" } ], @@ -68,6 +82,8 @@ "hobbies": { "heading": "Hobbies", "items": [ + { "name": "Poetry", "id": "788dcf5a-78ca-4866-8397-c7a29073d9a1" }, + { "name": "Travelling", "id": "e3523371-f50c-4348-8c5e-35fe84c0006d" }, { "id": "92c35e3b-6cd7-4cea-b505-61347ec61b68", "name": "Photography" }, { "id": "d36f2089-93a9-4f30-a425-3dd81c6b89df", @@ -75,7 +91,7 @@ }, { "id": "d1da41a9-ae83-48fb-8047-d45ebd869a69", - "name": "Working on Personal Projects" + "name": "Developing Reactive Resume" } ], "visible": true @@ -113,15 +129,26 @@ "text": "#212121" }, "font": "Open Sans", - "layout": [ - ["objective", "work", "education", "projects"], - ["hobbies", "languages"], - ["skills", "certifications", "awards", "references"] - ], - "template": "onyx" + "layout": { + "onyx": [ + ["objective", "work", "education", "projects"], + ["hobbies", "languages", "awards", "certifications"], + ["skills", "references"] + ], + "pikachu": [ + ["skills", "languages", "hobbies", "awards", "certifications"], + ["work", "education", "projects", "references"] + ], + "gengar": [ + ["objective", "skills"], + ["awards", "certifications", "languages", "references", "hobbies"], + ["work", "education", "projects"] + ] + }, + "template": "pikachu" }, "objective": { - "body": "I'm Amruth Pillai, and as you might have already read, I'm a designer, developer, photographer and a writer. This website was made to showcase all of what I can do and plan to do. Don't judge my writing based on this section though, this is by far my shoddiest work yet.\n \nI got into design because I consider myself a pseudo-perfectionist, if that's even a word? As in, I hate to see things 'not look good'. So I set out on a journey to make products that people use that 'look great', and I'm forever on that path.", + "body": "To obtain a job within my chosen field that will challenge me and allow me to use my education, skills and past experiences in a way that is mutually beneficial to both myself and my employer and allow for future growth and advancement.", "heading": "Objective", "visible": true }, @@ -137,7 +164,7 @@ "heading": "Profile", "lastName": "Pillai", "phone": "+91 98453 36113", - "photograph": "https://firebasestorage.googleapis.com/v0/b/rx-resume.appspot.com/o/users%2FNriQrOfocnfTtoRIpR3qEtHNxYq1%2Fphotographs%2Fx7vvg8?alt=media&token=99df9c05-f5e1-4360-b1e8-5c13ddd8cd84", + "photograph": "https://i.imgur.com/2dmLSCT.jpg", "profile": "", "subtitle": "Full Stack Web Developer", "website": "amruthpillai.com" @@ -181,6 +208,14 @@ "phone": "+91 93893 34353", "position": "CEO at Newton Motors", "summary": "" + }, + { + "name": "Lorraine Beasley", + "position": "Head of HR, Carson Logistics", + "phone": "+1 661-808-4188", + "email": "l.beasley@carsonlogistics.com", + "summary": "", + "id": "94e3447b-0a78-4fb7-b14d-591982d35320" } ], "visible": true @@ -191,22 +226,32 @@ { "id": "54e5bceb-d0e9-4f04-98d1-48a34f7cf920", "level": "Advanced", - "name": "ReactJS" + "name": "Customer Service Expertise" }, { "id": "f0274f62-2252-4cc0-bf12-9e1070942c50", - "level": "Advanced", - "name": "Angular" + "level": "Intermediate", + "name": "High-Volume Call Center" }, { "id": "689e2852-df1b-4d41-bda8-c41c88196264", - "level": "Advanced", - "name": "Flutter" + "level": "Intermediate", + "name": "Team Leader/Problem Solver" }, { "id": "3a4f73b1-50c1-4a85-a4b0-2a55dfe5053a", "level": "Novice", - "name": "Machine Learning" + "name": "Call Center Management" + }, + { + "name": "Teambuilding & Training", + "level": "Novice", + "id": "08d6c739-1465-41f7-8825-b8d94faa38d6" + }, + { + "name": "Continuous Improvement", + "level": "Fundamental Awareness", + "id": "261b8fc3-aeec-4347-88a8-bcacb1a17aa3" } ], "visible": true @@ -214,6 +259,12 @@ "social": { "heading": "Social", "items": [ + { + "url": "https://pillai.xyz/instagram", + "network": "Instagram", + "username": "AmruthPillai", + "id": "a832b37d-f11d-4a80-8b4d-24796e571b17" + }, { "id": "a72107fa-a4a5-407d-9e85-39bdb9c0b11a", "network": "Twitter", @@ -233,22 +284,31 @@ "heading": "Work Experience", "items": [ { - "company": "Postdot Technologies Pvt. Ltd.", - "endDate": "", + "company": "On Point Electronics, NYC, NY", + "endDate": "2018-07-01", "id": "d7c64937-0cb9-41b1-a3a6-0679c882fe63", - "position": "Full Stack Web Developer", - "startDate": "2020-06-08", - "summary": "Postman is a great tool when trying to dissect RESTful APIs made by others or test ones you have made yourself. It offers a sleek user interface with which to make HTML requests, without the hassle of writing a bunch of code just to test an API's functionality.", - "website": "https://postman.com" + "position": "Customer Service Representative", + "startDate": "2013-01-01", + "summary": "- Organized customer information and account data for business planning and customer service purposes.\n- Created excel spreadsheets to track customer data and perform intense reconciliation process.\n- Received 97% positive customer survey results.\n- Speed on calls was 10% above team average. \n**Key Achievement:** Designed and executed an automatized system for following up with customers, increasing customer retention by 22%.", + "website": "https://onpoint.com" }, { - "company": "GoDhiyo Solutions Pvt. Ltd.", - "endDate": "2020-04-01", + "company": "Excelsior Communications, NYC, NY", + "endDate": "2012-12-01", "id": "f5c5dcfe-2a60-4169-a2f1-b305355518ea", - "position": "Full Stack Web Developer", - "startDate": "2018-07-01", - "summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi laoreet volutpat lacus, sed tempor lacus eleifend feugiat. Pellentesque molestie libero ac varius finibus. Fusce convallis, arcu sit amet lacinia vehicula, nisl justo egestas tortor.\n\n- In vestibulum eros a enim rhoncus\n- Phasellus ullamcorper magna quis est sagittis", - "website": "https://dhiyo.ai" + "position": "Customer Service Representative", + "startDate": "2009-10-01", + "summary": "- Worked as a full time customer service rep in a high volume call center.\n- Received \"Associate of the Month\" award six times.\n- Chosen as an example for other associates in trainings. \n**Key Achievement:** Received Customer Appreciation bonus in three of four years.", + "website": "https://excelsior.com" + }, + { + "company": "Pizza Hut, Newark, NJ", + "position": "Waiter", + "website": "https://pizzahut.com", + "startDate": "2005-08-01", + "endDate": "2009-09-01", + "summary": "- Worked passionately in customer service in a high volume restaurant.\n- Completed the FAST customer service training class.\n- Maintained a high tip average thanks to consistent customer satisfaction.", + "id": "dd935088-6fe7-4a4b-8ff5-7417c32d2add" } ], "visible": true diff --git a/src/data/initialState.js b/src/data/initialState.js deleted file mode 100644 index 618bf339..00000000 --- a/src/data/initialState.js +++ /dev/null @@ -1,89 +0,0 @@ -import leftSections from './leftSections'; - -const initialState = { - 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: [], - }, - projects: { - heading: 'Projects', - 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: [], - }, - metadata: { - template: 'onyx', - font: 'Montserrat', - layout: [leftSections.filter((x) => !x.fixed).map((x) => x.id)], - colors: { - text: '#444444', - primary: '#5875DB', - background: '#FFFFFF', - }, - }, - public: true, - updatedAt: new Date(), -}; - -export default initialState; diff --git a/src/data/initialState.json b/src/data/initialState.json new file mode 100644 index 00000000..8d6a8ee4 --- /dev/null +++ b/src/data/initialState.json @@ -0,0 +1,99 @@ +{ + "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": [] + }, + "projects": { + "heading": "Projects", + "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": [] + }, + "metadata": { + "template": "onyx", + "font": "Montserrat", + "layout": { + "onyx": [ + ["objective", "work", "education", "projects"], + ["hobbies", "languages", "awards", "certifications"], + ["skills", "references"] + ], + "pikachu": [ + ["skills", "languages", "hobbies", "awards", "certifications"], + ["work", "education", "projects", "references"] + ], + "gengar": [ + ["objective", "skills"], + ["awards", "certifications", "languages", "references", "hobbies"], + ["work", "education", "projects"] + ] + }, + "colors": { + "text": "#444444", + "primary": "#5875DB", + "background": "#FFFFFF" + } + }, + "public": true +} diff --git a/src/data/templateOptions.js b/src/data/templateOptions.js index 8bcda2f7..bc2d0c02 100644 --- a/src/data/templateOptions.js +++ b/src/data/templateOptions.js @@ -9,6 +9,11 @@ const templateOptions = [ name: 'Pikachu', preview: 'https://source.unsplash.com/random/301x501', }, + { + id: 'gengar', + name: 'Gengar', + preview: 'https://source.unsplash.com/random/302x502', + }, ]; export default templateOptions; diff --git a/src/modals/sections/ExportModal.js b/src/modals/sections/ExportModal.js index 558e1d03..42b5a93b 100644 --- a/src/modals/sections/ExportModal.js +++ b/src/modals/sections/ExportModal.js @@ -10,6 +10,7 @@ const ExportModal = () => { const state = useSelector(); const [open, setOpen] = useState(false); const [isLoadingSingle, setLoadingSingle] = useState(false); + const [isLoadingMulti, setLoadingMulti] = useState(false); const functionsUrl = 'http://localhost:5001/rx-resume/us-central1'; const { emitter, events } = useContext(ModalContext); @@ -26,19 +27,30 @@ const ExportModal = () => { } }; + const openFile = (blob) => { + if (typeof window !== `undefined`) { + const url = window.URL.createObjectURL(blob, { oneTimeOnly: true }); + window && window.open(url); + setLoadingSingle(false); + } + }; + const handleSinglePageDownload = async () => { setLoadingSingle(true); fetch(`${functionsUrl}/printSinglePageResume?id=${state.id}`, { method: 'POST', }) .then((response) => response.blob()) - .then((data) => { - if (typeof window !== `undefined`) { - const url = window.URL.createObjectURL(data, { oneTimeOnly: true }); - window && window.open(url); - setLoadingSingle(false); - } - }); + .then(openFile); + }; + + const handleMultiPageDownload = async () => { + setLoadingMulti(true); + fetch(`${functionsUrl}/printMultiPageResume?id=${state.id}`, { + method: 'POST', + }) + .then((response) => response.blob()) + .then(openFile); }; const handleExportToJson = () => { @@ -99,7 +111,13 @@ const ExportModal = () => { > Single Page Resume - + diff --git a/src/pages/r/view.js b/src/pages/r/view.js index 59e4f0cc..81c88f24 100644 --- a/src/pages/r/view.js +++ b/src/pages/r/view.js @@ -6,6 +6,8 @@ import LoadingScreen from '../../components/router/LoadingScreen'; import DatabaseContext from '../../contexts/DatabaseContext'; import Onyx from '../../templates/Onyx'; import styles from './view.module.css'; +import Pikachu from '../../templates/Pikachu'; +import Gengar from '../../templates/Gengar'; const ResumeViewer = ({ id }) => { const [resume, setResume] = useState(null); @@ -46,6 +48,8 @@ const ResumeViewer = ({ id }) => { style={{ backgroundColor: resume.metadata.colors.background }} > {resume.metadata.template === 'onyx' && } + {resume.metadata.template === 'pikachu' && } + {resume.metadata.template === 'gengar' && }

diff --git a/src/templates/Gengar.js b/src/templates/Gengar.js new file mode 100644 index 00000000..07a65294 --- /dev/null +++ b/src/templates/Gengar.js @@ -0,0 +1,131 @@ +import React from 'react'; +import PageContext from '../contexts/PageContext'; +import { hexToRgb } from '../utils'; +import AwardsA from './blocks/Awards/AwardsA'; +import CertificationsA from './blocks/Certifications/CertificationsA'; +import ContactB from './blocks/Contact/ContactB'; +import EducationA from './blocks/Education/EducationA'; +import HeadingC from './blocks/Heading/HeadingC'; +import HobbiesA from './blocks/Hobbies/HobbiesA'; +import LanguagesA from './blocks/Languages/LanguagesA'; +import ObjectiveA from './blocks/Objective/ObjectiveA'; +import ProjectsA from './blocks/Projects/ProjectsA'; +import ReferencesB from './blocks/References/ReferencesB'; +import SkillsA from './blocks/Skills/SkillsA'; +import WorkA from './blocks/Work/WorkA'; + +const Blocks = { + objective: ObjectiveA, + work: WorkA, + education: EducationA, + projects: ProjectsA, + awards: AwardsA, + certifications: CertificationsA, + skills: SkillsA, + hobbies: HobbiesA, + languages: LanguagesA, + references: ReferencesB, +}; + +const Gengar = ({ data }) => { + const layout = data.metadata.layout.gengar; + const { r, g, b } = hexToRgb(data.metadata.colors.primary) || {}; + + const Photo = () => + data.profile.photograph !== '' && ( + {data.profile.firstName} + ); + + const Profile = () => ( +

+

+ {data.profile.firstName} +

+

+ {data.profile.lastName} +

+
{data.profile.subtitle}
+
+ ); + + return ( + +
+
+
+
+ + +
+ +
+ + +
+ +
+
+ {layout[0] && + layout[0].map((x) => { + const Component = Blocks[x]; + return Component && ; + })} +
+
+ +
+
+ {layout[1] && + layout[1].map((x) => { + const Component = Blocks[x]; + return Component && ; + })} +
+
+ +
+
+ {layout[2] && + layout[2].map((x) => { + const Component = Blocks[x]; + return Component && ; + })} +
+
+
+
+
+ ); +}; + +export default Gengar; diff --git a/src/templates/Onyx.js b/src/templates/Onyx.js index 95ad9a4d..450b1556 100644 --- a/src/templates/Onyx.js +++ b/src/templates/Onyx.js @@ -1,11 +1,10 @@ -import React, { memo, useEffect } from 'react'; +import React, { memo } 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 HeadingA from './blocks/Heading/HeadingA'; import HobbiesA from './blocks/Hobbies/HobbiesA'; import LanguagesA from './blocks/Languages/LanguagesA'; import ObjectiveA from './blocks/Objective/ObjectiveA'; @@ -28,18 +27,10 @@ const Blocks = { }; const Onyx = ({ data }) => { - const dispatch = useDispatch(); - - useEffect(() => { - dispatch({ - type: 'set_fixed_sections', - payload: ['profile', 'social'], - }); - dispatch({ type: 'set_block_count', payload: 3 }); - }, []); + const layout = data.metadata.layout.onyx; return ( - +
{ />
- {data.metadata.layout[0] && - data.metadata.layout[0].map((x) => { + {layout[0] && + layout[0].map((x) => { const Component = Blocks[x]; return Component && ; })}
- {data.metadata.layout[1] && - data.metadata.layout[1].map((x) => { + {layout[1] && + layout[1].map((x) => { const Component = Blocks[x]; return Component && ; })}
- {data.metadata.layout[2] && - data.metadata.layout[2].map((x) => { + {layout[2] && + layout[2].map((x) => { const Component = Blocks[x]; return Component && ; })} diff --git a/src/templates/Pikachu.js b/src/templates/Pikachu.js index 86a26513..9bb1b239 100644 --- a/src/templates/Pikachu.js +++ b/src/templates/Pikachu.js @@ -1,339 +1,116 @@ -import React, { useContext } from 'react'; +import React from 'react'; import ReactMarkdown from 'react-markdown'; +import PageContext from '../contexts/PageContext'; +import ContactA from './blocks/Contact/ContactA'; +import HeadingB from './blocks/Heading/HeadingB'; +import AwardsA from './blocks/Awards/AwardsA'; +import CertificationsA from './blocks/Certifications/CertificationsA'; +import EducationA from './blocks/Education/EducationA'; +import HobbiesA from './blocks/Hobbies/HobbiesA'; +import LanguagesA from './blocks/Languages/LanguagesA'; +import ProjectsA from './blocks/Projects/ProjectsA'; +import ReferencesA from './blocks/References/ReferencesA'; +import SkillsA from './blocks/Skills/SkillsA'; +import WorkA from './blocks/Work/WorkA'; -const Pikachu = () => { - const context = useContext(AppContext); - const { state } = context; - const { data, theme } = state; +const Blocks = { + work: WorkA, + education: EducationA, + projects: ProjectsA, + awards: AwardsA, + certifications: CertificationsA, + skills: SkillsA, + hobbies: HobbiesA, + languages: LanguagesA, + references: ReferencesA, +}; - const Photo = () => - data.profile.photo !== '' && ( -
- -
- ); - - const Header = () => ( -
-
-

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

-
- {data.profile.subtitle} -
- -
- - -
-
- ); - - const ContactItem = ({ icon, value, link = '#' }) => - value && ( -
- - {icon} - - - {value} - -
- ); - - const Heading = ({ title }) => ( -
- {title} -
- ); - - const SkillItem = (x) => ( - - {x.skill} - - ); - - const Skills = () => - data.skills && - data.skills.enable && ( -
- -
- {data.skills.items.map(SkillItem)} -
-
- ); - - const HobbyItem = (x) => ( - - {x.hobby} - - ); - - const Hobbies = () => - data.hobbies && - data.hobbies.enable && ( -
- -
- {data.hobbies.items.map(HobbyItem)} -
-
- ); - - const ReferenceItem = (x) => ( -
-
{x.name}
- {x.position} - {x.phone} - {x.email} - -
- ); - - const References = () => - data.references && - data.references.enable && ( -
- -
- {data.references.items.filter((x) => x.enable).map(ReferenceItem)} -
-
- ); - - const LanguageItem = (x) => ( -
-
{x.key}
-
- {x.level &&
{x.level}
} - {x.rating !== 0 && ( -
- {Array.from(Array(x.rating)).map((_, i) => ( - - star - - ))} -
- )} -
-
- ); - - const Languages = () => - data.languages && - data.languages.enable && ( -
- -
- {data.languages.items.filter((x) => x.enable).map(LanguageItem)} -
-
- ); - - const ExtraItem = (x) => ( -
-
{x.key}
-
{x.value}
-
- ); - - const Extras = () => - data.extras && - data.extras.enable && ( -
- -
- {data.extras.items.filter((x) => x.enable).map(ExtraItem)} -
-
- ); - - const WorkItem = (x) => ( -
-
-
-
{x.title}
-

{x.role}

-
- - ({x.start} - {x.end}) - -
- -
- ); - - const Work = () => - data.work && - data.work.enable && ( -
- -
- {data.work.items.filter((x) => x.enable).map(WorkItem)} -
-
- ); - - const EducationItem = (x) => ( -
-
-
-
{x.name}
-

{x.major}

-
-
- - {x.grade} - - - ({x.start} - {x.end}) - -
-
- -
- ); - - const Education = () => - data.education && - data.education.enable && ( -
- -
- {data.education.items.filter((x) => x.enable).map(EducationItem)} -
-
- ); - - const AwardItem = (x) => ( -
-
{x.title}
-

{x.subtitle}

- -
- ); - - const Awards = () => - data.awards && - data.awards.enable && ( -
- -
- {data.awards.items.filter((x) => x.enable).map(AwardItem)} -
-
- ); - - const CertificationItem = (x) => ( -
-
{x.title}
-

{x.subtitle}

- -
- ); - - const Certifications = () => - data.certifications && - data.certifications.enable && ( -
- -
- {data.certifications.items - .filter((x) => x.enable) - .map(CertificationItem)} -
-
- ); +const Pikachu = ({ data }) => { + const layout = data.metadata.layout.pikachu; return ( -
-
- + +
+
+ {data.profile.photograph && ( +
+ {data.profile.firstName} +
+ )} -
-
-
+
+
+
+

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

+
+ {data.profile.subtitle} +
-
-
- - - - + {data.objective.body && ( +
+
+ + +
+ )} +
+
- - - - -
+
+
+ -
- - - - - + {layout[0] && + layout[0].map((x) => { + const Component = Blocks[x]; + return Component && ; + })} +
+
+ +
+
+ {layout[1] && + layout[1].map((x) => { + const Component = Blocks[x]; + return Component && ; + })} +
+
-
+ ); }; diff --git a/src/templates/blocks/Awards/AwardsA.js b/src/templates/blocks/Awards/AwardsA.js index 98b2fe39..a8221b1d 100644 --- a/src/templates/blocks/Awards/AwardsA.js +++ b/src/templates/blocks/Awards/AwardsA.js @@ -12,7 +12,7 @@ const AwardItem = (x) => ( {x.awarder}
{x.date && ( -
+
{moment(x.date).format('MMMM YYYY')}
)} diff --git a/src/templates/blocks/Certifications/CertificationsA.js b/src/templates/blocks/Certifications/CertificationsA.js index 69e31069..62dccd42 100644 --- a/src/templates/blocks/Certifications/CertificationsA.js +++ b/src/templates/blocks/Certifications/CertificationsA.js @@ -12,7 +12,7 @@ const CertificationItem = (x) => ( {x.issuer}
{x.date && ( -
+
{moment(x.date).format('MMMM YYYY')}
)} diff --git a/src/templates/blocks/Contact/ContactB.js b/src/templates/blocks/Contact/ContactB.js new file mode 100644 index 00000000..118a50a2 --- /dev/null +++ b/src/templates/blocks/Contact/ContactB.js @@ -0,0 +1,64 @@ +import { get } from 'lodash'; +import React, { memo, useContext } from 'react'; +import { FaCaretRight } from 'react-icons/fa'; +import PageContext from '../../../contexts/PageContext'; +import { safetyCheck } from '../../../utils'; +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 ( +
+ + + + + {safetyCheck(data.social) && + data.social.items.map((x) => ( + + ))} +
+ ); +}; + +export default memo(ContactA); diff --git a/src/templates/blocks/Education/EducationA.js b/src/templates/blocks/Education/EducationA.js index aee2cc33..25cfec08 100644 --- a/src/templates/blocks/Education/EducationA.js +++ b/src/templates/blocks/Education/EducationA.js @@ -12,9 +12,9 @@ const EducationItem = (x) => ( {x.degree} {x.field}
-
+
{x.startDate && ( -
+
({formatDateRange({ startDate: x.startDate, endDate: x.endDate })})
)} diff --git a/src/templates/blocks/Heading/HeadingB.js b/src/templates/blocks/Heading/HeadingB.js new file mode 100644 index 00000000..d7ad21f9 --- /dev/null +++ b/src/templates/blocks/Heading/HeadingB.js @@ -0,0 +1,20 @@ +import React, { memo, useContext } from 'react'; +import PageContext from '../../../contexts/PageContext'; + +const HeadingB = ({ children }) => { + const { data } = useContext(PageContext); + + return ( +
+ {children} +
+ ); +}; + +export default memo(HeadingB); diff --git a/src/templates/blocks/Heading/HeadingC.js b/src/templates/blocks/Heading/HeadingC.js new file mode 100644 index 00000000..f9b85d9c --- /dev/null +++ b/src/templates/blocks/Heading/HeadingC.js @@ -0,0 +1,11 @@ +import React, { memo } from 'react'; + +const HeadingC = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +export default memo(HeadingC); diff --git a/src/templates/blocks/Projects/ProjectsA.js b/src/templates/blocks/Projects/ProjectsA.js index f150d1fc..93529647 100644 --- a/src/templates/blocks/Projects/ProjectsA.js +++ b/src/templates/blocks/Projects/ProjectsA.js @@ -16,7 +16,7 @@ const ProjectItem = (x) => ( )}
{x.date && ( -
+
{moment(x.date).format('MMMM YYYY')}
)} diff --git a/src/templates/blocks/References/ReferencesB.js b/src/templates/blocks/References/ReferencesB.js new file mode 100644 index 00000000..b5de74fd --- /dev/null +++ b/src/templates/blocks/References/ReferencesB.js @@ -0,0 +1,31 @@ +import React, { memo, useContext } from 'react'; +import ReactMarkdown from 'react-markdown'; +import PageContext from '../../../contexts/PageContext'; +import { safetyCheck } from '../../../utils'; + +const ReferenceItem = (x) => ( +
+
{x.name}
+ {x.position} + {x.phone} + {x.email} + {x.summary && ( + + )} +
+); + +const ReferencesB = () => { + const { data, heading: Heading } = useContext(PageContext); + + return safetyCheck(data.references) ? ( +
+ {data.references.heading} +
+ {data.references.items.map(ReferenceItem)} +
+
+ ) : null; +}; + +export default memo(ReferencesB); diff --git a/src/templates/blocks/Skills/SkillsB.js b/src/templates/blocks/Skills/SkillsB.js new file mode 100644 index 00000000..32c39ed4 --- /dev/null +++ b/src/templates/blocks/Skills/SkillsB.js @@ -0,0 +1,22 @@ +import React, { memo, useContext } from 'react'; +import PageContext from '../../../contexts/PageContext'; +import { safetyCheck } from '../../../utils'; + +const SkillItem = (x) => ( +
  • + {x.skill} +
  • +); + +const SkillsA = () => { + const { data, heading: Heading } = useContext(PageContext); + + return safetyCheck(data.skills) ? ( +
    + {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 index bc5d108c..0f6fa830 100644 --- a/src/templates/blocks/Work/WorkA.js +++ b/src/templates/blocks/Work/WorkA.js @@ -11,7 +11,7 @@ const WorkItem = (x) => ( {x.position}
    {x.startDate && ( -
    +
    ({formatDateRange({ startDate: x.startDate, endDate: x.endDate })})
    )} diff --git a/src/utils/index.js b/src/utils/index.js index c33eb328..86b25647 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -30,6 +30,19 @@ export const getFieldProps = (formik, schema, name) => ({ ...formik.getFieldProps(name), }); +export const hexToRgb = (hex) => { + const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b); + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; +}; + export const reorder = (list, startIndex, endIndex) => { const result = Array.from(list); const [removed] = result.splice(startIndex, 1);