Add redux saga for home apge

This commit is contained in:
= 2020-12-29 16:39:17 +01:00
parent 64ab7aef58
commit 8160437379
19 changed files with 532 additions and 315 deletions

View File

@ -1,91 +0,0 @@
import React from 'react'
import {
Grid,
IconButton,
Typography,
TextField,
List,
Checkbox,
Dialog,
DialogContent,
ListSubheader,
ListItemSecondaryAction,
DialogTitle
} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import {Close, CropFree} from '@material-ui/icons';
import Meal from "components/Dish";
import { MEALS_LIST } from 'utils/mock'
const useStyles = makeStyles((theme) => ({
closeButton: {
position: 'absolute',
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500],
},
searchInput: {
marginRight: theme.spacing(3),
}
}))
const AddMealModal = ({ isModalOpen, handleCloseModal }) => {
const styles = useStyles()
return (
<Dialog
open={isModalOpen}
onClose={handleCloseModal}
className={styles.root}
fullWidth
maxWidth="md"
>
<DialogTitle className={styles.header}>
<Typography variant="h6">Add product</Typography>
<IconButton className={styles.closeButton} onClick={handleCloseModal}>
<Close />
</IconButton>
</DialogTitle>
<DialogContent>
<Grid>
<List
dense
subheader={
<Grid container alignItems="center" justify="space-between" wrap="nowrap">
<TextField
label="Product name"
name="product name"
variant="outlined"
size="small"
fullWidth
className={styles.searchInput}
/>
<IconButton size="medium" color="primary">
<CropFree />
</IconButton>
</Grid>
}
>
{MEALS_LIST.map(({ name, macronutrients }, index) => (
<Meal
name={name}
macronutrients={macronutrients}
key={index}
action={
<ListItemSecondaryAction>
<Checkbox
edge="end"
/>
</ListItemSecondaryAction>
}
/>
))}
</List>
</Grid>
</DialogContent>
</Dialog>
);
};
export default AddMealModal;

View File

@ -1,61 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import {Grid, Paper, Typography} from "@material-ui/core";
import {makeStyles} from "@material-ui/core/styles";
import grey from '@material-ui/core/colors/grey';
import MacronutrientsChart from 'components/MacronutrientsChart'
const useStyles = makeStyles((theme) => ({
root: {
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
},
subtitle: {
color: grey[400]
}
}))
const DailyStats = ({ caloriesLeft, macronutrients }) => {
const classes = useStyles()
return (
<Paper className={classes.root}>
<Grid
container
direction="column"
justify="center"
alignItems="center"
>
<Grid item>
<Typography variant="subtitle1" align="center" className={classes.subtitle}>
Calories left
</Typography>
<Typography variant="h3">
{caloriesLeft} kcal
</Typography>
</Grid>
<Grid
container
justify="space-between"
alignItems="center"
>
{macronutrients.map(({ current, max, label, color }, index) => (
<MacronutrientsChart current={current} max={max} label={label} color={color} key={index} />
))}
</Grid>
</Grid>
</Paper>
)
}
DailyStats.propTypes = {
caloriesLeft: PropTypes.number.isRequired,
macronutrients: PropTypes.arrayOf(PropTypes.shape({
current: PropTypes.number.isRequired,
max: PropTypes.number.isRequired,
label: PropTypes.string.isRequired,
color: PropTypes.string.isRequired,
})).isRequired
}
export default DailyStats

View File

