- implement i18n

- translation dynamic for sections
- added articles for SEO
This commit is contained in:
Amruth Pillai
2020-07-16 08:42:19 +05:30
parent b7c565de79
commit a7657b4a5c
74 changed files with 2373 additions and 586 deletions

View File

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

View File

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

View File

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

View File

@ -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&apos;s give it a name. This can be the name of the role you want
to apply for, or if you&apos;re making a resume for a friend, you
could call it Alex&apos;s Resume.
</p>
<p className="leading-loose">{t('dashboard.helpText')}</p>
</DataModal>
)}
</Formik>

View File

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

View File

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

View File

@ -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 &amp; 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')}
/>

View File

@ -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&apos;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>

View File

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

View File

@ -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&apos;s own schema format to make the most of
all the customizable capabilities it has to offer. If you&apos;d like
to import a backup of your resume made with this app, just upload the
file using the button below.
<p 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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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