- complete onyx design template

- implement public sharable urls
- implement more actions
This commit is contained in:
Amruth Pillai
2020-07-11 20:42:16 +05:30
parent 0b5653fab5
commit 5ec1f21bd3
55 changed files with 1025 additions and 412 deletions

View File

@ -2,6 +2,7 @@
width: 210mm;
height: 297mm;
zoom: 0.8;
overflow: scroll;
box-shadow: var(--shadow);
@apply bg-white;
@apply bg-white rounded;
}

View File

@ -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}

View File

@ -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':

View 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);

View File

@ -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)}

View File

@ -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}

View File

@ -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>
);
};

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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) {

View File

@ -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,

View File

@ -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);

View File

@ -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>

View File

@ -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>

View 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;
}