@ -0,0 +1,71 @@
import React, { useState } from 'react';
import {Dialog, DialogTitle, Tooltip, DialogContent, Grid, Typography, TextField, DialogContentText, DialogActions, IconButton, Button} from "@material-ui/core";
import InputField from "components/InputField";
import {Edit as EditIcon, VerifiedUserTwoTone as VerifiedUserIcon, EcoTwoTone as EcoIcon} from "@material-ui/icons";
const ProductLabel = ({ label, eco, verified }) => {
return (
<DialogTitle>
<Grid container alignItems="center">
{label}
{verified && (
<Tooltip title="Verified product">
<VerifiedUserIcon fontSize="small" color="primary" />
</Tooltip>
)}
{eco && (
<Tooltip title="Eco product">
<EcoIcon color="primary" />
</Tooltip>
)}
</Grid>
</DialogTitle>
)
}
const EditProductDialog = () => {
const [isDialogOpen, setIsDialogOpen] = useState(false)
const handleOpenDialog = () => {
setIsDialogOpen(true)
}
const handleCloseDialog = () => {
setIsDialogOpen(false)
}
return (
<React.Fragment>
<IconButton variant="outlined" color="primary" onClick={handleOpenDialog}>
<EditIcon />
</IconButton>
<Dialog open={isDialogOpen} onClose={handleCloseDialog}>
<ProductLabel label={"eggs"} eco={true} verified={true}/>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseDialog} color="secondary">
Discard
</Button>
<Button onClick={handleCloseDialog} variant="contained" color="primary">
Save
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
export default EditProductDialog;

View File

@ -11,9 +11,9 @@ import {Add as AddIcon} from "@material-ui/icons";
import React from "react"; import React from "react";
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/Dish"; import Dish from "components/ProductCard";
const Meal = ({ label }) => { const MealCard = ({ label }) => {
const calcMealMacronutrients = (dishes) => { const calcMealMacronutrients = (dishes) => {
const mealMacronutrients = dishes const mealMacronutrients = dishes
@ -73,4 +73,4 @@ const Meal = ({ label }) => {
); );
}; };
export default Meal; export default MealCard;

View File

@ -2,9 +2,11 @@ import {ListItem, ListItemText, ListItemSecondaryAction, IconButton} from "@mate
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";
import {Delete as DeleteIcon, Edit as EditIcon} from "@material-ui/icons"; import EditProductDialog from "components/EditProductDialog";
import {Delete as DeleteIcon} from "@material-ui/icons";
const ProductCard = ({ label, macronutrients }) => {
const Dish = ({ label, macronutrients }) => {
return ( return (
<ListItem> <ListItem>
<ListItemText <ListItemText
@ -16,10 +18,8 @@ const Dish = ({ label, macronutrients }) => {
} }
/> />
<ListItemSecondaryAction> <ListItemSecondaryAction>
<IconButton onClick={() => console.log('elo')}> <EditProductDialog />
<EditIcon /> <IconButton onClick={() => alert('remove')}>
</IconButton>
<IconButton onClick={() => console.log('elo')}>
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
</ListItemSecondaryAction> </ListItemSecondaryAction>
@ -27,7 +27,7 @@ const Dish = ({ label, macronutrients }) => {
) )
} }
Dish.propTypes = { ProductCard.propTypes = {
label: PropTypes.string.isRequired, label: PropTypes.string.isRequired,
macronutrients: PropTypes.arrayOf(PropTypes.shape({ macronutrients: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.number.isRequired, value: PropTypes.number.isRequired,
@ -35,4 +35,4 @@ Dish.propTypes = {
})).isRequired, })).isRequired,
} }
export default Dish export default ProductCard

View File

@ -1,68 +0,0 @@
import React, {useState} from "react";
import PropTypes from "prop-types";
import { Box, Tab, Tabs } from "@material-ui/core";
import {makeStyles} from "@material-ui/core/styles";
import TabLabel from 'components/TabLabel';
import TabPanel from 'components/TabPanel';
// import MenuList from 'components/MenuList'
import { MENU_LIST } from 'utils/mock'
const a11yProps = (index) => {
return {
id: `scrollable-force-tab-${index}`,
'aria-controls': `scrollable-force-tabpanel-${index}`,
};
}
const useStyles = makeStyles((theme) => ({
tab: {
width: `100%`
}
}))
const ScrollableTabs = ({ tabs }) => {
const classes = useStyles()
const [value, setValue] = useState(0);
const handleChangeDay = (event, newDay) => {
setValue(newDay);
};
return (
<Box>
<Tabs
value={value}
onChange={handleChangeDay}
variant="scrollable"
indicatorColor="primary"
scrollButtons="on"
>
{tabs.map((label, index) => (
<Tab
key={index}
className={classes.tab}
label={<TabLabel label={label} />}
{...a11yProps(index)}
/>
))}
</Tabs>
<Box>
{tabs.map((_, index) => (
<TabPanel value={value} index={index} key={index}>
{/*<MenuList list={MENU_LIST} />*/}
hello from Scrollabel tabs
</TabPanel>
))}
</Box>
</Box>
);
}
ScrollableTabs.propTypes = {
tabs: PropTypes.arrayOf(PropTypes.string).isRequired,
}
export default ScrollableTabs

View File

