diff --git a/src/components/builder/center/Artboard.module.css b/src/components/builder/center/Artboard.module.css
index dc50c524..ebf30ae8 100644
--- a/src/components/builder/center/Artboard.module.css
+++ b/src/components/builder/center/Artboard.module.css
@@ -1,8 +1,10 @@
-.container {
- width: 210mm;
- height: 297mm;
- zoom: 0.8;
- overflow: scroll;
- box-shadow: var(--shadow);
- @apply bg-white rounded;
-}
+@media screen {
+ .container {
+ width: 210mm;
+ height: 297mm;
+ zoom: 0.8;
+ overflow: scroll;
+ box-shadow: var(--shadow);
+ @apply bg-white rounded;
+ }
+}
\ No newline at end of file
diff --git a/src/components/builder/right/RightSidebar.js b/src/components/builder/right/RightSidebar.js
index 5dc9c374..1eed06be 100644
--- a/src/components/builder/right/RightSidebar.js
+++ b/src/components/builder/right/RightSidebar.js
@@ -8,6 +8,7 @@ import Fonts from './sections/Fonts';
import Layout from './sections/Layout';
import Templates from './sections/Templates';
import Actions from './sections/Actions';
+import Settings from './sections/Settings';
const getComponent = (id) => {
switch (id) {
@@ -21,6 +22,8 @@ const getComponent = (id) => {
return Fonts;
case 'actions':
return Actions;
+ case 'settings':
+ return Settings;
default:
throw new Error();
}
diff --git a/src/components/builder/right/sections/Actions.js b/src/components/builder/right/sections/Actions.js
index 57fb85ab..db95dd2a 100644
--- a/src/components/builder/right/sections/Actions.js
+++ b/src/components/builder/right/sections/Actions.js
@@ -1,9 +1,7 @@
-import { clone } from 'lodash';
import React, { memo, useContext, useState } from 'react';
-import { MdImportExport } from 'react-icons/md';
+import { FaFileExport, FaFileImport } from 'react-icons/fa';
import ModalContext from '../../../../contexts/ModalContext';
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
-import UserContext from '../../../../contexts/UserContext';
import Button from '../../../shared/Button';
import Heading from '../../../shared/Heading';
import Input from '../../../shared/Input';
@@ -12,33 +10,14 @@ import styles from './Actions.module.css';
const Actions = () => {
const [loadDemoText, setLoadDemoText] = useState('Load Demo Data');
const [resetText, setResetText] = useState('Reset Everything');
- const [deleteText, setDeleteText] = useState('Delete Account');
const state = useSelector();
const dispatch = useDispatch();
const { emitter, events } = useContext(ModalContext);
- const { deleteAccount } = useContext(UserContext);
const handleImport = () => emitter.emit(events.IMPORT_MODAL);
- const handleExportToJson = () => {
- const backupObj = clone(state);
- delete backupObj.id;
- delete backupObj.user;
- delete backupObj.name;
- delete backupObj.createdAt;
- delete backupObj.updatedAt;
- const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(
- JSON.stringify(backupObj),
- )}`;
- const dlAnchor = document.getElementById('downloadAnchor');
- dlAnchor.setAttribute('href', dataStr);
- dlAnchor.setAttribute(
- 'download',
- `RxResume_${state.id}_${Date.now()}.json`,
- );
- dlAnchor.click();
- };
+ const handleExport = () => emitter.emit(events.EXPORT_MODAL);
const getSharableUrl = () => {
const shareId = state.id;
@@ -71,18 +50,6 @@ const Actions = () => {
dispatch({ type: 'reset_data' });
};
- const handleDeleteAccount = () => {
- if (deleteText === 'Delete Account') {
- setDeleteText('Are you sure?');
- return;
- }
-
- setDeleteText('Buh bye! :(');
- setTimeout(() => {
- deleteAccount();
- }, 500);
- };
-
return (
Actions
@@ -96,7 +63,7 @@ const Actions = () => {
-
+
Import
@@ -111,15 +78,10 @@ const Actions = () => {
- Save as PDF
-
- Export as JSON
+
+ Export
-
-
- Download Exported JSON
-
@@ -164,22 +126,6 @@ const Actions = () => {
{resetText}
-
-
-
Danger Zone
-
-
- If you would like to delete your account and erase all your resumes,
- it’s just one button away. Please be weary as this is an irreversible
- process.
-
-
-
-
- {deleteText}
-
-
-
);
};
diff --git a/src/components/builder/right/sections/Settings.js b/src/components/builder/right/sections/Settings.js
new file mode 100644
index 00000000..d51ac347
--- /dev/null
+++ b/src/components/builder/right/sections/Settings.js
@@ -0,0 +1,62 @@
+import React, { memo, useContext, useState } from 'react';
+import UserContext from '../../../../contexts/UserContext';
+import Button from '../../../shared/Button';
+import Heading from '../../../shared/Heading';
+import styles from './Settings.module.css';
+import Input from '../../../shared/Input';
+import ThemeContext from '../../../../contexts/ThemeContext';
+import themeConfig from '../../../../data/themeConfig';
+
+const Settings = () => {
+ const [deleteText, setDeleteText] = useState('Delete Account');
+ const { deleteAccount } = useContext(UserContext);
+ const { theme, setTheme } = useContext(ThemeContext);
+
+ const handleChangeTheme = (e) => {
+ setTheme(e.target.value);
+ };
+
+ const handleDeleteAccount = () => {
+ if (deleteText === 'Delete Account') {
+ setDeleteText('Are you sure?');
+ return;
+ }
+
+ setDeleteText('Buh bye! :(');
+ setTimeout(() => {
+ deleteAccount();
+ }, 500);
+ };
+
+ return (
+
+ Settings
+
+
+
+
+
Danger Zone
+
+
+ If you would like to delete your account and erase all your resumes,
+ it’s just one button away. Please be weary as this is an irreversible
+ process.
+
+
+
+
+ {deleteText}
+
+
+
+
+ );
+};
+
+export default memo(Settings);
diff --git a/src/components/builder/right/sections/Settings.module.css b/src/components/builder/right/sections/Settings.module.css
new file mode 100644
index 00000000..7943f429
--- /dev/null
+++ b/src/components/builder/right/sections/Settings.module.css
@@ -0,0 +1,11 @@
+.container {
+ @apply bg-primary-100 rounded grid gap-5 p-8;
+}
+
+.container h5 {
+ @apply text-lg font-semibold;
+}
+
+.container p {
+ @apply text-sm font-medium;
+}
\ No newline at end of file
diff --git a/src/components/builder/right/sections/Templates.js b/src/components/builder/right/sections/Templates.js
index 5f2d6a15..19b1793f 100644
--- a/src/components/builder/right/sections/Templates.js
+++ b/src/components/builder/right/sections/Templates.js
@@ -1,19 +1,16 @@
import cx from 'classnames';
-import React, { memo, useContext } from 'react';
+import React, { memo } from 'react';
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
import templateOptions from '../../../../data/templateOptions';
import { handleKeyUp } from '../../../../utils';
import Heading from '../../../shared/Heading';
import styles from './Templates.module.css';
-import ThemeContext from '../../../../contexts/ThemeContext';
const Templates = () => {
const dispatch = useDispatch();
const template = useSelector('metadata.template');
- const { toggleDarkMode } = useContext(ThemeContext);
const handleClick = (value) => {
- toggleDarkMode();
dispatch({
type: 'on_input',
payload: {
diff --git a/src/components/landing/Hero.js b/src/components/landing/Hero.js
index 52dc9162..374ff973 100644
--- a/src/components/landing/Hero.js
+++ b/src/components/landing/Hero.js
@@ -1,8 +1,6 @@
import { navigate } from 'gatsby';
import React, { memo, useContext } from 'react';
-import { FaGithub } from 'react-icons/fa';
import ModalContext from '../../contexts/ModalContext';
-import ThemeContext from '../../contexts/ThemeContext';
import UserContext from '../../contexts/UserContext';
import Button from '../shared/Button';
import Logo from '../shared/Logo';
@@ -10,7 +8,6 @@ import Logo from '../shared/Logo';
const Hero = () => {
const { emitter, events } = useContext(ModalContext);
const { user, loading } = useContext(UserContext);
- const { toggleDarkMode } = useContext(ThemeContext);
const handleLogin = () => emitter.emit(events.AUTH_MODAL);
@@ -36,14 +33,6 @@ const Hero = () => {
Login
)}
-
- GitHub
-
diff --git a/src/constants/ModalEvents.js b/src/constants/ModalEvents.js
index d2ee186c..ce62309d 100644
--- a/src/constants/ModalEvents.js
+++ b/src/constants/ModalEvents.js
@@ -12,6 +12,7 @@ const ModalEvents = {
LANGUAGE_MODAL: 'language_modal',
REFERENCE_MODAL: 'reference_modal',
IMPORT_MODAL: 'import_modal',
+ EXPORT_MODAL: 'export_modal',
};
export default ModalEvents;
diff --git a/src/contexts/ThemeContext.js b/src/contexts/ThemeContext.js
index 3fc63bcb..9d865a48 100644
--- a/src/contexts/ThemeContext.js
+++ b/src/contexts/ThemeContext.js
@@ -1,64 +1,38 @@
import React, { createContext, memo, useEffect, useState } from 'react';
-
-const COLOR_CONFIG = {
- light: {
- '--color-primary-50': '#FFFFFF',
- '--color-primary-100': '#FAFAFA',
- '--color-primary-200': '#F1F0F0',
- '--color-primary-300': '#D8D2CD',
- '--color-primary-400': '#CDC4BA',
- '--color-primary-500': '#ABA59D',
- '--color-primary-600': '#8A8680',
- '--color-primary-700': '#686663',
- '--color-primary-800': '#484745',
- '--color-primary-900': '#282727',
- },
- dark: {
- '--color-primary-50': '#212121',
- '--color-primary-100': '#2c2c2c',
- '--color-primary-200': '#424242',
- '--color-primary-300': '#616161',
- '--color-primary-400': '#757575',
- '--color-primary-500': '#9e9e9e',
- '--color-primary-600': '#bdbdbd',
- '--color-primary-700': '#e0e0e0',
- '--color-primary-800': '#eeeeee',
- '--color-primary-900': '#f5f5f5',
- },
-};
+import themeConfig from '../data/themeConfig';
const defaultState = {
- darkMode: true,
- toggleDarkMode: () => {},
+ theme: 'Dark',
+ setTheme: () => {},
};
const ThemeContext = createContext(defaultState);
const ThemeProvider = ({ children }) => {
- const [darkMode, setDarkMode] = useState(defaultState.darkMode);
+ const [theme, setThemeX] = useState(defaultState.theme);
useEffect(() => {
- const isDarkMode = JSON.parse(localStorage.getItem('darkMode'));
- isDarkMode ? setDarkMode(true) : setDarkMode(false);
+ const prefTheme = localStorage.getItem('theme') || defaultState.theme;
+ setThemeX(prefTheme);
}, []);
useEffect(() => {
- const colorConfig = darkMode ? COLOR_CONFIG.dark : COLOR_CONFIG.light;
+ const colorConfig = themeConfig[theme];
for (const [key, value] of Object.entries(colorConfig)) {
document.documentElement.style.setProperty(key, value);
}
- }, [darkMode]);
+ }, [theme]);
- const toggleDarkMode = () => {
- setDarkMode(!darkMode);
- localStorage.setItem('darkMode', JSON.stringify(!darkMode));
+ const setTheme = (themeRef) => {
+ setThemeX(themeRef);
+ localStorage.setItem('theme', themeRef);
};
return (
{children}
diff --git a/src/data/rightSections.js b/src/data/rightSections.js
index a699c3fe..c05e431b 100644
--- a/src/data/rightSections.js
+++ b/src/data/rightSections.js
@@ -3,6 +3,7 @@ import {
MdDashboard,
MdFontDownload,
MdImportExport,
+ MdSettings,
MdStyle,
} from 'react-icons/md';
@@ -32,4 +33,9 @@ export default [
name: 'Actions',
icon: MdImportExport,
},
+ {
+ id: 'settings',
+ name: 'Settings',
+ icon: MdSettings,
+ },
];
diff --git a/src/data/themeConfig.js b/src/data/themeConfig.js
new file mode 100644
index 00000000..5f708461
--- /dev/null
+++ b/src/data/themeConfig.js
@@ -0,0 +1,40 @@
+const themeConfig = {
+ Light: {
+ '--color-primary-50': '#FFFFFF',
+ '--color-primary-100': '#FAFAFA',
+ '--color-primary-200': '#F1F0F0',
+ '--color-primary-300': '#D8D2CD',
+ '--color-primary-400': '#CDC4BA',
+ '--color-primary-500': '#ABA59D',
+ '--color-primary-600': '#8A8680',
+ '--color-primary-700': '#686663',
+ '--color-primary-800': '#484745',
+ '--color-primary-900': '#282727',
+ },
+ Dark: {
+ '--color-primary-50': '#212121',
+ '--color-primary-100': '#2c2c2c',
+ '--color-primary-200': '#424242',
+ '--color-primary-300': '#616161',
+ '--color-primary-400': '#757575',
+ '--color-primary-500': '#9e9e9e',
+ '--color-primary-600': '#bdbdbd',
+ '--color-primary-700': '#e0e0e0',
+ '--color-primary-800': '#eeeeee',
+ '--color-primary-900': '#f5f5f5',
+ },
+ AMOLED: {
+ '--color-primary-50': '#010101',
+ '--color-primary-100': '#121212',
+ '--color-primary-200': '#222222',
+ '--color-primary-300': '#333333',
+ '--color-primary-400': '#444',
+ '--color-primary-500': '#696969',
+ '--color-primary-600': '#8F8F8F',
+ '--color-primary-700': '#B4B4B4',
+ '--color-primary-800': '#DADADA',
+ '--color-primary-900': '#FFFFFF',
+ },
+};
+
+export default themeConfig;
diff --git a/src/modals/ModalRegistrar.js b/src/modals/ModalRegistrar.js
index 86717ec6..b19a1b5c 100644
--- a/src/modals/ModalRegistrar.js
+++ b/src/modals/ModalRegistrar.js
@@ -4,6 +4,7 @@ import ResumeModal from './ResumeModal';
import AwardModal from './sections/AwardModal';
import CertificateModal from './sections/CertificateModal';
import EducationModal from './sections/EducationModal';
+import ExportModal from './sections/ExportModal';
import HobbyModal from './sections/HobbyModal';
import ImportModal from './sections/ImportModal';
import LanguageModal from './sections/LanguageModal';
@@ -29,6 +30,7 @@ const ModalRegistrar = () => {
+
>
);
};
diff --git a/src/modals/sections/ExportModal.js b/src/modals/sections/ExportModal.js
new file mode 100644
index 00000000..970bfb1f
--- /dev/null
+++ b/src/modals/sections/ExportModal.js
@@ -0,0 +1,106 @@
+import { clone } from 'lodash';
+import React, { memo, useContext, useEffect, useState } from 'react';
+import Button from '../../components/shared/Button';
+import ModalContext from '../../contexts/ModalContext';
+import { useSelector } from '../../contexts/ResumeContext';
+import BaseModal from '../BaseModal';
+
+const ExportModal = () => {
+ const state = useSelector();
+ const [open, setOpen] = useState(false);
+
+ const { emitter, events } = useContext(ModalContext);
+
+ useEffect(() => {
+ const unbind = emitter.on(events.EXPORT_MODAL, () => setOpen(true));
+
+ return () => unbind();
+ }, [emitter, events]);
+
+ const handleOpenPrintDialog = () => {
+ if (typeof window !== `undefined`) {
+ window && window.print();
+ }
+ };
+
+ const handleExportToJson = () => {
+ const backupObj = clone(state);
+ delete backupObj.id;
+ delete backupObj.user;
+ delete backupObj.name;
+ delete backupObj.createdAt;
+ delete backupObj.updatedAt;
+ const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(
+ JSON.stringify(backupObj),
+ )}`;
+ const dlAnchor = document.getElementById('downloadAnchor');
+ dlAnchor.setAttribute('href', dataStr);
+ dlAnchor.setAttribute(
+ 'download',
+ `RxResume_${state.id}_${Date.now()}.json`,
+ );
+ dlAnchor.click();
+ };
+
+ return (
+
+
+
+ Use Browser's Print Dialog
+
+
+
+ For those of you who want a quick solution, you need not look any
+ further than your browser. All you have to do is press Ctrl/Cmd + P
+ and open up the print dialog on your browser and get your resume
+ printed immediately.
+
+
+
+ Open Print Dialog
+
+
+
+
+
+
+
Download PDF
+
+
+ These options allow you to print a single page, unconstrained version
+ of your resume, perfect for those who have a lot of content.
+ Alternatively, you could download a multi-page version of your resume
+ as well with just one click.
+
+
+
+
+ Single Page Resume
+ Multi Page Resume
+
+
+
+
+
+
+
+
Export to JSON Format
+
+
+ You can also export your data into JSON format for safe keeping so
+ that you can easily import it back into Reactive Resume whenever you
+ want to edit or generate a resume.
+
+
+
+
+
+ );
+};
+
+export default memo(ExportModal);
diff --git a/src/modals/sections/ImportModal.js b/src/modals/sections/ImportModal.js
index c5954b84..6808cf03 100644
--- a/src/modals/sections/ImportModal.js
+++ b/src/modals/sections/ImportModal.js
@@ -29,7 +29,7 @@ const ImportModal = () => {
};
return (
-
+
Import from Reactive Resume
diff --git a/src/pages/r/view.js b/src/pages/r/view.js
index 4b51de7b..59e4f0cc 100644
--- a/src/pages/r/view.js
+++ b/src/pages/r/view.js
@@ -40,6 +40,7 @@ const ResumeViewer = ({ id }) => {
{resume.name} | Reactive Resume
+
span:first-child {
100% {
transform: rotate(360deg);
}
-}
+}w
.markdown {
@apply leading-relaxed whitespace-pre-wrap;
-}
\ No newline at end of file
+}
+
+@page {
+ margin: 0;
+}
+
+@media print {
+ html,
+ body,
+ body * {
+ -webkit-print-color-adjust: exact;
+ color-adjust: exact;
+ visibility: hidden;
+ }
+
+ #page,
+ #page * {
+ visibility: visible;
+ }
+
+ #page {
+ width: 21cm;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ zoom: 1;
+ box-shadow: none;
+ }
+}
diff --git a/src/templates/Onyx.js b/src/templates/Onyx.js
index 2fb7a597..968a0217 100644
--- a/src/templates/Onyx.js
+++ b/src/templates/Onyx.js
@@ -37,6 +37,7 @@ const Onyx = ({ data }) => {
return (
(
)}
-
+ {x.summary && (
+
+ )}
);
diff --git a/src/templates/blocks/Certifications/CertificationsA.js b/src/templates/blocks/Certifications/CertificationsA.js
index f6120a2f..69e31069 100644
--- a/src/templates/blocks/Certifications/CertificationsA.js
+++ b/src/templates/blocks/Certifications/CertificationsA.js
@@ -17,7 +17,9 @@ const CertificationItem = (x) => (
)}
-
+ {x.summary && (
+
+ )}
);
diff --git a/src/templates/blocks/Education/EducationA.js b/src/templates/blocks/Education/EducationA.js
index 200ff3a0..aee2cc33 100644
--- a/src/templates/blocks/Education/EducationA.js
+++ b/src/templates/blocks/Education/EducationA.js
@@ -21,7 +21,9 @@ const EducationItem = (x) => (
{x.gpa}
-
+ {x.summary && (
+
+ )}
);
diff --git a/src/templates/blocks/Projects/ProjectsA.js b/src/templates/blocks/Projects/ProjectsA.js
index ce61f1a1..f150d1fc 100644
--- a/src/templates/blocks/Projects/ProjectsA.js
+++ b/src/templates/blocks/Projects/ProjectsA.js
@@ -21,7 +21,9 @@ const ProjectItem = (x) => (
)}
-
+ {x.summary && (
+
+ )}
);
diff --git a/src/templates/blocks/References/ReferencesA.js b/src/templates/blocks/References/ReferencesA.js
index 428d56cc..5c9aa1a8 100644
--- a/src/templates/blocks/References/ReferencesA.js
+++ b/src/templates/blocks/References/ReferencesA.js
@@ -9,7 +9,9 @@ const ReferenceItem = (x) => (
{x.position}
{x.phone}
{x.email}
-
+ {x.summary && (
+
+ )}
);
diff --git a/src/templates/blocks/Skills/SkillsA.js b/src/templates/blocks/Skills/SkillsA.js
index b890b5c9..a8b06941 100644
--- a/src/templates/blocks/Skills/SkillsA.js
+++ b/src/templates/blocks/Skills/SkillsA.js
@@ -15,7 +15,7 @@ const SkillsA = () => {
return safetyCheck(data.skills) ? (
{data.skills.heading}
-
+
{data.skills.items.map(SkillItem)}
diff --git a/src/templates/blocks/Work/WorkA.js b/src/templates/blocks/Work/WorkA.js
index d061b436..bc5d108c 100644
--- a/src/templates/blocks/Work/WorkA.js
+++ b/src/templates/blocks/Work/WorkA.js
@@ -16,7 +16,9 @@ const WorkItem = (x) => (
)}
-
+ {x.summary && (
+
+ )}
);