This commit is contained in:
= 2021-01-24 21:23:52 +01:00
parent 8191799760
commit d3b1fa03fc
10 changed files with 88 additions and 125 deletions

View File

@ -1,71 +0,0 @@
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

@ -41,6 +41,7 @@ const MealCard = ({ id, label, products }) => {
<List> <List>
{products.map(({ _id, product, quantity, unit }) => ( {products.map(({ _id, product, quantity, unit }) => (
<ProductCard <ProductCard
mealId={id}
id={_id} id={_id}
product={product} product={product}
quantity={quantity} quantity={quantity}

View File

@ -2,11 +2,13 @@ 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 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' import ProductLabel from 'components/ProductLabel'
import {useDispatch } from 'react-redux'
import {removeProductFromMealAction} from 'pages/Home/actions'
const ProductCard = ({ id, product, quantity, unit }) => { const ProductCard = ({ mealId, id, product, quantity, unit }) => {
const dispatch = useDispatch()
const { label, calories, eco, verified } = product const { label, calories, eco, verified } = product
@ -20,6 +22,10 @@ const ProductCard = ({ id, product, quantity, unit }) => {
return (calories / 100) * quantity return (calories / 100) * quantity
} }
const removeProductFormMeal = () => {
dispatch(removeProductFromMealAction({ mealId, id }))
}
return ( return (
<ListItem> <ListItem>
<ListItemText <ListItemText
@ -28,8 +34,7 @@ const ProductCard = ({ id, product, quantity, unit }) => {
secondary={`calories: ${calculateCalories()}`} secondary={`calories: ${calculateCalories()}`}
/> />
<ListItemSecondaryAction> <ListItemSecondaryAction>
<EditProductDialog /> <IconButton onClick={removeProductFormMeal}>
<IconButton onClick={() => alert('remove')}>
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
</ListItemSecondaryAction> </ListItemSecondaryAction>

View File

@ -1,10 +1,10 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {Paper, IconButton} from "@material-ui/core"; import {Paper, IconButton} from "@material-ui/core";
import { Close as CloseIcon } from '@material-ui/icons' import { Close as CloseIcon } from '@material-ui/icons'
import PropTypes from 'prop-types'
import useStyles from "./styles"; import useStyles from "./styles";
const Barcode = ({ isScannerOpen, handleClose }) => { const Scanner = ({ handleClose, setBarcode }) => {
const [decodecBarcode, setDecodedBarcode] = useState('');
const classes = useStyles() const classes = useStyles()
const onClose = () => { const onClose = () => {
@ -268,7 +268,7 @@ const Barcode = ({ isScannerOpen, handleClose }) => {
// output // output
if(quality < config.quality) { if(quality < config.quality) {
const barcode = checkDigit + result.join('') const barcode = checkDigit + result.join('')
setDecodedBarcode(barcode) setBarcode(barcode)
onClose() onClose()
} }
@ -319,4 +319,9 @@ const Barcode = ({ isScannerOpen, handleClose }) => {
); );
} }
export default Barcode; Scanner.propTypes ={
handleClose: PropTypes.func.isRequired,
setBarcode: PropTypes.func.isRequired,
}
export default Scanner;

View File

@ -1,10 +1,14 @@
import React, {useState} from 'react'; import React, {useState, useEffect} from 'react';
import {IconButton} from "@material-ui/core"; import {IconButton} from "@material-ui/core";
import {CropFree as CropFreeIcon} from '@material-ui/icons' import {CropFree as CropFreeIcon} from '@material-ui/icons'
import Barcode from './Barcode' import Scanner from './Scanner'
import { useDispatch } from 'react-redux'
import {searchProductByBarcodeAction} from 'pages/Home/actions'
const BarcodeScanner = () => { const BarcodeScanner = () => {
const [isScannerOpen, setIsScannerOpen] = useState(false) const [isScannerOpen, setIsScannerOpen] = useState(false)
const [barcode, setBarcode] = useState('')
const dispatch = useDispatch()
const handleOpen = () => { const handleOpen = () => {
setIsScannerOpen(true) setIsScannerOpen(true)
@ -13,13 +17,20 @@ const BarcodeScanner = () => {
const handleClose = () => { const handleClose = () => {
setIsScannerOpen(false) setIsScannerOpen(false)
} }
useEffect(() => {
if (barcode) {
dispatch(searchProductByBarcodeAction({ barcode }))
}
}, [barcode])
return ( return (
<React.Fragment> <React.Fragment>
<IconButton onClick={handleOpen}> <IconButton onClick={handleOpen}>
<CropFreeIcon /> <CropFreeIcon />
</IconButton> </IconButton>
{isScannerOpen ? ( {isScannerOpen ? (
<Barcode isScannerOpen={isScannerOpen} handleClose={handleClose} /> <Scanner handleClose={handleClose} setBarcode={setBarcode} />
) : null} ) : null}
</React.Fragment> </React.Fragment>
); );

View File

@ -1,7 +1,6 @@
import React from 'react'; 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 {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 { CropFree as CropFreeIcon, Search as SearchIcon } from '@material-ui/icons'
import EditProductDialog from 'components/EditProductDialog'
import useStyles from './styles' import useStyles from './styles'
function generate(element) { function generate(element) {

View File

@ -17,6 +17,9 @@ import {
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,
REMOVE_PRODUCT_FROM_MEAL_REQUEST,
REMOVE_PRODUCT_FROM_MEAL_SUCCESS,
REMOVE_PRODUCT_FROM_MEAL_ERROR,
SET_SELECTED_MEAL, SET_SELECTED_MEAL,
} from './constants'; } from './constants';
@ -61,9 +64,10 @@ export const getMealsAction = ({ date }) => ({
date, date,
}) })
export const getMealsSuccessAction = ({ meals }) => ({ export const getMealsSuccessAction = ({ meals, dailyCalories }) => ({
type: GET_MEALS_SUCCESS, type: GET_MEALS_SUCCESS,
meals meals,
dailyCalories,
}) })
export const getMealsErrorAction = ({ error }) => ({ export const getMealsErrorAction = ({ error }) => ({
@ -92,12 +96,10 @@ export const createMealAction = ({ label, products, date }) => ({
date, date,
}) })
export const createMealSuccessAction = ({ id, label, products, date }) => ({ export const createMealSuccessAction = ({ meal, dailyCalories }) => ({
type: CREATE_MEAL_SUCCESS, type: CREATE_MEAL_SUCCESS,
id, meal,
label, dailyCalories,
products,
date,
}) })
export const createMealErrorAction = ({error}) => ({ export const createMealErrorAction = ({error}) => ({
@ -120,3 +122,19 @@ export const addProductsToMealErrorAction = ({error}) => ({
type: ADD_PRODUCTS_TO_MEAL_ERROR, type: ADD_PRODUCTS_TO_MEAL_ERROR,
error, error,
}) })
export const removeProductFromMealAction = ({ mealId, id }) => ({
type: REMOVE_PRODUCT_FROM_MEAL_REQUEST,
mealId,
id
})
export const removeProductFromMealSuccessAction = ({ meal }) => ({
type: REMOVE_PRODUCT_FROM_MEAL_SUCCESS,
meal,
})
export const removeProductFromMealErrorAction = ({error}) => ({
type: REMOVE_PRODUCT_FROM_MEAL_ERROR,
error,
})

View File

@ -14,6 +14,10 @@ export const ADD_PRODUCTS_TO_MEAL_REQUEST = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_R
export const ADD_PRODUCTS_TO_MEAL_SUCCESS = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_SUCCESS'; export const ADD_PRODUCTS_TO_MEAL_SUCCESS = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_SUCCESS';
export const ADD_PRODUCTS_TO_MEAL_ERROR = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_ERROR'; export const ADD_PRODUCTS_TO_MEAL_ERROR = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_ERROR';
export const REMOVE_PRODUCT_FROM_MEAL_REQUEST = 'app/HomePage/REMOVE_PRODUCT_FROM_MEAL_REQUEST'
export const REMOVE_PRODUCT_FROM_MEAL_SUCCESS = 'app/HomePage/REMOVE_PRODUCT_FROM_MEAL_SUCCESS';
export const REMOVE_PRODUCT_FROM_MEAL_ERROR = 'app/HomePage/REMOVE_PRODUCT_FROM_MEAL_ERROR';
export const SEARCH_PRODUCT_BY_LABEL_REQUEST = 'app/HomePage/SEARCH_PRODUCT_BY_LABEL_REQUEST' export const SEARCH_PRODUCT_BY_LABEL_REQUEST = 'app/HomePage/SEARCH_PRODUCT_BY_LABEL_REQUEST'
export const SEARCH_PRODUCT_BY_LABEL_SUCCESS = 'app/HomePage/SEARCH_PRODUCT_BY_LABEL_SUCCESS'; export const SEARCH_PRODUCT_BY_LABEL_SUCCESS = 'app/HomePage/SEARCH_PRODUCT_BY_LABEL_SUCCESS';
export const SEARCH_PRODUCT_BY_LABEL_ERROR = 'app/HomePage/SEARCH_PRODUCT_BY_LABEL_ERROR'; export const SEARCH_PRODUCT_BY_LABEL_ERROR = 'app/HomePage/SEARCH_PRODUCT_BY_LABEL_ERROR';

View File

@ -25,37 +25,22 @@ import {
const defaultMeals = [ const defaultMeals = [
{ {
id: null, id: null,
label: 'Breakfast', label: 'breakfast',
products: [], products: [],
}, },
{ {
id: null, id: null,
label: 'Snack I', label: 'lunch',
products: [], products: [],
}, },
{ {
id: null, id: null,
label: 'Lunch', label: 'dinner',
products: [], products: [],
}, },
{ {
id: null, id: null,
label: 'Snack II', label: 'supper',
products: [],
},
{
id: null,
label: 'Dinner',
products: [],
},
{
id: null,
label: 'Snack III',
products: [],
},
{
id: null,
label: 'Supper',
products: [], products: [],
}, },
] ]
@ -66,6 +51,7 @@ export const initialState = {
error: {}, error: {},
label: '', label: '',
products: [], products: [],
dailyCalories: 0,
barcode: '', barcode: '',
product: {}, product: {},
date: '', date: '',
@ -93,10 +79,12 @@ const homePageReducer = produce((draft, action) => {
break; break;
case GET_MEALS_SUCCESS: case GET_MEALS_SUCCESS:
draft.meals = const labels = action.meals.map(({ label }) => label)
action.meals.length > 0 draft.meals = defaultMeals
? action.meals .filter((candidate) => !labels.includes(candidate.label))
: defaultMeals; .concat(action.meals)
.sort(({ label: aLabel}, { label: bLabel }) => aLabel.localeCompare(bLabel))
draft.dailyCalories = action.dailyCalories;
draft.isLoading = false; draft.isLoading = false;
break; break;
@ -132,18 +120,21 @@ const homePageReducer = produce((draft, action) => {
break; break;
case ADD_PRODUCTS_TO_MEAL_SUCCESS: case ADD_PRODUCTS_TO_MEAL_SUCCESS:
console.log(ADD_PRODUCTS_TO_MEAL_SUCCESS, 'ADD_PRODUCTS_TO_MEAL_SUCCESS') draft.meals = defaultMeals
.filter((candidate) => !(candidate.label === action.meal.label))
const restMeals = draft.meals.filter(({ id }) => id === action.meal.id) .concat(action.meal)
.sort(
draft.meals = [...restMeals, action.meal] ({ label: aLabel}, { label: bLabel }) => aLabel.localeCompare(bLabel))
draft.isLoading = false; draft.isLoading = false;
break; break;
case CREATE_MEAL_SUCCESS: case CREATE_MEAL_SUCCESS:
const { id, label, products, date } = action draft.meals = defaultMeals
.filter((candidate) => !(candidate.label === action.meal.label))
draft.meals.push({ id, label, products, date }); .concat(action.meal)
.sort(
({ label: aLabel}, { label: bLabel }) => aLabel.localeCompare(bLabel))
draft.dailyCalories = action.dailyCalories;
draft.isLoading = false; draft.isLoading = false;
break; break;

View File

@ -20,8 +20,8 @@ export function* getMeals() {
}; };
try { try {
const meals = yield call(request, requestURL, requestParameters); const {meals, dailyCalories} = yield call(request, requestURL, requestParameters);
yield put(getMealsSuccessAction({ meals })); yield put(getMealsSuccessAction({ meals, dailyCalories }));
} catch (error) { } catch (error) {
yield put(getMealsErrorAction({ error: error.message })); yield put(getMealsErrorAction({ error: error.message }));
} }
@ -68,8 +68,8 @@ export function* createMeal() {
}; };
try { try {
const meal = yield call(request, requestURL, requestParameters); const {meal, dailyCalories} = yield call(request, requestURL, requestParameters);
yield put(createMealSuccessAction(meal)); yield put(createMealSuccessAction({meal, dailyCalories}));
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}));