@ -1,24 +0,0 @@
import React from 'react';
import { at } from 'lodash';
import { useField } from 'formik';
import {
Slider,
FormControl,
FormControlLabel,
FormHelperText
} from '@material-ui/core';
const SliderField = ({ label, ...rest }) => {
const [field, meta, helper] = useField({ label, ...rest });
const { setValue } = helper;
const _onChange = (e) => {
setValue(e.target.checked);
}
return (
<Slider onChange={_onChange} />
);
}
export default SliderField

View File

@ -1,23 +0,0 @@
import React from 'react';
import { Box, Typography } from "@material-ui/core";
import PropTypes from "prop-types";
const TabLabel = ({ label }) => {
console.log('TabLabel - todo')
return (
<Box display="flex" flexDirection="column">
<Typography component="span" variant="caption">
30 lis
</Typography>
<Typography component="span" variant="caption">
poniedziałek
</Typography>
</Box>
)
}
TabLabel.propTypes = {
label: PropTypes.string.isRequired,
}
export default TabLabel;

View File

@ -1,29 +0,0 @@
import {Box} from "@material-ui/core";
import React from "react";
import PropTypes from "prop-types";
const TabPanel = ({ children, value, index, ...other }) => {
return (
<Box
role="tabpanel"
hidden={value !== index}
id={`scrollable-force-tabpanel-${index}`}
aria-labelledby={`scrollable-force-tab-${index}`}
{...other}
>
{value === index && (
<Box p={3}>
{children}
</Box>
)}
</Box>
);
}
TabPanel.propTypes = {
children: PropTypes.node.isRequired,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired,
};
export default TabPanel

View File

@ -0,0 +1,72 @@
import React from 'react';
import {Paper, InputBase, FormLabel, ListItemSecondaryAction, ListItem, ListItemText, List, FormControl, FormGroup, FormControlLabel, Divider, Checkbox, Grid, Typography, FilledInput , DialogContentText, DialogActions, IconButton, Button} from "@material-ui/core";
import { CropFree as CropFreeIcon, Search as SearchIcon } from '@material-ui/icons'
import EditProductDialog from 'components/EditProductDialog'
import useStyles from './styles'
function generate(element) {
return [0, 1, 2].map((value) =>
React.cloneElement(element, {
key: value,
}),
);
}
const AddProductPage = () => {
const classes = useStyles()
return (
<Paper>
<Grid container xs={12} className={classes.addDishContainer}>
<Grid item xs={3}>
<FormControl>
<FormLabel>Filters</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox name="gilad" />}
label="Verified"
/>
<FormControlLabel
control={<Checkbox name="jason" />}
label="Eco"
/>
</FormGroup>
</FormControl>
</Grid>
<Grid item xs={9}>
<Grid item xs={12}>
<Paper component="form" className={classes.root}>
<IconButton>
<SearchIcon />
</IconButton>
<InputBase
className={classes.input}
placeholder="Search meals"
/>
<Divider className={classes.divider} orientation="vertical" />
<IconButton color="primary" className={classes.iconButton} aria-label="directions">
<CropFreeIcon />
</IconButton>
</Paper>
<List>
{generate(
<ListItem button onClick={alert('elo')}>
<ListItemText
primary="eggs"
/>
<ListItemSecondaryAction>
<Checkbox
edge="end"
/>
</ListItemSecondaryAction>
</ListItem>
)}
</List>
</Grid>
</Grid>
</Grid>
</Paper>
);
};
export default AddProductPage;

View File

@ -0,0 +1,27 @@
import {makeStyles} from "@material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
addDishContainer: {
padding: theme.spacing(2),
minWidth: 750,
},
root: {
padding: '2px 4px',
display: 'flex',
alignItems: 'center',
minWidth: 400,
},
input: {
marginLeft: theme.spacing(1),
flex: 1,
},
iconButton: {
padding: 10,
},
divider: {
height: 28,
margin: 4,
},
}))
export default useStyles

79
src/pages/Home/actions.js Normal file
View File

