mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-16 01:32:02 +10:00
- implement i18n
- translation dynamic for sections - added articles for SEO
This commit is contained in:
@ -1,11 +1,13 @@
|
||||
import { navigate } from 'gatsby';
|
||||
import React, { memo, useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../components/shared/Button';
|
||||
import ModalContext from '../contexts/ModalContext';
|
||||
import UserContext from '../contexts/UserContext';
|
||||
import BaseModal from './BaseModal';
|
||||
|
||||
const AuthModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isLoadingGoogle, setLoadingGoogle] = useState(false);
|
||||
const [isLoadingAnonymous, setLoadingAnonymous] = useState(false);
|
||||
@ -39,20 +41,20 @@ const AuthModal = () => {
|
||||
};
|
||||
|
||||
const getTitle = () =>
|
||||
user ? `Welcome, ${user.displayName || 'Agent 47'}` : 'Who are you?';
|
||||
user
|
||||
? t('modals.auth.welcome', { name: user.displayName || 'Agent 47' })
|
||||
: t('modals.auth.whoAreYou');
|
||||
|
||||
const getMessage = () =>
|
||||
user
|
||||
? `Awesome. Now that you've authenticated yourself, we can get on with the real reason you're here. Click on the Go to App button to start building your resume!`
|
||||
: `Reactive Resume needs to know who you are so it can securely authenticate you into the app and show you only your information. Once you are in, you can start building your resume, editing it to add new skills or sharing it with the world!`;
|
||||
user ? t('modals.auth.loggedInText') : t('modals.auth.loggedOutText');
|
||||
|
||||
const loggedInAction = (
|
||||
<>
|
||||
<Button outline className="mr-8" onClick={logout}>
|
||||
Logout
|
||||
{t('shared.buttons.logout')}
|
||||
</Button>
|
||||
<Button title="" onClick={handleGotoApp}>
|
||||
Go to App
|
||||
{t('landing.hero.goToApp')}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
@ -60,14 +62,14 @@ const AuthModal = () => {
|
||||
const loggedOutAction = (
|
||||
<div className="flex">
|
||||
<Button isLoading={isLoadingGoogle} onClick={handleSignInWithGoogle}>
|
||||
Sign in with Google
|
||||
{t('modals.auth.buttons.google')}
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-8"
|
||||
isLoading={isLoadingAnonymous}
|
||||
onClick={handleSignInAnonymously}
|
||||
>
|
||||
Sign in Anonymously
|
||||
{t('modals.auth.buttons.anonymous')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -4,6 +4,7 @@ import Modal from '@material-ui/core/Modal';
|
||||
import { isFunction } from 'lodash';
|
||||
import React, { forwardRef, memo, useImperativeHandle } from 'react';
|
||||
import { MdClose } from 'react-icons/md';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../components/shared/Button';
|
||||
import { handleKeyUp } from '../utils';
|
||||
import styles from './BaseModal.module.css';
|
||||
@ -11,6 +12,7 @@ import styles from './BaseModal.module.css';
|
||||
const BaseModal = forwardRef(
|
||||
({ title, state, children, action, hideActions = false, onDestroy }, ref) => {
|
||||
const [open, setOpen] = state;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
@ -47,7 +49,7 @@ const BaseModal = forwardRef(
|
||||
{!hideActions && (
|
||||
<div className={styles.actions}>
|
||||
<Button outline className="mr-8" onClick={handleClose}>
|
||||
Cancel
|
||||
{t('shared.buttons.cancel')}
|
||||
</Button>
|
||||
|
||||
{action}
|
||||
|
||||
@ -2,6 +2,7 @@ import { useFormikContext } from 'formik';
|
||||
import { isEmpty, isFunction } from 'lodash';
|
||||
import React, { memo, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Button from '../components/shared/Button';
|
||||
import ModalContext from '../contexts/ModalContext';
|
||||
import { useDispatch } from '../contexts/ResumeContext';
|
||||
@ -19,9 +20,11 @@ const DataModal = ({
|
||||
}) => {
|
||||
const modalRef = useRef(null);
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [data, setData] = useState(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isEditMode, setEditMode] = useState(false);
|
||||
|
||||
const { emitter } = useContext(ModalContext);
|
||||
@ -41,11 +44,13 @@ const DataModal = ({
|
||||
}, [data]);
|
||||
|
||||
const onSubmit = async (newData) => {
|
||||
setLoading(true);
|
||||
|
||||
if (isEmpty(await validateForm())) {
|
||||
if (isEditMode) {
|
||||
if (data !== newData) {
|
||||
isFunction(onEdit)
|
||||
? onEdit(newData)
|
||||
? await onEdit(newData)
|
||||
: dispatch({
|
||||
type: 'on_edit_item',
|
||||
payload: {
|
||||
@ -58,7 +63,7 @@ const DataModal = ({
|
||||
newData.id = uuidv4();
|
||||
|
||||
isFunction(onCreate)
|
||||
? onCreate(newData)
|
||||
? await onCreate(newData)
|
||||
: dispatch({
|
||||
type: 'on_add_item',
|
||||
payload: {
|
||||
@ -68,6 +73,7 @@ const DataModal = ({
|
||||
});
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
modalRef.current.handleClose();
|
||||
}
|
||||
};
|
||||
@ -80,7 +86,7 @@ const DataModal = ({
|
||||
|
||||
const submitAction = (
|
||||
<Button type="submit" onClick={() => onSubmit(values)}>
|
||||
{getTitle}
|
||||
{loading ? t('shared.buttons.loading') : getTitle}
|
||||
</Button>
|
||||
);
|
||||
|
||||
|
||||
@ -1,36 +1,27 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo, useContext } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../components/shared/Input';
|
||||
import ModalEvents from '../constants/ModalEvents';
|
||||
import DatabaseContext from '../contexts/DatabaseContext';
|
||||
import { getFieldProps } from '../utils';
|
||||
import DataModal from './DataModal';
|
||||
import leftSections from '../data/leftSections';
|
||||
|
||||
const initialValues = {
|
||||
name: '',
|
||||
metadata: {
|
||||
template: 'onyx',
|
||||
font: 'Montserrat',
|
||||
layout: [leftSections.map(({ id, name }) => ({ id, name }))],
|
||||
colors: {
|
||||
text: '#444444',
|
||||
primary: '#5875DB',
|
||||
background: '#FFFFFF',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.min(5, 'Please enter at least 5 characters.')
|
||||
.required('This is a required field.'),
|
||||
});
|
||||
|
||||
const ResumeModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const { createResume, updateResume } = useContext(DatabaseContext);
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string()
|
||||
.min(5, t('shared.forms.validation.min', { number: 5 }))
|
||||
.required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -39,28 +30,22 @@ const ResumeModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Resume"
|
||||
title={{
|
||||
create: 'Create Resume',
|
||||
edit: 'Edit Resume',
|
||||
create: t('dashboard.createResume'),
|
||||
edit: t('dashboard.editResume'),
|
||||
}}
|
||||
onEdit={updateResume}
|
||||
onCreate={createResume}
|
||||
event={ModalEvents.CREATE_RESUME_MODAL}
|
||||
>
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
className="mb-8"
|
||||
placeholder="Full Stack Web Developer"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<p className="leading-loose">
|
||||
You are going to be creating a new resume from scratch, but first,
|
||||
let's give it a name. This can be the name of the role you want
|
||||
to apply for, or if you're making a resume for a friend, you
|
||||
could call it Alex's Resume.
|
||||
</p>
|
||||
<p className="leading-loose">{t('dashboard.helpText')}</p>
|
||||
</DataModal>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
@ -13,14 +14,16 @@ const initialValues = {
|
||||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required('This is a required field.'),
|
||||
awarder: Yup.string().required('This is a required field.'),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const AwardModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required(t('shared.forms.validation.required')),
|
||||
awarder: Yup.string().required(t('shared.forms.validation.required')),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -29,33 +32,33 @@ const AwardModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Award"
|
||||
name={t('builder.sections.award')}
|
||||
path="awards.items"
|
||||
event={ModalEvents.AWARD_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Title"
|
||||
label={t('shared.forms.title')}
|
||||
className="col-span-2"
|
||||
placeholder="Intl. Flutter Hackathon '19"
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Awarder"
|
||||
label={t('builder.awards.awarder')}
|
||||
placeholder="Google"
|
||||
{...getFieldProps(formik, schema, 'awarder')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
label={t('shared.forms.date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
@ -13,14 +14,16 @@ const initialValues = {
|
||||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required('This is a required field.'),
|
||||
issuer: Yup.string().required('This is a required field.'),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const CertificateModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required(t('shared.forms.validation.required')),
|
||||
issuer: Yup.string().required(t('shared.forms.validation.required')),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -29,33 +32,33 @@ const CertificateModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Certificate"
|
||||
name={t('builder.sections.certification')}
|
||||
path="certifications.items"
|
||||
event={ModalEvents.CERTIFICATION_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Title"
|
||||
label={t('shared.forms.title')}
|
||||
className="col-span-2"
|
||||
placeholder="CCNP"
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Issuer"
|
||||
label={t('builder.certifications.issuer')}
|
||||
placeholder="Cisco Systems"
|
||||
{...getFieldProps(formik, schema, 'issuer')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
label={t('shared.forms.date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
@ -16,22 +17,27 @@ const initialValues = {
|
||||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
institution: Yup.string().required('This is a required field.'),
|
||||
field: Yup.string().required('This is a required field.'),
|
||||
degree: Yup.string(),
|
||||
gpa: Yup.string(),
|
||||
startDate: Yup.date().required('This is a required field.'),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, 'End Date must be later than Start Date'),
|
||||
),
|
||||
summary: Yup.string().min(10, 'Please enter at least 10 characters.'),
|
||||
});
|
||||
|
||||
const EducationModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
institution: Yup.string().required(t('shared.forms.validation.required')),
|
||||
field: Yup.string().required(t('shared.forms.validation.required')),
|
||||
degree: Yup.string(),
|
||||
gpa: Yup.string(),
|
||||
startDate: Yup.date().required(t('shared.forms.validation.required')),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, t('shared.forms.validation.dateRange')),
|
||||
),
|
||||
summary: Yup.string().min(
|
||||
10,
|
||||
t('shared.forms.validation.min', { number: 10 }),
|
||||
),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -40,54 +46,54 @@ const EducationModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Education"
|
||||
name={t('builder.sections.education')}
|
||||
path="education.items"
|
||||
event={ModalEvents.EDUCATION_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Institution"
|
||||
label={t('builder.education.institution')}
|
||||
className="col-span-2"
|
||||
placeholder="Dayananda Sagar College of Engineering"
|
||||
{...getFieldProps(formik, schema, 'institution')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Field of Study"
|
||||
label={t('builder.education.field')}
|
||||
className="col-span-2"
|
||||
placeholder="Computer Science & Engineering"
|
||||
{...getFieldProps(formik, schema, 'field')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Degree Type"
|
||||
label={t('builder.education.degree')}
|
||||
placeholder="Bachelor's Degree"
|
||||
{...getFieldProps(formik, schema, 'degree')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="GPA"
|
||||
label={t('builder.education.gpa')}
|
||||
placeholder="8.8"
|
||||
{...getFieldProps(formik, schema, 'gpa')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Start Date"
|
||||
label={t('shared.forms.startDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'startDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="End Date"
|
||||
label={t('shared.forms.endDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'endDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import firebase from 'gatsby-plugin-firebase';
|
||||
import { clone } from 'lodash';
|
||||
import React, { memo, useContext, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaPrint } from 'react-icons/fa';
|
||||
import Button from '../../components/shared/Button';
|
||||
import ModalContext from '../../contexts/ModalContext';
|
||||
@ -10,6 +11,7 @@ import BaseModal from '../BaseModal';
|
||||
|
||||
const ExportModal = () => {
|
||||
const state = useSelector();
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isLoadingSingle, setLoadingSingle] = useState(false);
|
||||
const [isLoadingMulti, setLoadingMulti] = useState(false);
|
||||
@ -72,35 +74,31 @@ const ExportModal = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseModal hideActions state={[open, setOpen]} title="Export Your Resume">
|
||||
<BaseModal
|
||||
hideActions
|
||||
state={[open, setOpen]}
|
||||
title={t('builder.actions.export.heading')}
|
||||
>
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
Use Browser's Print Dialog
|
||||
{t('modals.export.printDialog.heading')}
|
||||
</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
For those of you who want a quick solution, you need not look any
|
||||
further than your browser. All you have to do is press Ctrl/Cmd + P
|
||||
and open up the print dialog on your browser and get your resume
|
||||
printed immediately.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.export.printDialog.text')}</p>
|
||||
|
||||
<Button icon={FaPrint} className="mt-5" onClick={handleOpenPrintDialog}>
|
||||
Print Resume
|
||||
{t('modals.export.printDialog.button')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Download PDF</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.export.downloadPDF.heading')}
|
||||
</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
These options allow you to print a single page, unconstrained version
|
||||
of your resume, perfect for those who have a lot of content.
|
||||
Alternatively, you could download a multi-page version of your resume
|
||||
as well with just one click.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.export.downloadPDF.text')}</p>
|
||||
|
||||
<div className="mt-5 mb-4">
|
||||
<div className="flex">
|
||||
@ -108,14 +106,14 @@ const ExportModal = () => {
|
||||
isLoading={isLoadingSingle}
|
||||
onClick={handleSinglePageDownload}
|
||||
>
|
||||
Single Page Resume
|
||||
{t('modals.export.downloadPDF.buttons.single')}
|
||||
</Button>
|
||||
<Button
|
||||
className="ml-8"
|
||||
isLoading={isLoadingMulti}
|
||||
onClick={handleMultiPageDownload}
|
||||
>
|
||||
Multi Page Resume
|
||||
{t('modals.export.downloadPDF.buttons.multi')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -124,18 +122,18 @@ const ExportModal = () => {
|
||||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Export to JSON Format</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.export.jsonFormat.heading')}
|
||||
</h5>
|
||||
|
||||
<p className="leading-loose">
|
||||
You can also export your data into JSON format for safe keeping so
|
||||
that you can easily import it back into Reactive Resume whenever you
|
||||
want to edit or generate a resume.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.export.jsonFormat.text')}</p>
|
||||
|
||||
<div className="mt-5">
|
||||
<Button onClick={handleExportToJson}>Export JSON</Button>
|
||||
<Button onClick={handleExportToJson}>
|
||||
{t('modals.export.jsonFormat.button')}
|
||||
</Button>
|
||||
<a id="downloadAnchor" className="hidden">
|
||||
Export JSON
|
||||
{t('modals.export.jsonFormat.button')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
@ -10,11 +11,13 @@ const initialValues = {
|
||||
name: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
});
|
||||
|
||||
const HobbyModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -23,13 +26,13 @@ const HobbyModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Hobby"
|
||||
name={t('builder.sections.hobby')}
|
||||
path="hobbies.items"
|
||||
event={ModalEvents.HOBBY_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="Fishing"
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
import Ajv from 'ajv';
|
||||
import React, { memo, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'react-toastify';
|
||||
import Button from '../../components/shared/Button';
|
||||
import ModalContext from '../../contexts/ModalContext';
|
||||
@ -10,6 +11,7 @@ import BaseModal from '../BaseModal';
|
||||
|
||||
const ImportModal = () => {
|
||||
const ajv = new Ajv();
|
||||
const { t } = useTranslation();
|
||||
const fileInputRef = useRef(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
@ -38,21 +40,22 @@ const ImportModal = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseModal hideActions state={[open, setOpen]} title="Import Your Resume">
|
||||
<BaseModal
|
||||
hideActions
|
||||
state={[open, setOpen]}
|
||||
title={t('builder.actions.import.heading')}
|
||||
>
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
Import from Reactive Resume
|
||||
{t('modals.import.reactiveResume.heading')}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
Reactive Resume has it's own schema format to make the most of
|
||||
all the customizable capabilities it has to offer. If you'd like
|
||||
to import a backup of your resume made with this app, just upload the
|
||||
file using the button below.
|
||||
<p className="leading-loose">
|
||||
{t('modals.import.reactiveResume.text')}
|
||||
</p>
|
||||
|
||||
<Button className="mt-5" onClick={() => fileInputRef.current.click()}>
|
||||
Select File
|
||||
{t('modals.import.button')}
|
||||
</Button>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
@ -65,18 +68,15 @@ const ImportModal = () => {
|
||||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Import from JSON Resume</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.import.jsonResume.heading')}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
<a href="https://jsonresume.org/">JSON Resume</a> is an open standard
|
||||
for resume schema structure. If you are one of the many enthusiasts
|
||||
who have their resume ready in this format, all it takes it just one
|
||||
click to get started with Reactive Resume.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.import.jsonResume.text')}</p>
|
||||
|
||||
<Tooltip title="Coming Soon" placement="right" arrow>
|
||||
<div className="mt-5 inline-block">
|
||||
<Button className="opacity-50">Select File</Button>
|
||||
<Button className="opacity-50">{t('modals.import.button')}</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
@ -84,16 +84,15 @@ const ImportModal = () => {
|
||||
<hr className="my-8" />
|
||||
|
||||
<div>
|
||||
<h5 className="text-xl font-semibold mb-4">Import from LinkedIn</h5>
|
||||
<h5 className="text-xl font-semibold mb-4">
|
||||
{t('modals.import.linkedIn.heading')}
|
||||
</h5>
|
||||
|
||||
<p>
|
||||
You can import a JSON that was exported from Reactive Resume by
|
||||
clicking on the button below and selecting the appropriate file.
|
||||
</p>
|
||||
<p className="leading-loose">{t('modals.import.linkedIn.text')}</p>
|
||||
|
||||
<Tooltip title="Coming Soon" placement="right" arrow>
|
||||
<div className="mt-5 inline-block">
|
||||
<Button className="opacity-50">Select File</Button>
|
||||
<Button className="opacity-50">{t('modals.import.button')}</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
@ -11,12 +12,14 @@ const initialValues = {
|
||||
fluency: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
fluency: Yup.string().required('This is a required field.'),
|
||||
});
|
||||
|
||||
const LanguageModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
fluency: Yup.string().required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -25,19 +28,19 @@ const LanguageModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Language"
|
||||
name={t('builder.sections.language')}
|
||||
path="languages.items"
|
||||
event={ModalEvents.LANGUAGE_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="German"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Fluency"
|
||||
label={t('builder.languages.fluency')}
|
||||
placeholder="Native/B1"
|
||||
{...getFieldProps(formik, schema, 'fluency')}
|
||||
/>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
@ -13,14 +14,16 @@ const initialValues = {
|
||||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required('This is a required field.'),
|
||||
link: Yup.string().url('Must be a valid URL'),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const ProjectModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
title: Yup.string().required(t('shared.forms.validation.required')),
|
||||
link: Yup.string().url(t('shared.forms.validation.url')),
|
||||
date: Yup.date().max(new Date()),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -29,33 +32,33 @@ const ProjectModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Project"
|
||||
name={t('builder.sections.project')}
|
||||
path="projects.items"
|
||||
event={ModalEvents.PROJECT_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Title"
|
||||
label={t('shared.forms.title')}
|
||||
className="col-span-2"
|
||||
placeholder="Reactive Resume"
|
||||
{...getFieldProps(formik, schema, 'title')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Link"
|
||||
label={t('shared.forms.website')}
|
||||
placeholder="https://github.com/AmruthPillai/Reactive-Resume"
|
||||
{...getFieldProps(formik, schema, 'link')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Date"
|
||||
label={t('shared.forms.date')}
|
||||
{...getFieldProps(formik, schema, 'date')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
@ -14,15 +15,17 @@ const initialValues = {
|
||||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
position: Yup.string().required('This is a required field.'),
|
||||
phone: Yup.string(),
|
||||
email: Yup.string().email('Must be a valid email address.'),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
const ReferenceModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
position: Yup.string().required(t('shared.forms.validation.required')),
|
||||
phone: Yup.string(),
|
||||
email: Yup.string().email(t('shared.forms.validation.email')),
|
||||
summary: Yup.string(),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -31,38 +34,38 @@ const ReferenceModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Reference"
|
||||
name={t('builder.sections.reference')}
|
||||
path="references.items"
|
||||
event={ModalEvents.REFERENCE_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="Jane Doe"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="position"
|
||||
label={t('shared.forms.position')}
|
||||
placeholder="Assistant Manager"
|
||||
{...getFieldProps(formik, schema, 'position')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Phone Number"
|
||||
label={t('shared.forms.phone')}
|
||||
placeholder="+1 (708) 756-6065"
|
||||
{...getFieldProps(formik, schema, 'phone')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Email Address"
|
||||
label={t('shared.forms.email')}
|
||||
placeholder="janedoe@example.com"
|
||||
{...getFieldProps(formik, schema, 'email')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
@ -19,14 +20,16 @@ const initialValues = {
|
||||
level: SKILL_LEVELS[0],
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required('This is a required field.'),
|
||||
level: Yup.string()
|
||||
.oneOf(SKILL_LEVELS, 'Must be one of the options above.')
|
||||
.required('This is a required field.'),
|
||||
});
|
||||
|
||||
const SkillModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
name: Yup.string().required(t('shared.forms.validation.required')),
|
||||
level: Yup.string()
|
||||
.oneOf(SKILL_LEVELS)
|
||||
.required(t('shared.forms.validation.required')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -35,19 +38,19 @@ const SkillModal = () => {
|
||||
>
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
name="Skill"
|
||||
name={t('builder.sections.skill')}
|
||||
path="skills.items"
|
||||
event={ModalEvents.SKILL_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Name"
|
||||
label={t('shared.forms.name')}
|
||||
placeholder="ReactJS"
|
||||
{...getFieldProps(formik, schema, 'name')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Level"
|
||||
label={t('builder.skills.level')}
|
||||
type="dropdown"
|
||||
options={SKILL_LEVELS}
|
||||
{...getFieldProps(formik, schema, 'level')}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import * as Yup from 'yup';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
@ -12,18 +13,20 @@ const initialValues = {
|
||||
username: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
network: Yup.string()
|
||||
.min(5, 'Please enter at least 5 characters.')
|
||||
.required('This is a required field.'),
|
||||
username: Yup.string().required('This is a required field.'),
|
||||
url: Yup.string()
|
||||
.min(5, 'Please enter at least 5 characters.')
|
||||
.required('This is a required field.')
|
||||
.url('Must be a valid URL'),
|
||||
});
|
||||
|
||||
const SocialModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
network: Yup.string()
|
||||
.min(5, t('shared.forms.validation.min', { number: 5 }))
|
||||
.required(t('shared.forms.validation.required')),
|
||||
username: Yup.string().required(t('shared.forms.validation.required')),
|
||||
url: Yup.string()
|
||||
.min(5, t('shared.forms.validation.min', { number: 5 }))
|
||||
.required(t('shared.forms.validation.required'))
|
||||
.url(t('shared.forms.validation.url')),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -33,24 +36,24 @@ const SocialModal = () => {
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
path="social.items"
|
||||
name="Social Network"
|
||||
name={t('builder.sections.social')}
|
||||
event={ModalEvents.SOCIAL_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Network"
|
||||
label={t('builder.social.network')}
|
||||
placeholder="Twitter"
|
||||
{...getFieldProps(formik, schema, 'network')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Username"
|
||||
label={t('builder.social.username')}
|
||||
placeholder="KingOKings"
|
||||
{...getFieldProps(formik, schema, 'username')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="URL"
|
||||
label={t('builder.social.url')}
|
||||
className="col-span-2"
|
||||
placeholder="https://twitter.com/KingOKings"
|
||||
{...getFieldProps(formik, schema, 'url')}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Formik } from 'formik';
|
||||
import React, { memo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Input from '../../components/shared/Input';
|
||||
import ModalEvents from '../../constants/ModalEvents';
|
||||
import { getFieldProps } from '../../utils';
|
||||
@ -15,21 +16,26 @@ const initialValues = {
|
||||
summary: '',
|
||||
};
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
company: Yup.string().required('This is a required field.'),
|
||||
position: Yup.string().required('This is a required field.'),
|
||||
website: Yup.string().url('Must be a valid URL'),
|
||||
startDate: Yup.date().required('This is a required field.'),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, 'End Date must be later than Start Date'),
|
||||
),
|
||||
summary: Yup.string().min(10, 'Please enter at least 10 characters.'),
|
||||
});
|
||||
|
||||
const WorkModal = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const schema = Yup.object().shape({
|
||||
company: Yup.string().required(t('shared.forms.validation.required')),
|
||||
position: Yup.string().required(t('shared.forms.validation.required')),
|
||||
website: Yup.string().url(t('shared.forms.validation.url')),
|
||||
startDate: Yup.date().required(t('shared.forms.validation.required')),
|
||||
endDate: Yup.date().when(
|
||||
'startDate',
|
||||
(startDate, yupSchema) =>
|
||||
startDate &&
|
||||
yupSchema.min(startDate, t('shared.forms.validation.dateRange')),
|
||||
),
|
||||
summary: Yup.string().min(
|
||||
10,
|
||||
t('shared.forms.validation.min', { number: 10 }),
|
||||
),
|
||||
});
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validateOnBlur
|
||||
@ -39,46 +45,46 @@ const WorkModal = () => {
|
||||
{(formik) => (
|
||||
<DataModal
|
||||
path="work.items"
|
||||
name="Work Experience"
|
||||
name={t('builder.sections.work')}
|
||||
event={ModalEvents.WORK_MODAL}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
<Input
|
||||
label="Company"
|
||||
label={t('builder.work.company')}
|
||||
className="col-span-2"
|
||||
placeholder="Postdot Technologies Pvt. Ltd."
|
||||
{...getFieldProps(formik, schema, 'company')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Position"
|
||||
label={t('shared.forms.position')}
|
||||
placeholder="Full Stack Web Developer"
|
||||
{...getFieldProps(formik, schema, 'position')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Website"
|
||||
label={t('shared.forms.website')}
|
||||
placeholder="https://example.com/"
|
||||
{...getFieldProps(formik, schema, 'website')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="Start Date"
|
||||
label={t('shared.forms.startDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'startDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="date"
|
||||
label="End Date"
|
||||
label={t('shared.forms.endDate')}
|
||||
placeholder="6th August 208"
|
||||
{...getFieldProps(formik, schema, 'endDate')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="textarea"
|
||||
label="Summary"
|
||||
label={t('shared.forms.summary')}
|
||||
className="col-span-2"
|
||||
{...getFieldProps(formik, schema, 'summary')}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user