mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-16 01:32:02 +10:00
- implement gengar template
This commit is contained in:
@ -2,7 +2,9 @@ import React, { memo } from 'react';
|
|||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import { useSelector } from '../../../contexts/ResumeContext';
|
import { useSelector } from '../../../contexts/ResumeContext';
|
||||||
import Onyx from '../../../templates/Onyx';
|
import Onyx from '../../../templates/Onyx';
|
||||||
|
import Pikachu from '../../../templates/Pikachu';
|
||||||
import styles from './Artboard.module.css';
|
import styles from './Artboard.module.css';
|
||||||
|
import Gengar from '../../../templates/Gengar';
|
||||||
|
|
||||||
const Artboard = () => {
|
const Artboard = () => {
|
||||||
const state = useSelector();
|
const state = useSelector();
|
||||||
@ -18,6 +20,8 @@ const Artboard = () => {
|
|||||||
|
|
||||||
<div id="artboard" className={styles.container}>
|
<div id="artboard" className={styles.container}>
|
||||||
{template === 'onyx' && <Onyx data={state} />}
|
{template === 'onyx' && <Onyx data={state} />}
|
||||||
|
{template === 'pikachu' && <Pikachu data={state} />}
|
||||||
|
{template === 'gengar' && <Gengar data={state} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { memo, useState } from 'react';
|
||||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||||
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
|
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
|
||||||
import { move, reorder } from '../../../../utils';
|
import { move, reorder } from '../../../../utils';
|
||||||
@ -9,7 +9,8 @@ import styles from './Layout.module.css';
|
|||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
const [resetLayoutText, setResetLayoutText] = useState('Reset 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 dispatch = useDispatch();
|
||||||
|
|
||||||
const onDragEnd = (result) => {
|
const onDragEnd = (result) => {
|
||||||
@ -28,7 +29,7 @@ const Layout = () => {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: 'on_input',
|
type: 'on_input',
|
||||||
payload: {
|
payload: {
|
||||||
path: 'metadata.layout',
|
path: `metadata.layout.${template}`,
|
||||||
value: newState,
|
value: newState,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -40,7 +41,7 @@ const Layout = () => {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: 'on_input',
|
type: 'on_input',
|
||||||
payload: {
|
payload: {
|
||||||
path: 'metadata.layout',
|
path: `metadata.layout.${template}`,
|
||||||
value: newState,
|
value: newState,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -110,4 +111,4 @@ const Layout = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Layout;
|
export default memo(Layout);
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import firebase from 'gatsby-plugin-firebase';
|
|||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import React, { createContext, memo, useContext, useState } from 'react';
|
import React, { createContext, memo, useContext, useState } from 'react';
|
||||||
import initialState from '../data/initialState';
|
|
||||||
import UserContext from './UserContext';
|
import UserContext from './UserContext';
|
||||||
|
import initialState from '../data/initialState.json';
|
||||||
|
|
||||||
const DEBOUNCE_WAIT_TIME = 4000;
|
const DEBOUNCE_WAIT_TIME = 4000;
|
||||||
|
|
||||||
@ -68,6 +68,8 @@ const DatabaseProvider = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const duplicateResume = (originalResume) => {
|
const duplicateResume = (originalResume) => {
|
||||||
|
console.trace();
|
||||||
|
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
const createdAt = firebase.database.ServerValue.TIMESTAMP;
|
const createdAt = firebase.database.ServerValue.TIMESTAMP;
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,5 @@
|
|||||||
import arrayMove from 'array-move';
|
import arrayMove from 'array-move';
|
||||||
import {
|
import { clone, findIndex, get, isUndefined, merge, setWith } from 'lodash';
|
||||||
clone,
|
|
||||||
concat,
|
|
||||||
findIndex,
|
|
||||||
flatten,
|
|
||||||
get,
|
|
||||||
isUndefined,
|
|
||||||
merge,
|
|
||||||
setWith,
|
|
||||||
times,
|
|
||||||
} from 'lodash';
|
|
||||||
import React, {
|
import React, {
|
||||||
createContext,
|
createContext,
|
||||||
memo,
|
memo,
|
||||||
@ -18,9 +8,8 @@ import React, {
|
|||||||
useReducer,
|
useReducer,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import demoState from '../data/demoState.json';
|
import demoState from '../data/demoState.json';
|
||||||
import initialState from '../data/initialState';
|
import initialState from '../data/initialState.json';
|
||||||
import DatabaseContext from './DatabaseContext';
|
import DatabaseContext from './DatabaseContext';
|
||||||
import leftSections from '../data/leftSections';
|
|
||||||
|
|
||||||
const ResumeContext = createContext({});
|
const ResumeContext = createContext({});
|
||||||
|
|
||||||
@ -32,7 +21,6 @@ const ResumeProvider = ({ children }) => {
|
|||||||
let newState;
|
let newState;
|
||||||
let index;
|
let index;
|
||||||
let items;
|
let items;
|
||||||
let diff;
|
|
||||||
let temp;
|
let temp;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -85,46 +73,15 @@ const ResumeProvider = ({ children }) => {
|
|||||||
debouncedUpdateResume(newState);
|
debouncedUpdateResume(newState);
|
||||||
return 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':
|
case 'reset_layout':
|
||||||
items = [leftSections.filter((x) => !x.fixed).map((x) => x.id)];
|
temp = get(state, 'metadata.template');
|
||||||
newState = setWith(clone(state), 'metadata.layout', items, clone);
|
items = get(initialState, `metadata.layout.${temp}`);
|
||||||
|
newState = setWith(
|
||||||
|
clone(state),
|
||||||
|
`metadata.layout.${temp}`,
|
||||||
|
items,
|
||||||
|
clone,
|
||||||
|
);
|
||||||
debouncedUpdateResume(newState);
|
debouncedUpdateResume(newState);
|
||||||
return newState;
|
return newState;
|
||||||
|
|
||||||
@ -144,7 +101,13 @@ const ResumeProvider = ({ children }) => {
|
|||||||
return newState;
|
return newState;
|
||||||
|
|
||||||
case 'reset_data':
|
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);
|
debouncedUpdateResume(newState);
|
||||||
return newState;
|
return newState;
|
||||||
|
|
||||||
|
|||||||
@ -7,14 +7,21 @@
|
|||||||
"date": "2019-04-01",
|
"date": "2019-04-01",
|
||||||
"id": "6f857f2b-6312-4a0d-907d-2e17991954eb",
|
"id": "6f857f2b-6312-4a0d-907d-2e17991954eb",
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"title": "International Flutter Hackathon '19"
|
"title": "International Flutter Hackathon"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"awarder": "Venturesity",
|
"awarder": "Venturesity",
|
||||||
"date": "2016-06-01",
|
"date": "2016-06-01",
|
||||||
"id": "f6efa3f9-9741-4e36-a538-ba0d9779bc61",
|
"id": "f6efa3f9-9741-4e36-a538-ba0d9779bc61",
|
||||||
"summary": "",
|
"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
|
"visible": true
|
||||||
@ -25,16 +32,23 @@
|
|||||||
{
|
{
|
||||||
"date": "2018-02-01",
|
"date": "2018-02-01",
|
||||||
"id": "d2ec12bc-7876-46bc-afd4-11ae06faf3bd",
|
"id": "d2ec12bc-7876-46bc-afd4-11ae06faf3bd",
|
||||||
"issuer": "Google",
|
"issuer": "Cisco Systems",
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"title": "Applied CS with Android"
|
"title": "CCNP"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "2019-06-01",
|
"date": "2019-06-01",
|
||||||
"id": "f8312288-53ae-4504-a768-4b67aea95926",
|
"id": "f8312288-53ae-4504-a768-4b67aea95926",
|
||||||
"issuer": "Udemy",
|
"issuer": "VMWare",
|
||||||
"summary": "",
|
"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
|
"visible": true
|
||||||
@ -43,23 +57,23 @@
|
|||||||
"heading": "Education",
|
"heading": "Education",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"degree": "Bachelor's Degree",
|
"degree": "Masters",
|
||||||
"endDate": "2018-04-01",
|
"endDate": "2002-08-01",
|
||||||
"field": "Computer Science & Engineering",
|
"field": "Computer Science",
|
||||||
"gpa": "9.2",
|
"gpa": "7.2 CGPA",
|
||||||
"id": "c42e2a5a-3f0d-497e-838b-ac2019dcf045",
|
"id": "c42e2a5a-3f0d-497e-838b-ac2019dcf045",
|
||||||
"institution": "Dayananda Sagar College of Engineering",
|
"institution": "The City College of New York, NYC, NY",
|
||||||
"startDate": "2015-04-01",
|
"startDate": "2001-09-01",
|
||||||
"summary": ""
|
"summary": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"degree": "Diploma",
|
"degree": "Bachelors",
|
||||||
"endDate": "2015-04-01",
|
"endDate": "2001-08-01",
|
||||||
"field": "Computer Science",
|
"field": "Computer Science",
|
||||||
"gpa": "9.8",
|
"gpa": "8.4 CGPA",
|
||||||
"id": "278490a2-c327-4e83-8be8-adf913a9b36c",
|
"id": "278490a2-c327-4e83-8be8-adf913a9b36c",
|
||||||
"institution": "Dayananda Sagar Institute of Technology",
|
"institution": "University of California, Berkeley, CA",
|
||||||
"startDate": "2012-04-01",
|
"startDate": "1997-09-01",
|
||||||
"summary": ""
|
"summary": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -68,6 +82,8 @@
|
|||||||
"hobbies": {
|
"hobbies": {
|
||||||
"heading": "Hobbies",
|
"heading": "Hobbies",
|
||||||
"items": [
|
"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": "92c35e3b-6cd7-4cea-b505-61347ec61b68", "name": "Photography" },
|
||||||
{
|
{
|
||||||
"id": "d36f2089-93a9-4f30-a425-3dd81c6b89df",
|
"id": "d36f2089-93a9-4f30-a425-3dd81c6b89df",
|
||||||
@ -75,7 +91,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "d1da41a9-ae83-48fb-8047-d45ebd869a69",
|
"id": "d1da41a9-ae83-48fb-8047-d45ebd869a69",
|
||||||
"name": "Working on Personal Projects"
|
"name": "Developing Reactive Resume"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"visible": true
|
"visible": true
|
||||||
@ -113,15 +129,26 @@
|
|||||||
"text": "#212121"
|
"text": "#212121"
|
||||||
},
|
},
|
||||||
"font": "Open Sans",
|
"font": "Open Sans",
|
||||||
"layout": [
|
"layout": {
|
||||||
["objective", "work", "education", "projects"],
|
"onyx": [
|
||||||
["hobbies", "languages"],
|
["objective", "work", "education", "projects"],
|
||||||
["skills", "certifications", "awards", "references"]
|
["hobbies", "languages", "awards", "certifications"],
|
||||||
],
|
["skills", "references"]
|
||||||
"template": "onyx"
|
],
|
||||||
|
"pikachu": [
|
||||||
|
["skills", "languages", "hobbies", "awards", "certifications"],
|
||||||
|
["work", "education", "projects", "references"]
|
||||||
|
],
|
||||||
|
"gengar": [
|
||||||
|
["objective", "skills"],
|
||||||
|
["awards", "certifications", "languages", "references", "hobbies"],
|
||||||
|
["work", "education", "projects"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"template": "pikachu"
|
||||||
},
|
},
|
||||||
"objective": {
|
"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",
|
"heading": "Objective",
|
||||||
"visible": true
|
"visible": true
|
||||||
},
|
},
|
||||||
@ -137,7 +164,7 @@
|
|||||||
"heading": "Profile",
|
"heading": "Profile",
|
||||||
"lastName": "Pillai",
|
"lastName": "Pillai",
|
||||||
"phone": "+91 98453 36113",
|
"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": "",
|
"profile": "",
|
||||||
"subtitle": "Full Stack Web Developer",
|
"subtitle": "Full Stack Web Developer",
|
||||||
"website": "amruthpillai.com"
|
"website": "amruthpillai.com"
|
||||||
@ -181,6 +208,14 @@
|
|||||||
"phone": "+91 93893 34353",
|
"phone": "+91 93893 34353",
|
||||||
"position": "CEO at Newton Motors",
|
"position": "CEO at Newton Motors",
|
||||||
"summary": ""
|
"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
|
"visible": true
|
||||||
@ -191,22 +226,32 @@
|
|||||||
{
|
{
|
||||||
"id": "54e5bceb-d0e9-4f04-98d1-48a34f7cf920",
|
"id": "54e5bceb-d0e9-4f04-98d1-48a34f7cf920",
|
||||||
"level": "Advanced",
|
"level": "Advanced",
|
||||||
"name": "ReactJS"
|
"name": "Customer Service Expertise"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "f0274f62-2252-4cc0-bf12-9e1070942c50",
|
"id": "f0274f62-2252-4cc0-bf12-9e1070942c50",
|
||||||
"level": "Advanced",
|
"level": "Intermediate",
|
||||||
"name": "Angular"
|
"name": "High-Volume Call Center"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "689e2852-df1b-4d41-bda8-c41c88196264",
|
"id": "689e2852-df1b-4d41-bda8-c41c88196264",
|
||||||
"level": "Advanced",
|
"level": "Intermediate",
|
||||||
"name": "Flutter"
|
"name": "Team Leader/Problem Solver"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3a4f73b1-50c1-4a85-a4b0-2a55dfe5053a",
|
"id": "3a4f73b1-50c1-4a85-a4b0-2a55dfe5053a",
|
||||||
"level": "Novice",
|
"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
|
"visible": true
|
||||||
@ -214,6 +259,12 @@
|
|||||||
"social": {
|
"social": {
|
||||||
"heading": "Social",
|
"heading": "Social",
|
||||||
"items": [
|
"items": [
|
||||||
|
{
|
||||||
|
"url": "https://pillai.xyz/instagram",
|
||||||
|
"network": "Instagram",
|
||||||
|
"username": "AmruthPillai",
|
||||||
|
"id": "a832b37d-f11d-4a80-8b4d-24796e571b17"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "a72107fa-a4a5-407d-9e85-39bdb9c0b11a",
|
"id": "a72107fa-a4a5-407d-9e85-39bdb9c0b11a",
|
||||||
"network": "Twitter",
|
"network": "Twitter",
|
||||||
@ -233,22 +284,31 @@
|
|||||||
"heading": "Work Experience",
|
"heading": "Work Experience",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"company": "Postdot Technologies Pvt. Ltd.",
|
"company": "On Point Electronics, NYC, NY",
|
||||||
"endDate": "",
|
"endDate": "2018-07-01",
|
||||||
"id": "d7c64937-0cb9-41b1-a3a6-0679c882fe63",
|
"id": "d7c64937-0cb9-41b1-a3a6-0679c882fe63",
|
||||||
"position": "Full Stack Web Developer",
|
"position": "Customer Service Representative",
|
||||||
"startDate": "2020-06-08",
|
"startDate": "2013-01-01",
|
||||||
"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.",
|
"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://postman.com"
|
"website": "https://onpoint.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"company": "GoDhiyo Solutions Pvt. Ltd.",
|
"company": "Excelsior Communications, NYC, NY",
|
||||||
"endDate": "2020-04-01",
|
"endDate": "2012-12-01",
|
||||||
"id": "f5c5dcfe-2a60-4169-a2f1-b305355518ea",
|
"id": "f5c5dcfe-2a60-4169-a2f1-b305355518ea",
|
||||||
"position": "Full Stack Web Developer",
|
"position": "Customer Service Representative",
|
||||||
"startDate": "2018-07-01",
|
"startDate": "2009-10-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",
|
"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://dhiyo.ai"
|
"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
|
"visible": true
|
||||||
|
|||||||
@ -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;
|
|
||||||
99
src/data/initialState.json
Normal file
99
src/data/initialState.json
Normal file
@ -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
|
||||||
|
}
|
||||||
@ -9,6 +9,11 @@ const templateOptions = [
|
|||||||
name: 'Pikachu',
|
name: 'Pikachu',
|
||||||
preview: 'https://source.unsplash.com/random/301x501',
|
preview: 'https://source.unsplash.com/random/301x501',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'gengar',
|
||||||
|
name: 'Gengar',
|
||||||
|
preview: 'https://source.unsplash.com/random/302x502',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default templateOptions;
|
export default templateOptions;
|
||||||
|
|||||||
@ -10,6 +10,7 @@ const ExportModal = () => {
|
|||||||
const state = useSelector();
|
const state = useSelector();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [isLoadingSingle, setLoadingSingle] = useState(false);
|
const [isLoadingSingle, setLoadingSingle] = useState(false);
|
||||||
|
const [isLoadingMulti, setLoadingMulti] = useState(false);
|
||||||
const functionsUrl = 'http://localhost:5001/rx-resume/us-central1';
|
const functionsUrl = 'http://localhost:5001/rx-resume/us-central1';
|
||||||
|
|
||||||
const { emitter, events } = useContext(ModalContext);
|
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 () => {
|
const handleSinglePageDownload = async () => {
|
||||||
setLoadingSingle(true);
|
setLoadingSingle(true);
|
||||||
fetch(`${functionsUrl}/printSinglePageResume?id=${state.id}`, {
|
fetch(`${functionsUrl}/printSinglePageResume?id=${state.id}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
.then((response) => response.blob())
|
.then((response) => response.blob())
|
||||||
.then((data) => {
|
.then(openFile);
|
||||||
if (typeof window !== `undefined`) {
|
};
|
||||||
const url = window.URL.createObjectURL(data, { oneTimeOnly: true });
|
|
||||||
window && window.open(url);
|
const handleMultiPageDownload = async () => {
|
||||||
setLoadingSingle(false);
|
setLoadingMulti(true);
|
||||||
}
|
fetch(`${functionsUrl}/printMultiPageResume?id=${state.id}`, {
|
||||||
});
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.then((response) => response.blob())
|
||||||
|
.then(openFile);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExportToJson = () => {
|
const handleExportToJson = () => {
|
||||||
@ -99,7 +111,13 @@ const ExportModal = () => {
|
|||||||
>
|
>
|
||||||
Single Page Resume
|
Single Page Resume
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="ml-8">Multi Page Resume</Button>
|
<Button
|
||||||
|
className="ml-8"
|
||||||
|
isLoading={isLoadingMulti}
|
||||||
|
onClick={handleMultiPageDownload}
|
||||||
|
>
|
||||||
|
Multi Page Resume
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import LoadingScreen from '../../components/router/LoadingScreen';
|
|||||||
import DatabaseContext from '../../contexts/DatabaseContext';
|
import DatabaseContext from '../../contexts/DatabaseContext';
|
||||||
import Onyx from '../../templates/Onyx';
|
import Onyx from '../../templates/Onyx';
|
||||||
import styles from './view.module.css';
|
import styles from './view.module.css';
|
||||||
|
import Pikachu from '../../templates/Pikachu';
|
||||||
|
import Gengar from '../../templates/Gengar';
|
||||||
|
|
||||||
const ResumeViewer = ({ id }) => {
|
const ResumeViewer = ({ id }) => {
|
||||||
const [resume, setResume] = useState(null);
|
const [resume, setResume] = useState(null);
|
||||||
@ -46,6 +48,8 @@ const ResumeViewer = ({ id }) => {
|
|||||||
style={{ backgroundColor: resume.metadata.colors.background }}
|
style={{ backgroundColor: resume.metadata.colors.background }}
|
||||||
>
|
>
|
||||||
{resume.metadata.template === 'onyx' && <Onyx data={resume} />}
|
{resume.metadata.template === 'onyx' && <Onyx data={resume} />}
|
||||||
|
{resume.metadata.template === 'pikachu' && <Pikachu data={resume} />}
|
||||||
|
{resume.metadata.template === 'gengar' && <Gengar data={resume} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className={styles.footer}>
|
<p className={styles.footer}>
|
||||||
|
|||||||
131
src/templates/Gengar.js
Normal file
131
src/templates/Gengar.js
Normal file
@ -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 !== '' && (
|
||||||
|
<img
|
||||||
|
className="w-24 h-24 rounded-full mr-4 object-cover border-4"
|
||||||
|
style={{
|
||||||
|
borderColor: data.metadata.colors.background,
|
||||||
|
}}
|
||||||
|
src={data.profile.photograph}
|
||||||
|
alt={data.profile.firstName}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Profile = () => (
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold leading-tight">
|
||||||
|
{data.profile.firstName}
|
||||||
|
</h1>
|
||||||
|
<h1 className="text-2xl font-bold leading-tight">
|
||||||
|
{data.profile.lastName}
|
||||||
|
</h1>
|
||||||
|
<div className="text-xs font-medium mt-2">{data.profile.subtitle}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContext.Provider value={{ data, heading: HeadingC }}>
|
||||||
|
<div
|
||||||
|
id="page"
|
||||||
|
className="rounded"
|
||||||
|
style={{
|
||||||
|
fontFamily: data.metadata.font,
|
||||||
|
color: data.metadata.colors.text,
|
||||||
|
backgroundColor: data.metadata.colors.background,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-12">
|
||||||
|
<div
|
||||||
|
className="col-span-4 px-6 py-8"
|
||||||
|
style={{
|
||||||
|
backgroundColor: data.metadata.colors.primary,
|
||||||
|
color: data.metadata.colors.background,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Photo />
|
||||||
|
<Profile />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr
|
||||||
|
className="w-1/4 my-5 opacity-25"
|
||||||
|
style={{ borderColor: data.metadata.colors.background }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ContactB />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="col-span-8 px-6 py-8"
|
||||||
|
style={{ backgroundColor: `rgba(${r}, ${g}, ${b}, 0.1)` }}
|
||||||
|
>
|
||||||
|
<div className="grid gap-6 items-center">
|
||||||
|
{layout[0] &&
|
||||||
|
layout[0].map((x) => {
|
||||||
|
const Component = Blocks[x];
|
||||||
|
return Component && <Component key={x} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="col-span-4 px-6 py-8"
|
||||||
|
style={{ backgroundColor: `rgba(${r}, ${g}, ${b}, 0.1)` }}
|
||||||
|
>
|
||||||
|
<div className="grid gap-6">
|
||||||
|
{layout[1] &&
|
||||||
|
layout[1].map((x) => {
|
||||||
|
const Component = Blocks[x];
|
||||||
|
return Component && <Component key={x} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-span-8 px-6 py-8">
|
||||||
|
<div className="grid gap-6">
|
||||||
|
{layout[2] &&
|
||||||
|
layout[2].map((x) => {
|
||||||
|
const Component = Blocks[x];
|
||||||
|
return Component && <Component key={x} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Gengar;
|
||||||
@ -1,11 +1,10 @@
|
|||||||
import React, { memo, useEffect } from 'react';
|
import React, { memo } from 'react';
|
||||||
import PageContext from '../contexts/PageContext';
|
import PageContext from '../contexts/PageContext';
|
||||||
import { useDispatch } from '../contexts/ResumeContext';
|
|
||||||
import AwardsA from './blocks/Awards/AwardsA';
|
import AwardsA from './blocks/Awards/AwardsA';
|
||||||
import CertificationsA from './blocks/Certifications/CertificationsA';
|
import CertificationsA from './blocks/Certifications/CertificationsA';
|
||||||
import Contact from './blocks/Contact/ContactA';
|
import Contact from './blocks/Contact/ContactA';
|
||||||
import EducationA from './blocks/Education/EducationA';
|
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 HobbiesA from './blocks/Hobbies/HobbiesA';
|
||||||
import LanguagesA from './blocks/Languages/LanguagesA';
|
import LanguagesA from './blocks/Languages/LanguagesA';
|
||||||
import ObjectiveA from './blocks/Objective/ObjectiveA';
|
import ObjectiveA from './blocks/Objective/ObjectiveA';
|
||||||
@ -28,18 +27,10 @@ const Blocks = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Onyx = ({ data }) => {
|
const Onyx = ({ data }) => {
|
||||||
const dispatch = useDispatch();
|
const layout = data.metadata.layout.onyx;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch({
|
|
||||||
type: 'set_fixed_sections',
|
|
||||||
payload: ['profile', 'social'],
|
|
||||||
});
|
|
||||||
dispatch({ type: 'set_block_count', payload: 3 });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContext.Provider value={{ data, heading: Heading }}>
|
<PageContext.Provider value={{ data, heading: HeadingA }}>
|
||||||
<div
|
<div
|
||||||
id="page"
|
id="page"
|
||||||
className="p-10 rounded"
|
className="p-10 rounded"
|
||||||
@ -88,22 +79,22 @@ const Onyx = ({ data }) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
{data.metadata.layout[0] &&
|
{layout[0] &&
|
||||||
data.metadata.layout[0].map((x) => {
|
layout[0].map((x) => {
|
||||||
const Component = Blocks[x];
|
const Component = Blocks[x];
|
||||||
return Component && <Component key={x} />;
|
return Component && <Component key={x} />;
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
{data.metadata.layout[1] &&
|
{layout[1] &&
|
||||||
data.metadata.layout[1].map((x) => {
|
layout[1].map((x) => {
|
||||||
const Component = Blocks[x];
|
const Component = Blocks[x];
|
||||||
return Component && <Component key={x} />;
|
return Component && <Component key={x} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{data.metadata.layout[2] &&
|
{layout[2] &&
|
||||||
data.metadata.layout[2].map((x) => {
|
layout[2].map((x) => {
|
||||||
const Component = Blocks[x];
|
const Component = Blocks[x];
|
||||||
return Component && <Component key={x} />;
|
return Component && <Component key={x} />;
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -1,339 +1,116 @@
|
|||||||
import React, { useContext } from 'react';
|
import React from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
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 Blocks = {
|
||||||
const context = useContext(AppContext);
|
work: WorkA,
|
||||||
const { state } = context;
|
education: EducationA,
|
||||||
const { data, theme } = state;
|
projects: ProjectsA,
|
||||||
|
awards: AwardsA,
|
||||||
|
certifications: CertificationsA,
|
||||||
|
skills: SkillsA,
|
||||||
|
hobbies: HobbiesA,
|
||||||
|
languages: LanguagesA,
|
||||||
|
references: ReferencesA,
|
||||||
|
};
|
||||||
|
|
||||||
const Photo = () =>
|
const Pikachu = ({ data }) => {
|
||||||
data.profile.photo !== '' && (
|
const layout = data.metadata.layout.pikachu;
|
||||||
<div className="self-center col-span-4">
|
|
||||||
<img
|
|
||||||
className="w-48 h-48 rounded-full mx-auto object-cover"
|
|
||||||
src={data.profile.photo}
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Header = () => (
|
|
||||||
<div
|
|
||||||
className="h-48 rounded flex flex-col justify-center"
|
|
||||||
style={{
|
|
||||||
backgroundColor: theme.colors.accent,
|
|
||||||
color: theme.colors.background,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col justify-center mx-8 my-6">
|
|
||||||
<h1 className="text-3xl font-bold leading-tight">
|
|
||||||
{data.profile.firstName} {data.profile.lastName}
|
|
||||||
</h1>
|
|
||||||
<div className="text-sm font-medium tracking-wide">
|
|
||||||
{data.profile.subtitle}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr className="my-4 opacity-50" />
|
|
||||||
|
|
||||||
<ReactMarkdown className="text-sm" source={data.objective.body} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const ContactItem = ({ icon, value, link = '#' }) =>
|
|
||||||
value && (
|
|
||||||
<div className="flex items-center my-3">
|
|
||||||
<span
|
|
||||||
className="material-icons text-lg mr-2"
|
|
||||||
style={{ color: theme.colors.accent }}
|
|
||||||
>
|
|
||||||
{icon}
|
|
||||||
</span>
|
|
||||||
<a href={link}>
|
|
||||||
<span className="font-medium break-all">{value}</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Heading = ({ title }) => (
|
|
||||||
<div
|
|
||||||
className="mb-2 border-b-2 pb-1 font-bold uppercase tracking-wide text-sm"
|
|
||||||
style={{ color: theme.colors.accent, borderColor: theme.colors.accent }}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const SkillItem = (x) => (
|
|
||||||
<span
|
|
||||||
key={x.id}
|
|
||||||
className="leading-none rounded-lg text-sm font-medium bg-gray-300 py-3 my-1 px-4"
|
|
||||||
>
|
|
||||||
{x.skill}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Skills = () =>
|
|
||||||
data.skills &&
|
|
||||||
data.skills.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.skills.heading} />
|
|
||||||
<div className="flex flex-col mb-6">
|
|
||||||
{data.skills.items.map(SkillItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const HobbyItem = (x) => (
|
|
||||||
<span
|
|
||||||
key={x.id}
|
|
||||||
className="leading-none rounded-lg text-sm font-medium bg-gray-300 py-3 my-1 px-4"
|
|
||||||
>
|
|
||||||
{x.hobby}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Hobbies = () =>
|
|
||||||
data.hobbies &&
|
|
||||||
data.hobbies.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.hobbies.heading} />
|
|
||||||
<div className="flex flex-col mb-6">
|
|
||||||
{data.hobbies.items.map(HobbyItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const ReferenceItem = (x) => (
|
|
||||||
<div key={x.id} className="flex flex-col">
|
|
||||||
<h6 className="text-sm font-medium">{x.name}</h6>
|
|
||||||
<span className="text-xs">{x.position}</span>
|
|
||||||
<span className="text-xs">{x.phone}</span>
|
|
||||||
<span className="text-xs">{x.email}</span>
|
|
||||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const References = () =>
|
|
||||||
data.references &&
|
|
||||||
data.references.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.references.heading} />
|
|
||||||
<div className="grid grid-cols-2 gap-2 mb-6">
|
|
||||||
{data.references.items.filter((x) => x.enable).map(ReferenceItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const LanguageItem = (x) => (
|
|
||||||
<div key={x.id} className="grid grid-cols-2 items-center py-2">
|
|
||||||
<h6 className="text-sm font-medium">{x.key}</h6>
|
|
||||||
<div className="flex">
|
|
||||||
{x.level && <div className="font-bold text-sm mr-2">{x.level}</div>}
|
|
||||||
{x.rating !== 0 && (
|
|
||||||
<div className="flex">
|
|
||||||
{Array.from(Array(x.rating)).map((_, i) => (
|
|
||||||
<i
|
|
||||||
key={i}
|
|
||||||
className="material-icons text-lg"
|
|
||||||
style={{ color: theme.colors.accent }}
|
|
||||||
>
|
|
||||||
star
|
|
||||||
</i>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Languages = () =>
|
|
||||||
data.languages &&
|
|
||||||
data.languages.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.languages.heading} />
|
|
||||||
<div className="mb-6">
|
|
||||||
{data.languages.items.filter((x) => x.enable).map(LanguageItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const ExtraItem = (x) => (
|
|
||||||
<div key={x.id} className="text-sm my-1">
|
|
||||||
<h6 className="text-xs font-bold">{x.key}</h6>
|
|
||||||
<h6 className="">{x.value}</h6>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Extras = () =>
|
|
||||||
data.extras &&
|
|
||||||
data.extras.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.extras.heading} />
|
|
||||||
<div className="grid grid-cols-2">
|
|
||||||
{data.extras.items.filter((x) => x.enable).map(ExtraItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const WorkItem = (x) => (
|
|
||||||
<div key={x.id} className="mb-3">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<div>
|
|
||||||
<h6 className="font-semibold">{x.title}</h6>
|
|
||||||
<p className="text-xs">{x.role}</p>
|
|
||||||
</div>
|
|
||||||
<span className="text-xs font-medium">
|
|
||||||
({x.start} - {x.end})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Work = () =>
|
|
||||||
data.work &&
|
|
||||||
data.work.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.work.heading} />
|
|
||||||
<div className="flex flex-col mb-4">
|
|
||||||
{data.work.items.filter((x) => x.enable).map(WorkItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const EducationItem = (x) => (
|
|
||||||
<div key={x.id} className="mb-2">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<div>
|
|
||||||
<h6 className="font-semibold">{x.name}</h6>
|
|
||||||
<p className="text-xs">{x.major}</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col text-right items-end">
|
|
||||||
<span
|
|
||||||
className="text-sm font-bold"
|
|
||||||
style={{ color: theme.colors.accent }}
|
|
||||||
>
|
|
||||||
{x.grade}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs font-medium">
|
|
||||||
({x.start} - {x.end})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Education = () =>
|
|
||||||
data.education &&
|
|
||||||
data.education.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.education.heading} />
|
|
||||||
<div className="flex flex-col mb-4">
|
|
||||||
{data.education.items.filter((x) => x.enable).map(EducationItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const AwardItem = (x) => (
|
|
||||||
<div key={x.id} className="mb-2">
|
|
||||||
<h6 className="font-semibold">{x.title}</h6>
|
|
||||||
<p className="text-xs">{x.subtitle}</p>
|
|
||||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Awards = () =>
|
|
||||||
data.awards &&
|
|
||||||
data.awards.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.awards.heading} />
|
|
||||||
<div className="flex flex-col mb-2">
|
|
||||||
{data.awards.items.filter((x) => x.enable).map(AwardItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const CertificationItem = (x) => (
|
|
||||||
<div key={x.id} className="mb-3">
|
|
||||||
<h6 className="font-semibold">{x.title}</h6>
|
|
||||||
<p className="text-xs">{x.subtitle}</p>
|
|
||||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Certifications = () =>
|
|
||||||
data.certifications &&
|
|
||||||
data.certifications.enable && (
|
|
||||||
<div>
|
|
||||||
<Heading title={data.certifications.heading} />
|
|
||||||
<div className="flex flex-col mb-2">
|
|
||||||
{data.certifications.items
|
|
||||||
.filter((x) => x.enable)
|
|
||||||
.map(CertificationItem)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<PageContext.Provider value={{ data, heading: HeadingB }}>
|
||||||
className="p-10"
|
<div
|
||||||
style={{
|
id="page"
|
||||||
fontFamily: theme.font.family,
|
className="p-10 rounded"
|
||||||
backgroundColor: theme.colors.background,
|
style={{
|
||||||
color: theme.colors.primary,
|
fontFamily: data.metadata.font,
|
||||||
}}
|
color: data.metadata.colors.text,
|
||||||
>
|
backgroundColor: data.metadata.colors.background,
|
||||||
<div className="grid grid-cols-12 col-gap-6 row-gap-8">
|
}}
|
||||||
<Photo />
|
>
|
||||||
|
<div className="grid grid-cols-12 col-gap-6 row-gap-8">
|
||||||
|
{data.profile.photograph && (
|
||||||
|
<div className="self-center col-span-4">
|
||||||
|
<img
|
||||||
|
className="w-48 h-48 rounded-full mx-auto object-cover"
|
||||||
|
src={data.profile.photograph}
|
||||||
|
alt={data.profile.firstName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
data.profile.photo !== '' ? 'col-span-8' : 'col-span-12'
|
data.profile.photograph !== '' ? 'col-span-8' : 'col-span-12'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Header />
|
<div
|
||||||
</div>
|
className="h-48 rounded flex flex-col justify-center"
|
||||||
|
style={{
|
||||||
|
backgroundColor: data.metadata.colors.primary,
|
||||||
|
color: data.metadata.colors.background,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col justify-center mx-8 my-6">
|
||||||
|
<h1 className="text-3xl font-bold leading-tight">
|
||||||
|
{data.profile.firstName} {data.profile.lastName}
|
||||||
|
</h1>
|
||||||
|
<div className="text-sm font-medium tracking-wide">
|
||||||
|
{data.profile.subtitle}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="col-span-4 overflow-hidden">
|
{data.objective.body && (
|
||||||
<div className="text-sm mb-6">
|
<div>
|
||||||
<ContactItem
|
<hr
|
||||||
icon="phone"
|
className="my-5 opacity-25"
|
||||||
value={data.profile.phone}
|
style={{ borderColor: data.metadata.colors.background }}
|
||||||
link={`tel:${data.profile.phone}`}
|
/>
|
||||||
/>
|
|
||||||
<ContactItem
|
<ReactMarkdown
|
||||||
icon="language"
|
className="text-sm"
|
||||||
value={data.profile.website}
|
source={data.objective.body}
|
||||||
link={`http://${data.profile.website}`}
|
/>
|
||||||
/>
|
</div>
|
||||||
<ContactItem
|
)}
|
||||||
icon="email"
|
</div>
|
||||||
value={data.profile.email}
|
</div>
|
||||||
link={`mailto:${data.profile.email}`}
|
|
||||||
/>
|
|
||||||
<ContactItem
|
|
||||||
icon="location_on"
|
|
||||||
value={data.profile.address.line3}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Skills />
|
<div className="col-span-4">
|
||||||
<Hobbies />
|
<div className="grid gap-4">
|
||||||
<Languages />
|
<ContactA />
|
||||||
<Certifications />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-8">
|
{layout[0] &&
|
||||||
<Work />
|
layout[0].map((x) => {
|
||||||
<Education />
|
const Component = Blocks[x];
|
||||||
<Awards />
|
return Component && <Component key={x} />;
|
||||||
<References />
|
})}
|
||||||
<Extras />
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-span-8">
|
||||||
|
<div className="grid gap-4">
|
||||||
|
{layout[1] &&
|
||||||
|
layout[1].map((x) => {
|
||||||
|
const Component = Blocks[x];
|
||||||
|
return Component && <Component key={x} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PageContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ const AwardItem = (x) => (
|
|||||||
<span className="text-xs">{x.awarder}</span>
|
<span className="text-xs">{x.awarder}</span>
|
||||||
</div>
|
</div>
|
||||||
{x.date && (
|
{x.date && (
|
||||||
<h6 className="text-xs font-medium">
|
<h6 className="text-xs font-medium text-right">
|
||||||
{moment(x.date).format('MMMM YYYY')}
|
{moment(x.date).format('MMMM YYYY')}
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ const CertificationItem = (x) => (
|
|||||||
<span className="text-xs">{x.issuer}</span>
|
<span className="text-xs">{x.issuer}</span>
|
||||||
</div>
|
</div>
|
||||||
{x.date && (
|
{x.date && (
|
||||||
<h6 className="text-xs font-medium">
|
<h6 className="text-xs font-medium text-right">
|
||||||
{moment(x.date).format('MMMM YYYY')}
|
{moment(x.date).format('MMMM YYYY')}
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
|
|||||||
64
src/templates/blocks/Contact/ContactB.js
Normal file
64
src/templates/blocks/Contact/ContactB.js
Normal file
@ -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 ? (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Icon
|
||||||
|
size="10px"
|
||||||
|
className="mr-2"
|
||||||
|
style={{ color: data.metadata.colors.background }}
|
||||||
|
/>
|
||||||
|
{link ? (
|
||||||
|
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||||
|
<span className="font-medium break-all">{value}</span>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span className="font-medium break-all">{value}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContactA = () => {
|
||||||
|
const { data } = useContext(PageContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-xs grid gap-2">
|
||||||
|
<ContactItem
|
||||||
|
icon="phone"
|
||||||
|
value={data.profile.phone}
|
||||||
|
link={`tel:${data.profile.phone}`}
|
||||||
|
/>
|
||||||
|
<ContactItem
|
||||||
|
icon="website"
|
||||||
|
value={data.profile.website}
|
||||||
|
link={`http://${data.profile.website}`}
|
||||||
|
/>
|
||||||
|
<ContactItem
|
||||||
|
icon="email"
|
||||||
|
value={data.profile.email}
|
||||||
|
link={`mailto:${data.profile.email}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{safetyCheck(data.social) &&
|
||||||
|
data.social.items.map((x) => (
|
||||||
|
<ContactItem
|
||||||
|
key={x.id}
|
||||||
|
value={x.username}
|
||||||
|
icon={x.network}
|
||||||
|
link={x.url}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ContactA);
|
||||||
@ -12,9 +12,9 @@ const EducationItem = (x) => (
|
|||||||
<strong>{x.degree}</strong> {x.field}
|
<strong>{x.degree}</strong> {x.field}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-end">
|
<div className="flex flex-col items-end text-right">
|
||||||
{x.startDate && (
|
{x.startDate && (
|
||||||
<h6 className="text-xs font-medium">
|
<h6 className="text-xs font-medium mb-1">
|
||||||
({formatDateRange({ startDate: x.startDate, endDate: x.endDate })})
|
({formatDateRange({ startDate: x.startDate, endDate: x.endDate })})
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
|
|||||||
20
src/templates/blocks/Heading/HeadingB.js
Normal file
20
src/templates/blocks/Heading/HeadingB.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React, { memo, useContext } from 'react';
|
||||||
|
import PageContext from '../../../contexts/PageContext';
|
||||||
|
|
||||||
|
const HeadingB = ({ children }) => {
|
||||||
|
const { data } = useContext(PageContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<h6
|
||||||
|
className="mb-2 border-b-2 pb-1 font-bold uppercase tracking-wide text-sm"
|
||||||
|
style={{
|
||||||
|
color: data.metadata.colors.primary,
|
||||||
|
borderColor: data.metadata.colors.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</h6>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(HeadingB);
|
||||||
11
src/templates/blocks/Heading/HeadingC.js
Normal file
11
src/templates/blocks/Heading/HeadingC.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
|
const HeadingC = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<h6 className="font-bold text-xs uppercase tracking-wide mb-1">
|
||||||
|
{children}
|
||||||
|
</h6>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(HeadingC);
|
||||||
@ -16,7 +16,7 @@ const ProjectItem = (x) => (
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{x.date && (
|
{x.date && (
|
||||||
<h6 className="text-xs font-medium">
|
<h6 className="text-xs font-medium text-right">
|
||||||
{moment(x.date).format('MMMM YYYY')}
|
{moment(x.date).format('MMMM YYYY')}
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
|
|||||||
31
src/templates/blocks/References/ReferencesB.js
Normal file
31
src/templates/blocks/References/ReferencesB.js
Normal file
@ -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) => (
|
||||||
|
<div key={x.id} className="flex flex-col">
|
||||||
|
<h6 className="font-semibold">{x.name}</h6>
|
||||||
|
<span className="text-xs">{x.position}</span>
|
||||||
|
<span className="text-xs">{x.phone}</span>
|
||||||
|
<span className="text-xs">{x.email}</span>
|
||||||
|
{x.summary && (
|
||||||
|
<ReactMarkdown className="markdown mt-2 text-sm" source={x.summary} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const ReferencesB = () => {
|
||||||
|
const { data, heading: Heading } = useContext(PageContext);
|
||||||
|
|
||||||
|
return safetyCheck(data.references) ? (
|
||||||
|
<div>
|
||||||
|
<Heading>{data.references.heading}</Heading>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
{data.references.items.map(ReferenceItem)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ReferencesB);
|
||||||
22
src/templates/blocks/Skills/SkillsB.js
Normal file
22
src/templates/blocks/Skills/SkillsB.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React, { memo, useContext } from 'react';
|
||||||
|
import PageContext from '../../../contexts/PageContext';
|
||||||
|
import { safetyCheck } from '../../../utils';
|
||||||
|
|
||||||
|
const SkillItem = (x) => (
|
||||||
|
<li key={x.id} className="text-sm py-1">
|
||||||
|
{x.skill}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
const SkillsA = () => {
|
||||||
|
const { data, heading: Heading } = useContext(PageContext);
|
||||||
|
|
||||||
|
return safetyCheck(data.skills) ? (
|
||||||
|
<div>
|
||||||
|
<Heading>{data.skills.heading}</Heading>
|
||||||
|
<ul>{data.skills.items.map(SkillItem)}</ul>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(SkillsA);
|
||||||
@ -11,7 +11,7 @@ const WorkItem = (x) => (
|
|||||||
<span className="text-xs">{x.position}</span>
|
<span className="text-xs">{x.position}</span>
|
||||||
</div>
|
</div>
|
||||||
{x.startDate && (
|
{x.startDate && (
|
||||||
<h6 className="text-xs font-medium">
|
<h6 className="text-xs font-medium text-right">
|
||||||
({formatDateRange({ startDate: x.startDate, endDate: x.endDate })})
|
({formatDateRange({ startDate: x.startDate, endDate: x.endDate })})
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -30,6 +30,19 @@ export const getFieldProps = (formik, schema, name) => ({
|
|||||||
...formik.getFieldProps(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) => {
|
export const reorder = (list, startIndex, endIndex) => {
|
||||||
const result = Array.from(list);
|
const result = Array.from(list);
|
||||||
const [removed] = result.splice(startIndex, 1);
|
const [removed] = result.splice(startIndex, 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user