Merge pull request 'Add formik' (#2) from formik into master
Reviewed-on: #2
This commit is contained in:
commit
0a4461bbe4
@ -12,7 +12,7 @@
|
|||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
"connected-react-router": "^6.8.0",
|
"connected-react-router": "^6.8.0",
|
||||||
"date-fns": "^2.16.1",
|
"date-fns": "^2.9.0",
|
||||||
"formik": "^2.2.6",
|
"formik": "^2.2.6",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"immer": "^8.0.0",
|
"immer": "^8.0.0",
|
||||||
@ -23,7 +23,6 @@
|
|||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-hook-form": "^6.13.1",
|
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "4.0.0",
|
"react-scripts": "4.0.0",
|
||||||
|
57
src/components/DatePickerField/index.js
Normal file
57
src/components/DatePickerField/index.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import Grid from '@material-ui/core/Grid';
|
||||||
|
import {
|
||||||
|
MuiPickersUtilsProvider,
|
||||||
|
KeyboardDatePicker
|
||||||
|
} from '@material-ui/pickers';
|
||||||
|
import DateFnsUtils from '@date-io/date-fns';
|
||||||
|
|
||||||
|
const DatePickerField = (props) => {
|
||||||
|
const [field, meta, helper] = useField(props);
|
||||||
|
const { touched, error } = meta;
|
||||||
|
const { setValue } = helper;
|
||||||
|
const isError = touched && error && true;
|
||||||
|
const { value } = field;
|
||||||
|
const [selectedDate, setSelectedDate] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (value) {
|
||||||
|
const date = new Date(value);
|
||||||
|
setSelectedDate(date);
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const _onChange = (date) => {
|
||||||
|
if (date) {
|
||||||
|
setSelectedDate(date);
|
||||||
|
try {
|
||||||
|
const ISODateString = date.toISOString();
|
||||||
|
setValue(ISODateString);
|
||||||
|
} catch (error) {
|
||||||
|
setValue(date);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setValue(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container>
|
||||||
|
<MuiPickersUtilsProvider utils={DateFnsUtils}>
|
||||||
|
<KeyboardDatePicker
|
||||||
|
{...field}
|
||||||
|
{...props}
|
||||||
|
value={selectedDate}
|
||||||
|
onChange={_onChange}
|
||||||
|
error={isError}
|
||||||
|
invalidDateMessage={isError && error}
|
||||||
|
helperText={isError && error}
|
||||||
|
/>
|
||||||
|
</MuiPickersUtilsProvider>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DatePickerField
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {Button, Grid} from "@material-ui/core";
|
|
||||||
import {Skeleton} from "@material-ui/lab";
|
|
||||||
|
|
||||||
const Loader = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Button variant="text" fullWidth>
|
|
||||||
<Skeleton />
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Button variant="text" fullWidth>
|
|
||||||
<Skeleton />
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Button variant="text" fullWidth>
|
|
||||||
<Skeleton />
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Loader;
|
|
@ -1,105 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {Grid, FormControlLabel, Slider, Button, Typography, Radio, RadioGroup, Box} from '@material-ui/core';
|
|
||||||
import {useInjectReducer} from "redux-injectors";
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import {createStructuredSelector} from "reselect";
|
|
||||||
import { useFormContext, Controller } from "react-hook-form";
|
|
||||||
import reducer from "pages/Profile/reducer";
|
|
||||||
import {
|
|
||||||
makeSelectGoals,
|
|
||||||
makeSelectActivities,
|
|
||||||
makeSelectRatesOfChange,
|
|
||||||
} from "pages/Profile/selectors";
|
|
||||||
|
|
||||||
const stateSelector = createStructuredSelector({
|
|
||||||
goals: makeSelectGoals(),
|
|
||||||
activities: makeSelectActivities(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const key = 'profilePage'
|
|
||||||
const GoalForm = () => {
|
|
||||||
useInjectReducer({ key, reducer });
|
|
||||||
const { goals, activities, ratesOfChange } = useSelector(stateSelector)
|
|
||||||
const { control } = useFormContext()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
Goal
|
|
||||||
</Typography>
|
|
||||||
<Grid>
|
|
||||||
<Grid>
|
|
||||||
<Controller
|
|
||||||
name="goal"
|
|
||||||
control={control}
|
|
||||||
as={
|
|
||||||
<RadioGroup aria-label="Your goal">
|
|
||||||
<Grid container spacing={2} >
|
|
||||||
{goals.map(({ label, value }) => (
|
|
||||||
<Grid item xs={4} key={value}>
|
|
||||||
<Button variant="text" fullWidth>
|
|
||||||
<FormControlLabel
|
|
||||||
key={value}
|
|
||||||
value={value.toString()}
|
|
||||||
control={<Radio />}
|
|
||||||
label={label}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</RadioGroup>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid>
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
Activity
|
|
||||||
</Typography>
|
|
||||||
<Box mx={4}>
|
|
||||||
<Controller
|
|
||||||
name="activity"
|
|
||||||
control={control}
|
|
||||||
defaultValue={5}
|
|
||||||
render={({onChange, ...props}) => (
|
|
||||||
<Slider
|
|
||||||
{...props}
|
|
||||||
onChange={(_, value) => onChange(value)}
|
|
||||||
min={1.2}
|
|
||||||
step={null}
|
|
||||||
max={1.9}
|
|
||||||
marks={activities}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
<Grid>
|
|
||||||
<Typography mt={2} variant="h6" gutterBottom>
|
|
||||||
Rate of change
|
|
||||||
</Typography>
|
|
||||||
<Box mx={4}>
|
|
||||||
<Controller
|
|
||||||
name="rateOfChange"
|
|
||||||
control={control}
|
|
||||||
defaultValue={0}
|
|
||||||
render={({onChange, ...props}) => (
|
|
||||||
<Slider
|
|
||||||
{...props}
|
|
||||||
onChange={(_, value) => onChange(value)}
|
|
||||||
min={0}
|
|
||||||
step={0.1}
|
|
||||||
max={1}
|
|
||||||
valueLabelDisplay="auto"
|
|
||||||
marks={ratesOfChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GoalForm;
|
|
27
src/components/InputField/index.js
Normal file
27
src/components/InputField/index.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { at } from 'lodash';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import { TextField } from '@material-ui/core';
|
||||||
|
|
||||||
|
const InputField = (props) => {
|
||||||
|
const [field, meta] = useField(props);
|
||||||
|
|
||||||
|
const _renderHelperText = () => {
|
||||||
|
const [touched, error] = at(meta, 'touched', 'error');
|
||||||
|
if (touched && error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
type="text"
|
||||||
|
error={meta.touched && meta.error && true}
|
||||||
|
helperText={_renderHelperText()}
|
||||||
|
{...field}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InputField
|
39
src/components/RadioField/index.js
Normal file
39
src/components/RadioField/index.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { at } from 'lodash';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import {
|
||||||
|
Radio,
|
||||||
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
|
FormHelperText
|
||||||
|
} from '@material-ui/core';
|
||||||
|
|
||||||
|
const CheckboxField = ({ label, ...rest }) => {
|
||||||
|
const [field, meta, helper] = useField({ label, ...rest });
|
||||||
|
const { setValue } = helper;
|
||||||
|
|
||||||
|
const _renderHelperText = () => {
|
||||||
|
const [touched, error] = at(meta, 'touched', 'error');
|
||||||
|
if (touched && error) {
|
||||||
|
return <FormHelperText>{error}</FormHelperText>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _onChange = (e) => {
|
||||||
|
setValue(e.target.checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl {...rest}>
|
||||||
|
<FormControlLabel
|
||||||
|
value={field.checked}
|
||||||
|
checked={field.checked}
|
||||||
|
control={<Radio {...field} onChange={_onChange} />}
|
||||||
|
label={label}
|
||||||
|
/>
|
||||||
|
{_renderHelperText()}
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckboxField
|
48
src/components/SelectField/index.js
Normal file
48
src/components/SelectField/index.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { at } from 'lodash';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import {
|
||||||
|
InputLabel,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
FormHelperText
|
||||||
|
} from '@material-ui/core';
|
||||||
|
|
||||||
|
const SelectField = ({ label, data, ...rest }) => {
|
||||||
|
const [field, meta] = useField({ label, data, ...rest });
|
||||||
|
const { value: selectedValue } = field;
|
||||||
|
const [touched, error] = at(meta, 'touched', 'error');
|
||||||
|
const isError = touched && error && true;
|
||||||
|
|
||||||
|
const _renderHelperText = () => {
|
||||||
|
if (isError) {
|
||||||
|
return <FormHelperText>{error}</FormHelperText>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl {...rest} error={isError}>
|
||||||
|
<InputLabel>{label}</InputLabel>
|
||||||
|
<Select {...field} value={selectedValue ? selectedValue : ''}>
|
||||||
|
{data.map((item, index) => (
|
||||||
|
<MenuItem key={index} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
{_renderHelperText()}
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectField.defaultProps = {
|
||||||
|
data: []
|
||||||
|
};
|
||||||
|
|
||||||
|
SelectField.propTypes = {
|
||||||
|
data: PropTypes.array.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectField;
|
24
src/components/SliderField/index.js
Normal file
24
src/components/SliderField/index.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { at } from 'lodash';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
import {
|
||||||
|
Slider,
|
||||||
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
|
FormHelperText
|
||||||
|
} from '@material-ui/core';
|
||||||
|
|
||||||
|
const SliderField = ({ label, ...rest }) => {
|
||||||
|
const [field, meta, helper] = useField({ label, ...rest });
|
||||||
|
const { setValue } = helper;
|
||||||
|
|
||||||
|
const _onChange = (e) => {
|
||||||
|
setValue(e.target.checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slider onChange={_onChange} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SliderField
|
52
src/pages/Profile/GoalForm/index.js
Normal file
52
src/pages/Profile/GoalForm/index.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {Grid, Typography, Box} from '@material-ui/core';
|
||||||
|
import {useInjectReducer} from "redux-injectors";
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import {createStructuredSelector} from "reselect";
|
||||||
|
import reducer from "pages/Profile/reducer";
|
||||||
|
import {
|
||||||
|
makeSelectGoals,
|
||||||
|
makeSelectActivities,
|
||||||
|
makeSelectRatesOfChange,
|
||||||
|
} from "pages/Profile/selectors";
|
||||||
|
|
||||||
|
import SelectField from 'components/SelectField';
|
||||||
|
|
||||||
|
const stateSelector = createStructuredSelector({
|
||||||
|
goals: makeSelectGoals(),
|
||||||
|
activities: makeSelectActivities(),
|
||||||
|
ratesOfChange: makeSelectRatesOfChange(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = 'profilePage'
|
||||||
|
const GoalForm = () => {
|
||||||
|
useInjectReducer({ key, reducer });
|
||||||
|
const { goals, activities, ratesOfChange } = useSelector(stateSelector)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Goal
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SelectField label="goal" name="goal" data={goals} fullWidth />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Activity
|
||||||
|
</Typography>
|
||||||
|
<SelectField label="activity" name="activity" data={activities} fullWidth />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Rate of change
|
||||||
|
</Typography>
|
||||||
|
<SelectField label="Rate of Change" name="rateOfChange" data={ratesOfChange} fullWidth />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GoalForm;
|
86
src/pages/Profile/PersonalDetailsForm/index.js
Normal file
86
src/pages/Profile/PersonalDetailsForm/index.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import 'date-fns';
|
||||||
|
import React from 'react';
|
||||||
|
import {Typography, InputLabel, Grid, InputAdornment, Select, FormControl, MenuItem, TextField, Slider} from '@material-ui/core';
|
||||||
|
import DatePickerField from 'components/DatePickerField'
|
||||||
|
import InputField from 'components/InputField'
|
||||||
|
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";
|
||||||
|
import SelectField from "../../../components/SelectField";
|
||||||
|
|
||||||
|
const stateSelector = createStructuredSelector({
|
||||||
|
activities: makeSelectActivities(),
|
||||||
|
genders: makeSelectGenders(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = 'profilePage'
|
||||||
|
const PersonalDetailsForm = () => {
|
||||||
|
useInjectReducer({ key, reducer });
|
||||||
|
const { genders } = useSelector(stateSelector)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Personal details
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={3}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SelectField label="gender" name="gender" data={genders} fullWidth />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<DatePickerField
|
||||||
|
name="birthday"
|
||||||
|
label="birthday"
|
||||||
|
format="MM/yy"
|
||||||
|
views={['year', 'month']}
|
||||||
|
minDate={new Date('1900/01/01')}
|
||||||
|
maxDate={new Date()}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputField
|
||||||
|
type="number"
|
||||||
|
name="height"
|
||||||
|
label="Height"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">cm</InputAdornment>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputField
|
||||||
|
type="number"
|
||||||
|
label="Current Weight"
|
||||||
|
name="weight.current"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">Kg</InputAdornment>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputField
|
||||||
|
type="number"
|
||||||
|
label="Goal Weight"
|
||||||
|
name="weight.goal"
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">Kg</InputAdornment>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default PersonalDetailsForm
|
11
src/pages/Profile/ReviewProfileForm/index.js
Normal file
11
src/pages/Profile/ReviewProfileForm/index.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ReviewProfileForm = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
ReviewProfileForm
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReviewProfileForm;
|
@ -3,13 +3,12 @@ import { makeStyles } from '@material-ui/core/styles';
|
|||||||
import {Paper, Stepper, Step, StepLabel, Button, Typography } from '@material-ui/core';
|
import {Paper, Stepper, Step, StepLabel, Button, Typography } from '@material-ui/core';
|
||||||
import {useInjectReducer, useInjectSaga} from "redux-injectors";
|
import {useInjectReducer, useInjectSaga} from "redux-injectors";
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useForm, useFormContext, FormProvider } from "react-hook-form";
|
import { Formik, Form } from 'formik';
|
||||||
import GoalForm from 'components/GoalForm';
|
import GoalForm from './GoalForm';
|
||||||
import PersonalDetailsForm from 'components/PersonalDetailsForm';
|
import PersonalDetailsForm from './PersonalDetailsForm';
|
||||||
import ReviewProfileForm from 'components/ReviewProfileForm';
|
import ReviewProfileForm from './ReviewProfileForm';
|
||||||
import reducer from "./reducer";
|
import reducer from "./reducer";
|
||||||
import saga from "./saga";
|
import saga from "./saga";
|
||||||
import { updateProfileAction } from './actions'
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
layout: {
|
layout: {
|
||||||
@ -47,7 +46,7 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
|
|
||||||
const steps = ['Your goal', 'Personal details', 'Review your profile'];
|
const steps = ['Your goal', 'Personal details', 'Review your profile'];
|
||||||
|
|
||||||
const getStepContent = (step) => {
|
const renderStepContent = (step) => {
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 0:
|
case 0:
|
||||||
return <GoalForm/>;
|
return <GoalForm/>;
|
||||||
@ -60,40 +59,56 @@ const getStepContent = (step) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formInitialValues = {
|
||||||
|
gender: '',
|
||||||
|
goal: '',
|
||||||
|
birthday: '',
|
||||||
|
height: '',
|
||||||
|
weight: {
|
||||||
|
current: '',
|
||||||
|
goal: '',
|
||||||
|
},
|
||||||
|
rateOfChange: '',
|
||||||
|
activity: '',
|
||||||
|
}
|
||||||
|
|
||||||
const key = 'profilePage'
|
const key = 'profilePage'
|
||||||
const ProfilePage = () => {
|
const ProfilePage = () => {
|
||||||
const classes = useStyles();
|
|
||||||
const [activeStep, setActiveStep] = useState(0);
|
const [activeStep, setActiveStep] = useState(0);
|
||||||
const methods = useForm({
|
const isLastStep = activeStep === steps.length - 1;
|
||||||
defaultValues: {
|
const isFirstStep = activeStep === 0;
|
||||||
gender: '',
|
const classes = useStyles();
|
||||||
goal: 0,
|
|
||||||
birthday: '',
|
|
||||||
height: 0,
|
|
||||||
weight: {
|
|
||||||
current: 0,
|
|
||||||
goal: 0,
|
|
||||||
},
|
|
||||||
rateOfChange: 0,
|
|
||||||
activity: 0,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
useInjectReducer({ key, reducer });
|
useInjectReducer({ key, reducer });
|
||||||
useInjectSaga({ key, saga });
|
useInjectSaga({ key, saga });
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
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);
|
||||||
|
|
||||||
const handleNext = () => {
|
|
||||||
setActiveStep(activeStep + 1);
|
setActiveStep(activeStep + 1);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
const handleSubmit = (values, actions) => {
|
||||||
|
if (isLastStep) {
|
||||||
|
submitForm(values, actions);
|
||||||
|
} else {
|
||||||
|
setActiveStep(activeStep + 1);
|
||||||
|
actions.setTouched({});
|
||||||
|
actions.setSubmitting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
setActiveStep(activeStep - 1);
|
setActiveStep(activeStep - 1);
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmitProfile = data => {
|
const handleNext = () => {
|
||||||
console.log('data', data)
|
setActiveStep(activeStep + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -121,9 +136,13 @@ const ProfilePage = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : (
|
) : (
|
||||||
<FormProvider {...methods}>
|
<Formik
|
||||||
<form onSubmit={methods.handleSubmit(handleSubmitProfile)}>
|
initialValues={formInitialValues}
|
||||||
{getStepContent(activeStep)}
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
{({ isSubmitting }) => (
|
||||||
|
<Form id="createProfileForm">
|
||||||
|
{renderStepContent(activeStep)}
|
||||||
<div className={classes.buttons}>
|
<div className={classes.buttons}>
|
||||||
{activeStep !== 0 && (
|
{activeStep !== 0 && (
|
||||||
<Button onClick={handleBack} className={classes.button}>
|
<Button onClick={handleBack} className={classes.button}>
|
||||||
@ -133,16 +152,17 @@ const ProfilePage = () => {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
disabled={isSubmitting}
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
type={activeStep === steps.length - 1 ? 'submit' : 'button'}
|
type={isLastStep ? 'submit' : 'button'}
|
||||||
type="submit"
|
|
||||||
className={classes.button}
|
className={classes.button}
|
||||||
>
|
>
|
||||||
{activeStep === steps.length - 1 ? 'Save profile' : 'Next'}
|
{isLastStep ? 'Create profile' : 'Next'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</Form>
|
||||||
</FormProvider>
|
)}
|
||||||
|
</Formik>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
@ -34,19 +34,26 @@ export const initialState = {
|
|||||||
goals: [
|
goals: [
|
||||||
{
|
{
|
||||||
label: 'lose weight',
|
label: 'lose weight',
|
||||||
value: -1,
|
value: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'maintain weight',
|
label: 'maintain weight',
|
||||||
value: 0,
|
value: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'put on weight',
|
label: 'put on weight',
|
||||||
value: 1,
|
value: 3,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
ratesOfChange: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
|
ratesOfChange: [
|
||||||
genders: ['male', 'female'],
|
{ 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'},
|
||||||
|
],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: {},
|
error: {},
|
||||||
gender: '',
|
gender: '',
|
||||||
|
@ -4011,7 +4011,7 @@ data-urls@^2.0.0:
|
|||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
date-fns@^2.16.1:
|
date-fns@^2.9.0:
|
||||||
version "2.16.1"
|
version "2.16.1"
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b"
|
||||||
integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==
|
integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==
|
||||||
@ -9536,11 +9536,6 @@ react-helmet@^6.1.0:
|
|||||||
react-fast-compare "^3.1.1"
|
react-fast-compare "^3.1.1"
|
||||||
react-side-effect "^2.1.0"
|
react-side-effect "^2.1.0"
|
||||||
|
|
||||||
react-hook-form@^6.13.1:
|
|
||||||
version "6.13.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-6.13.1.tgz#b9c0aa61f746db8169ed5e1050de21cacb1947d6"
|
|
||||||
integrity sha512-Q0N7MYcbA8SigYufb02h9z97ZKCpIbe62rywOTPsK4Ntvh6fRTGDXSuzWuRhLHhArLoWbGrWYSNSS4tlb+OFXg==
|
|
||||||
|
|
||||||
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1:
|
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
|
Loading…
Reference in New Issue
Block a user