mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-16 17:51:43 +10:00
- complete onyx design template
- implement public sharable urls - implement more actions
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
zoom: 0.8;
|
||||
overflow: scroll;
|
||||
box-shadow: var(--shadow);
|
||||
@apply bg-white;
|
||||
@apply bg-white rounded;
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ const LeftNavbar = () => (
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 text-primary-400">
|
||||
<div className="grid grid-cols-1 gap-4 text-primary-500">
|
||||
{sections.map((x) => (
|
||||
<SectionIcon
|
||||
key={x.id}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import React, { Fragment, memo } from 'react';
|
||||
import { Element } from 'react-scroll';
|
||||
import sections from '../../../data/leftSections';
|
||||
import LeftNavbar from './LeftNavbar';
|
||||
import styles from './LeftSidebar.module.css';
|
||||
import Awards from './sections/Awards';
|
||||
import Certifications from './sections/Certifications';
|
||||
import Education from './sections/Education';
|
||||
@ -8,12 +10,11 @@ import Hobbies from './sections/Hobbies';
|
||||
import Languages from './sections/Languages';
|
||||
import Objective from './sections/Objective';
|
||||
import Profile from './sections/Profile';
|
||||
import Projects from './sections/Projects';
|
||||
import References from './sections/References';
|
||||
import Skills from './sections/Skills';
|
||||
import Social from './sections/Social';
|
||||
import Work from './sections/Work';
|
||||
import LeftNavbar from './LeftNavbar';
|
||||
import styles from './LeftSidebar.module.css';
|
||||
|
||||
const getComponent = (id) => {
|
||||
switch (id) {
|
||||
@ -27,6 +28,8 @@ const getComponent = (id) => {
|
||||
return Work;
|
||||
case 'education':
|
||||
return Education;
|
||||
case 'projects':
|
||||
return Projects;
|
||||
case 'awards':
|
||||
return Awards;
|
||||
case 'certifications':
|
||||
|
||||
23
src/components/builder/left/sections/Projects.js
Normal file
23
src/components/builder/left/sections/Projects.js
Normal file
@ -0,0 +1,23 @@
|
||||
import React, { memo } from 'react';
|
||||
import Heading from '../../../shared/Heading';
|
||||
import List from '../../lists/List';
|
||||
|
||||
const Projects = ({ id, name, event }) => {
|
||||
const path = `${id}.items`;
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>{name}</Heading>
|
||||
|
||||
<List
|
||||
path={path}
|
||||
event={event}
|
||||
titlePath="title"
|
||||
subtitlePath="link"
|
||||
textPath="summary"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Projects);
|
||||
@ -42,7 +42,11 @@ const List = ({
|
||||
subtitle={
|
||||
subtitle ||
|
||||
get(x, subtitlePath, '') ||
|
||||
(hasDate && formatDateRange(x))
|
||||
(hasDate &&
|
||||
formatDateRange({
|
||||
startDate: x.startDate,
|
||||
endDate: x.endDate,
|
||||
}))
|
||||
}
|
||||
text={text || get(x, textPath, '')}
|
||||
onEdit={() => handleEdit(x)}
|
||||
|
||||
@ -7,7 +7,7 @@ import SyncIndicator from './SyncIndicator';
|
||||
const RightNavbar = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className="grid grid-cols-1 gap-4 text-primary-400">
|
||||
<div className="grid grid-cols-1 gap-4 text-primary-500">
|
||||
{sections.map((x) => (
|
||||
<SectionIcon
|
||||
key={x.id}
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
import cx from 'classnames';
|
||||
import React, { memo, useContext } from 'react';
|
||||
import { MdSync, MdSyncDisabled } from 'react-icons/md';
|
||||
import { MdSync } from 'react-icons/md';
|
||||
import DatabaseContext from '../../../contexts/DatabaseContext';
|
||||
|
||||
const SyncIndicator = () => {
|
||||
const { isOffline, isUpdating } = useContext(DatabaseContext);
|
||||
const { isUpdating } = useContext(DatabaseContext);
|
||||
|
||||
return (
|
||||
<div className="text-4xl">
|
||||
{isOffline ? (
|
||||
<MdSyncDisabled className="text-red-600" />
|
||||
) : (
|
||||
<MdSync className={cx({ spin: isUpdating })} />
|
||||
)}
|
||||
<MdSync className={cx({ spin: isUpdating })} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,36 +1,88 @@
|
||||
import React, { memo, useContext } from 'react';
|
||||
import { MdImportExport } from 'react-icons/md';
|
||||
import { clone } from 'lodash';
|
||||
import Heading from '../../../shared/Heading';
|
||||
import Button from '../../../shared/Button';
|
||||
import styles from './Actions.module.css';
|
||||
import Input from '../../../shared/Input';
|
||||
import React, { memo, useContext, useState } from 'react';
|
||||
import { MdImportExport } from 'react-icons/md';
|
||||
import ModalContext from '../../../../contexts/ModalContext';
|
||||
import { useSelector } from '../../../../contexts/ResumeContext';
|
||||
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';
|
||||
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}.json`);
|
||||
dlAnchor.setAttribute(
|
||||
'download',
|
||||
`RxResume_${state.id}_${Date.now()}.json`,
|
||||
);
|
||||
dlAnchor.click();
|
||||
};
|
||||
|
||||
const getSharableUrl = () => {
|
||||
const shareId = state.id.split('-')[0];
|
||||
const shareId = state.id;
|
||||
return `https://rxresu.me/r/${shareId}`;
|
||||
};
|
||||
|
||||
const handleOpenLink = () => {
|
||||
if (typeof window !== `undefined`) {
|
||||
window && window.open(getSharableUrl());
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoadDemo = () => {
|
||||
if (loadDemoText === 'Load Demo Data') {
|
||||
setLoadDemoText('Are you sure?');
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({ type: 'load_demo_data' });
|
||||
setLoadDemoText('Load Demo Data');
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
if (resetText === 'Reset Everything') {
|
||||
setResetText('Are you sure?');
|
||||
return;
|
||||
}
|
||||
|
||||
setResetText('Reset Everything');
|
||||
dispatch({ type: 'reset_data' });
|
||||
};
|
||||
|
||||
const handleDeleteAccount = () => {
|
||||
if (deleteText === 'Delete Account') {
|
||||
setDeleteText('Are you sure?');
|
||||
return;
|
||||
}
|
||||
|
||||
setDeleteText('Buh bye! :(');
|
||||
setTimeout(() => {
|
||||
deleteAccount();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Actions</Heading>
|
||||
@ -79,7 +131,11 @@ const Actions = () => {
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<Input type="action" value={getSharableUrl()} onClick={() => {}} />
|
||||
<Input
|
||||
type="action"
|
||||
value={getSharableUrl()}
|
||||
onClick={handleOpenLink}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -92,12 +148,25 @@ const Actions = () => {
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<Button>Load Demo Data</Button>
|
||||
<Button onClick={handleLoadDemo}>{loadDemoText}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Delete Account</h5>
|
||||
<h5>Reset Everything</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
Feels like you made too many mistakes? No worries, clear everything
|
||||
with just one click, but be careful if there are no backups.
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<Button onClick={handleReset}>{resetText}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<h5>Danger Zone</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
If you would like to delete your account and erase all your resumes,
|
||||
@ -106,7 +175,9 @@ const Actions = () => {
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<Button isDelete>Delete Account</Button>
|
||||
<Button isDelete onClick={handleDeleteAccount}>
|
||||
{deleteText}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -48,7 +48,7 @@ const Layout = () => {
|
||||
<section>
|
||||
<Heading>Layout</Heading>
|
||||
|
||||
<p>
|
||||
<p className="leading-loose">
|
||||
This template supports {blocks.length} blocks. You can re-order or move
|
||||
sections by dragging/dropping the section names across lists.
|
||||
</p>
|
||||
|
||||
@ -32,7 +32,9 @@ const Hero = () => {
|
||||
Go to App
|
||||
</Button>
|
||||
) : (
|
||||
<Button title="Login" onClick={handleLogin} isLoading={loading} />
|
||||
<Button onClick={handleLogin} isLoading={loading}>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
outline
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { memo, useContext } from 'react';
|
||||
import UserContext from '../../contexts/UserContext';
|
||||
import LoadingScreen from './LoadingScreen';
|
||||
|
||||
const PrivateRoute = ({ component: Component, location, ...props }) => {
|
||||
const PrivateRoute = ({ component: Component, ...props }) => {
|
||||
const { user, loading } = useContext(UserContext);
|
||||
|
||||
if (loading) {
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
.container {
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.container label input,
|
||||
.container label textarea,
|
||||
.container label select {
|
||||
@apply py-3 px-4 rounded text-primary-900 bg-primary-200 border border-transparent appearance-none;
|
||||
@apply w-full py-3 px-4 rounded text-primary-900 bg-primary-200 border border-transparent appearance-none;
|
||||
}
|
||||
|
||||
.container label input::placeholder,
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
import { Field } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { MdAdd } from 'react-icons/md';
|
||||
import { getFieldProps, handleKeyUp } from '../../utils';
|
||||
import Input from './Input';
|
||||
|
||||
const InputArray = ({ formik, schema, helpers, label, path, placeholder }) => {
|
||||
const handleClickAdd = () => {
|
||||
formik.values.temp && helpers.push(formik.values.temp);
|
||||
formik.setFieldValue('temp', '');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="col-span-2">
|
||||
<label>
|
||||
<span>{label}</span>
|
||||
|
||||
{formik.values[path] &&
|
||||
formik.values[path].map((x, i) => (
|
||||
<Field key={i} name={`${path}.${i}`}>
|
||||
{({ field, meta }) => (
|
||||
<Input
|
||||
className="my-1"
|
||||
onClick={() => helpers.remove(i)}
|
||||
{...field}
|
||||
{...meta}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
))}
|
||||
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
{...getFieldProps(formik, schema, 'temp')}
|
||||
/>
|
||||
<MdAdd
|
||||
size="18px"
|
||||
tabIndex="0"
|
||||
className="mx-4 cursor-pointer opacity-50 hover:opacity-75"
|
||||
onKeyUp={(e) => handleKeyUp(e, handleClickAdd)}
|
||||
onClick={handleClickAdd}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(InputArray);
|
||||
@ -1,3 +1,4 @@
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
import React, { memo, useContext, useRef } from 'react';
|
||||
import { MdFileUpload } from 'react-icons/md';
|
||||
import StorageContext from '../../contexts/StorageContext';
|
||||
@ -20,27 +21,29 @@ const PhotoUpload = () => {
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
className={styles.circle}
|
||||
onClick={handleIconClick}
|
||||
onKeyUp={(e) => handleKeyUp(e, handleIconClick)}
|
||||
>
|
||||
<MdFileUpload size="22px" />
|
||||
<input
|
||||
name="file"
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
onChange={handleImageUpload}
|
||||
/>
|
||||
</div>
|
||||
<Tooltip title="Upload Photograph" placement="right-start">
|
||||
<div
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
className={styles.circle}
|
||||
onClick={handleIconClick}
|
||||
onKeyUp={(e) => handleKeyUp(e, handleIconClick)}
|
||||
>
|
||||
<MdFileUpload size="22px" />
|
||||
<input
|
||||
name="file"
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
onChange={handleImageUpload}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<Input
|
||||
name="photograph"
|
||||
label="Photograph"
|
||||
className="pl-6"
|
||||
className="pl-6 w-full"
|
||||
path="profile.photograph"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import { Link, scrollSpy } from 'react-scroll';
|
||||
import styles from './SectionIcon.module.css';
|
||||
|
||||
const SectionIcon = ({ section, containerId, tooltipPlacement }) => {
|
||||
const { id, name, icon: Icon } = section;
|
||||
@ -19,7 +20,7 @@ const SectionIcon = ({ section, containerId, tooltipPlacement }) => {
|
||||
duration={500}
|
||||
containerId={containerId}
|
||||
activeClass="text-primary-900"
|
||||
className="py-2 cursor-pointer focus:outline-none focus:text-primary-900 hover:text-primary-900"
|
||||
className={styles.icon}
|
||||
>
|
||||
<Icon size="18px" />
|
||||
</Link>
|
||||
|
||||
11
src/components/shared/SectionIcon.module.css
Normal file
11
src/components/shared/SectionIcon.module.css
Normal file
@ -0,0 +1,11 @@
|
||||
.icon {
|
||||
@apply py-2 cursor-pointer;
|
||||
}
|
||||
|
||||
.icon:focus {
|
||||
@apply outline-none text-primary-900;
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
@apply text-primary-900;
|
||||
}
|
||||
Reference in New Issue
Block a user