diff --git a/.gitignore b/.gitignore index c989495..a0749d6 100644 --- a/.gitignore +++ b/.gitignore @@ -94,4 +94,5 @@ typings/ .vscode/* avatars/ -images/ \ No newline at end of file +images/ +react-material-dashboard-master/ \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 2e0ac98..b6ec7d1 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -2948,6 +2948,32 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "chart.js": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.2.tgz", + "integrity": "sha512-AagP9h27gU7hhx8F64BOFpNZGV0R1Pz1nhsi0M1+KLhtniX6ElqLl0z0obKSiuGMl9tcRe6ZhruCGCJWmH6snQ==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "requires": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^1.9.3" + } + }, + "chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "requires": { + "color-name": "^1.0.0" + } + }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -10803,6 +10829,15 @@ "whatwg-fetch": "3.0.0" } }, + "react-chartjs-2": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.8.0.tgz", + "integrity": "sha512-BPpC+qfnh37DkcXvxRwA1rdD9rX/0AQrwru4VZTLofCCuZBwRsc7PbfxjilvoB6YlHhorwZu40YDWEQkoz7xfQ==", + "requires": { + "lodash": "^4.17.4", + "prop-types": "^15.5.8" + } + }, "react-dev-utils": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.1.0.tgz", diff --git a/client/package.json b/client/package.json index 5434776..2995cb5 100644 --- a/client/package.json +++ b/client/package.json @@ -4,14 +4,21 @@ "private": true, "dependencies": { "@date-io/moment": "1.3.11", + "@fullcalendar/core": "^4.3.1", + "@fullcalendar/daygrid": "^4.3.0", + "@fullcalendar/interaction": "^4.3.0", + "@fullcalendar/react": "^4.3.0", + "@fullcalendar/timegrid": "^4.3.0", "@material-ui/core": "4.6.0", "@material-ui/icons": "4.5.1", "@material-ui/lab": "4.0.0-alpha.31", "@material-ui/pickers": "3.2.7", "axios": "0.19.0", + "chart.js": "^2.9.2", "classnames": "2.2.6", "moment": "2.24.0", "react": "16.11.0", + "react-chartjs-2": "^2.8.0", "react-dom": "16.11.0", "react-facebook-login": "4.1.1", "react-google-login": "5.0.7", @@ -24,12 +31,7 @@ "redux-thunk": "2.3.0", "slick-carousel": "1.8.1", "typeface-montserrat": "0.0.75", - "uuid": "3.3.3", - "@fullcalendar/core": "^4.3.1", - "@fullcalendar/daygrid": "^4.3.0", - "@fullcalendar/interaction": "^4.3.0", - "@fullcalendar/react": "^4.3.0", - "@fullcalendar/timegrid": "^4.3.0" + "uuid": "3.3.3" }, "scripts": { "start": "react-scripts start", diff --git a/client/src/pages/Admin/DashboardPage/DashboardPage.js b/client/src/pages/Admin/DashboardPage/DashboardPage.js index 2c156d1..5328e6f 100644 --- a/client/src/pages/Admin/DashboardPage/DashboardPage.js +++ b/client/src/pages/Admin/DashboardPage/DashboardPage.js @@ -1,17 +1,51 @@ import React, { Component } from 'react'; -import { withStyles } from '@material-ui/core'; +import { withStyles, Grid } from '@material-ui/core'; import Dashboard from '../../../layouts/Dashboard/Dashboard'; +import { + TotalUsers, + TotalCinemas, + TotalMovies, + TotalReservations, + LatestSales, + UsersByDevice +} from './components'; const styles = theme => ({ root: { textAlign: 'center', - paddingTop: theme.spacing(2) + padding: theme.spacing(4) } }); class DashboardPage extends Component { render() { - return ; + const { classes } = this.props; + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ); } } diff --git a/client/src/pages/Admin/DashboardPage/components/LatestSales/LatestSales.js b/client/src/pages/Admin/DashboardPage/components/LatestSales/LatestSales.js new file mode 100644 index 0000000..4796876 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/LatestSales/LatestSales.js @@ -0,0 +1,65 @@ +import React from 'react'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import { Bar } from 'react-chartjs-2'; +import { makeStyles } from '@material-ui/styles'; +import { + Card, + CardHeader, + CardContent, + CardActions, + Divider, + Button +} from '@material-ui/core'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; +import ArrowRightIcon from '@material-ui/icons/ArrowRight'; + +import { data, options } from './chart'; + +const useStyles = makeStyles(() => ({ + root: {}, + chartContainer: { + height: 400, + position: 'relative' + }, + actions: { + justifyContent: 'flex-end' + } +})); + +const LatestSales = props => { + const { className } = props; + + const classes = useStyles(); + + return ( + + + Last 7 days + + } + title="Latest Sales" + /> + + + + + + + + + + Overview + + + + ); +}; + +LatestSales.propTypes = { + className: PropTypes.string +}; + +export default LatestSales; diff --git a/client/src/pages/Admin/DashboardPage/components/LatestSales/chart.js b/client/src/pages/Admin/DashboardPage/components/LatestSales/chart.js new file mode 100644 index 0000000..5a8fc47 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/LatestSales/chart.js @@ -0,0 +1,72 @@ +import palette from '../../../../../theme/palette'; + +export const data = { + labels: ['1 Aug', '2 Aug', '3 Aug', '4 Aug', '5 Aug', '6 Aug'], + datasets: [ + { + label: 'This year', + backgroundColor: palette.primary.main, + data: [18, 5, 19, 27, 29, 19, 20] + }, + { + label: 'Last year', + backgroundColor: palette.neutral, + data: [11, 20, 12, 29, 30, 25, 13] + } + ] +}; + +export const options = { + responsive: true, + maintainAspectRatio: false, + animation: false, + legend: { display: false }, + cornerRadius: 20, + tooltips: { + enabled: true, + mode: 'index', + intersect: false, + borderWidth: 1, + borderColor: palette.divider, + backgroundColor: palette.white, + titleFontColor: palette.text.primary, + bodyFontColor: palette.text.secondary, + footerFontColor: palette.text.secondary + }, + layout: { padding: 0 }, + scales: { + xAxes: [ + { + barThickness: 12, + maxBarThickness: 10, + barPercentage: 0.5, + categoryPercentage: 0.5, + ticks: { + fontColor: palette.text.secondary + }, + gridLines: { + display: false, + drawBorder: false + } + } + ], + yAxes: [ + { + ticks: { + fontColor: palette.text.secondary, + beginAtZero: true, + min: 0 + }, + gridLines: { + borderDash: [2], + borderDashOffset: [2], + color: palette.divider, + drawBorder: false, + zeroLineBorderDash: [2], + zeroLineBorderDashOffset: [2], + zeroLineColor: palette.divider + } + } + ] + } +}; diff --git a/client/src/pages/Admin/DashboardPage/components/LatestSales/index.js b/client/src/pages/Admin/DashboardPage/components/LatestSales/index.js new file mode 100644 index 0000000..f5cde02 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/LatestSales/index.js @@ -0,0 +1 @@ +export { default } from './LatestSales'; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalCinemas/TotalCinemas.js b/client/src/pages/Admin/DashboardPage/components/TotalCinemas/TotalCinemas.js new file mode 100644 index 0000000..bb05bcf --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalCinemas/TotalCinemas.js @@ -0,0 +1,86 @@ +import React from 'react'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import { makeStyles } from '@material-ui/styles'; +import { Card, CardContent, Grid, Typography, Avatar } from '@material-ui/core'; +import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; +import TheatersIcon from '@material-ui/icons/Theaters'; + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%' + }, + content: { + alignItems: 'center', + display: 'flex' + }, + title: { + fontWeight: 700 + }, + avatar: { + backgroundColor: theme.palette.error.main, + height: 56, + width: 56 + }, + icon: { + height: 32, + width: 32 + }, + difference: { + marginTop: theme.spacing(2), + display: 'flex', + alignItems: 'center' + }, + differenceIcon: { + color: theme.palette.error.dark + }, + differenceValue: { + color: theme.palette.error.dark, + marginRight: theme.spacing(1) + } +})); + +const TotalCinemas = props => { + const { className, cinemas } = props; + + const classes = useStyles(); + + return ( + + + + + + TOTAL CINEMAS + + {cinemas} + + + + + + + + + + + 16% + + + Since last month + + + + + ); +}; + +TotalCinemas.propTypes = { + className: PropTypes.string +}; + +export default TotalCinemas; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalCinemas/index.js b/client/src/pages/Admin/DashboardPage/components/TotalCinemas/index.js new file mode 100644 index 0000000..d5b9280 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalCinemas/index.js @@ -0,0 +1 @@ +export { default } from './TotalCinemas'; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalMovies/TotalMovies.js b/client/src/pages/Admin/DashboardPage/components/TotalMovies/TotalMovies.js new file mode 100644 index 0000000..ea929cd --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalMovies/TotalMovies.js @@ -0,0 +1,86 @@ +import React from 'react'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import { makeStyles } from '@material-ui/styles'; +import { Card, CardContent, Grid, Typography, Avatar } from '@material-ui/core'; +import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; +import TheatersIcon from '@material-ui/icons/Theaters'; + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%' + }, + content: { + alignItems: 'center', + display: 'flex' + }, + title: { + fontWeight: 700 + }, + avatar: { + backgroundColor: theme.palette.error.main, + height: 56, + width: 56 + }, + icon: { + height: 32, + width: 32 + }, + difference: { + marginTop: theme.spacing(2), + display: 'flex', + alignItems: 'center' + }, + differenceIcon: { + color: theme.palette.error.dark + }, + differenceValue: { + color: theme.palette.error.dark, + marginRight: theme.spacing(1) + } +})); + +const TotalMovies = props => { + const { className, movies } = props; + + const classes = useStyles(); + + return ( + + + + + + TOTAL MOVIES + + {movies} + + + + + + + + + + + 16% + + + Since last month + + + + + ); +}; + +TotalMovies.propTypes = { + className: PropTypes.string +}; + +export default TotalMovies; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalMovies/index.js b/client/src/pages/Admin/DashboardPage/components/TotalMovies/index.js new file mode 100644 index 0000000..f75fbc3 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalMovies/index.js @@ -0,0 +1 @@ +export { default } from './TotalMovies'; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalProfit/TotalProfit.js b/client/src/pages/Admin/DashboardPage/components/TotalProfit/TotalProfit.js new file mode 100644 index 0000000..5f7519d --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalProfit/TotalProfit.js @@ -0,0 +1,69 @@ +import React from 'react'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import { makeStyles } from '@material-ui/styles'; +import { Card, CardContent, Grid, Typography, Avatar } from '@material-ui/core'; +import AttachMoneyIcon from '@material-ui/icons/AttachMoney'; + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%', + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText + }, + content: { + alignItems: 'center', + display: 'flex' + }, + title: { + fontWeight: 700 + }, + avatar: { + backgroundColor: theme.palette.white, + color: theme.palette.primary.main, + height: 56, + width: 56 + }, + icon: { + height: 32, + width: 32 + } +})); + +const TotalProfit = props => { + const { className } = props; + + const classes = useStyles(); + + return ( + + + + + + TOTAL PROFIT + + + $23,200 + + + + + + + + + + + ); +}; + +TotalProfit.propTypes = { + className: PropTypes.string +}; + +export default TotalProfit; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalProfit/index.js b/client/src/pages/Admin/DashboardPage/components/TotalProfit/index.js new file mode 100644 index 0000000..756f00c --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalProfit/index.js @@ -0,0 +1 @@ +export { default } from './TotalProfit'; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalReservations/TotalReservations.js b/client/src/pages/Admin/DashboardPage/components/TotalReservations/TotalReservations.js new file mode 100644 index 0000000..b365338 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalReservations/TotalReservations.js @@ -0,0 +1,69 @@ +import React from 'react'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import { makeStyles } from '@material-ui/styles'; +import { Card, CardContent, Grid, Typography, Avatar } from '@material-ui/core'; +import EventIcon from '@material-ui/icons/Event'; + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%', + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText + }, + content: { + alignItems: 'center', + display: 'flex' + }, + title: { + fontWeight: 700 + }, + avatar: { + backgroundColor: theme.palette.white, + color: theme.palette.primary.main, + height: 56, + width: 56 + }, + icon: { + height: 32, + width: 32 + } +})); + +const TotalReservations = props => { + const { className, reservations } = props; + + const classes = useStyles(); + + return ( + + + + + + TOTAL RESERVATIONS + + + {reservations} + + + + + + + + + + + ); +}; + +TotalReservations.propTypes = { + className: PropTypes.string +}; + +export default TotalReservations; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalReservations/index.js b/client/src/pages/Admin/DashboardPage/components/TotalReservations/index.js new file mode 100644 index 0000000..019eb48 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalReservations/index.js @@ -0,0 +1 @@ +export { default } from './TotalReservations'; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalUsers/TotalUsers.js b/client/src/pages/Admin/DashboardPage/components/TotalUsers/TotalUsers.js new file mode 100644 index 0000000..73bf33a --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalUsers/TotalUsers.js @@ -0,0 +1,86 @@ +import React from 'react'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import { makeStyles } from '@material-ui/styles'; +import { Card, CardContent, Grid, Typography, Avatar } from '@material-ui/core'; +import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward'; +import PeopleIcon from '@material-ui/icons/PeopleOutlined'; + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%' + }, + content: { + alignItems: 'center', + display: 'flex' + }, + title: { + fontWeight: 700 + }, + avatar: { + backgroundColor: theme.palette.success.main, + height: 56, + width: 56 + }, + icon: { + height: 32, + width: 32 + }, + difference: { + marginTop: theme.spacing(2), + display: 'flex', + alignItems: 'center' + }, + differenceIcon: { + color: theme.palette.success.dark + }, + differenceValue: { + color: theme.palette.success.dark, + marginRight: theme.spacing(1) + } +})); + +const TotalUsers = props => { + const { className, users } = props; + + const classes = useStyles(); + + return ( + + + + + + TOTAL USERS + + {users} + + + + + + + + + + + 16% + + + Since last month + + + + + ); +}; + +TotalUsers.propTypes = { + className: PropTypes.string +}; + +export default TotalUsers; diff --git a/client/src/pages/Admin/DashboardPage/components/TotalUsers/index.js b/client/src/pages/Admin/DashboardPage/components/TotalUsers/index.js new file mode 100644 index 0000000..99ed27b --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/TotalUsers/index.js @@ -0,0 +1 @@ +export { default } from './TotalUsers'; diff --git a/client/src/pages/Admin/DashboardPage/components/UsersByDevice/UsersByDevice.js b/client/src/pages/Admin/DashboardPage/components/UsersByDevice/UsersByDevice.js new file mode 100644 index 0000000..bac0fd3 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/UsersByDevice/UsersByDevice.js @@ -0,0 +1,142 @@ +import React from 'react'; +import { Doughnut } from 'react-chartjs-2'; +import classnames from 'classnames'; +import PropTypes from 'prop-types'; +import { makeStyles, useTheme } from '@material-ui/styles'; +import { + Card, + CardHeader, + CardContent, + IconButton, + Divider, + Typography +} from '@material-ui/core'; +import LaptopMacIcon from '@material-ui/icons/LaptopMac'; +import PhoneIphoneIcon from '@material-ui/icons/PhoneIphone'; +import RefreshIcon from '@material-ui/icons/Refresh'; +import TabletMacIcon from '@material-ui/icons/TabletMac'; + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%' + }, + chartContainer: { + position: 'relative', + height: '300px' + }, + stats: { + marginTop: theme.spacing(2), + display: 'flex', + justifyContent: 'center' + }, + device: { + textAlign: 'center', + padding: theme.spacing(1) + }, + deviceIcon: { + color: theme.palette.icon + } +})); + +const UsersByDevice = props => { + const { className, ...rest } = props; + + const classes = useStyles(); + const theme = useTheme(); + + const data = { + datasets: [ + { + data: [63, 15, 22], + backgroundColor: [ + theme.palette.primary.main, + theme.palette.error.main, + theme.palette.warning.main + ], + borderWidth: 8, + borderColor: theme.palette.white, + hoverBorderColor: theme.palette.white + } + ], + labels: ['Desktop', 'Tablet', 'Mobile'] + }; + + const options = { + legend: { + display: false + }, + responsive: true, + maintainAspectRatio: false, + animation: false, + cutoutPercentage: 80, + layout: { padding: 0 }, + tooltips: { + enabled: true, + mode: 'index', + intersect: false, + borderWidth: 1, + borderColor: theme.palette.divider, + backgroundColor: theme.palette.white, + titleFontColor: theme.palette.text.primary, + bodyFontColor: theme.palette.text.secondary, + footerFontColor: theme.palette.text.secondary + } + }; + + const devices = [ + { + title: 'Desktop', + value: '63', + icon: , + color: theme.palette.primary.main + }, + { + title: 'Tablet', + value: '15', + icon: , + color: theme.palette.error.main + }, + { + title: 'Mobile', + value: '23', + icon: , + color: theme.palette.warning.main + } + ]; + + return ( + + + + + } + title="Users By Device" + /> + + + + + + + {devices.map(device => ( + + {device.icon} + {device.title} + + {device.value}% + + + ))} + + + + ); +}; + +UsersByDevice.propTypes = { + className: PropTypes.string +}; + +export default UsersByDevice; diff --git a/client/src/pages/Admin/DashboardPage/components/UsersByDevice/index.js b/client/src/pages/Admin/DashboardPage/components/UsersByDevice/index.js new file mode 100644 index 0000000..95465d2 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/UsersByDevice/index.js @@ -0,0 +1 @@ +export { default } from './UsersByDevice'; diff --git a/client/src/pages/Admin/DashboardPage/components/index.js b/client/src/pages/Admin/DashboardPage/components/index.js new file mode 100644 index 0000000..d34a348 --- /dev/null +++ b/client/src/pages/Admin/DashboardPage/components/index.js @@ -0,0 +1,6 @@ +export { default as TotalUsers } from './TotalUsers'; +export { default as TotalCinemas } from './TotalCinemas'; +export { default as TotalMovies } from './TotalMovies'; +export { default as TotalReservations } from './TotalReservations'; +export { default as LatestSales } from './LatestSales'; +export { default as UsersByDevice } from './UsersByDevice'; diff --git a/client/src/theme/palette.js b/client/src/theme/palette.js index 34e5411..e542f3c 100644 --- a/client/src/theme/palette.js +++ b/client/src/theme/palette.js @@ -1,4 +1,4 @@ -import { red, blue, yellow, green } from '@material-ui/core/colors'; +import { red, blue, blueGrey, yellow, green } from '@material-ui/core/colors'; const white = '#FFF'; const black = '#000'; @@ -56,7 +56,11 @@ export default { dark: red[700], contrastText: white }, - + text: { + primary: blueGrey[900], + secondary: blueGrey[600], + link: blue[600] + }, background: { default: '#f8fafc', dark: 'rgb(26, 26, 26)',