Connect Profile page with redux and saga
This commit is contained in:
parent
bac2bbe739
commit
156b78ca39
@ -3,13 +3,16 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@date-io/date-fns": "^1.3.13",
|
||||||
"@material-ui/core": "^4.11.1",
|
"@material-ui/core": "^4.11.1",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.57",
|
"@material-ui/lab": "^4.0.0-alpha.57",
|
||||||
|
"@material-ui/pickers": "^3.2.10",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@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",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"immer": "^8.0.0",
|
"immer": "^8.0.0",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
|
@ -74,7 +74,9 @@ const AddMealModal = ({ isModalOpen, handleCloseModal }) => {
|
|||||||
key={index}
|
key={index}
|
||||||
action={
|
action={
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<Checkbox />
|
<Checkbox
|
||||||
|
edge="end"
|
||||||
|
/>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
27
src/components/GoalForm/Loader.js
Normal file
27
src/components/GoalForm/Loader.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {Button, Grid} from "@material-ui/core";
|
||||||
|
import {Skeleton} from "@material-ui/lab";
|
||||||
|
|
||||||
|
const Loader = () => {
|
||||||
|
return (
|
||||||
|
<React.Element>
|
||||||
|
<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>
|
||||||
|
</React.Element>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Loader;
|
64
src/components/GoalForm/index.js
Normal file
64
src/components/GoalForm/index.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { Grid, Button, Typography, Radio, RadioGroup } from '@material-ui/core';
|
||||||
|
import {useInjectReducer, useInjectSaga} from "redux-injectors";
|
||||||
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import {createStructuredSelector} from "reselect";
|
||||||
|
import reducer from "pages/Profile/reducer";
|
||||||
|
import saga from "pages/Profile/saga";
|
||||||
|
import { profileInputChange, getGoalsAction } from 'pages/Profile/actions'
|
||||||
|
import {
|
||||||
|
makeSelectError,
|
||||||
|
makeSelectIsLoading,
|
||||||
|
makeSelectGoals,
|
||||||
|
} from "pages/Profile/selectors";
|
||||||
|
import Loader from './Loader'
|
||||||
|
|
||||||
|
const stateSelector = createStructuredSelector({
|
||||||
|
isLoading: makeSelectIsLoading(),
|
||||||
|
error: makeSelectError(),
|
||||||
|
goals: makeSelectGoals(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = 'profilePage'
|
||||||
|
const GoalForm = () => {
|
||||||
|
useInjectReducer({ key, reducer });
|
||||||
|
useInjectSaga({ key, saga });
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const { goals, error, isLoading } = useSelector(stateSelector)
|
||||||
|
|
||||||
|
const handleChangeGoal = ({ target: { name, value }}) => {
|
||||||
|
dispatch(profileInputChange({ name, value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(getGoalsAction())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Your goal { JSON.stringify(error) }
|
||||||
|
</Typography>
|
||||||
|
<RadioGroup defaultValue={1} onChange={handleChangeGoal}>
|
||||||
|
<Grid container spacing={3} direction="column">
|
||||||
|
{isLoading ? (
|
||||||
|
<Loader />
|
||||||
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
|
{goals.map(({ label, value }, index) => (
|
||||||
|
<Grid item xs={12} key={index}>
|
||||||
|
<Button variant="text" fullWidth>
|
||||||
|
{label}
|
||||||
|
<Radio color="secondary" name="goal" value={value} />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</RadioGroup>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GoalForm;
|
@ -1,4 +1,4 @@
|
|||||||
import {List, ListItem, ListItemText, ListItemSecondaryAction} from "@material-ui/core";
|
import {ListItem, ListItemText, ListItemSecondaryAction} from "@material-ui/core";
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import MacronutrientsDetails from "components/MacronutrientsDetails";
|
import MacronutrientsDetails from "components/MacronutrientsDetails";
|
||||||
|
139
src/components/PersonalDetailsForm/index.js
Normal file
139
src/components/PersonalDetailsForm/index.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import 'date-fns';
|
||||||
|
import React, { useEffect } 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 {
|
||||||
|
MuiPickersUtilsProvider,
|
||||||
|
KeyboardDatePicker,
|
||||||
|
} from '@material-ui/pickers';
|
||||||
|
import {useInjectReducer, useInjectSaga} from "redux-injectors";
|
||||||
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import {createStructuredSelector} from "reselect";
|
||||||
|
import reducer from "pages/Profile/reducer";
|
||||||
|
import saga from "pages/Profile/saga";
|
||||||
|
import { profileInputChange, getActivitiesAction, getGendersAction } from 'pages/Profile/actions'
|
||||||
|
import {
|
||||||
|
makeSelectError,
|
||||||
|
makeSelectIsLoading,
|
||||||
|
makeSelectActivities,
|
||||||
|
makeSelectGenders,
|
||||||
|
makeSelectWeight,
|
||||||
|
makeSelectHeight,
|
||||||
|
makeSelectBirthday,
|
||||||
|
makeSelectActivity,
|
||||||
|
makeSelectGender,
|
||||||
|
} from "pages/Profile/selectors";
|
||||||
|
|
||||||
|
const stateSelector = createStructuredSelector({
|
||||||
|
isLoading: makeSelectIsLoading(),
|
||||||
|
error: makeSelectError(),
|
||||||
|
activities: makeSelectActivities(),
|
||||||
|
genders: makeSelectGenders(),
|
||||||
|
weight: makeSelectWeight(),
|
||||||
|
height: makeSelectHeight(),
|
||||||
|
birthday: makeSelectBirthday(),
|
||||||
|
activity: makeSelectActivity(),
|
||||||
|
gender: makeSelectGender(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = 'profilePage'
|
||||||
|
const PersonalDetailsForm = () => {
|
||||||
|
useInjectReducer({ key, reducer });
|
||||||
|
useInjectSaga({ key, saga });
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
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 (
|
||||||
|
<React.Fragment>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Personal details
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={3}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<FormControl variant="outlined" fullWidth>
|
||||||
|
<InputLabel>Gender</InputLabel>
|
||||||
|
<Select
|
||||||
|
name="gender"
|
||||||
|
label="Gender"
|
||||||
|
onChange={handleChangeValue}
|
||||||
|
value={gender}
|
||||||
|
>
|
||||||
|
{genders.map(({ label, value }) => (
|
||||||
|
<MenuItem value={value}>{label}</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<MuiPickersUtilsProvider utils={DateFnsUtils}>
|
||||||
|
<KeyboardDatePicker
|
||||||
|
disableToolbar
|
||||||
|
inputVariant="outlined"
|
||||||
|
fullWidth
|
||||||
|
format="MM/dd/yyyy"
|
||||||
|
margin="normal"
|
||||||
|
name="birthday"
|
||||||
|
value={birthday}
|
||||||
|
onChange={handleChangeValue}
|
||||||
|
/>
|
||||||
|
</MuiPickersUtilsProvider>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
variant="outlined"
|
||||||
|
label="Height"
|
||||||
|
type="number"
|
||||||
|
name="height"
|
||||||
|
onChange={handleChangeValue}
|
||||||
|
value={height}
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">cm</InputAdornment>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TextField
|
||||||
|
required
|
||||||
|
variant="outlined"
|
||||||
|
label="Weight"
|
||||||
|
type="number"
|
||||||
|
name="weight"
|
||||||
|
onChange={handleChangeValue}
|
||||||
|
value={weight}
|
||||||
|
fullWidth
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <InputAdornment position="end">Kg</InputAdornment>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography gutterBottom>
|
||||||
|
Activity
|
||||||
|
</Typography>
|
||||||
|
<Box mx={4}>
|
||||||
|
<Slider
|
||||||
|
name="activity"
|
||||||
|
onChange={handleChangeValue}
|
||||||
|
value={activity}
|
||||||
|
min={0}
|
||||||
|
max={4}
|
||||||
|
marks={activities}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default PersonalDetailsForm
|
38
src/components/ReviewProfileForm/index.js
Normal file
38
src/components/ReviewProfileForm/index.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import {Typography, List, ListItem, ListItemText} from '@material-ui/core';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
listItem: {
|
||||||
|
padding: theme.spacing(1, 0),
|
||||||
|
},
|
||||||
|
total: {
|
||||||
|
fontWeight: 700,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ReviewProfileForm = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Profile summary
|
||||||
|
</Typography>
|
||||||
|
<List disablePadding>
|
||||||
|
TODO
|
||||||
|
{/*{profile.map(({ label, value }, index) => (*/}
|
||||||
|
{/* <ListItem className={classes.listItem} key={index}>*/}
|
||||||
|
{/* <ListItemText primary={label} />*/}
|
||||||
|
{/* <Typography variant="body2">{value}</Typography>*/}
|
||||||
|
{/* </ListItem>*/}
|
||||||
|
{/*))}*/}
|
||||||
|
</List>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReviewProfileForm
|
@ -19,8 +19,8 @@ const App = () => {
|
|||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
{/*{isLogged && <Navbar /> }*/}
|
{isLogged && <Navbar /> }
|
||||||
<Navbar />
|
{/*<Navbar />*/}
|
||||||
<Routes />
|
<Routes />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
@ -2,11 +2,24 @@ import React from 'react';
|
|||||||
import { useInjectReducer, useInjectSaga } from 'redux-injectors';
|
import { useInjectReducer, useInjectSaga } from 'redux-injectors';
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from 'reselect';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
import { Container, Grid, Box, Paper, Button, Typography, TextField } from '@material-ui/core'
|
import {
|
||||||
|
Container,
|
||||||
|
Grid,
|
||||||
|
Box,
|
||||||
|
Paper,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
TextField,
|
||||||
|
FormControlLabel,
|
||||||
|
Checkbox,
|
||||||
|
Link
|
||||||
|
} from '@material-ui/core'
|
||||||
import reducer from './reducer';
|
import reducer from './reducer';
|
||||||
import saga from './saga';
|
import saga from './saga';
|
||||||
import { makeSelectEmail, makeSelectPassword, makeSelectLoading } from './selectors'
|
import { makeSelectEmail, makeSelectPassword, makeSelectLoading } from './selectors'
|
||||||
import { loginInputChange, loginAction } from './actions'
|
import { loginInputChange, loginAction } from './actions'
|
||||||
|
import {routes} from "../../utils";
|
||||||
|
import {makeStyles} from "@material-ui/core/styles";
|
||||||
|
|
||||||
const stateSelector = createStructuredSelector({
|
const stateSelector = createStructuredSelector({
|
||||||
email: makeSelectEmail(),
|
email: makeSelectEmail(),
|
||||||
@ -14,8 +27,25 @@ const stateSelector = createStructuredSelector({
|
|||||||
loading: makeSelectLoading(),
|
loading: makeSelectLoading(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
paper: {
|
||||||
|
marginTop: theme.spacing(8),
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
width: '100%',
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
margin: theme.spacing(3, 0, 2),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const key = 'loginPage'
|
const key = 'loginPage'
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
|
const classes = useStyles()
|
||||||
useInjectReducer({ key, reducer });
|
useInjectReducer({ key, reducer });
|
||||||
useInjectSaga({ key, saga });
|
useInjectSaga({ key, saga });
|
||||||
const { email, password, loading } = useSelector(stateSelector)
|
const { email, password, loading } = useSelector(stateSelector)
|
||||||
@ -31,36 +61,61 @@ const Login = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container component="main" maxWidth="xs">
|
||||||
<Box marginTop={2}>
|
<div className={classes.paper}>
|
||||||
<Paper component="form" noValidate autoComplete="off" onSubmit={handleSubmit}>
|
<Typography component="h1" variant="h5">
|
||||||
<Grid
|
Login to Account
|
||||||
container
|
</Typography>
|
||||||
direction="column"
|
<form className={classes.form} onSubmit={handleSubmit} noValidate>
|
||||||
alignItems="center"
|
|
||||||
justify="center"
|
|
||||||
>
|
|
||||||
<Typography>Login</Typography>
|
|
||||||
<TextField
|
<TextField
|
||||||
type="text"
|
variant="outlined"
|
||||||
label="E-mail"
|
margin="normal"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
id="email"
|
||||||
|
label="Email Address"
|
||||||
name="email"
|
name="email"
|
||||||
variant="outlined"
|
autoComplete="email"
|
||||||
value={email}
|
autoFocus
|
||||||
onChange={onChangeInput}
|
onChange={onChangeInput}
|
||||||
|
value={email}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
type="password"
|
|
||||||
label="password"
|
|
||||||
name="password"
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={password}
|
margin="normal"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
name="password"
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
autoComplete="current-password"
|
||||||
onChange={onChangeInput}
|
onChange={onChangeInput}
|
||||||
|
value={password}
|
||||||
/>
|
/>
|
||||||
<Button type="submit" disabled={loading} color="primary">Sign in</Button>
|
<FormControlLabel
|
||||||
|
control={<Checkbox value="remember" color="primary" />}
|
||||||
|
label="Remember me"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
className={classes.submit}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
<Grid container alignItems="flex-end">
|
||||||
|
<Grid item>
|
||||||
|
<Link href={routes.register.path} color="secondary" variant="body2">
|
||||||
|
Don't have an account? Sign Up
|
||||||
|
</Link>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</Grid>
|
||||||
</Box>
|
</form>
|
||||||
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
|
GET_GOALS_REQUEST,
|
||||||
|
GET_GOALS_SUCCESS,
|
||||||
|
GET_GOALS_ERROR,
|
||||||
GET_ACTIVITIES_REQUEST,
|
GET_ACTIVITIES_REQUEST,
|
||||||
GET_ACTIVITIES_SUCCESS,
|
GET_ACTIVITIES_SUCCESS,
|
||||||
GET_ACTIVITIES_ERROR,
|
GET_ACTIVITIES_ERROR,
|
||||||
@ -24,18 +27,18 @@ export const getProfileAction = () => ({
|
|||||||
type: GET_PROFILE_REQUEST,
|
type: GET_PROFILE_REQUEST,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getProfileSuccessAction = ({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}) => ({
|
export const getProfileSuccessAction = ({birthday, gender, height, weight, goalWeight, rateOfChange, activity}) => ({
|
||||||
type: GET_PROFILE_SUCCESS,
|
type: GET_PROFILE_SUCCESS,
|
||||||
birthday,
|
birthday,
|
||||||
gender,
|
gender,
|
||||||
height,
|
height,
|
||||||
currentWeight,
|
weight,
|
||||||
goalWeight,
|
goalWeight,
|
||||||
rateOfChange,
|
rateOfChange,
|
||||||
activity
|
activity
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getProfileErrorAction = (error) => ({
|
export const getProfileErrorAction = ({error}) => ({
|
||||||
type: GET_PROFILE_ERROR,
|
type: GET_PROFILE_ERROR,
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
@ -45,18 +48,18 @@ export const updateProfileAction = () => ({
|
|||||||
type: UPDATE_PROFILE_REQUEST,
|
type: UPDATE_PROFILE_REQUEST,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const updateProfileSuccessAction = ({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}) => ({
|
export const updateProfileSuccessAction = ({birthday, gender, height, weight, goalWeight, rateOfChange, activity}) => ({
|
||||||
type: UPDATE_PROFILE_SUCCESS,
|
type: UPDATE_PROFILE_SUCCESS,
|
||||||
birthday,
|
birthday,
|
||||||
gender,
|
gender,
|
||||||
height,
|
height,
|
||||||
currentWeight,
|
weight,
|
||||||
goalWeight,
|
goalWeight,
|
||||||
rateOfChange,
|
rateOfChange,
|
||||||
activity
|
activity
|
||||||
})
|
})
|
||||||
|
|
||||||
export const updateProfileErrorAction = (error) => ({
|
export const updateProfileErrorAction = ({error}) => ({
|
||||||
type: UPDATE_PROFILE_ERROR,
|
type: UPDATE_PROFILE_ERROR,
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
@ -70,7 +73,7 @@ export const getActivitiesSuccessAction = ({ activities }) => ({
|
|||||||
activities,
|
activities,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getActivitiesErrorAction = (error) => ({
|
export const getActivitiesErrorAction = ({error}) => ({
|
||||||
type: GET_ACTIVITIES_ERROR,
|
type: GET_ACTIVITIES_ERROR,
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
@ -84,7 +87,22 @@ export const getGendersSuccessAction = ({ genders }) => ({
|
|||||||
genders,
|
genders,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getGendersErrorAction = (error) => ({
|
export const getGendersErrorAction = ({error}) => ({
|
||||||
type: GET_GENDERS_ERROR,
|
type: GET_GENDERS_ERROR,
|
||||||
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,
|
||||||
|
})
|
||||||
|
|
||||||
|
@ -10,8 +10,14 @@ export const GET_GENDERS_REQUEST = 'app/ProfilePage/GET_GENDERS_REQUEST';
|
|||||||
export const GET_GENDERS_SUCCESS = 'app/ProfilePage/GET_GENDERS_SUCCESS';
|
export const GET_GENDERS_SUCCESS = 'app/ProfilePage/GET_GENDERS_SUCCESS';
|
||||||
export const GET_GENDERS_ERROR = 'app/ProfilePage/GET_GENDERS_ERROR';
|
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_REQUEST = 'app/ProfilePage/GET_ACTIVITIES_REQUEST';
|
||||||
export const GET_ACTIVITIES_SUCCESS = 'app/ProfilePage/GET_ACTIVITIES_SUCCESS';
|
export const GET_ACTIVITIES_SUCCESS = 'app/ProfilePage/GET_ACTIVITIES_SUCCESS';
|
||||||
export const GET_ACTIVITIES_ERROR = 'app/ProfilePage/GET_ACTIVITIES_ERROR';
|
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';
|
||||||
|
@ -1,145 +1,136 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Container, Slider, Button, TextField, FormControl, MenuItem, InputLabel, Select, Grid, Typography } from '@material-ui/core'
|
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 {useInjectReducer, useInjectSaga} from "redux-injectors";
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import GoalForm from 'components/GoalForm';
|
||||||
|
import PersonalDetailsForm from 'components/PersonalDetailsForm';
|
||||||
|
import ReviewProfileForm from 'components/ReviewProfileForm';
|
||||||
import reducer from "./reducer";
|
import reducer from "./reducer";
|
||||||
import saga from "./saga";
|
import saga from "./saga";
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { updateProfileAction } from './actions'
|
||||||
import {createStructuredSelector} from "reselect";
|
|
||||||
import { profileInputChange, getProfileAction, getActivitiesAction, getGendersAction } from './actions'
|
|
||||||
import {
|
|
||||||
makeSelectError,
|
|
||||||
makeSelectBirthday,
|
|
||||||
makeSelectHeight,
|
|
||||||
makeSelectCurrentWeight,
|
|
||||||
makeSelectRateOfChange,
|
|
||||||
makeSelectActivity,
|
|
||||||
makeSelectGoalWeight,
|
|
||||||
makeSelectGender,
|
|
||||||
makeSelectLoading,
|
|
||||||
} from "./selectors";
|
|
||||||
|
|
||||||
const stateSelector = createStructuredSelector({
|
const useStyles = makeStyles((theme) => ({
|
||||||
loading: makeSelectLoading(),
|
layout: {
|
||||||
error: makeSelectError(),
|
width: 'auto',
|
||||||
gender: makeSelectGender(),
|
marginLeft: theme.spacing(2),
|
||||||
birthday: makeSelectBirthday(),
|
marginRight: theme.spacing(2),
|
||||||
height: makeSelectHeight(),
|
[theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {
|
||||||
currentWeight: makeSelectCurrentWeight(),
|
width: 600,
|
||||||
goalWeight: makeSelectGoalWeight(),
|
marginLeft: 'auto',
|
||||||
rateOfChange: makeSelectRateOfChange(),
|
marginRight: 'auto',
|
||||||
activity: makeSelectActivity(),
|
},
|
||||||
});
|
},
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const steps = ['Your goal', 'Personal details', 'Review your profile'];
|
||||||
|
|
||||||
|
const getStepContent = (step) => {
|
||||||
|
switch (step) {
|
||||||
|
case 0:
|
||||||
|
return <GoalForm />;
|
||||||
|
case 1:
|
||||||
|
return <PersonalDetailsForm />;
|
||||||
|
case 2:
|
||||||
|
return <ReviewProfileForm />;
|
||||||
|
default:
|
||||||
|
throw new Error('Unknown step');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const key = 'profilePage'
|
const key = 'profilePage'
|
||||||
const ProfilePage = () => {
|
const ProfilePage = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const [activeStep, setActiveStep] = useState(0);
|
||||||
|
|
||||||
useInjectReducer({ key, reducer });
|
useInjectReducer({ key, reducer });
|
||||||
useInjectSaga({ key, saga });
|
useInjectSaga({ key, saga });
|
||||||
|
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const {
|
|
||||||
gender,
|
|
||||||
birthday,
|
|
||||||
height,
|
|
||||||
currentWeight,
|
|
||||||
goalWeight,
|
|
||||||
rateOfChange,
|
|
||||||
activity,
|
|
||||||
} = useSelector(stateSelector)
|
|
||||||
|
|
||||||
const getProfile = () => dispatch(getProfileAction())
|
const handleNext = () => {
|
||||||
const getActivities = () => dispatch(getActivitiesAction())
|
setActiveStep(activeStep + 1);
|
||||||
const getGenders = () => dispatch(getGendersAction())
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const handleBack = () => {
|
||||||
getProfile()
|
setActiveStep(activeStep - 1);
|
||||||
getActivities()
|
};
|
||||||
getGenders()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const handleChange = ({ target: { name, value }}) => {
|
const handleSubmitProfile = (event) => {
|
||||||
dispatch(profileInputChange({ name, value }))
|
event.preventDefault();
|
||||||
|
dispatch(updateProfileAction())
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<main className={classes.layout}>
|
||||||
<Grid container direction="column" alignItems="center" justify="center">
|
<Paper className={classes.paper}>
|
||||||
<Typography variant="h5">Profile</Typography>
|
<Typography component="h1" variant="h4" align="center">
|
||||||
<FormControl variant="outlined" fullWidth>
|
Profile
|
||||||
<InputLabel id="gender-label">Gender</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="gender-label"
|
|
||||||
name="gender"
|
|
||||||
value={gender}
|
|
||||||
onChange={handleChange}
|
|
||||||
label="Gender"
|
|
||||||
>
|
|
||||||
<MenuItem value={`male`}>Male</MenuItem>
|
|
||||||
<MenuItem value={`female`}>Female</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<TextField
|
|
||||||
label="Height"
|
|
||||||
name="height"
|
|
||||||
type="number"
|
|
||||||
value={height}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<TextField
|
|
||||||
label="Current weight"
|
|
||||||
name="currentWeight"
|
|
||||||
type="number"
|
|
||||||
value={currentWeight}
|
|
||||||
onChange={handleChange}
|
|
||||||
variant="outlined"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<TextField
|
|
||||||
label="Goal weight"
|
|
||||||
name="goalWeight"
|
|
||||||
type="number"
|
|
||||||
value={goalWeight}
|
|
||||||
onChange={handleChange}
|
|
||||||
variant="outlined"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl fullWidth>
|
|
||||||
<Typography id="rate-of-change" gutterBottom>
|
|
||||||
The rate of weight change
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<Slider
|
<Stepper activeStep={activeStep} className={classes.stepper}>
|
||||||
value={rateOfChange}
|
{steps.map((label) => (
|
||||||
onChange={handleChange}
|
<Step key={label}>
|
||||||
aria-labelledby="rate-of-change"
|
<StepLabel>{label}</StepLabel>
|
||||||
step={0.1}
|
</Step>
|
||||||
marks
|
))}
|
||||||
min={0}
|
</Stepper>
|
||||||
max={1}
|
<React.Fragment>
|
||||||
valueLabelDisplay="auto"
|
{activeStep === steps.length ? (
|
||||||
/>
|
<React.Fragment>
|
||||||
</FormControl>
|
<Typography variant="h5" gutterBottom>
|
||||||
<FormControl fullWidth>
|
Thank you for your order.
|
||||||
<InputLabel id="activity-label">Activity</InputLabel>
|
</Typography>
|
||||||
<Select
|
<Typography variant="subtitle1">
|
||||||
labelId="activity-label"
|
Your order number is #2001539. We have emailed your order confirmation, and will
|
||||||
name="activity"
|
send you an update when your order has shipped.
|
||||||
value={activity}
|
</Typography>
|
||||||
onChange={handleChange}
|
</React.Fragment>
|
||||||
label="Activity"
|
) : (
|
||||||
|
<form onSubmit={handleSubmitProfile}>
|
||||||
|
{getStepContent(activeStep)}
|
||||||
|
<div className={classes.buttons}>
|
||||||
|
{activeStep !== 0 && (
|
||||||
|
<Button onClick={handleBack} className={classes.button}>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleNext}
|
||||||
|
type={activeStep === steps.length - 1 ? 'submit' : 'button'}
|
||||||
|
className={classes.button}
|
||||||
>
|
>
|
||||||
<MenuItem value={`1`}>Small</MenuItem>
|
{activeStep === steps.length - 1 ? 'Save profile' : 'Next'}
|
||||||
<MenuItem value={`1.2`}>Medium</MenuItem>
|
</Button>
|
||||||
<MenuItem value={`2`}>High</MenuItem>
|
</div>
|
||||||
</Select>
|
</form>
|
||||||
</FormControl>
|
)}
|
||||||
<FormControl fullWidth>
|
</React.Fragment>
|
||||||
<Button variant="contained" color="primary">Save</Button>
|
</Paper>
|
||||||
</FormControl>
|
</main>
|
||||||
</Grid>
|
)
|
||||||
</Container>
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProfilePage;
|
export default ProfilePage
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import {
|
import {
|
||||||
|
GET_GOALS_REQUEST,
|
||||||
|
GET_GOALS_SUCCESS,
|
||||||
|
GET_GOALS_ERROR,
|
||||||
GET_ACTIVITIES_SUCCESS,
|
GET_ACTIVITIES_SUCCESS,
|
||||||
GET_GENDERS_SUCCESS,
|
GET_GENDERS_SUCCESS,
|
||||||
GET_ACTIVITIES_ERROR,
|
GET_ACTIVITIES_ERROR,
|
||||||
@ -17,13 +20,14 @@ import {
|
|||||||
|
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
activities: [],
|
activities: [],
|
||||||
|
goals: [],
|
||||||
genders: [],
|
genders: [],
|
||||||
loading: false,
|
isLoading: false,
|
||||||
error: {},
|
error: {},
|
||||||
gender: '',
|
gender: '',
|
||||||
birthday: '',
|
birthday: new Date(),
|
||||||
height: 0,
|
height: 0,
|
||||||
currentWeight: 0,
|
weight: 0,
|
||||||
goalWeight: 0,
|
goalWeight: 0,
|
||||||
rateOfChange: 0,
|
rateOfChange: 0,
|
||||||
activity: 0,
|
activity: 0,
|
||||||
@ -33,12 +37,17 @@ const loginPageReducer = produce((draft, action) => {
|
|||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case GET_ACTIVITIES_SUCCESS:
|
case GET_ACTIVITIES_SUCCESS:
|
||||||
draft.activities = action.activities;
|
draft.activities = action.activities;
|
||||||
draft.loading = false;
|
draft.isLoading = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GET_GOALS_SUCCESS:
|
||||||
|
draft.goals = action.goals;
|
||||||
|
draft.isLoading = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GET_GENDERS_SUCCESS:
|
case GET_GENDERS_SUCCESS:
|
||||||
draft.genders = action.genders;
|
draft.genders = action.genders;
|
||||||
draft.loading = false;
|
draft.isLoading = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GET_PROFILE_SUCCESS:
|
case GET_PROFILE_SUCCESS:
|
||||||
@ -46,25 +55,27 @@ const loginPageReducer = produce((draft, action) => {
|
|||||||
draft.birthday = action.birthday;
|
draft.birthday = action.birthday;
|
||||||
draft.gender = action.gender;
|
draft.gender = action.gender;
|
||||||
draft.height = action.height;
|
draft.height = action.height;
|
||||||
draft.currentWeight = action.currentWeight;
|
draft.weight = action.weight;
|
||||||
draft.goalWeight = action.goalWeight;
|
draft.goalWeight = action.goalWeight;
|
||||||
draft.rateOfChange = action.rateOfChange;
|
draft.rateOfChange = action.rateOfChange;
|
||||||
draft.activity = action.activity;
|
draft.activity = action.activity;
|
||||||
draft.loading = false;
|
draft.isLoading = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GET_ACTIVITIES_REQUEST:
|
case GET_ACTIVITIES_REQUEST:
|
||||||
case GET_GENDERS_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.loading = true;
|
draft.isLoading = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GET_ACTIVITIES_ERROR:
|
case GET_ACTIVITIES_ERROR:
|
||||||
case GET_GENDERS_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.loading = false;
|
draft.isLoading = false;
|
||||||
draft.error = action.error;
|
draft.error = action.error;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -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_ACTIVITIES_REQUEST, GET_GENDERS_REQUEST, GET_PROFILE_REQUEST, UPDATE_PROFILE_REQUEST } from './constants';
|
import { GET_GOALS_REQUEST, GET_ACTIVITIES_REQUEST, GET_GENDERS_REQUEST, GET_PROFILE_REQUEST, UPDATE_PROFILE_REQUEST } from './constants';
|
||||||
import {
|
import {
|
||||||
makeSelectBirthday,
|
makeSelectBirthday,
|
||||||
makeSelectHeight,
|
makeSelectHeight,
|
||||||
makeSelectCurrentWeight,
|
makeSelectWeight,
|
||||||
makeSelectRateOfChange,
|
makeSelectRateOfChange,
|
||||||
makeSelectActivity,
|
makeSelectActivity,
|
||||||
makeSelectGoalWeight,
|
makeSelectGoalWeight,
|
||||||
makeSelectGender
|
makeSelectGender
|
||||||
} from './selectors';
|
} from './selectors';
|
||||||
import { getActivitiesSuccessAction, getActivitiesErrorAction, getGendersSuccessAction, getGendersErrorAction, updateProfileErrorAction, updateProfileSuccessAction, getProfileErrorAction, getProfileSuccessAction } from './actions';
|
import { getGoalsErrorAction, getGoalsSuccessAction, getActivitiesSuccessAction, getActivitiesErrorAction, getGendersSuccessAction, getGendersErrorAction, updateProfileErrorAction, updateProfileSuccessAction, getProfileErrorAction, getProfileSuccessAction } from './actions';
|
||||||
|
|
||||||
import { makeSelectTokens } from 'containers/App/selectors'
|
import { makeSelectTokens } from 'containers/App/selectors'
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export function* getProfile() {
|
|||||||
const { birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters);
|
const { birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters);
|
||||||
yield put(getProfileSuccessAction({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}));
|
yield put(getProfileSuccessAction({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(getProfileErrorAction(error.message));
|
yield put(getProfileErrorAction({ error: error.message }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ export function* updateProfile() {
|
|||||||
const birthday = yield select(makeSelectBirthday());
|
const birthday = yield select(makeSelectBirthday());
|
||||||
const gender = yield select(makeSelectGender());
|
const gender = yield select(makeSelectGender());
|
||||||
const height = yield select(makeSelectHeight());
|
const height = yield select(makeSelectHeight());
|
||||||
const currentWeight = yield select(makeSelectCurrentWeight());
|
const weight = yield select(makeSelectWeight());
|
||||||
const goalWeight = yield select(makeSelectGoalWeight());
|
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,14 +50,14 @@ 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, currentWeight, goalWeight, rateOfChange, activity }),
|
body: JSON.stringify({ birthday, gender, height, weight, goalWeight, rateOfChange, activity }),
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters);
|
const { birthday, gender, height, weight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters);
|
||||||
yield put(updateProfileSuccessAction({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}));
|
yield put(updateProfileSuccessAction({birthday, gender, height, weight, goalWeight, rateOfChange, activity}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(updateProfileErrorAction(error.message));
|
yield put(updateProfileErrorAction({error: error.message}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,11 +77,10 @@ export function* getActivities() {
|
|||||||
const { activities } = yield call(request, requestURL, requestParameters);
|
const { activities } = yield call(request, requestURL, requestParameters);
|
||||||
yield put(getActivitiesSuccessAction({ activities }));
|
yield put(getActivitiesSuccessAction({ activities }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(getActivitiesErrorAction(error.message));
|
yield put(getActivitiesErrorAction({error: error.message}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function* getGenders() {
|
export function* getGenders() {
|
||||||
const { access } = yield select(makeSelectTokens());
|
const { access } = yield select(makeSelectTokens());
|
||||||
|
|
||||||
@ -98,7 +97,26 @@ export function* getGenders() {
|
|||||||
const { genders } = yield call(request, requestURL, requestParameters);
|
const { genders } = yield call(request, requestURL, requestParameters);
|
||||||
yield put(getGendersSuccessAction({ genders }));
|
yield put(getGendersSuccessAction({ genders }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(getGendersErrorAction(error.message));
|
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 { goals } = yield call(request, requestURL, requestParameters);
|
||||||
|
yield put(getGoalsSuccessAction({ goals }));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(getGoalsErrorAction({ error: error.message }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,5 +125,6 @@ export default function* profilePageSaga() {
|
|||||||
yield takeLatest(GET_ACTIVITIES_REQUEST, getActivities);
|
yield takeLatest(GET_ACTIVITIES_REQUEST, getActivities);
|
||||||
yield takeLatest(GET_GENDERS_REQUEST, getGenders);
|
yield takeLatest(GET_GENDERS_REQUEST, getGenders);
|
||||||
yield takeLatest(GET_PROFILE_REQUEST, getProfile);
|
yield takeLatest(GET_PROFILE_REQUEST, getProfile);
|
||||||
|
yield takeLatest(GET_GOALS_REQUEST, getGoals);
|
||||||
yield takeLatest(UPDATE_PROFILE_REQUEST, updateProfile);
|
yield takeLatest(UPDATE_PROFILE_REQUEST, updateProfile);
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ const makeSelectBirthday = () =>
|
|||||||
const makeSelectHeight = () =>
|
const makeSelectHeight = () =>
|
||||||
createSelector(selectProfilePageDomain, (substate) => substate.height);
|
createSelector(selectProfilePageDomain, (substate) => substate.height);
|
||||||
|
|
||||||
const makeSelectCurrentWeight = () =>
|
const makeSelectWeight = () =>
|
||||||
createSelector(selectProfilePageDomain, (substate) => substate.currentWeight);
|
createSelector(selectProfilePageDomain, (substate) => substate.weight);
|
||||||
|
|
||||||
const makeSelectRateOfChange = () =>
|
const makeSelectRateOfChange = () =>
|
||||||
createSelector(selectProfilePageDomain, (substate) => substate.rateOfChange);
|
createSelector(selectProfilePageDomain, (substate) => substate.rateOfChange);
|
||||||
@ -27,18 +27,30 @@ const makeSelectGoalWeight = () =>
|
|||||||
const makeSelectGender = () =>
|
const makeSelectGender = () =>
|
||||||
createSelector(selectProfilePageDomain, (substate) => substate.gender);
|
createSelector(selectProfilePageDomain, (substate) => substate.gender);
|
||||||
|
|
||||||
const makeSelectLoading = () =>
|
const makeSelectIsLoading = () =>
|
||||||
createSelector(selectProfilePageDomain, (substate) => substate.loading);
|
createSelector(selectProfilePageDomain, (substate) => substate.isLoading);
|
||||||
|
|
||||||
|
const makeSelectGoals = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.goals);
|
||||||
|
|
||||||
|
const makeSelectActivities = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.activities);
|
||||||
|
|
||||||
|
const makeSelectGenders = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.genders);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
selectProfilePageDomain,
|
selectProfilePageDomain,
|
||||||
makeSelectError,
|
makeSelectError,
|
||||||
makeSelectBirthday,
|
makeSelectBirthday,
|
||||||
makeSelectHeight,
|
makeSelectHeight,
|
||||||
makeSelectCurrentWeight,
|
makeSelectWeight,
|
||||||
makeSelectRateOfChange,
|
makeSelectRateOfChange,
|
||||||
makeSelectActivity,
|
makeSelectActivity,
|
||||||
makeSelectGoalWeight,
|
makeSelectGoalWeight,
|
||||||
makeSelectGender,
|
makeSelectGender,
|
||||||
makeSelectLoading,
|
makeSelectIsLoading,
|
||||||
|
makeSelectGoals,
|
||||||
|
makeSelectActivities,
|
||||||
|
makeSelectGenders,
|
||||||
};
|
};
|
||||||
|
80
src/pages/Register/index.js
Normal file
80
src/pages/Register/index.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
import {Container, Typography, Grid, Button, TextField, FormControlLabel, Checkbox, Link} from '@material-ui/core';
|
||||||
|
import { routes } from 'utils';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
paper: {
|
||||||
|
marginTop: theme.spacing(8),
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
width: '100%',
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
margin: theme.spacing(3, 0, 2),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const RegisterPage = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container component="main" maxWidth="xs">
|
||||||
|
<div className={classes.paper}>
|
||||||
|
<Typography component="h1" variant="h5">
|
||||||
|
Create Account
|
||||||
|
</Typography>
|
||||||
|
<form className={classes.form} noValidate>
|
||||||
|
<TextField
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
id="email"
|
||||||
|
label="Email Address"
|
||||||
|
name="email"
|
||||||
|
autoComplete="email"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
variant="outlined"
|
||||||
|
margin="normal"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
name="password"
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
autoComplete="current-password"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={<Checkbox value="remember" color="primary" />}
|
||||||
|
label="Remember me"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
className={classes.submit}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
<Grid container alignItems="flex-end">
|
||||||
|
<Grid item>
|
||||||
|
<Link href={routes.login.path} color="secondary" variant="body2">
|
||||||
|
Have already account? Sign In
|
||||||
|
</Link>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RegisterPage
|
@ -3,6 +3,7 @@ const AUTH = 'auth';
|
|||||||
const PROFILE = 'profiles';
|
const PROFILE = 'profiles';
|
||||||
const ACTIVITY = 'activities';
|
const ACTIVITY = 'activities';
|
||||||
const GENDER = 'genders';
|
const GENDER = 'genders';
|
||||||
|
const GOALS = 'goals';
|
||||||
|
|
||||||
const urls = {
|
const urls = {
|
||||||
auth: {
|
auth: {
|
||||||
@ -12,6 +13,7 @@ const urls = {
|
|||||||
profile: `${API_BASE_URL}/${PROFILE}`,
|
profile: `${API_BASE_URL}/${PROFILE}`,
|
||||||
activities: `${API_BASE_URL}/${ACTIVITY}`,
|
activities: `${API_BASE_URL}/${ACTIVITY}`,
|
||||||
genders: `${API_BASE_URL}/${GENDER}`,
|
genders: `${API_BASE_URL}/${GENDER}`,
|
||||||
|
goals: `${API_BASE_URL}/${GOALS}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default urls
|
export default urls
|
||||||
|
@ -4,7 +4,7 @@ import blue from "@material-ui/core/colors/blue";
|
|||||||
|
|
||||||
export const MEALS_LIST = [
|
export const MEALS_LIST = [
|
||||||
{
|
{
|
||||||
name: 'eggs',
|
label: 'eggs',
|
||||||
macronutrients: [
|
macronutrients: [
|
||||||
{
|
{
|
||||||
value: 1245,
|
value: 1245,
|
||||||
@ -25,7 +25,7 @@ export const MEALS_LIST = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'bread',
|
label: 'bread',
|
||||||
macronutrients: [
|
macronutrients: [
|
||||||
{
|
{
|
||||||
value: 1245,
|
value: 1245,
|
||||||
@ -46,7 +46,7 @@ export const MEALS_LIST = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'corn flakes',
|
label: 'corn flakes',
|
||||||
macronutrients: [
|
macronutrients: [
|
||||||
{
|
{
|
||||||
value: 1245,
|
value: 1245,
|
||||||
@ -88,22 +88,22 @@ export const MENU_LIST = [
|
|||||||
unit: 'g',
|
unit: 'g',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
listName: 'BREAKFAST',
|
label: 'BREAKFAST',
|
||||||
meals: MEALS_LIST,
|
meals: MEALS_LIST,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
macronutrients: [],
|
macronutrients: [],
|
||||||
listName: 'LUNCH',
|
label: 'LUNCH',
|
||||||
meals: []
|
meals: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
macronutrients: [],
|
macronutrients: [],
|
||||||
listName: 'DINNER',
|
label: 'DINNER',
|
||||||
meals: []
|
meals: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
macronutrients: [],
|
macronutrients: [],
|
||||||
listName: 'SUPPER',
|
label: 'SUPPER',
|
||||||
meals: []
|
meals: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -131,4 +131,48 @@ export const MACRONUTRIENTS = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
export const GOALS = [
|
||||||
|
{
|
||||||
|
name: 'lose weight',
|
||||||
|
label: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'put on weight',
|
||||||
|
label: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'maintain weight',
|
||||||
|
label: 3,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const GENDERS = [
|
||||||
|
{label: 'male', value: 'male'},
|
||||||
|
{label: 'female', value: 'female'},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const ACTIVITY = [
|
||||||
|
{
|
||||||
|
label: 'very low',
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'low',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'average',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'high',
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'very high',
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export const TABS = Array.from({ length: 30 }, () => `${Math.floor(Math.random() * 120)}`)
|
export const TABS = Array.from({ length: 30 }, () => `${Math.floor(Math.random() * 120)}`)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import HomePage from "pages/Home";
|
import HomePage from "pages/Home";
|
||||||
import ProfilePage from "pages/Profile";
|
import ProfilePage from "pages/Profile";
|
||||||
import LoginPage from "pages/Login";
|
import LoginPage from "pages/Login";
|
||||||
|
import RegisterPage from "pages/Register";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
dashboard: {
|
dashboard: {
|
||||||
@ -18,6 +19,11 @@ const routes = {
|
|||||||
exact: true,
|
exact: true,
|
||||||
component: LoginPage,
|
component: LoginPage,
|
||||||
},
|
},
|
||||||
|
register: {
|
||||||
|
path: '/register',
|
||||||
|
exact: true,
|
||||||
|
component: RegisterPage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default routes
|
export default routes
|
||||||
|
@ -10,5 +10,10 @@ const theme = createMuiTheme({
|
|||||||
main: '#40c4ff',
|
main: '#40c4ff',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MuiSlider: {
|
||||||
|
markLabel: {
|
||||||
|
transform: 'translateX(-12%)',
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
export default theme
|
export default theme
|
||||||
|
49
yarn.lock
49
yarn.lock
@ -970,7 +970,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
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.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
"@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":
|
||||||
version "7.12.5"
|
version "7.12.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
|
||||||
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
|
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
|
||||||
@ -1033,6 +1033,18 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
|
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
|
||||||
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
|
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
|
||||||
|
|
||||||
|
"@date-io/core@1.x", "@date-io/core@^1.3.13":
|
||||||
|
version "1.3.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-io/core/-/core-1.3.13.tgz#90c71da493f20204b7a972929cc5c482d078b3fa"
|
||||||
|
integrity sha512-AlEKV7TxjeK+jxWVKcCFrfYAk8spX9aCyiToFIiLPtfQbsjmRGLIhb5VZgptQcJdHtLXo7+m0DuurwFgUToQuA==
|
||||||
|
|
||||||
|
"@date-io/date-fns@^1.3.13":
|
||||||
|
version "1.3.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-io/date-fns/-/date-fns-1.3.13.tgz#7798844041640ab393f7e21a7769a65d672f4735"
|
||||||
|
integrity sha512-yXxGzcRUPcogiMj58wVgFjc9qUYrCnnU9eLcyNbsQCmae4jPuZCDoIBR21j8ZURsM7GRtU62VOw5yNd4dDHunA==
|
||||||
|
dependencies:
|
||||||
|
"@date-io/core" "^1.3.13"
|
||||||
|
|
||||||
"@emotion/hash@^0.8.0":
|
"@emotion/hash@^0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||||
@ -1320,6 +1332,18 @@
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
react-is "^16.8.0 || ^17.0.0"
|
react-is "^16.8.0 || ^17.0.0"
|
||||||
|
|
||||||
|
"@material-ui/pickers@^3.2.10":
|
||||||
|
version "3.2.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.2.10.tgz#19df024895876eb0ec7cd239bbaea595f703f0ae"
|
||||||
|
integrity sha512-B8G6Obn5S3RCl7hwahkQj9sKUapwXWFjiaz/Bsw1fhYFdNMnDUolRiWQSoKPb1/oKe37Dtfszoywi1Ynbo3y8w==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.6.0"
|
||||||
|
"@date-io/core" "1.x"
|
||||||
|
"@types/styled-jsx" "^2.2.8"
|
||||||
|
clsx "^1.0.2"
|
||||||
|
react-transition-group "^4.0.0"
|
||||||
|
rifm "^0.7.0"
|
||||||
|
|
||||||
"@material-ui/styles@^4.11.1":
|
"@material-ui/styles@^4.11.1":
|
||||||
version "4.11.1"
|
version "4.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.1.tgz#089338637e9c358eddccd75c32f0bafd0237d573"
|
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.1.tgz#089338637e9c358eddccd75c32f0bafd0237d573"
|
||||||
@ -1840,6 +1864,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
|
||||||
integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
|
integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
|
||||||
|
|
||||||
|
"@types/styled-jsx@^2.2.8":
|
||||||
|
version "2.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.8.tgz#b50d13d8a3c34036282d65194554cf186bab7234"
|
||||||
|
integrity sha512-Yjye9VwMdYeXfS71ihueWRSxrruuXTwKCbzue4+5b2rjnQ//AtyM7myZ1BEhNhBQ/nL/RE7bdToUoLln2miKvg==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/tapable@*", "@types/tapable@^1.0.5":
|
"@types/tapable@*", "@types/tapable@^1.0.5":
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74"
|
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74"
|
||||||
@ -3296,7 +3327,7 @@ clone-deep@^4.0.1:
|
|||||||
kind-of "^6.0.2"
|
kind-of "^6.0.2"
|
||||||
shallow-clone "^3.0.0"
|
shallow-clone "^3.0.0"
|
||||||
|
|
||||||
clsx@^1.0.4:
|
clsx@^1.0.2, clsx@^1.0.4:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||||
@ -3951,6 +3982,11 @@ 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:
|
||||||
|
version "2.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b"
|
||||||
|
integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==
|
||||||
|
|
||||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
@ -9557,7 +9593,7 @@ react-side-effect@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3"
|
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3"
|
||||||
integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==
|
integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==
|
||||||
|
|
||||||
react-transition-group@^4.4.0:
|
react-transition-group@^4.0.0, react-transition-group@^4.4.0:
|
||||||
version "4.4.1"
|
version "4.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
|
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
|
||||||
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
|
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
|
||||||
@ -9970,6 +10006,13 @@ rgba-regex@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
|
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
|
||||||
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
|
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
|
||||||
|
|
||||||
|
rifm@^0.7.0:
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.7.0.tgz#debe951a9c83549ca6b33e5919f716044c2230be"
|
||||||
|
integrity sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.3.1"
|
||||||
|
|
||||||
rimraf@2.6.3:
|
rimraf@2.6.3:
|
||||||
version "2.6.3"
|
version "2.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||||
|
Loading…
Reference in New Issue
Block a user