mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-14 08:42:08 +10:00
Merge pull request #52 from AmruthPillai/template/glalie
New Template: Glalie + Updates
This commit is contained in:
4
docs/templates/README.md
vendored
4
docs/templates/README.md
vendored
@ -22,4 +22,8 @@ title: Templates
|
||||
<h2 id="castform"><a href="#castform" class="header-anchor">#</a> Castform</h2>
|
||||
<img src="./images/Castform.png" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 id="glalie"><a href="#glalie" class="header-anchor">#</a> Glalie</h2>
|
||||
<img src="./images/Glalie.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
BIN
docs/templates/images/Glalie.png
vendored
Normal file
BIN
docs/templates/images/Glalie.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 335 KiB |
16
package-lock.json
generated
16
package-lock.json
generated
@ -13943,6 +13943,14 @@
|
||||
"scheduler": "^0.19.1"
|
||||
}
|
||||
},
|
||||
"react-easy-panzoom": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-panzoom/-/react-easy-panzoom-0.4.4.tgz",
|
||||
"integrity": "sha512-1zgT6boDVPcrR3Egcz8KEVpM3fs50o22iIWPRlAqvev0/4nw5RnUNFsvmOJ/b5M2nd8MDGknLmyfBdhjoLB6+g==",
|
||||
"requires": {
|
||||
"warning": "4.0.3"
|
||||
}
|
||||
},
|
||||
"react-error-overlay": {
|
||||
"version": "6.0.7",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
|
||||
@ -17121,6 +17129,14 @@
|
||||
"makeerror": "1.0.x"
|
||||
}
|
||||
},
|
||||
"warning": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"postcss-cli": "^7.1.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-easy-panzoom": "^0.4.4",
|
||||
"react-i18next": "^11.3.4",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-scripts": "3.4.1",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useRef, useEffect, useContext, Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PanZoom } from 'react-easy-panzoom';
|
||||
|
||||
import AppContext from '../../context/AppContext';
|
||||
import PageContext from '../../context/PageContext';
|
||||
@ -8,9 +9,11 @@ import LeftSidebar from '../LeftSidebar/LeftSidebar';
|
||||
import RightSidebar from '../RightSidebar/RightSidebar';
|
||||
|
||||
import templates from '../../templates';
|
||||
import PageController from '../../shared/PageController';
|
||||
|
||||
const App = () => {
|
||||
const pageRef = useRef(null);
|
||||
const panZoomRef = useRef(null);
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const context = useContext(AppContext);
|
||||
@ -18,24 +21,38 @@ const App = () => {
|
||||
const { theme, settings } = state;
|
||||
|
||||
const pageContext = useContext(PageContext);
|
||||
const { setPageElement } = pageContext;
|
||||
const { setPageRef, setPanZoomRef } = pageContext;
|
||||
|
||||
useEffect(() => {
|
||||
setPageElement(pageRef);
|
||||
setPageRef(pageRef);
|
||||
setPanZoomRef(panZoomRef);
|
||||
i18n.changeLanguage(settings.language);
|
||||
const storedState = JSON.parse(localStorage.getItem('state'));
|
||||
dispatch({ type: 'import_data', payload: storedState });
|
||||
}, [dispatch, setPageElement, i18n, settings.language]);
|
||||
}, [dispatch, setPageRef, setPanZoomRef, i18n, settings.language]);
|
||||
|
||||
return (
|
||||
<Suspense fallback="Loading...">
|
||||
<div className="h-screen overflow-hidden grid grid-cols-5 items-center">
|
||||
<div className="h-screen grid grid-cols-5 items-center">
|
||||
<LeftSidebar />
|
||||
|
||||
<div className="z-0 h-screen col-span-3 flex overflow-scroll justify-center items-center">
|
||||
<div id="page" ref={pageRef} className="shadow-2xl">
|
||||
{templates.find(x => theme.layout.toLowerCase() === x.key).component()}
|
||||
</div>
|
||||
<div className="relative z-0 h-screen overflow-hidden col-span-3 flex justify-center items-center">
|
||||
<PanZoom
|
||||
ref={panZoomRef}
|
||||
minZoom="0.4"
|
||||
autoCenter
|
||||
autoCenterZoomLevel={0.7}
|
||||
enableBoundingBox
|
||||
boundaryRatioVertical={0.8}
|
||||
boundaryRatioHorizontal={0.8}
|
||||
style={{ outline: 'none' }}
|
||||
>
|
||||
<div id="page" ref={pageRef} className="shadow-2xl break-words">
|
||||
{templates.find(x => theme.layout.toLowerCase() === x.key).component()}
|
||||
</div>
|
||||
</PanZoom>
|
||||
|
||||
<PageController />
|
||||
</div>
|
||||
|
||||
<RightSidebar />
|
||||
|
||||
@ -13,7 +13,6 @@ import AddItemButton from '../../../shared/AddItemButton';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const AwardsTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -28,7 +27,7 @@ const AwardsTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.awards.heading}
|
||||
onChange={v => onChange('data.awards.heading', v)}
|
||||
/>
|
||||
@ -62,7 +61,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('awards.title.label')}
|
||||
placeholder={t('awards.title.placeholder')}
|
||||
placeholder="Code For Good Hackathon"
|
||||
value={item.title}
|
||||
onChange={v => onChange(`${identifier}title`, v)}
|
||||
/>
|
||||
@ -70,7 +69,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('awards.subtitle.label')}
|
||||
placeholder={t('awards.subtitle.placeholder')}
|
||||
placeholder="First Place, National Level"
|
||||
value={item.subtitle}
|
||||
onChange={v => onChange(`${identifier}subtitle`, v)}
|
||||
/>
|
||||
@ -78,7 +77,6 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextArea
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
placeholder={t('awards.description.placeholder')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
|
||||
@ -13,7 +13,6 @@ import ItemHeading from '../../../shared/ItemHeading';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
|
||||
const CertificationsTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -28,7 +27,7 @@ const CertificationsTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.certifications.heading}
|
||||
onChange={v => onChange('data.certifications.heading', v)}
|
||||
/>
|
||||
@ -62,7 +61,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('certifications.title.label')}
|
||||
placeholder={t('certifications.title.placeholder')}
|
||||
placeholder="CS50: Intro to Computer Science"
|
||||
value={item.title}
|
||||
onChange={v => onChange(`${identifier}title`, v)}
|
||||
/>
|
||||
@ -70,7 +69,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('certifications.subtitle.label')}
|
||||
placeholder={t('certifications.subtitle.placeholder')}
|
||||
placeholder="Harvard University"
|
||||
value={item.subtitle}
|
||||
onChange={v => onChange(`${identifier}subtitle`, v)}
|
||||
/>
|
||||
@ -78,7 +77,6 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextArea
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
placeholder={t('certifications.description.placeholder')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
|
||||
@ -13,7 +13,6 @@ import AddItemButton from '../../../shared/AddItemButton';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const EducationTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -28,7 +27,7 @@ const EducationTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.education.heading}
|
||||
onChange={v => onChange('data.education.heading', v)}
|
||||
/>
|
||||
@ -62,7 +61,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('education.name.label')}
|
||||
placeholder={t('education.name.placeholder')}
|
||||
placeholder="Harvard University"
|
||||
value={item.name}
|
||||
onChange={v => onChange(`${identifier}name`, v)}
|
||||
/>
|
||||
@ -70,7 +69,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('education.major.label')}
|
||||
placeholder={t('education.major.placeholder')}
|
||||
placeholder="Masters in Computer Science"
|
||||
value={item.major}
|
||||
onChange={v => onChange(`${identifier}major`, v)}
|
||||
/>
|
||||
@ -87,7 +86,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.startDate.label')}
|
||||
placeholder={t('app:item.startDate.placeholder')}
|
||||
placeholder="March 2018"
|
||||
value={item.start}
|
||||
onChange={v => onChange(`${identifier}start`, v)}
|
||||
/>
|
||||
@ -95,7 +94,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.endDate.label')}
|
||||
placeholder={t('app:item.endDate.placeholder')}
|
||||
placeholder="June 2022"
|
||||
value={item.end}
|
||||
onChange={v => onChange(`${identifier}end`, v)}
|
||||
/>
|
||||
@ -105,7 +104,6 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
rows="5"
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
placeholder={t('education.description.placeholder')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
|
||||
@ -12,7 +12,6 @@ import ItemHeading from '../../../shared/ItemHeading';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
|
||||
const ExtrasTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -27,7 +26,7 @@ const ExtrasTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.extras.heading}
|
||||
onChange={v => onChange('data.extras.heading', v)}
|
||||
/>
|
||||
@ -61,7 +60,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('extras.key.label')}
|
||||
placeholder={t('extras.key.placeholder')}
|
||||
placeholder="Date of Birth"
|
||||
value={item.key}
|
||||
onChange={v => onChange(`${identifier}key`, v)}
|
||||
/>
|
||||
@ -69,7 +68,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('extras.value.label')}
|
||||
placeholder={t('extras.value.placeholder')}
|
||||
placeholder="6th August 1995"
|
||||
value={item.value}
|
||||
onChange={v => onChange(`${identifier}value`, v)}
|
||||
/>
|
||||
|
||||
@ -13,7 +13,6 @@ import AddItemButton from '../../../shared/AddItemButton';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const LanguagesTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation('app');
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -47,7 +46,7 @@ const LanguagesTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.languages.heading}
|
||||
onChange={v => onChange('data.languages.heading', v)}
|
||||
/>
|
||||
@ -82,7 +81,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('languages.key.label')}
|
||||
placeholder={t('languages.key.placeholder')}
|
||||
placeholder="English"
|
||||
value={item.key}
|
||||
onChange={v => onChange(`${identifier}key`, v)}
|
||||
/>
|
||||
|
||||
@ -6,7 +6,7 @@ import TextField from '../../../shared/TextField';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
|
||||
const ObjectiveTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation(['leftSidebar', 'app']);
|
||||
const { t } = useTranslation('leftSidebar');
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -19,7 +19,7 @@ const ObjectiveTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('app:heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.objective.heading}
|
||||
onChange={v => onChange('data.objective.heading', v)}
|
||||
/>
|
||||
@ -32,8 +32,8 @@ const ObjectiveTab = ({ data, onChange }) => {
|
||||
rows="15"
|
||||
className="mb-4"
|
||||
label={t('objective.objective.label')}
|
||||
placeholder={t('objective.objective.placeholder')}
|
||||
value={data.objective.body}
|
||||
placeholder="Looking for a challenging role in a reputable organization to utilize my technical, database, and management skills for the growth of the organization as well as to enhance my knowledge about new and emerging trends in the IT sector."
|
||||
onChange={v => onChange('data.objective.body', v)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -20,7 +20,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.firstName.label')}
|
||||
placeholder={t('profile.firstName.placeholder')}
|
||||
placeholder="Jane"
|
||||
value={data.profile.firstName}
|
||||
onChange={v => onChange('data.profile.firstName', v)}
|
||||
/>
|
||||
@ -28,7 +28,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.lastName.label')}
|
||||
placeholder={t('profile.lastName.placeholder')}
|
||||
placeholder="Doe"
|
||||
value={data.profile.lastName}
|
||||
onChange={v => onChange('data.profile.lastName', v)}
|
||||
/>
|
||||
@ -37,7 +37,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.subtitle.label')}
|
||||
placeholder={t('profile.subtitle.placeholder')}
|
||||
placeholder="Full-Stack Web Developer"
|
||||
value={data.profile.subtitle}
|
||||
onChange={v => onChange('data.profile.subtitle', v)}
|
||||
/>
|
||||
@ -47,7 +47,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.address.line1.label')}
|
||||
placeholder={t('profile.address.line1.placeholder')}
|
||||
placeholder="Palladium Complex"
|
||||
value={data.profile.address.line1}
|
||||
onChange={v => onChange('data.profile.address.line1', v)}
|
||||
/>
|
||||
@ -55,7 +55,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.address.line2.label')}
|
||||
placeholder={t('profile.address.line2.placeholder')}
|
||||
placeholder="140 E 14th St"
|
||||
value={data.profile.address.line2}
|
||||
onChange={v => onChange('data.profile.address.line2', v)}
|
||||
/>
|
||||
@ -63,7 +63,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.address.line3.label')}
|
||||
placeholder={t('profile.address.line3.placeholder')}
|
||||
placeholder="New York, NY 10003 USA"
|
||||
value={data.profile.address.line3}
|
||||
onChange={v => onChange('data.profile.address.line3', v)}
|
||||
/>
|
||||
@ -81,7 +81,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.website.label')}
|
||||
placeholder="google.com"
|
||||
placeholder="janedoe.me"
|
||||
value={data.profile.website}
|
||||
onChange={v => onChange('data.profile.website', v)}
|
||||
/>
|
||||
@ -89,7 +89,7 @@ const ProfileTab = ({ data, onChange }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.email.label')}
|
||||
placeholder="john.doe@example.com"
|
||||
placeholder="jane.doe@example.com"
|
||||
value={data.profile.email}
|
||||
onChange={v => onChange('data.profile.email', v)}
|
||||
/>
|
||||
|
||||
@ -13,7 +13,6 @@ import ItemHeading from '../../../shared/ItemHeading';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
|
||||
const ReferencesTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -47,7 +46,7 @@ const ReferencesTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.references.heading}
|
||||
onChange={v => onChange('data.references.heading', v)}
|
||||
/>
|
||||
@ -82,7 +81,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('references.name.label')}
|
||||
placeholder={t('references.name.placeholder')}
|
||||
placeholder="Richard Hendricks"
|
||||
value={item.name}
|
||||
onChange={v => onChange(`${identifier}name`, v)}
|
||||
/>
|
||||
@ -90,7 +89,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('references.position.label')}
|
||||
placeholder={t('references.position.placeholder')}
|
||||
placeholder="CEO, Pied Piper"
|
||||
value={item.position}
|
||||
onChange={v => onChange(`${identifier}position`, v)}
|
||||
/>
|
||||
@ -115,7 +114,6 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
rows="5"
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
placeholder={t('references.description.placeholder')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
@ -8,7 +7,6 @@ import { addItem, deleteItem } from '../../../utils';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const SkillsTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -23,7 +21,7 @@ const SkillsTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.skills.heading}
|
||||
onChange={v => onChange('data.skills.heading', v)}
|
||||
/>
|
||||
@ -42,12 +40,10 @@ const SkillsTab = ({ data, onChange }) => {
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange }) => {
|
||||
const { t } = useTranslation('leftSidebar');
|
||||
|
||||
return (
|
||||
<input
|
||||
className="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
||||
placeholder={t('skills.item.placeholder')}
|
||||
placeholder="Team Building & Training"
|
||||
value={item}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
type="text"
|
||||
|
||||
@ -13,7 +13,6 @@ import AddItemButton from '../../../shared/AddItemButton';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const WorkTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation();
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
@ -25,7 +24,7 @@ const WorkTab = ({ data, onChange }) => {
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder={t('heading.placeholder')}
|
||||
placeholder="Heading"
|
||||
value={data.work.heading}
|
||||
onChange={v => onChange('data.work.heading', v)}
|
||||
/>
|
||||
@ -59,7 +58,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('work.name.label')}
|
||||
placeholder={t('work.name.placeholder')}
|
||||
placeholder="Amazon"
|
||||
value={item.title}
|
||||
onChange={v => onChange(`${identifier}title`, v)}
|
||||
/>
|
||||
@ -67,7 +66,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('work.role.label')}
|
||||
placeholder={t('work.role.placeholder')}
|
||||
placeholder="Full-Stack Web Developer"
|
||||
value={item.role}
|
||||
onChange={v => onChange(`${identifier}role`, v)}
|
||||
/>
|
||||
@ -76,7 +75,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.startDate.label')}
|
||||
placeholder={t('app:item.startDate.placeholder')}
|
||||
placeholder="March 2018"
|
||||
value={item.start}
|
||||
onChange={v => onChange(`${identifier}start`, v)}
|
||||
/>
|
||||
@ -84,7 +83,7 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.endDate.label')}
|
||||
placeholder={t('app:item.endDate.placeholder')}
|
||||
placeholder="June 2022"
|
||||
value={item.end}
|
||||
onChange={v => onChange(`${identifier}end`, v)}
|
||||
/>
|
||||
@ -94,7 +93,6 @@ const Form = ({ item, onChange, identifier = '' }) => {
|
||||
rows="5"
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
placeholder={t('work.description.placeholder')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
|
||||
@ -101,7 +101,7 @@ const AboutTab = () => {
|
||||
<div className="mt-5">
|
||||
<p className="text-xs font-gray-600 text-center">
|
||||
<Trans t={t} i18nKey="about.footer.credit">
|
||||
Reactive Resume is a project by
|
||||
Made with Love by
|
||||
<a
|
||||
className="font-bold hover:underline"
|
||||
href="https://www.amruthpillai.com/"
|
||||
@ -110,7 +110,6 @@ const AboutTab = () => {
|
||||
>
|
||||
Amruth Pillai
|
||||
</a>
|
||||
.
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="text-xs font-gray-600 text-center">{t('about.footer.thanks')}</p>
|
||||
|
||||
@ -2,60 +2,17 @@
|
||||
/* eslint-disable jsx-a11y/anchor-has-content */
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
import React, { useRef, useContext } from 'react';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import html2canvas from 'html2canvas';
|
||||
import * as jsPDF from 'jspdf';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import PageContext from '../../../context/PageContext';
|
||||
import { importJson, saveAsPdf } from '../../../utils';
|
||||
|
||||
const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
const pageContext = useContext(PageContext);
|
||||
const { pageElement } = pageContext;
|
||||
const { pageRef, panZoomRef } = pageContext;
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
const importJson = event => {
|
||||
const fr = new FileReader();
|
||||
fr.addEventListener('load', () => {
|
||||
const importedObject = JSON.parse(fr.result);
|
||||
dispatch({ type: 'import_data', payload: importedObject });
|
||||
dispatch({ type: 'save_data' });
|
||||
});
|
||||
fr.readAsText(event.target.files[0]);
|
||||
};
|
||||
|
||||
const printAsPdf = () => {
|
||||
pageElement.current.style.display = 'table';
|
||||
pageElement.current.style.overflow = 'visible';
|
||||
|
||||
html2canvas(pageElement.current, {
|
||||
scale: 5,
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
}).then(canvas => {
|
||||
const image = canvas.toDataURL('image/jpeg', 1.0);
|
||||
const doc = new jsPDF('p', 'mm', 'a4');
|
||||
const pageWidth = doc.internal.pageSize.getWidth();
|
||||
const pageHeight = doc.internal.pageSize.getHeight();
|
||||
|
||||
const widthRatio = pageWidth / canvas.width;
|
||||
const heightRatio = pageHeight / canvas.height;
|
||||
const ratio = widthRatio > heightRatio ? heightRatio : widthRatio;
|
||||
|
||||
const canvasWidth = canvas.width * ratio;
|
||||
const canvasHeight = canvas.height * ratio;
|
||||
|
||||
const marginX = (pageWidth - canvasWidth) / 2;
|
||||
const marginY = (pageHeight - canvasHeight) / 2;
|
||||
|
||||
pageElement.current.style.display = 'block';
|
||||
pageElement.current.style.overflow = 'scroll';
|
||||
|
||||
doc.addImage(image, 'JPEG', marginX, marginY, canvasWidth, canvasHeight, null, 'SLOW');
|
||||
doc.save(`RxResume_${Date.now()}.pdf`);
|
||||
});
|
||||
};
|
||||
|
||||
const exportToJson = () => {
|
||||
const backupObj = { data, theme };
|
||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(backupObj))}`;
|
||||
@ -86,7 +43,12 @@ const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
|
||||
<p className="text-sm">{t('actions.importExport.body')}</p>
|
||||
|
||||
<input ref={fileInputRef} type="file" className="hidden" onChange={importJson} />
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
className="hidden"
|
||||
onChange={e => importJson(e, dispatch)}
|
||||
/>
|
||||
<a id="downloadAnchor" className="hidden" />
|
||||
|
||||
<div className="mt-4 grid grid-cols-2 col-gap-6">
|
||||
@ -117,39 +79,19 @@ const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('actions.printResume.heading')}</h6>
|
||||
<h6 className="font-bold text-sm mb-2">{t('actions.downloadResume.heading')}</h6>
|
||||
<div className="text-sm">{t('actions.downloadResume.body')}</div>
|
||||
|
||||
<div className="text-sm">
|
||||
<Trans t={t} i18nKey="actions.printResume.body">
|
||||
You can click on the button below to generate a PDF instantly. Alternatively, you can
|
||||
also use <pre className="inline font-bold">Cmd/Ctrl + P</pre> but it would have
|
||||
different effects.
|
||||
</Trans>
|
||||
</div>
|
||||
|
||||
<div className="mt-1 grid grid-cols-2 col-gap-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={printAsPdf}
|
||||
className="mt-4 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium py-2 px-5 rounded"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<i className="material-icons mr-2 font-bold text-base">import_export</i>
|
||||
<span className="text-sm">{t('actions.printResume.buttons.export')}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={printAsPdf}
|
||||
className="mt-4 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium py-2 px-5 rounded"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<i className="material-icons mr-2 font-bold text-base">print</i>
|
||||
<span className="text-sm">{t('actions.printResume.buttons.print')}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => saveAsPdf(pageRef, panZoomRef)}
|
||||
className="mt-4 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium py-2 px-5 rounded"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<i className="material-icons mr-2 font-bold text-base">save</i>
|
||||
<span className="text-sm">{t('actions.downloadResume.buttons.saveAsPdf')}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
@ -4,12 +4,16 @@ const PageContext = React.createContext(null);
|
||||
const { Provider } = PageContext;
|
||||
|
||||
const StateProvider = ({ children }) => {
|
||||
const [pageElement, setPageElement] = useState(null);
|
||||
const [panZoomRef, setPanZoomRef] = useState(null);
|
||||
const [pageRef, setPageRef] = useState(null);
|
||||
|
||||
return (
|
||||
<Provider
|
||||
value={{
|
||||
pageElement,
|
||||
setPageElement,
|
||||
pageRef,
|
||||
setPageRef,
|
||||
panZoomRef,
|
||||
setPanZoomRef,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
{
|
||||
"heading": {
|
||||
"placeholder": "Heading"
|
||||
},
|
||||
"item": {
|
||||
"add": "Add {{- heading}}",
|
||||
"startDate": {
|
||||
"label": "Start Date",
|
||||
"placeholder": "March 2018"
|
||||
"label": "Start Date"
|
||||
},
|
||||
"endDate": {
|
||||
"label": "End Date",
|
||||
"placeholder": "March 2022"
|
||||
"label": "End Date"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description"
|
||||
|
||||
@ -3,7 +3,9 @@ import leftSidebar from './leftSidebar';
|
||||
import rightSidebar from './rightSidebar';
|
||||
|
||||
export default {
|
||||
app,
|
||||
leftSidebar,
|
||||
rightSidebar,
|
||||
en: {
|
||||
app,
|
||||
leftSidebar,
|
||||
rightSidebar,
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
{
|
||||
"title": {
|
||||
"label": "Title",
|
||||
"placeholder": "Math & Science Olympiad"
|
||||
"label": "Title"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "Subtitle",
|
||||
"placeholder": "First Place, International Level"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "You can write about what qualities made you succeed in getting this award."
|
||||
"label": "Subtitle"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
{
|
||||
"title": {
|
||||
"label": "Title",
|
||||
"placeholder": "Android Development Nanodegree"
|
||||
"label": "Name"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "Subtitle",
|
||||
"placeholder": "Udacity"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "You can write about what you learned from your certification program."
|
||||
"label": "Authority"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
{
|
||||
"name": {
|
||||
"label": "Name",
|
||||
"placeholder": "Harvard University"
|
||||
"label": "Name"
|
||||
},
|
||||
"major": {
|
||||
"label": "Major",
|
||||
"placeholder": "Masters in Computer Science"
|
||||
"label": "Major"
|
||||
},
|
||||
"grade": {
|
||||
"label": "Grade"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "You can write about projects or special credit classes that you took while studying at this school."
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
{
|
||||
"key": {
|
||||
"label": "Key",
|
||||
"placeholder": "Date of Birth"
|
||||
"label": "Key"
|
||||
},
|
||||
"value": {
|
||||
"label": "Value",
|
||||
"placeholder": "6th August 1995"
|
||||
"label": "Value"
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ import work from './work.json';
|
||||
import education from './education.json';
|
||||
import awards from './awards.json';
|
||||
import certifications from './certifications.json';
|
||||
import skills from './skills.json';
|
||||
import languages from './languages.json';
|
||||
import references from './references.json';
|
||||
import extras from './extras.json';
|
||||
@ -16,7 +15,6 @@ export default {
|
||||
education,
|
||||
awards,
|
||||
certifications,
|
||||
skills,
|
||||
languages,
|
||||
references,
|
||||
extras,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"key": {
|
||||
"label": "Key",
|
||||
"placeholder": "Dothraki"
|
||||
"label": "Name"
|
||||
},
|
||||
"rating": {
|
||||
"label": "Rating"
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
{
|
||||
"objective": {
|
||||
"label": "Objective",
|
||||
"placeholder": "Looking for a challenging role in a reputable organization to utilize my technical, database, and management skills for the growth of the organization as well as to enhance my knowledge about new and emerging trends in the IT sector."
|
||||
"label": "Objective"
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,29 +3,24 @@
|
||||
"label": "Photo URL"
|
||||
},
|
||||
"firstName": {
|
||||
"label": "First Name",
|
||||
"placeholder": "Jane"
|
||||
"label": "First Name"
|
||||
},
|
||||
"lastName": {
|
||||
"label": "Last Name",
|
||||
"placeholder": "Doe"
|
||||
"label": "Last Name"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "Subtitle",
|
||||
"placeholder": "Full Stack Web Developer"
|
||||
"label": "Subtitle"
|
||||
},
|
||||
"address": {
|
||||
"label": "Address",
|
||||
"line1": {
|
||||
"label": "Address Line 1",
|
||||
"placeholder": "Palladium Complex"
|
||||
"label": "Address Line 1"
|
||||
},
|
||||
"line2": {
|
||||
"label": "Address Line 2",
|
||||
"placeholder": "140 E 14th St"
|
||||
"label": "Address Line 2"
|
||||
},
|
||||
"line3": {
|
||||
"label": "Address Line 3",
|
||||
"placeholder": "New York, NY 10003 USA"
|
||||
"label": "Address Line 3"
|
||||
}
|
||||
},
|
||||
"phone": {
|
||||
|
||||
@ -1,19 +1,14 @@
|
||||
{
|
||||
"name": {
|
||||
"label": "Name",
|
||||
"placeholder": "Richard Hendricks"
|
||||
"label": "Name"
|
||||
},
|
||||
"position": {
|
||||
"label": "Position",
|
||||
"placeholder": "CEO, Pied Piper"
|
||||
"label": "Position"
|
||||
},
|
||||
"phone": {
|
||||
"label": "Phone Number"
|
||||
},
|
||||
"email": {
|
||||
"label": "Email Address"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "You can write about how you and the reference contact worked together and which projects you were a part of."
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"item": {
|
||||
"placeholder": "Cooking"
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,8 @@
|
||||
{
|
||||
"name": {
|
||||
"label": "Name",
|
||||
"placeholder": "Amazon"
|
||||
"label": "Name"
|
||||
},
|
||||
"role": {
|
||||
"label": "Role",
|
||||
"placeholder": "Front-end Web Developer"
|
||||
},
|
||||
"description": {
|
||||
"placeholder": "You can write about what you specialized in while working at the company and what projects you were a part of."
|
||||
"label": "Role"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"title": "About",
|
||||
"documentation": {
|
||||
"heading": "Documentation",
|
||||
"body": "Want to know more about the app? Wouldn't it be nice if there was a guide to setting it up on your local machine? Need information on how to contribute to the project? Look no further, there's comprehensive documentation made just for you.",
|
||||
"body": "Want to know more about the app? Need information on how to contribute to the project? Look no further, there's comprehensive guide made just for you.",
|
||||
"buttons": {
|
||||
"documentation": "Documentation"
|
||||
}
|
||||
@ -30,7 +30,7 @@
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"credit": "Reactive Resume is a project by <1>Amruth Pillai</1>.",
|
||||
"credit": "Made with Love by <1>Amruth Pillai</1>",
|
||||
"thanks": "Thank you for using Reactive Resume!"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,12 +9,11 @@
|
||||
"export": "Export"
|
||||
}
|
||||
},
|
||||
"printResume": {
|
||||
"heading": "Print Your Resume",
|
||||
"body": "You can click on the button below to generate a PDF instantly. Alternatively, you can also use <1>Cmd/Ctrl + P</1> but it would have different effects.",
|
||||
"downloadResume": {
|
||||
"heading": "Download Your Resume",
|
||||
"body": "You can click on the button below to download a PDF version of your resume instantly. For best results, please use the latest version of Google Chrome.",
|
||||
"buttons": {
|
||||
"export": "Export",
|
||||
"print": "Print"
|
||||
"saveAsPdf": "Save as PDF"
|
||||
}
|
||||
},
|
||||
"loadDemoData": {
|
||||
|
||||
@ -2,6 +2,6 @@
|
||||
"title": "Colors",
|
||||
"colorOptions": "Color Options",
|
||||
"primaryColor": "Primary Color",
|
||||
"accentColor": "Accent Color",
|
||||
"accentColor": "Secondary Color",
|
||||
"clipboardCopyAction": "{{color}} has been copied to the clipboard."
|
||||
}
|
||||
|
||||
@ -8,6 +8,15 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Titillium+Web:wght@400;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;500;700&display=swap');
|
||||
|
||||
* {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
@ -54,25 +63,21 @@ ul li {
|
||||
|
||||
#tabs {
|
||||
scroll-behavior: smooth;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
#tabs::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#page {
|
||||
width: 21cm;
|
||||
min-height: 29.7cm;
|
||||
max-height: 29.7cm;
|
||||
transform: scale(0.8);
|
||||
transform-origin: center;
|
||||
overflow: scroll;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#pageController {
|
||||
bottom: 25px;
|
||||
}
|
||||
|
||||
#pageController > div {
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@page {
|
||||
|
||||
58
src/shared/PageController.js
Normal file
58
src/shared/PageController.js
Normal file
@ -0,0 +1,58 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PageContext from '../context/PageContext';
|
||||
import { saveAsPdf } from '../utils';
|
||||
|
||||
const PageController = () => {
|
||||
const pageContext = useContext(PageContext);
|
||||
const { pageRef, panZoomRef } = pageContext;
|
||||
|
||||
const zoomIn = () => panZoomRef.current.zoomIn(2);
|
||||
const zoomOut = () => panZoomRef.current.zoomOut(2);
|
||||
const centerReset = () => {
|
||||
panZoomRef.current.autoCenter(1);
|
||||
panZoomRef.current.reset(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
id="pageController"
|
||||
className="absolute z-20 opacity-75 hover:opacity-100 transition-all duration-150"
|
||||
>
|
||||
<div className="px-8 border border-gray-200 rounded-full bg-white flex justify-center items-center select-none">
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex" onClick={zoomIn}>
|
||||
<i className="material-icons">zoom_in</i>
|
||||
</div>
|
||||
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex" onClick={zoomOut}>
|
||||
<i className="material-icons">zoom_out</i>
|
||||
</div>
|
||||
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex" onClick={centerReset}>
|
||||
<i className="material-icons">center_focus_strong</i>
|
||||
</div>
|
||||
|
||||
<div className="text-gray-400 p-3">|</div>
|
||||
|
||||
<div
|
||||
className="p-3 hover:bg-gray-200 cursor-pointer flex"
|
||||
onClick={() => saveAsPdf(pageRef, panZoomRef)}
|
||||
>
|
||||
<i className="material-icons">save</i>
|
||||
</div>
|
||||
|
||||
<div className="text-gray-400 p-3">|</div>
|
||||
|
||||
<a
|
||||
className="p-3 hover:bg-gray-200 cursor-pointer flex"
|
||||
href="https://docs.rxresu.me/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<i className="material-icons">help_outline</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageController;
|
||||
@ -117,7 +117,7 @@ const Gengar = () => {
|
||||
<div key={x.title} className="mb-3">
|
||||
<h6 className="font-semibold">{x.title}</h6>
|
||||
<p className="text-xs">{x.subtitle}</p>
|
||||
<ReactMarkdown className="mt-2 text-sm break-words" source={x.description} />
|
||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
298
src/templates/glalie/Glalie.js
Normal file
298
src/templates/glalie/Glalie.js
Normal file
@ -0,0 +1,298 @@
|
||||
import React, { useContext } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
import AppContext from '../../context/AppContext';
|
||||
import { hexToRgb } from '../../utils';
|
||||
|
||||
const Glalie = () => {
|
||||
const context = useContext(AppContext);
|
||||
const { state } = context;
|
||||
const { data, theme } = state;
|
||||
|
||||
const { r, g, b } = hexToRgb(theme.colors.accent) || {};
|
||||
|
||||
const Photo = () =>
|
||||
data.profile.photo !== '' && (
|
||||
<img
|
||||
className="w-40 h-40 rounded-full mx-auto"
|
||||
src={data.profile.photo}
|
||||
alt="Resume Photograph"
|
||||
/>
|
||||
);
|
||||
|
||||
const FullName = () => (
|
||||
<div className="text-4xl font-bold leading-none">
|
||||
<h1>{data.profile.firstName}</h1>
|
||||
<h1>{data.profile.lastName}</h1>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Subtitle = () => (
|
||||
<div className="tracking-wide text-xs uppercase font-medium">{data.profile.subtitle}</div>
|
||||
);
|
||||
|
||||
const Divider = () => (
|
||||
<div className="w-1/2 mx-auto my-2 border-b-2" style={{ borderColor: theme.colors.accent }} />
|
||||
);
|
||||
|
||||
const ContactItem = ({ title, value }) =>
|
||||
value && (
|
||||
<div className="flex flex-col">
|
||||
<h6 className="text-xs font-bold" style={{ color: theme.colors.accent }}>
|
||||
{title}
|
||||
</h6>
|
||||
<p className="text-sm">{value}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ContactInformation = () => (
|
||||
<div
|
||||
className="w-full border-2 pl-4 pr-4 pb-6"
|
||||
style={{
|
||||
borderColor: theme.colors.accent,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="inline-block relative px-4"
|
||||
style={{ top: '-.9em', color: theme.colors.accent }}
|
||||
>
|
||||
<h2 className="flex">
|
||||
<i className="material-icons">flare</i>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 row-gap-4">
|
||||
<ContactItem title="Phone Number" value={data.profile.phone} />
|
||||
<ContactItem title="Email Address" value={data.profile.email} />
|
||||
<ContactItem title="Website" value={data.profile.website} />
|
||||
|
||||
<div className="flex flex-col">
|
||||
<i className="material-icons text-lg" style={{ color: theme.colors.accent }}>
|
||||
home
|
||||
</i>
|
||||
<p className="text-sm">{data.profile.address.line1}</p>
|
||||
<p className="text-sm">{data.profile.address.line2}</p>
|
||||
<p className="text-sm">{data.profile.address.line3}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Heading = ({ title }) => (
|
||||
<h6
|
||||
className="text-sm font-semibold uppercase pb-1 mb-2 border-b"
|
||||
style={{ borderColor: theme.colors.accent, color: theme.colors.accent }}
|
||||
>
|
||||
{title}
|
||||
</h6>
|
||||
);
|
||||
|
||||
const Objective = () =>
|
||||
data.objective.enable && (
|
||||
<div>
|
||||
<Heading title={data.objective.heading} />
|
||||
<p className="text-sm text-justify">{data.objective.body}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const WorkItem = x => (
|
||||
<div key={x.title} className="mt-3">
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<h6 className="font-semibold text-sm">{x.title}</h6>
|
||||
<p className="text-xs opacity-75 font-medium">
|
||||
{x.role} / {x.start} - {x.end}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const Work = () =>
|
||||
data.work &&
|
||||
data.work.enable && (
|
||||
<div>
|
||||
<Heading title={data.work.heading} />
|
||||
{data.work.items.filter(x => x.enable).map(WorkItem)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const EducationItem = x => (
|
||||
<div key={x.name} className="mt-3">
|
||||
<div>
|
||||
<h6 className="font-semibold text-xs">{x.name}</h6>
|
||||
<p className="text-xs opacity-75">{x.major}</p>
|
||||
<p className="text-xs opacity-75">
|
||||
{x.start} - {x.end}
|
||||
</p>
|
||||
</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="grid grid-cols-2 gap-4">
|
||||
{data.education.items.filter(x => x.enable).map(EducationItem)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AwardItem = x => (
|
||||
<div key={x.title} className="mt-3 text-left">
|
||||
<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} />
|
||||
{data.awards.items.filter(x => x.enable).map(AwardItem)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const CertificationItem = x => (
|
||||
<div key={x.title} className="mt-3 text-left">
|
||||
<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} />
|
||||
{data.certifications.items.filter(x => x.enable).map(CertificationItem)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const SkillItem = x => (
|
||||
<li key={x} className="text-xs font-medium">
|
||||
{x}
|
||||
</li>
|
||||
);
|
||||
|
||||
const Skills = () =>
|
||||
data.skills &&
|
||||
data.skills.enable && (
|
||||
<div>
|
||||
<Heading title={data.skills.heading} />
|
||||
<ul className="pt-2 grid grid-cols-2 gap-3">{data.skills.items.map(SkillItem)}</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
const LanguageItem = x => (
|
||||
<div key={x.id} className="grid grid-cols-2 items-center py-2">
|
||||
<h6 className="text-xs font-medium text-left">{x.key}</h6>
|
||||
<div className="flex">
|
||||
{Array.from(Array(x.value)).map((_, i) => (
|
||||
<i key={i} className="material-icons text-lg" style={{ color: theme.colors.accent }}>
|
||||
star
|
||||
</i>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Languages = () =>
|
||||
data.languages &&
|
||||
data.languages.enable && (
|
||||
<div>
|
||||
<Heading title={data.languages.heading} />
|
||||
<div className="w-3/4">{data.languages.items.filter(x => x.enable).map(LanguageItem)}</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-3 gap-8">
|
||||
{data.references.items.filter(x => x.enable).map(ReferenceItem)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ExtraItem = x => (
|
||||
<tr key={x.id}>
|
||||
<td className="border font-medium px-4 py-2 text-xs">{x.key}</td>
|
||||
<td className="border px-4 py-2 text-xs">{x.value}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
const Extras = () =>
|
||||
data.extras &&
|
||||
data.extras.enable && (
|
||||
<div>
|
||||
<Heading title={data.extras.heading} />
|
||||
<table className="mt-4 w-2/3 table-auto">
|
||||
<tbody>{data.extras.items.filter(x => x.enable).map(ExtraItem)}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
fontFamily: theme.font.family,
|
||||
backgroundColor: theme.colors.background,
|
||||
color: theme.colors.primary,
|
||||
}}
|
||||
>
|
||||
<div className="grid grid-cols-12">
|
||||
<div
|
||||
className="h-full col-span-4 p-8 grid grid-cols-1 row-gap-8 text-center"
|
||||
style={{ backgroundColor: `rgba(${r}, ${g}, ${b}, 0.1)` }}
|
||||
>
|
||||
<div className="grid grid-cols-1 row-gap-4">
|
||||
<Photo />
|
||||
<FullName />
|
||||
<Subtitle />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
<ContactInformation />
|
||||
<Divider />
|
||||
|
||||
<Objective />
|
||||
<Languages />
|
||||
<Certifications />
|
||||
</div>
|
||||
|
||||
<div className="col-span-8 p-8">
|
||||
<div className="grid grid-cols-1 row-gap-6">
|
||||
<Work />
|
||||
<Education />
|
||||
<Skills />
|
||||
<Awards />
|
||||
<References />
|
||||
<Extras />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Glalie;
|
||||
5
src/templates/glalie/index.js
Normal file
5
src/templates/glalie/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import Glalie from './Glalie';
|
||||
import image from './preview.png';
|
||||
|
||||
export const Image = image;
|
||||
export default Glalie;
|
||||
BIN
src/templates/glalie/preview.png
Normal file
BIN
src/templates/glalie/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 335 KiB |
@ -2,6 +2,7 @@ import Onyx, { Image as OnyxPreview } from './onyx';
|
||||
import Pikachu, { Image as PikachuPreview } from './pikachu';
|
||||
import Gengar, { Image as GengarPreview } from './gengar';
|
||||
import Castform, { Image as CastformPreview } from './castform';
|
||||
import Glalie, { Image as GlaliePreview } from './glalie';
|
||||
|
||||
export default [
|
||||
{
|
||||
@ -28,4 +29,10 @@ export default [
|
||||
component: Castform,
|
||||
preview: CastformPreview,
|
||||
},
|
||||
{
|
||||
key: 'glalie',
|
||||
name: 'Glalie',
|
||||
component: Glalie,
|
||||
preview: GlaliePreview,
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
/* eslint-disable new-cap */
|
||||
import html2canvas from 'html2canvas';
|
||||
import * as jsPDF from 'jspdf';
|
||||
|
||||
const move = (array, element, delta) => {
|
||||
const index = array.indexOf(element);
|
||||
const newIndex = index + delta;
|
||||
@ -90,4 +94,58 @@ const moveItemDown = (dispatch, key, value) => {
|
||||
saveData(dispatch);
|
||||
};
|
||||
|
||||
export { move, hexToRgb, copyToClipboard, saveData, addItem, deleteItem, moveItemUp, moveItemDown };
|
||||
const importJson = (event, dispatch) => {
|
||||
const fr = new FileReader();
|
||||
fr.addEventListener('load', () => {
|
||||
const importedObject = JSON.parse(fr.result);
|
||||
dispatch({ type: 'import_data', payload: importedObject });
|
||||
dispatch({ type: 'save_data' });
|
||||
});
|
||||
fr.readAsText(event.target.files[0]);
|
||||
};
|
||||
|
||||
const saveAsPdf = (pageRef, panZoomRef) => {
|
||||
panZoomRef.current.autoCenter(1);
|
||||
panZoomRef.current.reset();
|
||||
|
||||
setTimeout(() => {
|
||||
html2canvas(pageRef.current, {
|
||||
scale: 5,
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
}).then(canvas => {
|
||||
const image = canvas.toDataURL('image/jpeg', 1.0);
|
||||
const doc = new jsPDF('p', 'mm', 'a4');
|
||||
const pageWidth = doc.internal.pageSize.getWidth();
|
||||
const pageHeight = doc.internal.pageSize.getHeight();
|
||||
|
||||
const widthRatio = pageWidth / canvas.width;
|
||||
const heightRatio = pageHeight / canvas.height;
|
||||
const ratio = widthRatio > heightRatio ? heightRatio : widthRatio;
|
||||
|
||||
const canvasWidth = canvas.width * ratio;
|
||||
const canvasHeight = canvas.height * ratio;
|
||||
|
||||
const marginX = (pageWidth - canvasWidth) / 2;
|
||||
const marginY = (pageHeight - canvasHeight) / 2;
|
||||
|
||||
panZoomRef.current.autoCenter(0.7);
|
||||
|
||||
doc.addImage(image, 'JPEG', marginX, marginY, canvasWidth, canvasHeight, null, 'SLOW');
|
||||
doc.save(`RxResume_${Date.now()}.pdf`);
|
||||
});
|
||||
}, 250);
|
||||
};
|
||||
|
||||
export {
|
||||
move,
|
||||
hexToRgb,
|
||||
copyToClipboard,
|
||||
saveData,
|
||||
addItem,
|
||||
deleteItem,
|
||||
moveItemUp,
|
||||
moveItemDown,
|
||||
importJson,
|
||||
saveAsPdf,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user