- implement actions section

This commit is contained in:
Amruth Pillai
2020-07-10 13:40:48 +05:30
parent 89fa8236e8
commit a8c5d29858
23 changed files with 304 additions and 167 deletions

View File

@ -1,6 +1,7 @@
{
"globals": {
"document": true,
"FileReader": true,
"localStorage": true
},
"extends": ["airbnb", "prettier"],

166
package-lock.json generated
View File

@ -1089,14 +1089,14 @@
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
},
"@firebase/analytics": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.3.8.tgz",
"integrity": "sha512-HpNRBJHnrGq5jtVTNRgA8Ozng2ilt0pkej8D5EvXoaylu80U+ICKLBlIT8TdUSEfkXC/RPjvLXg6vn/sq/CyqA==",
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.3.9.tgz",
"integrity": "sha512-l4dNskm8uQ+UqO6Lw+fuyO1enZBXUV6xNMxeVABEnVrp3wOP90KKb/ZwYgleAxF1It52lorcTtkA1YFpv3iEIQ==",
"requires": {
"@firebase/analytics-types": "0.3.1",
"@firebase/component": "0.1.15",
"@firebase/installations": "0.4.13",
"@firebase/logger": "0.2.5",
"@firebase/component": "0.1.16",
"@firebase/installations": "0.4.14",
"@firebase/logger": "0.2.6",
"@firebase/util": "0.2.50",
"tslib": "^1.11.1"
}
@ -1107,13 +1107,13 @@
"integrity": "sha512-63vVJ5NIBh/JF8l9LuPrQYSzFimk7zYHySQB4Dk9rVdJ8kV/vGQoVTvRu1UW05sEc2Ug5PqtEChtTHU+9hvPcA=="
},
"@firebase/app": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.7.tgz",
"integrity": "sha512-6NpIZ3iMrCR2XOShK5oi3YYB0GXX5yxVD8p3+2N+X4CF5cERyIrDRf8+YXOFgr+bDHSbVcIyzpWv6ijhg4MJlw==",
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.8.tgz",
"integrity": "sha512-Tm7Pi6Dtpx4FFKcpm0jcrZ/qI9oREBxmP3pWlw1jgDW4syRJHmN9/5DYvfFk6FAhj3FrY8E/6F+ngWJfqONotQ==",
"requires": {
"@firebase/app-types": "0.6.1",
"@firebase/component": "0.1.15",
"@firebase/logger": "0.2.5",
"@firebase/component": "0.1.16",
"@firebase/logger": "0.2.6",
"@firebase/util": "0.2.50",
"dom-storage": "2.1.0",
"tslib": "^1.11.1",
@ -1126,9 +1126,9 @@
"integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg=="
},
"@firebase/auth": {
"version": "0.14.7",
"resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.14.7.tgz",
"integrity": "sha512-NTQY9luV70XUA6zGYOWloDSaOT+l0/R4u3W7ptqVCfZNc4DAt7euUkTbj7SDD14902sHF54j+tk5kmpEmMd0jA==",
"version": "0.14.8",
"resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.14.8.tgz",
"integrity": "sha512-LufoWcFpYAoCUkFDKSELH69xI8NdOjNTUFKvWfADZN7ysr4dpPdDs2ZYnH67FqcMb0tX+Jdx6vWrF6VZ37AAJQ==",
"requires": {
"@firebase/auth-types": "0.10.1"
}
@ -1144,23 +1144,23 @@
"integrity": "sha512-/+gBHb1O9x/YlG7inXfxff/6X3BPZt4zgBv4kql6HEmdzNQCodIRlEYnI+/da+lN+dha7PjaFH7C7ewMmfV7rw=="
},
"@firebase/component": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.15.tgz",
"integrity": "sha512-HqFb1qQl1vtlUMIzPM15plNz27jqM8DWjuQQuGeDfG+4iRRflwKfgNw1BOyoP4kQ8vOBCL7t/71yPXSomNdJdQ==",
"version": "0.1.16",
"resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.1.16.tgz",
"integrity": "sha512-FvffvFN0LWgv1H/FIyruTECOL69Dhy+JfwoTq+mV39V8Mz9lNpo41etonL5AOr7KmXxYJVbNwkx0L9Ei88i7JA==",
"requires": {
"@firebase/util": "0.2.50",
"tslib": "^1.11.1"
}
},
"@firebase/database": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.6.tgz",
"integrity": "sha512-TqUJOaCATF/h3wpqhPT9Fz1nZI6gBv/M2pHZztUjX4A9o9Bq93NyqUurYiZnGB7zpSkEADFCVT4f0VBrWdHlNw==",
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.6.7.tgz",
"integrity": "sha512-vm0ch2zNSoHfXWnDG6WVjf0p/BdXOMBL1lAfkGu3DYH/Rkl4p97x57w0WNOURNfL4GY2LIqScSYKCidV7jqTog==",
"requires": {
"@firebase/auth-interop-types": "0.1.5",
"@firebase/component": "0.1.15",
"@firebase/component": "0.1.16",
"@firebase/database-types": "0.5.1",
"@firebase/logger": "0.2.5",
"@firebase/logger": "0.2.6",
"@firebase/util": "0.2.50",
"faye-websocket": "0.11.3",
"tslib": "^1.11.1"
@ -1175,13 +1175,13 @@
}
},
"@firebase/firestore": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.15.5.tgz",
"integrity": "sha512-unkRIC2hL2Ge5er/Hj43aUYiEKlW5bpju8TnIaF33avg/wZpSsmtVrMlAQVkBWFhvWeYpJSr2QOzNLa1bQvuCA==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.16.0.tgz",
"integrity": "sha512-RsgGIP9e6HW5soEHIuo0CGVFpeTKe0hqjrgOLk92W0mjL6irzBlqmd5HcGMY4F5QiZryc2vMT1/3LvRhkUyf8g==",
"requires": {
"@firebase/component": "0.1.15",
"@firebase/firestore-types": "1.11.0",
"@firebase/logger": "0.2.5",
"@firebase/component": "0.1.16",
"@firebase/firestore-types": "1.12.0",
"@firebase/logger": "0.2.6",
"@firebase/util": "0.2.50",
"@firebase/webchannel-wrapper": "0.2.41",
"@grpc/grpc-js": "^1.0.0",
@ -1190,16 +1190,16 @@
}
},
"@firebase/firestore-types": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-1.11.0.tgz",
"integrity": "sha512-hD7+cmMUvT5OJeWVrcRkE87PPuj/0/Wic6bntCopJE1WIX/Dm117AUkHgKd3S7Ici6DLp4bdlx1MjjwWL5942w=="
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-1.12.0.tgz",
"integrity": "sha512-OqNxVb63wPZdUc7YnpacAW1WNIMSKERSewCRi+unCQ0YI0KNfrDSypyGCyel+S3GdOtKMk9KnvDknaGbnaFX4g=="
},
"@firebase/functions": {
"version": "0.4.47",
"resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.47.tgz",
"integrity": "sha512-wiyMezW1EYq80Uk15M4poapCG10PjN5UJEY0jJr7DhCnDAoADMGlsIYFYio60+biGreij5/hpOybw5mU9WpXUw==",
"version": "0.4.48",
"resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.48.tgz",
"integrity": "sha512-BwI/JzO/f/nquKG1IS3VqmwMaKEhvM58/08vTnp46krHBsOYqsdD9T2amz+HXGT9fe2HhDsUhgFE8D00S0vqbg==",
"requires": {
"@firebase/component": "0.1.15",
"@firebase/component": "0.1.16",
"@firebase/functions-types": "0.3.17",
"@firebase/messaging-types": "0.4.5",
"isomorphic-fetch": "2.2.1",
@ -1212,11 +1212,11 @@
"integrity": "sha512-DGR4i3VI55KnYk4IxrIw7+VG7Q3gA65azHnZxo98Il8IvYLr2UTBlSh72dTLlDf25NW51HqvJgYJDKvSaAeyHQ=="
},
"@firebase/installations": {
"version": "0.4.13",
"resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.4.13.tgz",
"integrity": "sha512-Sic7BtWgdUwk+Z1C4L49Edkhzaol/ijEIdv0pkHfjedIPirIU2V8CJ5qykx2y4aTiyVbdFqfjIpp1c6A6W3GBA==",
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.4.14.tgz",
"integrity": "sha512-hQPsaU7wdTq3CFMtFQwZy6LgdXZAkXoUToV4O+ekPbjM65QzaGVogJVU8O2H6ADXoq37SarcUXKe86pcUWdFLA==",
"requires": {
"@firebase/component": "0.1.15",
"@firebase/component": "0.1.16",
"@firebase/installations-types": "0.3.4",
"@firebase/util": "0.2.50",
"idb": "3.0.2",
@ -1229,17 +1229,17 @@
"integrity": "sha512-RfePJFovmdIXb6rYwtngyxuEcWnOrzdZd9m7xAW0gRxDIjBT20n3BOhjpmgRWXo/DAxRmS7bRjWAyTHY9cqN7Q=="
},
"@firebase/logger": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.5.tgz",
"integrity": "sha512-qqw3m0tWs/qrg7axTZG/QZq24DIMdSY6dGoWuBn08ddq7+GLF5HiqkRj71XznYeUUbfRq5W9C/PSFnN4JxX+WA=="
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz",
"integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw=="
},
"@firebase/messaging": {
"version": "0.6.19",
"resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.6.19.tgz",
"integrity": "sha512-PhqK69m70G+GGgvbdnGz2+PyoqfmR5b+nouj1JV+HgyBCjMAhF8rDYQzCWWgy4HaWbLoS/xW6AZUKG20Kv2H1A==",
"version": "0.6.20",
"resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.6.20.tgz",
"integrity": "sha512-1MqyljXnbFBeHYhL6QInVM9aO5MW820yhNmOIVxk58wNXq4tOQLzqnKuvlgZ+ttgqlDzrIYiVf3EOHh5DptttQ==",
"requires": {
"@firebase/component": "0.1.15",
"@firebase/installations": "0.4.13",
"@firebase/component": "0.1.16",
"@firebase/installations": "0.4.14",
"@firebase/messaging-types": "0.4.5",
"@firebase/util": "0.2.50",
"idb": "3.0.2",
@ -1252,13 +1252,13 @@
"integrity": "sha512-sux4fgqr/0KyIxqzHlatI04Ajs5rc3WM+WmtCpxrKP1E5Bke8xu/0M+2oy4lK/sQ7nov9z15n3iltAHCgTRU3Q=="
},
"@firebase/performance": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.3.8.tgz",
"integrity": "sha512-jODXrtFLyfnRiBehHuMBmsBtMv38U9sTictRxJSz+9JahvWYm1AF0YDzPlfeyYj+kxM6+S5wdQxUaPVdcWAvWg==",
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.3.9.tgz",
"integrity": "sha512-Fj22DZXRhhKv1OSUzDxX7AqpJUcDld6tzXK1yxOC8e3v1DFPQMQdM9FoG1m1b/Vrqa6pCCqnqG6gh6VPnEcAzQ==",
"requires": {
"@firebase/component": "0.1.15",
"@firebase/installations": "0.4.13",
"@firebase/logger": "0.2.5",
"@firebase/component": "0.1.16",
"@firebase/installations": "0.4.14",
"@firebase/logger": "0.2.6",
"@firebase/performance-types": "0.0.13",
"@firebase/util": "0.2.50",
"tslib": "^1.11.1"
@ -1287,13 +1287,13 @@
}
},
"@firebase/remote-config": {
"version": "0.1.24",
"resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.1.24.tgz",
"integrity": "sha512-/Kd+I5mNPI2wJJFySOC8Mjj4lRnEwZhU0RteuVlzFCDWWEyTE//r+p2TLAufQ9J+Fd3Ru5fVMFLNyU8k71Viiw==",
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.1.25.tgz",
"integrity": "sha512-8YWefBhy77HMbWXWdbenalx+IDY/XkS+iURQ9qRYvSIFYx6RL04DzlakZNOY9CQAcxTA+cTSt4NNlhjopBjf2Q==",
"requires": {
"@firebase/component": "0.1.15",
"@firebase/installations": "0.4.13",
"@firebase/logger": "0.2.5",
"@firebase/component": "0.1.16",
"@firebase/installations": "0.4.14",
"@firebase/logger": "0.2.6",
"@firebase/remote-config-types": "0.1.9",
"@firebase/util": "0.2.50",
"tslib": "^1.11.1"
@ -1305,11 +1305,11 @@
"integrity": "sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA=="
},
"@firebase/storage": {
"version": "0.3.37",
"resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.37.tgz",
"integrity": "sha512-RLbiRQlnvXRP/30OaEiUoRHBxZygqrZyotPPWD2WmD3JMM9qGTVpYNQ092mqL3R8ViyejwlpjlPvrDo7Z9BzgQ==",
"version": "0.3.38",
"resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.3.38.tgz",
"integrity": "sha512-gWVQr5xqrU3cfhhwbAE+9iJ0XMvzbxWMvteKurn5cRNaGbmSob/O/ISOAvsQgPnk+K9zPMd2OwyzaTOl9PEMrw==",
"requires": {
"@firebase/component": "0.1.15",
"@firebase/component": "0.1.16",
"@firebase/storage-types": "0.3.12",
"@firebase/util": "0.2.50",
"tslib": "^1.11.1"
@ -1369,9 +1369,9 @@
}
},
"@grpc/grpc-js": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.1.tgz",
"integrity": "sha512-mhZRszS0SKwnWPJaNyrECePZ9U7vaHFGqrzxQbWinWR3WznBIU+nmh2L5J3elF+lp5DEUIzARXkifbs6LQVAHA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.2.tgz",
"integrity": "sha512-k2u86Bkm/3xrjUaSWeIyzXScBt/cC8uE7BznR0cpueQi11R33W6qfJdMrkrsmSHirp5likR55JSXUrcWG6ybHA==",
"requires": {
"semver": "^6.2.0"
}
@ -8021,23 +8021,23 @@
}
},
"firebase": {
"version": "7.15.5",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-7.15.5.tgz",
"integrity": "sha512-yeXo3KDp/ZWO0/Uyen99cUvGM76femebmyNOBTHcGSDkBXvIGth6235KhclxLROIKCC5b3YNwmKX11tbaC6RJg==",
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-7.16.0.tgz",
"integrity": "sha512-fYimLYkY0SS/jv4+ZnSp5u2+QdtwsTtLwPUfmWiKQnjqas1M5mqhQr3QB7vPQuSANhC3UJZZ5KPxLbFomMJLcA==",
"requires": {
"@firebase/analytics": "0.3.8",
"@firebase/app": "0.6.7",
"@firebase/analytics": "0.3.9",
"@firebase/app": "0.6.8",
"@firebase/app-types": "0.6.1",
"@firebase/auth": "0.14.7",
"@firebase/database": "0.6.6",
"@firebase/firestore": "1.15.5",
"@firebase/functions": "0.4.47",
"@firebase/installations": "0.4.13",
"@firebase/messaging": "0.6.19",
"@firebase/performance": "0.3.8",
"@firebase/auth": "0.14.8",
"@firebase/database": "0.6.7",
"@firebase/firestore": "1.16.0",
"@firebase/functions": "0.4.48",
"@firebase/installations": "0.4.14",
"@firebase/messaging": "0.6.20",
"@firebase/performance": "0.3.9",
"@firebase/polyfill": "0.3.36",
"@firebase/remote-config": "0.1.24",
"@firebase/storage": "0.3.37",
"@firebase/remote-config": "0.1.25",
"@firebase/storage": "0.3.38",
"@firebase/util": "0.2.50"
}
},
@ -19528,9 +19528,9 @@
"integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
},
"whatwg-fetch": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.1.1.tgz",
"integrity": "sha512-UlBvc5VApYAwNutfXFeuC9Jp3QCMazcqobfNqSK/RghRr3F8b0+i/QELUlEPsHjDHfijio6H5KPJcZwYXhuZsA=="
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.2.0.tgz",
"integrity": "sha512-SdGPoQMMnzVYThUbSrEvqTlkvC1Ux27NehaJ/GUHBfNrh5Mjg+1/uRyFMwVnxO2MrikMWvWAqUGgQOfVU4hT7w=="
},
"which": {
"version": "1.3.1",

View File

@ -22,7 +22,7 @@
"array-move": "^2.2.2",
"classnames": "^2.2.6",
"dotenv": "^8.2.0",
"firebase": "^7.15.5",
"firebase": "^7.16.0",
"formik": "^2.1.4",
"gatsby": "^2.24.1",
"gatsby-image": "^2.4.13",

View File

@ -53,13 +53,9 @@ const List = ({
)}
</div>
<Button
outline
icon={MdAdd}
title="Add New"
onClick={handleAdd}
className="mt-8 ml-auto"
/>
<Button outline icon={MdAdd} onClick={handleAdd} className="mt-8 ml-auto">
Add New
</Button>
</div>
);
};

