react form hooks

This commit is contained in:
= 2020-12-17 15:58:07 +01:00
parent 6c807dcc30
commit 803bd65a87
11 changed files with 342 additions and 339 deletions

View File

@ -13,14 +13,17 @@
"@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.16.1",
"formik": "^2.2.6",
"history": "4.10.1", "history": "4.10.1",
"immer": "^8.0.0", "immer": "^8.0.0",
"joi": "^17.3.0",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"quagga": "^0.12.1", "quagga": "^0.12.1",
"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",

View File

@ -1,62 +1,103 @@
import React, { useEffect } from 'react'; import React from 'react';
import { Grid, Button, Typography, Radio, RadioGroup } from '@material-ui/core'; import {Grid, FormControlLabel, Slider, Button, Typography, Radio, RadioGroup, Box} from '@material-ui/core';
import {useInjectReducer, useInjectSaga} from "redux-injectors"; import {useInjectReducer} from "redux-injectors";
import { useSelector, useDispatch } from 'react-redux'; import { useSelector } from 'react-redux';
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import { useFormContext, Controller } from "react-hook-form";
import reducer from "pages/Profile/reducer"; import reducer from "pages/Profile/reducer";
import saga from "pages/Profile/saga";
import { profileInputChange, getGoalsAction } from 'pages/Profile/actions'
import { import {
makeSelectError,
makeSelectIsLoading,
makeSelectGoals, makeSelectGoals,
makeSelectActivities,
makeSelectRatesOfChange,
} from "pages/Profile/selectors"; } from "pages/Profile/selectors";
import Loader from './Loader'
const stateSelector = createStructuredSelector({ const stateSelector = createStructuredSelector({
isLoading: makeSelectIsLoading(),
error: makeSelectError(),
goals: makeSelectGoals(), goals: makeSelectGoals(),
activities: makeSelectActivities(),
}); });
const key = 'profilePage' const key = 'profilePage'
const GoalForm = () => { const GoalForm = () => {
useInjectReducer({ key, reducer }); useInjectReducer({ key, reducer });
useInjectSaga({ key, saga }); const { goals, activities, ratesOfChange } = useSelector(stateSelector)
const dispatch = useDispatch() const { control } = useFormContext()
const { goals, error, isLoading } = useSelector(stateSelector)
const handleChangeGoal = ({ target: { name, value }}) => {
dispatch(profileInputChange({ name, value }))
}
useEffect(() => {
dispatch(getGoalsAction())
}, [])
return ( return (
<React.Fragment> <React.Fragment>
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
Your goal Goal
</Typography> </Typography>
<RadioGroup defaultValue={1} onChange={handleChangeGoal}> <Grid>
<Grid container spacing={3} direction="column"> <Grid>
{isLoading ? ( <Controller
<Loader /> name="goal"
) : ( control={control}
<React.Fragment> as={
{goals.map(({ name, value }, index) => ( <RadioGroup aria-label="Your goal">
<Grid item xs={12} key={index}> <Grid container spacing={2} >
<Button variant="text" fullWidth> {goals.map(({ label, value }) => (
{name} <Grid item xs={4} key={value}>
<Radio color="secondary" name="goal" value={value} /> <Button variant="text" fullWidth>
</Button> <FormControlLabel
</Grid> key={value}
))} value={value.toString()}
</React.Fragment> control={<Radio />}
)} label={label}
/>
</Button>
</Grid>
))}
</Grid>
</RadioGroup>
}
/>
</Grid> </Grid>
</RadioGroup> <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> </React.Fragment>
); );
} }

View File

