├── .env
├── .gitignore
├── README.md
├── debug.log
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── font
│ ├── apercu-bold.woff
│ ├── apercu-medium.woff
│ └── apercu-regular.woff
├── img
│ └── customers.png
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
├── md-img
│ ├── board.png
│ ├── inventory.png
│ ├── manage-order.png
│ ├── order-control.png
│ ├── orders.png
│ ├── settings.png
│ └── teams.png
└── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── Board
├── Board.js
├── Column.js
└── Post.js
├── Common
├── AnimalAvatars.js
├── FieldModal.js
├── FieldRow.js
├── Loading.js
├── PageTitle.js
├── PrivateRoute.js
├── TableBuilder.js
├── WarningModal.js
└── img
│ ├── avatars
│ ├── butterfly.png
│ ├── deer.png
│ ├── elephant.png
│ ├── fox.png
│ ├── frog.png
│ ├── giraffe.png
│ ├── hamster.png
│ ├── lion.png
│ ├── monkey.png
│ ├── mouse.png
│ ├── octopus.png
│ ├── panda.png
│ ├── pig.png
│ ├── reindeer.png
│ ├── rhino.png
│ └── shark.png
│ ├── board.png
│ ├── change-password.png
│ ├── construction.png
│ ├── manage-accounts.png
│ ├── new-product.png
│ ├── orders.png
│ ├── product-information.png
│ ├── product-stock.png
│ └── shopcast.svg
├── Home
├── Card.js
└── Home.js
├── Inventory
├── CreateProduct.js
├── CreateProductForm.js
├── EmptyInventory.js
├── Form
│ ├── Basics.js
│ └── Media.js
├── Inventory.js
├── InventoryItem.js
├── ProductModal.js
├── RemoveProduct.js
├── SearchModal.js
├── UpdateProduct.js
└── UpdateProductStock.js
├── Login
├── ErrorMessage.js
├── Login.js
└── LoginCard.js
├── Orders
├── DraftOrder.js
├── Manage
│ ├── Manage.js
│ ├── Metadata.js
│ └── New.js
├── Orders.js
├── OrdersStats.js
├── OrdersTable.js
├── Search.js
└── Sidebar.js
├── Settings
├── SettingTabs.js
├── Settings.js
├── TabButton.js
└── Views
│ ├── Account.js
│ ├── Notifications.js
│ ├── Password.js
│ └── UserGroups.js
├── Team
├── Member.js
├── Profile.js
└── Team.js
├── index.css
├── index.js
├── logo.svg
├── serviceWorker.js
├── store
├── actions
│ ├── auth.js
│ ├── products.js
│ └── types
│ │ └── types.js
└── reducers
│ ├── auth.js
│ ├── message.js
│ ├── product.js
│ └── rootReducer.js
└── utils
├── getTokenTimeRemaining.js
└── setAuthorizationToken.js
/.env:
--------------------------------------------------------------------------------
1 | PORT=3000
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ecommerce Dashboard Frontend
2 | ## Board
3 | 
4 |
5 | ## Teams
6 | 
7 |
8 | ## View Orders
9 | 
10 |
11 | ## Manage Order
12 | 
13 |
14 | ## Settings
15 | 
16 |
17 | ## Inventory 1
18 | 
19 |
20 | # Details
21 | - Built with create-react-app (npm run start/dev).
22 | - Core UI library is Material-UI.
23 | - There is state management for managing user sessions via JWT.
24 | - This is only a front end service, you will need to deploy the backend service as well.
--------------------------------------------------------------------------------
/debug.log:
--------------------------------------------------------------------------------
1 | [0827/122859.047:ERROR:crash_report_database_win.cc(469)] failed to stat report
2 | [0828/123901.964:ERROR:crash_report_database_win.cc(469)] failed to stat report
3 | [0828/193124.670:ERROR:crash_report_database_win.cc(469)] failed to stat report
4 | [0829/160303.694:ERROR:crash_report_database_win.cc(469)] failed to stat report
5 | [0829/183050.122:ERROR:crash_report_database_win.cc(469)] failed to stat report
6 | [0830/011400.286:ERROR:crash_report_database_win.cc(469)] failed to stat report
7 | [0830/011400.286:ERROR:crash_report_database_win.cc(469)] failed to stat report
8 | [0830/154404.160:ERROR:crash_report_database_win.cc(469)] failed to stat report
9 | [0830/154404.161:ERROR:crash_report_database_win.cc(469)] failed to stat report
10 | [0831/125110.278:ERROR:crash_report_database_win.cc(469)] failed to stat report
11 | [0831/125110.279:ERROR:crash_report_database_win.cc(469)] failed to stat report
12 | [0831/173628.955:ERROR:crash_report_database_win.cc(469)] failed to stat report
13 | [0831/173628.956:ERROR:crash_report_database_win.cc(469)] failed to stat report
14 | [0901/122449.896:ERROR:crash_report_database_win.cc(469)] failed to stat report
15 | [0901/122449.896:ERROR:crash_report_database_win.cc(469)] failed to stat report
16 | [0903/120725.284:ERROR:crash_report_database_win.cc(469)] failed to stat report
17 | [0903/120725.285:ERROR:crash_report_database_win.cc(469)] failed to stat report
18 | [0910/133514.933:ERROR:crash_report_database_win.cc(469)] failed to stat report
19 | [0910/133514.933:ERROR:crash_report_database_win.cc(469)] failed to stat report
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecommerce-dashboard-react",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.10.0",
7 | "@material-ui/icons": "^4.4.1",
8 | "@material-ui/lab": "^4.0.0-alpha.54",
9 | "axios": "^0.19.0",
10 | "faker": "^4.1.0",
11 | "jwt-decode": "^2.2.0",
12 | "react": "16.8.0",
13 | "react-dom": "16.8.0",
14 | "react-redux": "^7.1.1",
15 | "react-router-dom": "^5.2.0",
16 | "react-scripts": "3.1.1",
17 | "react-useanimations": "^1.2.14",
18 | "redux": "^4.0.4",
19 | "redux-thunk": "^2.3.0"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": "react-app"
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/favicon.ico
--------------------------------------------------------------------------------
/public/font/apercu-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/font/apercu-bold.woff
--------------------------------------------------------------------------------
/public/font/apercu-medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/font/apercu-medium.woff
--------------------------------------------------------------------------------
/public/font/apercu-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/font/apercu-regular.woff
--------------------------------------------------------------------------------
/public/img/customers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/img/customers.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
25 | React App
26 |
29 |
30 |
31 |
32 |
33 |
34 |
44 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/md-img/board.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/md-img/board.png
--------------------------------------------------------------------------------
/public/md-img/inventory.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/md-img/inventory.png
--------------------------------------------------------------------------------
/public/md-img/manage-order.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/md-img/manage-order.png
--------------------------------------------------------------------------------
/public/md-img/order-control.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/md-img/order-control.png
--------------------------------------------------------------------------------
/public/md-img/orders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/md-img/orders.png
--------------------------------------------------------------------------------
/public/md-img/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/md-img/settings.png
--------------------------------------------------------------------------------
/public/md-img/teams.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/public/md-img/teams.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Apercu';
3 | src: url('/font/apercu-regular.woff') format('woff');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | @font-face {
9 | font-family: 'ApercuBold';
10 | src: url('/font/apercu-bold.woff') format('woff');
11 | font-weight: normal;
12 | font-style: normal;
13 | }
14 |
15 | @font-face {
16 | font-family: 'ApercuMedium';
17 | src: url('/font/apercu-medium.woff') format('woff');
18 | font-weight: normal;
19 | font-style: normal;
20 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { Switch, Route, Link, BrowserRouter, Redirect } from "react-router-dom";
3 | import { connect } from "react-redux";
4 | import PropTypes from "prop-types";
5 | import { userSignOutRequest } from "./store/actions/auth";
6 |
7 | import { makeStyles, createMuiTheme } from "@material-ui/core/styles";
8 | import { ThemeProvider } from "@material-ui/styles";
9 | import blue from "@material-ui/core/colors/blue";
10 | import {
11 | Avatar,
12 | Modal,
13 | Backdrop,
14 | Fade,
15 | Typography,
16 | Box,
17 | Button,
18 | Paper,
19 | Tabs,
20 | Tab,
21 | } from "@material-ui/core";
22 | import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
23 |
24 | import Orders from "./Orders/Orders";
25 | import Home from "./Home/Home";
26 | import Inventory from "./Inventory/Inventory";
27 | import Settings from "./Settings/Settings";
28 | import Board from "./Board/Board";
29 | import Login from "./Login/Login";
30 | import Team from "./Team/Team";
31 |
32 | import PrivateRoute from "./Common/PrivateRoute";
33 | import getAvatar from "./Common/AnimalAvatars";
34 |
35 | import "./App.css";
36 | import MonkeyAvatar from "../src/Common/img/avatars/monkey.png";
37 |
38 | const useStyles = makeStyles({
39 | root: {
40 | flexGrow: 1,
41 | zIndex: 3000,
42 | boxShadow: "0 10px 20px rgba(0,0,0,0.025), 0 2px 2px rgba(0,0,0,0.05)",
43 | borderRadius: "0px",
44 | padding: "2px 0px 0px 5px",
45 | overflow: "hidden",
46 | // backgroundColor: '#1a237e',
47 | },
48 | tab: {
49 | fontFamily: "ApercuMedium",
50 | textTransform: "none",
51 | // color: '#fff',
52 | color: "#525f7f",
53 | fontSize: "0.85rem",
54 | },
55 | avatar: {
56 | width: 35,
57 | height: 35,
58 | backgroundColor: "none",
59 | },
60 | avatarGroup: {
61 | position: "absolute",
62 | top: "0.5rem",
63 | left: "calc(100% - 75px)",
64 | color: "rgba(0,0,0,0.54)",
65 | "&:hover": {
66 | cursor: "pointer",
67 | color: blue[500],
68 | },
69 | },
70 | paper: {
71 | backgroundColor: "#fff",
72 | // boxShadow: theme.shadows[5],
73 | boxShadow: "0 20px 60px -2px rgba(27,33,58,.4)",
74 | padding: "2em",
75 | outline: "none",
76 | borderRadius: "8px",
77 | },
78 | modal: {
79 | display: "flex",
80 | alignItems: "center",
81 | justifyContent: "center",
82 | },
83 | title: {
84 | fontFamily: "ApercuMedium",
85 | marginTop: "2em",
86 | marginBottom: "2em",
87 | },
88 | button: {
89 | boxShadow: "none",
90 | },
91 | avatarIcon: {
92 | marginLeft: 5,
93 | marginTop: 5,
94 | },
95 | });
96 |
97 | const theme = createMuiTheme({
98 | palette: {
99 | primary: {
100 | light: blue[300],
101 | main: blue[500],
102 | dark: blue[700],
103 | },
104 | },
105 | typography: {
106 | fontFamily: [
107 | "Apercu",
108 | "-apple-system",
109 | "BlinkMacSystemFont",
110 | '"Segoe UI"',
111 | "Roboto",
112 | '"Helvetica Neue"',
113 | "Arial",
114 | "sans-serif",
115 | '"Apple Color Emoji"',
116 | '"Segoe UI Emoji"',
117 | '"Segoe UI Symbol"',
118 | ].join(","),
119 | },
120 | overrides: {
121 | MuiOutlinedInput: {
122 | root: {
123 | "&:hover:not($disabled):not($focused):not($error) $notchedOutline": {
124 | border: "2px solid",
125 | borderColor: blue[200],
126 | },
127 | },
128 | },
129 | MuiTableCell: {
130 | root: {
131 | borderBottom: "none",
132 | },
133 | },
134 | },
135 | });
136 |
137 | function App(props) {
138 | const classes = useStyles();
139 |
140 | const { isAuthenticated, user } = props.auth;
141 |
142 | const [logoutModal, setLogoutModal] = React.useState(false);
143 |
144 | const RedirectToDashboard = () => (
145 |
146 | {isAuthenticated ? : null}
147 |
148 | );
149 |
150 | const handleLogoutOpen = () => {
151 | setLogoutModal(true);
152 | };
153 |
154 | const handleLogoutClose = () => {
155 | setLogoutModal(false);
156 | };
157 |
158 | const logout = (e) => {
159 | e.preventDefault();
160 | handleLogoutClose();
161 | props.userSignOutRequest();
162 | };
163 |
164 | const AvatarGroup = (props) => {
165 | return (
166 |
172 |
173 |
174 |
175 | );
176 | };
177 |
178 | return (
179 |
180 |
181 |
182 |
183 |
184 |
(
187 |
188 |
189 |
195 |
203 |
211 |
219 |
227 |
235 |
243 |
244 |
245 |
246 |
247 | Inventory Wizard
}
251 | />
252 |
253 |
254 |
255 |
260 |
265 |
266 |
267 | )}
268 | />
269 | {isAuthenticated ? : null}
270 |
271 |
283 |
284 |
285 |
286 | Are you sure you want to sign out?
287 |
288 |
293 |
300 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 | );
316 | }
317 |
318 | App.propTypes = {
319 | auth: PropTypes.object.isRequired,
320 | userSignOutRequest: PropTypes.func.isRequired,
321 | };
322 |
323 | function mapStateToProps(state) {
324 | return {
325 | auth: state.auth,
326 | };
327 | }
328 |
329 | export default connect(mapStateToProps, { userSignOutRequest })(App);
330 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/Board/Board.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { makeStyles, useTheme } from '@material-ui/core/styles';
3 | import Container from '@material-ui/core/Container';
4 | import { Typography, Grid } from '@material-ui/core';
5 |
6 | import Column from './Column';
7 | import PageTitle from './../Common/PageTitle';
8 |
9 | const useStyles = makeStyles(theme => ({
10 | title: {
11 | fontFamily: 'ApercuMedium',
12 | marginTop: theme.spacing(4),
13 | marginBottom: theme.spacing(4),
14 | color: '#525f7f'
15 | },
16 | }));
17 |
18 | export default function Board() {
19 | const classes = useStyles();
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | }
--------------------------------------------------------------------------------
/src/Board/Column.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Container from "@material-ui/core/Container";
4 | import { Typography } from "@material-ui/core";
5 | import Paper from "@material-ui/core/Paper";
6 | import Button from "@material-ui/core/Button";
7 | import Add from "@material-ui/icons/Add";
8 | import Grid from "@material-ui/core/Grid";
9 | import blue from "@material-ui/core/colors/blue";
10 |
11 | import Post from "./Post";
12 |
13 | const useStyles = makeStyles((theme) => ({
14 | paper: {
15 | border: "1px solid #e3f2fd",
16 | boxShadow: "0 0 11px #eaf0f6",
17 | marginBottom: "0.5rem",
18 | "&:hover": {
19 | backgroundColor: "#e3f2fd",
20 | color: "white",
21 | },
22 | },
23 | button: {
24 | backgroundColor: blue[100],
25 | "&:hover": {
26 | backgroundColor: theme.palette.primary.main,
27 | "& $addIcon": {
28 | color: "white",
29 | },
30 | },
31 | },
32 | addIcon: {
33 | color: theme.palette.primary.main,
34 | },
35 | }));
36 |
37 | const posts = [
38 | {
39 | id: 12,
40 | title: "Review Fall/Winter Product Launch",
41 | content: "Meeting for Adidas 112, Bonobos 325, Calvin Klein 42.",
42 | date: "Oct 8",
43 | },
44 | {
45 | id: 43,
46 | title: "Organize September 15 Incoming Shipment",
47 | content: "Expecting Hershal, Banana Republic top up.",
48 | date: "Sep 15",
49 | },
50 | ];
51 |
52 | export default function Board(props) {
53 | const classes = useStyles();
54 |
55 | return (
56 |
57 |
62 | {props.name}
63 |
64 |
65 |
68 |
69 |
76 | {posts.map((post) => (
77 |
78 |
79 |
80 | ))}
81 |
82 |
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/src/Board/Post.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import clsx from "clsx";
4 | import Card from "@material-ui/core/Card";
5 | import CardHeader from "@material-ui/core/CardHeader";
6 | import CardMedia from "@material-ui/core/CardMedia";
7 | import CardContent from "@material-ui/core/CardContent";
8 | import CardActions from "@material-ui/core/CardActions";
9 | import Button from "@material-ui/core/Button";
10 | import Avatar from "@material-ui/core/Avatar";
11 | import IconButton from "@material-ui/core/IconButton";
12 | import Typography from "@material-ui/core/Typography";
13 | import { red } from "@material-ui/core/colors";
14 | import CommentIcon from "@material-ui/icons/Comment";
15 | import ThumbUpIcon from "@material-ui/icons/ThumbUp";
16 | import ExpandMoreIcon from "@material-ui/icons/MoreHoriz";
17 |
18 | import getAvatar from "../Common/AnimalAvatars";
19 |
20 | const useStyles = makeStyles((theme) => ({
21 | card: {
22 | border: "1px solid #eaf0f6",
23 | boxShadow: "0 0 11px #eaf0f6",
24 | },
25 | media: {
26 | height: 0,
27 | paddingTop: "56.25%", // 16:9
28 | },
29 | expand: {
30 | marginLeft: "auto",
31 | },
32 | expandOpen: {
33 | transform: "rotate(180deg)",
34 | },
35 | avatar: {},
36 | rightIcon: {
37 | marginLeft: theme.spacing(1),
38 | },
39 | button: {
40 | marginLeft: "auto",
41 | },
42 | }));
43 |
44 | export default function Post(props) {
45 | const classes = useStyles();
46 |
47 | return (
48 |
49 |
56 | }
57 | action={
58 |
59 |
60 |
61 | }
62 | title={props.title}
63 | subheader={props.date}
64 | />
65 |
66 |
67 | {props.content}
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
79 |
80 |
81 | );
82 | }
83 |
--------------------------------------------------------------------------------
/src/Common/AnimalAvatars.js:
--------------------------------------------------------------------------------
1 | import Butterfly from "../Common/img/avatars/butterfly.png";
2 | import Deer from "../Common/img/avatars/deer.png";
3 | import Elephant from "../Common/img/avatars/elephant.png";
4 | import Fox from "../Common/img/avatars/fox.png";
5 | import Frog from "../Common/img/avatars/frog.png";
6 | import Giraffe from "../Common/img/avatars/giraffe.png";
7 | import Hamster from "../Common/img/avatars/hamster.png";
8 | import Lion from "../Common/img/avatars/lion.png";
9 | import Monkey from "../Common/img/avatars/monkey.png";
10 | import Mouse from "../Common/img/avatars/mouse.png";
11 | import Octopus from "../Common/img/avatars/octopus.png";
12 | import Panda from "../Common/img/avatars/panda.png";
13 | import Pig from "../Common/img/avatars/pig.png";
14 | import Reindeer from "../Common/img/avatars/reindeer.png";
15 | import Rhino from "../Common/img/avatars/rhino.png";
16 | import Shark from "../Common/img/avatars/shark.png";
17 |
18 | const animals = [
19 | Butterfly,
20 | Deer,
21 | Elephant,
22 | Fox,
23 | Frog,
24 | Giraffe,
25 | Hamster,
26 | Lion,
27 | Monkey,
28 | Mouse,
29 | Octopus,
30 | Panda,
31 | Pig,
32 | Reindeer,
33 | Rhino,
34 | Shark,
35 | ];
36 |
37 | export default function getAvatar(index) {
38 | index =
39 | (index < animals.length ? index : null) ||
40 | Math.floor(Math.random() * animals.length);
41 | return animals[index];
42 | }
43 |
--------------------------------------------------------------------------------
/src/Common/FieldModal.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles, TextField, Container, Typography, Divider, Box, IconButton, Button } from '@material-ui/core';
3 | import ArrowBack from '@material-ui/icons/ArrowBack';
4 |
5 | const useStyles = makeStyles(theme => ({
6 | modal: {
7 | display: 'flex',
8 | alignItems: 'center',
9 | justifyContent: 'center',
10 | },
11 | paper: {
12 | backgroundColor: theme.palette.background.paper,
13 | boxShadow: '0 20px 60px -2px rgba(27,33,58,.4)',
14 | padding: theme.spacing(2, 4, 3),
15 | borderRadius: '8px',
16 | minWidth: 550
17 | },
18 | textfield: {
19 |
20 | },
21 | label: {
22 | marginTop: 8
23 | },
24 | button: {
25 | boxShadow: 'none',
26 | marginLeft: '1rem'
27 | }
28 | }));
29 |
30 | export default function FieldModal(props) {
31 | const classes = useStyles();
32 |
33 | const [value, setValue] = React.useState(props.value);
34 |
35 | const handleChange = (e) => {
36 | setValue(e.target.value);
37 | }
38 |
39 | function Input() {
40 | if (props.variant === 'textarea') {
41 | return (
42 |
54 | )
55 | } else {
56 | return (
57 |
67 | )
68 | }
69 | }
70 |
71 | return (
72 |
73 |
74 |
75 |
76 |
77 | {props.label}
78 |
79 |
80 |
81 |
82 |
85 |
88 |
89 |
90 | );
91 | }
--------------------------------------------------------------------------------
/src/Common/FieldRow.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 |
4 | import { Grid, Typography, Divider, Box } from '@material-ui/core';
5 | import { makeStyles } from '@material-ui/core/styles';
6 | import Edit from '@material-ui/icons/KeyboardArrowRight';
7 |
8 | const useStyles = makeStyles(theme => ({
9 | label: {
10 | letterSpacing: '.07272727em',
11 | fontSize: '.6875rem',
12 | fontWeight: 500,
13 | lineHeight: '1rem',
14 | textTransform: 'uppercase',
15 | color: '#5f6368',
16 | marginLeft: '10px',
17 | },
18 | hover: {
19 | // margin: theme.spacing(1),
20 | '&:hover': {
21 | backgroundColor: '#f5f5f5',
22 | cursor: 'pointer'
23 | }
24 | },
25 | row: {
26 | paddingTop: theme.spacing(1),
27 | },
28 | icon: {
29 | color: '#5f6368',
30 | }
31 | }));
32 |
33 | const FieldRow = (props) => {
34 | const classes = useStyles();
35 |
36 | const handleClick = () => {
37 | if (props.openModal) {
38 | props.openModal({
39 | label: props.label,
40 | value: props.value
41 | });
42 | }
43 | }
44 |
45 | return (
46 |
47 |
48 |
49 |
50 | {props.label}
51 |
52 |
53 |
54 |
55 | {props.value}
56 |
57 |
58 |
59 |
60 | {props.openModal ? : null}
61 |
62 |
63 |
64 |
65 |
66 | )
67 | }
68 |
69 | export default FieldRow;
--------------------------------------------------------------------------------
/src/Common/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grid from '@material-ui/core/Grid';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import CircularProgress from '@material-ui/core/CircularProgress';
5 |
6 | const useStyles = makeStyles(theme => ({
7 | progress: {
8 | margin: theme.spacing(4),
9 | },
10 | }));
11 |
12 | export default function Loading(props) {
13 | const classes = useStyles();
14 |
15 | if (props.visible) {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | )
23 | } else {
24 | return null;
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Common/PageTitle.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useHistory } from "react-router-dom";
3 |
4 | import { makeStyles, Box, IconButton, Typography } from "@material-ui/core";
5 | import { ArrowBack } from "@material-ui/icons";
6 |
7 | const useStyles = makeStyles((theme) => ({
8 | container: {
9 | marginTop: theme.spacing(3),
10 | marginBottom: theme.spacing(3),
11 | },
12 | title: {
13 | fontFamily: "ApercuMedium",
14 | color: "#525f7f",
15 | marginTop: 4,
16 | marginLeft: 4,
17 | },
18 | }));
19 |
20 | export default function PageTitle(props) {
21 | const classes = useStyles();
22 | const history = useHistory();
23 |
24 | const changeRoute = () => {
25 | const route = props.route ? props.route : "/dashboard/home";
26 | history.push(route);
27 |
28 | // Temporary workaround to allow another function to be passed via onClick prop
29 | if (props.onClick !== undefined) {
30 | props.onClick.call(this);
31 | }
32 | };
33 |
34 | return (
35 |
42 |
43 |
44 |
45 |
46 | {props.title}
47 |
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/src/Common/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, Redirect } from "react-router-dom";
3 |
4 | export default function PrivateRoute({ component: Component, authed, ...rest }) {
5 | return (
6 | authed === true
9 | ?
10 | : }
11 | />
12 | )
13 | }
--------------------------------------------------------------------------------
/src/Common/TableBuilder.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import clsx from "clsx";
3 |
4 | import {
5 | Table,
6 | TableHead,
7 | TableRow,
8 | TableCell,
9 | TableBody,
10 | Box,
11 | IconButton,
12 | Typography,
13 | makeStyles,
14 | Paper,
15 | } from "@material-ui/core";
16 |
17 | import UnfoldMoreIcon from "@material-ui/icons/UnfoldMore";
18 | import KeyboardArrowRightIcon from "@material-ui/icons/KeyboardArrowRight";
19 | import blue from "@material-ui/core/colors/blue";
20 |
21 | const useStyles = makeStyles((theme) => ({
22 | tableHeadRow: {
23 | backgroundColor: "#fff",
24 | borderColor: "#fff",
25 | borderStyle: "solid",
26 | borderLeftWidth: "3px",
27 | },
28 | tableHeadAvatar: {
29 | width: "50px",
30 | },
31 | headerCell: {
32 | padding: theme.spacing(0),
33 | },
34 | headerText: {
35 | fontFamily: "ApercuMedium",
36 | fontSize: "0.875rem",
37 | color: "#525f7f",
38 | paddingLeft: theme.spacing(2),
39 | paddingTop: theme.spacing(2),
40 | paddingBottom: theme.spacing(2),
41 | },
42 | row: {
43 | borderColor: "#fff",
44 | borderStyle: "solid",
45 | borderLeftWidth: "3px",
46 | borderBottomWidth: "0px",
47 | borderTopWidth: "0px",
48 | borderRightWidth: "3px",
49 | "&:hover": {
50 | borderColor: theme.palette.primary.light,
51 | borderStyle: "solid",
52 | borderLeftWidth: "3px",
53 | backgroundColor: blue[50],
54 | borderRightColor: blue[50],
55 | },
56 | },
57 | root: {
58 | boxShadow: "0 0 11px #eaf0f6",
59 | borderRadius: "4px",
60 | overflow: "hidden",
61 | border: "1px solid #eaf0f6",
62 | },
63 | }));
64 |
65 | const TableBuilder = (props) => {
66 | const classes = useStyles();
67 |
68 | const { data } = props;
69 |
70 | const getKeys = () => {
71 | return Object.keys(data[0]);
72 | };
73 |
74 | const getHeader = () => {
75 | const keys = getKeys();
76 |
77 | return keys.map((key, index) => {
78 | return (
79 |
80 |
81 | {key}
82 |
83 |
84 |
85 |
86 |
87 | );
88 | });
89 | };
90 |
91 | const RenderRow = (props) => {
92 | return props.keys.map((key, index) => {
93 | return {props.data[key]};
94 | });
95 | };
96 |
97 | const getRowsData = () => {
98 | const keys = getKeys();
99 | return data.map((row, index) => {
100 | return (
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | );
110 | });
111 | };
112 |
113 | return (
114 |
115 |
116 |
117 |
118 | {getHeader()}
119 |
120 | {/*
121 |
122 | Name
123 |
124 |
127 |
128 |
129 | */}
130 |
131 |
132 |
133 |
134 |
135 | {getRowsData()}
136 | {/*
137 |
138 |
139 |
140 |
141 |
142 | */}
143 |
144 |
145 |
146 | );
147 | };
148 |
149 | export default TableBuilder;
150 |
--------------------------------------------------------------------------------
/src/Common/WarningModal.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import UseAnimations from "react-useanimations";
4 | import {
5 | Modal,
6 | Fade,
7 | Box,
8 | Typography,
9 | Backdrop,
10 | Button,
11 | } from "@material-ui/core";
12 |
13 | const useStyles = makeStyles((theme) => ({
14 | modal: {
15 | display: "flex",
16 | alignItems: "center",
17 | justifyContent: "center",
18 | },
19 | paper: {
20 | backgroundColor: theme.palette.background.paper,
21 | boxShadow: "0 20px 60px -2px rgba(27,33,58,.4)",
22 | padding: theme.spacing(2, 4, 3),
23 | outline: "none",
24 | borderRadius: "8px",
25 | maxWidth: "450px",
26 | },
27 | title: {
28 | fontFamily: "ApercuMedium",
29 | fontSize: "1.5em",
30 | marginLeft: theme.spacing(2),
31 | color: "#b71c1c",
32 | },
33 | icon: {
34 | color: "#b71c1c",
35 | marginBottom: "-4px",
36 | },
37 | circle: {
38 | backgroundColor: "#ffcdd2",
39 | width: "64px",
40 | height: "64px",
41 | borderRadius: "50%",
42 | display: "flex",
43 | justifyContent: "center",
44 | alignItems: "center",
45 | },
46 | text: {
47 | marginTop: theme.spacing(2),
48 | },
49 | actionBox: {
50 | marginTop: theme.spacing(4),
51 | },
52 | abandon: {
53 | marginLeft: theme.spacing(2),
54 | paddingLeft: theme.spacing(2),
55 | paddingRight: theme.spacing(2),
56 | backgroundColor: "#ffcdd2",
57 | color: "#b71c1c",
58 | // margin: theme.spacing(1),
59 | borderRadius: "30px",
60 | "&:hover": {
61 | backgroundColor: "#b71c1c",
62 | color: "white",
63 | },
64 | },
65 | }));
66 |
67 | export default function WarningModal(props) {
68 | const { open, setOpen, quit } = props;
69 |
70 | const defaultDetails = {
71 | animationKey: "alertCircle",
72 | title: "Modal Title",
73 | subtitle: ["Subtitle1", "Subtitle2"],
74 | action: "Quit",
75 | };
76 |
77 | const details = props.details || defaultDetails;
78 |
79 | const handleClose = () => {
80 | setOpen(false);
81 | };
82 |
83 | const classes = useStyles();
84 |
85 | return (
86 |
98 |
99 |
100 |
101 |
102 |
107 |
108 | {details.title}
109 |
110 | {details.subtitle.map((text, key) => (
111 |
112 | {text}
113 |
114 | ))}
115 |
120 |
121 |
124 |
125 |
126 |
127 |
128 | );
129 | }
130 |
--------------------------------------------------------------------------------
/src/Common/img/avatars/butterfly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/butterfly.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/deer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/deer.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/elephant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/elephant.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/fox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/fox.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/frog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/frog.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/giraffe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/giraffe.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/hamster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/hamster.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/lion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/lion.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/monkey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/monkey.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/mouse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/mouse.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/octopus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/octopus.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/panda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/panda.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/pig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/pig.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/reindeer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/reindeer.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/rhino.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/rhino.png
--------------------------------------------------------------------------------
/src/Common/img/avatars/shark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/avatars/shark.png
--------------------------------------------------------------------------------
/src/Common/img/board.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/board.png
--------------------------------------------------------------------------------
/src/Common/img/change-password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/change-password.png
--------------------------------------------------------------------------------
/src/Common/img/construction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/construction.png
--------------------------------------------------------------------------------
/src/Common/img/manage-accounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/manage-accounts.png
--------------------------------------------------------------------------------
/src/Common/img/new-product.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/new-product.png
--------------------------------------------------------------------------------
/src/Common/img/orders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/orders.png
--------------------------------------------------------------------------------
/src/Common/img/product-information.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/product-information.png
--------------------------------------------------------------------------------
/src/Common/img/product-stock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/borisrch/ecommerce-dashboard-react/a111b8d2dc00889e38be622851bba5bb2e7a516a/src/Common/img/product-stock.png
--------------------------------------------------------------------------------
/src/Common/img/shopcast.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/Home/Card.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Card from '@material-ui/core/Card';
4 | import CardActionArea from '@material-ui/core/CardActionArea';
5 | import CardActions from '@material-ui/core/CardActions';
6 | import CardContent from '@material-ui/core/CardContent';
7 | import CardMedia from '@material-ui/core/CardMedia';
8 | import Button from '@material-ui/core/Button';
9 | import Typography from '@material-ui/core/Typography';
10 | import { Link } from 'react-router-dom';
11 |
12 | const useStyles = makeStyles({
13 | card: {
14 | maxWidth: 345,
15 | boxShadow: '0 0 1px 0 rgba(0,0,0,.22)',
16 | // boxShadow: '0 0.938em 1.588em rgba(50,50,93,.1), 0 0.313em 0.938em rgba(0,0,0,.07)'
17 | },
18 | media: {
19 | userSelect: 'none',
20 | pointerEvents: 'none',
21 | }
22 | });
23 |
24 | export default function ImgMediaCard(props) {
25 | const classes = useStyles();
26 |
27 | return (
28 |
29 |
36 |
37 |
38 | {props.title}
39 |
40 |
41 | {props.description}
42 |
43 |
44 |
45 |
46 |
49 |
50 |
51 | );
52 | }
--------------------------------------------------------------------------------
/src/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Container from '@material-ui/core/Container';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Card from './Card';
5 | import Grid from '@material-ui/core/Grid';
6 | import Typography from '@material-ui/core/Typography';
7 |
8 | import Orders from '../Common/img/orders.png';
9 | import Board from '../Common/img/board.png';
10 | import NewProduct from '../Common/img/new-product.png';
11 | import ChangePassword from '../Common/img/change-password.png';
12 | import ManageAccounts from '../Common/img/manage-accounts.png';
13 | import ProductInformation from '../Common/img/product-information.png';
14 | import ProductStock from '../Common/img/product-stock.png';
15 |
16 | const useStyles = makeStyles(theme => ({
17 | container: {
18 | marginBottom: '5em'
19 | },
20 | title: {
21 | fontFamily: 'ApercuMedium',
22 | marginTop: theme.spacing(4),
23 | marginBottom: theme.spacing(4),
24 | }
25 | }));
26 |
27 | function Home(props) {
28 | const [count, setCount] = useState(0);
29 | const classes = useStyles();
30 |
31 | return (
32 |
33 | Dashboards
34 |
35 |
36 |
43 |
44 |
45 |
52 |
53 |
54 |
55 | Inventory
56 |
57 |
58 |
65 |
66 |
67 |
74 |
75 |
76 |
83 |
84 |
85 |
86 | Account
87 |
88 |
89 |
96 |
97 |
98 |
105 |
106 |
107 |
108 | );
109 | }
110 |
111 | export default Home;
--------------------------------------------------------------------------------
/src/Inventory/CreateProduct.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Button from "@material-ui/core/Button";
3 | import { makeStyles } from "@material-ui/core/styles";
4 | import Icon from "@material-ui/core/Icon";
5 | import AddIcon from "@material-ui/icons/Add";
6 | import PublishIcon from "@material-ui/icons/Publish";
7 |
8 | const useStyles = makeStyles((theme) => ({
9 | button: {
10 | boxShadow: "none",
11 | fontFamily: "ApercuMedium",
12 | },
13 | leftIcon: {
14 | marginRight: theme.spacing(1),
15 | },
16 | rightIcon: {
17 | marginLeft: theme.spacing(1),
18 | },
19 | iconSmall: {
20 | fontSize: 20,
21 | },
22 | }));
23 |
24 | export default function CreateProduct(props) {
25 | const classes = useStyles();
26 |
27 | return (
28 |
29 |
39 |
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/src/Inventory/CreateProductForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Container, Box, makeStyles, Button, Stepper, Step, StepLabel, Grid } from "@material-ui/core";
3 |
4 | import Basics from './Form/Basics';
5 |
6 | const useStyles = makeStyles(theme => ({
7 | grid: {
8 | flexGrow: 1,
9 | marginBottom: theme.spacing(1),
10 | marginTop: theme.spacing(1)
11 | },
12 | box: {
13 | },
14 | root: {
15 | width: "80vw",
16 | overflow: "scroll"
17 | },
18 | formControl: {
19 | minWidth: 120,
20 | marginTop: theme.spacing(2)
21 | },
22 | button: {
23 | boxShadow: 'none',
24 | },
25 | content: {
26 | minHeight: 400,
27 | }
28 | }));
29 |
30 | function getSteps() {
31 | return ['Basics', 'Media', 'Pricing', 'Shipping', 'Inventory'];
32 | }
33 |
34 | export default function CreateProductForm() {
35 | const classes = useStyles();
36 |
37 | const [product, setProduct] = React.useState({
38 | name: '',
39 | brand: '',
40 | description: '',
41 | category: '',
42 | });
43 |
44 | function getStepContent(stepIndex) {
45 | switch (stepIndex) {
46 | case 0:
47 | return ;
48 | case 1:
49 | return 'What is an ad group anyways?';
50 | case 2:
51 | return 'This is the bit I really care about!';
52 | default:
53 | return 'Uknown stepIndex';
54 | }
55 | }
56 |
57 |
58 | const [activeStep, setActiveStep] = React.useState(0);
59 | const steps = getSteps();
60 |
61 | const handleNext = () => {
62 | setActiveStep(prevActiveStep => prevActiveStep + 1);
63 | };
64 |
65 | const handleBack = () => {
66 | setActiveStep(prevActiveStep => prevActiveStep - 1);
67 | };
68 |
69 | const handleReset = () => {
70 | setActiveStep(0);
71 | };
72 |
73 | return (
74 |
75 |
76 |
77 | {steps.map(label => (
78 |
79 | {label}
80 |
81 | ))}
82 |
83 |
84 |
85 | {getStepContent(activeStep)}
86 |
87 |
88 |
89 |
90 |
93 |
94 |
95 |
96 | {activeStep > 0 ? : null}
97 | {activeStep === steps.length - 1 ?
98 | (
99 |
102 | ) : (
103 |
106 | )}
107 |
108 |
109 |
110 |
111 |
112 |
113 | { /*
114 |
115 |
116 |
117 |
118 | Pricing
119 |
120 |
121 |
122 |
123 |
124 | €,
132 | }}
133 | />
134 |
135 |
136 |
147 | }
148 | label="Charge tax on product"
149 | />
150 |
151 |
152 |
153 |
154 | */ }
155 |
156 |
157 | );
158 | }
159 |
--------------------------------------------------------------------------------
/src/Inventory/EmptyInventory.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Container from '@material-ui/core/Container';
4 | import Typography from '@material-ui/core/Typography';
5 | import InboxIcon from '@material-ui/icons/Inbox';
6 | import { Box } from '@material-ui/core';
7 | import Construction from '../Common/img/construction.png';
8 |
9 | const useStyles = makeStyles(theme => ({
10 | image: {
11 | width: 600
12 | },
13 | bold: {
14 | fontFamily: 'ApercuBold',
15 | }
16 | }));
17 |
18 |
19 | export default function EmptyInventory() {
20 |
21 | const classes = useStyles();
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 | Inventory is empty.
31 |
32 |
33 | Create New Product to add new items to Inventory.
34 |
35 |
36 |
37 | )
38 | }
--------------------------------------------------------------------------------
/src/Inventory/Form/Basics.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles, Grid, Box, Typography, TextField, FormControl, InputLabel, Select, MenuItem } from '@material-ui/core';
3 |
4 | const useStyles = makeStyles((theme) => ({
5 | title: {
6 | fontFamily: 'ApercuMedium'
7 | },
8 | formControl: {
9 | minWidth: 120,
10 | marginTop: theme.spacing(2)
11 | },
12 | }));
13 |
14 | export default function Basics(props) {
15 | const classes = useStyles();
16 |
17 | const inputLabel = React.useRef(null);
18 | const [labelWidth, setLabelWidth] = React.useState(0);
19 | React.useEffect(() => {
20 | setLabelWidth(inputLabel.current.offsetWidth);
21 | }, []);
22 |
23 | return (
24 |
25 |
26 |
27 | Basics
28 | Name, brand, and description let shoppers quickly scan through each product.
29 |
30 |
31 |
32 |
33 |
40 |
41 |
42 |
49 |
50 |
51 |
62 |
63 |
64 |
65 |
66 | Category
67 |
68 |
86 |
87 |
88 |
89 |
90 | );
91 | }
--------------------------------------------------------------------------------
/src/Inventory/Form/Media.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles, Grid, Box, Typography } from '@material-ui/core';
3 |
4 | const useStyles = makeStyles((theme) => ({
5 | title: {
6 | fontFamily: 'ApercuMedium'
7 | },
8 | formControl: {
9 | minWidth: 120,
10 | marginTop: theme.spacing(2)
11 | },
12 | }));
13 |
14 | export default function Media() {
15 | const classes = useStyles();
16 |
17 | return (
18 |
19 |
20 |
21 | Basics
22 | Name, brand, and description let shoppers quickly scan through each product.
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
--------------------------------------------------------------------------------
/src/Inventory/Inventory.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { connect } from "react-redux";
4 | import { fetchProducts } from "../store/actions/products";
5 |
6 | import { makeStyles, useTheme } from "@material-ui/core/styles";
7 | import IconButton from "@material-ui/core/IconButton";
8 | import Grid from "@material-ui/core/Grid";
9 | import Container from "@material-ui/core/Container";
10 | import Typography from "@material-ui/core/Typography";
11 |
12 | import Fab from "@material-ui/core/Fab";
13 | import Zoom from "@material-ui/core/Zoom";
14 | import Modal from "@material-ui/core/Modal";
15 | import Backdrop from "@material-ui/core/Backdrop";
16 | import Fade from "@material-ui/core/Fade";
17 | import Paper from "@material-ui/core/Paper";
18 | import Button from "@material-ui/core/Button";
19 |
20 | import SearchIcon from "@material-ui/icons/Search";
21 | import ViewModuleIcon from "@material-ui/icons/ViewModule";
22 | import ViewHeadlineIcon from "@material-ui/icons/ViewHeadline";
23 | import RefreshIcon from "@material-ui/icons/Refresh";
24 |
25 | import CreateProduct from "./CreateProduct";
26 | import InventoryItem from "./InventoryItem";
27 | import EmptyInventory from "./EmptyInventory";
28 | import SearchModal from "./SearchModal";
29 | import ProductModal from "./ProductModal";
30 | import CreateProductForm from "./CreateProductForm";
31 | import PageTitle from "./../Common/PageTitle";
32 |
33 | const useStyles = makeStyles((theme) => ({
34 | fab: {
35 | margin: 0,
36 | top: "auto",
37 | left: "auto",
38 | position: "fixed",
39 | bottom: theme.spacing(7),
40 | right: theme.spacing(7),
41 | },
42 | action: {
43 | marginLeft: "auto",
44 | marginTop: "0.8rem",
45 | marginRight: theme.spacing(2),
46 | },
47 | modal: {
48 | display: "flex",
49 | alignItems: "center",
50 | justifyContent: "center",
51 | },
52 | createProductModal: {
53 | display: "flex",
54 | alignItems: "center",
55 | justifyContent: "center",
56 | overflow: "scroll",
57 | },
58 | paper: {
59 | backgroundColor: theme.palette.background.paper,
60 | // boxShadow: theme.shadows[5],
61 | boxShadow: "0 20px 60px -2px rgba(27,33,58,.4)",
62 | padding: theme.spacing(2, 4, 3),
63 | outline: "none",
64 | borderRadius: "8px",
65 | },
66 | emptyIcon: {
67 | color: "#00000032",
68 | fontSize: "10em",
69 | },
70 | emptyContainer: {
71 | marginTop: "25vh",
72 | },
73 | title: {
74 | fontFamily: "ApercuMedium",
75 | marginTop: theme.spacing(4),
76 | marginBottom: theme.spacing(4),
77 | },
78 | button: {
79 | margin: theme.spacing(1),
80 | },
81 | toolbar: {
82 | // boxShadow: '0 0 1px 0 rgba(0,0,0,.22)',
83 | boxShadow: "0 0 11px #eaf0f6",
84 | display: "inline-block",
85 | marginBottom: theme.spacing(1),
86 | width: "100%",
87 | },
88 | lastUpdated: {
89 | marginTop: theme.spacing(2),
90 | padding: 0,
91 | color: "rgb(112, 117, 122)",
92 | },
93 | }));
94 |
95 | const Inventory = (props) => {
96 | const classes = useStyles();
97 | const theme = useTheme();
98 | const [open, setOpen] = React.useState(false);
99 | const [product, setProduct] = React.useState({
100 | id: "ID",
101 | name: "Name",
102 | type: "Type",
103 | description: "Description",
104 | });
105 |
106 | const [searchModal, setSearchModal] = React.useState(false);
107 | const [createProductModal, setCreateProductModal] = React.useState(false);
108 | const [lastUpdatedTime, setLastUpdatedTime] = React.useState("N/A");
109 |
110 | React.useEffect(() => {
111 | props.dispatch(fetchProducts());
112 | setLastUpdatedTime(`${new Date().toLocaleString()}`);
113 | }, []);
114 |
115 | const openCreateNewProductModal = () => {
116 | setCreateProductModal(true);
117 | };
118 |
119 | const closeCreateNewProductModal = () => {
120 | setCreateProductModal(false);
121 | };
122 |
123 | const openSearchModal = () => {
124 | setSearchModal(true);
125 | };
126 |
127 | const closeSearchModal = () => {
128 | setSearchModal(false);
129 | };
130 |
131 | const handleOpen = (product) => {
132 | setProduct(product);
133 | setOpen(true);
134 | };
135 |
136 | const handleClose = () => {
137 | setOpen(false);
138 | };
139 |
140 | const transitionDuration = {
141 | enter: theme.transitions.duration.enteringScreen,
142 | exit: theme.transitions.duration.leavingScreen,
143 | };
144 |
145 | return (
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 | {props.products.length === 0 || props.products.length === null ? (
168 |
169 | ) : (
170 |
171 |
172 | {props.products.map((product) => (
173 |
174 |
175 |
176 | ))}
177 |
178 |
179 |
180 | Inventory up to date. Last retrieved at {lastUpdatedTime}
181 |
182 |
183 |
184 | )}
185 |
186 |
187 |
195 |
201 |
202 |
203 |
204 |
205 |
217 |
218 |
221 |
222 |
223 |
224 | {/* Search Modal */}
225 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 | {/* Create Product Modal */}
245 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 | );
265 | };
266 |
267 | Inventory.defaultProps = {
268 | products: [],
269 | };
270 |
271 | Inventory.propTypes = {
272 | products: PropTypes.array.isRequired,
273 | };
274 |
275 | function mapStateToProps(state) {
276 | return {
277 | products: state.product.products,
278 | };
279 | }
280 |
281 | export default connect(mapStateToProps, null)(Inventory);
282 |
--------------------------------------------------------------------------------
/src/Inventory/InventoryItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Card from '@material-ui/core/Card';
4 | import CardActions from '@material-ui/core/CardActions';
5 | import CardContent from '@material-ui/core/CardContent';
6 | import Button from '@material-ui/core/Button';
7 | import Typography from '@material-ui/core/Typography';
8 | import Grid from '@material-ui/core/Grid';
9 |
10 | const useStyles = makeStyles({
11 | card: {
12 | minWidth: 275,
13 | // boxShadow: '0 0 1px 0 rgba(0,0,0,.22)'
14 | boxShadow: '0 0 11px #eaf0f6',
15 | },
16 | bullet: {
17 | display: 'inline-block',
18 | margin: '0 2px',
19 | transform: 'scale(0.8)',
20 | },
21 | title: {
22 | fontSize: 14,
23 | },
24 | subtitle: {
25 | fontFamily: 'ApercuBold'
26 | }
27 | });
28 |
29 | export default function InventoryItem(props) {
30 | const classes = useStyles();
31 |
32 | const openProductModal = () => {
33 | props.openModal(props.item);
34 | }
35 |
36 | const closeProductModal = () => {
37 | console.log('closing modal');
38 | props.closeModal();
39 | }
40 |
41 | return (
42 |
43 |
44 |
45 | {props.item.name}
46 |
47 |
48 |
49 |
50 | Brand
51 |
52 |
53 | {props.item.brand}
54 |
55 |
56 |
57 |
58 | ID
59 |
60 |
61 | {props.item.id}
62 |
63 |
64 |
65 |
66 | Category
67 |
68 |
69 | {props.item.category}
70 |
71 |
72 |
73 |
74 | Stock
75 |
76 |
77 | {Math.floor(Math.random() * 100)}
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | );
91 | }
--------------------------------------------------------------------------------
/src/Inventory/ProductModal.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Button from '@material-ui/core/Button';
5 | import Box from '@material-ui/core/Box';
6 | import Grid from '@material-ui/core/Grid';
7 | import Typography from '@material-ui/core/Typography';
8 | import { Container, Modal } from '@material-ui/core';
9 |
10 | import UpdateProduct from './UpdateProduct';
11 | import UpdateProductStock from './UpdateProductStock';
12 | import RemoveProduct from './RemoveProduct';
13 | import FieldRow from './../Common/FieldRow';
14 | import FieldModal from './../Common/FieldModal';
15 |
16 | const useStyles = makeStyles(theme => ({
17 | container: {
18 | padding: theme.spacing(1),
19 | minWidth: '25vw'
20 | },
21 | textField: {
22 | marginRight: theme.spacing(1),
23 | width: '375px'
24 | },
25 | button: {
26 | boxShadow: 'none',
27 | },
28 | title: {
29 | fontFamily: 'ApercuMedium',
30 | marginTop: theme.spacing(2),
31 | marginBottom: theme.spacing(2)
32 | },
33 | subtitle: {
34 | fontFamily: 'ApercuBold'
35 | },
36 | image: {
37 | width: '10vw',
38 | boxShadow: '0 0 1px 0 rgba(0,0,0,.22)',
39 | padding: 15,
40 | marginBottom: theme.spacing(2),
41 | borderRadius: 4
42 | },
43 | modal: {
44 | display: 'flex',
45 | alignItems: 'center',
46 | justifyContent: 'center',
47 | },
48 | paper: {
49 | backgroundColor: theme.palette.background.paper,
50 | // boxShadow: theme.shadows[5],
51 | boxShadow: '0 20px 60px -2px rgba(27,33,58,.4)',
52 | padding: theme.spacing(2, 4, 3),
53 | outline: 'none',
54 | borderRadius: '8px'
55 | },
56 | }));
57 |
58 |
59 | export default function ProductModal(props) {
60 | const classes = useStyles();
61 |
62 | const [fieldModal, setFieldModal] = React.useState({
63 | open: false,
64 | field: {
65 | label: null,
66 | value: null
67 | }
68 | });
69 |
70 | const [product, setProduct] = React.useState({
71 | ...props.item
72 | })
73 |
74 | const showFieldModal = (field) => {
75 | setFieldModal({
76 | open: true,
77 | field,
78 | });
79 | }
80 |
81 | const handleClose = () => {
82 | setFieldModal({
83 | ...fieldModal,
84 | open: false
85 | });
86 | }
87 |
88 | return (
89 |
90 | {props.item.name}
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
110 |
111 |
112 |
113 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | );
131 | }
--------------------------------------------------------------------------------
/src/Inventory/RemoveProduct.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@material-ui/core/Button';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Icon from '@material-ui/core/Icon';
5 | import DeleteForever from '@material-ui/icons/DeleteForever';
6 | import red from '@material-ui/core/colors/red';
7 |
8 | const useStyles = makeStyles(theme => ({
9 | leftIcon: {
10 | marginRight: theme.spacing(1),
11 | },
12 | rightIcon: {
13 | marginLeft: theme.spacing(1),
14 | },
15 | iconSmall: {
16 | fontSize: 20,
17 | },
18 | dangerButton: {
19 | borderColor: red[500],
20 | color: red[500],
21 | '&:hover': {
22 | backgroundColor: red[50]
23 | },
24 | fontFamily: 'ApercuMedium',
25 | },
26 | }));
27 |
28 | export default function CreateProduct() {
29 | const classes = useStyles();
30 |
31 | return (
32 |
36 | )
37 | }
--------------------------------------------------------------------------------
/src/Inventory/SearchModal.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import MenuItem from '@material-ui/core/MenuItem';
4 | import TextField from '@material-ui/core/TextField';
5 | import Button from '@material-ui/core/Button';
6 | import Box from '@material-ui/core/Box';
7 | import SearchIcon from '@material-ui/icons/Search';
8 | import Typography from '@material-ui/core/Typography';
9 | import InputAdornment from '@material-ui/core/InputAdornment';
10 |
11 | const useStyles = makeStyles(theme => ({
12 | container: {
13 | display: 'flex',
14 | flexWrap: 'wrap',
15 | },
16 | textField: {
17 | marginRight: theme.spacing(1),
18 | width: '375px'
19 | },
20 | typeField: {
21 | marginLeft: theme.spacing(1),
22 | width: '175px'
23 | },
24 | dense: {
25 | marginTop: theme.spacing(2),
26 | },
27 | menu: {
28 | width: 200,
29 | },
30 | rightIcon: {
31 | marginLeft: theme.spacing(1),
32 | },
33 | button: {
34 | boxShadow: 'none',
35 | },
36 | title: {
37 | fontFamily: 'ApercuMedium',
38 | marginTop: theme.spacing(4),
39 | marginBottom: theme.spacing(4)
40 | }
41 | }));
42 |
43 |
44 |
45 | export default function Search(props) {
46 | const classes = useStyles();
47 | const [values, setValues] = React.useState({
48 | type: 'ID',
49 | });
50 |
51 | const [queryValue, setQueryValue] = React.useState('');
52 |
53 | const attributes = [
54 | {
55 | value: 'ID',
56 | label: 'ID',
57 | },
58 | {
59 | value: 'Name',
60 | label: 'Name',
61 | },
62 | {
63 | value: 'Type',
64 | label: 'Type',
65 | },
66 | ];
67 |
68 | function handleChange(event) {
69 | setValues(oldValues => ({
70 | type: event.target.value,
71 | }));
72 | }
73 |
74 | function handleQueryValue(e) {
75 | setQueryValue(e.target.value);
76 | }
77 |
78 | return (
79 |
80 | Search Inventory
81 |
93 |
94 |
95 | ),
96 | }}
97 | />
98 |
113 | {attributes.map(option => (
114 |
117 | ))}
118 |
119 |
120 |
121 |
124 |
127 |
128 |
129 |
130 | );
131 | }
--------------------------------------------------------------------------------
/src/Inventory/UpdateProduct.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@material-ui/core/Button';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Icon from '@material-ui/core/Icon';
5 | import Edit from '@material-ui/icons/Edit';
6 | import BlueGrey from '@material-ui/core/colors/blueGrey';
7 |
8 | const useStyles = makeStyles(theme => ({
9 | button: {
10 | fontFamily: 'ApercuMedium',
11 | },
12 | rightIcon: {
13 | marginLeft: theme.spacing(1),
14 | },
15 | iconSmall: {
16 | fontSize: 20,
17 | },
18 | }));
19 |
20 | export default function UpdateProduct() {
21 | const classes = useStyles();
22 |
23 | return (
24 |
28 | )
29 | }
--------------------------------------------------------------------------------
/src/Inventory/UpdateProductStock.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@material-ui/core/Button';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Icon from '@material-ui/core/Icon';
5 | import UnarchiveIcon from '@material-ui/icons/Unarchive';
6 | import BlueGrey from '@material-ui/core/colors/blueGrey';
7 |
8 | const useStyles = makeStyles(theme => ({
9 | button: {
10 | fontFamily: 'ApercuMedium',
11 | },
12 | rightIcon: {
13 | marginLeft: theme.spacing(1),
14 | },
15 | iconSmall: {
16 | fontSize: 20,
17 | },
18 | }));
19 |
20 | export default function UpdateProductStock() {
21 | const classes = useStyles();
22 |
23 | return (
24 |
28 | )
29 | }
--------------------------------------------------------------------------------
/src/Login/ErrorMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import Button from '@material-ui/core/Button';
5 | import CheckCircleIcon from '@material-ui/icons/CheckCircle';
6 | import ErrorIcon from '@material-ui/icons/Error';
7 | import InfoIcon from '@material-ui/icons/Info';
8 | import CloseIcon from '@material-ui/icons/Close';
9 | import { amber, green } from '@material-ui/core/colors';
10 | import IconButton from '@material-ui/core/IconButton';
11 | import Snackbar from '@material-ui/core/Snackbar';
12 | import SnackbarContent from '@material-ui/core/SnackbarContent';
13 | import WarningIcon from '@material-ui/icons/Warning';
14 | import { makeStyles } from '@material-ui/core/styles';
15 |
16 | const variantIcon = {
17 | success: CheckCircleIcon,
18 | warning: WarningIcon,
19 | error: ErrorIcon,
20 | info: InfoIcon,
21 | };
22 |
23 | const useStyles1 = makeStyles(theme => ({
24 | success: {
25 | backgroundColor: green[600],
26 | },
27 | error: {
28 | color: '#b71c1c',
29 | backgroundColor: '#ffcdd2'
30 | },
31 | info: {
32 | backgroundColor: theme.palette.primary.main,
33 | },
34 | warning: {
35 | backgroundColor: amber[700],
36 | },
37 | icon: {
38 | fontSize: 20,
39 | },
40 | iconVariant: {
41 | opacity: 0.9,
42 | marginRight: theme.spacing(1),
43 | },
44 | message: {
45 | display: 'flex',
46 | alignItems: 'center',
47 | },
48 | }));
49 |
50 | function MySnackbarContentWrapper(props) {
51 | const classes = useStyles1();
52 | const { className, message, onClose, variant, ...other } = props;
53 | const Icon = variantIcon[variant];
54 |
55 | return (
56 |
61 |
62 | {message}
63 |
64 | }
65 | {...other}
66 | />
67 | );
68 | }
69 |
70 | MySnackbarContentWrapper.propTypes = {
71 | className: PropTypes.string,
72 | message: PropTypes.string,
73 | onClose: PropTypes.func,
74 | variant: PropTypes.oneOf(['error', 'info', 'success', 'warning']).isRequired,
75 | };
76 |
77 | const useStyles2 = makeStyles(theme => ({
78 | margin: {
79 | margin: theme.spacing(1),
80 | boxShadow: 'none'
81 | },
82 | }));
83 |
84 | export default function CustomizedSnackbars(props) {
85 | const classes = useStyles2();
86 |
87 | return (
88 |
93 | );
94 | }
--------------------------------------------------------------------------------
/src/Login/Login.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { connect } from "react-redux";
4 |
5 | import Container from "@material-ui/core/Container";
6 | import { makeStyles } from "@material-ui/core/styles";
7 | import { Grid } from "@material-ui/core";
8 |
9 | import LoginCard from "./LoginCard";
10 | import Welcome from "../Common/img/welcome.svg";
11 | import { userSignInRequest } from "../store/actions/auth";
12 |
13 | const useStyles = makeStyles((theme) => ({
14 | image: {
15 | width: "100%",
16 | pointerEvents: "none",
17 | userSelect: "none",
18 | },
19 | }));
20 |
21 | function Login(props) {
22 | const classes = useStyles();
23 |
24 | const { userSignInRequest } = props;
25 |
26 | return (
27 |
28 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | );
44 | }
45 |
46 | Login.propTypes = {
47 | userSignInRequest: PropTypes.func.isRequired,
48 | };
49 |
50 | export default connect(null, { userSignInRequest })(Login);
51 |
--------------------------------------------------------------------------------
/src/Login/LoginCard.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { connect } from "react-redux";
4 | import isEmpty from "lodash/isEmpty";
5 |
6 | import Container from "@material-ui/core/Container";
7 | import { makeStyles } from "@material-ui/core/styles";
8 | import {
9 | Paper,
10 | withStyles,
11 | Grid,
12 | TextField,
13 | Button,
14 | FormControlLabel,
15 | Checkbox,
16 | Typography,
17 | InputAdornment,
18 | CircularProgress,
19 | Box,
20 | } from "@material-ui/core";
21 | import { LockOutlined, PersonOutline } from "@material-ui/icons";
22 | import ErrorMessage from "./ErrorMessage";
23 |
24 | import ShopCastLogo from "../Common/img/shopcast.svg";
25 |
26 | const useStyles = makeStyles((theme) => ({
27 | paper: {
28 | padding: theme.spacing(1),
29 | // boxShadow: '0 0 1px 0 rgba(0,0,0,.22)',
30 | boxShadow: "0 0 11px #eaf0f6",
31 | },
32 | margin: {
33 | margin: theme.spacing(2),
34 | },
35 | title: {
36 | fontFamily: "ApercuMedium",
37 | marginBottom: theme.spacing(6),
38 | paddingTop: "20vh",
39 | },
40 | textfield: {
41 | marginBottom: theme.spacing(4),
42 | },
43 | password: {
44 | marginBottom: theme.spacing(1),
45 | },
46 | inactive: {
47 | color: "#757575",
48 | },
49 | active: {
50 | color: "#2196F3",
51 | },
52 | button: {
53 | marginTop: theme.spacing(4),
54 | },
55 | formControlLabel: {
56 | fontSize: "0.875rem",
57 | },
58 | logo: {
59 | width: "128px",
60 | userSelect: "none",
61 | pointerEvents: "none",
62 | },
63 | }));
64 |
65 | const Login = (props) => {
66 | const classes = useStyles();
67 |
68 | // Misc UI indicators
69 | const [username, setUsername] = React.useState(false);
70 | const [password, setPassword] = React.useState(false);
71 | const [submit, setSubmit] = React.useState(false);
72 |
73 | // Values
74 | const [creds, setCreds] = React.useState({
75 | username: "name@company.com",
76 | password: "",
77 | });
78 | const [errors, setErrors] = React.useState({
79 | username: false,
80 | password: false,
81 | });
82 |
83 | const onSubmit = () => {
84 | setSubmit(true);
85 |
86 | if (!isEmpty(creds.username) && !isEmpty(creds.password)) {
87 | props.userSignInRequest(creds);
88 | } else {
89 | setSubmit(false);
90 | if (isEmpty(creds.username)) {
91 | setErrors({
92 | username: true,
93 | });
94 | }
95 | if (isEmpty(creds.password)) {
96 | setErrors({
97 | ...errors,
98 | password: true,
99 | });
100 | }
101 | }
102 | };
103 |
104 | const ShowErrorMessage = () => {
105 | setSubmit(false);
106 | return (
107 |
111 | );
112 | };
113 |
114 | return (
115 |
116 |
117 |
118 |
119 |
124 |
125 |
126 | Welcome back!
127 |
128 |
138 |
141 |
142 | ),
143 | }}
144 | onFocus={() => setUsername(true)}
145 | onBlur={() => setUsername(false)}
146 | onChange={(e) => setCreds({ ...creds, username: e.target.value })}
147 | autoComplete="off"
148 | disabled={submit}
149 | error={errors.username}
150 | fullWidth
151 | autoFocus
152 | required
153 | />
154 |
164 |
167 |
168 | ),
169 | }}
170 | onFocus={() => setPassword(true)}
171 | onBlur={() => setPassword(false)}
172 | onChange={(e) => setCreds({ ...creds, password: e.target.value })}
173 | autoComplete="off"
174 | disabled={submit}
175 | error={errors.password}
176 | fullWidth
177 | required
178 | />
179 |
180 |
181 | }
183 | label={
184 |
185 | Remember Me
186 |
187 | }
188 | />
189 |
190 |
191 |
201 |
202 |
203 |
204 |
222 |
223 | {props.errorMessage.message ? : null}
224 |
225 |
226 |
227 |
228 |
229 | );
230 | };
231 |
232 | Login.propTypes = {
233 | userSignInRequest: PropTypes.func.isRequired,
234 | };
235 |
236 | function mapStateToProps(state) {
237 | return {
238 | errorMessage: state.auth.error,
239 | };
240 | }
241 |
242 | export default connect(mapStateToProps)(Login);
243 |
--------------------------------------------------------------------------------
/src/Orders/DraftOrder.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useSelector } from "react-redux";
3 | import PropTypes from "prop-types";
4 | import { makeStyles } from "@material-ui/core/styles";
5 |
6 | import { Container, Paper, Typography, Button } from "@material-ui/core/";
7 |
8 | import PageTitle from "../Common/PageTitle";
9 | import WarningModal from "../Common/WarningModal";
10 | import TableBuilder from "../Common/TableBuilder";
11 |
12 | const useStyles = makeStyles((theme) => ({
13 | root: {
14 | boxShadow: "0 0 11px #eaf0f6",
15 | borderRadius: "4px",
16 | overflow: "hidden",
17 | border: "1px solid #eaf0f6",
18 | },
19 | title: {
20 | fontFamily: "ApercuMedium",
21 | },
22 | }));
23 |
24 | const DraftOrder = (props) => {
25 | const classes = useStyles();
26 | const [open, setOpen] = React.useState(false);
27 |
28 | const products = useSelector((state) => state.product.products);
29 |
30 | const handleOpen = () => {
31 | setOpen(true);
32 | };
33 |
34 | const quitWarning = () => {
35 | handleOpen(true);
36 | };
37 |
38 | const quit = () => {
39 | props.setPageControl({
40 | manage: false,
41 | orderDetails: null,
42 | draftOrder: false,
43 | root: true,
44 | });
45 | };
46 |
47 | const modalDetails = {
48 | animationKey: "alertCircle",
49 | title: "Abandon Draft Order",
50 | subtitle: [
51 | "Leaving will return you to the Orders page. You will lose all progress on creating your Draft Order.",
52 | "You will have to start over to create new Draft Order.",
53 | ],
54 | action: "Abandon",
55 | };
56 |
57 | return (
58 |
59 |
64 |
70 |
71 | Order Details
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | DraftOrder.defaultProps = {
79 | products: [
80 | {
81 | id: 1,
82 | name: "name",
83 | price: 1,
84 | pictureUrl: "url",
85 | description: "desc",
86 | brand: "brand",
87 | category: "cat",
88 | colour: "colour",
89 | },
90 | ],
91 | };
92 |
93 | DraftOrder.propTypes = {
94 | products: PropTypes.array.isRequired,
95 | };
96 |
97 | export default DraftOrder;
98 |
--------------------------------------------------------------------------------
/src/Orders/Manage/Manage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import {
4 | makeStyles,
5 | Container,
6 | Paper,
7 | Typography,
8 | Stepper,
9 | Step,
10 | StepLabel,
11 | Button,
12 | } from "@material-ui/core/";
13 |
14 | import PageTitle from "../../Common/PageTitle";
15 | import New from "./New";
16 |
17 | const useStyles = makeStyles((theme) => ({
18 | root: {
19 | width: "100%",
20 | marginTop: theme.spacing(3),
21 | boxShadow: "0 0 11px #eaf0f6",
22 | borderRadius: "4px",
23 | },
24 | paper: {
25 | boxShadow: "0 0 1px 0 rgba(0,0,0,.22)",
26 | },
27 | backButton: {
28 | marginRight: theme.spacing(1),
29 | },
30 | instructions: {
31 | marginTop: theme.spacing(1),
32 | marginBottom: theme.spacing(1),
33 | },
34 | }));
35 |
36 | function getSteps() {
37 | return [
38 | "New",
39 | "Active",
40 | "Inventory",
41 | "Package",
42 | "Approve",
43 | "Fulfilled",
44 | "Delivered",
45 | ];
46 | }
47 |
48 | export default function Manage(props) {
49 | const classes = useStyles();
50 |
51 | const [activeStep, setActiveStep] = React.useState(0);
52 | const steps = getSteps();
53 |
54 | const handleNext = () => {
55 | setActiveStep((prevActiveStep) => prevActiveStep + 1);
56 | };
57 |
58 | const handleBack = () => {
59 | setActiveStep((prevActiveStep) => prevActiveStep - 1);
60 | };
61 |
62 | const handleReset = () => {
63 | setActiveStep(0);
64 | };
65 |
66 | const quit = () => {
67 | props.setPageControl({
68 | manage: false,
69 | root: true,
70 | draftOrder: false,
71 | });
72 | };
73 |
74 | function getStepContent(stepIndex) {
75 | switch (stepIndex) {
76 | case 0:
77 | return (
78 |
83 | );
84 | case 1:
85 | return "What is an ad group anyways?";
86 | case 2:
87 | return "This is the bit I really care about!";
88 | default:
89 | return "Unknown stepIndex";
90 | }
91 | }
92 |
93 | return (
94 |
95 |
96 |
101 |
102 |
103 | {steps.map((label) => (
104 |
105 | {label}
106 |
107 | ))}
108 |
109 |
110 |
111 |
112 | {activeStep === steps.length ? (
113 |
114 |
115 | All steps completed
116 |
117 |
118 |
119 | ) : (
120 |
{getStepContent(activeStep)}
121 | )}
122 |
123 |
124 |
125 |
126 | );
127 | }
128 |
--------------------------------------------------------------------------------
/src/Orders/Manage/Metadata.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import faker from "faker";
3 |
4 | import { Typography, TextField, Box, makeStyles } from "@material-ui/core";
5 |
6 | import Autocomplete from "@material-ui/lab/Autocomplete";
7 |
8 | const useStyles = makeStyles((theme) => ({
9 | textfield: {
10 | marginRight: theme.spacing(2),
11 | marginBottom: theme.spacing(3),
12 | },
13 | title: {
14 | fontFamily: "ApercuMedium",
15 | color: theme.palette.action.active,
16 | marginBottom: theme.spacing(2),
17 | },
18 | }));
19 |
20 | export default function Metadata(props) {
21 | const classes = useStyles();
22 |
23 | // TODO: Add state to
24 |
25 | const testProps = {
26 | ...props.pageControl.orderDetails,
27 | assignedTo: `${faker.name.firstName()} ${faker.name.lastName()}`,
28 | // inventorySignoff: `${faker.name.firstName()} ${faker.name.lastName()}`,
29 | packingSignoff: `${faker.name.firstName()} ${faker.name.lastName()}`,
30 | };
31 |
32 | const {
33 | orderId,
34 | created,
35 | assignedTo,
36 | inventorySignoff,
37 | packingSignoff,
38 | } = testProps;
39 |
40 | const managers = [
41 | { name: "Boris Chan", email: "boris@shopcast.com" },
42 | { name: "Conan O'Brien", email: "conan@shopcast.com" },
43 | { name: "Linus Sebastian", email: "linus@shopcast.com" },
44 | ];
45 |
46 | return (
47 |
48 |
49 | Order Metadata
50 |
51 |
52 |
63 |
74 |
85 | {assignedTo && (
86 |
97 | )}
98 |
99 | {/* {inventorySignoff && (
100 |
manager.name)}
103 | renderInput={(params) => (
104 |
113 | )}
114 | />
115 | )}
116 | {packingSignoff && (
117 | manager.name)}
120 | renderInput={(params) => (
121 |
130 | )}
131 | />
132 | )} */}
133 |
134 |
135 |
136 | );
137 | }
138 |
--------------------------------------------------------------------------------
/src/Orders/Manage/New.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import clsx from "clsx";
3 | import {
4 | makeStyles,
5 | Container,
6 | Typography,
7 | TextField,
8 | Box,
9 | Button,
10 | IconButton,
11 | Popover,
12 | } from "@material-ui/core";
13 |
14 | import DoneIcon from "@material-ui/icons/Done";
15 | import PostAdd from "@material-ui/icons/PostAdd";
16 |
17 | import Metadata from "./Metadata";
18 |
19 | const useStyles = makeStyles((theme) => ({
20 | textfield: {
21 | marginRight: theme.spacing(2),
22 | marginBottom: theme.spacing(3),
23 | },
24 | root: {
25 | padding: theme.spacing(4),
26 | },
27 | title: {
28 | fontFamily: "ApercuMedium",
29 | color: theme.palette.action.active,
30 | marginBottom: theme.spacing(2),
31 | },
32 | subtitle: {
33 | fontFamily: "ApercuMedium",
34 | color: theme.palette.action.active,
35 | marginBottom: theme.spacing(2),
36 | marginTop: theme.spacing(2),
37 | },
38 | button: {
39 | boxShadow: "none",
40 | fontFamily: "ApercuMedium",
41 | },
42 | rightIcon: {
43 | marginLeft: theme.spacing(1),
44 | },
45 | active: {
46 | color: theme.palette.primary.main,
47 | },
48 | popover: {
49 | padding: theme.spacing(2),
50 | },
51 | }));
52 |
53 | export default function New(props) {
54 | const classes = useStyles();
55 |
56 | // TODO: Import the schema instead of manually deconstructing
57 | const {
58 | orderId,
59 | created,
60 | firstName,
61 | lastName,
62 | email,
63 | address,
64 | city,
65 | country,
66 | zip,
67 | fulfillment,
68 | total,
69 | status,
70 | updated,
71 | } = props.pageControl.orderDetails;
72 |
73 | const handleNext = () => {
74 | // Send RESTful call to update Order state on backend.
75 | props.handleNext();
76 | };
77 |
78 | return (
79 |
80 |
176 |
177 | );
178 | }
179 |
--------------------------------------------------------------------------------
/src/Orders/Orders.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route } from "react-router-dom";
3 | import faker from "faker";
4 | import { connect } from "react-redux";
5 | import { makeStyles } from "@material-ui/core/styles";
6 | import { fetchProducts } from "../store/actions/products";
7 |
8 | import {
9 | IconButton,
10 | Button,
11 | Container,
12 | Box,
13 | Tooltip,
14 | } from "@material-ui/core/";
15 | import RefreshIcon from "@material-ui/icons/Refresh";
16 | import SortIcon from "@material-ui/icons/Sort";
17 | import SearchIcon from "@material-ui/icons/Search";
18 | import PlaylistAddIcon from "@material-ui/icons/PlaylistAdd";
19 | import blue from "@material-ui/core/colors/blue";
20 |
21 | import OrdersTable from "./OrdersTable";
22 | import Manage from "./Manage/Manage";
23 | import DraftOrder from "./DraftOrder";
24 | import PageTitle from "./../Common/PageTitle";
25 |
26 | const drawerWidth = 210;
27 |
28 | const useStyles = makeStyles((theme) => ({
29 | root: {
30 | display: "flex",
31 | zIndex: 0,
32 | },
33 | drawer: {
34 | width: drawerWidth,
35 | flexShrink: 0,
36 | whiteSpace: "nowrap",
37 | },
38 | paper: {
39 | boxShadow: "0 0 1px 0 rgba(0,0,0,.22)",
40 | },
41 | toolbar: {
42 | boxShadow: "0 0 11px #eaf0f6",
43 | display: "inline-block",
44 | marginBottom: theme.spacing(1),
45 | width: "100%",
46 | },
47 | button: {
48 | margin: theme.spacing(1),
49 | },
50 | action: {
51 | marginLeft: "auto",
52 | marginTop: "0.8rem",
53 | marginRight: theme.spacing(2),
54 | },
55 | lastUpdated: {
56 | marginTop: theme.spacing(2),
57 | padding: 0,
58 | color: "rgb(112, 117, 122)",
59 | },
60 | iconButton: {
61 | margin: theme.spacing(1),
62 | },
63 | button: {
64 | backgroundColor: blue[100],
65 | // margin: theme.spacing(1),
66 | borderRadius: "30px",
67 | "&:hover": {
68 | backgroundColor: theme.palette.primary.main,
69 | "& $icon": {
70 | color: "white",
71 | },
72 | },
73 | },
74 | icon: {
75 | color: theme.palette.primary.main,
76 | },
77 | }));
78 |
79 | // TODO: Organize scheme to its own repository.
80 |
81 | function createData(
82 | orderId,
83 | created,
84 | firstName,
85 | lastName,
86 | email,
87 | address,
88 | city,
89 | country,
90 | zip,
91 | fulfillment,
92 | total,
93 | status,
94 | updated
95 | ) {
96 | return {
97 | orderId,
98 | created,
99 | firstName,
100 | lastName,
101 | email,
102 | address,
103 | city,
104 | country,
105 | zip,
106 | fulfillment,
107 | total,
108 | status,
109 | updated,
110 | };
111 | }
112 |
113 | // TODO: State should be retrieved from here.
114 |
115 | const populate = (n) => {
116 | const data = [];
117 | const set = new Set();
118 | for (let i = 0; i < n; i++) {
119 | let random = faker.random.number(100);
120 |
121 | while (set.has(random)) {
122 | random = faker.random.number(100);
123 | }
124 |
125 | set.add(random);
126 |
127 | data.push(
128 | createData(
129 | random,
130 | faker.date.recent(7).toLocaleDateString(),
131 | faker.name.firstName(),
132 | faker.name.lastName(),
133 | faker.internet.email(),
134 | faker.address.streetAddress(),
135 | faker.address.city(),
136 | faker.address.country(),
137 | faker.address.zipCode(),
138 | "Processing",
139 | faker.commerce.price(),
140 | "Paid",
141 | "Today"
142 | )
143 | );
144 | }
145 |
146 | return data;
147 | };
148 |
149 | const ordersData = populate(8);
150 |
151 | const Orders = (props) => {
152 | const classes = useStyles();
153 |
154 | const [pageControl, setPageControl] = React.useState({
155 | manage: false,
156 | orderDetails: null,
157 | draftOrder: false,
158 | root: true,
159 | });
160 |
161 | const openDraftOrder = () => {
162 | setPageControl({
163 | manage: false,
164 | orderDetails: null,
165 | draftOrder: true,
166 | root: false,
167 | });
168 | };
169 |
170 | const [lastUpdatedTime, setLastUpdatedTime] = React.useState("N/A");
171 |
172 | React.useEffect(() => {
173 | props.dispatch(fetchProducts());
174 | setLastUpdatedTime(`${new Date().toLocaleString()}`);
175 | }, []);
176 |
177 | const OrdersMain = (props) => {
178 | return (
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
203 |
204 |
211 |
212 |
213 |
214 |
215 |
216 |
217 | );
218 | };
219 |
220 | // TODO: Need to refactor. This is a workaround for Router (since its connected to Tabs, its difficult to hack)
221 |
222 | return (
223 |
224 | {pageControl.manage && (
225 |
226 | )}
227 | {pageControl.root && }
228 | {pageControl.draftOrder && (
229 |
230 | )}
231 |
232 | );
233 | };
234 |
235 | function mapStateToProps(state) {
236 | return {
237 | products: state.product.products,
238 | };
239 | }
240 |
241 | export default connect(mapStateToProps, null)(Orders);
242 |
--------------------------------------------------------------------------------
/src/Orders/OrdersStats.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Paper from '@material-ui/core/Paper';
4 | import Grid from '@material-ui/core/Grid';
5 |
6 | const useStyles = makeStyles(theme => ({
7 | root: {
8 | flexGrow: 1,
9 | paddingTop: '1em',
10 | },
11 | paper: {
12 | padding: theme.spacing(2),
13 | textAlign: 'center',
14 | color: theme.palette.text.secondary,
15 | },
16 | }));
17 |
18 | export default function OrderStats() {
19 | const classes = useStyles();
20 |
21 | return (
22 |
23 |
24 |
25 | Active Orders
26 |
27 |
28 | Unfulfilled
29 |
30 |
31 |
32 | );
33 | }
--------------------------------------------------------------------------------
/src/Orders/OrdersTable.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import clsx from "clsx";
3 |
4 | import { makeStyles } from "@material-ui/core/styles";
5 | import Table from "@material-ui/core/Table";
6 | import TableBody from "@material-ui/core/TableBody";
7 | import TableCell from "@material-ui/core/TableCell";
8 | import TableHead from "@material-ui/core/TableHead";
9 | import TableRow from "@material-ui/core/TableRow";
10 | import Paper from "@material-ui/core/Paper";
11 | import Chip from "@material-ui/core/Chip";
12 | import blue from "@material-ui/core/colors/blue";
13 | import grey from "@material-ui/core/colors/grey";
14 |
15 | import { getThemeProps } from "@material-ui/styles";
16 | import DoneIcon from "@material-ui/icons/Done";
17 | import ErrorIcon from "@material-ui/icons/Error";
18 | import CachedIcon from "@material-ui/icons/Cached";
19 | import ConfirmedIcon from "@material-ui/icons/AssignmentLate";
20 | import ShippingIcon from "@material-ui/icons/LocalShipping";
21 | import PackingIcon from "@material-ui/icons/MoveToInbox";
22 | import UnfoldMoreIcon from "@material-ui/icons/UnfoldMore";
23 | import KeyboardArrowRightIcon from "@material-ui/icons/KeyboardArrowRight";
24 |
25 | import { Typography, Box, IconButton } from "@material-ui/core";
26 | import { Pagination } from "@material-ui/lab";
27 |
28 | const useStyles = makeStyles((theme) => ({
29 | root: {
30 | boxShadow: "0 0 11px #eaf0f6",
31 | borderRadius: "4px",
32 | overflow: "hidden",
33 | border: "1px solid #eaf0f6",
34 | },
35 | table: {
36 | minWidth: 650,
37 | },
38 | tableHead: {
39 | fontFamily: "ApercuMedium",
40 | fontSize: "0.875rem",
41 | color: "#525f7f",
42 | paddingLeft: theme.spacing(2),
43 | paddingTop: theme.spacing(2),
44 | paddingBottom: theme.spacing(2),
45 | },
46 | tableHeadCell: {
47 | padding: theme.spacing(1),
48 | },
49 | tableHeadRow: {
50 | backgroundColor: "#fff",
51 | borderColor: "#fff",
52 | borderStyle: "solid",
53 | borderLeftWidth: "3px",
54 | },
55 | tableFoot: {
56 | fontFamily: "ApercuMedium",
57 | fontSize: "0.875rem",
58 | color: "#525f7f",
59 | },
60 | button: {
61 | margin: theme.spacing(1),
62 | },
63 | primaryButton: {
64 | backgroundColor: "#2196f3",
65 | },
66 | input: {
67 | display: "none",
68 | },
69 | tableRow: {
70 | borderColor: "#fff",
71 | borderStyle: "solid",
72 | borderLeftWidth: "3px",
73 | borderBottomWidth: "0px",
74 | borderTopWidth: "0px",
75 | borderRightWidth: "3px",
76 | "&:hover": {
77 | borderColor: theme.palette.primary.light,
78 | borderStyle: "solid",
79 | borderLeftWidth: "3px",
80 | backgroundColor: blue[50],
81 | borderRightColor: blue[50],
82 | cursor: "pointer",
83 | },
84 | },
85 | paidChip: {
86 | backgroundColor: "#66BB6A",
87 | color: "#fff",
88 | },
89 | unfulfilledChip: {
90 | backgroundColor: "#F44336",
91 | color: "#fff",
92 | },
93 | active: {
94 | color: theme.palette.primary.main,
95 | },
96 | even: {
97 | backgroundColor: "#fff",
98 | },
99 | }));
100 |
101 | export default function SimpleTable(props) {
102 | const classes = useStyles();
103 |
104 | const [lastUpdatedTime, setLastUpdatedTime] = React.useState("N/A");
105 | React.useEffect(() => {
106 | // props.dispatch(fetchProducts());
107 | setLastUpdatedTime(`${new Date().toLocaleString()}`);
108 | }, []);
109 |
110 | const [data, setData] = React.useState(props.orders);
111 |
112 | /* -1: unsorted/unused
113 | 0: is NOT ascending
114 | 1: is ascending
115 | */
116 | const [sortData, setSortData] = React.useState({
117 | id: -1,
118 | });
119 |
120 | const sortById = () => {
121 | const dataset = [...data];
122 |
123 | if (sortData.id < 1) {
124 | console.log("Unsorted. Sorting By Ascending");
125 | dataset.sort(function (a, b) {
126 | return a.orderId - b.orderId;
127 | });
128 | setSortData({
129 | ...sortData,
130 | id: 1,
131 | });
132 | } else {
133 | console.log("Sorted. Sorting By Descending");
134 | dataset.reverse();
135 | setSortData({
136 | ...sortData,
137 | id: 0,
138 | });
139 | }
140 | setData(dataset);
141 | };
142 |
143 | // Placeholder hooks for pagination.
144 | const [page, setPage] = React.useState(1);
145 | const handleChange = (event, value) => {
146 | setPage(value);
147 | };
148 |
149 | function StatusChip(props) {
150 | if (props.status === "Paid") {
151 | return Paid;
152 | } else {
153 | return (
154 |
158 | );
159 | }
160 | }
161 |
162 | function Fulfillment(props) {
163 | switch (props.fulfillment) {
164 | case "Processing":
165 | return (
166 | }
168 | label={props.fulfillment}
169 | style={{
170 | color: "#263238",
171 | backgroundColor: "#ECEFF1",
172 | paddingLeft: 2,
173 | }}
174 | />
175 | );
176 |
177 | case "Confirmed":
178 | return (
179 |
182 | }
183 | label={props.fulfillment}
184 | style={{
185 | color: "#FF6F00",
186 | backgroundColor: "#FFECB3",
187 | paddingLeft: 2,
188 | }}
189 | />
190 | );
191 |
192 | case "Packing":
193 | return (
194 | }
196 | label={props.fulfillment}
197 | style={{
198 | color: "#33691E",
199 | backgroundColor: "#DCEDC8",
200 | paddingLeft: 2,
201 | }}
202 | />
203 | );
204 |
205 | case "Shipped":
206 | return (
207 |
210 | }
211 | label={props.fulfillment}
212 | style={{
213 | color: "#0D47A1",
214 | backgroundColor: "#BBDEFB",
215 | paddingLeft: 2,
216 | }}
217 | />
218 | );
219 |
220 | case "Unfulfilled":
221 | return (
222 | }
224 | label={props.fulfillment}
225 | style={{
226 | color: "#b71c1c",
227 | backgroundColor: "#ffcdd2",
228 | paddingLeft: 2,
229 | }}
230 | />
231 | );
232 |
233 | default:
234 | return (
235 | }
237 | label={props.fulfillment}
238 | />
239 | );
240 | }
241 | }
242 |
243 | return (
244 |
245 |
246 |
247 |
248 |
249 |
250 |
255 |
256 | Order ID
257 |
258 |
259 | -1 && classes.active)}
262 | />
263 |
264 |
265 |
266 |
267 | Created
268 |
269 |
270 | Customer
271 |
272 |
273 | Email
274 |
275 |
276 | Fulfillment
277 |
278 |
279 | Total
280 |
281 |
282 | Status
283 |
284 |
285 | Last Updated
286 |
287 |
288 |
289 |
290 |
291 | {data.map((row, index) => (
292 |
296 | props.pageControl({
297 | manage: true,
298 | orderDetails: data[index],
299 | root: false,
300 | purchaseOrder: false,
301 | })
302 | }
303 | >
304 |
305 | {row.orderId}
306 |
307 | {row.created}
308 | {`${row.firstName} ${row.lastName}`}
309 | {row.email}
310 |
311 |
312 |
313 | {row.total}
314 |
315 |
316 |
317 | {row.updated}
318 |
319 |
320 |
321 |
322 |
323 |
324 | ))}
325 |
326 |
327 |
328 |
329 |
330 |
331 | {/*
332 | 1-{data.length} of {Math.floor(Math.random() * 100)} results
333 | */}
334 |
335 |
336 |
337 |
338 | Orders up to date. Last retrieved at {lastUpdatedTime}
339 |
340 |
341 |
342 |
343 |
344 | );
345 | }
346 |
--------------------------------------------------------------------------------
/src/Orders/Search.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import MenuItem from '@material-ui/core/MenuItem';
4 | import TextField from '@material-ui/core/TextField';
5 | import Button from '@material-ui/core/Button';
6 | import Box from '@material-ui/core/Box';
7 | import SearchIcon from '@material-ui/icons/Search';
8 |
9 | const useStyles = makeStyles(theme => ({
10 | container: {
11 | display: 'flex',
12 | flexWrap: 'wrap',
13 | },
14 | textField: {
15 | marginLeft: theme.spacing(1),
16 | width: '275px'
17 | },
18 | typeField: {
19 | marginRight: theme.spacing(1),
20 | width: '175px'
21 | },
22 | dense: {
23 | marginTop: theme.spacing(2),
24 | },
25 | menu: {
26 | width: 200,
27 | },
28 | rightIcon: {
29 | marginLeft: theme.spacing(1),
30 | },
31 | button: {
32 | boxShadow: 'none',
33 | }
34 | }));
35 |
36 |
37 |
38 | export default function Search() {
39 | const classes = useStyles();
40 | const [values, setValues] = React.useState({
41 | type: '',
42 | });
43 |
44 | const [queryValue, setQueryValue] = React.useState('');
45 |
46 | const attributes = [
47 | {
48 | value: 'Order ID',
49 | label: 'Order ID',
50 | },
51 | {
52 | value: 'Email',
53 | label: 'Email',
54 | },
55 | ];
56 |
57 | function handleChange(event) {
58 | setValues(oldValues => ({
59 | type: event.target.value,
60 | }));
61 | }
62 |
63 | function handleQueryValue(e) {
64 | setQueryValue(e.target.value);
65 | }
66 |
67 | return (
68 |
69 |
Query Orders
70 |
86 | {attributes.map(option => (
87 |
90 | ))}
91 |
92 |
101 |
102 |
106 |
107 |
108 |
109 | );
110 | }
--------------------------------------------------------------------------------
/src/Orders/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles, useTheme } from '@material-ui/core/styles';
3 | import Drawer from '@material-ui/core/Drawer';
4 | import List from '@material-ui/core/List';
5 | import Divider from '@material-ui/core/Divider';
6 | import ListItem from '@material-ui/core/ListItem';
7 | import ListItemIcon from '@material-ui/core/ListItemIcon';
8 | import ListItemText from '@material-ui/core/ListItemText';
9 | import InboxIcon from '@material-ui/icons/MoveToInbox';
10 | import MailIcon from '@material-ui/icons/Mail';
11 | import EditIcon from '@material-ui/icons/Edit';
12 | import SearchIcon from '@material-ui/icons/Search';
13 | import RefreshIcon from '@material-ui/icons/Refresh';
14 | import Typography from '@material-ui/core/Typography';
15 | import Modal from '@material-ui/core/Modal';
16 | import Backdrop from '@material-ui/core/Backdrop';
17 | import Fade from '@material-ui/core/Fade';
18 | import blue from "@material-ui/core/colors/blue";
19 |
20 | import Search from './Search';
21 |
22 | const drawerWidth = 120;
23 |
24 | const useStyles = makeStyles(theme => ({
25 | root: {
26 | display: 'flex',
27 | zIndex: 0,
28 | backgroundColor: blue[500],
29 | },
30 | appBarShift: {
31 | marginLeft: drawerWidth,
32 | width: `calc(100% - ${drawerWidth}px)`,
33 | transition: theme.transitions.create(['width', 'margin'], {
34 | easing: theme.transitions.easing.sharp,
35 | duration: theme.transitions.duration.enteringScreen,
36 | }),
37 | },
38 | menuButton: {
39 | marginRight: 36,
40 | },
41 | hide: {
42 | display: 'none',
43 | },
44 | drawer: {
45 | width: drawerWidth,
46 | flexShrink: 0,
47 | whiteSpace: 'nowrap',
48 | },
49 | toolbar: {
50 | display: 'flex',
51 | alignItems: 'center',
52 | justifyContent: 'flex-end',
53 | // padding: theme.spacing(0, 1),
54 | ...theme.mixins.toolbar,
55 | },
56 | content: {
57 | flexGrow: 1,
58 | padding: theme.spacing(3),
59 | },
60 | icon: {
61 | minWidth: 35
62 | },
63 | text: {
64 | color: 'rgba(0, 0, 0, 0.54)',
65 | },
66 | modal: {
67 | display: 'flex',
68 | alignItems: 'center',
69 | justifyContent: 'center',
70 | },
71 | paper: {
72 | padding: theme.spacing(2, 4, 3),
73 | backgroundColor: '#fff'
74 | },
75 | }));
76 |
77 | export default function MiniDrawer() {
78 | const classes = useStyles();
79 |
80 | const [open, setOpen] = React.useState(false);
81 |
82 | const handleOpen = () => {
83 | setOpen(true);
84 | };
85 |
86 | const handleClose = () => {
87 | setOpen(false);
88 | };
89 |
90 | return (
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | Search } />
102 |
103 |
104 |
105 |
106 |
107 |
108 | Update } />
111 |
112 |
113 |
114 |
115 |
116 |
117 | Message } />
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | Archive } />
132 |
133 |
134 |
135 |
136 |
137 |
138 | Refresh } />
141 |
142 |
143 |
144 |
145 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | );
166 | }
--------------------------------------------------------------------------------
/src/Settings/SettingTabs.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Tabs from '@material-ui/core/Tabs';
5 | import Tab from '@material-ui/core/Tab';
6 | import Typography from '@material-ui/core/Typography';
7 | import Box from '@material-ui/core/Box';
8 |
9 |
10 | import TabButton from './TabButton';
11 | import Account from './Views/Account';
12 | import Password from './Views/Password';
13 | import Notifications from './Views/Notifications';
14 | import UserGroups from './Views/UserGroups';
15 |
16 | function TabPanel(props) {
17 | const { children, value, index, ...other } = props;
18 |
19 | return (
20 |
29 | {children}
30 |
31 | );
32 | }
33 |
34 | TabPanel.propTypes = {
35 | children: PropTypes.node,
36 | index: PropTypes.any.isRequired,
37 | value: PropTypes.any.isRequired,
38 | };
39 |
40 | function a11yProps(index) {
41 | return {
42 | id: `vertical-tab-${index}`,
43 | 'aria-controls': `vertical-tabpanel-${index}`,
44 | };
45 | }
46 |
47 | const useStyles = makeStyles(theme => ({
48 | root: {
49 | flexGrow: 1,
50 | backgroundColor: theme.palette.background.paper,
51 | display: 'flex',
52 | height: 500,
53 | },
54 | tabs: {
55 | borderRight: `1px solid rgba(0,0,0,.08)`,
56 | textAlign: 'left'
57 | },
58 | leftIcon: {
59 | marginRight: theme.spacing(1),
60 | },
61 | }));
62 |
63 |
64 | export default function VerticalTabs() {
65 | const classes = useStyles();
66 | const [value, setValue] = React.useState(0);
67 |
68 | function handleChange(event, newValue) {
69 | setValue(newValue);
70 | }
71 |
72 | return (
73 |
74 |
84 | } {...a11yProps(0)} />
85 | } {...a11yProps(1)} />
86 | } {...a11yProps(2)} />
87 | } {...a11yProps(3)} />
88 | } {...a11yProps(4)} />
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | Help
104 |
105 |
106 | );
107 | }
--------------------------------------------------------------------------------
/src/Settings/Settings.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Container from '@material-ui/core/Container';
4 | import Typography from '@material-ui/core/Typography';
5 | import Paper from '@material-ui/core/Paper';
6 |
7 | import SettingTabs from './SettingTabs';
8 | import PageTitle from './../Common/PageTitle';
9 |
10 | const useStyles = makeStyles(theme => ({
11 | paper: {
12 | // boxShadow: '0 0 1px 0 rgba(0,0,0,.22)',
13 | boxShadow: '0 0 11px #eaf0f6',
14 | borderRadius: '4px',
15 | overflow: 'hidden',
16 | marginTop: theme.spacing(4),
17 | },
18 | title: {
19 | fontFamily: 'ApercuMedium',
20 | marginTop: theme.spacing(4),
21 | }
22 | }));
23 |
24 | export default function Settings() {
25 |
26 | const classes = useStyles();
27 |
28 | return (
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
--------------------------------------------------------------------------------
/src/Settings/TabButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Box from '@material-ui/core/Box';
4 | import Typography from '@material-ui/core/Typography';
5 | import AccountIcon from '@material-ui/icons/AccountCircle';
6 | import PasswordIcon from '@material-ui/icons/Lock';
7 | import UserGroupsIcon from '@material-ui/icons/SupervisorAccount';
8 | import NotificationsIcon from '@material-ui/icons/Notifications';
9 | import HelpIcon from '@material-ui/icons/Help';
10 | import Grid from '@material-ui/core/Grid';
11 |
12 | const useStyles = makeStyles(theme => ({
13 | button: {
14 | marginTop: '4px',
15 | fontSize: '1em',
16 | width: 175,
17 | marginLeft: theme.spacing(2),
18 | fontFamily: 'ApercuMedium',
19 | },
20 | leftIcon: {
21 | marginRight: theme.spacing(2),
22 | },
23 | text: {
24 | marginBottom: '5px',
25 | }
26 | }));
27 |
28 | export default function TabButton(props) {
29 | const classes = useStyles();
30 |
31 | function GetIcon() {
32 | switch (props.icon) {
33 | case 'account':
34 | return ();
35 | case 'password':
36 | return ();
37 | case 'usergroups':
38 | return ();
39 | case 'notifications':
40 | return ();
41 | case 'help':
42 | return ();
43 | default:
44 | throw new SyntaxError('Invalid button type');
45 | }
46 | }
47 |
48 | return (
49 |
50 |
51 |
52 |
53 |
54 | { props.text }
55 |
56 |
57 |
58 | )
59 | }
--------------------------------------------------------------------------------
/src/Settings/Views/Account.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Grid from '@material-ui/core/Grid';
4 | import Typography from '@material-ui/core/Typography';
5 | import Edit from '@material-ui/icons/KeyboardArrowRight';
6 | import Divider from '@material-ui/core/Divider';
7 | import Modal from '@material-ui/core/Modal';
8 |
9 | import Loading from './../../Common/Loading';
10 | import FieldRow from './../../Common/FieldRow';
11 | import FieldModal from './../../Common/FieldModal';
12 |
13 | const useStyles = makeStyles(theme => ({
14 | root: {
15 | flexGrow: 1,
16 | },
17 | paper: {
18 | padding: theme.spacing(2),
19 | textAlign: 'center',
20 | color: theme.palette.text.secondary,
21 | },
22 | rightIcon: {
23 | fontSize: '1em',
24 | },
25 | textField: {
26 | marginLeft: theme.spacing(1),
27 | marginRight: theme.spacing(1),
28 | },
29 | label: {
30 | letterSpacing: '.07272727em',
31 | fontSize: '.6875rem',
32 | fontWeight: 500,
33 | lineHeight: '1rem',
34 | textTransform: 'uppercase',
35 | color: '#5f6368',
36 | marginLeft: '10px',
37 | },
38 | row: {
39 | // margin: theme.spacing(1),
40 | paddingTop: theme.spacing(1),
41 | '&:hover': {
42 | backgroundColor: '#f5f5f5',
43 | cursor: 'pointer'
44 | }
45 | },
46 | modal: {
47 | display: 'flex',
48 | alignItems: 'center',
49 | justifyContent: 'center',
50 | },
51 | }));
52 |
53 | function a() {
54 | return alert('hi');
55 | }
56 |
57 | export default function Account() {
58 | const classes = useStyles();
59 |
60 | const [loading, setLoading] = React.useState(true);
61 |
62 | const [fieldModal, setFieldModal] = React.useState({
63 | open: false,
64 | field: {
65 | label: null,
66 | value: null
67 | }
68 | });
69 |
70 | const showFieldModal = (field) => {
71 | setFieldModal({
72 | open: true,
73 | field,
74 | });
75 | };
76 |
77 | const handleClose = () => {
78 | setFieldModal({
79 | ...fieldModal,
80 | open: false
81 | });
82 | };
83 |
84 | setTimeout(() => {
85 | setLoading(false);
86 | }, 1000);
87 |
88 | function Content() {
89 | return (
90 |
91 |
92 | Profile
93 |
94 |
95 |
96 |
97 |
98 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | );
116 | }
117 |
118 |
119 | return (
120 |
121 | {loading ? (
122 |
123 | ) : (
124 |
125 | )}
126 |
127 | )
128 | }
--------------------------------------------------------------------------------
/src/Settings/Views/Notifications.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Grid from '@material-ui/core/Grid';
5 | import Paper from '@material-ui/core/Paper';
6 | import Typography from '@material-ui/core/Typography';
7 | import Edit from '@material-ui/icons/KeyboardArrowRight';
8 | import TextField from '@material-ui/core/TextField';
9 | import Divider from '@material-ui/core/Divider';
10 | import Checkbox from '@material-ui/core/Checkbox';
11 |
12 | const useStyles = makeStyles(theme => ({
13 | root: {
14 | flexGrow: 1,
15 | },
16 | paper: {
17 | padding: theme.spacing(2),
18 | textAlign: 'center',
19 | color: theme.palette.text.secondary,
20 | },
21 | rightIcon: {
22 | fontSize: '1em',
23 | },
24 | textField: {
25 | marginLeft: theme.spacing(1),
26 | marginRight: theme.spacing(1),
27 | },
28 | label: {
29 | letterSpacing: '.07272727em',
30 | fontSize: '.6875rem',
31 | fontWeight: 500,
32 | lineHeight: '1rem',
33 | textTransform: 'uppercase',
34 | color: '#5f6368',
35 | marginLeft: '10px',
36 | },
37 | row: {
38 | // margin: theme.spacing(1),
39 | paddingTop: theme.spacing(1),
40 | '&:hover': {
41 | backgroundColor: '#f5f5f5',
42 | cursor: 'pointer'
43 | }
44 | },
45 | checkbox: {
46 | marginLeft: theme.spacing(1),
47 | marginBottom: theme.spacing(1)
48 | }
49 | }));
50 |
51 | function a() {
52 | return alert('hi');
53 | }
54 |
55 | export default function Notifications() {
56 | const classes = useStyles();
57 |
58 | const [state, setState] = React.useState({
59 | newProductEmail: true,
60 | newProductMobile: false,
61 | });
62 |
63 | const handleChange = name => event => {
64 | setState({ ...state, [name]: event.target.checked });
65 | };
66 |
67 | return (
68 |
69 |
70 | Notifications
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Email
80 |
81 |
82 |
83 |
84 | Mobile
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | New Product Is Created
97 |
98 |
99 |
100 |
110 |
111 |
112 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | )
131 | }
--------------------------------------------------------------------------------
/src/Settings/Views/Password.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import Grid from '@material-ui/core/Grid';
5 | import Paper from '@material-ui/core/Paper';
6 | import Typography from '@material-ui/core/Typography';
7 | import Edit from '@material-ui/icons/KeyboardArrowRight';
8 | import TextField from '@material-ui/core/TextField';
9 | import Divider from '@material-ui/core/Divider';
10 |
11 | const useStyles = makeStyles(theme => ({
12 | root: {
13 | flexGrow: 1,
14 | },
15 | paper: {
16 | padding: theme.spacing(2),
17 | textAlign: 'center',
18 | color: theme.palette.text.secondary,
19 | },
20 | rightIcon: {
21 | fontSize: '1em',
22 | },
23 | textField: {
24 | marginLeft: theme.spacing(1),
25 | marginRight: theme.spacing(1),
26 | },
27 | label: {
28 | letterSpacing: '.07272727em',
29 | fontSize: '.6875rem',
30 | fontWeight: 500,
31 | lineHeight: '1rem',
32 | textTransform: 'uppercase',
33 | color: '#5f6368',
34 | marginLeft: '10px',
35 | },
36 | row: {
37 | // margin: theme.spacing(1),
38 | paddingTop: theme.spacing(1),
39 | '&:hover': {
40 | backgroundColor: '#f5f5f5',
41 | cursor: 'pointer'
42 | }
43 | }
44 | }));
45 |
46 | function a() {
47 | return alert('hi');
48 | }
49 |
50 | export default function Password() {
51 | const classes = useStyles();
52 |
53 | const date = new Date().toLocaleString('en-US');
54 |
55 | return (
56 |
57 |
58 | Password
59 |
60 |
61 |
62 |
63 |
64 |
65 | Password
66 |
67 |
68 |
69 |
70 | •••••••••
71 |
72 |
73 |
74 |
75 | Last changed {date}
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | 2-Step Verification
90 |
91 |
92 |
93 |
94 | Disabled
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | )
106 | }
--------------------------------------------------------------------------------
/src/Settings/Views/UserGroups.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Grid from '@material-ui/core/Grid';
4 | import Typography from '@material-ui/core/Typography';
5 | import Edit from '@material-ui/icons/KeyboardArrowRight';
6 | import Divider from '@material-ui/core/Divider';
7 | import Table from '@material-ui/core/Table';
8 | import TableBody from '@material-ui/core/TableBody';
9 | import TableCell from '@material-ui/core/TableCell';
10 | import TableHead from '@material-ui/core/TableHead';
11 | import TableRow from '@material-ui/core/TableRow';
12 | import Paper from '@material-ui/core/Paper';
13 | import Button from '@material-ui/core/Button';
14 | import AddIcon from '@material-ui/icons/Add';
15 | import Container from '@material-ui/core/Container';
16 |
17 | const useStyles = makeStyles(theme => ({
18 | root: {
19 | flexGrow: 1,
20 | },
21 | paper: {
22 | padding: theme.spacing(2),
23 | textAlign: 'center',
24 | color: theme.palette.text.secondary,
25 | },
26 | rightIcon: {
27 | fontSize: '1em',
28 | },
29 | textField: {
30 | marginLeft: theme.spacing(1),
31 | marginRight: theme.spacing(1),
32 | },
33 | label: {
34 | letterSpacing: '.07272727em',
35 | fontSize: '.6875rem',
36 | fontWeight: 500,
37 | lineHeight: '1rem',
38 | textTransform: 'uppercase',
39 | color: '#5f6368',
40 | },
41 | row: {
42 | // margin: theme.spacing(1),
43 | paddingTop: theme.spacing(1),
44 | '&:hover': {
45 | backgroundColor: '#f5f5f5',
46 | cursor: 'pointer'
47 | }
48 | },
49 | paper: {
50 | width: '100%',
51 | marginTop: theme.spacing(2),
52 | overflowX: 'auto',
53 | boxShadow: '0 0 1px 0 rgba(0,0,0,.22)',
54 | },
55 | rightIcon: {
56 | marginLeft: theme.spacing(1),
57 | },
58 | iconSmall: {
59 | fontSize: 20,
60 | },
61 | button: {
62 | boxShadow: 'none',
63 | fontFamily: 'ApercuMedium',
64 | marginTop: theme.spacing(4),
65 | },
66 | }));
67 |
68 | function a() {
69 | return alert('hi');
70 | }
71 |
72 | export default function Password() {
73 | const classes = useStyles();
74 |
75 | const date = new Date().toLocaleString('en-US');
76 |
77 | return (
78 |
79 |
80 | User Groups
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | Login
90 |
91 |
92 |
93 |
94 | Password
95 |
96 |
97 |
98 |
99 | Email
100 |
101 |
102 |
103 |
104 | Role
105 |
106 |
107 |
108 |
109 | Date Created
110 |
111 |
112 |
113 |
114 |
115 |
116 | bobby.darcy
117 | •••••••••
118 | bobby.darcy@company.com
119 | Admin
120 | 01/01/2010
121 |
122 |
123 | boris.chan
124 | •••••••••
125 | boris.chan@company.com
126 | User
127 | 01/01/2013
128 |
129 |
130 |
131 |
132 |
133 |
137 |
138 |
139 | )
140 | }
--------------------------------------------------------------------------------
/src/Team/Member.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles, withStyles } from "@material-ui/core/styles";
3 | import clsx from "clsx";
4 | import Card from "@material-ui/core/Card";
5 | import CardHeader from "@material-ui/core/CardHeader";
6 | import CardMedia from "@material-ui/core/CardMedia";
7 | import CardContent from "@material-ui/core/CardContent";
8 | import CardActions from "@material-ui/core/CardActions";
9 | import Button from "@material-ui/core/Button";
10 | import Avatar from "@material-ui/core/Avatar";
11 | import IconButton from "@material-ui/core/IconButton";
12 | import Typography from "@material-ui/core/Typography";
13 | import CommentIcon from "@material-ui/icons/Comment";
14 | import ThumbUpIcon from "@material-ui/icons/ThumbUp";
15 | import ExpandMoreIcon from "@material-ui/icons/MoreHoriz";
16 | import Divider from "@material-ui/core/Divider";
17 | import Badge from "@material-ui/core/Badge";
18 | import Box from "@material-ui/core/Box";
19 |
20 | import getAvatar from "../Common/AnimalAvatars";
21 |
22 | const useStyles = makeStyles((theme) => ({
23 | outerCard: {
24 | boxShadow: "0 0 11px #eaf0f6",
25 | border: "1px solid #eaf0f6",
26 | "&:hover": {},
27 | },
28 | card: {
29 | borderColor: "#fff",
30 | borderStyle: "solid",
31 | borderBottomWidth: "3px",
32 | borderLeftWidth: "0px",
33 | borderTopWidth: "0px",
34 | borderRightWidth: "0px",
35 | "&:hover": {
36 | borderColor: theme.palette.primary.light,
37 | borderStyle: "solid",
38 | borderBottomWidth: "3px",
39 | },
40 | },
41 | media: {
42 | height: 0,
43 | paddingTop: "56.25%", // 16:9
44 | },
45 | expand: {
46 | marginLeft: "auto",
47 | },
48 | expandOpen: {
49 | transform: "rotate(180deg)",
50 | },
51 | avatar: {
52 | width: theme.spacing(7),
53 | height: theme.spacing(7),
54 | },
55 | rightIcon: {
56 | marginLeft: theme.spacing(1),
57 | },
58 | button: {
59 | marginLeft: "auto",
60 | },
61 | name: {
62 | fontFamily: "ApercuMedium",
63 | },
64 | subtitle: {
65 | color: theme.palette.action.active,
66 | },
67 | }));
68 |
69 | export default function Member(props) {
70 | const classes = useStyles();
71 |
72 | const handleClick = () => {
73 | if (props.openModal) {
74 | props.openModal(props.member);
75 | }
76 | };
77 |
78 | return (
79 |
80 |
81 |
88 | }
89 | action={
90 |
91 |
92 |
93 | }
94 | title={props.title}
95 | subheader={props.date}
96 | />
97 |
98 |
99 | {props.member.firstName} {props.member.lastName}
100 |
101 |
102 | {props.member.role}
103 |
104 |
108 |
109 | {props.member.email}
110 |
111 |
112 | {props.member.phone}
113 |
114 |
115 |
116 |
117 | );
118 | }
119 |
--------------------------------------------------------------------------------
/src/Team/Profile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Container,
4 | Box,
5 | makeStyles,
6 | Avatar,
7 | Typography,
8 | Divider,
9 | TextField,
10 | Grid,
11 | Button,
12 | } from "@material-ui/core";
13 |
14 | const useStyles = makeStyles((theme) => ({
15 | root: {},
16 | paper: {
17 | backgroundColor: theme.palette.background.paper,
18 | boxShadow: "0 20px 60px -2px rgba(27,33,58,.4)",
19 | padding: theme.spacing(2, 4, 3),
20 | borderRadius: "8px",
21 | width: 375,
22 | },
23 | large: {
24 | width: theme.spacing(12),
25 | height: theme.spacing(12),
26 | },
27 | name: {
28 | marginTop: theme.spacing(2),
29 | fontFamily: "ApercuBold",
30 | },
31 | role: {
32 | marginTop: theme.spacing(2),
33 | marginBottom: theme.spacing(2),
34 | },
35 | description: {
36 | marginTop: theme.spacing(2),
37 | marginBottom: theme.spacing(2),
38 | },
39 | resize: {
40 | fontSize: "0.875em",
41 | },
42 | }));
43 |
44 | export default function Profiles(props) {
45 | const classes = useStyles();
46 |
47 | const {
48 | id,
49 | firstName,
50 | lastName,
51 | avatar,
52 | role,
53 | email,
54 | phone,
55 | location,
56 | description,
57 | } = props.member;
58 |
59 | return (
60 |
61 |
62 |
63 |
64 |
65 | {`${firstName} ${lastName}`}
66 |
67 |
68 | {`${role}`}
69 |
70 |
71 |
84 |
85 |
86 |
96 |
97 |
98 |
108 |
109 |
110 | {/*
111 |
114 | */}
115 |
116 | );
117 | }
118 |
--------------------------------------------------------------------------------
/src/Team/Team.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import clsx from "clsx";
3 | import faker from "faker";
4 |
5 | import {
6 | Container,
7 | Typography,
8 | Box,
9 | makeStyles,
10 | Select,
11 | MenuItem,
12 | IconButton,
13 | Grid,
14 | Avatar,
15 | Modal,
16 | Backdrop,
17 | Fade,
18 | } from "@material-ui/core";
19 |
20 | import Table from "@material-ui/core/Table";
21 | import TableBody from "@material-ui/core/TableBody";
22 | import TableCell from "@material-ui/core/TableCell";
23 | import TableContainer from "@material-ui/core/TableContainer";
24 | import TableHead from "@material-ui/core/TableHead";
25 | import TableRow from "@material-ui/core/TableRow";
26 | import Paper from "@material-ui/core/Paper";
27 | import blue from "@material-ui/core/colors/blue";
28 | import grey from "@material-ui/core/colors/grey";
29 |
30 | import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
31 | import MoreIcon from "@material-ui/icons/MoreVert";
32 |
33 | import SearchIcon from "@material-ui/icons/Search";
34 | import ViewModuleIcon from "@material-ui/icons/ViewModule";
35 | import ViewHeadlineIcon from "@material-ui/icons/ViewHeadline";
36 | import UnfoldMoreIcon from "@material-ui/icons/UnfoldMore";
37 |
38 | import PageTitle from "../Common/PageTitle";
39 | import Member from "./Member";
40 | import Profile from "./Profile";
41 |
42 | const useStyles = makeStyles((theme) => ({
43 | menuText: {
44 | fontFamily: "ApercuMedium",
45 | fontSize: "0.875em",
46 | marginBottom: "1px",
47 | color: theme.palette.action.active,
48 | },
49 | select: {
50 | marginLeft: theme.spacing(1.5),
51 | fontSize: "0.875em",
52 | width: "75px",
53 | },
54 | selectGroup: {
55 | // height: "50px",
56 | },
57 | button: {
58 | margin: theme.spacing(1),
59 | },
60 | avatar: {
61 | marginLeft: theme.spacing(1),
62 | },
63 | root: {
64 | boxShadow: "0 0 11px #eaf0f6",
65 | borderRadius: "4px",
66 | overflow: "hidden",
67 | border: "1px solid #eaf0f6",
68 | },
69 | table: {},
70 | tableHead: {
71 | fontFamily: "ApercuMedium",
72 | fontSize: "0.875rem",
73 | color: "#525f7f",
74 | paddingLeft: theme.spacing(2),
75 | paddingTop: theme.spacing(2),
76 | paddingBottom: theme.spacing(2),
77 | // backgroundColor: grey[50],
78 | },
79 | tableHeadAvatar: {
80 | width: "50px",
81 | },
82 | tableHeadCell: {
83 | padding: theme.spacing(0),
84 | },
85 | active: {
86 | color: theme.palette.primary.main,
87 | },
88 | tableRow: {
89 | borderColor: "#fff",
90 | borderStyle: "solid",
91 | borderLeftWidth: "3px",
92 | borderBottomWidth: "0px",
93 | borderTopWidth: "0px",
94 | borderRightWidth: "3px",
95 | "&:hover": {
96 | borderColor: theme.palette.primary.light,
97 | borderStyle: "solid",
98 | borderLeftWidth: "3px",
99 | backgroundColor: blue[50],
100 | borderRightColor: blue[50],
101 | },
102 | },
103 | tableHeadRow: {
104 | backgroundColor: "#fff",
105 | borderColor: "#fff",
106 | borderStyle: "solid",
107 | borderLeftWidth: "3px",
108 | },
109 | modal: {
110 | display: "flex",
111 | alignItems: "center",
112 | justifyContent: "center",
113 | },
114 | active: {
115 | color: theme.palette.primary.main,
116 | },
117 | }));
118 |
119 | const populate = (n = 1) => {
120 | const members = [];
121 |
122 | for (let i = 0; i < n; i++) {
123 | members.push({
124 | id: i,
125 | firstName: faker.name.firstName(),
126 | lastName: faker.name.lastName(),
127 | avatar: faker.image.avatar(),
128 | role: faker.name.jobTitle(),
129 | email: faker.internet.email(),
130 | phone: faker.phone.phoneNumber(),
131 | location: `${faker.address.city()}, ${faker.address.stateAbbr()}`,
132 | description: faker.hacker.phrase(),
133 | });
134 | }
135 |
136 | return members;
137 | };
138 |
139 | const members = populate(7);
140 |
141 | export default function Team() {
142 | const classes = useStyles();
143 |
144 | const [data, setData] = React.useState(members);
145 |
146 | /* -1: unsorted/unused
147 | 0: is NOT ascending
148 | 1: is ascending
149 | */
150 | const [sortData, setSortData] = React.useState({
151 | name: -1,
152 | role: -1,
153 | email: -1,
154 | });
155 |
156 | const sortByAttribute = (attribute) => {
157 | const dataset = [...data];
158 |
159 | if (sortData[attribute] < 1) {
160 | dataset.sort(function (a, b) {
161 | const al = a[attribute].toLowerCase();
162 | const bl = b[attribute].toLowerCase();
163 | // No swap if b is greater (ascending).
164 | if (al < bl) {
165 | return -1;
166 | }
167 | if (al > bl) {
168 | return 1;
169 | }
170 | return 0;
171 | });
172 | const sortDataObj = {
173 | name: -1,
174 | role: -1,
175 | email: -1,
176 | };
177 | sortDataObj[attribute] = 1;
178 | setSortData(sortDataObj);
179 | } else {
180 | dataset.reverse();
181 | const sortDataObj = {
182 | name: -1,
183 | role: -1,
184 | email: -1,
185 | };
186 | sortDataObj[attribute] = 0;
187 | setSortData(sortDataObj);
188 | }
189 | setData(dataset);
190 | };
191 |
192 | const sortByName = () => {
193 | const dataset = [...data];
194 | // attribute is unsorted.
195 | if (sortData.name < 1) {
196 | // alphabetic sort
197 | dataset.sort(function (a, b) {
198 | const al = a.firstName.toLowerCase();
199 | const bl = b.firstName.toLowerCase();
200 | // No swap if b is greater (ascending).
201 | if (al < bl) {
202 | return -1;
203 | }
204 | if (al > bl) {
205 | return 1;
206 | }
207 | return 0;
208 | });
209 | setSortData({
210 | name: -1,
211 | role: -1,
212 | email: -1,
213 | name: 1,
214 | });
215 | } else {
216 | dataset.reverse();
217 | setSortData({
218 | name: -1,
219 | role: -1,
220 | email: -1,
221 | name: 0,
222 | });
223 | }
224 | setData(dataset);
225 | };
226 |
227 | const [modal, setModal] = React.useState({
228 | open: false,
229 | member: null,
230 | });
231 |
232 | const showModal = (member) => {
233 | setModal({
234 | open: true,
235 | member,
236 | });
237 | };
238 |
239 | const handleClose = () => {
240 | setModal({
241 | ...modal,
242 | open: false,
243 | });
244 | };
245 |
246 | const [showGrid, setShowGrid] = React.useState(true);
247 | const [sortBy, setSortBy] = React.useState("all");
248 |
249 | const handleChange = (event) => {
250 | setSortBy(event.target.value);
251 |
252 | switch (event.target.value) {
253 | case "name":
254 | sortByName();
255 | return;
256 | case "role":
257 | sortByAttribute("role");
258 | return;
259 | case "email":
260 | sortByAttribute("email");
261 | return;
262 | default:
263 | return;
264 | }
265 | };
266 |
267 | const handleModalClose = () => {
268 | setModal({
269 | ...modal,
270 | open: false,
271 | });
272 | };
273 |
274 | function DataDisplay() {
275 | function GridView() {
276 | return (
277 |
284 | {data.map((member) => (
285 |
286 |
287 |
288 | ))}
289 |
290 | );
291 | }
292 |
293 | function TableView() {
294 | return (
295 |
296 |
297 |
298 |
299 |
300 |
301 |
306 | Name
307 |
311 | -1 && classes.active)}
314 | />
315 |
316 |
317 |
318 |
319 |
324 | Role
325 | sortByAttribute("role")}
328 | >
329 | -1 && classes.active)}
332 | />
333 |
334 |
335 |
336 |
337 |
342 | Email
343 | sortByAttribute("email")}
346 | >
347 | -1 && classes.active)}
350 | />
351 |
352 |
353 |
354 |
355 | Phone
356 |
357 |
358 |
359 |
360 |
361 | {data.map((member) => (
362 |
363 |
364 |
369 |
370 |
371 | {member.firstName} {member.lastName}
372 |
373 | {member.role}
374 | {member.email}
375 | {member.phone}
376 |
377 | showModal(member)}
381 | >
382 |
383 |
384 |
385 |
386 | ))}
387 |
388 |
389 |
390 | );
391 | }
392 |
393 | return showGrid ? : ;
394 | }
395 |
396 | return (
397 |
398 |
399 |
400 |
401 | setShowGrid(true)}
404 | >
405 |
406 |
407 | setShowGrid(false)}
410 | >
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 | {showGrid && (
419 |
425 | Sort by:
426 |
437 |
438 | )}
439 |
440 |
441 |
451 |
454 |
455 |
456 | );
457 | }
458 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Provider } from 'react-redux';
4 | import { applyMiddleware, createStore, compose } from 'redux';
5 | import thunk from 'redux-thunk';
6 | import rootReducer from './store/reducers/rootReducer';
7 | import jwtDecode from 'jwt-decode';
8 | import { setCurrentUser } from './store/actions/auth';
9 | import setAuthorizationToken from './utils/setAuthorizationToken';
10 | import getTokenTimeRemaining from './utils/getTokenTimeRemaining';
11 |
12 | import './index.css';
13 | import App from './App';
14 |
15 | // Redux dev-tools setup
16 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
17 |
18 | const store = createStore(
19 | rootReducer, composeEnhancers(applyMiddleware(thunk))
20 | )
21 |
22 | // Authorizes if user token is valid on page.
23 | if (localStorage.token) {
24 |
25 | const decodedToken = jwtDecode(localStorage.token);
26 | if (decodedToken.exp < new Date().getTime() / 1000) {
27 | console.log("EXPIRED");
28 | } else {
29 | setAuthorizationToken(localStorage.token);
30 | store.dispatch(setCurrentUser(decodedToken));
31 | getTokenTimeRemaining(decodedToken);
32 | }
33 | }
34 |
35 | ReactDOM.render(
36 |
37 |
38 | ,
39 | document.getElementById('root')
40 | );
41 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/store/actions/auth.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import setAuthorizationToken from "../../utils/setAuthorizationToken";
3 | import jwtDecode from "jwt-decode";
4 | import {
5 | SET_CURRENT_USER,
6 | SET_INVALID_CREDENTIALS,
7 | SET_INTERNAL_SERVER_ERROR,
8 | SET_CONNECTION_REFUSED_ERROR,
9 | } from "./types/types";
10 |
11 | const URL = "http://localhost:8080";
12 |
13 | export function setCurrentUser(user) {
14 | return {
15 | type: SET_CURRENT_USER,
16 | user,
17 | };
18 | }
19 |
20 | export function setInvalidCredentials(errorStatus) {
21 | return {
22 | type: SET_INVALID_CREDENTIALS,
23 | error: {
24 | status: errorStatus,
25 | message:
26 | "There was an error with your username or password combination. Please try again.",
27 | },
28 | };
29 | }
30 |
31 | export function setInternalServerError(errorStatus) {
32 | return {
33 | type: SET_INTERNAL_SERVER_ERROR,
34 | error: {
35 | status: errorStatus,
36 | message: "An unexpected error occurred. Please try again later.",
37 | },
38 | };
39 | }
40 |
41 | export function setConnectionRefusedError() {
42 | return {
43 | type: SET_CONNECTION_REFUSED_ERROR,
44 | error: {
45 | status: 502,
46 | message: "Bad Gateway - Connection refused. Please try again later.",
47 | },
48 | };
49 | }
50 |
51 | export function userSignInRequest(userData) {
52 | return async (dispatch) => {
53 | try {
54 | const res = await axios.post(`${URL}/login`, userData);
55 | if (res.headers.authorization) {
56 | // Expect "Bearer "
57 | const token = res.headers.authorization.substring(
58 | 7,
59 | res.headers.authorization.length
60 | );
61 | localStorage.setItem("token", token);
62 | setAuthorizationToken(token);
63 | // dispatch(setCurrentUser(jwtDecode(token)));
64 |
65 | const avatarId = await axios.get(
66 | `${URL}/users/avatar?username=${userData.username}`
67 | );
68 | dispatch(
69 | setCurrentUser({ ...jwtDecode(token), avatarId: avatarId.data })
70 | );
71 | }
72 | } catch (error) {
73 | if (error.response) {
74 | if (error.response.status === 403) {
75 | dispatch(setInvalidCredentials(error.response.status));
76 | } else {
77 | dispatch(setInternalServerError(error.response.status));
78 | }
79 | } else {
80 | dispatch(setConnectionRefusedError());
81 | }
82 | }
83 | };
84 | }
85 |
86 | export function userSignOutRequest() {
87 | return (dispatch) => {
88 | localStorage.removeItem("token");
89 | // Remove authorization header from future requests.
90 | setAuthorizationToken(false);
91 | dispatch(setCurrentUser({}));
92 | };
93 | }
94 |
--------------------------------------------------------------------------------
/src/store/actions/products.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { FETCH_PRODUCTS_SUCCESS, FETCH_PRODUCTS_ERROR, FETCH_PRODUCTS_PENDING } from './types/types';
3 |
4 | const URL = 'http://localhost:8080';
5 |
6 | export function fetchProductsPending() {
7 | return {
8 | type: FETCH_PRODUCTS_PENDING
9 | }
10 | }
11 |
12 | export function fetchProductsSuccess(products) {
13 | return {
14 | type: FETCH_PRODUCTS_SUCCESS,
15 | products
16 | }
17 | }
18 |
19 | export function fetchProductsError(error) {
20 | return {
21 | type: FETCH_PRODUCTS_ERROR,
22 | error
23 | }
24 | }
25 |
26 | export function fetchProducts() {
27 | return async (dispatch) => {
28 | try {
29 | dispatch(fetchProductsPending());
30 | const res = await axios.get(`${URL}/products`);
31 | if (res.data && res.status === 200) {
32 | console.log(res);
33 | dispatch(fetchProductsSuccess(res.data))
34 | }
35 | } catch (error) {
36 | dispatch(fetchProductsError(error));
37 | console.log(error);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/store/actions/types/types.js:
--------------------------------------------------------------------------------
1 | export const SET_CURRENT_USER = 'SET_CURRENT_USER';
2 | export const SET_INVALID_CREDENTIALS = 'SET_INVALID_CREDENTIALS';
3 | export const SET_INTERNAL_SERVER_ERROR = 'SET_INTERNAL_SERVER_ERROR';
4 | export const SET_CONNECTION_REFUSED_ERROR = 'SET_CONNECTION_REFUSED_ERROR';
5 |
6 | export const FETCH_PRODUCTS_PENDING = 'FETCH_PRODUCTS_PENDING';
7 | export const FETCH_PRODUCTS_SUCCESS = 'FETCH_PRODUCTS_SUCCESS';
8 | export const FETCH_PRODUCTS_ERROR = 'FETCH_PRODUCTS_ERROR';
9 |
--------------------------------------------------------------------------------
/src/store/reducers/auth.js:
--------------------------------------------------------------------------------
1 | import { SET_CURRENT_USER, SET_INVALID_CREDENTIALS, SET_INTERNAL_SERVER_ERROR, SET_CONNECTION_REFUSED_ERROR } from '../actions/types/types';
2 | import isEmpty from 'lodash/isEmpty';
3 |
4 | const initialState = {
5 | isAuthenticated: false,
6 | user: {},
7 | error: {}
8 | }
9 |
10 | export default (state = initialState, action = {}) => {
11 | switch (action.type) {
12 | case SET_CURRENT_USER:
13 | return {
14 | isAuthenticated: !isEmpty(action.user),
15 | user: action.user,
16 | error: {}
17 | }
18 |
19 | case SET_INVALID_CREDENTIALS:
20 | return {
21 | isAuthenticated: false,
22 | user: {},
23 | error: {
24 | message: action.error,
25 | variant: 'error'
26 | }
27 | }
28 |
29 | case SET_INTERNAL_SERVER_ERROR:
30 | return {
31 | isAuthenticated: false,
32 | user: {},
33 | error: {
34 | message: action.error,
35 | variant: 'warning'
36 | }
37 | }
38 |
39 | case SET_CONNECTION_REFUSED_ERROR:
40 | return {
41 | isAuthenticated: false,
42 | user: {},
43 | error: {
44 | message: action.error,
45 | variant: 'error'
46 | }
47 | }
48 |
49 | default:
50 | return state;
51 | }
52 | }
--------------------------------------------------------------------------------
/src/store/reducers/message.js:
--------------------------------------------------------------------------------
1 | /* Reducers are simple functions which take State and Actions,
2 | and returns new State.
3 | */
4 | export default (state = [], action = {}) => {
5 | return state;
6 | }
--------------------------------------------------------------------------------
/src/store/reducers/product.js:
--------------------------------------------------------------------------------
1 | import { FETCH_PRODUCTS_SUCCESS, FETCH_PRODUCTS_ERROR, FETCH_PRODUCTS_PENDING } from '../actions/types/types';
2 |
3 | const initialState = {
4 | pending: false,
5 | error: null,
6 | products: []
7 | }
8 |
9 | export default (state = initialState, action = {}) => {
10 | switch (action.type) {
11 | case FETCH_PRODUCTS_PENDING:
12 | return {
13 | pending: true,
14 | error: null,
15 | products: []
16 | }
17 |
18 | case FETCH_PRODUCTS_ERROR:
19 | return {
20 | pending: false,
21 | error: action.error,
22 | products: []
23 | }
24 |
25 | case FETCH_PRODUCTS_SUCCESS:
26 | return {
27 | pending: false,
28 | error: null,
29 | products: action.products
30 | }
31 |
32 | default:
33 | return state;
34 | }
35 | }
--------------------------------------------------------------------------------
/src/store/reducers/rootReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import product from './product';
3 | import auth from './auth';
4 |
5 | export default combineReducers({
6 | product,
7 | auth
8 | });
--------------------------------------------------------------------------------
/src/utils/getTokenTimeRemaining.js:
--------------------------------------------------------------------------------
1 | export default function getTokenTimeRemaining(decodedToken) {
2 | const now = new Date().getTime() / 1000;
3 | const diff = decodedToken.exp - now;
4 | console.log(`Hours left: ${new Date(diff * 1000).getHours()}, Minutes: ${new Date(diff * 1000).getMinutes()}`);
5 | }
--------------------------------------------------------------------------------
/src/utils/setAuthorizationToken.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | // Given a token, we want to add token to headers for every request.
4 | export default function setAuthorizationToken(token) {
5 | if (token) {
6 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
7 | } else {
8 | delete axios.defaults.headers.common['Authorization'];
9 | }
10 | }
--------------------------------------------------------------------------------