mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-15 09:11:57 +10:00
clearing the slate
This commit is contained in:
79
src/assets/css/animate.css
vendored
79
src/assets/css/animate.css
vendored
@ -1,79 +0,0 @@
|
||||
@-webkit-keyframes slideInLeft {
|
||||
from {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInLeft {
|
||||
from {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.slideInLeft {
|
||||
-webkit-animation-name: slideInLeft;
|
||||
animation-name: slideInLeft;
|
||||
}
|
||||
|
||||
@-webkit-keyframes slideInRight {
|
||||
from {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.slideInRight {
|
||||
-webkit-animation-name: slideInRight;
|
||||
animation-name: slideInRight;
|
||||
}
|
||||
|
||||
.animated {
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@media (print), (prefers-reduced-motion: reduce) {
|
||||
.animated {
|
||||
-webkit-animation-duration: 1ms !important;
|
||||
animation-duration: 1ms !important;
|
||||
-webkit-transition-duration: 1ms !important;
|
||||
transition-duration: 1ms !important;
|
||||
-webkit-animation-iteration-count: 1 !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
}
|
||||
}
|
||||
@ -1,304 +0,0 @@
|
||||
/* Material Icons */
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Material Icons'), local('MaterialIcons-Regular'),
|
||||
url('../fonts/MaterialIcons/MaterialIcons-Regular.woff2') format('woff2'),
|
||||
url('../fonts/MaterialIcons/MaterialIcons-Regular.woff') format('woff');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: 'liga';
|
||||
}
|
||||
|
||||
/* Montserrat 400 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Montserrat Regular'), local('Montserrat-Regular'),
|
||||
url('../fonts/Montserrat/Montserrat-400.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Montserrat 500 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Montserrat Medium'), local('Montserrat-Medium'),
|
||||
url('../fonts/Montserrat/Montserrat-500.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Montserrat 600 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Montserrat SemiBold'), local('Montserrat-SemiBold'),
|
||||
url('../fonts/Montserrat/Montserrat-600.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Montserrat 700 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Montserrat Bold'), local('Montserrat-Bold'),
|
||||
url('../fonts/Montserrat/Montserrat-700.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Lato 400 */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Lato Regular'), local('Lato-Regular'),
|
||||
url('../fonts/Lato/Lato-400.woff2') format('woff2'),
|
||||
url('../fonts/Lato/Lato-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Lato 700 */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Lato Bold'), local('Lato-Bold'), url('../fonts/Lato/Lato-700.woff2') format('woff2'),
|
||||
url('../fonts/Lato/Lato-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Nunito 400 */
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Nunito Regular'), local('Nunito-Regular'),
|
||||
url('../fonts/Nunito/Nunito-400.woff') format('woff2'),
|
||||
url('../fonts/Nunito/Nunito-400.woff2') format('woff');
|
||||
}
|
||||
|
||||
/* Nunito 600 */
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Nunito SemiBold'), local('Nunito-SemiBold'),
|
||||
url('../fonts/Nunito/Nunito-600.woff') format('woff2'),
|
||||
url('../fonts/Nunito/Nunito-600.woff2') format('woff');
|
||||
}
|
||||
|
||||
/* Nunito 700 */
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Nunito Bold'), local('Nunito-Bold'),
|
||||
url('../fonts/Nunito/Nunito-700.woff') format('woff2'),
|
||||
url('../fonts/Nunito/Nunito-700.woff2') format('woff');
|
||||
}
|
||||
|
||||
/* Open Sans 400 */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'),
|
||||
url('../fonts/OpenSans/OpenSans-400.woff2') format('woff2'),
|
||||
url('../fonts/OpenSans/OpenSans-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Open Sans 600 */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'),
|
||||
url('../fonts/OpenSans/OpenSans-600.woff2') format('woff2'),
|
||||
url('../fonts/OpenSans/OpenSans-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Open Sans 700 */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'),
|
||||
url('../fonts/OpenSans/OpenSans-700.woff2') format('woff2'),
|
||||
url('../fonts/OpenSans/OpenSans-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 400 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Raleway'), local('Raleway-Regular'),
|
||||
url('../fonts/Raleway/Raleway-400.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 500 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Raleway Medium'), local('Raleway-Medium'),
|
||||
url('../fonts/Raleway/Raleway-500.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 600 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Raleway SemiBold'), local('Raleway-SemiBold'),
|
||||
url('../fonts/Raleway/Raleway-600.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 700 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Raleway Bold'), local('Raleway-Bold'),
|
||||
url('../fonts/Raleway/Raleway-700.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rubik 400 */
|
||||
@font-face {
|
||||
font-family: 'Rubik';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Rubik'), local('Rubik-Regular'), url('../fonts/Rubik/Rubik-400.woff2') format('woff2'),
|
||||
url('../fonts/Rubik/Rubik-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rubik 500 */
|
||||
@font-face {
|
||||
font-family: 'Rubik';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Rubik Medium'), local('Rubik-Medium'),
|
||||
url('../fonts/Rubik/Rubik-500.woff2') format('woff2'),
|
||||
url('../fonts/Rubik/Rubik-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rubik 700 */
|
||||
@font-face {
|
||||
font-family: 'Rubik';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Rubik Bold'), local('Rubik-Bold'),
|
||||
url('../fonts/Rubik/Rubik-700.woff2') format('woff2'),
|
||||
url('../fonts/Rubik/Rubik-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Source Sans Pro 400 */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-400.woff2') format('woff2'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Source Sans Pro 600 */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Source Sans Pro SemiBold'), local('SourceSansPro-SemiBold'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-600.woff2') format('woff2'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Source Sans Pro 700 */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-700.woff2') format('woff2'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Titillium Web 400 */
|
||||
@font-face {
|
||||
font-family: 'Titillium Web';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Titillium Web Regular'), local('TitilliumWeb-Regular'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-400.woff2') format('woff2'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Titillium Web 600 */
|
||||
@font-face {
|
||||
font-family: 'Titillium Web';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Titillium Web SemiBold'), local('TitilliumWeb-SemiBold'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-600.woff2') format('woff2'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Titillium Web 700 */
|
||||
@font-face {
|
||||
font-family: 'Titillium Web';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Titillium Web Bold'), local('TitilliumWeb-Bold'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-700.woff2') format('woff2'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Ubuntu 400 */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Ubuntu Regular'), local('Ubuntu-Regular'),
|
||||
url('../fonts/Ubuntu/Ubuntu-400.woff2') format('woff2'),
|
||||
url('../fonts/Ubuntu/Ubuntu-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Ubuntu 500 */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Ubuntu Medium'), local('Ubuntu-Medium'),
|
||||
url('../fonts/Ubuntu/Ubuntu-500.woff2') format('woff2'),
|
||||
url('../fonts/Ubuntu/Ubuntu-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Ubuntu 700 */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Ubuntu Bold'), local('Ubuntu-Bold'),
|
||||
url('../fonts/Ubuntu/Ubuntu-700.woff2') format('woff2'),
|
||||
url('../fonts/Ubuntu/Ubuntu-700.woff') format('woff');
|
||||
}
|
||||
@ -1,304 +0,0 @@
|
||||
{
|
||||
"data": {
|
||||
"profile": {
|
||||
"heading": "Profile",
|
||||
"photo": "https://i.imgur.com/Icr472Z.jpg",
|
||||
"firstName": "Nancy",
|
||||
"lastName": "Jackson",
|
||||
"subtitle": "Customer Sales Representative",
|
||||
"address": {
|
||||
"line1": "3879 Gateway Avenue",
|
||||
"line2": "Bakersfield,",
|
||||
"line3": "California, USA"
|
||||
},
|
||||
"phone": "+1 661-808-4188",
|
||||
"website": "nancyontheweb.com",
|
||||
"email": "nancyjack43@gmail.com"
|
||||
},
|
||||
"objective": {
|
||||
"enable": true,
|
||||
"heading": "Professional Objective",
|
||||
"body": "To obtain a job within my chosen field that will challenge me and allow me to use my education, skills and past experiences in a way that is mutually beneficial to both myself and my employer and allow for future growth and advancement."
|
||||
},
|
||||
"work": {
|
||||
"enable": true,
|
||||
"heading": "Work Experience",
|
||||
"items": [
|
||||
{
|
||||
"id": "a208ec03-76e3-4428-ac5b-e17c3de4ac18",
|
||||
"title": "On Point Electronics, NYC, NY",
|
||||
"role": "Customer Service Representative",
|
||||
"start": "Jan 2013",
|
||||
"end": "July 2018",
|
||||
"description": "- Organized customer information and account data for business planning and customer service purposes.\n- Created excel spreadsheets to track customer data and perform intense reconciliation process.\n- Received 97% positive customer survey results.\n- Speed on calls was 10% above team average. \n**Key Achievement:** Designed and executed an automatized system for following up with customers, increasing customer retention by 22%.",
|
||||
"enabled": true,
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "bd8649f2-42d1-4424-acaf-a02c08c3322c",
|
||||
"title": "Excelsior Communications, NYC, NY",
|
||||
"role": "Customer Service Representative",
|
||||
"start": "Oct 2009",
|
||||
"end": "Dec 2012",
|
||||
"description": "- Worked as a full time customer service rep in a high volume call center.\n- Received \"Associate of the Month\" award six times.\n- Chosen as an example for other associates in trainings. \n**Key Achievement:** Received Customer Appreciation bonus in three of four years.",
|
||||
"enabled": true,
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "dde47711-a7a6-424f-9751-73483a0ef4ed",
|
||||
"title": "Pizza Hut, Newark, NJ",
|
||||
"role": "Waitress",
|
||||
"start": "Aug 2005",
|
||||
"end": "Sep 2009",
|
||||
"description": "- Worked passionately in customer service in a high volume restaurant.\n- Completed the FAST customer service training class.\n- Maintained a high tip average thanks to consistent customer satisfaction.",
|
||||
"enabled": true,
|
||||
"enable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"education": {
|
||||
"enable": true,
|
||||
"heading": "Education",
|
||||
"items": [
|
||||
{
|
||||
"id": "624f32ab-2d78-4052-86ad-1354fd41d754",
|
||||
"name": "The City College of New York, NYC, NY",
|
||||
"major": "MS in Computer Science",
|
||||
"start": "Sep 2001",
|
||||
"end": "Aug 2002",
|
||||
"grade": "7.2 CGPA",
|
||||
"description": "",
|
||||
"enabled": true,
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "71a9852f-ed14-4281-bff2-4db9a2275978",
|
||||
"name": "University of California, Berkeley, CA",
|
||||
"major": "BS in Computer Science",
|
||||
"start": "Sep 1997",
|
||||
"end": "Aug 2001",
|
||||
"grade": "8.4 CGPA",
|
||||
"description": "",
|
||||
"enabled": true,
|
||||
"enable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"awards": {
|
||||
"enable": true,
|
||||
"heading": "Honors & Awards",
|
||||
"items": [
|
||||
{
|
||||
"id": "121f0976-18cb-4e46-921d-0e156b6bf7fb",
|
||||
"title": "Cast Member of a Musical - Oklahoma",
|
||||
"subtitle": "Winter, 2007",
|
||||
"description": "",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "e5f27346-72ad-4d4f-bab3-726a111e4932",
|
||||
"title": "Class Representative to ASB",
|
||||
"subtitle": "Fall, 2008",
|
||||
"description": "",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "f71ba9bc-8c14-46b5-99dd-e1333e9aceb9",
|
||||
"title": "Most Improved - Varsity Soccer",
|
||||
"subtitle": "Fall, 2007",
|
||||
"description": "",
|
||||
"enable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"certifications": {
|
||||
"enable": true,
|
||||
"heading": "Certifications",
|
||||
"items": [
|
||||
{
|
||||
"id": "e5170d99-b21d-4131-a7dc-26a4670037f5",
|
||||
"title": "CCNP",
|
||||
"subtitle": "Cisco Systems",
|
||||
"description": "",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "788e4042-9ecb-40c5-849d-7688b4e23888",
|
||||
"title": "VCP6-DCV",
|
||||
"subtitle": "VMWare",
|
||||
"description": "",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "97a1a8d9-3c03-47fb-93ab-e84f864ffe17",
|
||||
"title": "DCUCI 642-999",
|
||||
"subtitle": "Cisco Systems",
|
||||
"description": "",
|
||||
"enable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"skills": {
|
||||
"enable": true,
|
||||
"heading": "Skills",
|
||||
"items": [
|
||||
{
|
||||
"id": "2562d78a-3459-4370-8604-c81b00738db1",
|
||||
"skill": "Customer Service Expertise"
|
||||
},
|
||||
{
|
||||
"id": "58c31587-9770-4522-a34c-f5ad92fe33e5",
|
||||
"skill": "High-Volume Call Center"
|
||||
},
|
||||
{
|
||||
"id": "7aa9a4b1-a2bb-4bcd-8711-b66c0d246971",
|
||||
"skill": "Team Leader/Problem Solver"
|
||||
},
|
||||
{
|
||||
"id": "e7fd33e8-5d77-462d-8115-5be57f52832e",
|
||||
"skill": "Call Center Management"
|
||||
},
|
||||
{
|
||||
"id": "7bad2af1-c24d-4e01-b68b-be01cfa784ce",
|
||||
"skill": "Teambuilding & Training"
|
||||
},
|
||||
{
|
||||
"id": "64fe1710-c2d1-4f53-922e-a5d751eee967",
|
||||
"skill": "Continuous Improvement"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hobbies": {
|
||||
"enable": true,
|
||||
"heading": "Hobbies",
|
||||
"items": [
|
||||
{
|
||||
"id": "dd2efad7-e900-4384-bdc0-b2ab5f62bb71",
|
||||
"hobby": "Poetry"
|
||||
|
||||
},
|
||||
{
|
||||
"id": "96023eb7-8c93-4b1d-b581-b8fc4107351a",
|
||||
"hobby": "Travelling"
|
||||
},
|
||||
{
|
||||
"id": "7e5a6168-9cbe-4fe6-b9b9-43a47d8bb15a",
|
||||
"hobby": "Beatboxing"
|
||||
},
|
||||
{
|
||||
"id": "dd7f4ffd-9c16-4dbf-8968-1165b9e30db8",
|
||||
"hobby": "Sketching"
|
||||
}
|
||||
]
|
||||
},
|
||||
"languages": {
|
||||
"enable": true,
|
||||
"heading": "Languages",
|
||||
"items": [
|
||||
{
|
||||
"id": "9d34cfcb-c9f0-4d25-ab27-cf81652dd1d0",
|
||||
"key": "English (US)",
|
||||
"value": 5,
|
||||
"enable": true,
|
||||
"level": "",
|
||||
"rating": 5
|
||||
},
|
||||
{
|
||||
"id": "3511a86b-7ea9-44ac-8144-6acc7f3bd54f",
|
||||
"key": "Spanish",
|
||||
"value": 4,
|
||||
"enable": true,
|
||||
"rating": 4
|
||||
},
|
||||
{
|
||||
"id": "d1e17542-f7cc-473a-aa0e-978765907454",
|
||||
"key": "Japanese",
|
||||
"value": 4,
|
||||
"enable": true,
|
||||
"level": "N4",
|
||||
"rating": 2
|
||||
},
|
||||
{
|
||||
"id": "b1e8442a-7059-4c6f-8a9c-415383133b0e",
|
||||
"key": "German",
|
||||
"value": 3,
|
||||
"enable": true,
|
||||
"level": "B1",
|
||||
"rating": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"references": {
|
||||
"enable": true,
|
||||
"heading": "References",
|
||||
"items": [
|
||||
{
|
||||
"id": "ba3662e6-29cb-4a03-9766-b3618d1621f3",
|
||||
"name": "Lorraine Beasley",
|
||||
"position": "Head of HR, Carson Logistics",
|
||||
"phone": "+1 661-808-4188",
|
||||
"email": "l.beasley@carsonlogistics.com",
|
||||
"description": "",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "62fd3293-0e93-4242-882b-ae19b7865fef",
|
||||
"name": "Mikhail Nabakov",
|
||||
"position": "Assistant Manager, Bullseye",
|
||||
"phone": "+1 661-808-4188",
|
||||
"email": "mikhail@bullseyemart.nyc",
|
||||
"description": "",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "eaab2e32-8591-497c-8676-d122cf3a4798",
|
||||
"name": "Katherine Rose",
|
||||
"position": "CEO , DownToPlay",
|
||||
"phone": "+1 661-808-4188",
|
||||
"email": "k.rose@downtoplay.xyz",
|
||||
"description": "",
|
||||
"enable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"extras": {
|
||||
"enable": true,
|
||||
"heading": "Additional Information",
|
||||
"items": [
|
||||
{
|
||||
"id": "3834a270-2c01-4105-b670-80863c955347",
|
||||
"key": "Skype",
|
||||
"value": "@NancyJack5436",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "b0c4fd85-cfda-421e-bd31-008b9aad1dfe",
|
||||
"key": "Hometown",
|
||||
"value": "New Jersey, NY",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "7f0a4971-9770-4ca7-b135-2b0ccd867879",
|
||||
"key": "Hobbies",
|
||||
"value": "Playing Soccer & Guitar",
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"id": "e17552a2-e7e9-4605-8145-795e2b62c30e",
|
||||
"key": "Valid Work Visas",
|
||||
"value": "US, UK, EU",
|
||||
"enable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"layout": "castform",
|
||||
"font": { "family": "Montserrat" },
|
||||
"colors": {
|
||||
"background": "#ffffff",
|
||||
"primary": "#212121",
|
||||
"accent": "#f44336"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
@ -1,5 +0,0 @@
|
||||
@tailwind base;
|
||||
|
||||
@tailwind components;
|
||||
|
||||
@tailwind utilities;
|
||||
@ -1,74 +0,0 @@
|
||||
/* eslint-disable jsx-a11y/media-has-caption */
|
||||
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';
|
||||
|
||||
import LeftSidebar from '../LeftSidebar/LeftSidebar';
|
||||
import RightSidebar from '../RightSidebar/RightSidebar';
|
||||
|
||||
import templates from '../../templates';
|
||||
import PageController from '../../shared/PageController';
|
||||
import PrintDialog from '../../shared/PrintDialog';
|
||||
import PanZoomAnimation from '../../shared/PanZoomAnimation';
|
||||
|
||||
const App = () => {
|
||||
const pageRef = useRef(null);
|
||||
const panZoomRef = useRef(null);
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const context = useContext(AppContext);
|
||||
const { state, dispatch } = context;
|
||||
const { theme, settings } = state;
|
||||
|
||||
const pageContext = useContext(PageContext);
|
||||
const { setPageRef, setPanZoomRef } = pageContext;
|
||||
|
||||
useEffect(() => {
|
||||
setPageRef(pageRef);
|
||||
setPanZoomRef(panZoomRef);
|
||||
i18n.changeLanguage(settings.language);
|
||||
const storedState = JSON.parse(localStorage.getItem('state'));
|
||||
dispatch({ type: 'import_data', payload: storedState });
|
||||
}, [dispatch, setPageRef, setPanZoomRef, i18n, settings.language]);
|
||||
|
||||
return (
|
||||
<Suspense fallback="Loading...">
|
||||
<div className="h-screen grid grid-cols-5 items-center">
|
||||
<LeftSidebar />
|
||||
|
||||
<div className="relative z-10 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>
|
||||
|
||||
<div id="printPage" className="break-words">
|
||||
{templates.find(x => theme.layout.toLowerCase() === x.key).component()}
|
||||
</div>
|
||||
|
||||
<RightSidebar />
|
||||
|
||||
<PanZoomAnimation />
|
||||
<PrintDialog />
|
||||
</div>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@ -1,88 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
|
||||
import AppContext from '../../context/AppContext';
|
||||
import TabBar from '../../shared/TabBar';
|
||||
import ProfileTab from './tabs/Profile';
|
||||
import ObjectiveTab from './tabs/Objective';
|
||||
import WorkTab from './tabs/Work';
|
||||
import EducationTab from './tabs/Education';
|
||||
import AwardsTab from './tabs/Awards';
|
||||
import CertificationsTab from './tabs/Certifications';
|
||||
import SkillsTab from './tabs/Skills';
|
||||
import ExtrasTab from './tabs/Extras';
|
||||
import LanguagesTab from './tabs/Languages';
|
||||
import ReferencesTab from './tabs/References';
|
||||
import HobbiesTab from './tabs/Hobbies';
|
||||
|
||||
const LeftSidebar = () => {
|
||||
const context = useContext(AppContext);
|
||||
const { state, dispatch } = context;
|
||||
const { data } = state;
|
||||
|
||||
const tabs = [
|
||||
{ key: 'profile', name: data.profile.heading },
|
||||
{ key: 'objective', name: data.objective.heading },
|
||||
{ key: 'work', name: data.work.heading },
|
||||
{ key: 'education', name: data.education.heading },
|
||||
{ key: 'awards', name: data.awards.heading },
|
||||
{ key: 'certifications', name: data.certifications.heading },
|
||||
{ key: 'skills', name: data.skills.heading },
|
||||
{ key: 'hobbies', name: data.hobbies.heading },
|
||||
{ key: 'languages', name: data.languages.heading },
|
||||
{ key: 'references', name: data.references.heading },
|
||||
{ key: 'extras', name: data.extras.heading },
|
||||
];
|
||||
const [currentTab, setCurrentTab] = useState(tabs[0].key);
|
||||
const onChange = (key, value) => {
|
||||
dispatch({
|
||||
type: 'on_input',
|
||||
payload: {
|
||||
key,
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({ type: 'save_data' });
|
||||
};
|
||||
|
||||
const renderTabs = () => {
|
||||
switch (currentTab) {
|
||||
case 'profile':
|
||||
return <ProfileTab data={data} onChange={onChange} />;
|
||||
case 'objective':
|
||||
return <ObjectiveTab data={data} onChange={onChange} />;
|
||||
case 'work':
|
||||
return <WorkTab data={data} onChange={onChange} />;
|
||||
case 'education':
|
||||
return <EducationTab data={data} onChange={onChange} />;
|
||||
case 'awards':
|
||||
return <AwardsTab data={data} onChange={onChange} />;
|
||||
case 'certifications':
|
||||
return <CertificationsTab data={data} onChange={onChange} />;
|
||||
case 'skills':
|
||||
return <SkillsTab data={data} onChange={onChange} />;
|
||||
case 'hobbies':
|
||||
return <HobbiesTab data={data} onChange={onChange} />;
|
||||
case 'languages':
|
||||
return <LanguagesTab data={data} onChange={onChange} />;
|
||||
case 'references':
|
||||
return <ReferencesTab data={data} onChange={onChange} />;
|
||||
case 'extras':
|
||||
return <ExtrasTab data={data} onChange={onChange} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
id="leftSidebar"
|
||||
className="animated slideInLeft z-10 py-6 h-screen bg-white col-span-1 shadow-2xl overflow-y-scroll"
|
||||
>
|
||||
<TabBar tabs={tabs} currentTab={currentTab} setCurrentTab={setCurrentTab} />
|
||||
<div className="px-6">{renderTabs()}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LeftSidebar;
|
||||
@ -1,153 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import TextArea from '../../../shared/TextArea';
|
||||
import { addItem } from '../../../utils';
|
||||
import ItemActions from '../../../shared/ItemActions';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const AwardsTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.awards.enable}
|
||||
onChange={v => onChange('data.awards.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.awards.heading}
|
||||
onChange={v => onChange('data.awards.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.awards.items.map((x, index) => (
|
||||
<Item
|
||||
item={x}
|
||||
key={x.id}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
dispatch={dispatch}
|
||||
first={index === 0}
|
||||
last={index === data.awards.items.length - 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
<AddItem heading={data.awards.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange, identifier = '' }) => {
|
||||
const { t } = useTranslation(['leftSidebar', 'app']);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('awards.title.label')}
|
||||
placeholder="Code For Good Hackathon"
|
||||
value={item.title}
|
||||
onChange={v => onChange(`${identifier}title`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('awards.subtitle.label')}
|
||||
placeholder="First Place, National Level"
|
||||
value={item.subtitle}
|
||||
onChange={v => onChange(`${identifier}subtitle`, v)}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
title: '',
|
||||
subtitle: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const onChange = (key, value) => setItem(set({ ...item }, key, value));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (item.title === '') return;
|
||||
|
||||
addItem(dispatch, 'awards', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
title: '',
|
||||
subtitle: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} />
|
||||
|
||||
<AddItemButton onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch, first, last }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const identifier = `data.awards.items[${index}].`;
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading title={item.title} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} identifier={identifier} />
|
||||
|
||||
<ItemActions
|
||||
dispatch={dispatch}
|
||||
first={first}
|
||||
identifier={identifier}
|
||||
item={item}
|
||||
last={last}
|
||||
onChange={onChange}
|
||||
type="awards"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AwardsTab;
|
||||
@ -1,153 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import TextArea from '../../../shared/TextArea';
|
||||
import { addItem } from '../../../utils';
|
||||
import ItemActions from '../../../shared/ItemActions';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
|
||||
const CertificationsTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.certifications.enable}
|
||||
onChange={v => onChange('data.certifications.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.certifications.heading}
|
||||
onChange={v => onChange('data.certifications.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.certifications.items.map((x, index) => (
|
||||
<Item
|
||||
item={x}
|
||||
key={x.id}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
dispatch={dispatch}
|
||||
first={index === 0}
|
||||
last={index === data.certifications.items.length - 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
<AddItem heading={data.certifications.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange, identifier = '' }) => {
|
||||
const { t } = useTranslation(['leftSidebar', 'app']);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('certifications.title.label')}
|
||||
placeholder="CS50: Intro to Computer Science"
|
||||
value={item.title}
|
||||
onChange={v => onChange(`${identifier}title`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('certifications.subtitle.label')}
|
||||
placeholder="Harvard University"
|
||||
value={item.subtitle}
|
||||
onChange={v => onChange(`${identifier}subtitle`, v)}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
title: '',
|
||||
subtitle: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const onChange = (key, value) => setItem(set({ ...item }, key, value));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (item.title === '') return;
|
||||
|
||||
addItem(dispatch, 'certifications', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
title: '',
|
||||
subtitle: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} />
|
||||
|
||||
<AddItemButton onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch, first, last }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const identifier = `data.certifications.items[${index}].`;
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading title={item.title} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} identifier={identifier} />
|
||||
|
||||
<ItemActions
|
||||
dispatch={dispatch}
|
||||
first={first}
|
||||
identifier={identifier}
|
||||
item={item}
|
||||
last={last}
|
||||
onChange={onChange}
|
||||
type="certifications"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CertificationsTab;
|
||||
@ -1,185 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import TextArea from '../../../shared/TextArea';
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import { addItem } from '../../../utils';
|
||||
import ItemActions from '../../../shared/ItemActions';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const EducationTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.education.enable}
|
||||
onChange={v => onChange('data.education.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.education.heading}
|
||||
onChange={v => onChange('data.education.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.education.items.map((x, index) => (
|
||||
<Item
|
||||
item={x}
|
||||
key={x.id}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
dispatch={dispatch}
|
||||
first={index === 0}
|
||||
last={index === data.education.items.length - 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
<AddItem heading={data.education.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange, identifier = '' }) => {
|
||||
const { t } = useTranslation(['leftSidebar', 'app']);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('education.name.label')}
|
||||
placeholder="Harvard University"
|
||||
value={item.name}
|
||||
onChange={v => onChange(`${identifier}name`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('education.major.label')}
|
||||
placeholder="Masters in Computer Science"
|
||||
value={item.major}
|
||||
onChange={v => onChange(`${identifier}major`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('education.grade.label')}
|
||||
placeholder="7.2 CGPA"
|
||||
value={item.grade}
|
||||
onChange={v => onChange(`${identifier}grade`, v)}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 col-gap-4">
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.startDate.label')}
|
||||
placeholder="March 2018"
|
||||
value={item.start}
|
||||
onChange={v => onChange(`${identifier}start`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.endDate.label')}
|
||||
placeholder="June 2022"
|
||||
value={item.end}
|
||||
onChange={v => onChange(`${identifier}end`, v)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextArea
|
||||
rows="5"
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
name: '',
|
||||
major: '',
|
||||
start: '',
|
||||
end: '',
|
||||
grade: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const onChange = (key, value) => setItem(set({ ...item }, key, value));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (item.name === '' || item.major === '') return;
|
||||
|
||||
addItem(dispatch, 'education', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
name: '',
|
||||
role: '',
|
||||
start: '',
|
||||
end: '',
|
||||
grade: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} />
|
||||
<AddItemButton onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch, first, last }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const identifier = `data.education.items[${index}].`;
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading title={item.name} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} identifier={identifier} />
|
||||
|
||||
<ItemActions
|
||||
dispatch={dispatch}
|
||||
first={first}
|
||||
identifier={identifier}
|
||||
item={item}
|
||||
last={last}
|
||||
onChange={onChange}
|
||||
type="education"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EducationTab;
|
||||
@ -1,143 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import { addItem } from '../../../utils';
|
||||
import ItemActions from '../../../shared/ItemActions';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
|
||||
const ExtrasTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.extras.enable}
|
||||
onChange={v => onChange('data.extras.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.extras.heading}
|
||||
onChange={v => onChange('data.extras.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.extras.items.map((x, index) => (
|
||||
<Item
|
||||
item={x}
|
||||
key={x.id}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
dispatch={dispatch}
|
||||
first={index === 0}
|
||||
last={index === data.extras.items.length - 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
<AddItem heading={data.extras.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange, identifier = '' }) => {
|
||||
const { t } = useTranslation('leftSidebar');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('extras.key.label')}
|
||||
placeholder="Date of Birth"
|
||||
value={item.key}
|
||||
onChange={v => onChange(`${identifier}key`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('extras.value.label')}
|
||||
placeholder="6th August 1995"
|
||||
value={item.value}
|
||||
onChange={v => onChange(`${identifier}value`, v)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
key: '',
|
||||
value: '',
|
||||
});
|
||||
|
||||
const onChange = (key, value) => setItem(items => set({ ...items }, key, value));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (item.key === '' || item.value === '') return;
|
||||
|
||||
addItem(dispatch, 'extras', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
key: '',
|
||||
value: '',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} />
|
||||
|
||||
<AddItemButton onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch, first, last }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const identifier = `data.extras.items[${index}].`;
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading title={item.key} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} identifier={identifier} />
|
||||
|
||||
<ItemActions
|
||||
dispatch={dispatch}
|
||||
first={first}
|
||||
identifier={identifier}
|
||||
item={item}
|
||||
last={last}
|
||||
onChange={onChange}
|
||||
type="extras"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExtrasTab;
|
||||
@ -1,140 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import TextField from '../../../shared/TextField';
|
||||
import { addItem, deleteItem, moveItemUp, moveItemDown } from '../../../utils';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const HobbiesTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.hobbies.enable}
|
||||
onChange={v => onChange('data.hobbies.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.hobbies.heading}
|
||||
onChange={v => onChange('data.hobbies.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.hobbies.items.map((x, index) => (
|
||||
<Item item={x} key={index} index={index} onChange={onChange} dispatch={dispatch} />
|
||||
))}
|
||||
|
||||
<AddItem heading={data.hobbies.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange }) => {
|
||||
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="Beatboxing"
|
||||
value={item.hobby}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
type="text"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
hobby: ''
|
||||
});
|
||||
|
||||
const add = () => {
|
||||
if (item.hobby === '') return;
|
||||
|
||||
addItem(dispatch, 'hobbies', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
hobby: ''
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<div className="col-span-3">
|
||||
<Form item={item} onChange={v => setItem({...item, hobby: v})} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={add}
|
||||
className="col-span-1 bg-gray-600 hover:bg-gray-700 text-sm font-medium rounded"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<i className="material-icons font-bold text-white text-lg">add</i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch }) => {
|
||||
const identifier = `data.hobbies.items[${index}]`;
|
||||
|
||||
return (
|
||||
<div className="my-4 grid grid-cols-12">
|
||||
<div className="col-span-9">
|
||||
<Form item={item} onChange={v => onChange(identifier, v)} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemUp(dispatch, 'hobbies', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_upward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemDown(dispatch, 'hobbies', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_downward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => deleteItem(dispatch, 'hobbies', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">close</i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HobbiesTab;
|
||||
@ -1,182 +0,0 @@
|
||||
import set from 'lodash/set';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import Counter from '../../../shared/Counter';
|
||||
import ItemActions from '../../../shared/ItemActions';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
import TextField from '../../../shared/TextField';
|
||||
import { addItem } from '../../../utils';
|
||||
|
||||
const LanguagesTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
useEffect(() => {
|
||||
if (!('languages' in data)) {
|
||||
dispatch({
|
||||
type: 'migrate_section',
|
||||
payload: {
|
||||
key: 'languages',
|
||||
value: {
|
||||
enable: false,
|
||||
heading: 'Languages',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({ type: 'save_data' });
|
||||
}
|
||||
}, [data, dispatch]);
|
||||
|
||||
return (
|
||||
'languages' in data && (
|
||||
<>
|
||||
<div className="mb-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.languages.enable}
|
||||
onChange={v => onChange('data.languages.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.languages.heading}
|
||||
onChange={v => onChange('data.languages.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.languages.items.map((x, index) => (
|
||||
<Item
|
||||
item={x}
|
||||
key={x.id}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
dispatch={dispatch}
|
||||
first={index === 0}
|
||||
last={index === data.languages.items.length - 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
<AddItem heading={data.languages.heading} dispatch={dispatch} />
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange, identifier = '' }) => {
|
||||
const { t } = useTranslation('leftSidebar');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('languages.key.label')}
|
||||
placeholder="English"
|
||||
value={item.key}
|
||||
onChange={v => onChange(`${identifier}key`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('languages.level.label')}
|
||||
placeholder="B1"
|
||||
value={item.level}
|
||||
onChange={v => onChange(`${identifier}level`, v)}
|
||||
/>
|
||||
|
||||
<Counter
|
||||
className="mb-6"
|
||||
label={t('languages.rating.label')}
|
||||
value={item.rating}
|
||||
onDecrement={() =>
|
||||
item.rating > 1
|
||||
? onChange(`${identifier}rating`, item.rating - 1)
|
||||
: onChange(`${identifier}rating`, 0)
|
||||
}
|
||||
onIncrement={() =>
|
||||
item.rating < 5
|
||||
? onChange(`${identifier}rating`, item.rating + 1)
|
||||
: onChange(`${identifier}rating`, 0)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
key: '',
|
||||
value: '',
|
||||
rating: 1,
|
||||
});
|
||||
|
||||
const onChange = (key, value) => setItem(items => set({ ...items }, key, value));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (item.key === '') return;
|
||||
|
||||
addItem(dispatch, 'languages', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
key: '',
|
||||
value: '',
|
||||
rating: 1,
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} />
|
||||
|
||||
<AddItemButton onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch, first, last }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const identifier = `data.languages.items[${index}].`;
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading title={item.key} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} identifier={identifier} />
|
||||
|
||||
<ItemActions
|
||||
dispatch={dispatch}
|
||||
first={first}
|
||||
identifier={identifier}
|
||||
item={item}
|
||||
last={last}
|
||||
onChange={onChange}
|
||||
type="languages"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LanguagesTab;
|
||||
@ -1,43 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import TextArea from '../../../shared/TextArea';
|
||||
import TextField from '../../../shared/TextField';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
|
||||
const ObjectiveTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation('leftSidebar');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.objective.enable}
|
||||
onChange={v => onChange('data.objective.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.objective.heading}
|
||||
onChange={v => onChange('data.objective.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<TextArea
|
||||
rows="15"
|
||||
className="mb-4"
|
||||
label={t('objective.objective.label')}
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default ObjectiveTab;
|
||||
@ -1,109 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
|
||||
const ProfileTab = ({ data, onChange }) => {
|
||||
const { t } = useTranslation('leftSidebar');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
placeholder="Heading"
|
||||
value={data.profile.heading}
|
||||
onChange={v => onChange('data.profile.heading', v)}
|
||||
/>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.photoUrl.label')}
|
||||
placeholder="https://i.imgur.com/..."
|
||||
value={data.profile.photo}
|
||||
onChange={v => onChange('data.profile.photo', v)}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 col-gap-4">
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.firstName.label')}
|
||||
placeholder="Jane"
|
||||
value={data.profile.firstName}
|
||||
onChange={v => onChange('data.profile.firstName', v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.lastName.label')}
|
||||
placeholder="Doe"
|
||||
value={data.profile.lastName}
|
||||
onChange={v => onChange('data.profile.lastName', v)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.subtitle.label')}
|
||||
placeholder="Full-Stack Web Developer"
|
||||
value={data.profile.subtitle}
|
||||
onChange={v => onChange('data.profile.subtitle', v)}
|
||||
/>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.address.line1.label')}
|
||||
placeholder="Palladium Complex"
|
||||
value={data.profile.address.line1}
|
||||
onChange={v => onChange('data.profile.address.line1', v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.address.line2.label')}
|
||||
placeholder="140 E 14th St"
|
||||
value={data.profile.address.line2}
|
||||
onChange={v => onChange('data.profile.address.line2', v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.address.line3.label')}
|
||||
placeholder="New York, NY 10003 USA"
|
||||
value={data.profile.address.line3}
|
||||
onChange={v => onChange('data.profile.address.line3', v)}
|
||||
/>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.phone.label')}
|
||||
placeholder="+1 541 754 3010"
|
||||
value={data.profile.phone}
|
||||
onChange={v => onChange('data.profile.phone', v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.website.label')}
|
||||
placeholder="janedoe.me"
|
||||
value={data.profile.website}
|
||||
onChange={v => onChange('data.profile.website', v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('profile.email.label')}
|
||||
placeholder="jane.doe@example.com"
|
||||
value={data.profile.email}
|
||||
onChange={v => onChange('data.profile.email', v)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileTab;
|
||||
@ -1,194 +0,0 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import TextArea from '../../../shared/TextArea';
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import { addItem } from '../../../utils';
|
||||
import ItemActions from '../../../shared/ItemActions';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
|
||||
const ReferencesTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
useEffect(() => {
|
||||
if (!('references' in data)) {
|
||||
dispatch({
|
||||
type: 'migrate_section',
|
||||
payload: {
|
||||
key: 'references',
|
||||
value: {
|
||||
enable: false,
|
||||
heading: 'References',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({ type: 'save_data' });
|
||||
}
|
||||
}, [data, dispatch]);
|
||||
|
||||
return (
|
||||
'references' in data && (
|
||||
<>
|
||||
<div className="mb-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.references.enable}
|
||||
onChange={v => onChange('data.references.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.references.heading}
|
||||
onChange={v => onChange('data.references.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.references.items.map((x, index) => (
|
||||
<Item
|
||||
item={x}
|
||||
key={x.id}
|
||||
index={index}
|
||||
onChange={onChange}
|
||||
dispatch={dispatch}
|
||||
first={index === 0}
|
||||
last={index === data.references.items.length - 1}
|
||||
/>
|
||||
))}
|
||||
|
||||
<AddItem heading={data.references.heading} dispatch={dispatch} />
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange, identifier = '' }) => {
|
||||
const { t } = useTranslation(['leftSidebar', 'app']);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('references.name.label')}
|
||||
placeholder="Richard Hendricks"
|
||||
value={item.name}
|
||||
onChange={v => onChange(`${identifier}name`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('references.position.label')}
|
||||
placeholder="CEO, Pied Piper"
|
||||
value={item.position}
|
||||
onChange={v => onChange(`${identifier}position`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('references.phone.label')}
|
||||
placeholder="+1 541 754 3010"
|
||||
value={item.phone}
|
||||
onChange={v => onChange(`${identifier}phone`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('references.email.label')}
|
||||
placeholder="richard@piedpiper.com"
|
||||
value={item.email}
|
||||
onChange={v => onChange(`${identifier}email`, v)}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
rows="5"
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
name: '',
|
||||
position: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const onChange = (key, value) => setItem(set({ ...item }, key, value));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (item.name === '' || item.position === '') return;
|
||||
|
||||
addItem(dispatch, 'references', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
name: '',
|
||||
position: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} />
|
||||
|
||||
<AddItemButton onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch, first, last }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const identifier = `data.references.items[${index}].`;
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading title={item.name} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} identifier={identifier} />
|
||||
|
||||
<ItemActions
|
||||
dispatch={dispatch}
|
||||
first={first}
|
||||
identifier={identifier}
|
||||
item={item}
|
||||
last={last}
|
||||
onChange={onChange}
|
||||
type="references"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferencesTab;
|
||||
@ -1,140 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import TextField from '../../../shared/TextField';
|
||||
import { addItem, deleteItem, moveItemUp, moveItemDown } from '../../../utils';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const SkillsTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.skills.enable}
|
||||
onChange={v => onChange('data.skills.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.skills.heading}
|
||||
onChange={v => onChange('data.skills.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.skills.items.map((x, index) => (
|
||||
<Item item={x} key={x.id} index={index} onChange={onChange} dispatch={dispatch} />
|
||||
))}
|
||||
|
||||
<AddItem heading={data.skills.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange }) => {
|
||||
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="Team Building & Training"
|
||||
value={item.skill}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
type="text"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
skill: ''
|
||||
});
|
||||
|
||||
const add = () => {
|
||||
if (item.skill === '') return;
|
||||
|
||||
addItem(dispatch, 'skills', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
skill: ''
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<div className="col-span-3">
|
||||
<Form item={item} onChange={v => setItem({...item, skill: v})} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={add}
|
||||
className="col-span-1 bg-gray-600 hover:bg-gray-700 text-sm font-medium rounded"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<i className="material-icons font-bold text-white text-lg">add</i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch }) => {
|
||||
const identifier = `data.skills.items[${index}]`;
|
||||
|
||||
return (
|
||||
<div className="my-4 grid grid-cols-12">
|
||||
<div className="col-span-9">
|
||||
<Form item={item} onChange={v => onChange(identifier, {...item, skill: v})} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemUp(dispatch, 'skills', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_upward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemDown(dispatch, 'skills', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_downward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => deleteItem(dispatch, 'skills', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">close</i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkillsTab;
|
||||
@ -1,173 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import TextArea from '../../../shared/TextArea';
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import { addItem } from '../../../utils';
|
||||
import ItemActions from '../../../shared/ItemActions';
|
||||
import AddItemButton from '../../../shared/AddItemButton';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const WorkTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox checked={data.work.enable} onChange={v => onChange('data.work.enable', v)} />
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.work.heading}
|
||||
onChange={v => onChange('data.work.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.work.items.map((x, index) => (
|
||||
<Item
|
||||
dispatch={dispatch}
|
||||
first={index === 0}
|
||||
index={index}
|
||||
item={x}
|
||||
key={x.id}
|
||||
last={index === data.work.items.length - 1}
|
||||
onChange={onChange}
|
||||
/>
|
||||
))}
|
||||
|
||||
<AddItem heading={data.work.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange, identifier = '' }) => {
|
||||
const { t } = useTranslation(['leftSidebar', 'app']);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('work.name.label')}
|
||||
placeholder="Amazon"
|
||||
value={item.title}
|
||||
onChange={v => onChange(`${identifier}title`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('work.role.label')}
|
||||
placeholder="Full-Stack Web Developer"
|
||||
value={item.role}
|
||||
onChange={v => onChange(`${identifier}role`, v)}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 col-gap-4">
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.startDate.label')}
|
||||
placeholder="March 2018"
|
||||
value={item.start}
|
||||
onChange={v => onChange(`${identifier}start`, v)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mb-6"
|
||||
label={t('app:item.endDate.label')}
|
||||
placeholder="June 2022"
|
||||
value={item.end}
|
||||
onChange={v => onChange(`${identifier}end`, v)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextArea
|
||||
rows="5"
|
||||
className="mb-6"
|
||||
label={t('app:item.description.label')}
|
||||
value={item.description}
|
||||
onChange={v => onChange(`${identifier}description`, v)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
title: '',
|
||||
role: '',
|
||||
start: '',
|
||||
end: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const onChange = (key, value) => setItem(set({ ...item }, key, value));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (item.title === '' || item.role === '') return;
|
||||
|
||||
addItem(dispatch, 'work', item);
|
||||
|
||||
setItem({
|
||||
id: uuidv4(),
|
||||
enable: true,
|
||||
title: '',
|
||||
role: '',
|
||||
start: '',
|
||||
end: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} />
|
||||
|
||||
<AddItemButton onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch, first, last }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const identifier = `data.work.items[${index}].`;
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading title={item.title} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<Form item={item} onChange={onChange} identifier={identifier} />
|
||||
|
||||
<ItemActions
|
||||
dispatch={dispatch}
|
||||
first={first}
|
||||
identifier={identifier}
|
||||
item={item}
|
||||
last={last}
|
||||
onChange={onChange}
|
||||
type="work"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkTab;
|
||||
@ -1,90 +0,0 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import AppContext from '../../context/AppContext';
|
||||
import TabBar from '../../shared/TabBar';
|
||||
import TemplatesTab from './tabs/Templates';
|
||||
import ColorsTab from './tabs/Colors';
|
||||
import FontsTab from './tabs/Fonts';
|
||||
import ActionsTab from './tabs/Actions';
|
||||
import AboutTab from './tabs/About';
|
||||
import SettingsTab from './tabs/Settings';
|
||||
|
||||
const RightSidebar = () => {
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
|
||||
const context = useContext(AppContext);
|
||||
const { state, dispatch } = context;
|
||||
const { data, theme, settings } = state;
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
key: 'templates',
|
||||
name: t('templates.title'),
|
||||
},
|
||||
{
|
||||
key: 'colors',
|
||||
name: t('colors.title'),
|
||||
},
|
||||
{
|
||||
key: 'fonts',
|
||||
name: t('fonts.title'),
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
name: t('actions.title'),
|
||||
},
|
||||
{
|
||||
key: 'settings',
|
||||
name: t('settings.title'),
|
||||
},
|
||||
{
|
||||
key: 'about',
|
||||
name: t('about.title'),
|
||||
},
|
||||
];
|
||||
const [currentTab, setCurrentTab] = useState(tabs[0].key);
|
||||
|
||||
const onChange = (key, value) => {
|
||||
dispatch({
|
||||
type: 'on_input',
|
||||
payload: {
|
||||
key,
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({ type: 'save_data' });
|
||||
};
|
||||
|
||||
const renderTabs = () => {
|
||||
switch (currentTab) {
|
||||
case tabs[0].key:
|
||||
return <TemplatesTab theme={theme} onChange={onChange} />;
|
||||
case tabs[1].key:
|
||||
return <ColorsTab theme={theme} onChange={onChange} />;
|
||||
case tabs[2].key:
|
||||
return <FontsTab theme={theme} onChange={onChange} />;
|
||||
case tabs[3].key:
|
||||
return <ActionsTab data={data} theme={theme} dispatch={dispatch} />;
|
||||
case tabs[4].key:
|
||||
return <SettingsTab settings={settings} onChange={onChange} />;
|
||||
case tabs[5].key:
|
||||
return <AboutTab />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
id="rightSidebar"
|
||||
className="animated slideInRight z-10 py-6 h-screen bg-white col-span-1 shadow-2xl overflow-y-scroll"
|
||||
>
|
||||
<TabBar tabs={tabs} currentTab={currentTab} setCurrentTab={setCurrentTab} />
|
||||
<div className="px-6">{renderTabs()}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RightSidebar;
|
||||
@ -1,121 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
const AboutTab = () => {
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('about.documentation.heading')}</h6>
|
||||
|
||||
<div className="text-sm">{t('about.documentation.body')}</div>
|
||||
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://docs.rxresu.me/"
|
||||
className="flex justify-center items-center mt-4 bg-gray-600 hover:bg-gray-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">description</i>
|
||||
<span className="text-sm">{t('about.documentation.buttons.documentation')}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr className="my-5" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('about.bugOrFeatureRequest.heading')}</h6>
|
||||
|
||||
<div className="text-sm">{t('about.bugOrFeatureRequest.body')}</div>
|
||||
|
||||
<div className="grid grid-cols-1">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/issues/new"
|
||||
className="mt-4 bg-red-600 hover:bg-red-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">bug_report</i>
|
||||
<span className="text-sm">{t('about.bugOrFeatureRequest.buttons.raiseIssue')}</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="mailto:im.amruth@gmail.com?subject=Feature Request/Reporting a Bug in Reactive Resume: "
|
||||
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">email</i>
|
||||
<span className="text-sm">{t('about.bugOrFeatureRequest.buttons.sendEmail')}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-5" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('about.sourceCode.heading')}</h6>
|
||||
|
||||
<div className="text-sm">{t('about.sourceCode.body')}</div>
|
||||
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume"
|
||||
className="flex justify-center items-center mt-4 bg-green-600 hover:bg-green-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">code</i>
|
||||
<span className="text-sm">{t('about.sourceCode.buttons.githubRepo')}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr className="my-5" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('about.license.heading')}</h6>
|
||||
|
||||
<div className="text-sm">{t('about.license.body')}</div>
|
||||
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/blob/master/LICENSE"
|
||||
className="flex justify-center items-center mt-4 bg-gray-600 hover:bg-gray-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">gavel</i>
|
||||
<span className="text-sm">{t('about.license.buttons.mitLicense')}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-5">
|
||||
<p className="text-xs font-gray-600 text-center">
|
||||
<Trans t={t} i18nKey="about.footer.credit">
|
||||
Made with Love by
|
||||
<a
|
||||
className="font-bold hover:underline"
|
||||
href="https://www.amruthpillai.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Amruth Pillai
|
||||
</a>
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="text-xs font-gray-600 text-center">{t('about.footer.thanks')}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutTab;
|
||||
@ -1,139 +0,0 @@
|
||||
/* eslint-disable new-cap */
|
||||
/* eslint-disable jsx-a11y/anchor-has-content */
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
|
||||
import React, { useRef, useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import PageContext from '../../../context/PageContext';
|
||||
import { importJson } from '../../../utils';
|
||||
|
||||
const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
const pageContext = useContext(PageContext);
|
||||
const { setPrintDialogOpen } = pageContext;
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
const exportToJson = () => {
|
||||
const backupObj = { data, theme };
|
||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(backupObj))}`;
|
||||
const dlAnchor = document.getElementById('downloadAnchor');
|
||||
dlAnchor.setAttribute('href', dataStr);
|
||||
dlAnchor.setAttribute('download', `RxResumeBackup_${Date.now()}.json`);
|
||||
dlAnchor.click();
|
||||
};
|
||||
|
||||
const loadDemoData = () => {
|
||||
dispatch({ type: 'load_demo_data' });
|
||||
dispatch({ type: 'save_data' });
|
||||
};
|
||||
|
||||
const resetEverything = () => {
|
||||
dispatch({ type: 'reset' });
|
||||
dispatch({ type: 'save_data' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="shadow text-center text-sm p-5">{t('actions.disclaimer')}</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('actions.importExport.heading')}</h6>
|
||||
|
||||
<p className="text-sm">{t('actions.importExport.body')}</p>
|
||||
|
||||
<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">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fileInputRef.current.click()}
|
||||
className="bg-gray-600 hover:bg-gray-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">publish</i>
|
||||
<span className="text-sm">{t('actions.importExport.buttons.import')}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={exportToJson}
|
||||
className="bg-gray-600 hover:bg-gray-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">get_app</i>
|
||||
<span className="text-sm">{t('actions.importExport.buttons.export')}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('actions.downloadResume.heading')}</h6>
|
||||
<div className="text-sm">{t('actions.downloadResume.body')}</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPrintDialogOpen(true)}
|
||||
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" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('actions.loadDemoData.heading')}</h6>
|
||||
|
||||
<div className="text-sm">{t('actions.loadDemoData.body')}</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={loadDemoData}
|
||||
className="mt-4 bg-green-600 hover:bg-green-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">flight_takeoff</i>
|
||||
<span className="text-sm">{t('actions.loadDemoData.buttons.loadData')}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">{t('actions.reset.heading')}</h6>
|
||||
|
||||
<div className="text-sm">{t('actions.reset.body')}</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={resetEverything}
|
||||
className="mt-4 bg-red-600 hover:bg-red-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">refresh</i>
|
||||
<span className="text-sm">{t('actions.reset.buttons.reset')}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionsTab;
|
||||
@ -1,95 +0,0 @@
|
||||
import React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import { copyToClipboard } from '../../../utils';
|
||||
|
||||
const colorOptions = [
|
||||
'#f44336',
|
||||
'#E91E63',
|
||||
'#9C27B0',
|
||||
'#673AB7',
|
||||
'#3F51B5',
|
||||
'#2196F3',
|
||||
'#03A9F4',
|
||||
'#00BCD4',
|
||||
'#009688',
|
||||
'#4CAF50',
|
||||
'#8BC34A',
|
||||
'#CDDC39',
|
||||
'#FFEB3B',
|
||||
'#FFC107',
|
||||
'#FF9800',
|
||||
'#FF5722',
|
||||
'#795548',
|
||||
'#9E9E9E',
|
||||
'#607D8B',
|
||||
'#FAFAFA',
|
||||
'#212121',
|
||||
'#263238',
|
||||
];
|
||||
|
||||
const ColorsTab = ({ theme, onChange }) => {
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
|
||||
const copyColorToClipboard = color => {
|
||||
copyToClipboard(color);
|
||||
toast(t('colors.clipboardCopyAction', { color }), {
|
||||
bodyClassName: 'text-center text-gray-800 py-2',
|
||||
});
|
||||
onChange('theme.colors.accent', color);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="uppercase tracking-wide text-gray-600 text-xs font-semibold mb-4">
|
||||
{t('colors.colorOptions')}
|
||||
</div>
|
||||
<div className="mb-6 grid grid-cols-8 col-gap-2 row-gap-3">
|
||||
{colorOptions.map(color => (
|
||||
<div
|
||||
key={color}
|
||||
className="cursor-pointer rounded-full border border-gray-200 h-6 w-6 hover:opacity-75"
|
||||
style={{ backgroundColor: color }}
|
||||
onClick={() => copyColorToClipboard(color)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="my-6 grid grid-cols-6 items-end">
|
||||
<div
|
||||
className="rounded-full w-8 h-8 mb-2 border-2"
|
||||
style={{ backgroundColor: theme.colors.primary }}
|
||||
/>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
label={t('colors.primaryColor')}
|
||||
placeholder="#FFFFFF"
|
||||
value={theme.colors.primary}
|
||||
onChange={v => onChange('theme.colors.primary', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-6 grid grid-cols-6 items-end">
|
||||
<div
|
||||
className="rounded-full w-8 h-8 mb-2 border-2"
|
||||
style={{ backgroundColor: theme.colors.accent }}
|
||||
/>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
label={t('colors.accentColor')}
|
||||
placeholder="#FFFFFF"
|
||||
value={theme.colors.accent}
|
||||
onChange={v => onChange('theme.colors.accent', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorsTab;
|
||||
@ -1,51 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
|
||||
const fontOptions = [
|
||||
'Lato',
|
||||
'Montserrat',
|
||||
'Nunito',
|
||||
'Open Sans',
|
||||
'Raleway',
|
||||
'Rubik',
|
||||
'Source Sans Pro',
|
||||
'Titillium Web',
|
||||
'Ubuntu',
|
||||
];
|
||||
|
||||
const FontsTab = ({ theme, onChange }) => {
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
{fontOptions.map(x => (
|
||||
<div
|
||||
key={x}
|
||||
style={{ fontFamily: x }}
|
||||
onClick={() => onChange('theme.font.family', x)}
|
||||
className={`w-full rounded border py-4 shadow text-xl text-center ${
|
||||
theme.font.family === x ? 'border-gray-500' : 'border-transparent'
|
||||
} hover:border-gray-400 cursor-pointer`}
|
||||
>
|
||||
{x}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
className="mb-3"
|
||||
label={t('fonts.fontFamily.label')}
|
||||
placeholder="Avenir Next"
|
||||
value={theme.font.family}
|
||||
onChange={v => onChange('theme.font.family', v)}
|
||||
/>
|
||||
|
||||
<p className="text-gray-800 text-xs">{t('fonts.fontFamily.helpText')}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FontsTab;
|
||||
@ -1,42 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
|
||||
import { languages } from '../../../i18n';
|
||||
import Dropdown from '../../../shared/Dropdown';
|
||||
|
||||
const SettingsTab = ({ settings, onChange }) => {
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Dropdown
|
||||
label={t('settings.language.label')}
|
||||
value={settings.language}
|
||||
onChange={x => onChange('settings.language', x)}
|
||||
options={languages}
|
||||
optionItem={x => (
|
||||
<option key={x.code} value={x.code}>
|
||||
{x.name}
|
||||
</option>
|
||||
)}
|
||||
/>
|
||||
|
||||
<p className="text-gray-800 text-xs">
|
||||
<Trans t={t} i18nKey="settings.language.helpText">
|
||||
If you would like to help translate the app into your own language, please refer the
|
||||
<a
|
||||
className="text-blue-600 hover:underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://docs.rxresu.me/translation/"
|
||||
>
|
||||
Translation Documentation
|
||||
</a>
|
||||
.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsTab;
|
||||
@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import templates from '../../../templates';
|
||||
|
||||
const TemplatesTab = ({ theme, onChange }) => {
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
{templates.map(x => (
|
||||
<div key={x.key} className="text-center" onClick={() => onChange('theme.layout', x.key)}>
|
||||
<img
|
||||
className={`rounded cursor-pointer object-cover border shadow hover:shadow-md ${
|
||||
theme.layout.toLowerCase() === x.key
|
||||
? 'border-gray-600 hover:border-gray-600'
|
||||
: 'border-transparent '
|
||||
} hover:border-gray-500 cursor-pointer`}
|
||||
src={x.preview}
|
||||
alt={x.name}
|
||||
/>
|
||||
<p className="mt-1 text-sm font-medium">{x.name}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TemplatesTab;
|
||||
@ -1,157 +0,0 @@
|
||||
import React, { createContext, useReducer } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import set from 'lodash/set';
|
||||
import remove from 'lodash/remove';
|
||||
|
||||
import demoData from '../assets/demo/data.json';
|
||||
import { move } from '../utils';
|
||||
|
||||
const initialState = {
|
||||
data: {
|
||||
profile: {
|
||||
heading: 'Profile',
|
||||
photo: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
subtitle: '',
|
||||
address: {
|
||||
line1: '',
|
||||
line2: '',
|
||||
line3: '',
|
||||
},
|
||||
phone: '',
|
||||
website: '',
|
||||
email: '',
|
||||
},
|
||||
objective: {
|
||||
enable: true,
|
||||
heading: 'Objective',
|
||||
body: '',
|
||||
},
|
||||
work: {
|
||||
enable: true,
|
||||
heading: 'Work Experience',
|
||||
items: [],
|
||||
},
|
||||
education: {
|
||||
enable: true,
|
||||
heading: 'Education',
|
||||
items: [],
|
||||
},
|
||||
awards: {
|
||||
enable: true,
|
||||
heading: 'Honors & Awards',
|
||||
items: [],
|
||||
},
|
||||
certifications: {
|
||||
enable: true,
|
||||
heading: 'Certifications',
|
||||
items: [],
|
||||
},
|
||||
skills: {
|
||||
enable: true,
|
||||
heading: 'Skills',
|
||||
items: [],
|
||||
},
|
||||
hobbies: {
|
||||
enable: true,
|
||||
heading: 'Hobbies',
|
||||
items: [],
|
||||
},
|
||||
languages: {
|
||||
enable: true,
|
||||
heading: 'Languages',
|
||||
items: [],
|
||||
},
|
||||
references: {
|
||||
enable: true,
|
||||
heading: 'References',
|
||||
items: [],
|
||||
},
|
||||
extras: {
|
||||
enable: true,
|
||||
heading: 'Personal Information',
|
||||
items: [],
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
layout: 'Onyx',
|
||||
font: {
|
||||
family: '',
|
||||
},
|
||||
colors: {
|
||||
background: '#ffffff',
|
||||
primary: '#212121',
|
||||
accent: '#f44336',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
language: 'en',
|
||||
},
|
||||
};
|
||||
|
||||
const reducer = (state, { type, payload }) => {
|
||||
let items;
|
||||
const newState = JSON.parse(JSON.stringify(state));
|
||||
|
||||
switch (type) {
|
||||
case 'migrate_section':
|
||||
return set({ ...newState }, `data.${payload.key}`, payload.value);
|
||||
case 'add_item':
|
||||
items = get({ ...newState }, `data.${payload.key}.items`, []);
|
||||
items.push(payload.value);
|
||||
return set({ ...newState }, `data.${payload.key}.items`, items);
|
||||
case 'delete_item':
|
||||
items = get({ ...newState }, `data.${payload.key}.items`, []);
|
||||
remove(items, x => x.id === payload.value.id);
|
||||
return set({ ...newState }, `data.${payload.key}.items`, items);
|
||||
case 'move_item_up':
|
||||
items = get({ ...newState }, `data.${payload.key}.items`, []);
|
||||
move(items, payload.value, -1);
|
||||
return set({ ...newState }, `data.${payload.key}.items`, items);
|
||||
case 'move_item_down':
|
||||
items = get({ ...newState }, `data.${payload.key}.items`, []);
|
||||
move(items, payload.value, 1);
|
||||
return set({ ...newState }, `data.${payload.key}.items`, items);
|
||||
case 'on_input':
|
||||
return set({ ...newState }, payload.key, payload.value);
|
||||
case 'save_data':
|
||||
localStorage.setItem('state', JSON.stringify(newState));
|
||||
return newState;
|
||||
case 'import_data':
|
||||
if (payload === null) return initialState;
|
||||
|
||||
for (const section of Object.keys(initialState.data)) {
|
||||
if (!(section in payload.data)) {
|
||||
payload.data[section] = initialState.data[section];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...newState,
|
||||
...payload,
|
||||
};
|
||||
case 'load_demo_data':
|
||||
return {
|
||||
...newState,
|
||||
...demoData,
|
||||
};
|
||||
case 'reset':
|
||||
return initialState;
|
||||
default:
|
||||
return newState;
|
||||
}
|
||||
};
|
||||
|
||||
const AppContext = createContext(initialState);
|
||||
const { Provider } = AppContext;
|
||||
|
||||
const StateProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
return <Provider value={{ state, dispatch }}>{children}</Provider>;
|
||||
};
|
||||
|
||||
export const AppProvider = StateProvider;
|
||||
export const AppConsumer = AppContext.Consumer;
|
||||
|
||||
export default AppContext;
|
||||
@ -1,30 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const PageContext = React.createContext(null);
|
||||
const { Provider } = PageContext;
|
||||
|
||||
const StateProvider = ({ children }) => {
|
||||
const [pageRef, setPageRef] = useState(null);
|
||||
const [panZoomRef, setPanZoomRef] = useState(null);
|
||||
const [isPrintDialogOpen, setPrintDialogOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Provider
|
||||
value={{
|
||||
pageRef,
|
||||
setPageRef,
|
||||
panZoomRef,
|
||||
setPanZoomRef,
|
||||
isPrintDialogOpen,
|
||||
setPrintDialogOpen,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const PageProvider = StateProvider;
|
||||
export const PageConsumer = PageContext.Consumer;
|
||||
|
||||
export default PageContext;
|
||||
@ -1,93 +0,0 @@
|
||||
import i18n from 'i18next';
|
||||
import backend from 'i18next-http-backend';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import detector from 'i18next-browser-languagedetector';
|
||||
|
||||
import resources from './locales';
|
||||
|
||||
const languages = [
|
||||
{
|
||||
code: 'ar',
|
||||
name: 'Arabic (عربى)',
|
||||
},
|
||||
{
|
||||
code: 'zh',
|
||||
name: 'Chinese (中文)',
|
||||
},
|
||||
{
|
||||
code: 'da',
|
||||
name: 'Danish (Dansk)',
|
||||
},
|
||||
{
|
||||
code: 'nl',
|
||||
name: 'Dutch (Nederlands)',
|
||||
},
|
||||
{
|
||||
code: 'en',
|
||||
name: 'English (US)',
|
||||
},
|
||||
{
|
||||
code: 'fr',
|
||||
name: 'French (Français)',
|
||||
},
|
||||
{
|
||||
code: 'de',
|
||||
name: 'German (Deutsche)',
|
||||
},
|
||||
{
|
||||
code: 'he',
|
||||
name: 'Hebrew (עברית)',
|
||||
},
|
||||
{
|
||||
code: 'hi',
|
||||
name: 'Hindi (हिन्दी)',
|
||||
},
|
||||
{
|
||||
code: 'it',
|
||||
name: 'Italian (Italiano)',
|
||||
},
|
||||
{
|
||||
code: 'kn',
|
||||
name: 'Kannada (ಕನ್ನಡ)',
|
||||
},
|
||||
{
|
||||
code: 'pl',
|
||||
name: 'Polish (Polskie)',
|
||||
},
|
||||
{
|
||||
code: 'pt',
|
||||
name: 'Portuguese (Português)',
|
||||
},
|
||||
{
|
||||
code: 'ru',
|
||||
name: 'Russian (русский)',
|
||||
},
|
||||
{
|
||||
code: 'es',
|
||||
name: 'Spanish (Español)',
|
||||
},
|
||||
{
|
||||
code: 'ta',
|
||||
name: 'Tamil (தமிழ்)',
|
||||
},
|
||||
{
|
||||
code: 'vi',
|
||||
name: 'Vietnamese (Tiếng Việt)',
|
||||
},
|
||||
];
|
||||
|
||||
i18n
|
||||
.use(detector)
|
||||
.use(backend)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
ns: ['app', 'leftSidebar', 'rightSidebar'],
|
||||
defaultNS: 'app',
|
||||
});
|
||||
|
||||
export { languages };
|
||||
|
||||
export default i18n;
|
||||
@ -1,48 +0,0 @@
|
||||
{
|
||||
"item": {
|
||||
"add": "Add {{- heading}}",
|
||||
"startDate": {
|
||||
"label": "Start Date"
|
||||
},
|
||||
"endDate": {
|
||||
"label": "End Date"
|
||||
},
|
||||
"description": {
|
||||
"label": "Description"
|
||||
}
|
||||
},
|
||||
"buttons": {
|
||||
"add": {
|
||||
"label": "Add"
|
||||
},
|
||||
"delete": {
|
||||
"label": "Delete"
|
||||
}
|
||||
},
|
||||
"printDialog": {
|
||||
"heading": "Download Your Resume",
|
||||
"quality": {
|
||||
"label": "Quality"
|
||||
},
|
||||
"printType": {
|
||||
"label": "Type",
|
||||
"types": {
|
||||
"unconstrained": "Unconstrained",
|
||||
"fitInA4": "Fit in A4",
|
||||
"multiPageA4": "Multi-Page A4"
|
||||
}
|
||||
},
|
||||
"helpText": [
|
||||
"This export method makes use of HTML canvas to convert the resume to an image and print it on a PDF, which means it will lose all selecting/parsing capabilities.",
|
||||
"If that is important to you, please try printing the resume instead, using Cmd/Ctrl + P or the print button below. The result may vary as the output is browser dependent, but it is known to work best on the latest version of Google Chrome."
|
||||
],
|
||||
"buttons": {
|
||||
"cancel": "Cancel",
|
||||
"saveAsPdf": "Save as PDF"
|
||||
}
|
||||
},
|
||||
"panZoomAnimation": {
|
||||
"helpText": "You can pan and zoom around the artboard at any time to get a closer look at your resume."
|
||||
},
|
||||
"markdownHelpText": "You can use <1>GitHub Flavored Markdown</1> to style this section of the text."
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
import app from './app.json';
|
||||
|
||||
export default app;
|
||||
@ -1,9 +0,0 @@
|
||||
import app from './app';
|
||||
import leftSidebar from './leftSidebar';
|
||||
import rightSidebar from './rightSidebar';
|
||||
|
||||
export default {
|
||||
app,
|
||||
leftSidebar,
|
||||
rightSidebar,
|
||||
};
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"title": {
|
||||
"label": "Title"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "Subtitle"
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"title": {
|
||||
"label": "Name"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "Authority"
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"major": {
|
||||
"label": "Major"
|
||||
},
|
||||
"grade": {
|
||||
"label": "Grade"
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"key": {
|
||||
"label": "Key"
|
||||
},
|
||||
"value": {
|
||||
"label": "Value"
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import profile from './profile.json';
|
||||
import objective from './objective.json';
|
||||
import work from './work.json';
|
||||
import education from './education.json';
|
||||
import awards from './awards.json';
|
||||
import certifications from './certifications.json';
|
||||
import languages from './languages.json';
|
||||
import references from './references.json';
|
||||
import extras from './extras.json';
|
||||
|
||||
export default {
|
||||
profile,
|
||||
objective,
|
||||
work,
|
||||
education,
|
||||
awards,
|
||||
certifications,
|
||||
languages,
|
||||
references,
|
||||
extras,
|
||||
};
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"key": {
|
||||
"label": "Name"
|
||||
},
|
||||
"level": {
|
||||
"label": "Level"
|
||||
},
|
||||
"rating": {
|
||||
"label": "Rating"
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"objective": {
|
||||
"label": "Objective"
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
{
|
||||
"photoUrl": {
|
||||
"label": "Photo URL"
|
||||
},
|
||||
"firstName": {
|
||||
"label": "First Name"
|
||||
},
|
||||
"lastName": {
|
||||
"label": "Last Name"
|
||||
},
|
||||
"subtitle": {
|
||||
"label": "Subtitle"
|
||||
},
|
||||
"address": {
|
||||
"label": "Address",
|
||||
"line1": {
|
||||
"label": "Address Line 1"
|
||||
},
|
||||
"line2": {
|
||||
"label": "Address Line 2"
|
||||
},
|
||||
"line3": {
|
||||
"label": "Address Line 3"
|
||||
}
|
||||
},
|
||||
"phone": {
|
||||
"label": "Phone Number"
|
||||
},
|
||||
"website": {
|
||||
"label": "Website"
|
||||
},
|
||||
"email": {
|
||||
"label": "Email Address"
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"position": {
|
||||
"label": "Position"
|
||||
},
|
||||
"phone": {
|
||||
"label": "Phone Number"
|
||||
},
|
||||
"email": {
|
||||
"label": "Email Address"
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"role": {
|
||||
"label": "Role"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user