@ -1,56 +1,32 @@
import 'date-fns'; import 'date-fns';
import React, { useEffect } from 'react'; import React from 'react';
import {Typography, InputLabel, Box, Grid, InputAdornment, Select, FormControl, MenuItem, TextField, Slider} from '@material-ui/core'; import {Typography, InputLabel, Box, Grid, InputAdornment, Select, FormControl, MenuItem, TextField, Slider} from '@material-ui/core';
import DateFnsUtils from '@date-io/date-fns'; import DateFnsUtils from '@date-io/date-fns';
import { useFormContext, Controller } from "react-hook-form";
import { import {
MuiPickersUtilsProvider, MuiPickersUtilsProvider,
KeyboardDatePicker, DatePicker,
} from '@material-ui/pickers'; } from '@material-ui/pickers';
import {useInjectReducer, useInjectSaga} from "redux-injectors"; import {useInjectReducer} from "redux-injectors";
import { useSelector, useDispatch } from 'react-redux'; import { useSelector } from 'react-redux';
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import reducer from "pages/Profile/reducer"; import reducer from "pages/Profile/reducer";
import saga from "pages/Profile/saga";
import { profileInputChange, getActivitiesAction, getGendersAction } from 'pages/Profile/actions'
import { import {
makeSelectError,
makeSelectIsLoading,
makeSelectActivities, makeSelectActivities,
makeSelectGenders, makeSelectGenders,
makeSelectWeight,
makeSelectHeight,
makeSelectBirthday,
makeSelectActivity,
makeSelectGender,
} from "pages/Profile/selectors"; } from "pages/Profile/selectors";
const stateSelector = createStructuredSelector({ const stateSelector = createStructuredSelector({
isLoading: makeSelectIsLoading(),
error: makeSelectError(),
activities: makeSelectActivities(), activities: makeSelectActivities(),
genders: makeSelectGenders(), genders: makeSelectGenders(),
weight: makeSelectWeight(),
height: makeSelectHeight(),
birthday: makeSelectBirthday(),
activity: makeSelectActivity(),
gender: makeSelectGender(),
}); });
const key = 'profilePage' const key = 'profilePage'
const PersonalDetailsForm = () => { const PersonalDetailsForm = () => {
useInjectReducer({ key, reducer }); useInjectReducer({ key, reducer });
useInjectSaga({ key, saga }); const { activities, genders } = useSelector(stateSelector)
const dispatch = useDispatch() const { register, control } = useFormContext()
const { gender, activity, birthday, height, weight, activities, genders, error, isLoading } = useSelector(stateSelector)
const handleChangeValue = ({ target: { name, value }}) => {
dispatch(profileInputChange({ name, value }))
}
useEffect(() => {
dispatch(getActivitiesAction())
dispatch(getGendersAction())
}, [])
return ( return (
<React.Fragment> <React.Fragment>
@ -61,79 +37,93 @@ const PersonalDetailsForm = () => {
<Grid item xs={12}> <Grid item xs={12}>
<FormControl variant="outlined" fullWidth> <FormControl variant="outlined" fullWidth>
<InputLabel>Gender</InputLabel> <InputLabel>Gender</InputLabel>
<Select <Controller
control={control}
name="gender" name="gender"
label="Gender" as={
onChange={handleChangeValue} <Select
value={gender} label="Gender"
> name="gender"
{genders.map(({ id, name }) => ( >
<MenuItem value={id} key={id}>{name}</MenuItem> {genders.map(label => (
))} <MenuItem value={label} key={label}>{label}</MenuItem>
</Select> ))}
</Select>
}
/>
</FormControl> </FormControl>
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<MuiPickersUtilsProvider utils={DateFnsUtils}> <Controller
<KeyboardDatePicker control={control}
disableToolbar onChange={([selected]) => selected}
inputVariant="outlined" name="birthday"
fullWidth render={({ onChange, value }) => (
format="MM/dd/yyyy" <MuiPickersUtilsProvider utils={DateFnsUtils}>
margin="normal" <DatePicker
name="birthday" disableFuture
value={birthday} fullWidth
onChange={handleChangeValue} openTo="year"
/> format="dd/MM/yyyy"
</MuiPickersUtilsProvider> label="Date of birth"
views={["year", "month", "date"]}
onChange={onChange}
value={value}
/>
</MuiPickersUtilsProvider>
)}
/>
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <Controller
required control={control}
variant="outlined"
label="Height"
type="number"
name="height" name="height"
onChange={handleChangeValue} as={
value={height} <TextField
fullWidth type="number"
InputProps={{ label="Height"
endAdornment: <InputAdornment position="end">cm</InputAdornment> variant="outlined"
}} fullWidth
InputProps={{
endAdornment: <InputAdornment position="end">cm</InputAdornment>
}}
/>
}
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <Controller
required control={control}
variant="outlined" name="weight.current"
label="Weight" as={
type="number" <TextField
name="weight" type="number"
onChange={handleChangeValue} label="Current Weight"
value={weight} variant="outlined"
fullWidth fullWidth
InputProps={{ InputProps={{
endAdornment: <InputAdornment position="end">Kg</InputAdornment> endAdornment: <InputAdornment position="end">Kg</InputAdornment>
}} }}
/>
}
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<Typography gutterBottom> <Controller
Activity control={control}
</Typography> name="weight.goal"
<Box mx={4}> as={
<Slider <TextField
name="activity" type="number"
onChange={handleChangeValue} label="Goal Weight"
value={activity} variant="outlined"
min={0} fullWidth
max={4} InputProps={{
marks={activities.map(({ name, factor, id }) => ({ endAdornment: <InputAdornment position="end">Kg</InputAdornment>
name, }}
value: factor />
}))} }
/> />
</Box>
</Grid> </Grid>
</Grid> </Grid>
</React.Fragment> </React.Fragment>

View File

@ -1,13 +1,4 @@
import { import {
GET_GOALS_REQUEST,
GET_GOALS_SUCCESS,
GET_GOALS_ERROR,
GET_ACTIVITIES_REQUEST,
GET_ACTIVITIES_SUCCESS,
GET_ACTIVITIES_ERROR,
GET_GENDERS_ERROR,
GET_GENDERS_REQUEST,
GET_GENDERS_SUCCESS,
PROFILE_INPUT_CHANGE, PROFILE_INPUT_CHANGE,
GET_PROFILE_REQUEST, GET_PROFILE_REQUEST,
GET_PROFILE_SUCCESS, GET_PROFILE_SUCCESS,
@ -43,15 +34,15 @@ export const getProfileErrorAction = ({error}) => ({
error, error,
}) })
export const updateProfileAction = () => ({ export const updateProfileAction = () => ({
type: UPDATE_PROFILE_REQUEST, type: UPDATE_PROFILE_REQUEST,
}) })
export const updateProfileSuccessAction = ({birthday, gender, height, weight, goalWeight, rateOfChange, activity}) => ({ export const updateProfileSuccessAction = ({birthday, goal, gender, height, weight, goalWeight, rateOfChange, activity}) => ({
type: UPDATE_PROFILE_SUCCESS, type: UPDATE_PROFILE_SUCCESS,
birthday, birthday,
gender, gender,
goal,
height, height,
weight, weight,
goalWeight, goalWeight,
@ -64,45 +55,3 @@ export const updateProfileErrorAction = ({error}) => ({
error, error,
}) })
export const getActivitiesAction = () => ({
type: GET_ACTIVITIES_REQUEST,
})
export const getActivitiesSuccessAction = ({ activities }) => ({
type: GET_ACTIVITIES_SUCCESS,
activities,
})
export const getActivitiesErrorAction = ({error}) => ({
type: GET_ACTIVITIES_ERROR,
error,
})
export const getGendersAction = () => ({
type: GET_GENDERS_REQUEST,
})
export const getGendersSuccessAction = ({ genders }) => ({
type: GET_GENDERS_SUCCESS,
genders,
})
export const getGendersErrorAction = ({error}) => ({
type: GET_GENDERS_ERROR,
error,
})
export const getGoalsAction = () => ({
type: GET_GOALS_REQUEST,
})
export const getGoalsSuccessAction = ({ goals }) => ({
type: GET_GOALS_SUCCESS,
goals,
})
export const getGoalsErrorAction = ({error}) => ({
type: GET_GOALS_ERROR,
error,
})

View File

@ -6,18 +6,4 @@ export const UPDATE_PROFILE_REQUEST = 'app/ProfilePage/UPDATE_PROFILE_REQUEST';
export const UPDATE_PROFILE_SUCCESS = 'app/ProfilePage/UPDATE_PROFILE_SUCCESS'; export const UPDATE_PROFILE_SUCCESS = 'app/ProfilePage/UPDATE_PROFILE_SUCCESS';
export const UPDATE_PROFILE_ERROR = 'app/ProfilePage/UPDATE_PROFILE_ERROR'; export const UPDATE_PROFILE_ERROR = 'app/ProfilePage/UPDATE_PROFILE_ERROR';
export const GET_GENDERS_REQUEST = 'app/ProfilePage/GET_GENDERS_REQUEST';
export const GET_GENDERS_SUCCESS = 'app/ProfilePage/GET_GENDERS_SUCCESS';
export const GET_GENDERS_ERROR = 'app/ProfilePage/GET_GENDERS_ERROR';
export const GET_GOALS_REQUEST = 'app/ProfilePage/GET_GOALS_REQUEST';
export const GET_GOALS_SUCCESS = 'app/ProfilePage/GET_GOALS_SUCCESS';
export const GET_GOALS_ERROR = 'app/ProfilePage/GET_GOALS_ERROR';
export const GET_ACTIVITIES_REQUEST = 'app/ProfilePage/GET_ACTIVITIES_REQUEST';
export const GET_ACTIVITIES_SUCCESS = 'app/ProfilePage/GET_ACTIVITIES_SUCCESS';
export const GET_ACTIVITIES_ERROR = 'app/ProfilePage/GET_ACTIVITIES_ERROR';
export const PROFILE_INPUT_CHANGE = 'app/ProfilePage/PROFILE_INPUT_CHANGE'; export const PROFILE_INPUT_CHANGE = 'app/ProfilePage/PROFILE_INPUT_CHANGE';

View File

@ -3,6 +3,7 @@ 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 GoalForm from 'components/GoalForm'; import GoalForm from 'components/GoalForm';
import PersonalDetailsForm from 'components/PersonalDetailsForm'; import PersonalDetailsForm from 'components/PersonalDetailsForm';
import ReviewProfileForm from 'components/ReviewProfileForm'; import ReviewProfileForm from 'components/ReviewProfileForm';
@ -49,11 +50,11 @@ const steps = ['Your goal', 'Personal details', 'Review your profile'];
const getStepContent = (step) => { const getStepContent = (step) => {
switch (step) { switch (step) {
case 0: case 0:
return <GoalForm />; return <GoalForm/>;
case 1: case 1:
return <PersonalDetailsForm />; return <PersonalDetailsForm/>;
case 2: case 2:
return <ReviewProfileForm />; return <ReviewProfileForm/>;
default: default:
throw new Error('Unknown step'); throw new Error('Unknown step');
} }
@ -63,6 +64,20 @@ const key = 'profilePage'
const ProfilePage = () => { const ProfilePage = () => {
const classes = useStyles(); const classes = useStyles();
const [activeStep, setActiveStep] = useState(0); const [activeStep, setActiveStep] = useState(0);
const methods = useForm({
defaultValues: {
gender: '',
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 });
@ -77,9 +92,8 @@ const ProfilePage = () => {
setActiveStep(activeStep - 1); setActiveStep(activeStep - 1);
}; };
const handleSubmitProfile = (event) => { const handleSubmitProfile = data => {
event.preventDefault(); console.log('data', data)
dispatch(updateProfileAction())
} }
return ( return (
@ -107,25 +121,28 @@ const ProfilePage = () => {
</Typography> </Typography>
</React.Fragment> </React.Fragment>
) : ( ) : (
<form onSubmit={handleSubmitProfile}> <FormProvider {...methods}>
{getStepContent(activeStep)} <form onSubmit={methods.handleSubmit(handleSubmitProfile)}>
<div className={classes.buttons}> {getStepContent(activeStep)}
{activeStep !== 0 && ( <div className={classes.buttons}>
<Button onClick={handleBack} className={classes.button}> {activeStep !== 0 && (
Back <Button onClick={handleBack} className={classes.button}>
</Button> back
)} </Button>
<Button )}
variant="contained" <Button
color="primary" variant="contained"
onClick={handleNext} color="primary"
type={activeStep === steps.length - 1 ? 'submit' : 'button'} onClick={handleNext}
className={classes.button} type={activeStep === steps.length - 1 ? 'submit' : 'button'}
> type="submit"
{activeStep === steps.length - 1 ? 'Save profile' : 'Next'} className={classes.button}
</Button> >
</div> {activeStep === steps.length - 1 ? 'Save profile' : 'Next'}
</form> </Button>
</div>
</form>
</FormProvider>
)} )}
</React.Fragment> </React.Fragment>
</Paper> </Paper>

View File

@ -1,14 +1,5 @@
import produce from 'immer'; import produce from 'immer';
import { import {
GET_GOALS_REQUEST,
GET_GOALS_SUCCESS,
GET_GOALS_ERROR,
GET_ACTIVITIES_SUCCESS,
GET_GENDERS_SUCCESS,
GET_ACTIVITIES_ERROR,
GET_GENDERS_ERROR,
GET_ACTIVITIES_REQUEST,
GET_GENDERS_REQUEST,
PROFILE_INPUT_CHANGE, PROFILE_INPUT_CHANGE,
GET_PROFILE_REQUEST, GET_PROFILE_REQUEST,
GET_PROFILE_SUCCESS, GET_PROFILE_SUCCESS,
@ -19,61 +10,78 @@ import {
} from './constants'; } from './constants';
export const initialState = { export const initialState = {
activities: [], activities: [
goals: [], {
genders: [], label: 'very low',
value: 1.2
},
{
label: 'low',
value: 1.375
},
{
label: 'medium',
value: 1.55
},{
label: 'high',
value: 1.725
},
{
label: 'very high',
value: 1.9
},
],
goals: [
{
label: 'lose weight',
value: -1,
},
{
label: 'maintain weight',
value: 0,
},
{
label: 'put on weight',
value: 1,
}
],
ratesOfChange: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
genders: ['male', 'female'],
isLoading: false, isLoading: false,
error: {}, error: {},
gender: '', gender: '',
goal: 0,
birthday: new Date(), birthday: new Date(),
height: 0, height: 0,
weight: 0, weight: {
goalWeight: 0, current: 0,
goal: 0,
},
rateOfChange: 0, rateOfChange: 0,
activity: 0, activity: 0,
}; };
const loginPageReducer = produce((draft, action) => { const loginPageReducer = produce((draft, action) => {
switch(action.type) { switch(action.type) {
case GET_ACTIVITIES_SUCCESS:
draft.activities = action.activities;
draft.isLoading = false;
break;
case GET_GOALS_SUCCESS:
draft.goals = action.goals;
draft.isLoading = false;
break;
case GET_GENDERS_SUCCESS:
draft.genders = action.genders;
draft.isLoading = false;
break;
case GET_PROFILE_SUCCESS: case GET_PROFILE_SUCCESS:
case UPDATE_PROFILE_SUCCESS: case UPDATE_PROFILE_SUCCESS:
draft.birthday = action.birthday; draft.birthday = action.birthday;
draft.gender = action.gender; draft.gender = action.gender;
draft.goal = action.goal;
draft.height = action.height; draft.height = action.height;
draft.weight = action.weight; draft.weight.current = action.weight.current;
draft.goalWeight = action.goalWeight; draft.weight.goal = action.weight.goal;
draft.rateOfChange = action.rateOfChange; draft.rateOfChange = action.rateOfChange;
draft.activity = action.activity; draft.activity = action.activity;
draft.isLoading = false; draft.isLoading = false;
break; break;
case GET_ACTIVITIES_REQUEST:
case GET_GENDERS_REQUEST:
case GET_PROFILE_REQUEST: case GET_PROFILE_REQUEST:
case GET_GOALS_REQUEST:
case UPDATE_PROFILE_REQUEST: case UPDATE_PROFILE_REQUEST:
draft.isLoading = true; draft.isLoading = true;
break; break;
case GET_ACTIVITIES_ERROR:
case GET_GENDERS_ERROR:
case GET_PROFILE_ERROR: case GET_PROFILE_ERROR:
case GET_GOALS_ERROR:
case UPDATE_PROFILE_ERROR: case UPDATE_PROFILE_ERROR:
draft.isLoading = false; draft.isLoading = false;
draft.error = action.error; draft.error = action.error;

View File

@ -1,16 +1,16 @@
import { takeLatest, call, put, select } from 'redux-saga/effects'; import { takeLatest, call, put, select } from 'redux-saga/effects';
import { api, request } from 'utils'; import { api, request } from 'utils';
import { GET_GOALS_REQUEST, GET_ACTIVITIES_REQUEST, GET_GENDERS_REQUEST, GET_PROFILE_REQUEST, UPDATE_PROFILE_REQUEST } from './constants'; import { GET_PROFILE_REQUEST, UPDATE_PROFILE_REQUEST } from './constants';
import { import {
makeSelectBirthday, makeSelectBirthday,
makeSelectHeight, makeSelectHeight,
makeSelectWeight, makeSelectWeight,
makeSelectRateOfChange, makeSelectRateOfChange,
makeSelectActivity, makeSelectActivity,
makeSelectGoalWeight, makeSelectGoal,
makeSelectGender makeSelectGender
} from './selectors'; } from './selectors';
import { getGoalsErrorAction, getGoalsSuccessAction, getActivitiesSuccessAction, getActivitiesErrorAction, getGendersSuccessAction, getGendersErrorAction, updateProfileErrorAction, updateProfileSuccessAction, getProfileErrorAction, getProfileSuccessAction } from './actions'; import { updateProfileErrorAction, updateProfileSuccessAction, getProfileErrorAction, getProfileSuccessAction } from './actions';
import { makeSelectTokens } from 'containers/App/selectors' import { makeSelectTokens } from 'containers/App/selectors'
@ -37,11 +37,11 @@ export function* getProfile() {
export function* updateProfile() { export function* updateProfile() {
const { access } = yield select(makeSelectTokens()); const { access } = yield select(makeSelectTokens());
const birthday = yield select(makeSelectBirthday());
const gender = yield select(makeSelectGender()); const gender = yield select(makeSelectGender());
const goal = yield select(makeSelectGoal());
const birthday = yield select(makeSelectBirthday());
const height = yield select(makeSelectHeight()); const height = yield select(makeSelectHeight());
const weight = yield select(makeSelectWeight()); const weight = yield select(makeSelectWeight());
const goalWeight = yield select(makeSelectGoalWeight());
const rateOfChange = yield select(makeSelectRateOfChange()); const rateOfChange = yield select(makeSelectRateOfChange());
const activity = yield select(makeSelectActivity()); const activity = yield select(makeSelectActivity());
@ -50,7 +50,7 @@ export function* updateProfile() {
const requestParameters = { const requestParameters = {
method: 'POST', method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${access.token}`, }, headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${access.token}`, },
body: JSON.stringify({ birthday, gender, height, weight, goalWeight, rateOfChange, activity }), body: JSON.stringify({ birthday, gender, height, weight, goal, rateOfChange, activity }),
}; };
try { try {
@ -61,70 +61,7 @@ export function* updateProfile() {
} }
} }
export function* getActivities() {
const { access } = yield select(makeSelectTokens());
const requestURL = api.activities;
const requestParameters = {
method: 'GET',
headers: {
Authorization: `Bearer ${access.token}`,
},
};
try {
const { results } = yield call(request, requestURL, requestParameters);
yield put(getActivitiesSuccessAction({ activities: results }));
} catch (error) {
yield put(getActivitiesErrorAction({error: error.message}));
}
}
export function* getGenders() {
const { access } = yield select(makeSelectTokens());
const requestURL = api.genders;
const requestParameters = {
method: 'GET',
headers: {
Authorization: `Bearer ${access.token}`,
},
};
try {
const { results } = yield call(request, requestURL, requestParameters);
yield put(getGendersSuccessAction({ genders: results }));
} catch (error) {
yield put(getGendersErrorAction({ error: error.message }));
}
}
export function* getGoals() {
const { access } = yield select(makeSelectTokens());
const requestURL = api.goals;
const requestParameters = {
method: 'GET',
headers: {
Authorization: `Bearer ${access.token}`,
},
};
try {
const { results } = yield call(request, requestURL, requestParameters);
yield put(getGoalsSuccessAction({ goals: results }));
} catch (error) {
yield put(getGoalsErrorAction({ error: error.message }));
}
}
export default function* profilePageSaga() { export default function* profilePageSaga() {
yield takeLatest(GET_PROFILE_REQUEST, getProfile); yield takeLatest(GET_PROFILE_REQUEST, getProfile);
yield takeLatest(GET_ACTIVITIES_REQUEST, getActivities);
yield takeLatest(GET_GENDERS_REQUEST, getGenders);
yield takeLatest(GET_PROFILE_REQUEST, getProfile);
yield takeLatest(GET_GOALS_REQUEST, getGoals);
yield takeLatest(UPDATE_PROFILE_REQUEST, updateProfile); yield takeLatest(UPDATE_PROFILE_REQUEST, updateProfile);
} }

View File

@ -21,8 +21,8 @@ const makeSelectRateOfChange = () =>
const makeSelectActivity = () => const makeSelectActivity = () =>
createSelector(selectProfilePageDomain, (substate) => substate.activity); createSelector(selectProfilePageDomain, (substate) => substate.activity);
const makeSelectGoalWeight = () => const makeSelectGoal = () =>
createSelector(selectProfilePageDomain, (substate) => substate.goalWeight); createSelector(selectProfilePageDomain, (substate) => substate.goal);
const makeSelectGender = () => const makeSelectGender = () =>
createSelector(selectProfilePageDomain, (substate) => substate.gender); createSelector(selectProfilePageDomain, (substate) => substate.gender);
@ -39,6 +39,10 @@ const makeSelectActivities = () =>
const makeSelectGenders = () => const makeSelectGenders = () =>
createSelector(selectProfilePageDomain, (substate) => substate.genders); createSelector(selectProfilePageDomain, (substate) => substate.genders);
const makeSelectRatesOfChange = () =>
createSelector(selectProfilePageDomain, (substate) => substate.ratesOfChange);
export { export {
selectProfilePageDomain, selectProfilePageDomain,
makeSelectError, makeSelectError,
@ -47,10 +51,11 @@ export {
makeSelectWeight, makeSelectWeight,
makeSelectRateOfChange, makeSelectRateOfChange,
makeSelectActivity, makeSelectActivity,
makeSelectGoalWeight, makeSelectGoal,
makeSelectGender, makeSelectGender,
makeSelectIsLoading, makeSelectIsLoading,
makeSelectGoals, makeSelectGoals,
makeSelectActivities, makeSelectActivities,
makeSelectGenders, makeSelectGenders,
makeSelectRatesOfChange
}; };

View File

@ -1,9 +1,6 @@
const API_BASE_URL = 'http://localhost:3001/v1' const API_BASE_URL = 'http://localhost:3001/v1'
const AUTH = 'auth'; const AUTH = 'auth';
const PROFILE = 'profiles'; const PROFILE = 'profiles';
const ACTIVITY = 'activities';
const GENDER = 'genders';
const GOALS = 'goals';
const urls = { const urls = {
auth: { auth: {
@ -11,9 +8,6 @@ const urls = {
register: `${API_BASE_URL}/${AUTH}/register`, register: `${API_BASE_URL}/${AUTH}/register`,
}, },
profile: `${API_BASE_URL}/${PROFILE}`, profile: `${API_BASE_URL}/${PROFILE}`,
activities: `${API_BASE_URL}/${ACTIVITY}`,
genders: `${API_BASE_URL}/${GENDER}`,
goals: `${API_BASE_URL}/${GOALS}`,
} }
export default urls export default urls

View File

@ -1081,6 +1081,11 @@
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06"
integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== 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": "@hapi/joi@^15.1.0":
version "15.1.1" version "15.1.1"
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7"
@ -1098,6 +1103,13 @@
dependencies: dependencies:
"@hapi/hoek" "^8.3.0" "@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": "@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@ -1511,6 +1523,23 @@
estree-walker "^1.0.1" estree-walker "^1.0.1"
picomatch "^2.2.2" 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": "@sinonjs/commons@^1.7.0":
version "1.8.1" version "1.8.1"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217"
@ -4045,6 +4074,11 @@ deep-is@^0.1.3, deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deepmerge@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
deepmerge@^4.2.2: deepmerge@^4.2.2:
version "4.2.2" version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@ -5224,6 +5258,19 @@ form-data@~2.3.2:
combined-stream "^1.0.6" combined-stream "^1.0.6"
mime-types "^2.1.12" mime-types "^2.1.12"
formik@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.6.tgz#378a4bafe4b95caf6acf6db01f81f3fe5147559d"
integrity sha512-Kxk2zQRafy56zhLmrzcbryUpMBvT0tal5IvcifK5+4YNGelKsnrODFJ0sZQRMQboblWNym4lAW3bt+tf2vApSA==
dependencies:
deepmerge "^2.1.1"
hoist-non-react-statics "^3.3.0"
lodash "^4.17.14"
lodash-es "^4.17.14"
react-fast-compare "^2.0.1"
tiny-warning "^1.0.2"
tslib "^1.10.0"
forwarded@~0.1.2: forwarded@~0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@ -5625,7 +5672,7 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0" minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1" minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2: hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2" version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -6869,6 +6916,17 @@ jest@26.6.0:
import-local "^3.0.2" import-local "^3.0.2"
jest-cli "^26.6.0" 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: jpeg-js@^0.3.2:
version "0.3.7" version "0.3.7"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.7.tgz#471a89d06011640592d314158608690172b1028d" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.7.tgz#471a89d06011640592d314158608690172b1028d"
@ -7266,6 +7324,11 @@ locate-path@^5.0.0:
dependencies: dependencies:
p-locate "^4.1.0" p-locate "^4.1.0"
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==
lodash._reinterpolate@^3.0.0: lodash._reinterpolate@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@ -9453,6 +9516,11 @@ react-error-overlay@^6.0.8:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.8.tgz#474ed11d04fc6bda3af643447d85e9127ed6b5de" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.8.tgz#474ed11d04fc6bda3af643447d85e9127ed6b5de"
integrity sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw== integrity sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==
react-fast-compare@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-fast-compare@^3.1.1: react-fast-compare@^3.1.1:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
@ -9468,6 +9536,11 @@ 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"