Add basic meals list #1
18
package.json
18
package.json
@ -5,20 +5,28 @@
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@tailwindcss/forms": "^0.2.1",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"@zxing/library": "^0.18.3",
|
||||
"add": "^2.0.6",
|
||||
"autoprefixer": "^10.0.4",
|
||||
"lodash": "^4.17.20",
|
||||
"postcss": "^8.1.10",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.1",
|
||||
"react-barcode-reader": "^0.0.2",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.0",
|
||||
"react-webcam": "^5.2.2",
|
||||
"react-webcam-barcode-scanner": "^0.0.2-rc2",
|
||||
"styled-components": "^5.2.1",
|
||||
"recharts": "^1.8.5",
|
||||
"redux": "^4.0.5",
|
||||
"redux-saga": "^1.1.3",
|
||||
"reselect": "^4.0.0",
|
||||
"tailwindcss": "^2.0.1",
|
||||
"web-vitals": "^0.2.4",
|
||||
"yarn": "^1.22.10"
|
||||
},
|
||||
@ -46,7 +54,5 @@
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
10
src/components/AuthHeader/index.js
Normal file
10
src/components/AuthHeader/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
const AuthHeader = () => (
|
||||
<header className="flex md:h-screen md:w-7/12 flex-col justify-center items-center text-center mt-10 mb-20">
|
||||
<h1 className="text-6xl font-bold uppercase text-primary-base">Fitwave</h1>
|
||||
<p className="mt-1 text-base text-xl">Your best calorie calculator</p>
|
||||
</header>
|
||||
);
|
||||
|
||||
export default AuthHeader;
|
@ -1,4 +1,5 @@
|
||||
import React, { useRef, useCallback, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { BrowserMultiFormatReader } from '@zxing/library'
|
||||
import Webcam from 'react-webcam'
|
||||
|
||||
@ -44,4 +45,11 @@ const BarcodeScanner = ({
|
||||
)
|
||||
}
|
||||
|
||||
BarcodeScanner.propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onError: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default BarcodeScanner
|
||||
|
90
src/components/MenuList/index.js
Normal file
90
src/components/MenuList/index.js
Normal file
@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import {List, ListItem, ListItemSecondaryAction, ListItemText, IconButton, Box, Typography} from '@material-ui/core';
|
||||
import { Add as AddIcon } from '@material-ui/icons';
|
||||
|
||||
const ITEMS = [
|
||||
{
|
||||
calories: 0,
|
||||
proteins: 0,
|
||||
fats: 0,
|
||||
carbohydrates: 0,
|
||||
mealName: 'breakfast',
|
||||
meals: []
|
||||
},
|
||||
{
|
||||
calories: 0,
|
||||
proteins: 0,
|
||||
fats: 0,
|
||||
carbohydrates: 0,
|
||||
mealName: 'lunch',
|
||||
meals: []
|
||||
},
|
||||
{
|
||||
calories: 0,
|
||||
proteins: 0,
|
||||
fats: 0,
|
||||
carbohydrates: 0,
|
||||
mealName: 'dinner',
|
||||
meals: []
|
||||
},
|
||||
{
|
||||
calories: 0,
|
||||
proteins: 0,
|
||||
fats: 0,
|
||||
carbohydrates: 0,
|
||||
mealName: 'supper',
|
||||
meals: []
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingRight: theme.spacing(2)
|
||||
},
|
||||
}));
|
||||
|
||||
const Macronutrients = ({ calories, proteins, fats, carbohydrates }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const macronutrients = [
|
||||
calories, proteins, fats, carbohydrates
|
||||
]
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
{macronutrients.map((value, index) => (
|
||||
<Typography paragraph key={index}>
|
||||
{value}{' '}{index === 0 ? 'kcal' : 'g'}
|
||||
</Typography>
|
||||
))}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const MenuList = ({ items }) => {
|
||||
|
||||
return (
|
||||
<List dense>
|
||||
{ITEMS.map(({calories, proteins, fats, carbohydrates, mealName, meals}, index) => (
|
||||
<ListItem key={index}>
|
||||
<ListItemText
|
||||
primary={mealName.toUpperCase()}
|
||||
secondary={<Macronutrients calories={calories} proteins={proteins} fats={fats} carbohydrates={carbohydrates} />}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton edge="end" aria-label="add meal" color="secondary">
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
export default MenuList
|
37
src/components/Navbar/index.js
Normal file
37
src/components/Navbar/index.js
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import {AppBar, Button, IconButton, Toolbar, Typography} from "@material-ui/core";
|
||||
import MenuIcon from "@material-ui/icons/Menu";
|
||||
import Waves from "@material-ui/icons/Waves";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
menuButton: {
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
}));
|
||||
|
||||
const Navbar = () => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
<IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu">
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" className={classes.title}>
|
||||
<Waves />
|
||||
</Typography>
|
||||
<Button color="inherit">Login</Button>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
55
src/components/ScrollableTabs/index.js
Normal file
55
src/components/ScrollableTabs/index.js
Normal file
@ -0,0 +1,55 @@
|
||||
import React, {useState} from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Box, Tab, Tabs } from "@material-ui/core";
|
||||
|
||||
import TabLabel from 'components/TabLabel';
|
||||
import TabPanel from 'components/TabPanel';
|
||||
|
||||
const a11yProps = (index) => {
|
||||
return {
|
||||
id: `scrollable-force-tab-${index}`,
|
||||
'aria-controls': `scrollable-force-tabpanel-${index}`,
|
||||
};
|
||||
}
|
||||
|
||||
const ScrollableTabs = ({ tabs, panels }) => {
|
||||
const [value, setValue] = useState(0);
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Tabs
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
variant="scrollable"
|
||||
scrollButtons="desktop"
|
||||
indicatorColor="primary"
|
||||
>
|
||||
{tabs.map((label, index) => (
|
||||
<Tab
|
||||
style={{ minWidth: '33%' }}
|
||||
label={<TabLabel label={label} />}
|
||||
{...a11yProps(index)}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
<Box>
|
||||
{panels.map((Component, index) => (
|
||||
<TabPanel value={value} index={index}>
|
||||
<Component />
|
||||
</TabPanel>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
ScrollableTabs.propTypes = {
|
||||
tabs: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
panels: PropTypes.arrayOf(PropTypes.node).isRequired
|
||||
}
|
||||
|
||||
export default ScrollableTabs
|
23
src/components/TabLabel/index.js
Normal file
23
src/components/TabLabel/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
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;
|
29
src/components/TabPanel/index.js
Normal file
29
src/components/TabPanel/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
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,
|
||||
index: PropTypes.any.isRequired,
|
||||
value: PropTypes.any.isRequired,
|
||||
};
|
||||
|
||||
export default TabPanel
|
@ -1,20 +1,30 @@
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Switch } from 'react-router-dom';
|
||||
import { ROUTES } from 'utils/routes';
|
||||
import RouteWithSubRoutes from 'containers/RouteWithSubRoutes'
|
||||
import { ThemeProvider } from '@material-ui/core/styles'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
import { ROUTES } from 'utils/routes';
|
||||
import { theme } from 'utils/theme'
|
||||
|
||||
const AppWrapper = styled.div``
|
||||
|
||||
import Navbar from 'components/Navbar'
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<AppWrapper>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Navbar />
|
||||
<Switch>
|
||||
{ROUTES.map(({ key, ...props }) => <RouteWithSubRoutes key={key} {...props} />)}
|
||||
{ROUTES.map(({ key, component, path, exact, routes }) => (
|
||||
<RouteWithSubRoutes
|
||||
key={key}
|
||||
component={component}
|
||||
path={path}
|
||||
exact={exact}
|
||||
routes={routes}
|
||||
/>
|
||||
))}
|
||||
</Switch>
|
||||
</AppWrapper>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Route } from 'react-router-dom'
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const RouteWithSubRoutes = ({ component: Component, path, exact, routes }) => (
|
||||
const RouteWithSubRoutes = ({ component: Component, path, exact, routes = [] }) => (
|
||||
<Route
|
||||
path={path}
|
||||
exact={exact}
|
||||
@ -11,10 +11,10 @@ const RouteWithSubRoutes = ({ component: Component, path, exact, routes }) => (
|
||||
)
|
||||
|
||||
RouteWithSubRoutes.propTypes = {
|
||||
component: PropTypes.node.isRequired,
|
||||
routes: PropTypes.array,
|
||||
component: PropTypes.elementType.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
exact: PropTypes.bool.isRequired,
|
||||
routes: PropTypes.array.isRequired,
|
||||
}
|
||||
|
||||
export default RouteWithSubRoutes;
|
||||
|
@ -1,10 +1,46 @@
|
||||
import React from 'react';
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
import { Container, Grid, Paper } from '@material-ui/core';
|
||||
|
||||
import ScrollableTabs from 'components/ScrollableTabs';
|
||||
import MenuList from 'components/MenuList'
|
||||
|
||||
let i = 1
|
||||
const TABS = Array.from({ length: 30 }, () => i++)
|
||||
const PANELS = Array.from({ length: 30 }, () => MenuList)
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
marginTop: theme.spacing(2),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
const HomePage = () => {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<div>
|
||||
Home page hello world
|
||||
</div>
|
||||
<Container className={classes.root}>
|
||||
<Grid xs="12" container spacing={2} justify="center">
|
||||
<Grid item xs="12" md="6">
|
||||
<Paper>
|
||||
<ScrollableTabs tabs={TABS} panels={PANELS} />
|
||||
</Paper >
|
||||
</Grid>
|
||||
<Grid item xs="12" md="6">
|
||||
<Paper>
|
||||
<span>Hello world</span>
|
||||
</Paper >
|
||||
|
||||
<Paper>
|
||||
<span>Hello world</span>
|
||||
</Paper >
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const LoginPage = () => {
|
||||
return (
|
||||
<div>
|
||||
Login page
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
@ -1,5 +1,4 @@
|
||||
import HomePage from 'pages/Home'
|
||||
import LoginPage from 'pages/Login'
|
||||
|
||||
export const ROUTES = [
|
||||
{
|
||||
@ -8,10 +7,4 @@ export const ROUTES = [
|
||||
exact: true,
|
||||
component: HomePage
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
key: 'LOGIN',
|
||||
exact: true,
|
||||
component: LoginPage
|
||||
}
|
||||
]
|
||||
|
13
src/utils/theme.js
Normal file
13
src/utils/theme.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { createMuiTheme } from '@material-ui/core/styles';
|
||||
|
||||
export const theme = createMuiTheme({
|
||||
palette: {
|
||||
type: 'dark',
|
||||
primary: {
|
||||
main: '#512da8',
|
||||
},
|
||||
secondary: {
|
||||
main: '#40c4ff',
|
||||
},
|
||||
},
|
||||
})
|
Loading…
Reference in New Issue
Block a user