@ -0,0 +1,79 @@
import {
CREATE_MEAL_REQUEST,
CREATE_MEAL_SUCCESS,
CREATE_MEAL_ERROR,
GET_MEALS_REQUEST,
GET_MEALS_SUCCESS,
GET_MEALS_ERROR,
UPDATE_MEAL_REQUEST,
UPDATE_MEAL_SUCCESS,
UPDATE_MEAL_ERROR,
ADD_PRODUCTS_TO_MEAL_REQUEST,
ADD_PRODUCTS_TO_MEAL_SUCCESS,
ADD_PRODUCTS_TO_MEAL_ERROR,
} from './constants';
export const getMealsAction = () => ({
type: GET_MEALS_REQUEST,
})
export const getMealsSuccessAction = ({ meals }) => ({
type: GET_MEALS_SUCCESS,
meals
})
export const getMealsErrorAction = ({ error }) => ({
type: GET_MEALS_ERROR,
error,
})
export const updateMealAction = () => ({
type: UPDATE_MEAL_REQUEST,
})
export const updateMealSuccessAction = ({ label, products, date }) => ({
type: UPDATE_MEAL_SUCCESS,
label,
products,
date,
})
export const updateMealErrorAction = ({error}) => ({
type: UPDATE_MEAL_ERROR,
error,
})
export const createMealAction = ({ label, products, date }) => ({
type: CREATE_MEAL_REQUEST,
label,
products,
date,
})
export const createMealSuccessAction = ({ label, products, date }) => ({
type: CREATE_MEAL_SUCCESS,
label,
products,
date,
})
export const createMealErrorAction = ({error}) => ({
type: CREATE_MEAL_ERROR,
error,
})
export const addProductsToMealAction = ({ products }) => ({
type: ADD_PRODUCTS_TO_MEAL_REQUEST,
products,
})
export const addProductsToSuccessAction = ({ meal }) => ({
type: ADD_PRODUCTS_TO_MEAL_SUCCESS,
meal
})
export const addProductsToMealErrorAction = ({error}) => ({
type: ADD_PRODUCTS_TO_MEAL_ERROR,
error,
})

View File

@ -0,0 +1,15 @@
export const GET_MEALS_REQUEST = 'app/HomePage/GET_MEALS_REQUEST'
export const GET_MEALS_SUCCESS = 'app/HomePage/GET_MEALS_SUCCESS';
export const GET_MEALS_ERROR = 'app/LoginPage/GET_MEALS_ERROR';
export const CREATE_MEAL_REQUEST = 'app/HomePage/CREATE_MEAL_REQUEST'
export const CREATE_MEAL_SUCCESS = 'app/HomePage/CREATE_MEAL_SUCCESS';
export const CREATE_MEAL_ERROR = 'app/LoginPage/CREATE_MEAL_ERROR';
export const UPDATE_MEAL_REQUEST = 'app/HomePage/UPDATE_MEAL_REQUEST'
export const UPDATE_MEAL_SUCCESS = 'app/HomePage/UPDATE_MEAL_SUCCESS';
export const UPDATE_MEAL_ERROR = 'app/LoginPage/UPDATE_MEAL_ERROR';
export const ADD_PRODUCTS_TO_MEAL_REQUEST = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_REQUEST'
export const ADD_PRODUCTS_TO_MEAL_SUCCESS = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_SUCCESS';
export const ADD_PRODUCTS_TO_MEAL_ERROR = 'app/LoginPage/ADD_PRODUCTS_TO_MEAL_ERROR';

View File

@ -1,23 +1,22 @@
import React from 'react'; import React from 'react';
import { Grid, IconButton, Accordion, Container, List, AccordionSummary, Typography, AccordionDetails } from '@material-ui/core'; import { Grid, IconButton, Accordion, Container, List, AccordionSummary, Typography, AccordionDetails } from '@material-ui/core';
import {Add as AddIcon} from '@material-ui/icons'; import {Add as AddIcon} from '@material-ui/icons';
import ScrollableTabs from 'components/ScrollableTabs';
import MacronutrientsChart from 'components/MacronutrientsChart' import MacronutrientsChart from 'components/MacronutrientsChart'
import { TABS, MEALS_LIST, MACRONUTRIENTS } from 'utils/mock' import { TABS, MEALS_LIST, MACRONUTRIENTS } from 'utils/mock'
import Meal from "components/Meal"; import Meal from "components/MealCard";
const HomePage = () => { const HomePage = () => {
return ( return (
<Container > <Container >
<Meal label="Breakfast" /> <Meal label="Breakfast" />
{/*<Meal label="Snack I" />*/} <Meal label="Snack I" />
{/*<Meal label="Lunch" />*/} <Meal label="Lunch" />
{/*<Meal label="Snack II" />*/} <Meal label="Snack II" />
{/*<Meal label="Dinner" />*/} <Meal label="Dinner" />
{/*<Meal label="Snack III" />*/} <Meal label="Snack III" />
{/*<Meal label="Supper" />*/} <Meal label="Supper" />
</Container> </Container>
); );
}; };

