Add searching products by name
This commit is contained in:
parent
c11dd4a8af
commit
bf835d2a89
@ -1,4 +1,4 @@
|
|||||||
<svg width="35" height="23" viewBox="0 0 35 23">
|
<svg width="36" height="36" viewBox="0 0 302 211" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M33.7945 11.4338C34.2146 11.4338 34.516 11.5221 34.6986 11.6985C34.8995 11.875 35 12.1043 35 12.3867C35 12.8454 34.8539 13.2512 34.5616 13.6041C34.2877 13.957 33.8493 14.1423 33.2466 14.16C31.8037 14.1776 30.5069 14.0717 29.3562 13.8423C28.0776 16.4714 26.5342 18.6594 24.726 20.4062C22.9361 22.1354 21.1644 23 19.411 23C17.8037 23 16.5982 22.1795 15.7945 20.5386C14.9909 18.8799 14.5342 16.6655 14.4247 13.8953C13.3288 16.9655 12.0868 19.2505 10.6986 20.7503C9.32877 22.2501 7.86758 23 6.31507 23C4.56164 23 3.23744 21.9501 2.34247 19.8504C1.44749 17.733 1 14.8922 1 11.328C1 8.73418 1.23744 5.88454 1.71233 2.77906C1.84018 1.89682 2.06849 1.28807 2.39726 0.952818C2.74429 0.599922 3.28311 0.423475 4.0137 0.423475C4.56164 0.423475 4.98174 0.538166 5.27397 0.767548C5.58447 0.996931 5.73973 1.42041 5.73973 2.03797C5.73973 2.16149 5.72146 2.39969 5.68493 2.75259C5.13699 6.36977 4.86301 9.44879 4.86301 11.9896C4.86301 14.354 5.07306 16.1803 5.49315 17.4684C5.91324 18.7564 6.47945 19.4005 7.19178 19.4005C7.83105 19.4005 8.59817 18.7564 9.49315 17.4684C10.4064 16.1626 11.3105 14.2394 12.2055 11.6985C13.1005 9.14001 13.8584 6.09628 14.4795 2.56732C14.6256 1.75566 14.8904 1.19985 15.274 0.899884C15.6758 0.582278 16.2146 0.423475 16.8904 0.423475C17.4566 0.423475 17.8676 0.546988 18.1233 0.794015C18.3973 1.0234 18.5342 1.37629 18.5342 1.8527C18.5342 2.13502 18.516 2.35558 18.4795 2.51438C17.968 5.39049 17.7123 8.26659 17.7123 11.1427C17.7123 13.1013 17.7763 14.6628 17.9041 15.8274C18.0502 16.9919 18.3151 17.883 18.6986 18.5006C19.1005 19.1005 19.6758 19.4005 20.4247 19.4005C21.3014 19.4005 22.2785 18.7652 23.3562 17.4948C24.4338 16.2068 25.4201 14.6099 26.3151 12.7043C25.2009 12.0338 24.3607 11.1692 23.7945 10.1105C23.2283 9.03414 22.9452 7.799 22.9452 6.40506C22.9452 5.01112 23.1644 3.83774 23.6027 2.88492C24.0594 1.91446 24.6712 1.19102 25.4384 0.714615C26.2237 0.238205 27.0913 0 28.0411 0C29.21 0 30.1324 0.40583 30.8082 1.21749C31.5023 2.02915 31.8493 3.14077 31.8493 4.55236C31.8493 6.54622 31.4018 8.76064 30.5069 11.1956C31.4384 11.3544 32.5342 11.4338 33.7945 11.4338ZM25.7123 6.21979C25.7123 7.94898 26.2877 9.22823 27.4384 10.0575C28.1507 8.08132 28.5068 6.44918 28.5068 5.1611C28.5068 4.42002 28.4064 3.88186 28.2055 3.54661C28.0046 3.19371 27.7306 3.01726 27.3836 3.01726C26.8904 3.01726 26.4886 3.29958 26.1781 3.86421C25.8676 4.4112 25.7123 5.19639 25.7123 6.21979Z" fill="#411A9E"/>
|
<path d="M66.1447 169.653C59.9927 185.037 67.4767 202.495 82.8607 208.647C98.2448 214.799 115.703 207.315 121.855 191.931L66.1447 169.653ZM179.317 48.2403C185.469 32.8562 177.985 15.3978 162.601 9.2457C147.217 3.09365 129.758 10.5777 123.606 25.9617L179.317 48.2403ZM184.581 169.717C178.464 185.115 185.989 202.556 201.387 208.673C216.785 214.789 234.226 207.265 240.343 191.867L184.581 169.717ZM299.843 42.0748C305.959 26.6766 298.435 9.23549 283.037 3.11903C267.638 -2.99742 250.197 4.52695 244.081 19.9252L299.843 42.0748ZM122.731 108.946L94.8756 97.8071L122.731 108.946ZM240.343 191.867L299.843 42.0748L244.081 19.9252L184.581 169.717L240.343 191.867ZM121.855 191.931L150.586 120.086L94.8756 97.8071L66.1447 169.653L121.855 191.931ZM150.586 120.086L179.317 48.2403L123.606 25.9617L94.8756 97.8071L150.586 120.086Z" fill="#42D51F" />
|
||||||
<path d="M32.7945 11.4338C33.2146 11.4338 33.516 11.5221 33.6986 11.6985C33.8995 11.875 34 12.1043 34 12.3867C34 12.8454 33.8539 13.2512 33.5616 13.6041C33.2877 13.957 32.8493 14.1423 32.2466 14.16C30.8037 14.1776 29.5069 14.0717 28.3562 13.8423C27.0776 16.4714 25.5342 18.6594 23.726 20.4062C21.9361 22.1354 20.1644 23 18.411 23C16.8037 23 15.5982 22.1795 14.7945 20.5386C13.9909 18.8799 13.5342 16.6655 13.4247 13.8953C12.3288 16.9655 11.0868 19.2505 9.69863 20.7503C8.32877 22.2501 6.86758 23 5.31507 23C3.56164 23 2.23744 21.9501 1.34247 19.8504C0.447489 17.733 0 14.8922 0 11.328C0 8.73418 0.237443 5.88454 0.712329 2.77906C0.840183 1.89682 1.06849 1.28807 1.39726 0.952818C1.74429 0.599922 2.28311 0.423475 3.0137 0.423475C3.56164 0.423475 3.98174 0.538166 4.27397 0.767548C4.58447 0.996931 4.73973 1.42041 4.73973 2.03797C4.73973 2.16149 4.72146 2.39969 4.68493 2.75259C4.13699 6.36977 3.86301 9.44879 3.86301 11.9896C3.86301 14.354 4.07306 16.1803 4.49315 17.4684C4.91324 18.7564 5.47945 19.4005 6.19178 19.4005C6.83105 19.4005 7.59817 18.7564 8.49315 17.4684C9.40639 16.1626 10.3105 14.2394 11.2055 11.6985C12.1005 9.14001 12.8584 6.09628 13.4795 2.56732C13.6256 1.75566 13.8904 1.19985 14.274 0.899884C14.6758 0.582278 15.2146 0.423475 15.8904 0.423475C16.4566 0.423475 16.8676 0.546988 17.1233 0.794015C17.3973 1.0234 17.5342 1.37629 17.5342 1.8527C17.5342 2.13502 17.516 2.35558 17.4795 2.51438C16.968 5.39049 16.7123 8.26659 16.7123 11.1427C16.7123 13.1013 16.7763 14.6628 16.9041 15.8274C17.0502 16.9919 17.3151 17.883 17.6986 18.5006C18.1005 19.1005 18.6758 19.4005 19.4247 19.4005C20.3014 19.4005 21.2785 18.7652 22.3562 17.4948C23.4338 16.2068 24.4201 14.6099 25.3151 12.7043C24.2009 12.0338 23.3607 11.1692 22.7945 10.1105C22.2283 9.03414 21.9452 7.799 21.9452 6.40506C21.9452 5.01112 22.1644 3.83774 22.6027 2.88492C23.0594 1.91446 23.6712 1.19102 24.4384 0.714615C25.2237 0.238205 26.0913 0 27.0411 0C28.21 0 29.1324 0.40583 29.8082 1.21749C30.5023 2.02915 30.8493 3.14077 30.8493 4.55236C30.8493 6.54622 30.4018 8.76064 29.5069 11.1956C30.4384 11.3544 31.5342 11.4338 32.7945 11.4338ZM24.7123 6.21979C24.7123 7.94898 25.2877 9.22823 26.4384 10.0575C27.1507 8.08132 27.5068 6.44918 27.5068 5.1611C27.5068 4.42002 27.4064 3.88186 27.2055 3.54661C27.0046 3.19371 26.7306 3.01726 26.3836 3.01726C25.8904 3.01726 25.4886 3.29958 25.1781 3.86421C24.8676 4.4112 24.7123 5.19639 24.7123 6.21979Z" fill="#512DA8"/>
|
<path d="M58.6512 19.3633C52.2244 4.09198 34.6346 -3.07794 19.3633 3.34883C4.09198 9.7756 -3.07794 27.3654 3.34883 42.6367L58.6512 19.3633ZM66.3871 192.429C72.8139 207.7 90.4037 214.87 105.675 208.443C120.946 202.016 128.116 184.426 121.689 169.155L66.3871 192.429ZM179.115 25.3779C172.64 10.1268 155.028 3.01185 139.777 9.48631C124.526 15.9608 117.411 33.5729 123.885 48.824L179.115 25.3779ZM184.885 192.515C191.36 207.766 208.972 214.881 224.223 208.407C239.474 201.932 246.589 184.32 240.115 169.069L184.885 192.515ZM3.34883 42.6367L66.3871 192.429L121.689 169.155L58.6512 19.3633L3.34883 42.6367ZM123.885 48.824L184.885 192.515L240.115 169.069L179.115 25.3779L123.885 48.824Z" fill="#4FE02D"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.6 KiB |
38
src/components/AddProductToMealDialog/ProductItem.js
Normal file
38
src/components/AddProductToMealDialog/ProductItem.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import {Checkbox, ListItem, ListItemSecondaryAction, ListItemText} from "@material-ui/core";
|
||||||
|
import ProductLabel from 'components/ProductLabel'
|
||||||
|
|
||||||
|
const ProductItem = ({ id, verified, eco, label, unit, servingCapacity, macronutrients }) => {
|
||||||
|
return (
|
||||||
|
<ListItem button>
|
||||||
|
<ListItemText
|
||||||
|
primary={<ProductLabel text={label} verified={verified} eco={eco} />}
|
||||||
|
disableTypography
|
||||||
|
/>
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<Checkbox
|
||||||
|
edge="end"
|
||||||
|
/>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProductItem.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
verified: PropTypes.bool.isRequired,
|
||||||
|
eco: PropTypes.bool.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 ProductItem;
|
96
src/components/AddProductToMealDialog/index.js
Normal file
96
src/components/AddProductToMealDialog/index.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import {
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
Grid,
|
||||||
|
Button,
|
||||||
|
DialogActions,
|
||||||
|
IconButton,
|
||||||
|
Dialog,
|
||||||
|
List,
|
||||||
|
} from '@material-ui/core';
|
||||||
|
import {Add as AddIcon, Delete as DeleteIcon} from "@material-ui/icons";
|
||||||
|
import SearchInput from 'components/SearchInput'
|
||||||
|
import BarcodeScanner from 'components/BarcodeScanner'
|
||||||
|
import { createStructuredSelector } from 'reselect'
|
||||||
|
import { makeSelectProducts, makeSelectProduct } from 'pages/Home/selectors'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
import ProductItem from './ProductItem'
|
||||||
|
|
||||||
|
const stateSelector = createStructuredSelector({
|
||||||
|
products: makeSelectProducts(),
|
||||||
|
product: makeSelectProduct(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const AddProductToMealDialog = ({ mealLabel }) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const { products, product } = useSelector(stateSelector)
|
||||||
|
|
||||||
|
const handleClose = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setIsOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpen = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setIsOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<IconButton onClick={handleOpen} onFocus={(event) => event.stopPropagation()}>
|
||||||
|
<AddIcon />
|
||||||
|
</IconButton>
|
||||||
|
<Dialog
|
||||||
|
fullScreen
|
||||||
|
open={isOpen}
|
||||||
|
onClose={handleClose}
|
||||||
|
>
|
||||||
|
<DialogTitle>Add products to {mealLabel}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
direction="row"
|
||||||
|
justify="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<SearchInput />
|
||||||
|
<BarcodeScanner />
|
||||||
|
</Grid>
|
||||||
|
<List>
|
||||||
|
{products.map(({ id, verified, eco, salt, brand, servingCapacity, barcode, label, unit, calories, fat, carbohydrates, protein }) => (
|
||||||
|
<ProductItem
|
||||||
|
key={id}
|
||||||
|
id={id}
|
||||||
|
verified={verified}
|
||||||
|
eco={eco}
|
||||||
|
label={label}
|
||||||
|
unit={unit}
|
||||||
|
servingCapacity={servingCapacity}
|
||||||
|
macronutrients={{
|
||||||
|
salt,
|
||||||
|
calories,
|
||||||
|
fat,
|
||||||
|
carbohydrates,
|
||||||
|
protein,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={handleClose} color="primary">
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
AddProductToMealDialog.propTypes = {
|
||||||
|
mealLabel: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddProductToMealDialog;
|
@ -1,55 +1,16 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { Box } from '@material-ui/core'
|
import {IconButton, Grid} from '@material-ui/core'
|
||||||
import Quagga from 'quagga';
|
import {CropFree as CropFreeIcon} from '@material-ui/icons'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
|
||||||
import {ReactComponent as Overlay} from './overlay.svg'
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
|
||||||
overlay: {
|
|
||||||
fill: 'transparent',
|
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
inputStream: {
|
|
||||||
name: 'Live',
|
|
||||||
type : 'LiveStream',
|
|
||||||
constraints: {
|
|
||||||
width: {min: 'auto', max: 640},
|
|
||||||
height: {min: 'auto', max: 480},
|
|
||||||
aspectRatio: 447 / 250,
|
|
||||||
facingMode: 'environment',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
locator: {
|
|
||||||
patchSize: 'medium',
|
|
||||||
halfSample: true
|
|
||||||
},
|
|
||||||
numOfWorkers: 4,
|
|
||||||
frequency: 10,
|
|
||||||
decoder: {
|
|
||||||
readers : [ "ean_8_reader"]
|
|
||||||
},
|
|
||||||
locate: true
|
|
||||||
}
|
|
||||||
|
|
||||||
const BarcodeScanner = ({ onDetected }) => {
|
|
||||||
const styles = useStyles()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
Quagga.init(config, () => Quagga.start())
|
|
||||||
return () => Quagga.offDetected(onDetected)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
|
const BarcodeScanner = () => {
|
||||||
return (
|
return (
|
||||||
<Box id="interactive" className="viewport" position="relative">
|
<React.Fragment>
|
||||||
<Overlay className={styles.overlay}/>
|
<Grid item xs={2}>
|
||||||
</Box>
|
<IconButton color="primary">
|
||||||
|
<CropFreeIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ const MacronutrientsChart = ({ max, current, unit, label }) => {
|
|||||||
height={40}
|
height={40}
|
||||||
className={classes.macronutrientsChart}
|
className={classes.macronutrientsChart}
|
||||||
/>
|
/>
|
||||||
<Typography variant="caption">
|
<Typography variant="caption" component="span" >
|
||||||
{current}{unit}
|
{current}{unit}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -6,8 +6,8 @@ import MacronutrientsChart from "components/MacronutrientsChart";
|
|||||||
const MacronutrientsDetails = ({ macronutrients }) => {
|
const MacronutrientsDetails = ({ macronutrients }) => {
|
||||||
return (
|
return (
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid container flexGrow={1}>
|
<Grid container>
|
||||||
<Typography variant="body2">250g</Typography>
|
<Typography component="span" variant="body2">250g</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
|
@ -3,20 +3,16 @@ import {
|
|||||||
AccordionDetails,
|
AccordionDetails,
|
||||||
AccordionSummary,
|
AccordionSummary,
|
||||||
Grid,
|
Grid,
|
||||||
IconButton,
|
|
||||||
List,
|
List,
|
||||||
Typography
|
Typography
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
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/ProductCard";
|
import Dish from "components/ProductCard";
|
||||||
|
import AddProductToMealDialog from 'components/AddProductToMealDialog';
|
||||||
|
|
||||||
const MealCard = ({ label, products }) => {
|
const MealCard = ({ label, products }) => {
|
||||||
|
|
||||||
console.log(products[0])
|
|
||||||
|
|
||||||
const calcMealMacronutrients = (dishes) => {
|
const calcMealMacronutrients = (dishes) => {
|
||||||
const mealMacronutrients = dishes
|
const mealMacronutrients = dishes
|
||||||
.flatMap(({ macronutrients }) => macronutrients)
|
.flatMap(({ macronutrients }) => macronutrients)
|
||||||
@ -36,19 +32,11 @@ const MealCard = ({ label, products }) => {
|
|||||||
<Accordion>
|
<Accordion>
|
||||||
<AccordionSummary>
|
<AccordionSummary>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
{JSON.stringify(products)}
|
|
||||||
<Grid container alignItems="center" justify="space-between">
|
<Grid container alignItems="center" justify="space-between">
|
||||||
<Typography variant="h5">
|
<Typography variant="h5">
|
||||||
{label}
|
{label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<AddProductToMealDialog mealLabel={label} />
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={(event) => event.stopPropagation()}
|
|
||||||
onFocus={(event) => event.stopPropagation()}
|
|
||||||
>
|
|
||||||
<AddIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid container alignItems="center">
|
<Grid container alignItems="center">
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,8 @@ import {AppBar, IconButton, Toolbar, Typography} from "@material-ui/core";
|
|||||||
import {Menu as MenuIcon, Waves as WavesIcon } from "@material-ui/icons";
|
import {Menu as MenuIcon, Waves as WavesIcon } from "@material-ui/icons";
|
||||||
import useStyles from './styles'
|
import useStyles from './styles'
|
||||||
|
|
||||||
|
import {ReactComponent as LogoIcon} from 'assets/logo.svg'
|
||||||
|
|
||||||
const Navbar = ({ toggleDrawerNav }) => {
|
const Navbar = ({ toggleDrawerNav }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
@ -14,7 +16,7 @@ const Navbar = ({ toggleDrawerNav }) => {
|
|||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" className={classes.title}>
|
<Typography variant="h6" className={classes.title}>
|
||||||
<WavesIcon />
|
<LogoIcon />
|
||||||
</Typography>
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
@ -11,6 +11,7 @@ const ProductCard = ({ label, macronutrients }) => {
|
|||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={label}
|
primary={label}
|
||||||
|
disableTypography
|
||||||
secondary={
|
secondary={
|
||||||
<MacronutrientsDetails
|
<MacronutrientsDetails
|
||||||
macronutrients={macronutrients}
|
macronutrients={macronutrients}
|
||||||
|
18
src/components/ProductLabel/index.js
Normal file
18
src/components/ProductLabel/index.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import {Grid, Typography } from "@material-ui/core";
|
||||||
|
import { Eco as EcoIcon, VerifiedUser as VerifiedIcon } from '@material-ui/icons'
|
||||||
|
|
||||||
|
const ProductLabel = ({ text, eco, verified }) => (
|
||||||
|
<Grid container>
|
||||||
|
<Typography variant="subtitle1">{text}</Typography> {eco && <EcoIcon size="small" />} {verified && <VerifiedIcon />}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
|
||||||
|
ProductLabel.propTypes = {
|
||||||
|
verified: PropTypes.bool.isRequired,
|
||||||
|
eco: PropTypes.bool.isRequired,
|
||||||
|
text: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProductLabel;
|
@ -9,7 +9,7 @@ const RadialChart = ({ strokeWidth = 20, circleRadius = 50, progress, color, wi
|
|||||||
const strokeLength = circumference / 100 * progress;
|
const strokeLength = circumference / 100 * progress;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.chartContainer}>
|
<span className={classes.chartContainer}>
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 180 180"
|
viewBox="0 0 180 180"
|
||||||
className={classes.svg}
|
className={classes.svg}
|
||||||
@ -37,7 +37,7 @@ const RadialChart = ({ strokeWidth = 20, circleRadius = 50, progress, color, wi
|
|||||||
cy="90"
|
cy="90"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
src/components/SearchInput/index.js
Normal file
27
src/components/SearchInput/index.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {TextField, Grid} from '@material-ui/core'
|
||||||
|
import { throttle } from 'lodash'
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
|
import { searchProductByLabelAction } from 'pages/Home/actions'
|
||||||
|
|
||||||
|
const SearchInput = () => {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
const searchByLabel = ({ target: { value }}) => {
|
||||||
|
dispatch(searchProductByLabelAction({ label: value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
type="text"
|
||||||
|
label="Search product"
|
||||||
|
onChange={throttle(searchByLabel, 1000)}
|
||||||
|
name="label"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SearchInput;
|
@ -8,7 +8,7 @@ import { LOGIN_SUCCESS } from 'pages/Login/constants'
|
|||||||
import { REGISTER_SUCCESS } from 'pages/Register/constants'
|
import { REGISTER_SUCCESS } from 'pages/Register/constants'
|
||||||
|
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
isLogged: true,
|
isLogged: false,
|
||||||
notifications: [],
|
notifications: [],
|
||||||
tokens: {
|
tokens: {
|
||||||
access: {
|
access: {
|
||||||
|
@ -9,27 +9,29 @@ const Routes = () => {
|
|||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Container className={classes.container}>
|
||||||
<Container className={classes.contentConainer}>
|
<Switch>
|
||||||
{Object.values(routes).map(({privateRoute, exact, path, component, }, index) => (
|
<React.Fragment>
|
||||||
<React.Fragment key={index}>
|
{Object.values(routes).map(({privateRoute, exact, path, component, }, index) => (
|
||||||
{privateRoute ? (
|
<React.Fragment key={index}>
|
||||||
<PrivateRoute
|
{privateRoute ? (
|
||||||
exact={exact}
|
<PrivateRoute
|
||||||
path={path}
|
exact={exact}
|
||||||
component={component}
|
path={path}
|
||||||
/>
|
component={component}
|
||||||
) : (
|
/>
|
||||||
<Route
|
) : (
|
||||||
exact={exact}
|
<Route
|
||||||
path={path}
|
exact={exact}
|
||||||
component={component}
|
path={path}
|
||||||
/>
|
component={component}
|
||||||
)}
|
/>
|
||||||
</React.Fragment>
|
)}
|
||||||
))}
|
</React.Fragment>
|
||||||
</Container>
|
))}
|
||||||
</Switch>
|
</React.Fragment>
|
||||||
|
</Switch>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {makeStyles} from "@material-ui/core/styles";
|
import {makeStyles} from "@material-ui/core/styles";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
contentConainer: {
|
container: {
|
||||||
marginTop: theme.spacing(10),
|
marginTop: theme.spacing(10),
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
@ -11,8 +11,44 @@ import {
|
|||||||
ADD_PRODUCTS_TO_MEAL_REQUEST,
|
ADD_PRODUCTS_TO_MEAL_REQUEST,
|
||||||
ADD_PRODUCTS_TO_MEAL_SUCCESS,
|
ADD_PRODUCTS_TO_MEAL_SUCCESS,
|
||||||
ADD_PRODUCTS_TO_MEAL_ERROR,
|
ADD_PRODUCTS_TO_MEAL_ERROR,
|
||||||
|
SEARCH_PRODUCT_BY_LABEL_REQUEST,
|
||||||
|
SEARCH_PRODUCT_BY_BARCODE_REQUEST,
|
||||||
|
SEARCH_PRODUCT_BY_BARCODE_SUCCESS,
|
||||||
|
SEARCH_PRODUCT_BY_BARCODE_ERROR,
|
||||||
|
SEARCH_PRODUCT_BY_LABEL_SUCCESS,
|
||||||
|
SEARCH_PRODUCT_BY_LABEL_ERROR
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
|
export const searchProductByLabelAction = ({ label }) => ({
|
||||||
|
type: SEARCH_PRODUCT_BY_LABEL_REQUEST,
|
||||||
|
label,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const searchProductByLabelSuccessAction = ({ products }) => ({
|
||||||
|
type: SEARCH_PRODUCT_BY_LABEL_SUCCESS,
|
||||||
|
products,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const searchProductByLabelErrorAction = ({ error }) => ({
|
||||||
|
type: SEARCH_PRODUCT_BY_LABEL_ERROR,
|
||||||
|
error,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const searchProductByBarcodeAction = ({ barcode }) => ({
|
||||||
|
type: SEARCH_PRODUCT_BY_BARCODE_REQUEST,
|
||||||
|
barcode,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const searchProductByBarcodeSuccessAction = ({ product }) => ({
|
||||||
|
type: SEARCH_PRODUCT_BY_BARCODE_SUCCESS,
|
||||||
|
product,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const searchProductByBarcodeErrorAction = ({ error }) => ({
|
||||||
|
type: SEARCH_PRODUCT_BY_BARCODE_ERROR,
|
||||||
|
error,
|
||||||
|
})
|
||||||
|
|
||||||
export const getMealsAction = ({ date }) => ({
|
export const getMealsAction = ({ date }) => ({
|
||||||
type: GET_MEALS_REQUEST,
|
type: GET_MEALS_REQUEST,
|
||||||
date,
|
date,
|
||||||
|
@ -13,3 +13,11 @@ export const UPDATE_MEAL_ERROR = 'app/HomePage/UPDATE_MEAL_ERROR';
|
|||||||
export const ADD_PRODUCTS_TO_MEAL_REQUEST = 'app/HomePage/ADD_PRODUCTS_TO_MEAL_REQUEST'
|
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_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 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_ERROR = 'app/HomePage/SEARCH_PRODUCT_BY_LABEL_ERROR';
|
||||||
|
|
||||||
|
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_ERROR = 'app/HomePage/SEARCH_PRODUCT_BY_BARCODE_ERROR';
|
||||||
|
@ -13,51 +13,89 @@ import {
|
|||||||
ADD_PRODUCTS_TO_MEAL_REQUEST,
|
ADD_PRODUCTS_TO_MEAL_REQUEST,
|
||||||
ADD_PRODUCTS_TO_MEAL_SUCCESS,
|
ADD_PRODUCTS_TO_MEAL_SUCCESS,
|
||||||
ADD_PRODUCTS_TO_MEAL_ERROR,
|
ADD_PRODUCTS_TO_MEAL_ERROR,
|
||||||
|
SEARCH_PRODUCT_BY_BARCODE_REQUEST,
|
||||||
|
SEARCH_PRODUCT_BY_BARCODE_SUCCESS,
|
||||||
|
SEARCH_PRODUCT_BY_BARCODE_ERROR,
|
||||||
|
SEARCH_PRODUCT_BY_LABEL_REQUEST,
|
||||||
|
SEARCH_PRODUCT_BY_LABEL_SUCCESS,
|
||||||
|
SEARCH_PRODUCT_BY_LABEL_ERROR
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
|
const defaultMeals = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
label: 'Breakfast',
|
||||||
|
products: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
label: 'Snack I',
|
||||||
|
products: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
label: 'Lunch',
|
||||||
|
products: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
label: 'Snack II',
|
||||||
|
products: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
label: 'Dinner',
|
||||||
|
products: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
label: 'Snack III',
|
||||||
|
products: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
label: 'Supper',
|
||||||
|
products: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: {},
|
error: {},
|
||||||
label: '',
|
label: '',
|
||||||
products: [],
|
products: [],
|
||||||
|
barcode: '',
|
||||||
|
product: {},
|
||||||
date: '',
|
date: '',
|
||||||
mealId: '',
|
mealId: '',
|
||||||
meals: [
|
meals: defaultMeals
|
||||||
{
|
|
||||||
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) => {
|
const homePageReducer = produce((draft, action) => {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case GET_MEALS_SUCCESS:
|
case GET_MEALS_SUCCESS:
|
||||||
draft.meals = action.meals;
|
draft.meals = action.meals.length || defaultMeals;
|
||||||
|
draft.isLoading = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEARCH_PRODUCT_BY_BARCODE_REQUEST:
|
||||||
|
draft.barcode = action.barcode;
|
||||||
|
draft.isLoading = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEARCH_PRODUCT_BY_BARCODE_SUCCESS:
|
||||||
|
draft.product = action.product;
|
||||||
|
draft.isLoading = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEARCH_PRODUCT_BY_LABEL_REQUEST:
|
||||||
|
draft.label = action.label;
|
||||||
|
draft.isLoading = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEARCH_PRODUCT_BY_LABEL_SUCCESS:
|
||||||
|
draft.products = action.products;
|
||||||
draft.isLoading = false;
|
draft.isLoading = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -95,6 +133,8 @@ const homePageReducer = produce((draft, action) => {
|
|||||||
case GET_MEALS_ERROR:
|
case GET_MEALS_ERROR:
|
||||||
case UPDATE_MEAL_ERROR:
|
case UPDATE_MEAL_ERROR:
|
||||||
case ADD_PRODUCTS_TO_MEAL_ERROR:
|
case ADD_PRODUCTS_TO_MEAL_ERROR:
|
||||||
|
case SEARCH_PRODUCT_BY_LABEL_ERROR:
|
||||||
|
case SEARCH_PRODUCT_BY_BARCODE_ERROR:
|
||||||
draft.isLoading = false;
|
draft.isLoading = false;
|
||||||
draft.error = action.error;
|
draft.error = action.error;
|
||||||
break;
|
break;
|
||||||
|
@ -1,11 +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, 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 {
|
import { searchProductByLabelErrorAction, searchProductByLabelSuccessAction, createMealSuccessAction, createMealErrorAction, searchProductByBarcodeSuccessAction, searchProductByBarcodeErrorAction, updateMealErrorAction, updateMealSuccessAction, getMealsSuccessAction, getMealsErrorAction } from './actions';
|
||||||
|
import { makeSelectLabel, makeSelectMealId, makeSelectBarcode, makeSelectProducts, makeSelectDate, } from './selectors'
|
||||||
} 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 { makeSelectTokens } from 'containers/App/selectors'
|
||||||
import {push} from "connected-react-router";
|
import {push} from "connected-react-router";
|
||||||
|
|
||||||
@ -98,7 +95,52 @@ export function* addProductsToMeal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function* searchProductByBarcode() {
|
||||||
|
const { access } = yield select(makeSelectTokens());
|
||||||
|
const barcode = yield select(makeSelectBarcode());
|
||||||
|
|
||||||
|
const requestURL = `${api.products}?barcode=${barcode}`;
|
||||||
|
|
||||||
|
const requestParameters = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${access.token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { product } = yield call(request, requestURL, requestParameters);
|
||||||
|
yield put(searchProductByBarcodeSuccessAction({product}));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(searchProductByBarcodeErrorAction({error: error.message}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* searchProductByLabel() {
|
||||||
|
const { access } = yield select(makeSelectTokens());
|
||||||
|
const label = yield select(makeSelectLabel());
|
||||||
|
|
||||||
|
const requestURL = `${api.products}?label=${label}`;
|
||||||
|
|
||||||
|
const requestParameters = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${access.token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const products = yield call(request, requestURL, requestParameters);
|
||||||
|
yield put(searchProductByLabelSuccessAction({products}));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(searchProductByLabelErrorAction({error: error.message}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function* MealPageSaga() {
|
export default function* MealPageSaga() {
|
||||||
|
yield takeLatest(SEARCH_PRODUCT_BY_BARCODE_REQUEST, searchProductByBarcode);
|
||||||
|
yield takeLatest(SEARCH_PRODUCT_BY_LABEL_REQUEST, searchProductByLabel);
|
||||||
yield takeLatest(GET_MEALS_REQUEST, getMeals);
|
yield takeLatest(GET_MEALS_REQUEST, getMeals);
|
||||||
yield takeLatest(UPDATE_MEAL_REQUEST, updateMeal);
|
yield takeLatest(UPDATE_MEAL_REQUEST, updateMeal);
|
||||||
yield takeLatest(CREATE_MEAL_REQUEST, createMeal);
|
yield takeLatest(CREATE_MEAL_REQUEST, createMeal);
|
||||||
|
@ -24,6 +24,12 @@ const makeSelectLabel = () =>
|
|||||||
const makeSelectProducts = () =>
|
const makeSelectProducts = () =>
|
||||||
createSelector(selectHomePageDomain, (substate) => substate.products);
|
createSelector(selectHomePageDomain, (substate) => substate.products);
|
||||||
|
|
||||||
|
const makeSelectProduct = () =>
|
||||||
|
createSelector(selectHomePageDomain, (substate) => substate.product);
|
||||||
|
|
||||||
|
const makeSelectBarcode = () =>
|
||||||
|
createSelector(selectHomePageDomain, (substate) => substate.barocde);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
selectHomePageDomain,
|
selectHomePageDomain,
|
||||||
makeSelectMealId,
|
makeSelectMealId,
|
||||||
@ -33,4 +39,7 @@ export {
|
|||||||
makeSelectError,
|
makeSelectError,
|
||||||
makeSelectIsLoading,
|
makeSelectIsLoading,
|
||||||
makeSelectMeals,
|
makeSelectMeals,
|
||||||
|
makeSelectProduct,
|
||||||
|
makeSelectBarcode,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ const API_BASE_URL = 'http://localhost:3001/v1'
|
|||||||
const AUTH = 'auth';
|
const AUTH = 'auth';
|
||||||
const PROFILE = 'profiles';
|
const PROFILE = 'profiles';
|
||||||
const MEALS = 'meals';
|
const MEALS = 'meals';
|
||||||
|
const PRODUCTS = 'products'
|
||||||
|
|
||||||
const urls = {
|
const urls = {
|
||||||
auth: {
|
auth: {
|
||||||
@ -11,6 +12,7 @@ const urls = {
|
|||||||
},
|
},
|
||||||
profile: `${API_BASE_URL}/${PROFILE}`,
|
profile: `${API_BASE_URL}/${PROFILE}`,
|
||||||
meals: `${API_BASE_URL}/${MEALS}`,
|
meals: `${API_BASE_URL}/${MEALS}`,
|
||||||
|
products: `${API_BASE_URL}/${PRODUCTS}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default urls
|
export default urls
|
||||||
|
Loading…
Reference in New Issue
Block a user