Fix creating meal list and adding products
This commit is contained in:
parent
bf835d2a89
commit
1a611fbea1
@ -0,0 +1,14 @@
|
|||||||
|
import productFormModel from './productFormModel';
|
||||||
|
const {
|
||||||
|
formField: {
|
||||||
|
quantity,
|
||||||
|
unit,
|
||||||
|
}
|
||||||
|
} = productFormModel;
|
||||||
|
|
||||||
|
const formInitialValues = {
|
||||||
|
[quantity.name]: 1,
|
||||||
|
[unit.name]: 'portion',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default formInitialValues
|
@ -0,0 +1,17 @@
|
|||||||
|
const productFormModel = {
|
||||||
|
formId: 'addProductsToMealForm',
|
||||||
|
formField: {
|
||||||
|
quantity: {
|
||||||
|
name: 'quantity',
|
||||||
|
label: 'Quantity*',
|
||||||
|
requiredErrorMsg: 'Quantity is required'
|
||||||
|
},
|
||||||
|
unit: {
|
||||||
|
name: 'unit',
|
||||||
|
label: 'Unit*',
|
||||||
|
requiredErrorMsg: 'Unit is required'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default productFormModel
|
@ -0,0 +1,15 @@
|
|||||||
|
import * as Yup from 'yup';
|
||||||
|
import productFormModel from './productFormModel';
|
||||||
|
const {
|
||||||
|
formField: {
|
||||||
|
unit,
|
||||||
|
quantity,
|
||||||
|
}
|
||||||
|
} = productFormModel;
|
||||||
|
|
||||||
|
const validationSchema =
|
||||||
|
Yup.object().shape({
|
||||||
|
[unit.name]: Yup.string().required(`${unit.requiredErrorMsg}`),
|
||||||
|
[quantity.name]: Yup.number().required(`${quantity.requiredErrorMsg}`),
|
||||||
|
})
|
||||||
|
export default validationSchema
|
@ -0,0 +1,111 @@
|
|||||||
|
import {Button, Dialog, DialogActions, Typography, FormControl, DialogContent, DialogTitle} from "@material-ui/core";
|
||||||
|
import React from "react";
|
||||||
|
import {Form, Formik} from "formik";
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { createStructuredSelector } from 'reselect'
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import SelectField from "components/SelectField";
|
||||||
|
import InputField from "components/InputField";
|
||||||
|
import formInitialValues from "../../FormModel/formInitialValues";
|
||||||
|
import validationSchema from "../../FormModel/validationSchema";
|
||||||
|
import productFormModel from "../../FormModel/productFormModel";
|
||||||
|
import {createMealAction, addProductsToMealAction} from "pages/Home/actions";
|
||||||
|
import { makeSelectMealId, makeSelectMealLabel } from 'pages/Home/selectors'
|
||||||
|
import {format} from "date-fns";
|
||||||
|
|
||||||
|
const stateSelector = createStructuredSelector({
|
||||||
|
mealId: makeSelectMealId(),
|
||||||
|
mealLabel: makeSelectMealLabel()
|
||||||
|
})
|
||||||
|
|
||||||
|
const { formId, formField } = productFormModel;
|
||||||
|
|
||||||
|
const ProductDetailsForm = ({
|
||||||
|
isOpen,
|
||||||
|
handleClose,
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
unit,
|
||||||
|
servingCapacity,
|
||||||
|
macronutrients
|
||||||
|
}) => {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const { mealId, mealLabel } = useSelector(stateSelector)
|
||||||
|
|
||||||
|
const unitTypes = [
|
||||||
|
{ label: 'None', value: undefined },
|
||||||
|
{ label: 'portion', value: 'portion' },
|
||||||
|
{ label: unit, value: unit },
|
||||||
|
]
|
||||||
|
|
||||||
|
const today = format(new Date(), "yyyy-MM-dd");
|
||||||
|
|
||||||
|
const onSubmit = (values, actions) => {
|
||||||
|
const products = [{
|
||||||
|
...values,
|
||||||
|
product: id,
|
||||||
|
}]
|
||||||
|
|
||||||
|
if (mealId) {
|
||||||
|
dispatch(addProductsToMealAction({ id: mealId, products }))
|
||||||
|
} else {
|
||||||
|
dispatch(createMealAction({ date: today, label: mealLabel, products }))
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.setSubmitting(false);
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={isOpen}
|
||||||
|
>
|
||||||
|
<Formik
|
||||||
|
initialValues={formInitialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
>
|
||||||
|
{({ isSubmitting }) => (
|
||||||
|
<Form id={formId}>
|
||||||
|
<DialogTitle>{label}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Typography variant="body2">1 portion = {servingCapacity}${unit}</Typography>
|
||||||
|
<FormControl>
|
||||||
|
<InputField label={formField.quantity.label} name={formField.quantity.name} type="number" min="1" />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl >
|
||||||
|
<SelectField label={formField.unit.label} name={formField.unit.name} data={unitTypes} />
|
||||||
|
</FormControl>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose}>
|
||||||
|
cancel
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" color="primary" disabled={isSubmitting}>
|
||||||
|
Ok
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductDetailsForm.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
handleClose: PropTypes.func.isRequired,
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
unit: PropTypes.string.isRequired,
|
||||||
|
servingCapacity: PropTypes.number.isRequired,
|
||||||
|
macronutrients: PropTypes.shape({
|
||||||
|
salt: PropTypes.number.isRequired,
|
||||||
|
calories: PropTypes.number.isRequired,
|
||||||
|
fat: PropTypes.number.isRequired,
|
||||||
|
carbohydrates: PropTypes.number.isRequired,
|
||||||
|
protein: PropTypes.number.isRequired,
|
||||||
|
}).isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProductDetailsForm
|
@ -1,25 +1,55 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import {Checkbox, ListItem, ListItemSecondaryAction, ListItemText} from "@material-ui/core";
|
import {Checkbox, ListItem, Typography, ListItemSecondaryAction, ListItemText} from "@material-ui/core";
|
||||||
import ProductLabel from 'components/ProductLabel'
|
import ProductLabel from 'components/ProductLabel'
|
||||||
|
import ProductDetailsForm from './Forms/ProductDetailsForm'
|
||||||
|
|
||||||
const ProductItem = ({ id, verified, eco, label, unit, servingCapacity, macronutrients }) => {
|
const ProductItem = ({ id, verified, eco, label, unit, servingCapacity, macronutrients }) => {
|
||||||
|
const [isProductDetailsDialogOpen, setIsProductDetailsDialogOpen] = useState(false);
|
||||||
|
const { calories } = macronutrients
|
||||||
|
const handleOpenDialog = () => {
|
||||||
|
setIsProductDetailsDialogOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseDialog = () => {
|
||||||
|
setIsProductDetailsDialogOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem button>
|
<React.Fragment>
|
||||||
<ListItemText
|
<ListItem button onClick={handleOpenDialog}>
|
||||||
primary={<ProductLabel text={label} verified={verified} eco={eco} />}
|
<ListItemText
|
||||||
disableTypography
|
primary={<ProductLabel text={label} verified={verified} eco={eco} />}
|
||||||
/>
|
secondary={
|
||||||
<ListItemSecondaryAction>
|
<Typography variant="body2">1 portion = {Math.floor((servingCapacity / 100) * calories)}kcal</Typography>
|
||||||
<Checkbox
|
}
|
||||||
edge="end"
|
disableTypography
|
||||||
/>
|
/>
|
||||||
</ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
</ListItem>
|
<Checkbox
|
||||||
|
value={id}
|
||||||
|
name="product"
|
||||||
|
edge="end"
|
||||||
|
/>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
</ListItem>
|
||||||
|
<ProductDetailsForm
|
||||||
|
isOpen={isProductDetailsDialogOpen}
|
||||||
|
id={id}
|
||||||
|
handleClose={handleCloseDialog}
|
||||||
|
verified={verified}
|
||||||
|
eco={eco}
|
||||||
|
label={label}
|
||||||
|
unit={unit}
|
||||||
|
servingCapacity={servingCapacity}
|
||||||
|
macronutrients={macronutrients}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ProductItem.propTypes = {
|
ProductItem.propTypes = {
|
||||||
|
selected: PropTypes.bool.isRequired,
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
verified: PropTypes.bool.isRequired,
|
verified: PropTypes.bool.isRequired,
|
||||||
eco: PropTypes.bool.isRequired,
|
eco: PropTypes.bool.isRequired,
|
||||||
|
@ -10,22 +10,26 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
List,
|
List,
|
||||||
} from '@material-ui/core';
|
} from '@material-ui/core';
|
||||||
import {Add as AddIcon, Delete as DeleteIcon} from "@material-ui/icons";
|
import { useDispatch } from 'react-redux'
|
||||||
|
import {Add as AddIcon} from "@material-ui/icons";
|
||||||
import SearchInput from 'components/SearchInput'
|
import SearchInput from 'components/SearchInput'
|
||||||
import BarcodeScanner from 'components/BarcodeScanner'
|
import BarcodeScanner from 'components/BarcodeScanner'
|
||||||
import { createStructuredSelector } from 'reselect'
|
import { createStructuredSelector } from 'reselect'
|
||||||
import { makeSelectProducts, makeSelectProduct } from 'pages/Home/selectors'
|
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
import { makeSelectProducts } from 'pages/Home/selectors'
|
||||||
import ProductItem from './ProductItem'
|
import ProductItem from './ProductItem'
|
||||||
|
import { setSelectedMealAction } from 'pages/Home/actions'
|
||||||
|
|
||||||
const stateSelector = createStructuredSelector({
|
const stateSelector = createStructuredSelector({
|
||||||
products: makeSelectProducts(),
|
products: makeSelectProducts(),
|
||||||
product: makeSelectProduct(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const AddProductToMealDialog = ({ mealLabel }) => {
|
const AddProductToMealDialog = ({ mealId, mealLabel }) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { products, product } = useSelector(stateSelector)
|
const { products } = useSelector(stateSelector)
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
const selectMeal = () => dispatch(setSelectedMealAction({ label: mealLabel, id: mealId }))
|
||||||
|
|
||||||
const handleClose = (event) => {
|
const handleClose = (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@ -35,8 +39,8 @@ const AddProductToMealDialog = ({ mealLabel }) => {
|
|||||||
const handleOpen = (event) => {
|
const handleOpen = (event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
setIsOpen(true)
|
setIsOpen(true)
|
||||||
|
selectMeal();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<IconButton onClick={handleOpen} onFocus={(event) => event.stopPropagation()}>
|
<IconButton onClick={handleOpen} onFocus={(event) => event.stopPropagation()}>
|
||||||
@ -61,6 +65,7 @@ const AddProductToMealDialog = ({ mealLabel }) => {
|
|||||||
<List>
|
<List>
|
||||||
{products.map(({ id, verified, eco, salt, brand, servingCapacity, barcode, label, unit, calories, fat, carbohydrates, protein }) => (
|
{products.map(({ id, verified, eco, salt, brand, servingCapacity, barcode, label, unit, calories, fat, carbohydrates, protein }) => (
|
||||||
<ProductItem
|
<ProductItem
|
||||||
|
selected={false}
|
||||||
key={id}
|
key={id}
|
||||||
id={id}
|
id={id}
|
||||||
verified={verified}
|
verified={verified}
|
||||||
@ -76,7 +81,7 @@ const AddProductToMealDialog = ({ mealLabel }) => {
|
|||||||
protein,
|
protein,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={handleClose} color="primary">
|
<Button onClick={handleClose} color="primary">
|
||||||
|
@ -7,26 +7,20 @@ import {
|
|||||||
Typography
|
Typography
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import {MACRONUTRIENTS, MEALS_LIST} from "utils/mock";
|
import {MACRONUTRIENTS, MEALS_LIST} from "utils/mock";
|
||||||
import MacronutrientsChart from "components/MacronutrientsChart";
|
import MacronutrientsChart from "components/MacronutrientsChart";
|
||||||
import Dish from "components/ProductCard";
|
import ProductCard from "components/ProductCard";
|
||||||
import AddProductToMealDialog from 'components/AddProductToMealDialog';
|
import AddProductToMealDialog from 'components/AddProductToMealDialog';
|
||||||
|
|
||||||
const MealCard = ({ label, products }) => {
|
|
||||||
const calcMealMacronutrients = (dishes) => {
|
|
||||||
const mealMacronutrients = dishes
|
|
||||||
.flatMap(({ macronutrients }) => macronutrients)
|
|
||||||
.reduce((acc, { label, value, unit }) => ({
|
|
||||||
...acc,
|
|
||||||
[label]: {
|
|
||||||
label,
|
|
||||||
unit,
|
|
||||||
value: acc[label] ? acc[label].value + value : value
|
|
||||||
}
|
|
||||||
}), {})
|
|
||||||
|
|
||||||
return Object.entries(mealMacronutrients).map(([_, macronutrients]) => macronutrients)
|
// {/*{*/}
|
||||||
}
|
// {/* calcMealMacronutrients(products).map(({ unit, value, label }, index) => (*/}
|
||||||
|
// {/* <MacronutrientsChart current={value} unit={unit} max={5000} label={label} key={index} />*/}
|
||||||
|
// {/* ))*/}
|
||||||
|
// {/*}*/}
|
||||||
|
|
||||||
|
const MealCard = ({ id, label, products }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion>
|
<Accordion>
|
||||||
@ -36,25 +30,22 @@ const MealCard = ({ label, products }) => {
|
|||||||
<Typography variant="h5">
|
<Typography variant="h5">
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<AddProductToMealDialog mealLabel={label} />
|
<AddProductToMealDialog mealId={id} mealLabel={label} />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid container alignItems="center">
|
<Grid container alignItems="center">
|
||||||
{
|
|
||||||
calcMealMacronutrients(products).map(({ unit, value, label }, index) => (
|
|
||||||
<MacronutrientsChart current={value} unit={unit} max={5000} label={label} key={index} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<List>
|
<List>
|
||||||
{MEALS_LIST.map(({ label, macronutrients }, index) => (
|
{products.map(({ _id, product, quantity, unit }) => (
|
||||||
<Dish
|
<ProductCard
|
||||||
label={label}
|
id={_id}
|
||||||
macronutrients={macronutrients}
|
product={product}
|
||||||
key={index}
|
quantity={quantity}
|
||||||
|
unit={unit}
|
||||||
|
key={_id}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
@ -64,4 +55,13 @@ const MealCard = ({ label, products }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MealCard.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
products: PropTypes.oneOfType([
|
||||||
|
PropTypes.array,
|
||||||
|
PropTypes.arrayOf(PropTypes.object),
|
||||||
|
]).isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
export default MealCard;
|
export default MealCard;
|
||||||
|
@ -4,19 +4,28 @@ import React from "react";
|
|||||||
import MacronutrientsDetails from "components/MacronutrientsDetails";
|
import MacronutrientsDetails from "components/MacronutrientsDetails";
|
||||||
import EditProductDialog from "components/EditProductDialog";
|
import EditProductDialog from "components/EditProductDialog";
|
||||||
import {Delete as DeleteIcon} from "@material-ui/icons";
|
import {Delete as DeleteIcon} from "@material-ui/icons";
|
||||||
|
import ProductLabel from 'components/ProductLabel'
|
||||||
|
|
||||||
const ProductCard = ({ label, macronutrients }) => {
|
const ProductCard = ({ id, product, quantity, unit }) => {
|
||||||
|
|
||||||
|
const { label, calories, eco, verified } = product
|
||||||
|
|
||||||
|
const calculateCalories = () => {
|
||||||
|
const isUnitPortion = unit === 'portion'
|
||||||
|
|
||||||
|
if (isUnitPortion) {
|
||||||
|
return calories * quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
return (calories / 100) * quantity
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={label}
|
primary={<ProductLabel eco={eco} verified={verified} text={label}/>}
|
||||||
disableTypography
|
disableTypography
|
||||||
secondary={
|
secondary={`calories: ${calculateCalories()}`}
|
||||||
<MacronutrientsDetails
|
|
||||||
macronutrients={macronutrients}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<EditProductDialog />
|
<EditProductDialog />
|
||||||
@ -29,11 +38,10 @@ const ProductCard = ({ label, macronutrients }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProductCard.propTypes = {
|
ProductCard.propTypes = {
|
||||||
label: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
macronutrients: PropTypes.arrayOf(PropTypes.shape({
|
product: PropTypes.object.isRequired,
|
||||||
value: PropTypes.number.isRequired,
|
quantity: PropTypes.number.isRequired,
|
||||||
unit: PropTypes.string.isRequired,
|
unit: PropTypes.string.isRequired,
|
||||||
})).isRequired,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ProductCard
|
export default ProductCard
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useFormikContext } from 'formik';
|
|
||||||
import {Typography, Grid, Box} from '@material-ui/core';
|
|
||||||
import useStyles from './styles';
|
|
||||||
|
|
||||||
|
|
||||||
const ReviewProfile = () => {
|
|
||||||
const classes = useStyles();
|
|
||||||
const { values: formValues } = useFormikContext();
|
|
||||||
console.log('TODO: ReviewProfile')
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<Grid container direction="column">
|
|
||||||
<Typography variant="h6" gutterBottom className={classes.title}>
|
|
||||||
Payment details
|
|
||||||
</Typography>
|
|
||||||
<Grid container alignContent="space-between">
|
|
||||||
{Object.entries(formValues).map(([label, value], index) => (
|
|
||||||
<Grid key={index} item xs={12}>
|
|
||||||
<Typography gutterBottom>{label}</Typography>
|
|
||||||
<Typography gutterBottom>{value}</Typography>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ReviewProfile
|
|
@ -1,15 +0,0 @@
|
|||||||
import { makeStyles } from '@material-ui/core/styles';
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
|
||||||
listItem: {
|
|
||||||
padding: theme.spacing(1, 0),
|
|
||||||
},
|
|
||||||
total: {
|
|
||||||
fontWeight: 700,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
marginTop: theme.spacing(2),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default useStyles
|
|
@ -1,13 +1,13 @@
|
|||||||
export const GET_PROFILE_REQUEST = 'app/ProfilePage/GET_PROFILE_REQUEST';
|
export const GET_PROFILE_REQUEST = 'app/CreateProfilePage/GET_PROFILE_REQUEST';
|
||||||
export const GET_PROFILE_SUCCESS = 'app/ProfilePage/GET_PROFILE_SUCCESS';
|
export const GET_PROFILE_SUCCESS = 'app/CreateProfilePage/GET_PROFILE_SUCCESS';
|
||||||
export const GET_PROFILE_ERROR = 'app/ProfilePage/GET_PROFILE_ERROR';
|
export const GET_PROFILE_ERROR = 'app/CreateProfilePage/GET_PROFILE_ERROR';
|
||||||
|
|
||||||
export const CREATE_PROFILE_REQUEST = 'app/ProfilePage/CREATE_PROFILE_REQUEST';
|
export const CREATE_PROFILE_REQUEST = 'app/CreateProfilePage/CREATE_PROFILE_REQUEST';
|
||||||
export const CREATE_PROFILE_SUCCESS = 'app/ProfilePage/CREATE_PROFILE_SUCCESS';
|
export const CREATE_PROFILE_SUCCESS = 'app/CreateProfilePage/CREATE_PROFILE_SUCCESS';
|
||||||
export const CREATE_PROFILE_ERROR = 'app/ProfilePage/CREATE_PROFILE_ERROR';
|
export const CREATE_PROFILE_ERROR = 'app/CreateProfilePage/CREATE_PROFILE_ERROR';
|
||||||
|
|
||||||
export const UPDATE_PROFILE_REQUEST = 'app/ProfilePage/UPDATE_PROFILE_REQUEST';
|
export const UPDATE_PROFILE_REQUEST = 'app/CreateProfilePage/UPDATE_PROFILE_REQUEST';
|
||||||
export const UPDATE_PROFILE_SUCCESS = 'app/ProfilePage/UPDATE_PROFILE_SUCCESS';
|
export const UPDATE_PROFILE_SUCCESS = 'app/CreateProfilePage/UPDATE_PROFILE_SUCCESS';
|
||||||
export const UPDATE_PROFILE_ERROR = 'app/ProfilePage/UPDATE_PROFILE_ERROR';
|
export const UPDATE_PROFILE_ERROR = 'app/CreateProfilePage/UPDATE_PROFILE_ERROR';
|
||||||
|
|
||||||
export const PROFILE_INPUT_CHANGE = 'app/ProfilePage/PROFILE_INPUT_CHANGE';
|
export const PROFILE_INPUT_CHANGE = 'app/CreateProfilePage/PROFILE_INPUT_CHANGE';
|
||||||
|
@ -5,7 +5,6 @@ import { useDispatch } from 'react-redux';
|
|||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import GoalForm from './Forms/GoalForm';
|
import GoalForm from './Forms/GoalForm';
|
||||||
import PersonalDetailsForm from './Forms/PersonalDetailsForm';
|
import PersonalDetailsForm from './Forms/PersonalDetailsForm';
|
||||||
import ReviewProfile from './ReviewProfile';
|
|
||||||
import reducer from "./reducer";
|
import reducer from "./reducer";
|
||||||
import saga from "./saga";
|
import saga from "./saga";
|
||||||
import { createProfileAction } from './actions'
|
import { createProfileAction } from './actions'
|
||||||
@ -14,7 +13,7 @@ import validationSchema from './FormModel/validationSchema'
|
|||||||
import formInitialValues from './FormModel/formInitialValues'
|
import formInitialValues from './FormModel/formInitialValues'
|
||||||
import profileFormModel from './FormModel/profileFormModel'
|
import profileFormModel from './FormModel/profileFormModel'
|
||||||
|
|
||||||
const steps = ['Your goal', 'Personal details', 'Review your profile'];
|
const steps = ['Your goal', 'Personal details'];
|
||||||
const { formId, formField } = profileFormModel;
|
const { formId, formField } = profileFormModel;
|
||||||
|
|
||||||
const renderStepContent = (step) => {
|
const renderStepContent = (step) => {
|
||||||
@ -23,8 +22,6 @@ const renderStepContent = (step) => {
|
|||||||
return <GoalForm formField={formField} />;
|
return <GoalForm formField={formField} />;
|
||||||
case 1:
|
case 1:
|
||||||
return <PersonalDetailsForm formField={formField} />;
|
return <PersonalDetailsForm formField={formField} />;
|
||||||
case 2:
|
|
||||||
return <ReviewProfile />;
|
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown step');
|
throw new Error('Unknown step');
|
||||||
}
|
}
|
||||||
@ -42,7 +39,6 @@ const ProfilePage = () => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const submitForm = (values, actions) => {
|
const submitForm = (values, actions) => {
|
||||||
console.log(values)
|
|
||||||
dispatch(createProfileAction(values))
|
dispatch(createProfileAction(values))
|
||||||
actions.setSubmitting(false);
|
actions.setSubmitting(false);
|
||||||
setActiveStep(activeStep + 1);
|
setActiveStep(activeStep + 1);
|
||||||
|
@ -16,9 +16,16 @@ import {
|
|||||||
SEARCH_PRODUCT_BY_BARCODE_SUCCESS,
|
SEARCH_PRODUCT_BY_BARCODE_SUCCESS,
|
||||||
SEARCH_PRODUCT_BY_BARCODE_ERROR,
|
SEARCH_PRODUCT_BY_BARCODE_ERROR,
|
||||||
SEARCH_PRODUCT_BY_LABEL_SUCCESS,
|
SEARCH_PRODUCT_BY_LABEL_SUCCESS,
|
||||||
SEARCH_PRODUCT_BY_LABEL_ERROR
|
SEARCH_PRODUCT_BY_LABEL_ERROR,
|
||||||
|
SET_SELECTED_MEAL,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
|
export const setSelectedMealAction = ({ label, id }) => ({
|
||||||
|
type: SET_SELECTED_MEAL,
|
||||||
|
label,
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
|
||||||
export const searchProductByLabelAction = ({ label }) => ({
|
export const searchProductByLabelAction = ({ label }) => ({
|
||||||
type: SEARCH_PRODUCT_BY_LABEL_REQUEST,
|
type: SEARCH_PRODUCT_BY_LABEL_REQUEST,
|
||||||
label,
|
label,
|
||||||
@ -68,11 +75,9 @@ export const updateMealAction = () => ({
|
|||||||
type: UPDATE_MEAL_REQUEST,
|
type: UPDATE_MEAL_REQUEST,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const updateMealSuccessAction = ({ label, products, date }) => ({
|
export const updateMealSuccessAction = ({ meal }) => ({
|
||||||
type: UPDATE_MEAL_SUCCESS,
|
type: UPDATE_MEAL_SUCCESS,
|
||||||
label,
|
meal
|
||||||
products,
|
|
||||||
date,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const updateMealErrorAction = ({error}) => ({
|
export const updateMealErrorAction = ({error}) => ({
|
||||||
@ -87,8 +92,9 @@ export const createMealAction = ({ label, products, date }) => ({
|
|||||||
date,
|
date,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const createMealSuccessAction = ({ label, products, date }) => ({
|
export const createMealSuccessAction = ({ id, label, products, date }) => ({
|
||||||
type: CREATE_MEAL_SUCCESS,
|
type: CREATE_MEAL_SUCCESS,
|
||||||
|
id,
|
||||||
label,
|
label,
|
||||||
products,
|
products,
|
||||||
date,
|
date,
|
||||||
@ -99,9 +105,10 @@ export const createMealErrorAction = ({error}) => ({
|
|||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const addProductsToMealAction = ({ products }) => ({
|
export const addProductsToMealAction = ({ id, products }) => ({
|
||||||
type: ADD_PRODUCTS_TO_MEAL_REQUEST,
|
type: ADD_PRODUCTS_TO_MEAL_REQUEST,
|
||||||
products,
|
products,
|
||||||
|
id,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const addProductsToSuccessAction = ({ meal }) => ({
|
export const addProductsToSuccessAction = ({ meal }) => ({
|
||||||
@ -113,4 +120,3 @@ export const addProductsToMealErrorAction = ({error}) => ({
|
|||||||
type: ADD_PRODUCTS_TO_MEAL_ERROR,
|
type: ADD_PRODUCTS_TO_MEAL_ERROR,
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -21,3 +21,5 @@ export const SEARCH_PRODUCT_BY_LABEL_ERROR = 'app/HomePage/SEARCH_PRODUCT_BY_LAB
|
|||||||
export const SEARCH_PRODUCT_BY_BARCODE_REQUEST = 'app/HomePage/SEARCH_PRODUCT_BY_BARCODE_REQUEST'
|
export const SEARCH_PRODUCT_BY_BARCODE_REQUEST = 'app/HomePage/SEARCH_PRODUCT_BY_BARCODE_REQUEST'
|
||||||
export const SEARCH_PRODUCT_BY_BARCODE_SUCCESS = 'app/HomePage/SEARCH_PRODUCT_BY_BARCODE_SUCCESS';
|
export const SEARCH_PRODUCT_BY_BARCODE_SUCCESS = 'app/HomePage/SEARCH_PRODUCT_BY_BARCODE_SUCCESS';
|
||||||
export const SEARCH_PRODUCT_BY_BARCODE_ERROR = 'app/HomePage/SEARCH_PRODUCT_BY_BARCODE_ERROR';
|
export const SEARCH_PRODUCT_BY_BARCODE_ERROR = 'app/HomePage/SEARCH_PRODUCT_BY_BARCODE_ERROR';
|
||||||
|
|
||||||
|
export const SET_SELECTED_MEAL = 'app/HomePage/SET_SELECTED_MEAL'
|
||||||
|
@ -11,8 +11,6 @@ import { getMealsAction } from './actions'
|
|||||||
import MealCard from "components/MealCard";
|
import MealCard from "components/MealCard";
|
||||||
import { createStructuredSelector } from 'reselect'
|
import { createStructuredSelector } from 'reselect'
|
||||||
|
|
||||||
const TODAY = format(new Date(), "yyyy-MM-dd")
|
|
||||||
|
|
||||||
const stateSelector = createStructuredSelector({
|
const stateSelector = createStructuredSelector({
|
||||||
meals: makeSelectMeals()
|
meals: makeSelectMeals()
|
||||||
})
|
})
|
||||||
@ -26,13 +24,14 @@ const HomePage = () => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(getMealsAction({ date: '2020-12-29' }))
|
const today = format(new Date(), "yyyy-MM-dd");
|
||||||
|
dispatch(getMealsAction({ date: today }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{meals.map(({ id, products, label }) => (
|
{meals.map(({ id, products, label }, index) => (
|
||||||
<MealCard label={label} products={products} key={id} />
|
<MealCard id={id} label={label} products={products} key={index} />
|
||||||
))}
|
))}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
@ -18,42 +18,43 @@ import {
|
|||||||
SEARCH_PRODUCT_BY_BARCODE_ERROR,
|
SEARCH_PRODUCT_BY_BARCODE_ERROR,
|
||||||
SEARCH_PRODUCT_BY_LABEL_REQUEST,
|
SEARCH_PRODUCT_BY_LABEL_REQUEST,
|
||||||
SEARCH_PRODUCT_BY_LABEL_SUCCESS,
|
SEARCH_PRODUCT_BY_LABEL_SUCCESS,
|
||||||
SEARCH_PRODUCT_BY_LABEL_ERROR
|
SEARCH_PRODUCT_BY_LABEL_ERROR,
|
||||||
|
SET_SELECTED_MEAL,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
const defaultMeals = [
|
const defaultMeals = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: null,
|
||||||
label: 'Breakfast',
|
label: 'Breakfast',
|
||||||
products: [],
|
products: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: null,
|
||||||
label: 'Snack I',
|
label: 'Snack I',
|
||||||
products: [],
|
products: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: null,
|
||||||
label: 'Lunch',
|
label: 'Lunch',
|
||||||
products: [],
|
products: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: null,
|
||||||
label: 'Snack II',
|
label: 'Snack II',
|
||||||
products: [],
|
products: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: null,
|
||||||
label: 'Dinner',
|
label: 'Dinner',
|
||||||
products: [],
|
products: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: null,
|
||||||
label: 'Snack III',
|
label: 'Snack III',
|
||||||
products: [],
|
products: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 7,
|
id: null,
|
||||||
label: 'Supper',
|
label: 'Supper',
|
||||||
products: [],
|
products: [],
|
||||||
},
|
},
|
||||||
@ -68,14 +69,35 @@ export const initialState = {
|
|||||||
barcode: '',
|
barcode: '',
|
||||||
product: {},
|
product: {},
|
||||||
date: '',
|
date: '',
|
||||||
mealId: '',
|
form: {
|
||||||
|
id: '',
|
||||||
|
label: '',
|
||||||
|
products: [],
|
||||||
|
date: null,
|
||||||
|
},
|
||||||
meals: defaultMeals
|
meals: defaultMeals
|
||||||
};
|
};
|
||||||
|
|
||||||
const homePageReducer = produce((draft, action) => {
|
const homePageReducer = produce((draft, action) => {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
|
case SET_SELECTED_MEAL:
|
||||||
|
draft.form.id = action.id;
|
||||||
|
draft.form.label = action.label.toLowerCase();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CREATE_MEAL_REQUEST:
|
||||||
|
draft.isLoading = true;
|
||||||
|
draft.form.label = action.label.toLowerCase();
|
||||||
|
draft.form.products = action.products;
|
||||||
|
draft.form.date = action.date;
|
||||||
|
break;
|
||||||
|
|
||||||
case GET_MEALS_SUCCESS:
|
case GET_MEALS_SUCCESS:
|
||||||
draft.meals = action.meals.length || defaultMeals;
|
draft.meals =
|
||||||
|
action.meals.length > 0
|
||||||
|
? action.meals
|
||||||
|
: defaultMeals;
|
||||||
|
|
||||||
draft.isLoading = false;
|
draft.isLoading = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -105,7 +127,7 @@ const homePageReducer = produce((draft, action) => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case UPDATE_MEAL_SUCCESS:
|
case UPDATE_MEAL_SUCCESS:
|
||||||
console.log(UPDATE_MEAL_SUCCESS, 'UPDATE_MEAL_SUCCESS')
|
|
||||||
draft.isLoading = false;
|
draft.isLoading = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -119,13 +141,19 @@ const homePageReducer = produce((draft, action) => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CREATE_MEAL_SUCCESS:
|
case CREATE_MEAL_SUCCESS:
|
||||||
draft.meals.push(action.meal);
|
const { id, label, products, date } = action
|
||||||
|
|
||||||
|
draft.meals.push({ id, label, products, date });
|
||||||
draft.isLoading = false;
|
draft.isLoading = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CREATE_MEAL_REQUEST:
|
|
||||||
case UPDATE_MEAL_REQUEST:
|
|
||||||
case ADD_PRODUCTS_TO_MEAL_REQUEST:
|
case ADD_PRODUCTS_TO_MEAL_REQUEST:
|
||||||
|
draft.form.id = action.id;
|
||||||
|
draft.form.products = action.products;
|
||||||
|
draft.isLoading = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UPDATE_MEAL_REQUEST:
|
||||||
draft.isLoading = true;
|
draft.isLoading = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { takeLatest, call, put, select } from 'redux-saga/effects';
|
import { takeLatest, call, put, select } from 'redux-saga/effects';
|
||||||
import {api, request, routes} from 'utils';
|
import {api, request, routes} from 'utils';
|
||||||
import { GET_MEALS_REQUEST, UPDATE_MEAL_REQUEST, SEARCH_PRODUCT_BY_BARCODE_REQUEST, SEARCH_PRODUCT_BY_LABEL_REQUEST, CREATE_MEAL_REQUEST, ADD_PRODUCTS_TO_MEAL_REQUEST } from './constants';
|
import { GET_MEALS_REQUEST, UPDATE_MEAL_REQUEST, SEARCH_PRODUCT_BY_BARCODE_REQUEST, SEARCH_PRODUCT_BY_LABEL_REQUEST, CREATE_MEAL_REQUEST, ADD_PRODUCTS_TO_MEAL_REQUEST } from './constants';
|
||||||
import { searchProductByLabelErrorAction, searchProductByLabelSuccessAction, createMealSuccessAction, createMealErrorAction, searchProductByBarcodeSuccessAction, searchProductByBarcodeErrorAction, updateMealErrorAction, updateMealSuccessAction, getMealsSuccessAction, getMealsErrorAction } from './actions';
|
import { searchProductByLabelErrorAction, addProductsToSuccessAction, addProductsToMealErrorAction, searchProductByLabelSuccessAction, createMealSuccessAction, createMealErrorAction, searchProductByBarcodeSuccessAction, searchProductByBarcodeErrorAction, updateMealErrorAction, updateMealSuccessAction, getMealsSuccessAction, getMealsErrorAction } from './actions';
|
||||||
import { makeSelectLabel, makeSelectMealId, makeSelectBarcode, makeSelectProducts, makeSelectDate, } from './selectors'
|
import { makeSelectLabel, makeSelectFormProducts, makeSelectMealLabel, makeSelectMealId, makeSelectBarcode, makeSelectDate, } from './selectors'
|
||||||
import { makeSelectTokens } from 'containers/App/selectors'
|
import { makeSelectTokens } from 'containers/App/selectors'
|
||||||
import {push} from "connected-react-router";
|
import {push} from "connected-react-router";
|
||||||
|
|
||||||
@ -29,8 +29,8 @@ export function* getMeals() {
|
|||||||
|
|
||||||
export function* updateMeal() {
|
export function* updateMeal() {
|
||||||
const { access } = yield select(makeSelectTokens());
|
const { access } = yield select(makeSelectTokens());
|
||||||
const label = yield select(makeSelectLabel());
|
const products = yield select(makeSelectFormProducts());
|
||||||
const products = yield select(makeSelectProducts());
|
const label = yield select(makeSelectMealLabel());
|
||||||
const date = yield select(makeSelectDate());
|
const date = yield select(makeSelectDate());
|
||||||
const mealId = yield select(makeSelectMealId());
|
const mealId = yield select(makeSelectMealId());
|
||||||
|
|
||||||
@ -43,8 +43,9 @@ export function* updateMeal() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters);
|
const meal = yield call(request, requestURL, requestParameters);
|
||||||
yield put(updateMealSuccessAction({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}));
|
console.log('pages/home/saga/UPDATE_MEAL', meal)
|
||||||
|
// yield put(updateMealSuccessAction({ meal }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(updateMealErrorAction({error: error.message}));
|
yield put(updateMealErrorAction({error: error.message}));
|
||||||
}
|
}
|
||||||
@ -52,12 +53,14 @@ export function* updateMeal() {
|
|||||||
|
|
||||||
export function* createMeal() {
|
export function* createMeal() {
|
||||||
const { access } = yield select(makeSelectTokens());
|
const { access } = yield select(makeSelectTokens());
|
||||||
const label = yield select(makeSelectLabel());
|
const label = yield select(makeSelectMealLabel());
|
||||||
const products = yield select(makeSelectProducts());
|
const products = yield select(makeSelectFormProducts());
|
||||||
const date = yield select(makeSelectDate());
|
const date = yield select(makeSelectDate());
|
||||||
|
|
||||||
const requestURL = api.meals;
|
const requestURL = api.meals;
|
||||||
|
|
||||||
|
console.log(products)
|
||||||
|
|
||||||
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}`, },
|
||||||
@ -65,8 +68,8 @@ export function* createMeal() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { id, label, products, date } = yield call(request, requestURL, requestParameters);
|
const meal = yield call(request, requestURL, requestParameters);
|
||||||
yield put(createMealSuccessAction({ id, label, products, date }));
|
yield put(createMealSuccessAction(meal));
|
||||||
yield put(push(routes.dashboard.path));
|
yield put(push(routes.dashboard.path));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(createMealErrorAction({error: error.message}));
|
yield put(createMealErrorAction({error: error.message}));
|
||||||
@ -75,23 +78,23 @@ export function* createMeal() {
|
|||||||
|
|
||||||
export function* addProductsToMeal() {
|
export function* addProductsToMeal() {
|
||||||
const { access } = yield select(makeSelectTokens());
|
const { access } = yield select(makeSelectTokens());
|
||||||
const products = yield select(makeSelectProducts());
|
const products = yield select(makeSelectFormProducts());
|
||||||
const mealId = yield select(makeSelectMealId());
|
const mealId = yield select(makeSelectMealId());
|
||||||
|
|
||||||
const requestURL = `${api.meals}/${mealId}`;
|
const requestURL = `${api.meals}/${mealId}`;
|
||||||
|
|
||||||
const requestParameters = {
|
const requestParameters = {
|
||||||
method: 'POST',
|
method: 'PATCH',
|
||||||
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({ products }),
|
body: JSON.stringify({ products }),
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { meal } = yield call(request, requestURL, requestParameters);
|
const meal = yield call(request, requestURL, requestParameters);
|
||||||
yield put(createMealSuccessAction({ meal }));
|
yield put(addProductsToSuccessAction({ meal }));
|
||||||
yield put(push(routes.dashboard.path));
|
yield put(push(routes.dashboard.path));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put(createMealErrorAction({error: error.message}));
|
yield put(addProductsToMealErrorAction({error: error.message}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,6 @@ const makeSelectIsLoading = () =>
|
|||||||
const makeSelectMeals = () =>
|
const makeSelectMeals = () =>
|
||||||
createSelector(selectHomePageDomain, (substate) => substate.meals);
|
createSelector(selectHomePageDomain, (substate) => substate.meals);
|
||||||
|
|
||||||
const makeSelectMealId = () =>
|
|
||||||
createSelector(selectHomePageDomain, (substate) => substate.mealId);
|
|
||||||
|
|
||||||
const makeSelectDate = () =>
|
const makeSelectDate = () =>
|
||||||
createSelector(selectHomePageDomain, (substate) => substate.date);
|
createSelector(selectHomePageDomain, (substate) => substate.date);
|
||||||
|
|
||||||
@ -30,8 +27,19 @@ const makeSelectProduct = () =>
|
|||||||
const makeSelectBarcode = () =>
|
const makeSelectBarcode = () =>
|
||||||
createSelector(selectHomePageDomain, (substate) => substate.barocde);
|
createSelector(selectHomePageDomain, (substate) => substate.barocde);
|
||||||
|
|
||||||
|
const makeSelectMealLabel = () =>
|
||||||
|
createSelector(selectHomePageDomain, (substate) => substate.form.label);
|
||||||
|
|
||||||
|
const makeSelectMealId = () =>
|
||||||
|
createSelector(selectHomePageDomain, (substate) => substate.form.id);
|
||||||
|
|
||||||
|
const makeSelectFormProducts = () =>
|
||||||
|
createSelector(selectHomePageDomain, (substate) => substate.form.products);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
selectHomePageDomain,
|
selectHomePageDomain,
|
||||||
|
makeSelectFormProducts,
|
||||||
|
makeSelectMealLabel,
|
||||||
makeSelectMealId,
|
makeSelectMealId,
|
||||||
makeSelectDate,
|
makeSelectDate,
|
||||||
makeSelectLabel,
|
makeSelectLabel,
|
||||||
|
@ -18,7 +18,6 @@ import useStyles from './styles'
|
|||||||
import validationSchema from './FormModel/validationSchema'
|
import validationSchema from './FormModel/validationSchema'
|
||||||
import formInitialValues from './FormModel/formInitialValues'
|
import formInitialValues from './FormModel/formInitialValues'
|
||||||
import loginFormModel from './FormModel/loginFormModel'
|
import loginFormModel from './FormModel/loginFormModel'
|
||||||
import reducer from "./reducer";
|
|
||||||
import saga from "./saga";
|
import saga from "./saga";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
29
src/pages/Profile/actions.js
Normal file
29
src/pages/Profile/actions.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import {
|
||||||
|
GET_PROFILE_REQUEST,
|
||||||
|
GET_PROFILE_SUCCESS,
|
||||||
|
GET_PROFILE_ERROR,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
export const getProfileAction = () => ({
|
||||||
|
type: GET_PROFILE_REQUEST,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getProfileSuccessAction = ({ weeksToGoal, currentWeight, goal, dailyCalories, birthday, gender, height, weight, goalWeight, rateOfChange, activity}) => ({
|
||||||
|
type: GET_PROFILE_SUCCESS,
|
||||||
|
birthday,
|
||||||
|
weeksToGoal,
|
||||||
|
dailyCalories,
|
||||||
|
gender,
|
||||||
|
height,
|
||||||
|
weight,
|
||||||
|
goal,
|
||||||
|
currentWeight,
|
||||||
|
goalWeight,
|
||||||
|
rateOfChange,
|
||||||
|
activity
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getProfileErrorAction = ({error}) => ({
|
||||||
|
type: GET_PROFILE_ERROR,
|
||||||
|
error,
|
||||||
|
})
|
3
src/pages/Profile/constants.js
Normal file
3
src/pages/Profile/constants.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const GET_PROFILE_REQUEST = 'app/ProfilePage/GET_PROFILE_REQUEST';
|
||||||
|
export const GET_PROFILE_SUCCESS = 'app/ProfilePage/GET_PROFILE_SUCCESS';
|
||||||
|
export const GET_PROFILE_ERROR = 'app/ProfilePage/GET_PROFILE_ERROR';
|
@ -1,10 +1,59 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import {useInjectSaga, useInjectReducer} from "redux-injectors";
|
||||||
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
import { Grid, List, ListItem, ListItemText } from '@material-ui/core'
|
||||||
|
import { startCase } from 'lodash'
|
||||||
|
import saga from "./saga";
|
||||||
|
import reducer from "./reducer";
|
||||||
|
import {getProfileAction} from './actions'
|
||||||
|
import { createStructuredSelector } from 'reselect'
|
||||||
|
import {
|
||||||
|
makeSelectBirthday,
|
||||||
|
makeSelectHeight,
|
||||||
|
makeSelectCurrentWeight,
|
||||||
|
makeSelectGoalWeight,
|
||||||
|
makeSelectRateOfChange,
|
||||||
|
makeSelectActivity,
|
||||||
|
makeSelectGoal,
|
||||||
|
makeSelectGender,
|
||||||
|
makeSelectDailyCalories,
|
||||||
|
makeSelectWeeksToGoal,
|
||||||
|
} from './selectors'
|
||||||
|
|
||||||
|
const stateSelector = createStructuredSelector({
|
||||||
|
birthday: makeSelectBirthday(),
|
||||||
|
height: makeSelectHeight(),
|
||||||
|
currentWeight: makeSelectCurrentWeight(),
|
||||||
|
goalWeight: makeSelectGoalWeight(),
|
||||||
|
rateOfChange: makeSelectRateOfChange(),
|
||||||
|
activity: makeSelectActivity(),
|
||||||
|
goal: makeSelectGoal(),
|
||||||
|
gender: makeSelectGender(),
|
||||||
|
dailyCalories: makeSelectDailyCalories(),
|
||||||
|
weeksToGoal: makeSelectWeeksToGoal(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const key = 'profilePage'
|
||||||
const ProfilePage = () => {
|
const ProfilePage = () => {
|
||||||
|
useInjectSaga({ key, saga });
|
||||||
|
useInjectReducer({ key, reducer })
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const userProfileDetails = useSelector(stateSelector)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(getProfileAction())
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Grid container>
|
||||||
ProfilePage
|
<List>
|
||||||
</div>
|
{Object.keys(userProfileDetails).map((key, index) => (
|
||||||
|
<ListItem key={index}>
|
||||||
|
<ListItemText primary={`${startCase(key)}: ${userProfileDetails[key]}`} />
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
51
src/pages/Profile/reducer.js
Normal file
51
src/pages/Profile/reducer.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import produce from 'immer';
|
||||||
|
import {
|
||||||
|
GET_PROFILE_REQUEST,
|
||||||
|
GET_PROFILE_SUCCESS,
|
||||||
|
GET_PROFILE_ERROR,
|
||||||
|
} from './constants';
|
||||||
|
import {format} from "date-fns";
|
||||||
|
|
||||||
|
export const initialState = {
|
||||||
|
isLoading: false,
|
||||||
|
error: {},
|
||||||
|
gender: '',
|
||||||
|
goal: 0,
|
||||||
|
birthday: '',
|
||||||
|
height: 0,
|
||||||
|
currentWeight: 0,
|
||||||
|
goalWeight: 0,
|
||||||
|
rateOfChange: 0,
|
||||||
|
activity: 0,
|
||||||
|
dailyCalories: 0,
|
||||||
|
weeksToGoal: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginPageReducer = produce((draft, action) => {
|
||||||
|
switch(action.type) {
|
||||||
|
case GET_PROFILE_SUCCESS:
|
||||||
|
draft.birthday = action.birthday;
|
||||||
|
draft.gender = action.gender;
|
||||||
|
draft.goal = action.goal;
|
||||||
|
draft.height = action.height;
|
||||||
|
draft.currentWeight = action.currentWeight;
|
||||||
|
draft.goalWeight = action.goalWeight;
|
||||||
|
draft.rateOfChange = action.rateOfChange;
|
||||||
|
draft.activity = action.activity;
|
||||||
|
draft.weeksToGoal = action.weeksToGoal;
|
||||||
|
draft.dailyCalories = action.dailyCalories;
|
||||||
|
draft.isLoading = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GET_PROFILE_REQUEST:
|
||||||
|
draft.isLoading = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GET_PROFILE_ERROR:
|
||||||
|
draft.isLoading = false;
|
||||||
|
draft.error = action.error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, initialState);
|
||||||
|
|
||||||
|
export default loginPageReducer;
|
40
src/pages/Profile/saga.js
Normal file
40
src/pages/Profile/saga.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { takeLatest, call, put, select } from 'redux-saga/effects';
|
||||||
|
import {api, request} from 'utils';
|
||||||
|
import { GET_PROFILE_REQUEST } from './constants';
|
||||||
|
import { getProfileErrorAction, getProfileSuccessAction } from './actions';
|
||||||
|
import { makeSelectTokens } from 'containers/App/selectors'
|
||||||
|
|
||||||
|
export function* getProfile() {
|
||||||
|
const { access } = yield select(makeSelectTokens());
|
||||||
|
|
||||||
|
const requestURL = api.profile;
|
||||||
|
|
||||||
|
const requestParameters = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${access.token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
birthday,
|
||||||
|
gender,
|
||||||
|
height,
|
||||||
|
currentWeight,
|
||||||
|
goalWeight,
|
||||||
|
rateOfChange,
|
||||||
|
goal,
|
||||||
|
activity,
|
||||||
|
dailyCalories,
|
||||||
|
weeksToGoal,
|
||||||
|
} = yield call(request, requestURL, requestParameters);
|
||||||
|
yield put(getProfileSuccessAction({ weeksToGoal, goal, dailyCalories, birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(getProfileErrorAction({ error: error.message }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function* profilePageSaga() {
|
||||||
|
yield takeLatest(GET_PROFILE_REQUEST, getProfile);
|
||||||
|
}
|
57
src/pages/Profile/selectors.js
Normal file
57
src/pages/Profile/selectors.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { initialState } from './reducer';
|
||||||
|
|
||||||
|
const selectProfilePageDomain = (state) => state.profilePage || initialState;
|
||||||
|
|
||||||
|
const makeSelectError = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.error);
|
||||||
|
|
||||||
|
const makeSelectBirthday = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.birthday);
|
||||||
|
|
||||||
|
const makeSelectHeight = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.height)
|
||||||
|
|
||||||
|
const makeSelectCurrentWeight = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.currentWeight);
|
||||||
|
|
||||||
|
const makeSelectGoalWeight = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.goalWeight);
|
||||||
|
|
||||||
|
const makeSelectRateOfChange = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.rateOfChange);
|
||||||
|
|
||||||
|
const makeSelectActivity = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.activity);
|
||||||
|
|
||||||
|
const makeSelectGoal = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.goal);
|
||||||
|
|
||||||
|
const makeSelectGender = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.gender);
|
||||||
|
|
||||||
|
const makeSelectIsLoading = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.isLoading);
|
||||||
|
|
||||||
|
const makeSelectDailyCalories = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.dailyCalories);
|
||||||
|
|
||||||
|
const makeSelectWeeksToGoal = () =>
|
||||||
|
createSelector(selectProfilePageDomain, (substate) => substate.weeksToGoal);
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
selectProfilePageDomain,
|
||||||
|
makeSelectError,
|
||||||
|
makeSelectBirthday,
|
||||||
|
makeSelectHeight,
|
||||||
|
makeSelectCurrentWeight,
|
||||||
|
makeSelectGoalWeight,
|
||||||
|
makeSelectRateOfChange,
|
||||||
|
makeSelectActivity,
|
||||||
|
makeSelectGoal,
|
||||||
|
makeSelectGender,
|
||||||
|
makeSelectWeeksToGoal,
|
||||||
|
makeSelectDailyCalories,
|
||||||
|
makeSelectIsLoading,
|
||||||
|
};
|
@ -5,10 +5,12 @@ import {useDispatch} from "react-redux";
|
|||||||
import InputField from "components/InputField";
|
import InputField from "components/InputField";
|
||||||
import useStyles from './styles'
|
import useStyles from './styles'
|
||||||
import {Form, Formik} from "formik";
|
import {Form, Formik} from "formik";
|
||||||
|
import {registerAction} from "./actions";
|
||||||
import formInitialValues from "./FormModel/formInitialValues";
|
import formInitialValues from "./FormModel/formInitialValues";
|
||||||
import validationSchema from "./FormModel/validationSchema";
|
import validationSchema from "./FormModel/validationSchema";
|
||||||
import {registerAction} from "./actions";
|
|
||||||
import registerFormModel from './FormModel/registerFormModel'
|
import registerFormModel from './FormModel/registerFormModel'
|
||||||
|
import {useInjectSaga} from "redux-injectors";
|
||||||
|
import saga from './saga'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
formId,
|
formId,
|
||||||
@ -18,11 +20,13 @@ const {
|
|||||||
}
|
}
|
||||||
} = registerFormModel;
|
} = registerFormModel;
|
||||||
|
|
||||||
|
const key = 'registerPage'
|
||||||
const RegisterPage = () => {
|
const RegisterPage = () => {
|
||||||
|
useInjectSaga({ key, saga });
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const handleSubmit = (values, actions) => {
|
const onSubmit = (values, actions) => {
|
||||||
dispatch(registerAction(values))
|
dispatch(registerAction(values))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,12 +34,12 @@ const RegisterPage = () => {
|
|||||||
<Container component="main" maxWidth="xs">
|
<Container component="main" maxWidth="xs">
|
||||||
<div className={classes.paper}>
|
<div className={classes.paper}>
|
||||||
<Typography component="h1" variant="h5">
|
<Typography component="h1" variant="h5">
|
||||||
Login to Account
|
Create account
|
||||||
</Typography>
|
</Typography>
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={formInitialValues}
|
initialValues={formInitialValues}
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={onSubmit}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{({ isSubmitting }) => (
|
||||||
<Form id={formId}>
|
<Form id={formId}>
|
||||||
@ -55,10 +59,6 @@ const RegisterPage = () => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
className={classes.input}
|
className={classes.input}
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
|
||||||
control={<Checkbox value="remember" color="primary" />}
|
|
||||||
label="Remember me"
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
fullWidth
|
fullWidth
|
||||||
|
Loading…
Reference in New Issue
Block a user