100
src/pages/Home/reducer.js Normal file
View File

@ -0,0 +1,100 @@
import { format } from 'date-fns'
import produce from 'immer';
import {
CREATE_MEAL_REQUEST,
CREATE_MEAL_SUCCESS,
CREATE_MEAL_ERROR,
GET_MEALS_REQUEST,
GET_MEALS_SUCCESS,
GET_MEALS_ERROR,
UPDATE_MEAL_REQUEST,
UPDATE_MEAL_SUCCESS,
UPDATE_MEAL_ERROR,
ADD_PRODUCTS_TO_MEAL_REQUEST,
ADD_PRODUCTS_TO_MEAL_SUCCESS,
ADD_PRODUCTS_TO_MEAL_ERROR,
} from './constants';
export const initialState = {
isLoading: false,
error: {},
label: '',
products: [],
date: format(new Date(), "yyyy-MM-dd"),
mealId: '',
meals: [
{
label: 'Breakfast',
products: [],
},
{
label: 'Snack I',
products: [],
},
{
label: 'Lunch',
products: [],
},
{
label: 'Snack II',
products: [],
},
{
label: 'Dinner',
products: [],
},
{
label: 'Snack III',
products: [],
},
{
label: 'Supper',
products: [],
},
]
};
const homePageReducer = produce((draft, action) => {
switch(action.type) {
case GET_MEALS_SUCCESS:
draft.meals = action.meals;
draft.isLoading = false;
break;
case UPDATE_MEAL_SUCCESS:
console.log(UPDATE_MEAL_SUCCESS, 'UPDATE_MEAL_SUCCESS')
draft.isLoading = false;
break;
case ADD_PRODUCTS_TO_MEAL_SUCCESS:
console.log(ADD_PRODUCTS_TO_MEAL_SUCCESS, 'ADD_PRODUCTS_TO_MEAL_SUCCESS')
const restMeals = draft.meals.filter(({ id }) => id === action.meal.id)
draft.meals = [...restMeals, action.meal]
draft.isLoading = false;
break;
case CREATE_MEAL_SUCCESS:
draft.meals.push(action.meal);
draft.isLoading = false;
break;
case GET_MEALS_REQUEST:
case CREATE_MEAL_REQUEST:
case UPDATE_MEAL_REQUEST:
case ADD_PRODUCTS_TO_MEAL_REQUEST:
draft.isLoading = true;
break;
case CREATE_MEAL_ERROR:
case GET_MEALS_ERROR:
case UPDATE_MEAL_ERROR:
case ADD_PRODUCTS_TO_MEAL_ERROR:
draft.isLoading = false;
draft.error = action.error;
break;
}
}, initialState);
export default homePageReducer;

105
src/pages/Home/saga.js Normal file
View File

