diff --git a/package.json b/package.json index 16954ab..97aaa4e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "formik": "^2.2.6", "history": "4.10.1", "immer": "^8.0.0", - "joi": "^17.3.0", "lodash": "^4.17.20", "prop-types": "^15.7.2", "quagga": "^0.12.1", @@ -31,7 +30,8 @@ "redux-saga": "^1.1.3", "reselect": "^4.0.0", "web-vitals": "^0.2.4", - "yarn": "^1.22.10" + "yarn": "^1.22.10", + "yup": "^0.32.8" }, "scripts": { "start": "react-scripts start", diff --git a/src/components/InputField/index.js b/src/components/InputField/index.js index 073d3dc..1b85262 100644 --- a/src/components/InputField/index.js +++ b/src/components/InputField/index.js @@ -4,9 +4,10 @@ import { useField } from 'formik'; import { TextField } from '@material-ui/core'; const InputField = (props) => { + const { errorText, ...rest } = props; const [field, meta] = useField(props); - const _renderHelperText = () => { + const renderHelperText = () => { const [touched, error] = at(meta, 'touched', 'error'); if (touched && error) { return error; @@ -17,9 +18,9 @@ const InputField = (props) => { ); } diff --git a/src/components/PersonalDetailsForm/index.js b/src/components/PersonalDetailsForm/index.js deleted file mode 100644 index af8d426..0000000 --- a/src/components/PersonalDetailsForm/index.js +++ /dev/null @@ -1,132 +0,0 @@ -import 'date-fns'; -import React from 'react'; -import {Typography, InputLabel, Box, Grid, InputAdornment, Select, FormControl, MenuItem, TextField, Slider} from '@material-ui/core'; -import DateFnsUtils from '@date-io/date-fns'; -import { useFormContext, Controller } from "react-hook-form"; -import { - MuiPickersUtilsProvider, - DatePicker, -} from '@material-ui/pickers'; -import {useInjectReducer} from "redux-injectors"; -import { useSelector } from 'react-redux'; -import {createStructuredSelector} from "reselect"; -import reducer from "pages/Profile/reducer"; -import { - makeSelectActivities, - makeSelectGenders, -} from "pages/Profile/selectors"; - -const stateSelector = createStructuredSelector({ - activities: makeSelectActivities(), - genders: makeSelectGenders(), -}); - -const key = 'profilePage' -const PersonalDetailsForm = () => { - useInjectReducer({ key, reducer }); - const { activities, genders } = useSelector(stateSelector) - const { register, control } = useFormContext() - - - return ( - - - Personal details - - - - - Gender - - {genders.map(label => ( - {label} - ))} - - } - /> - - - - selected} - name="birthday" - render={({ onChange, value }) => ( - - - - )} - /> - - - cm - }} - /> - } - /> - - - Kg - }} - /> - } - /> - - - Kg - }} - /> - } - /> - - - - ); -} -export default PersonalDetailsForm diff --git a/src/components/SelectField/index.js b/src/components/SelectField/index.js index 417262d..5a52271 100644 --- a/src/components/SelectField/index.js +++ b/src/components/SelectField/index.js @@ -16,7 +16,7 @@ const SelectField = ({ label, data, ...rest }) => { const [touched, error] = at(meta, 'touched', 'error'); const isError = touched && error && true; - const _renderHelperText = () => { + const renderHelperText = () => { if (isError) { return {error}; } @@ -32,16 +32,13 @@ const SelectField = ({ label, data, ...rest }) => { ))} - {_renderHelperText()} + {renderHelperText()} ); } -SelectField.defaultProps = { - data: [] -}; - SelectField.propTypes = { + label: PropTypes.string.isRequired, data: PropTypes.array.isRequired }; diff --git a/src/pages/Profile/FormModel/formInitialValues.js b/src/pages/Profile/FormModel/formInitialValues.js new file mode 100644 index 0000000..c2981e4 --- /dev/null +++ b/src/pages/Profile/FormModel/formInitialValues.js @@ -0,0 +1,26 @@ +import checkoutFormModel from './profileFormModel'; +const { + formField: { + gender, + goal, + birthday, + height, + currentWeight, + goalWeight, + rateOfChange, + activity + } +} = checkoutFormModel; + +const formInitialValues = { + [gender.name]: '', + [goal.name]: '', + [birthday.name]: '', + [height.name]: '', + [currentWeight.name]: '', + [goalWeight.name]: '', + [rateOfChange.name]: '', + [activity.name]: '', +}; + +export default formInitialValues diff --git a/src/pages/Profile/FormModel/profileFormModel.js b/src/pages/Profile/FormModel/profileFormModel.js new file mode 100644 index 0000000..f2e5274 --- /dev/null +++ b/src/pages/Profile/FormModel/profileFormModel.js @@ -0,0 +1,51 @@ +const profileFormModel = { + formId: 'createProfileForm', + formField: { + gender: { + name: 'gender', + label: 'Gender*', + requiredErrorMsg: 'Gender is required' + }, + goal: { + name: 'goal', + label: 'Goal*', + requiredErrorMsg: 'Goal is required' + }, + birthday: { + name: 'birthday', + label: 'Birthday*', + requiredErrorMsg: 'Birthday date is required', + invalidErrorMsg: 'Birthday date is not valid' + }, + height: { + name: 'height', + label: 'Height*', + requiredErrorMsg: 'Height is required', + invalidErrorMsg: 'Height is invalid (e.g. 175)' + }, + currentWeight: { + name: 'currentWeight', + label: 'Current weight*', + requiredErrorMsg: 'Current weight is required', + invalidErrorMsg: 'Current weight is invalid (e.g. 75)' + }, + goalWeight: { + name: 'goalWeight', + label: 'Goal weight*', + requiredErrorMsg: 'Goal weight is required', + invalidErrorMsg: 'Goal weight is invalid (e.g. 70)' + }, + rateOfChange: { + name: 'rateOfChange', + label: 'Rate of change*', + requiredErrorMsg: 'Rate of change is required' + }, + activity: { + name: 'activity', + label: 'Activity*', + requiredErrorMsg: 'Activity is required' + }, + } +}; + +export default profileFormModel diff --git a/src/pages/Profile/FormModel/validationSchema.js b/src/pages/Profile/FormModel/validationSchema.js new file mode 100644 index 0000000..9d140d4 --- /dev/null +++ b/src/pages/Profile/FormModel/validationSchema.js @@ -0,0 +1,46 @@ +import * as Yup from 'yup'; +import checkoutFormModel from './profileFormModel'; +const { + formField: { + gender, + goal, + birthday, + height, + currentWeight, + goalWeight, + rateOfChange, + activity + } +} = checkoutFormModel; + +const validationSchema = [ + Yup.object().shape({ + [goal.name]: Yup.string().required(`${goal.requiredErrorMsg}`), + [rateOfChange.name]: Yup.string().required(`${goal.requiredErrorMsg}`), + [activity.name]: Yup.string().required(`${activity.requiredErrorMsg}`), + }), + Yup.object().shape({ + [gender.name]: Yup.string().required(`${gender.requiredErrorMsg}`), + [birthday.name]: Yup.string().required(`${birthday.requiredErrorMsg}`), + [height.name]: Yup.string().required(`${height.requiredErrorMsg}`) + .test( + 'len', + `${height.invalidErrorMsg}`, + value => value && value.toString().length === 3 + ), + [currentWeight.name]: Yup.string().required(`${currentWeight.requiredErrorMsg}`) + .test( + 'len', + `${currentWeight.invalidErrorMsg}`, + value => value && value.toString().length >= 2 && value.toString().length <= 3 + ), + [goalWeight.name]: Yup.string().required(`${goalWeight.requiredErrorMsg}`) + .test( + 'len', + `${goalWeight.invalidErrorMsg}`, + value => value && value.toString().length >= 2 && value.toString().length <= 3 + ), + }) +]; + +export default validationSchema diff --git a/src/pages/Profile/GoalForm/index.js b/src/pages/Profile/Forms/GoalForm/index.js similarity index 73% rename from src/pages/Profile/GoalForm/index.js rename to src/pages/Profile/Forms/GoalForm/index.js index e578f62..830def1 100644 --- a/src/pages/Profile/GoalForm/index.js +++ b/src/pages/Profile/Forms/GoalForm/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import {Grid, Typography, Box} from '@material-ui/core'; +import {Grid, Typography} from '@material-ui/core'; import {useInjectReducer} from "redux-injectors"; import { useSelector } from 'react-redux'; import {createStructuredSelector} from "reselect"; @@ -19,7 +19,13 @@ const stateSelector = createStructuredSelector({ }); const key = 'profilePage' -const GoalForm = () => { +const GoalForm = ({ + formField: { + rateOfChange, + goal, + activity + } +}) => { useInjectReducer({ key, reducer }); const { goals, activities, ratesOfChange } = useSelector(stateSelector) @@ -30,19 +36,19 @@ const GoalForm = () => { - + Activity - + Rate of change - + diff --git a/src/pages/Profile/PersonalDetailsForm/index.js b/src/pages/Profile/Forms/PersonalDetailsForm/index.js similarity index 75% rename from src/pages/Profile/PersonalDetailsForm/index.js rename to src/pages/Profile/Forms/PersonalDetailsForm/index.js index 1b6cc5f..7f6fd23 100644 --- a/src/pages/Profile/PersonalDetailsForm/index.js +++ b/src/pages/Profile/Forms/PersonalDetailsForm/index.js @@ -1,6 +1,6 @@ import 'date-fns'; import React from 'react'; -import {Typography, InputLabel, Grid, InputAdornment, Select, FormControl, MenuItem, TextField, Slider} from '@material-ui/core'; +import {Typography, Grid, InputAdornment} from '@material-ui/core'; import DatePickerField from 'components/DatePickerField' import InputField from 'components/InputField' import {useInjectReducer} from "redux-injectors"; @@ -11,7 +11,7 @@ import { makeSelectActivities, makeSelectGenders, } from "pages/Profile/selectors"; -import SelectField from "../../../components/SelectField"; +import SelectField from "components/SelectField"; const stateSelector = createStructuredSelector({ activities: makeSelectActivities(), @@ -19,7 +19,15 @@ const stateSelector = createStructuredSelector({ }); const key = 'profilePage' -const PersonalDetailsForm = () => { +const Index = ({ + formField: { + gender, + birthday, + height, + currentWeight, + goalWeight, + } +}) => { useInjectReducer({ key, reducer }); const { genders } = useSelector(stateSelector) @@ -30,12 +38,12 @@ const PersonalDetailsForm = () => { - + { { { { ); } -export default PersonalDetailsForm +export default Index diff --git a/src/components/ReviewProfileForm/index.js b/src/pages/Profile/Forms/ReviewProfileForm/index.js similarity index 100% rename from src/components/ReviewProfileForm/index.js rename to src/pages/Profile/Forms/ReviewProfileForm/index.js diff --git a/src/pages/Profile/ReviewProfileForm/index.js b/src/pages/Profile/ReviewProfileForm/index.js deleted file mode 100644 index 1231114..0000000 --- a/src/pages/Profile/ReviewProfileForm/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -const ReviewProfileForm = () => { - return ( -
- ReviewProfileForm -
- ); -}; - -export default ReviewProfileForm; diff --git a/src/pages/Profile/actions.js b/src/pages/Profile/actions.js index aa0bb98..3310ee5 100644 --- a/src/pages/Profile/actions.js +++ b/src/pages/Profile/actions.js @@ -38,13 +38,13 @@ export const updateProfileAction = () => ({ type: UPDATE_PROFILE_REQUEST, }) -export const updateProfileSuccessAction = ({birthday, goal, gender, height, weight, goalWeight, rateOfChange, activity}) => ({ +export const updateProfileSuccessAction = ({birthday, goal, gender, height, currentWeight, goalWeight, rateOfChange, activity}) => ({ type: UPDATE_PROFILE_SUCCESS, birthday, gender, goal, height, - weight, + currentWeight, goalWeight, rateOfChange, activity diff --git a/src/pages/Profile/index.js b/src/pages/Profile/index.js index ad1ca89..dff0358 100644 --- a/src/pages/Profile/index.js +++ b/src/pages/Profile/index.js @@ -1,57 +1,27 @@ import React, { useState } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; import {Paper, Stepper, Step, StepLabel, Button, Typography } from '@material-ui/core'; import {useInjectReducer, useInjectSaga} from "redux-injectors"; import { useDispatch } from 'react-redux'; import { Formik, Form } from 'formik'; -import GoalForm from './GoalForm'; -import PersonalDetailsForm from './PersonalDetailsForm'; -import ReviewProfileForm from './ReviewProfileForm'; +import GoalForm from './Forms/GoalForm'; +import PersonalDetailsForm from './Forms/PersonalDetailsForm'; +import ReviewProfileForm from './Forms/ReviewProfileForm'; import reducer from "./reducer"; import saga from "./saga"; - -const useStyles = makeStyles((theme) => ({ - layout: { - width: 'auto', - marginLeft: theme.spacing(2), - marginRight: theme.spacing(2), - [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: { - width: 600, - marginLeft: 'auto', - marginRight: 'auto', - }, - }, - paper: { - marginTop: theme.spacing(3), - marginBottom: theme.spacing(3), - padding: theme.spacing(2), - [theme.breakpoints.up(600 + theme.spacing(3) * 2)]: { - marginTop: theme.spacing(6), - marginBottom: theme.spacing(6), - padding: theme.spacing(3), - }, - }, - stepper: { - padding: theme.spacing(3, 0, 5), - }, - buttons: { - display: 'flex', - justifyContent: 'flex-end', - }, - button: { - marginTop: theme.spacing(3), - marginLeft: theme.spacing(1), - }, -})); +import useStyles from './styles'; +import validationSchema from './FormModel/validationSchema' +import formInitialValues from './FormModel/formInitialValues' +import profileFormModel from './FormModel/profileFormModel' const steps = ['Your goal', 'Personal details', 'Review your profile']; +const { formId, formField } = profileFormModel; const renderStepContent = (step) => { switch (step) { case 0: - return ; + return ; case 1: - return ; + return ; case 2: return ; default: @@ -59,37 +29,19 @@ const renderStepContent = (step) => { } } -const formInitialValues = { - gender: '', - goal: '', - birthday: '', - height: '', - weight: { - current: '', - goal: '', - }, - rateOfChange: '', - activity: '', -} - const key = 'profilePage' const ProfilePage = () => { const [activeStep, setActiveStep] = useState(0); + const currentValidationSchema = validationSchema[activeStep]; const isLastStep = activeStep === steps.length - 1; - const isFirstStep = activeStep === 0; const classes = useStyles(); useInjectReducer({ key, reducer }); useInjectSaga({ key, saga }); - const sleep = (ms) => { - return new Promise(resolve => setTimeout(resolve, ms)); - } - const submitForm = async (values, actions) => { console.log(JSON.stringify(values, null, 2)); actions.setSubmitting(false); - setActiveStep(activeStep + 1); } @@ -107,10 +59,6 @@ const ProfilePage = () => { setActiveStep(activeStep - 1); } - const handleNext = () => { - setActiveStep(activeStep + 1); - } - return (
@@ -138,10 +86,11 @@ const ProfilePage = () => { ) : ( {({ isSubmitting }) => ( -
+ {renderStepContent(activeStep)}
{activeStep !== 0 && ( @@ -153,8 +102,7 @@ const ProfilePage = () => { variant="contained" color="primary" disabled={isSubmitting} - onClick={handleNext} - type={isLastStep ? 'submit' : 'button'} + type="submit" className={classes.button} > {isLastStep ? 'Create profile' : 'Next'} diff --git a/src/pages/Profile/reducer.js b/src/pages/Profile/reducer.js index 34dbb50..082799d 100644 --- a/src/pages/Profile/reducer.js +++ b/src/pages/Profile/reducer.js @@ -11,6 +11,7 @@ import { export const initialState = { activities: [ + { label: 'None', value: undefined }, { label: 'very low', value: 1.2 @@ -32,6 +33,10 @@ export const initialState = { }, ], goals: [ + { + label: 'None', + value: undefined, + }, { label: 'lose weight', value: 1, @@ -46,13 +51,15 @@ export const initialState = { } ], ratesOfChange: [ - { value: 1, label: '0 kg'}, - { value: 2, label: '0.5 kg'}, - { value: 3, label: '1 kg'}, + { label: 'None', value: undefined }, + { value: 1, label: '0 kg' }, + { value: 2, label: '0.5 kg' }, + { value: 3, label: '1 kg' }, ], genders: [ - {value: 1, label: 'male'}, - {value: 2, label: 'female'}, + { label: 'None', value: undefined }, + {value: 1, label: 'male' }, + {value: 2, label: 'female' }, ], isLoading: false, error: {}, @@ -60,10 +67,8 @@ export const initialState = { goal: 0, birthday: new Date(), height: 0, - weight: { - current: 0, - goal: 0, - }, + currentWeight: 0, + goalWeight: 0, rateOfChange: 0, activity: 0, }; @@ -76,8 +81,8 @@ const loginPageReducer = produce((draft, action) => { draft.gender = action.gender; draft.goal = action.goal; draft.height = action.height; - draft.weight.current = action.weight.current; - draft.weight.goal = action.weight.goal; + draft.currentWeight = action.currentWeight; + draft.goalWeight = action.goalWeight; draft.rateOfChange = action.rateOfChange; draft.activity = action.activity; draft.isLoading = false; diff --git a/src/pages/Profile/saga.js b/src/pages/Profile/saga.js index bb439c5..dc4b8ee 100644 --- a/src/pages/Profile/saga.js +++ b/src/pages/Profile/saga.js @@ -4,7 +4,8 @@ import { GET_PROFILE_REQUEST, UPDATE_PROFILE_REQUEST } from './constants'; import { makeSelectBirthday, makeSelectHeight, - makeSelectWeight, + makeSelectCurrentWeight, + makeSelectGoalWeight, makeSelectRateOfChange, makeSelectActivity, makeSelectGoal, @@ -41,7 +42,8 @@ export function* updateProfile() { const goal = yield select(makeSelectGoal()); const birthday = yield select(makeSelectBirthday()); const height = yield select(makeSelectHeight()); - const weight = yield select(makeSelectWeight()); + const currentWeight = yield select(makeSelectCurrentWeight()); + const goalWeight = yield select(makeSelectGoalWeight()); const rateOfChange = yield select(makeSelectRateOfChange()); const activity = yield select(makeSelectActivity()); @@ -50,12 +52,12 @@ export function* updateProfile() { const requestParameters = { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${access.token}`, }, - body: JSON.stringify({ birthday, gender, height, weight, goal, rateOfChange, activity }), + body: JSON.stringify({ birthday, gender, height, currentWeight, goalWeight, goal, rateOfChange, activity }), }; try { - const { birthday, gender, height, weight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters); - yield put(updateProfileSuccessAction({birthday, gender, height, weight, goalWeight, rateOfChange, activity})); + const { birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters); + yield put(updateProfileSuccessAction({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity})); } catch (error) { yield put(updateProfileErrorAction({error: error.message})); } diff --git a/src/pages/Profile/selectors.js b/src/pages/Profile/selectors.js index a209fde..4520f06 100644 --- a/src/pages/Profile/selectors.js +++ b/src/pages/Profile/selectors.js @@ -10,10 +10,13 @@ const makeSelectBirthday = () => createSelector(selectProfilePageDomain, (substate) => substate.birthday); const makeSelectHeight = () => - createSelector(selectProfilePageDomain, (substate) => substate.height); + createSelector(selectProfilePageDomain, (substate) => substate.height) -const makeSelectWeight = () => - createSelector(selectProfilePageDomain, (substate) => substate.weight); +const makeSelectCurrentWeight = () => + createSelector(selectProfilePageDomain, (substate) => substate.currentWeight); + +const makeSelectGoalWeight = () => + createSelector(selectProfilePageDomain, (substate) => substate.goalWeight); const makeSelectRateOfChange = () => createSelector(selectProfilePageDomain, (substate) => substate.rateOfChange); @@ -48,7 +51,8 @@ export { makeSelectError, makeSelectBirthday, makeSelectHeight, - makeSelectWeight, + makeSelectCurrentWeight, + makeSelectGoalWeight, makeSelectRateOfChange, makeSelectActivity, makeSelectGoal, diff --git a/src/pages/Profile/styles.js b/src/pages/Profile/styles.js new file mode 100644 index 0000000..9baa92b --- /dev/null +++ b/src/pages/Profile/styles.js @@ -0,0 +1,37 @@ +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme) => ({ + layout: { + width: 'auto', + marginLeft: theme.spacing(2), + marginRight: theme.spacing(2), + [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: { + width: 600, + marginLeft: 'auto', + marginRight: 'auto', + }, + }, + paper: { + marginTop: theme.spacing(3), + marginBottom: theme.spacing(3), + padding: theme.spacing(2), + [theme.breakpoints.up(600 + theme.spacing(3) * 2)]: { + marginTop: theme.spacing(6), + marginBottom: theme.spacing(6), + padding: theme.spacing(3), + }, + }, + stepper: { + padding: theme.spacing(3, 0, 5), + }, + buttons: { + display: 'flex', + justifyContent: 'flex-end', + }, + button: { + marginTop: theme.spacing(3), + marginLeft: theme.spacing(1), + }, +})); + +export default useStyles diff --git a/yarn.lock b/yarn.lock index 79d8133..3558841 100644 --- a/yarn.lock +++ b/yarn.lock @@ -970,7 +970,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== @@ -1081,11 +1081,6 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== -"@hapi/hoek@^9.0.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6" - integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw== - "@hapi/joi@^15.1.0": version "15.1.1" resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" @@ -1103,13 +1098,6 @@ dependencies: "@hapi/hoek" "^8.3.0" -"@hapi/topo@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.0.0.tgz#c19af8577fa393a06e9c77b60995af959be721e7" - integrity sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw== - dependencies: - "@hapi/hoek" "^9.0.0" - "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1523,23 +1511,6 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@sideway/address@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.0.tgz#0b301ada10ac4e0e3fa525c90615e0b61a72b78d" - integrity sha512-wAH/JYRXeIFQRsxerIuLjgUu2Xszam+O5xKeatJ4oudShOOirfmsQ1D6LL54XOU2tizpCYku+s1wmU0SYdpoSA== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== - -"@sideway/pinpoint@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" - integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== - "@sinonjs/commons@^1.7.0": version "1.8.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" @@ -1826,6 +1797,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash@^4.14.165": + version "4.14.165" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f" + integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -6916,17 +6892,6 @@ jest@26.6.0: import-local "^3.0.2" jest-cli "^26.6.0" -joi@^17.3.0: - version "17.3.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.3.0.tgz#f1be4a6ce29bc1716665819ac361dfa139fff5d2" - integrity sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg== - dependencies: - "@hapi/hoek" "^9.0.0" - "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.0" - "@sideway/formula" "^3.0.0" - "@sideway/pinpoint" "^2.0.0" - jpeg-js@^0.3.2: version "0.3.7" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.7.tgz#471a89d06011640592d314158608690172b1028d" @@ -7324,7 +7289,7 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash-es@^4.17.14: +lodash-es@^4.17.11, lodash-es@^4.17.14: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -7764,6 +7729,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== + nanoid@^3.1.15: version "3.1.15" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.15.tgz#28e7c4ce56aff2d0c2d37814c7aef9d6c5b3e6f3" @@ -9302,6 +9272,11 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +property-expr@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" + integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -11205,6 +11180,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= + tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -12157,3 +12137,16 @@ yarn@^1.22.10: version "1.22.10" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.10.tgz#c99daa06257c80f8fa2c3f1490724e394c26b18c" integrity sha512-IanQGI9RRPAN87VGTF7zs2uxkSyQSrSPsju0COgbsKQOOXr5LtcVPeyXWgwVa0ywG3d8dg6kSYKGBuYK021qeA== + +yup@^0.32.8: + version "0.32.8" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.8.tgz#16e4a949a86a69505abf99fd0941305ac9adfc39" + integrity sha512-SZulv5FIZ9d5H99EN5tRCRPXL0eyoYxWIP1AacCrjC9d4DfP13J1dROdKGfpfRHT3eQB6/ikBl5jG21smAfCkA== + dependencies: + "@babel/runtime" "^7.10.5" + "@types/lodash" "^4.14.165" + lodash "^4.17.20" + lodash-es "^4.17.11" + nanoclone "^0.2.1" + property-expr "^2.0.4" + toposort "^2.0.2"