View File

@ -1,25 +1,52 @@
import React, { memo } from 'react';
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 ModalContext from '../../../../contexts/ModalContext';
import { useSelector } from '../../../../contexts/ResumeContext';
const Actions = () => {
const state = useSelector();
const { emitter, events } = useContext(ModalContext);
const handleImport = () => emitter.emit(events.IMPORT_MODAL);
const handleExportToJson = () => {
const backupObj = clone(state);
delete backupObj.id;
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.click();
};
const getSharableUrl = () => {
const shareId = state.id.split('-')[0];
return `https://rxresu.me/r/${shareId}`;
};
return (
<section>
<Heading>Actions</Heading>
<div className={styles.container}>
<h5>Import from Other Sources</h5>
<h5>Import Your Resume</h5>
<p>
You can import your information from various sources like JSON Resume
or your LinkedIn profile to autofill most of the data for your resume.
or your LinkedIn to autofill most of the data for your resume.
</p>
<div className="mt-4 flex">
<Button icon={MdImportExport} title="Import" />
<Button icon={MdImportExport} onClick={handleImport}>
Import
</Button>
</div>
</div>
@ -32,21 +59,27 @@ const Actions = () => {
</p>
<div className="mt-4 flex">
<Button title="Save as PDF" />
<Button outline title="Export as JSON" className="ml-6" />
<Button>Save as PDF</Button>
<Button outline className="ml-6" onClick={handleExportToJson}>
Export as JSON
</Button>
</div>
<a id="downloadAnchor" className="hidden">
Download Exported JSON
</a>
</div>
<div className={styles.container}>
<h5>Share Your Resume</h5>
<p>
The link below will be accessible publicly if you choose, and you can
share the latest version of your resume to anyone in the world.
The link below will be accessible publicly if you choose to share it,
and viewers would see the latest version of your resume at any time.
</p>
<div>
<Input type="action" value="https://google.com" onClick={() => {}} />
<Input type="action" value={getSharableUrl()} onClick={() => {}} />
</div>
</div>
@ -59,7 +92,7 @@ const Actions = () => {
</p>
<div className="mt-4 flex">
<Button title="Load Demo Data" />
<Button>Load Demo Data</Button>
</div>
</div>
@ -73,7 +106,7 @@ const Actions = () => {
</p>
<div className="mt-4 flex">
<Button title="Delete Account" />
<Button isDelete>Delete Account</Button>
</div>
</div>
</section>

View File

@ -1,7 +1,7 @@
/* eslint-disable jsx-a11y/control-has-associated-label */
import React, { memo } from 'react';
import { useDispatch } from '../../../../contexts/ResumeContext';
import colors from '../../../../data/colors';
import colorOptions from '../../../../data/colorOptions';
import { handleKeyUp } from '../../../../utils';
import Heading from '../../../shared/Heading';
import Input from '../../../shared/Input';
@ -25,7 +25,7 @@ const Colors = () => {
<Heading>Colors</Heading>
<div className="mb-6 grid grid-cols-8 col-gap-2 row-gap-6">
{colors.map((color) => (
{colorOptions.map((color) => (
<div
key={color}
tabIndex="0"

View File

@ -1,7 +1,7 @@
import cx from 'classnames';
import React, { memo } from 'react';
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
import fonts from '../../../../data/fonts';
import fontOptions from '../../../../data/fontOptions';
import { handleKeyUp } from '../../../../utils';
import Heading from '../../../shared/Heading';
import styles from './Fonts.module.css';
@ -25,7 +25,7 @@ const Fonts = () => {
<Heading>Fonts</Heading>
<div className="grid grid-cols-2 gap-8">
{fonts.map((x) => (
{fontOptions.map((x) => (
<div
key={x}
tabIndex="0"

View File

@ -1,7 +1,7 @@
import cx from 'classnames';
import React, { memo, useContext } from 'react';
import { useDispatch, useSelector } from '../../../../contexts/ResumeContext';
import templates from '../../../../data/templates';
import templateOptions from '../../../../data/templateOptions';
import { handleKeyUp } from '../../../../utils';
import Heading from '../../../shared/Heading';
import styles from './Templates.module.css';
@ -28,7 +28,7 @@ const Templates = () => {
<Heading>Templates</Heading>
<div className="grid grid-cols-2 gap-8">
{templates.map((x) => (
{templateOptions.map((x) => (
<div
key={x.id}
tabIndex="0"

View File

@ -28,21 +28,20 @@ const Hero = () => {
<div className="mt-12 flex">
{user ? (
<Button
title="Go to App"
onClick={handleGotoApp}
isLoading={loading}
/>
<Button onClick={handleGotoApp} isLoading={loading}>
Go to App
</Button>
) : (
<Button title="Login" onClick={handleLogin} isLoading={loading} />
)}
<Button
outline
className="ml-8"
title="GitHub"
icon={FaGithub}
onClick={toggleDarkMode}
/>
>
GitHub
</Button>
</div>
</div>
</div>

View File

@ -3,7 +3,15 @@ import React, { memo } from 'react';
import { handleKeyUp } from '../../utils';
import styles from './Button.module.css';
const Button = ({ icon, title, onClick, outline, className, isLoading }) => {
const Button = ({
icon,
onClick,
outline,
children,
className,
isLoading,
isDelete,
}) => {
const Icon = icon;
return (
@ -12,10 +20,11 @@ const Button = ({ icon, title, onClick, outline, className, isLoading }) => {
onClick={isLoading ? undefined : onClick}
className={cx(styles.container, className, {
[styles.outline]: outline,
[styles.delete]: isDelete,
})}
>
{icon && <Icon size="14" className="mr-3" />}
{isLoading ? 'Loading...' : title}
{isLoading ? 'Loading...' : children}
</button>
);
};

View File

@ -29,3 +29,15 @@
.container.outline:focus {
@apply outline-none;
}
.container.delete {
@apply bg-red-600 border-red-600 text-white;
}
.container.delete:hover {
@apply bg-red-700 border-red-700;
}
.container.delete:focus {
@apply outline-none;
}

View File

@ -4,7 +4,6 @@ import React, { memo, useEffect, useState } from 'react';
import { FaAngleDown } from 'react-icons/fa';
import { MdClose, MdOpenInNew } from 'react-icons/md';
import { v4 as uuidv4 } from 'uuid';
import { IoIosCopy } from 'react-icons/io';
import { useDispatch, useSelector } from '../../contexts/ResumeContext';
import { handleKeyUp } from '../../utils';
import styles from './Input.module.css';

View File

@ -10,6 +10,7 @@ const ModalEvents = {
HOBBY_MODAL: 'hobby_modal',
LANGUAGE_MODAL: 'language_modal',
REFERENCE_MODAL: 'reference_modal',
IMPORT_MODAL: 'import_modal',
};
export default ModalEvents;

View File

@ -14,18 +14,18 @@ const DEBOUNCE_WAIT_TIME = 4000;
const defaultState = {
isOffline: false,
isUpdating: false,
createResume: () => {},
deleteResume: () => {},
getResume: async () => {},
getResumes: async () => {},
createResume: () => {},
updateResume: async () => {},
debouncedUpdateResume: async () => {},
debouncedUpdateMetadata: async () => {},
deleteResume: () => {},
};
const DatabaseContext = createContext(defaultState);
const DatabaseProvider = ({ children }) => {
const [resumeId, setResumeId] = useState(false);
const [isOffline, setOffline] = useState(false);
const [isUpdating, setUpdating] = useState(false);
const { user } = useContext(UserContext);
@ -38,6 +38,7 @@ const DatabaseProvider = ({ children }) => {
}, []);
const getResume = async (id) => {
setResumeId(id);
const snapshot = await firebase
.database()
.ref(`users/${user.uid}/resumes/${id}`)
@ -71,13 +72,11 @@ const DatabaseProvider = ({ children }) => {
};
const updateResume = async (resume) => {
const { id } = resume;
setUpdating(true);
await firebase
.database()
.ref(`users/${user.uid}/resumes/${id}`)
.ref(`users/${user.uid}/resumes/${resumeId}`)
.update({
...resume,
updatedAt: firebase.database.ServerValue.TIMESTAMP,
@ -88,22 +87,6 @@ const DatabaseProvider = ({ children }) => {
const debouncedUpdateResume = debounce(updateResume, DEBOUNCE_WAIT_TIME);
const updateMetadata = async (resumeId, metadata) => {
setUpdating(true);
await firebase
.database()
.ref(`users/${user.uid}/resumes/${resumeId}`)
.update({
metadata,
updatedAt: firebase.database.ServerValue.TIMESTAMP,
});
setUpdating(false);
};
const debouncedUpdateMetadata = debounce(updateMetadata, DEBOUNCE_WAIT_TIME);
const deleteResume = (id) => {
firebase.database().ref(`users/${user.uid}/resumes/${id}`).remove();
};
@ -116,9 +99,8 @@ const DatabaseProvider = ({ children }) => {
getResume,
createResume,
updateResume,
debouncedUpdateResume,
debouncedUpdateMetadata,
deleteResume,
debouncedUpdateResume,
}}
>
{children}

View File

@ -90,6 +90,11 @@ const ResumeProvider = ({ children }) => {
debouncedUpdateResume(newState);
return newState;
case 'on_import':
newState = { id: state.id, ...payload };
debouncedUpdateResume(newState);
return newState;
case 'set_data':
return payload;

View File

@ -1,4 +1,4 @@
const colors = [
const colorOptions = [
'#f44336',
'#E91E63',
'#9C27B0',
@ -17,4 +17,4 @@ const colors = [
'#FF5722',
];
export default colors;
export default colorOptions;

View File

@ -1,4 +1,4 @@
const fonts = [
const fontOptions = [
'Lato',
'Montserrat',
'Nunito',
@ -9,4 +9,4 @@ const fonts = [
'Titillium Web',
];
export default fonts;
export default fontOptions;

View File

@ -1,4 +1,4 @@
const templates = [
const templateOptions = [
{
id: 'onyx',
name: 'Onyx',
@ -11,4 +11,4 @@ const templates = [
},
];
export default templates;
export default templateOptions;

View File

@ -39,23 +39,25 @@ const AuthModal = () => {
const loggedInAction = (
<>
<Button outline className="mr-8" title="Logout" onClick={logout} />
<Button title="Go to App" onClick={handleGotoApp} />
<Button outline className="mr-8" onClick={logout}>
Logout
</Button>
<Button title="" onClick={handleGotoApp}>
Go to App
</Button>
</>
);
const loggedOutAction = (
<Button
isLoading={isLoading}
title="Sign in with Google"
onClick={handleSignInWithGoogle}
/>
<Button isLoading={isLoading} onClick={handleSignInWithGoogle}>
Sign in with Google
</Button>
);
return (
<BaseModal
state={[open, setOpen]}
title={getTitle()}
state={[open, setOpen]}
action={user ? loggedInAction : loggedOutAction}
>
<p>{getMessage()}</p>

View File

@ -9,7 +9,7 @@ import { handleKeyUp } from '../utils';
import styles from './BaseModal.module.css';
const BaseModal = forwardRef(
({ title, state, children, action, onDestroy }, ref) => {
({ title, state, children, action, hideActions = false, onDestroy }, ref) => {
const [open, setOpen] = state;
const handleClose = () => {
@ -44,16 +44,15 @@ const BaseModal = forwardRef(
<div className={styles.body}>{children}</div>
<div className={styles.actions}>
<Button
outline
title="Cancel"
className="mr-8"
onClick={handleClose}
/>
{!hideActions && (
<div className={styles.actions}>
<Button outline className="mr-8" onClick={handleClose}>
Cancel
</Button>
{action}
</div>
{action}
</div>
)}
</div>
</Fade>
</Modal>

View File

@ -79,7 +79,9 @@ const DataModal = ({
: title.create;
const submitAction = (
<Button type="submit" title={getTitle} onClick={() => onSubmit(values)} />
<Button type="submit" onClick={() => onSubmit(values)}>
{getTitle}
</Button>
);
const onDestroy = () => {

View File

@ -5,6 +5,7 @@ import AwardModal from './sections/AwardModal';
import CertificateModal from './sections/CertificateModal';
import EducationModal from './sections/EducationModal';
import HobbyModal from './sections/HobbyModal';
import ImportModal from './sections/ImportModal';
import LanguageModal from './sections/LanguageModal';
import ReferenceModal from './sections/ReferenceModal';
import SkillModal from './sections/SkillModal';
@ -25,6 +26,7 @@ const ModalRegistrar = () => {
<HobbyModal />
<LanguageModal />
<ReferenceModal />
<ImportModal />
</>
);
};

View File

@ -0,0 +1,95 @@
import React, { memo, useContext, useEffect, useState, useRef } from 'react';
import { Tooltip } from '@material-ui/core';
import ModalContext from '../../contexts/ModalContext';
import BaseModal from '../BaseModal';
import Button from '../../components/shared/Button';
import { useDispatch } from '../../contexts/ResumeContext';
const ImportModal = () => {
const fileInputRef = useRef(null);
const [open, setOpen] = useState(false);
const dispatch = useDispatch();
const { emitter, events } = useContext(ModalContext);
useEffect(() => {
const unbind = emitter.on(events.IMPORT_MODAL, () => setOpen(true));
return () => unbind();
}, [emitter, events]);
const importReactiveResumeJson = (event) => {
const fr = new FileReader();
fr.addEventListener('load', () => {
const payload = JSON.parse(fr.result);
dispatch({ type: 'on_import', payload });
setOpen(false);
});
fr.readAsText(event.target.files[0]);
};
return (
<BaseModal hideActions state={[open, setOpen]} title="Import Data">
<div>
<h5 className="text-xl font-semibold mb-4">
Import from Reactive Resume
</h5>
<p>
Reactive Resume has it&apos;s own schema format to make the most of
all the customizable capabilities it has to offer. If you&apos;d like
to import a backup of your resume made with this app, just upload the
file using the button below.
</p>
<Button className="mt-5" onClick={() => fileInputRef.current.click()}>
Select File
</Button>
<input
ref={fileInputRef}
type="file"
className="hidden"
onChange={importReactiveResumeJson}
/>
</div>
<hr className="my-8" />
<div>
<h5 className="text-xl font-semibold mb-4">Import from JSON Resume</h5>
<p>
<a href="https://jsonresume.org/">JSON Resume</a> is an open standard
for resume schema structure. If you are one of the many enthusiasts
who have their resume ready in this format, all it takes it just one
click to get started with Reactive Resume.
</p>
<Tooltip title="Coming Soon" placement="right" arrow>
<div className="mt-5 inline-block">
<Button className="opacity-50">Select File</Button>
</div>
</Tooltip>
</div>
<hr className="my-8" />
<div>
<h5 className="text-xl font-semibold mb-4">Import from LinkedIn</h5>
<p>
You can import a JSON that was exported from Reactive Resume by
clicking on the button below and selecting the appropriate file.
</p>
<Tooltip title="Coming Soon" placement="right" arrow>
<div className="mt-5 inline-block">
<Button className="opacity-50">Select File</Button>
</div>
</Tooltip>
</div>
</BaseModal>
);
};
export default memo(ImportModal);