@ -0,0 +1,105 @@
import { takeLatest, call, put, select } from 'redux-saga/effects';
import {api, request, routes} from 'utils';
import { GET_MEALS_REQUEST, UPDATE_MEAL_REQUEST, CREATE_MEAL_REQUEST, ADD_PRODUCTS_TO_MEAL_REQUEST } from './constants';
import {
} from './selectors';
import { createMealSuccessAction, createMealErrorAction, updateMealErrorAction, updateMealSuccessAction, getMealsSuccessAction, getMealsErrorAction } from './actions';
import { makeSelectLabel, makeSelectMealId, makeSelectProducts, makeSelectDate, } from './selectors'
import { makeSelectTokens } from 'containers/App/selectors'
import {push} from "connected-react-router";
export function* getMeals() {
const { access } = yield select(makeSelectTokens());
const requestURL = api.meals;
const requestParameters = {
method: 'GET',
headers: {
Authorization: `Bearer ${access.token}`,
},
};
try {
const { meals } = yield call(request, requestURL, requestParameters);
yield put(getMealsSuccessAction({ meals }));
} catch (error) {
yield put(getMealsErrorAction({ error: error.message }));
}
}
export function* updateMeal() {
const { access } = yield select(makeSelectTokens());
const label = yield select(makeSelectLabel());
const products = yield select(makeSelectProducts());
const date = yield select(makeSelectDate());
const mealId = yield select(makeSelectMealId());
const requestURL = `${api.meals}/${mealId}`;
const requestParameters = {
method: 'PUT',
headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${access.token}`, },
body: JSON.stringify({ label, products, date }),
};
try {
const { birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity } = yield call(request, requestURL, requestParameters);
yield put(updateMealSuccessAction({birthday, gender, height, currentWeight, goalWeight, rateOfChange, activity}));
} catch (error) {
yield put(updateMealErrorAction({error: error.message}));
}
}
export function* createMeal() {
const { access } = yield select(makeSelectTokens());
const label = yield select(makeSelectLabel());
const products = yield select(makeSelectProducts());
const date = yield select(makeSelectDate());
const requestURL = api.meals;
const requestParameters = {
method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${access.token}`, },
body: JSON.stringify({ label, products, date }),
};
try {
const { id, label, products, date } = yield call(request, requestURL, requestParameters);
yield put(createMealSuccessAction({ id, label, products, date }));
yield put(push(routes.dashboard.path));
} catch (error) {
yield put(createMealErrorAction({error: error.message}));
}
}
export function* addProductsToMeal() {
const { access } = yield select(makeSelectTokens());
const products = yield select(makeSelectProducts());
const mealId = yield select(makeSelectMealId());
const requestURL = `${api.meals}/${mealId}`;
const requestParameters = {
method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${access.token}`, },
body: JSON.stringify({ products }),
};
try {
const { meal } = yield call(request, requestURL, requestParameters);
yield put(createMealSuccessAction({ meal }));
yield put(push(routes.dashboard.path));
} catch (error) {
yield put(createMealErrorAction({error: error.message}));
}
}
export default function* MealPageSaga() {
yield takeLatest(GET_MEALS_REQUEST, getMeals);
yield takeLatest(UPDATE_MEAL_REQUEST, updateMeal);
yield takeLatest(CREATE_MEAL_REQUEST, createMeal);
yield takeLatest(ADD_PRODUCTS_TO_MEAL_REQUEST, addProductsToMeal);
}

View File

@ -0,0 +1,36 @@
import { createSelector } from 'reselect';
import { initialState } from './reducer';
const selectHomePageDomain = (state) => state.homePage || initialState;
const makeSelectError = () =>
createSelector(selectHomePageDomain, (substate) => substate.error);
const makeSelectIsLoading = () =>
createSelector(selectHomePageDomain, (substate) => substate.isLoading);
const makeSelectMeals = () =>
createSelector(selectHomePageDomain, (substate) => substate.meals);
const makeSelectMealId = () =>
createSelector(selectHomePageDomain, (substate) => substate.mealId);
const makeSelectDate = () =>
createSelector(selectHomePageDomain, (substate) => substate.date);
const makeSelectLabel = () =>
createSelector(selectHomePageDomain, (substate) => substate.label);
const makeSelectProducts = () =>
createSelector(selectHomePageDomain, (substate) => substate.products);
export {
selectHomePageDomain,
makeSelectMealId,
makeSelectDate,
makeSelectLabel,
makeSelectProducts,
makeSelectError,
makeSelectIsLoading,
makeSelectMeals,
};

View File

@ -1,6 +1,7 @@
const API_BASE_URL = 'http://localhost:3001/v1' const API_BASE_URL = 'http://localhost:3001/v1'
const AUTH = 'auth'; const AUTH = 'auth';
const PROFILE = 'profiles'; const PROFILE = 'profiles';
const MEALS = 'meals';
const urls = { const urls = {
auth: { auth: {
@ -9,6 +10,7 @@ const urls = {
refreshToken: `${API_BASE_URL}/${AUTH}/refresh-tokens` refreshToken: `${API_BASE_URL}/${AUTH}/refresh-tokens`
}, },
profile: `${API_BASE_URL}/${PROFILE}`, profile: `${API_BASE_URL}/${PROFILE}`,
meals: `${API_BASE_URL}/${MEALS}`,
} }
export default urls export default urls

View File

@ -3,6 +3,7 @@ import CreateProfilePage from "pages/CreateProfile";
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"; import RegisterPage from "pages/Register";
import AddDishPage from "pages/AddDish";
const routes = { const routes = {
dashboard: { dashboard: {
@ -17,6 +18,12 @@ const routes = {
privateRoute: true, privateRoute: true,
component: CreateProfilePage, component: CreateProfilePage,
}, },
addDish: {
path: '/add-dish',
exact: true,
privateRoute: true,
component: AddDishPage,
},
profile: { profile: {
path: '/profile', path: '/profile',
exact: true, exact: true,