├── .gitignore ├── .prettierrc ├── LICENSE.txt ├── README.md ├── changelog.md ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── components │ ├── App.js │ ├── Header │ │ ├── Header.js │ │ ├── HeaderView.js │ │ ├── package.json │ │ └── styles.js │ ├── Layout │ │ ├── Layout.js │ │ ├── package.json │ │ └── styles.js │ ├── Notification │ │ ├── Notification.js │ │ ├── package.json │ │ └── styles.js │ ├── PageTitle │ │ ├── PageTitle.js │ │ ├── package.json │ │ └── styles.js │ ├── Sidebar │ │ ├── Sidebar.js │ │ ├── SidebarView.js │ │ ├── components │ │ │ ├── Dot.js │ │ │ └── SidebarLink │ │ │ │ ├── SidebarLink.js │ │ │ │ └── styles.js │ │ ├── package.json │ │ └── styles.js │ ├── UserAvatar │ │ ├── UserAvatar.js │ │ ├── package.json │ │ └── styles.js │ ├── Widget │ │ ├── Widget.js │ │ ├── WidgetView.js │ │ ├── package.json │ │ └── styles.js │ └── Wrappers │ │ ├── Wrappers.js │ │ └── package.json ├── context │ ├── LayoutContext.js │ └── UserContext.js ├── images │ └── google.svg ├── index.js ├── pages │ ├── charts │ │ ├── Charts.js │ │ ├── components │ │ │ ├── ApexHeatmap.js │ │ │ └── ApexLineChart.js │ │ └── package.json │ ├── dashboard │ │ ├── Dashboard.js │ │ ├── components │ │ │ ├── BigStat │ │ │ │ ├── BigStat.js │ │ │ │ └── styles.js │ │ │ └── Table │ │ │ │ └── Table.js │ │ ├── mock.js │ │ ├── package.json │ │ └── styles.js │ ├── error │ │ ├── Error.js │ │ ├── logo.svg │ │ ├── package.json │ │ └── styles.js │ ├── icons │ │ ├── Icons.js │ │ ├── package.json │ │ └── styles.js │ ├── login │ │ ├── Login.js │ │ ├── logo.svg │ │ ├── package.json │ │ └── styles.js │ ├── maps │ │ ├── Maps.js │ │ ├── package.json │ │ └── styles.js │ ├── notifications │ │ ├── Notifications.js │ │ ├── NotificationsContainer.js │ │ ├── NotificationsView.js │ │ ├── package.json │ │ └── styles.js │ ├── tables │ │ ├── Tables.js │ │ └── package.json │ └── typography │ │ ├── Typography.js │ │ ├── package.json │ │ └── styles.js ├── serviceWorker.js └── themes │ ├── default.js │ └── index.js └── yarn.lock /.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 | .idea/ 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "singleQuote": false, 4 | "semi": true, 5 | "trailingComma": "all" 6 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2019 Flatlogic LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Material Admin — Material-UI Dashboard Template 2 | 3 | Built with [React](https://facebook.github.io/react/), [Material-UI](https://material-ui.com), [React Router](https://reacttraining.com/react-router/). 4 | **No jQuery and Bootstrap!** 5 | 6 | **This version uses React 16.8.6, React Router v5, MaterialUI v4, built with React Hooks and React Context (No Redux)** 7 | 8 | [Demo](https://flatlogic.com/admin-dashboards/react-material-admin/demo). Use any credentials to log in. 9 | 10 | [![image](https://user-images.githubusercontent.com/24964748/55800639-df780300-5adc-11e9-84b7-7c2437088516.png)](https://flatlogic.com/admin-dashboards/react-material-admin/demo) 11 | 12 | ## Full Version 13 | 14 | This is a limited version of [**Full React Material Admin**](https://flatlogic.com/templates/react-material-admin-full/demo) with more components, pages and theme support. 15 | 16 | ## Features 17 | 18 | - React (**16.8.6**) 19 | - React Hooks 20 | - React Context 21 | - **No jQuery and Bootstrap!** 22 | - Mobile friendly layout (responsive) 23 | - Create-react-app under the hood 24 | - React Router v5 25 | - Material-UI v4 26 | - Modular Architecture 27 | - CSS-in-JS styles 28 | - Webpack build 29 | - Stylish, clean, responsive layout 30 | - Authentication 31 | 32 | ## Pages 33 | 34 | We have implemented some basic pages, so you can see our template in action. 35 | 36 | - Dashboard 37 | - Typography 38 | - Tables 39 | - Notifications 40 | - Charts 41 | - Icons 42 | - Maps 43 | - Login 44 | - Error 45 | 46 | ## Quick Start 47 | 48 | #### 1. Get the latest version 49 | 50 | You can start by cloning the latest version of React Dashboard on your 51 | local machine by running: 52 | 53 | ```shell 54 | $ git clone https://github.com/flatlogic/react-material-admin.git MyApp 55 | $ cd MyApp 56 | ``` 57 | 58 | #### 2. Run `yarn install` 59 | 60 | This will install both run-time project dependencies and developer tools listed 61 | in [package.json](package.json) file. 62 | 63 | #### 3. Run `yarn start` 64 | 65 | Runs the app in the development mode. 66 | 67 | Open http://localhost:3000 to view it in the browser. Whenever you modify any of the source files inside the `/src` folder, 68 | the module bundler ([Webpack](http://webpack.github.io/)) will recompile the 69 | app on the fly and refresh all the connected browsers. 70 | 71 | #### 4. Run `yarn build` 72 | 73 | Builds the app for production to the build folder. 74 | It correctly bundles React in production mode and optimizes the build for the best performance. 75 | 76 | The build is minified and the filenames include the hashes. 77 | Your app is ready to be deployed! 78 | 79 | ## Support 80 | 81 | For any additional information please refer to [Flatlogic homepage](https://flatlogic.com). 82 | 83 | ## How can I support developers? 84 | 85 | - Star our GitHub repo :star: 86 | - [Tweet about it](https://twitter.com/intent/tweet?text=Amazing%20dashboard%20built%20with%20NodeJS,%20React%20and%20Bootstrap!&url=https://github.com/flatlogic/react-material-template&via=flatlogic). 87 | - Create pull requests, submit bugs, suggest new features or documentation updates :wrench: 88 | - Follow [@flatlogic on Twitter](https://twitter.com/flatlogic). 89 | - Subscribe to Flatlogic newsletter at [flatlogic.com](https://flatlogic.com/) 90 | - Like our page on [Facebook](https://www.facebook.com/flatlogic/) :thumbsup: 91 | 92 | ## More from Flatlogic 93 | 94 | - [React Native Starter](https://github.com/flatlogic/react-native-starter) - 🚀 A powerful react native starter template that bootstraps development of your mobile application 95 | - [Sing App](https://github.com/flatlogic/sing-app) - 💥 Free and open-source admin dashboard template built with Bootstrap 4 96 | - [Awesome Bootstrap Checkboxes & Radios](https://github.com/flatlogic/awesome-bootstrap-checkbox) - ✅ Pure css way to make inputs look prettier 97 | - [React Dashboard](https://github.com/flatlogic/react-dashboard) - 🔥 React Dashboard - isomorphic admin dashboard template with GraphQL 98 | - [Light Blue Dashboard](https://github.com/flatlogic/light-blue-dashboard) - 💦 Free and open-source admin dashboard template built with Bootstrap 99 | 100 | ## Premium themes 101 | 102 | Looking for premium themes and templates? Check out more [admin dashboard templates at flatlogic.com](https://flatlogic.com/admin-dashboards). 103 | 104 | ## License 105 | 106 | [MIT](https://github.com/flatlogic/react-material-dashboard/blob/master/LICENSE.txt). 107 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | # [1.2.3] 4 | 5 | ### Updated 6 | - Fixed security vulnerabilities in dependencies 7 | 8 | # [1.2.2] 9 | 10 | ### Updated 11 | - Packages updated 12 | 13 | # [1.2.1] 14 | 15 | ### Updated 16 | - Packages updated 17 | 18 | ### Fixed 19 | - Sign up name type of input 20 | - Dot component size prop 21 | - Performance errors 22 | 23 | # [1.2.0] 24 | 25 | ### Updated 26 | - Packages update 27 | - Link to Full version 28 | 29 | ### Fixed 30 | - User login state improvements 31 | 32 | ## [1.1.0] 33 | 34 | ### New Feactures 35 | 36 | - React v16.8.6 37 | - React Router v5 38 | - new React Hooks 39 | - Material UI v4.3 40 | 41 | Bug fixes 42 | 43 | ## [1.0.0] 44 | 45 | Initial version of the project 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-material-admin", 3 | "version": "1.1.0", 4 | "private": true, 5 | "resolutions": { 6 | "websocket-extensions": "^0.1.4", 7 | "node-forge": "^0.10.0", 8 | "node-fetch": "^2.6.1" 9 | }, 10 | "dependencies": { 11 | "@material-ui/core": "^4.11.0", 12 | "@material-ui/icons": "^4.2.1", 13 | "@material-ui/styles": "^4.10.0", 14 | "apexcharts": "^3.20.0", 15 | "classnames": "^2.2.6", 16 | "font-awesome": "^4.7.0", 17 | "mui-datatables": "^3.4.1", 18 | "react": "^16.13.1", 19 | "react-apexcharts": "^1.3.7", 20 | "react-dom": "^16.13.1", 21 | "react-google-maps": "^9.4.5", 22 | "react-router-dom": "^5.2.0", 23 | "react-scripts": "3.4.3", 24 | "react-syntax-highlighter": "^13.5.3", 25 | "react-toastify": "^6.0.8", 26 | "recharts": "^1.6.2", 27 | "tinycolor2": "^1.4.1" 28 | }, 29 | "scripts": { 30 | "start": "react-scripts start", 31 | "build": "react-scripts build", 32 | "test": "react-scripts test", 33 | "eject": "react-scripts eject" 34 | }, 35 | "eslintConfig": { 36 | "extends": "react-app" 37 | }, 38 | "browserslist": { 39 | "production": [ 40 | ">0.2%", 41 | "not dead", 42 | "not op_mini all" 43 | ], 44 | "development": [ 45 | "last 1 chrome version", 46 | "last 1 firefox version", 47 | "last 1 safari version" 48 | ] 49 | }, 50 | "devDependencies": { 51 | "@babel/helper-call-delegate": "^7.11.4", 52 | "prettier": "^2.1.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srsedev/react-material-admin/7e257e3e020dcf77d5d3543cb5a7fa93cf4f02d6/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 25 | React Material Admin 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React Material Admin", 3 | "name": "React Material Admin is a React Template built with Material-UI", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#536DFE", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { HashRouter, Route, Switch, Redirect } from "react-router-dom"; 3 | 4 | // components 5 | import Layout from "./Layout"; 6 | 7 | // pages 8 | import Error from "../pages/error"; 9 | import Login from "../pages/login"; 10 | 11 | // context 12 | import { useUserState } from "../context/UserContext"; 13 | 14 | export default function App() { 15 | // global 16 | var { isAuthenticated } = useUserState(); 17 | 18 | return ( 19 | 20 | 21 | } /> 22 | } 26 | /> 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | 34 | // ####################################################################### 35 | 36 | function PrivateRoute({ component, ...rest }) { 37 | return ( 38 | 41 | isAuthenticated ? ( 42 | React.createElement(component, props) 43 | ) : ( 44 | 52 | ) 53 | } 54 | /> 55 | ); 56 | } 57 | 58 | function PublicRoute({ component, ...rest }) { 59 | return ( 60 | 63 | isAuthenticated ? ( 64 | 69 | ) : ( 70 | React.createElement(component, props) 71 | ) 72 | } 73 | /> 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | AppBar, 4 | Toolbar, 5 | IconButton, 6 | InputBase, 7 | Menu, 8 | MenuItem, 9 | Fab, 10 | Link 11 | } from "@material-ui/core"; 12 | import { 13 | Menu as MenuIcon, 14 | MailOutline as MailIcon, 15 | NotificationsNone as NotificationsIcon, 16 | Person as AccountIcon, 17 | Search as SearchIcon, 18 | Send as SendIcon, 19 | ArrowBack as ArrowBackIcon, 20 | } from "@material-ui/icons"; 21 | import classNames from "classnames"; 22 | 23 | // styles 24 | import useStyles from "./styles"; 25 | 26 | // components 27 | import { Badge, Typography, Button } from "../Wrappers/Wrappers"; 28 | import Notification from "../Notification/Notification"; 29 | import UserAvatar from "../UserAvatar/UserAvatar"; 30 | 31 | // context 32 | import { 33 | useLayoutState, 34 | useLayoutDispatch, 35 | toggleSidebar, 36 | } from "../../context/LayoutContext"; 37 | import { useUserDispatch, signOut } from "../../context/UserContext"; 38 | 39 | const messages = [ 40 | { 41 | id: 0, 42 | variant: "warning", 43 | name: "Jane Hew", 44 | message: "Hey! How is it going?", 45 | time: "9:32", 46 | }, 47 | { 48 | id: 1, 49 | variant: "success", 50 | name: "Lloyd Brown", 51 | message: "Check out my new Dashboard", 52 | time: "9:18", 53 | }, 54 | { 55 | id: 2, 56 | variant: "primary", 57 | name: "Mark Winstein", 58 | message: "I want rearrange the appointment", 59 | time: "9:15", 60 | }, 61 | { 62 | id: 3, 63 | variant: "secondary", 64 | name: "Liana Dutti", 65 | message: "Good news from sale department", 66 | time: "9:09", 67 | }, 68 | ]; 69 | 70 | const notifications = [ 71 | { id: 0, color: "warning", message: "Check out this awesome ticket" }, 72 | { 73 | id: 1, 74 | color: "success", 75 | type: "info", 76 | message: "What is the best way to get ...", 77 | }, 78 | { 79 | id: 2, 80 | color: "secondary", 81 | type: "notification", 82 | message: "This is just a simple notification", 83 | }, 84 | { 85 | id: 3, 86 | color: "primary", 87 | type: "e-commerce", 88 | message: "12 new orders has arrived today", 89 | }, 90 | ]; 91 | 92 | export default function Header(props) { 93 | var classes = useStyles(); 94 | 95 | // global 96 | var layoutState = useLayoutState(); 97 | var layoutDispatch = useLayoutDispatch(); 98 | var userDispatch = useUserDispatch(); 99 | 100 | // local 101 | var [mailMenu, setMailMenu] = useState(null); 102 | var [isMailsUnread, setIsMailsUnread] = useState(true); 103 | var [notificationsMenu, setNotificationsMenu] = useState(null); 104 | var [isNotificationsUnread, setIsNotificationsUnread] = useState(true); 105 | var [profileMenu, setProfileMenu] = useState(null); 106 | var [isSearchOpen, setSearchOpen] = useState(false); 107 | 108 | return ( 109 | 110 | 111 | toggleSidebar(layoutDispatch)} 114 | className={classNames( 115 | classes.headerMenuButton, 116 | classes.headerMenuButtonCollapse, 117 | )} 118 | > 119 | {layoutState.isSidebarOpened ? ( 120 | 128 | ) : ( 129 | 137 | )} 138 | 139 | 140 | React Material Admin 141 | 142 |
143 | 144 |
149 |
setSearchOpen(!isSearchOpen)} 154 | > 155 | 156 |
157 | 164 |
165 | { 170 | setNotificationsMenu(e.currentTarget); 171 | setIsNotificationsUnread(false); 172 | }} 173 | className={classes.headerMenuButton} 174 | > 175 | 179 | 180 | 181 | 182 | { 187 | setMailMenu(e.currentTarget); 188 | setIsMailsUnread(false); 189 | }} 190 | className={classes.headerMenuButton} 191 | > 192 | 196 | 197 | 198 | 199 | setProfileMenu(e.currentTarget)} 205 | > 206 | 207 | 208 | setMailMenu(null)} 213 | MenuListProps={{ className: classes.headerMenuList }} 214 | className={classes.headerMenu} 215 | classes={{ paper: classes.profileMenu }} 216 | disableAutoFocusItem 217 | > 218 |
219 | 220 | New Messages 221 | 222 | 227 | {messages.length} New Messages 228 | 229 |
230 | {messages.map(message => ( 231 | 232 |
233 | 234 | 235 | {message.time} 236 | 237 |
238 |
244 | 245 | {message.name} 246 | 247 | 248 | {message.message} 249 | 250 |
251 |
252 | ))} 253 | 259 | Send New Message 260 | 261 | 262 |
263 | setNotificationsMenu(null)} 268 | className={classes.headerMenu} 269 | disableAutoFocusItem 270 | > 271 | {notifications.map(notification => ( 272 | setNotificationsMenu(null)} 275 | className={classes.headerMenuItem} 276 | > 277 | 278 | 279 | ))} 280 | 281 | setProfileMenu(null)} 286 | className={classes.headerMenu} 287 | classes={{ paper: classes.profileMenu }} 288 | disableAutoFocusItem 289 | > 290 |
291 | 292 | John Smith 293 | 294 | 300 | Flalogic.com 301 | 302 |
303 | 309 | Profile 310 | 311 | 317 | Tasks 318 | 319 | 325 | Messages 326 | 327 |
328 | signOut(userDispatch, props.history)} 332 | > 333 | Sign Out 334 | 335 |
336 |
337 | 338 | 339 | ); 340 | } 341 | -------------------------------------------------------------------------------- /src/components/Header/HeaderView.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | AppBar, 4 | Toolbar, 5 | IconButton, 6 | InputBase, 7 | Menu, 8 | MenuItem, 9 | Fab, 10 | withStyles 11 | } from "@material-ui/core"; 12 | import { 13 | Menu as MenuIcon, 14 | MailOutline as MailIcon, 15 | NotificationsNone as NotificationsIcon, 16 | Person as AccountIcon, 17 | Search as SearchIcon, 18 | Send as SendIcon, 19 | ArrowBack as ArrowBackIcon 20 | } from "@material-ui/icons"; 21 | import { fade } from "@material-ui/core/styles/colorManipulator"; 22 | import classNames from "classnames"; 23 | 24 | import { Badge, Typography } from "../Wrappers"; 25 | import Notification from "../Notification"; 26 | import UserAvatar from "../UserAvatar"; 27 | 28 | const messages = [ 29 | { 30 | id: 0, 31 | variant: "warning", 32 | name: "Jane Hew", 33 | message: "Hey! How is it going?", 34 | time: "9:32" 35 | }, 36 | { 37 | id: 1, 38 | variant: "success", 39 | name: "Lloyd Brown", 40 | message: "Check out my new Dashboard", 41 | time: "9:18" 42 | }, 43 | { 44 | id: 2, 45 | variant: "primary", 46 | name: "Mark Winstein", 47 | message: "I want rearrange the appointment", 48 | time: "9:15" 49 | }, 50 | { 51 | id: 3, 52 | variant: "secondary", 53 | name: "Liana Dutti", 54 | message: "Good news from sale department", 55 | time: "9:09" 56 | } 57 | ]; 58 | 59 | const notifications = [ 60 | { id: 0, color: "warning", message: "Check out this awesome ticket" }, 61 | { 62 | id: 1, 63 | color: "success", 64 | type: "info", 65 | message: "What is the best way to get ..." 66 | }, 67 | { 68 | id: 2, 69 | color: "secondary", 70 | type: "notification", 71 | message: "This is just a simple notification" 72 | }, 73 | { 74 | id: 3, 75 | color: "primary", 76 | type: "e-commerce", 77 | message: "12 new orders has arrived today" 78 | } 79 | ]; 80 | 81 | const Header = ({ classes, isSidebarOpened, toggleSidebar, ...props }) => ( 82 | 83 | 84 | 92 | {isSidebarOpened ? ( 93 | 98 | ) : ( 99 | 104 | )} 105 | 106 | React Material Admin 107 |
108 |
113 |
119 | 120 |
121 | 128 |
129 | 136 | 142 | 143 | 144 | 145 | 152 | 156 | 157 | 158 | 159 | 166 | 167 | 168 | 178 |
179 | 180 | New Messages 181 | 182 | 187 | {messages.length} New Messages 188 | 189 |
190 | {messages.map(message => ( 191 | 192 |
193 | 194 | 195 | {message.time} 196 | 197 |
198 |
204 | 205 | {message.name} 206 | 207 | {message.message} 208 |
209 |
210 | ))} 211 | 217 | Send New Message 218 | 219 | 220 |
221 | 229 | {notifications.map(notification => ( 230 | 235 | 236 | 237 | ))} 238 | 239 | 248 |
249 | 250 | John Smith 251 | 252 | 258 | Flalogic.com 259 | 260 |
261 | 267 | Profile 268 | 269 | 275 | Tasks 276 | 277 | 283 | Messages 284 | 285 |
286 | 291 | Sign Out 292 | 293 |
294 |
295 | 296 | 297 | ); 298 | 299 | const styles = theme => ({ 300 | logotype: { 301 | color: "white", 302 | marginLeft: theme.spacing.unit * 2.5, 303 | marginRight: theme.spacing.unit * 2.5, 304 | fontWeight: 500, 305 | fontSize: 18, 306 | whiteSpace: "nowrap", 307 | [theme.breakpoints.down("xs")]: { 308 | display: "none" 309 | } 310 | }, 311 | appBar: { 312 | width: "100vw", 313 | zIndex: theme.zIndex.drawer + 1, 314 | transition: theme.transitions.create(["margin"], { 315 | easing: theme.transitions.easing.sharp, 316 | duration: theme.transitions.duration.leavingScreen 317 | }) 318 | }, 319 | toolbar: { 320 | paddingLeft: theme.spacing.unit * 2, 321 | paddingRight: theme.spacing.unit * 2 322 | }, 323 | hide: { 324 | display: "none" 325 | }, 326 | grow: { 327 | flexGrow: 1 328 | }, 329 | search: { 330 | position: "relative", 331 | borderRadius: 25, 332 | paddingLeft: theme.spacing.unit * 2.5, 333 | width: 36, 334 | backgroundColor: fade(theme.palette.common.black, 0), 335 | transition: theme.transitions.create(["background-color", "width"]), 336 | "&:hover": { 337 | cursor: "pointer", 338 | backgroundColor: fade(theme.palette.common.black, 0.08) 339 | } 340 | }, 341 | searchFocused: { 342 | backgroundColor: fade(theme.palette.common.black, 0.08), 343 | width: "100%", 344 | [theme.breakpoints.up("md")]: { 345 | width: 250 346 | } 347 | }, 348 | searchIcon: { 349 | width: 36, 350 | right: 0, 351 | height: "100%", 352 | position: "absolute", 353 | display: "flex", 354 | alignItems: "center", 355 | justifyContent: "center", 356 | transition: theme.transitions.create("right"), 357 | "&:hover": { 358 | cursor: "pointer" 359 | } 360 | }, 361 | searchIconOpened: { 362 | right: theme.spacing.unit * 1.25 363 | }, 364 | inputRoot: { 365 | color: "inherit", 366 | width: "100%" 367 | }, 368 | inputInput: { 369 | height: 36, 370 | padding: 0, 371 | paddingRight: 36 + theme.spacing.unit * 1.25, 372 | width: "100%" 373 | }, 374 | messageContent: { 375 | display: "flex", 376 | flexDirection: "column" 377 | }, 378 | headerMenu: { 379 | marginTop: theme.spacing.unit * 7 380 | }, 381 | headerMenuList: { 382 | display: "flex", 383 | flexDirection: "column" 384 | }, 385 | headerMenuItem: { 386 | "&:hover, &:focus": { 387 | backgroundColor: theme.palette.primary.main, 388 | color: "white" 389 | } 390 | }, 391 | headerMenuButton: { 392 | marginLeft: theme.spacing.unit * 2, 393 | padding: theme.spacing.unit / 2 394 | }, 395 | headerMenuButtonCollapse: { 396 | marginRight: theme.spacing.unit * 2 397 | }, 398 | headerIcon: { 399 | fontSize: 28, 400 | color: "rgba(255, 255, 255, 0.35)" 401 | }, 402 | headerIconCollapse: { 403 | color: "white" 404 | }, 405 | profileMenu: { 406 | minWidth: 265 407 | }, 408 | profileMenuUser: { 409 | display: "flex", 410 | flexDirection: "column", 411 | padding: theme.spacing.unit * 2 412 | }, 413 | profileMenuItem: { 414 | color: theme.palette.text.hint 415 | }, 416 | profileMenuIcon: { 417 | marginRight: theme.spacing.unit * 2, 418 | color: theme.palette.text.hint 419 | }, 420 | profileMenuLink: { 421 | fontSize: 16, 422 | textDecoration: "none", 423 | "&:hover": { 424 | cursor: "pointer" 425 | } 426 | }, 427 | messageNotification: { 428 | height: "auto", 429 | display: "flex", 430 | alignItems: "center", 431 | "&:hover, &:focus": { 432 | backgroundColor: theme.palette.background.light 433 | } 434 | }, 435 | messageNotificationSide: { 436 | display: "flex", 437 | flexDirection: "column", 438 | alignItems: "center", 439 | marginRight: theme.spacing.unit * 2 440 | }, 441 | messageNotificationBodySide: { 442 | alignItems: "flex-start", 443 | marginRight: 0 444 | }, 445 | sendMessageButton: { 446 | margin: theme.spacing.unit * 4, 447 | marginTop: theme.spacing.unit * 2, 448 | marginBottom: theme.spacing.unit * 2, 449 | textTransform: "none" 450 | }, 451 | sendButtonIcon: { 452 | marginLeft: theme.spacing.unit * 2 453 | } 454 | }); 455 | 456 | export default withStyles(styles)(Header); 457 | -------------------------------------------------------------------------------- /src/components/Header/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Header", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "Header.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Header/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | import { fade } from "@material-ui/core/styles/colorManipulator"; 3 | 4 | export default makeStyles(theme => ({ 5 | logotype: { 6 | color: "white", 7 | marginLeft: theme.spacing(2.5), 8 | marginRight: theme.spacing(2.5), 9 | fontWeight: 500, 10 | fontSize: 18, 11 | whiteSpace: "nowrap", 12 | [theme.breakpoints.down("xs")]: { 13 | display: "none", 14 | }, 15 | }, 16 | appBar: { 17 | width: "100vw", 18 | zIndex: theme.zIndex.drawer + 1, 19 | transition: theme.transitions.create(["margin"], { 20 | easing: theme.transitions.easing.sharp, 21 | duration: theme.transitions.duration.leavingScreen, 22 | }), 23 | }, 24 | toolbar: { 25 | paddingLeft: theme.spacing(2), 26 | paddingRight: theme.spacing(2), 27 | }, 28 | hide: { 29 | display: "none", 30 | }, 31 | grow: { 32 | flexGrow: 1, 33 | }, 34 | search: { 35 | position: "relative", 36 | borderRadius: 25, 37 | paddingLeft: theme.spacing(2.5), 38 | width: 36, 39 | backgroundColor: fade(theme.palette.common.black, 0), 40 | transition: theme.transitions.create(["background-color", "width"]), 41 | "&:hover": { 42 | cursor: "pointer", 43 | backgroundColor: fade(theme.palette.common.black, 0.08), 44 | }, 45 | }, 46 | searchFocused: { 47 | backgroundColor: fade(theme.palette.common.black, 0.08), 48 | width: "100%", 49 | [theme.breakpoints.up("md")]: { 50 | width: 250, 51 | }, 52 | }, 53 | searchIcon: { 54 | width: 36, 55 | right: 0, 56 | height: "100%", 57 | position: "absolute", 58 | display: "flex", 59 | alignItems: "center", 60 | justifyContent: "center", 61 | transition: theme.transitions.create("right"), 62 | "&:hover": { 63 | cursor: "pointer", 64 | }, 65 | }, 66 | searchIconOpened: { 67 | right: theme.spacing(1.25), 68 | }, 69 | inputRoot: { 70 | color: "inherit", 71 | width: "100%", 72 | }, 73 | inputInput: { 74 | height: 36, 75 | padding: 0, 76 | paddingRight: 36 + theme.spacing(1.25), 77 | width: "100%", 78 | }, 79 | messageContent: { 80 | display: "flex", 81 | flexDirection: "column", 82 | }, 83 | headerMenu: { 84 | marginTop: theme.spacing(7), 85 | }, 86 | headerMenuList: { 87 | display: "flex", 88 | flexDirection: "column", 89 | }, 90 | headerMenuItem: { 91 | "&:hover, &:focus": { 92 | backgroundColor: theme.palette.primary.main, 93 | color: "white", 94 | }, 95 | }, 96 | headerMenuButton: { 97 | marginLeft: theme.spacing(2), 98 | padding: theme.spacing(0.5), 99 | }, 100 | headerMenuButtonCollapse: { 101 | marginRight: theme.spacing(2), 102 | }, 103 | headerIcon: { 104 | fontSize: 28, 105 | color: "rgba(255, 255, 255, 0.35)", 106 | }, 107 | headerIconCollapse: { 108 | color: "white", 109 | }, 110 | profileMenu: { 111 | minWidth: 265, 112 | }, 113 | profileMenuUser: { 114 | display: "flex", 115 | flexDirection: "column", 116 | padding: theme.spacing(2), 117 | }, 118 | profileMenuItem: { 119 | color: theme.palette.text.hint, 120 | }, 121 | profileMenuIcon: { 122 | marginRight: theme.spacing(2), 123 | color: theme.palette.text.hint, 124 | }, 125 | profileMenuLink: { 126 | fontSize: 16, 127 | textDecoration: "none", 128 | "&:hover": { 129 | cursor: "pointer", 130 | }, 131 | }, 132 | messageNotification: { 133 | height: "auto", 134 | display: "flex", 135 | alignItems: "center", 136 | "&:hover, &:focus": { 137 | backgroundColor: theme.palette.background.light, 138 | }, 139 | }, 140 | messageNotificationSide: { 141 | display: "flex", 142 | flexDirection: "column", 143 | alignItems: "center", 144 | marginRight: theme.spacing(2), 145 | }, 146 | messageNotificationBodySide: { 147 | alignItems: "flex-start", 148 | marginRight: 0, 149 | }, 150 | sendMessageButton: { 151 | margin: theme.spacing(4), 152 | marginTop: theme.spacing(2), 153 | marginBottom: theme.spacing(2), 154 | textTransform: "none", 155 | }, 156 | sendButtonIcon: { 157 | marginLeft: theme.spacing(2), 158 | }, 159 | })); 160 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | Route, 4 | Switch, 5 | Redirect, 6 | withRouter, 7 | } from "react-router-dom"; 8 | import classnames from "classnames"; 9 | 10 | // styles 11 | import useStyles from "./styles"; 12 | 13 | // components 14 | import Header from "../Header"; 15 | import Sidebar from "../Sidebar"; 16 | 17 | // pages 18 | import Dashboard from "../../pages/dashboard"; 19 | import Typography from "../../pages/typography"; 20 | import Notifications from "../../pages/notifications"; 21 | import Maps from "../../pages/maps"; 22 | import Tables from "../../pages/tables"; 23 | import Icons from "../../pages/icons"; 24 | import Charts from "../../pages/charts"; 25 | 26 | // context 27 | import { useLayoutState } from "../../context/LayoutContext"; 28 | 29 | function Layout(props) { 30 | var classes = useStyles(); 31 | 32 | // global 33 | var layoutState = useLayoutState(); 34 | 35 | return ( 36 |
37 | <> 38 |
39 | 40 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | } 55 | /> 56 | 57 | 58 | 59 | 60 |
61 | 62 |
63 | ); 64 | } 65 | 66 | export default withRouter(Layout); 67 | -------------------------------------------------------------------------------- /src/components/Layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Layout", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "Layout.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Layout/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | root: { 5 | display: "flex", 6 | maxWidth: "100vw", 7 | overflowX: "hidden", 8 | }, 9 | content: { 10 | flexGrow: 1, 11 | padding: theme.spacing(3), 12 | width: `calc(100vw - 240px)`, 13 | minHeight: "100vh", 14 | }, 15 | contentShift: { 16 | width: `calc(100vw - ${240 + theme.spacing(6)}px)`, 17 | transition: theme.transitions.create(["width", "margin"], { 18 | easing: theme.transitions.easing.sharp, 19 | duration: theme.transitions.duration.enteringScreen, 20 | }), 21 | }, 22 | fakeToolbar: { 23 | ...theme.mixins.toolbar, 24 | }, 25 | })); 26 | -------------------------------------------------------------------------------- /src/components/Notification/Notification.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button } from "@material-ui/core"; 3 | import { 4 | NotificationsNone as NotificationsIcon, 5 | ThumbUp as ThumbUpIcon, 6 | ShoppingCart as ShoppingCartIcon, 7 | LocalOffer as TicketIcon, 8 | BusinessCenter as DeliveredIcon, 9 | SmsFailed as FeedbackIcon, 10 | DiscFull as DiscIcon, 11 | Email as MessageIcon, 12 | Report as ReportIcon, 13 | Error as DefenceIcon, 14 | AccountBox as CustomerIcon, 15 | Done as ShippedIcon, 16 | Publish as UploadIcon, 17 | } from "@material-ui/icons"; 18 | import { useTheme } from "@material-ui/styles"; 19 | import classnames from "classnames"; 20 | import tinycolor from "tinycolor2"; 21 | 22 | // styles 23 | import useStyles from "./styles"; 24 | 25 | // components 26 | import { Typography } from "../Wrappers"; 27 | 28 | const typesIcons = { 29 | "e-commerce": , 30 | notification: , 31 | offer: , 32 | info: , 33 | message: , 34 | feedback: , 35 | customer: , 36 | shipped: , 37 | delivered: , 38 | defence: , 39 | report: , 40 | upload: , 41 | disc: , 42 | }; 43 | 44 | export default function Notification({ variant, ...props }) { 45 | var classes = useStyles(); 46 | var theme = useTheme(); 47 | 48 | const icon = getIconByType(props.type); 49 | const iconWithStyles = React.cloneElement(icon, { 50 | classes: { 51 | root: classes.notificationIcon, 52 | }, 53 | style: { 54 | color: 55 | variant !== "contained" && 56 | theme.palette[props.color] && 57 | theme.palette[props.color].main, 58 | }, 59 | }); 60 | 61 | return ( 62 |
74 |
88 | {iconWithStyles} 89 |
90 |
91 | 98 | {props.message} 99 | 100 | {props.extraButton && props.extraButtonClick && ( 101 | 108 | )} 109 |
110 |
111 | ); 112 | } 113 | 114 | // #################################################################### 115 | function getIconByType(type = "offer") { 116 | return typesIcons[type]; 117 | } 118 | -------------------------------------------------------------------------------- /src/components/Notification/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Notification", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "Notification.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Notification/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | notificationContainer: { 5 | display: "flex", 6 | alignItems: "center", 7 | }, 8 | notificationContained: { 9 | borderRadius: 45, 10 | height: 45, 11 | boxShadow: theme.customShadows.widgetDark, 12 | }, 13 | notificationContainedShadowless: { 14 | boxShadow: "none", 15 | }, 16 | notificationIconContainer: { 17 | minWidth: 45, 18 | height: 45, 19 | borderRadius: 45, 20 | display: "flex", 21 | alignItems: "center", 22 | justifyContent: "center", 23 | fontSize: 24, 24 | }, 25 | notificationIconContainerContained: { 26 | fontSize: 18, 27 | color: "#FFFFFF80", 28 | }, 29 | notificationIconContainerRounded: { 30 | marginRight: theme.spacing(2), 31 | }, 32 | containedTypography: { 33 | color: "white", 34 | }, 35 | messageContainer: { 36 | display: "flex", 37 | alignItems: "center", 38 | justifyContent: "space-between", 39 | flexGrow: 1, 40 | }, 41 | extraButton: { 42 | color: "white", 43 | "&:hover, &:focus": { 44 | background: "transparent", 45 | }, 46 | }, 47 | })); 48 | -------------------------------------------------------------------------------- /src/components/PageTitle/PageTitle.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button } from "@material-ui/core"; 3 | 4 | // styles 5 | import useStyles from "./styles"; 6 | 7 | // components 8 | import { Typography } from "../Wrappers"; 9 | 10 | export default function PageTitle(props) { 11 | var classes = useStyles(); 12 | 13 | return ( 14 |
15 | 16 | {props.title} 17 | 18 | {props.button && ( 19 | 27 | )} 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/components/PageTitle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PageTitle", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "PageTitle.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/PageTitle/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | pageTitleContainer: { 5 | display: "flex", 6 | justifyContent: "space-between", 7 | marginBottom: theme.spacing(4), 8 | marginTop: theme.spacing(5), 9 | }, 10 | typo: { 11 | color: theme.palette.text.hint, 12 | }, 13 | button: { 14 | boxShadow: theme.customShadows.widget, 15 | textTransform: "none", 16 | "&:active": { 17 | boxShadow: theme.customShadows.widgetWide, 18 | }, 19 | }, 20 | })); 21 | -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Drawer, IconButton, List } from "@material-ui/core"; 3 | import { 4 | Home as HomeIcon, 5 | NotificationsNone as NotificationsIcon, 6 | FormatSize as TypographyIcon, 7 | FilterNone as UIElementsIcon, 8 | BorderAll as TableIcon, 9 | QuestionAnswer as SupportIcon, 10 | LibraryBooks as LibraryIcon, 11 | HelpOutline as FAQIcon, 12 | ArrowBack as ArrowBackIcon, 13 | } from "@material-ui/icons"; 14 | import { useTheme } from "@material-ui/styles"; 15 | import { withRouter } from "react-router-dom"; 16 | import classNames from "classnames"; 17 | 18 | // styles 19 | import useStyles from "./styles"; 20 | 21 | // components 22 | import SidebarLink from "./components/SidebarLink/SidebarLink"; 23 | import Dot from "./components/Dot"; 24 | 25 | // context 26 | import { 27 | useLayoutState, 28 | useLayoutDispatch, 29 | toggleSidebar, 30 | } from "../../context/LayoutContext"; 31 | 32 | const structure = [ 33 | { id: 0, label: "Dashboard", link: "/app/dashboard", icon: }, 34 | { 35 | id: 1, 36 | label: "Typography", 37 | link: "/app/typography", 38 | icon: , 39 | }, 40 | { id: 2, label: "Tables", link: "/app/tables", icon: }, 41 | { 42 | id: 3, 43 | label: "Notifications", 44 | link: "/app/notifications", 45 | icon: , 46 | }, 47 | { 48 | id: 4, 49 | label: "UI Elements", 50 | link: "/app/ui", 51 | icon: , 52 | children: [ 53 | { label: "Icons", link: "/app/ui/icons" }, 54 | { label: "Charts", link: "/app/ui/charts" }, 55 | { label: "Maps", link: "/app/ui/maps" }, 56 | ], 57 | }, 58 | { id: 5, type: "divider" }, 59 | { id: 6, type: "title", label: "HELP" }, 60 | { id: 7, label: "Library", link: "", icon: }, 61 | { id: 8, label: "Support", link: "", icon: }, 62 | { id: 9, label: "FAQ", link: "", icon: }, 63 | { id: 10, type: "divider" }, 64 | { id: 11, type: "title", label: "PROJECTS" }, 65 | { 66 | id: 12, 67 | label: "My recent", 68 | link: "", 69 | icon: , 70 | }, 71 | { 72 | id: 13, 73 | label: "Starred", 74 | link: "", 75 | icon: , 76 | }, 77 | { 78 | id: 14, 79 | label: "Background", 80 | link: "", 81 | icon: , 82 | }, 83 | ]; 84 | 85 | function Sidebar({ location }) { 86 | var classes = useStyles(); 87 | var theme = useTheme(); 88 | 89 | // global 90 | var { isSidebarOpened } = useLayoutState(); 91 | var layoutDispatch = useLayoutDispatch(); 92 | 93 | // local 94 | var [isPermanent, setPermanent] = useState(true); 95 | 96 | useEffect(function() { 97 | window.addEventListener("resize", handleWindowWidthChange); 98 | handleWindowWidthChange(); 99 | return function cleanup() { 100 | window.removeEventListener("resize", handleWindowWidthChange); 101 | }; 102 | }); 103 | 104 | return ( 105 | 119 |
120 |
121 | toggleSidebar(layoutDispatch)}> 122 | 127 | 128 |
129 | 130 | {structure.map(link => ( 131 | 137 | ))} 138 | 139 | 140 | ); 141 | 142 | // ################################################################## 143 | function handleWindowWidthChange() { 144 | var windowWidth = window.innerWidth; 145 | var breakpointWidth = theme.breakpoints.values.md; 146 | var isSmallScreen = windowWidth < breakpointWidth; 147 | 148 | if (isSmallScreen && isPermanent) { 149 | setPermanent(false); 150 | } else if (!isSmallScreen && !isPermanent) { 151 | setPermanent(true); 152 | } 153 | } 154 | } 155 | 156 | export default withRouter(Sidebar); 157 | -------------------------------------------------------------------------------- /src/components/Sidebar/SidebarView.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Drawer, 4 | IconButton, 5 | List, 6 | withStyles } from "@material-ui/core"; 7 | import { 8 | Home as HomeIcon, 9 | NotificationsNone as NotificationsIcon, 10 | FormatSize as TypographyIcon, 11 | FilterNone as UIElementsIcon, 12 | BorderAll as TableIcon, 13 | QuestionAnswer as SupportIcon, 14 | LibraryBooks as LibraryIcon, 15 | HelpOutline as FAQIcon, 16 | ArrowBack as ArrowBackIcon, 17 | } from "@material-ui/icons"; 18 | import classNames from 'classnames'; 19 | 20 | import SidebarLink from './components/SidebarLink/SidebarLinkContainer'; 21 | import Dot from './components/Dot'; 22 | 23 | const structure = [ 24 | { id: 0, label: 'Dashboard', link: '/app/dashboard', icon: }, 25 | { id: 1, label: 'Typography', link: '/app/typography', icon: }, 26 | { id: 2, label: 'Tables', link: '/app/tables', icon: }, 27 | { id: 3, label: 'Notifications', link: '/app/notifications', icon: }, 28 | { 29 | id: 4, 30 | label: 'UI Elements', 31 | link: '/app/ui', 32 | icon: , 33 | children: [ 34 | { label: 'Icons', link: '/app/ui/icons' }, 35 | { label: 'Charts', link: '/app/ui/charts' }, 36 | { label: 'Maps', link: '/app/ui/maps' }, 37 | ], 38 | }, 39 | { id: 5, type: 'divider' }, 40 | { id: 6, type: 'title', label: 'HELP' }, 41 | { id: 7, label: 'Library', link: '', icon: }, 42 | { id: 8, label: 'Support', link: '', icon: }, 43 | { id: 9, label: 'FAQ', link: '', icon: }, 44 | { id: 10, type: 'divider' }, 45 | { id: 11, type: 'title', label: 'PROJECTS' }, 46 | { id: 12, label: 'My recent', link: '', icon: }, 47 | { id: 13, label: 'Starred', link: '', icon: }, 48 | { id: 14, label: 'Background', link: '', icon: }, 49 | ]; 50 | 51 | const SidebarView = ({ classes, theme, toggleSidebar, isSidebarOpened, isPermanent, location }) => { 52 | return ( 53 | 67 |
68 | 71 | 72 | 73 |
74 | 75 | {structure.map(link => )} 76 | 77 |
78 | ); 79 | } 80 | 81 | const drawerWidth = 240; 82 | 83 | const styles = theme => ({ 84 | menuButton: { 85 | marginLeft: 12, 86 | marginRight: 36, 87 | }, 88 | hide: { 89 | display: 'none', 90 | }, 91 | drawer: { 92 | width: drawerWidth, 93 | flexShrink: 0, 94 | whiteSpace: 'nowrap', 95 | top: theme.spacing.unit * 8, 96 | [theme.breakpoints.down("sm")]: { 97 | top: 0, 98 | } 99 | }, 100 | drawerOpen: { 101 | width: drawerWidth, 102 | transition: theme.transitions.create('width', { 103 | easing: theme.transitions.easing.sharp, 104 | duration: theme.transitions.duration.enteringScreen, 105 | }), 106 | }, 107 | drawerClose: { 108 | transition: theme.transitions.create('width', { 109 | easing: theme.transitions.easing.sharp, 110 | duration: theme.transitions.duration.leavingScreen, 111 | }), 112 | overflowX: 'hidden', 113 | width: theme.spacing.unit * 7 + 40, 114 | [theme.breakpoints.down("sm")]: { 115 | width: drawerWidth, 116 | } 117 | }, 118 | toolbar: { 119 | ...theme.mixins.toolbar, 120 | [theme.breakpoints.down("sm")]: { 121 | display: 'none', 122 | } 123 | }, 124 | content: { 125 | flexGrow: 1, 126 | padding: theme.spacing.unit * 3, 127 | }, 128 | mobileBackButton: { 129 | marginTop: theme.spacing.unit * .5, 130 | marginLeft: theme.spacing.unit * 3, 131 | [theme.breakpoints.only("sm")]: { 132 | marginTop: theme.spacing.unit * .625, 133 | }, 134 | [theme.breakpoints.up("md")]: { 135 | display: 'none', 136 | } 137 | } 138 | }); 139 | 140 | export default withStyles(styles, { withTheme: true })(SidebarView); 141 | -------------------------------------------------------------------------------- /src/components/Sidebar/components/Dot.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { makeStyles, useTheme } from "@material-ui/styles"; 3 | import classnames from "classnames"; 4 | 5 | // styles 6 | var useStyles = makeStyles(theme => ({ 7 | dotBase: { 8 | width: 8, 9 | height: 8, 10 | backgroundColor: theme.palette.text.hint, 11 | borderRadius: "50%", 12 | transition: theme.transitions.create("background-color"), 13 | }, 14 | dotSmall: { 15 | width: 5, 16 | height: 5 17 | }, 18 | dotLarge: { 19 | width: 11, 20 | height: 11, 21 | }, 22 | })); 23 | 24 | export default function Dot({ size, color }) { 25 | var classes = useStyles(); 26 | var theme = useTheme(); 27 | 28 | return ( 29 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/components/Sidebar/components/SidebarLink/SidebarLink.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | Collapse, 4 | Divider, 5 | List, 6 | ListItem, 7 | ListItemIcon, 8 | ListItemText, 9 | Typography, 10 | } from "@material-ui/core"; 11 | import { Inbox as InboxIcon } from "@material-ui/icons"; 12 | import { Link } from "react-router-dom"; 13 | import classnames from "classnames"; 14 | 15 | // styles 16 | import useStyles from "./styles"; 17 | 18 | // components 19 | import Dot from "../Dot"; 20 | 21 | export default function SidebarLink({ 22 | link, 23 | icon, 24 | label, 25 | children, 26 | location, 27 | isSidebarOpened, 28 | nested, 29 | type, 30 | }) { 31 | var classes = useStyles(); 32 | 33 | // local 34 | var [isOpen, setIsOpen] = useState(false); 35 | var isLinkActive = 36 | link && 37 | (location.pathname === link || location.pathname.indexOf(link) !== -1); 38 | 39 | if (type === "title") 40 | return ( 41 | 46 | {label} 47 | 48 | ); 49 | 50 | if (type === "divider") return ; 51 | 52 | if (!children) 53 | return ( 54 | 67 | 72 | {nested ? : icon} 73 | 74 | 83 | 84 | ); 85 | 86 | return ( 87 | <> 88 | 96 | 101 | {icon ? icon : } 102 | 103 | 112 | 113 | {children && ( 114 | 120 | 121 | {children.map(childrenLink => ( 122 | 130 | ))} 131 | 132 | 133 | )} 134 | 135 | ); 136 | 137 | // ########################################################### 138 | 139 | function toggleCollapse(e) { 140 | if (isSidebarOpened) { 141 | e.preventDefault(); 142 | setIsOpen(!isOpen); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/components/Sidebar/components/SidebarLink/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | link: { 5 | textDecoration: "none", 6 | "&:hover, &:focus": { 7 | backgroundColor: theme.palette.background.light, 8 | }, 9 | }, 10 | linkActive: { 11 | backgroundColor: theme.palette.background.light, 12 | }, 13 | linkNested: { 14 | paddingLeft: 0, 15 | "&:hover, &:focus": { 16 | backgroundColor: "#FFFFFF", 17 | }, 18 | }, 19 | linkIcon: { 20 | marginRight: theme.spacing(1), 21 | color: theme.palette.text.secondary + "99", 22 | transition: theme.transitions.create("color"), 23 | width: 24, 24 | display: "flex", 25 | justifyContent: "center", 26 | }, 27 | linkIconActive: { 28 | color: theme.palette.primary.main, 29 | }, 30 | linkText: { 31 | padding: 0, 32 | color: theme.palette.text.secondary + "CC", 33 | transition: theme.transitions.create(["opacity", "color"]), 34 | fontSize: 16, 35 | }, 36 | linkTextActive: { 37 | color: theme.palette.text.primary, 38 | }, 39 | linkTextHidden: { 40 | opacity: 0, 41 | }, 42 | nestedList: { 43 | paddingLeft: theme.spacing(2) + 30, 44 | }, 45 | sectionTitle: { 46 | marginLeft: theme.spacing(4.5), 47 | marginTop: theme.spacing(2), 48 | marginBottom: theme.spacing(2), 49 | }, 50 | divider: { 51 | marginTop: theme.spacing(2), 52 | marginBottom: theme.spacing(4), 53 | height: 1, 54 | backgroundColor: "#D8D8D880", 55 | }, 56 | })); 57 | -------------------------------------------------------------------------------- /src/components/Sidebar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sidebar", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "Sidebar.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Sidebar/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | const drawerWidth = 240; 4 | 5 | export default makeStyles(theme => ({ 6 | menuButton: { 7 | marginLeft: 12, 8 | marginRight: 36, 9 | }, 10 | hide: { 11 | display: "none", 12 | }, 13 | drawer: { 14 | width: drawerWidth, 15 | flexShrink: 0, 16 | whiteSpace: "nowrap", 17 | }, 18 | drawerOpen: { 19 | width: drawerWidth, 20 | transition: theme.transitions.create("width", { 21 | easing: theme.transitions.easing.sharp, 22 | duration: theme.transitions.duration.enteringScreen, 23 | }), 24 | }, 25 | drawerClose: { 26 | transition: theme.transitions.create("width", { 27 | easing: theme.transitions.easing.sharp, 28 | duration: theme.transitions.duration.leavingScreen, 29 | }), 30 | overflowX: "hidden", 31 | width: theme.spacing(7) + 40, 32 | [theme.breakpoints.down("sm")]: { 33 | width: drawerWidth, 34 | }, 35 | }, 36 | toolbar: { 37 | ...theme.mixins.toolbar, 38 | [theme.breakpoints.down("sm")]: { 39 | display: "none", 40 | }, 41 | }, 42 | content: { 43 | flexGrow: 1, 44 | padding: theme.spacing(3), 45 | }, 46 | /* sidebarList: { 47 | marginTop: theme.spacing(6), 48 | }, */ 49 | mobileBackButton: { 50 | marginTop: theme.spacing(0.5), 51 | marginLeft: theme.spacing(3), 52 | [theme.breakpoints.only("sm")]: { 53 | marginTop: theme.spacing(0.625), 54 | }, 55 | [theme.breakpoints.up("md")]: { 56 | display: "none", 57 | }, 58 | }, 59 | })); 60 | -------------------------------------------------------------------------------- /src/components/UserAvatar/UserAvatar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTheme } from "@material-ui/styles"; 3 | 4 | // styles 5 | import useStyles from "./styles"; 6 | 7 | // components 8 | import { Typography } from "../Wrappers"; 9 | 10 | export default function UserAvatar({ color = "primary", ...props }) { 11 | var classes = useStyles(); 12 | var theme = useTheme(); 13 | 14 | var letters = props.name 15 | .split(" ") 16 | .map(word => word[0]) 17 | .join(""); 18 | 19 | return ( 20 |
24 | {letters} 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/components/UserAvatar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UserAvatar", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "UserAvatar.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/UserAvatar/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(() => ({ 4 | avatar: { 5 | width: 30, 6 | height: 30, 7 | display: "flex", 8 | alignItems: "center", 9 | justifyContent: "center", 10 | borderRadius: "50%", 11 | }, 12 | text: { 13 | color: "white", 14 | }, 15 | })); 16 | -------------------------------------------------------------------------------- /src/components/Widget/Widget.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | Paper, 4 | IconButton, 5 | Menu, 6 | MenuItem, 7 | Typography, 8 | } from "@material-ui/core"; 9 | import { MoreVert as MoreIcon } from "@material-ui/icons"; 10 | import classnames from "classnames"; 11 | 12 | // styles 13 | import useStyles from "./styles"; 14 | 15 | export default function Widget({ 16 | children, 17 | title, 18 | noBodyPadding, 19 | bodyClass, 20 | disableWidgetMenu, 21 | header, 22 | ...props 23 | }) { 24 | var classes = useStyles(); 25 | 26 | // local 27 | var [moreButtonRef, setMoreButtonRef] = useState(null); 28 | var [isMoreMenuOpen, setMoreMenuOpen] = useState(false); 29 | 30 | return ( 31 |
32 | 33 |
34 | {header ? ( 35 | header 36 | ) : ( 37 | 38 | 39 | {title} 40 | 41 | {!disableWidgetMenu && ( 42 | setMoreMenuOpen(true)} 48 | buttonRef={setMoreButtonRef} 49 | > 50 | 51 | 52 | )} 53 | 54 | )} 55 |
56 |
62 | {children} 63 |
64 |
65 | setMoreMenuOpen(false)} 70 | disableAutoFocusItem 71 | > 72 | 73 | Edit 74 | 75 | 76 | Copy 77 | 78 | 79 | Delete 80 | 81 | 82 | Print 83 | 84 | 85 |
86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /src/components/Widget/WidgetView.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classnames from "classnames"; 3 | import { 4 | Paper, 5 | IconButton, 6 | Menu, 7 | MenuItem, 8 | withStyles 9 | } from "@material-ui/core"; 10 | import { MoreVert as MoreIcon } from "@material-ui/icons"; 11 | import Typography from "@material-ui/core/es/Typography/Typography"; 12 | 13 | const Widget = ({ 14 | classes, 15 | children, 16 | title, 17 | noBodyPadding, 18 | bodyClass, 19 | className, 20 | disableWidgetMenu, 21 | ...props 22 | }) => ( 23 |
24 | 25 |
26 | {props.header ? ( 27 | props.header 28 | ) : ( 29 | 30 | 31 | {title} 32 | 33 | {!disableWidgetMenu && ( 34 | props.setMoreMenuOpen(true)} 40 | buttonRef={props.setMoreButtonRef} 41 | > 42 | 43 | 44 | )} 45 | 46 | )} 47 |
48 |
54 | {children} 55 |
56 |
57 | props.setMoreMenuOpen(false)} 62 | disableAutoFocusItem 63 | > 64 | 65 | Edit 66 | 67 | 68 | Copy 69 | 70 | 71 | Delete 72 | 73 | 74 | Print 75 | 76 | 77 |
78 | ); 79 | 80 | const styles = theme => ({ 81 | widgetWrapper: { 82 | display: "flex", 83 | minHeight: "100%" 84 | }, 85 | widgetHeader: { 86 | padding: theme.spacing.unit * 3, 87 | paddingBottom: theme.spacing.unit, 88 | display: "flex", 89 | justifyContent: "space-between", 90 | alignItems: "center" 91 | }, 92 | widgetRoot: { 93 | boxShadow: theme.customShadows.widget 94 | }, 95 | widgetBody: { 96 | paddingBottom: theme.spacing.unit * 3, 97 | paddingRight: theme.spacing.unit * 3, 98 | paddingLeft: theme.spacing.unit * 3 99 | }, 100 | noPadding: { 101 | padding: 0 102 | }, 103 | paper: { 104 | display: "flex", 105 | flexDirection: "column", 106 | flexGrow: 1, 107 | overflow: "hidden" 108 | }, 109 | moreButton: { 110 | margin: -theme.spacing.unit, 111 | padding: 0, 112 | width: 40, 113 | height: 40, 114 | color: theme.palette.text.hint, 115 | "&:hover": { 116 | backgroundColor: theme.palette.primary.main, 117 | color: "rgba(255, 255, 255, 0.35)" 118 | } 119 | } 120 | }); 121 | 122 | export default withStyles(styles, { withTheme: true })(Widget); 123 | -------------------------------------------------------------------------------- /src/components/Widget/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Widget", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "Widget.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Widget/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | widgetWrapper: { 5 | display: "flex", 6 | minHeight: "100%", 7 | }, 8 | widgetHeader: { 9 | padding: theme.spacing(3), 10 | paddingBottom: theme.spacing(1), 11 | display: "flex", 12 | justifyContent: "space-between", 13 | alignItems: "center", 14 | }, 15 | widgetRoot: { 16 | boxShadow: theme.customShadows.widget, 17 | }, 18 | widgetBody: { 19 | paddingBottom: theme.spacing(3), 20 | paddingRight: theme.spacing(3), 21 | paddingLeft: theme.spacing(3), 22 | }, 23 | noPadding: { 24 | padding: 0, 25 | }, 26 | paper: { 27 | display: "flex", 28 | flexDirection: "column", 29 | flexGrow: 1, 30 | overflow: "hidden", 31 | }, 32 | moreButton: { 33 | margin: -theme.spacing(1), 34 | padding: 0, 35 | width: 40, 36 | height: 40, 37 | color: theme.palette.text.hint, 38 | "&:hover": { 39 | backgroundColor: theme.palette.primary.main, 40 | color: "rgba(255, 255, 255, 0.35)", 41 | }, 42 | }, 43 | })); 44 | -------------------------------------------------------------------------------- /src/components/Wrappers/Wrappers.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | withStyles, 4 | Badge as BadgeBase, 5 | Typography as TypographyBase, 6 | Button as ButtonBase, 7 | } from "@material-ui/core"; 8 | import { useTheme, makeStyles } from "@material-ui/styles"; 9 | import classnames from "classnames"; 10 | 11 | // styles 12 | var useStyles = makeStyles(theme => ({ 13 | badge: { 14 | fontWeight: 600, 15 | height: 16, 16 | minWidth: 16, 17 | }, 18 | })); 19 | 20 | function Badge({ children, colorBrightness, color, ...props }) { 21 | var classes = useStyles(); 22 | var theme = useTheme(); 23 | var Styled = createStyled({ 24 | badge: { 25 | backgroundColor: getColor(color, theme, colorBrightness), 26 | }, 27 | }); 28 | 29 | return ( 30 | 31 | {styledProps => ( 32 | 38 | {children} 39 | 40 | )} 41 | 42 | ); 43 | } 44 | 45 | function Typography({ 46 | children, 47 | weight, 48 | size, 49 | colorBrightness, 50 | color, 51 | ...props 52 | }) { 53 | var theme = useTheme(); 54 | 55 | return ( 56 | 64 | {children} 65 | 66 | ); 67 | } 68 | 69 | function Button({ children, color, className, ...props }) { 70 | var theme = useTheme(); 71 | 72 | var Styled = createStyled({ 73 | root: { 74 | color: getColor(color, theme), 75 | }, 76 | contained: { 77 | backgroundColor: getColor(color, theme), 78 | boxShadow: theme.customShadows.widget, 79 | color: `${color ? "white" : theme.palette.text.primary} !important`, 80 | "&:hover": { 81 | backgroundColor: getColor(color, theme, "light"), 82 | boxShadow: theme.customShadows.widgetWide, 83 | }, 84 | "&:active": { 85 | boxShadow: theme.customShadows.widgetWide, 86 | }, 87 | }, 88 | outlined: { 89 | color: getColor(color, theme), 90 | borderColor: getColor(color, theme), 91 | }, 92 | select: { 93 | backgroundColor: theme.palette.primary.main, 94 | color: "#fff", 95 | }, 96 | }); 97 | 98 | return ( 99 | 100 | {({ classes }) => ( 101 | 115 | {children} 116 | 117 | )} 118 | 119 | ); 120 | } 121 | 122 | export { Badge, Typography, Button }; 123 | 124 | // ######################################################################## 125 | 126 | function getColor(color, theme, brigtness = "main") { 127 | if (color && theme.palette[color] && theme.palette[color][brigtness]) { 128 | return theme.palette[color][brigtness]; 129 | } 130 | } 131 | 132 | function getFontWeight(style) { 133 | switch (style) { 134 | case "light": 135 | return 300; 136 | case "medium": 137 | return 500; 138 | case "bold": 139 | return 600; 140 | default: 141 | return 400; 142 | } 143 | } 144 | 145 | function getFontSize(size, variant = "", theme) { 146 | var multiplier; 147 | 148 | switch (size) { 149 | case "sm": 150 | multiplier = 0.8; 151 | break; 152 | case "md": 153 | multiplier = 1.5; 154 | break; 155 | case "xl": 156 | multiplier = 2; 157 | break; 158 | case "xxl": 159 | multiplier = 3; 160 | break; 161 | default: 162 | multiplier = 1; 163 | break; 164 | } 165 | 166 | var defaultSize = 167 | variant && theme.typography[variant] 168 | ? theme.typography[variant].fontSize 169 | : theme.typography.fontSize + "px"; 170 | 171 | return `calc(${defaultSize} * ${multiplier})`; 172 | } 173 | 174 | function createStyled(styles, options) { 175 | var Styled = function(props) { 176 | const { children, ...other } = props; 177 | return children(other); 178 | }; 179 | 180 | return withStyles(styles, options)(Styled); 181 | } 182 | -------------------------------------------------------------------------------- /src/components/Wrappers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Wrappers", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "Wrappers.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/context/LayoutContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | var LayoutStateContext = React.createContext(); 4 | var LayoutDispatchContext = React.createContext(); 5 | 6 | function layoutReducer(state, action) { 7 | switch (action.type) { 8 | case "TOGGLE_SIDEBAR": 9 | return { ...state, isSidebarOpened: !state.isSidebarOpened }; 10 | default: { 11 | throw new Error(`Unhandled action type: ${action.type}`); 12 | } 13 | } 14 | } 15 | 16 | function LayoutProvider({ children }) { 17 | var [state, dispatch] = React.useReducer(layoutReducer, { 18 | isSidebarOpened: true, 19 | }); 20 | return ( 21 | 22 | 23 | {children} 24 | 25 | 26 | ); 27 | } 28 | 29 | function useLayoutState() { 30 | var context = React.useContext(LayoutStateContext); 31 | if (context === undefined) { 32 | throw new Error("useLayoutState must be used within a LayoutProvider"); 33 | } 34 | return context; 35 | } 36 | 37 | function useLayoutDispatch() { 38 | var context = React.useContext(LayoutDispatchContext); 39 | if (context === undefined) { 40 | throw new Error("useLayoutDispatch must be used within a LayoutProvider"); 41 | } 42 | return context; 43 | } 44 | 45 | export { LayoutProvider, useLayoutState, useLayoutDispatch, toggleSidebar }; 46 | 47 | // ########################################################### 48 | function toggleSidebar(dispatch) { 49 | dispatch({ 50 | type: "TOGGLE_SIDEBAR", 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /src/context/UserContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | var UserStateContext = React.createContext(); 4 | var UserDispatchContext = React.createContext(); 5 | 6 | function userReducer(state, action) { 7 | switch (action.type) { 8 | case "LOGIN_SUCCESS": 9 | return { ...state, isAuthenticated: true }; 10 | case "SIGN_OUT_SUCCESS": 11 | return { ...state, isAuthenticated: false }; 12 | default: { 13 | throw new Error(`Unhandled action type: ${action.type}`); 14 | } 15 | } 16 | } 17 | 18 | function UserProvider({ children }) { 19 | var [state, dispatch] = React.useReducer(userReducer, { 20 | isAuthenticated: !!localStorage.getItem("id_token"), 21 | }); 22 | 23 | return ( 24 | 25 | 26 | {children} 27 | 28 | 29 | ); 30 | } 31 | 32 | function useUserState() { 33 | var context = React.useContext(UserStateContext); 34 | if (context === undefined) { 35 | throw new Error("useUserState must be used within a UserProvider"); 36 | } 37 | return context; 38 | } 39 | 40 | function useUserDispatch() { 41 | var context = React.useContext(UserDispatchContext); 42 | if (context === undefined) { 43 | throw new Error("useUserDispatch must be used within a UserProvider"); 44 | } 45 | return context; 46 | } 47 | 48 | export { UserProvider, useUserState, useUserDispatch, loginUser, signOut }; 49 | 50 | // ########################################################### 51 | 52 | function loginUser(dispatch, login, password, history, setIsLoading, setError) { 53 | setError(false); 54 | setIsLoading(true); 55 | 56 | if (!!login && !!password) { 57 | setTimeout(() => { 58 | localStorage.setItem('id_token', 1) 59 | setError(null) 60 | setIsLoading(false) 61 | dispatch({ type: 'LOGIN_SUCCESS' }) 62 | 63 | history.push('/app/dashboard') 64 | }, 2000); 65 | } else { 66 | dispatch({ type: "LOGIN_FAILURE" }); 67 | setError(true); 68 | setIsLoading(false); 69 | } 70 | } 71 | 72 | function signOut(dispatch, history) { 73 | localStorage.removeItem("id_token"); 74 | dispatch({ type: "SIGN_OUT_SUCCESS" }); 75 | history.push("/login"); 76 | } 77 | -------------------------------------------------------------------------------- /src/images/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { ThemeProvider } from "@material-ui/styles"; 4 | import { CssBaseline } from "@material-ui/core"; 5 | 6 | import Themes from "./themes"; 7 | import App from "./components/App"; 8 | import * as serviceWorker from "./serviceWorker"; 9 | import { LayoutProvider } from "./context/LayoutContext"; 10 | import { UserProvider } from "./context/UserContext"; 11 | 12 | ReactDOM.render( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | , 21 | document.getElementById("root"), 22 | ); 23 | 24 | // If you want your app to work offline and load faster, you can change 25 | // unregister() to register() below. Note this comes with some pitfalls. 26 | // Learn more about service workers: http://bit.ly/CRA-PWA 27 | serviceWorker.unregister(); 28 | -------------------------------------------------------------------------------- /src/pages/charts/Charts.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Grid } from "@material-ui/core"; 3 | import { useTheme } from "@material-ui/styles"; 4 | import { 5 | CartesianGrid, 6 | Legend, 7 | Line, 8 | LineChart, 9 | Pie, 10 | PieChart, 11 | ResponsiveContainer, 12 | Sector, 13 | Tooltip, 14 | XAxis, 15 | YAxis, 16 | } from "recharts"; 17 | 18 | // components 19 | import Widget from "../../components/Widget/Widget"; 20 | import ApexLineChart from "./components/ApexLineChart"; 21 | import ApexHeatmap from "./components/ApexHeatmap"; 22 | import PageTitle from "../../components/PageTitle/PageTitle"; 23 | 24 | const lineChartData = [ 25 | { 26 | name: "Page A", 27 | uv: 4000, 28 | pv: 2400, 29 | amt: 2400, 30 | }, 31 | { 32 | name: "Page B", 33 | uv: 3000, 34 | pv: 1398, 35 | amt: 2210, 36 | }, 37 | { 38 | name: "Page C", 39 | uv: 2000, 40 | pv: 9800, 41 | amt: 2290, 42 | }, 43 | { 44 | name: "Page D", 45 | uv: 2780, 46 | pv: 3908, 47 | amt: 2000, 48 | }, 49 | { 50 | name: "Page E", 51 | uv: 1890, 52 | pv: 4800, 53 | amt: 2181, 54 | }, 55 | { 56 | name: "Page F", 57 | uv: 2390, 58 | pv: 3800, 59 | amt: 2500, 60 | }, 61 | { 62 | name: "Page G", 63 | uv: 3490, 64 | pv: 4300, 65 | amt: 2100, 66 | }, 67 | ]; 68 | 69 | const pieChartData = [ 70 | { name: "Group A", value: 400 }, 71 | { name: "Group B", value: 300 }, 72 | { name: "Group C", value: 300 }, 73 | { name: "Group D", value: 200 }, 74 | ]; 75 | 76 | export default function Charts(props) { 77 | var theme = useTheme(); 78 | 79 | // local 80 | var [activeIndex, setActiveIndexId] = useState(0); 81 | 82 | return ( 83 | <> 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 110 | 111 | 112 | 113 | 114 | 115 | 121 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | setActiveIndexId(id)} 145 | /> 146 | 147 | 148 | 149 | 150 | 151 | 152 | ); 153 | } 154 | 155 | // ################################################################ 156 | 157 | function renderActiveShape(props) { 158 | var RADIAN = Math.PI / 180; 159 | var { 160 | cx, 161 | cy, 162 | midAngle, 163 | innerRadius, 164 | outerRadius, 165 | startAngle, 166 | endAngle, 167 | fill, 168 | payload, 169 | percent, 170 | value, 171 | } = props; 172 | var sin = Math.sin(-RADIAN * midAngle); 173 | var cos = Math.cos(-RADIAN * midAngle); 174 | var sx = cx + (outerRadius + 10) * cos; 175 | var sy = cy + (outerRadius + 10) * sin; 176 | var mx = cx + (outerRadius + 30) * cos; 177 | var my = cy + (outerRadius + 30) * sin; 178 | var ex = mx + (cos >= 0 ? 1 : -1) * 22; 179 | var ey = my; 180 | var textAnchor = cos >= 0 ? "start" : "end"; 181 | 182 | return ( 183 | 184 | 185 | {payload.name} 186 | 187 | 196 | 205 | 210 | 211 | = 0 ? 1 : -1) * 12} 213 | y={ey} 214 | textAnchor={textAnchor} 215 | fill="#333" 216 | >{`PV ${value}`} 217 | = 0 ? 1 : -1) * 12} 219 | y={ey} 220 | dy={18} 221 | textAnchor={textAnchor} 222 | fill="#999" 223 | > 224 | {`(Rate ${(percent * 100).toFixed(2)}%)`} 225 | 226 | 227 | ); 228 | } 229 | -------------------------------------------------------------------------------- /src/pages/charts/components/ApexHeatmap.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTheme } from "@material-ui/styles"; 3 | import ApexCharts from "react-apexcharts"; 4 | 5 | const series = [ 6 | { 7 | name: "Metric1", 8 | data: generateData(18, { 9 | min: 0, 10 | max: 90, 11 | }), 12 | }, 13 | { 14 | name: "Metric2", 15 | data: generateData(18, { 16 | min: 0, 17 | max: 90, 18 | }), 19 | }, 20 | { 21 | name: "Metric3", 22 | data: generateData(18, { 23 | min: 0, 24 | max: 90, 25 | }), 26 | }, 27 | { 28 | name: "Metric4", 29 | data: generateData(18, { 30 | min: 0, 31 | max: 90, 32 | }), 33 | }, 34 | { 35 | name: "Metric5", 36 | data: generateData(18, { 37 | min: 0, 38 | max: 90, 39 | }), 40 | }, 41 | { 42 | name: "Metric6", 43 | data: generateData(18, { 44 | min: 0, 45 | max: 90, 46 | }), 47 | }, 48 | { 49 | name: "Metric7", 50 | data: generateData(18, { 51 | min: 0, 52 | max: 90, 53 | }), 54 | }, 55 | { 56 | name: "Metric8", 57 | data: generateData(18, { 58 | min: 0, 59 | max: 90, 60 | }), 61 | }, 62 | { 63 | name: "Metric9", 64 | data: generateData(18, { 65 | min: 0, 66 | max: 90, 67 | }), 68 | }, 69 | ]; 70 | 71 | export default function ApexLineChart() { 72 | var theme = useTheme(); 73 | 74 | return ( 75 | 81 | ); 82 | } 83 | 84 | // ################################################################## 85 | function generateData(count, yrange) { 86 | var i = 0; 87 | var series = []; 88 | while (i < count) { 89 | var x = "w" + (i + 1).toString(); 90 | var y = 91 | Math.floor(Math.random() * (yrange.max - yrange.min + 1)) + yrange.min; 92 | 93 | series.push({ 94 | x: x, 95 | y: y, 96 | }); 97 | i++; 98 | } 99 | 100 | return series; 101 | } 102 | 103 | function themeOptions(theme) { 104 | return { 105 | chart: { 106 | toolbar: { 107 | show: false, 108 | }, 109 | }, 110 | dataLabels: { 111 | enabled: false, 112 | }, 113 | colors: [theme.palette.primary.main], 114 | }; 115 | } 116 | -------------------------------------------------------------------------------- /src/pages/charts/components/ApexLineChart.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ApexCharts from "react-apexcharts"; 3 | import { useTheme } from "@material-ui/styles"; 4 | 5 | const series = [ 6 | { 7 | name: "series1", 8 | data: [31, 40, 28, 51, 42, 109, 100], 9 | }, 10 | { 11 | name: "series2", 12 | data: [11, 32, 45, 32, 34, 52, 41], 13 | }, 14 | ]; 15 | 16 | export default function ApexLineChart() { 17 | var theme = useTheme(); 18 | 19 | return ( 20 | 26 | ); 27 | } 28 | 29 | // ############################################################ 30 | function themeOptions(theme) { 31 | return { 32 | dataLabels: { 33 | enabled: false, 34 | }, 35 | stroke: { 36 | curve: "smooth", 37 | }, 38 | xaxis: { 39 | type: "datetime", 40 | categories: [ 41 | "2018-09-19T00:00:00", 42 | "2018-09-19T01:30:00", 43 | "2018-09-19T02:30:00", 44 | "2018-09-19T03:30:00", 45 | "2018-09-19T04:30:00", 46 | "2018-09-19T05:30:00", 47 | "2018-09-19T06:30:00", 48 | ], 49 | }, 50 | tooltip: { 51 | x: { 52 | format: "dd/MM/yy HH:mm", 53 | }, 54 | }, 55 | fill: { 56 | colors: [theme.palette.primary.light, theme.palette.success.light], 57 | }, 58 | colors: [theme.palette.primary.main, theme.palette.success.main], 59 | chart: { 60 | toolbar: { 61 | show: false, 62 | }, 63 | }, 64 | legend: { 65 | show: false, 66 | }, 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /src/pages/charts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Charts", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "Charts.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/dashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | Grid, 4 | LinearProgress, 5 | Select, 6 | OutlinedInput, 7 | MenuItem, 8 | } from "@material-ui/core"; 9 | import { useTheme } from "@material-ui/styles"; 10 | import { 11 | ResponsiveContainer, 12 | ComposedChart, 13 | AreaChart, 14 | LineChart, 15 | Line, 16 | Area, 17 | PieChart, 18 | Pie, 19 | Cell, 20 | YAxis, 21 | XAxis, 22 | } from "recharts"; 23 | 24 | // styles 25 | import useStyles from "./styles"; 26 | 27 | // components 28 | import mock from "./mock"; 29 | import Widget from "../../components/Widget"; 30 | import PageTitle from "../../components/PageTitle"; 31 | import { Typography } from "../../components/Wrappers"; 32 | import Dot from "../../components/Sidebar/components/Dot"; 33 | import Table from "./components/Table/Table"; 34 | import BigStat from "./components/BigStat/BigStat"; 35 | 36 | const mainChartData = getMainChartData(); 37 | const PieChartData = [ 38 | { name: "Group A", value: 400, color: "primary" }, 39 | { name: "Group B", value: 300, color: "secondary" }, 40 | { name: "Group C", value: 300, color: "warning" }, 41 | { name: "Group D", value: 200, color: "success" }, 42 | ]; 43 | 44 | export default function Dashboard(props) { 45 | var classes = useStyles(); 46 | var theme = useTheme(); 47 | 48 | // local 49 | var [mainChartState, setMainChartState] = useState("monthly"); 50 | 51 | return ( 52 | <> 53 | 54 | 55 | 56 | 62 |
63 | 64 | 12, 678 65 | 66 | 78 | 85 | 86 |
87 | 93 | 94 | 95 | Registrations 96 | 97 | 860 98 | 99 | 100 | 101 | Sign Out 102 | 103 | 32 104 | 105 | 106 | 107 | Rate 108 | 109 | 3.25% 110 | 111 | 112 |
113 |
114 | 115 | 121 |
122 |
123 | 124 | 129 | Integration 130 | 131 |
132 |
133 | 134 | 139 | SDK 140 | 141 |
142 |
143 |
144 | 150 | Integration 151 | 152 | 158 |
159 |
160 | 166 | SDK 167 | 168 | 174 |
175 |
176 |
177 | 178 | 184 |
185 | 190 | 60% / 37°С / 3.3 Ghz 191 | 192 |
193 | 194 | 195 | 203 | 204 | 205 |
206 |
207 |
208 | 213 | 54% / 31°С / 3.3 Ghz 214 | 215 |
216 | 217 | 218 | 226 | 227 | 228 |
229 |
230 |
231 | 236 | 57% / 21°С / 3.3 Ghz 237 | 238 |
239 | 240 | 241 | 249 | 250 | 251 |
252 |
253 |
254 |
255 | 256 | 257 | 258 | 259 | 260 | 261 | 267 | {PieChartData.map((entry, index) => ( 268 | 272 | ))} 273 | 274 | 275 | 276 | 277 | 278 |
279 | {PieChartData.map(({ name, value, color }, index) => ( 280 |
281 | 282 | 283 |  {name}  284 | 285 | 286 |  {value} 287 | 288 |
289 | ))} 290 |
291 |
292 |
293 |
294 |
295 | 296 | 300 | 305 | Daily Line Chart 306 | 307 |
308 |
309 | 310 | 311 | Tablet 312 | 313 |
314 |
315 | 316 | 317 | Mobile 318 | 319 |
320 |
321 | 322 | 323 | Desktop 324 | 325 |
326 |
327 | 345 |
346 | } 347 | > 348 | 349 | 353 | 359 | i + 1} 361 | tick={{ fill: theme.palette.text.hint + "80", fontSize: 14 }} 362 | stroke={theme.palette.text.hint + "80"} 363 | tickLine={false} 364 | /> 365 | 372 | 380 | 391 | 392 | 393 | 394 | 395 | {mock.bigStat.map(stat => ( 396 | 397 | 398 | 399 | ))} 400 | 401 | 407 | 408 | 409 | 410 | 411 | 412 | ); 413 | } 414 | 415 | // ####################################################################### 416 | function getRandomData(length, min, max, multiplier = 10, maxDiff = 10) { 417 | var array = new Array(length).fill(); 418 | let lastValue; 419 | 420 | return array.map((item, index) => { 421 | let randomValue = Math.floor(Math.random() * multiplier + 1); 422 | 423 | while ( 424 | randomValue <= min || 425 | randomValue >= max || 426 | (lastValue && randomValue - lastValue > maxDiff) 427 | ) { 428 | randomValue = Math.floor(Math.random() * multiplier + 1); 429 | } 430 | 431 | lastValue = randomValue; 432 | 433 | return { value: randomValue }; 434 | }); 435 | } 436 | 437 | function getMainChartData() { 438 | var resultArray = []; 439 | var tablet = getRandomData(31, 3500, 6500, 7500, 1000); 440 | var desktop = getRandomData(31, 1500, 7500, 7500, 1500); 441 | var mobile = getRandomData(31, 1500, 7500, 7500, 1500); 442 | 443 | for (let i = 0; i < tablet.length; i++) { 444 | resultArray.push({ 445 | tablet: tablet[i].value, 446 | desktop: desktop[i].value, 447 | mobile: mobile[i].value, 448 | }); 449 | } 450 | 451 | return resultArray; 452 | } 453 | -------------------------------------------------------------------------------- /src/pages/dashboard/components/BigStat/BigStat.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Grid, Select, MenuItem, Input } from "@material-ui/core"; 3 | import { ArrowForward as ArrowForwardIcon } from "@material-ui/icons"; 4 | import { useTheme } from "@material-ui/styles"; 5 | import { BarChart, Bar } from "recharts"; 6 | import classnames from "classnames"; 7 | 8 | // styles 9 | import useStyles from "./styles"; 10 | 11 | // components 12 | import Widget from "../../../../components/Widget"; 13 | import { Typography } from "../../../../components/Wrappers"; 14 | 15 | export default function BigStat(props) { 16 | var { product, total, color, registrations, bounce } = props; 17 | var classes = useStyles(); 18 | var theme = useTheme(); 19 | 20 | // local 21 | var [value, setValue] = useState("daily"); 22 | 23 | return ( 24 | 27 | {product} 28 | 29 | 37 | } 38 | className={classes.select} 39 | > 40 | Daily 41 | Weekly 42 | Monthly 43 | 44 | 45 | } 46 | upperTitle 47 | > 48 |
49 |
50 | 51 | {total[value]} 52 | 53 | 54 |  {total.percent.profit ? "+" : "-"} 55 | {total.percent.value}% 56 | 57 |
58 | 59 | 65 | 66 |
67 |
68 |
69 | 70 | {registrations[value].value} 71 | 76 | 77 | 78 | Registrations 79 | 80 |
81 |
82 | 83 | {bounce[value].value}% 84 | 89 | 90 | 91 | Bounce Rate 92 | 93 |
94 |
95 | 96 | 97 | {registrations[value].value * 10} 98 | 99 | 104 | 105 | 106 | Views 107 | 108 |
109 |
110 |
111 | ); 112 | } 113 | 114 | // ####################################################################### 115 | 116 | function getRandomData() { 117 | return Array(7) 118 | .fill() 119 | .map(() => ({ value: Math.floor(Math.random() * 10) + 1 })); 120 | } 121 | -------------------------------------------------------------------------------- /src/pages/dashboard/components/BigStat/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | title: { 5 | display: "flex", 6 | flexDirection: "row", 7 | justifyContent: "space-between", 8 | alignItems: "center", 9 | width: "100%", 10 | marginBottom: theme.spacing(1), 11 | }, 12 | bottomStatsContainer: { 13 | display: "flex", 14 | justifyContent: "space-between", 15 | margin: theme.spacing(1) * -2, 16 | marginTop: theme.spacing(1), 17 | }, 18 | statCell: { 19 | padding: theme.spacing(2), 20 | }, 21 | totalValueContainer: { 22 | display: "flex", 23 | alignItems: "flex-end", 24 | justifyContent: "space-between", 25 | }, 26 | totalValue: { 27 | display: "flex", 28 | alignItems: "baseline", 29 | }, 30 | profitArrow: { 31 | transform: "rotate(-45deg)", 32 | fill: theme.palette.success.main, 33 | }, 34 | profitArrowDanger: { 35 | transform: "rotate(45deg)", 36 | fill: theme.palette.secondary.main, 37 | }, 38 | selectInput: { 39 | padding: 10, 40 | paddingRight: 25, 41 | "&:focus": { 42 | backgroundColor: "white", 43 | }, 44 | }, 45 | })); -------------------------------------------------------------------------------- /src/pages/dashboard/components/Table/Table.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | Table, 4 | TableRow, 5 | TableHead, 6 | TableBody, 7 | TableCell, 8 | } from "@material-ui/core"; 9 | 10 | // components 11 | import { Button } from "../../../../components/Wrappers"; 12 | 13 | const states = { 14 | sent: "success", 15 | pending: "warning", 16 | declined: "secondary", 17 | }; 18 | 19 | export default function TableComponent({ data }) { 20 | var keys = Object.keys(data[0]).map(i => i.toUpperCase()); 21 | keys.shift(); // delete "id" key 22 | 23 | return ( 24 |
25 | 26 | 27 | {keys.map(key => ( 28 | {key} 29 | ))} 30 | 31 | 32 | 33 | {data.map(({ id, name, email, product, price, date, city, status }) => ( 34 | 35 | {name} 36 | {email} 37 | {product} 38 | {price} 39 | {date} 40 | {city} 41 | 42 | 50 | 51 | 52 | ))} 53 | 54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/pages/dashboard/mock.js: -------------------------------------------------------------------------------- 1 | export default { 2 | tasks: [ 3 | { 4 | id: 0, 5 | type: "Meeting", 6 | title: "Meeting with Andrew Piker", 7 | time: "9:00" 8 | }, 9 | { 10 | id: 1, 11 | type: "Call", 12 | title: "Call with HT Company", 13 | time: "12:00" 14 | }, 15 | { 16 | id: 2, 17 | type: "Meeting", 18 | title: "Meeting with Zoe Alison", 19 | time: "14:00" 20 | }, 21 | { 22 | id: 3, 23 | type: "Interview", 24 | title: "Interview with HR", 25 | time: "15:00" 26 | } 27 | ], 28 | bigStat: [ 29 | { 30 | product: "Light Blue", 31 | total: { 32 | monthly: 4232, 33 | weekly: 1465, 34 | daily: 199, 35 | percent: { value: 3.7, profit: false } 36 | }, 37 | color: "primary", 38 | registrations: { 39 | monthly: { value: 830, profit: false }, 40 | weekly: { value: 215, profit: true }, 41 | daily: { value: 33, profit: true } 42 | }, 43 | bounce: { 44 | monthly: { value: 4.5, profit: false }, 45 | weekly: { value: 3, profit: true }, 46 | daily: { value: 3.25, profit: true } 47 | } 48 | }, 49 | { 50 | product: "Sing App", 51 | total: { 52 | monthly: 754, 53 | weekly: 180, 54 | daily: 27, 55 | percent: { value: 2.5, profit: true } 56 | }, 57 | color: "warning", 58 | registrations: { 59 | monthly: { value: 32, profit: true }, 60 | weekly: { value: 8, profit: true }, 61 | daily: { value: 2, profit: false } 62 | }, 63 | bounce: { 64 | monthly: { value: 2.5, profit: true }, 65 | weekly: { value: 4, profit: false }, 66 | daily: { value: 4.5, profit: false } 67 | } 68 | }, 69 | { 70 | product: "RNS", 71 | total: { 72 | monthly: 1025, 73 | weekly: 301, 74 | daily: 44, 75 | percent: { value: 3.1, profit: true } 76 | }, 77 | color: "secondary", 78 | registrations: { 79 | monthly: { value: 230, profit: true }, 80 | weekly: { value: 58, profit: false }, 81 | daily: { value: 15, profit: false } 82 | }, 83 | bounce: { 84 | monthly: { value: 21.5, profit: false }, 85 | weekly: { value: 19.35, profit: false }, 86 | daily: { value: 10.1, profit: true } 87 | } 88 | } 89 | ], 90 | notifications: [ 91 | { 92 | id: 0, 93 | icon: "thumbs-up", 94 | color: "primary", 95 | content: 96 | 'Ken accepts your invitation' 97 | }, 98 | { 99 | id: 1, 100 | icon: "file", 101 | color: "success", 102 | content: "Report from LT Company" 103 | }, 104 | { 105 | id: 2, 106 | icon: "envelope", 107 | color: "danger", 108 | content: '4 Private Mails' 109 | }, 110 | { 111 | id: 3, 112 | icon: "comment", 113 | color: "success", 114 | content: '3 Comments to your Post' 115 | }, 116 | { 117 | id: 4, 118 | icon: "cog", 119 | color: "light", 120 | content: 'New Version of RNS app' 121 | }, 122 | { 123 | id: 5, 124 | icon: "bell", 125 | color: "info", 126 | content: 127 | '15 Notifications from Social Apps' 128 | } 129 | ], 130 | table: [ 131 | { 132 | id: 0, 133 | name: "Mark Otto", 134 | email: "ottoto@wxample.com", 135 | product: "ON the Road", 136 | price: "$25 224.2", 137 | date: "11 May 2017", 138 | city: "Otsego", 139 | status: "Sent" 140 | }, 141 | { 142 | id: 1, 143 | name: "Jacob Thornton", 144 | email: "thornton@wxample.com", 145 | product: "HP Core i7", 146 | price: "$1 254.2", 147 | date: "4 Jun 2017", 148 | city: "Fivepointville", 149 | status: "Sent" 150 | }, 151 | { 152 | id: 2, 153 | name: "Larry the Bird", 154 | email: "bird@wxample.com", 155 | product: "Air Pro", 156 | price: "$1 570.0", 157 | date: "27 Aug 2017", 158 | city: "Leadville North", 159 | status: "Pending" 160 | }, 161 | { 162 | id: 3, 163 | name: "Joseph May", 164 | email: "josephmay@wxample.com", 165 | product: "Version Control", 166 | price: "$5 224.5", 167 | date: "19 Feb 2018", 168 | city: "Seaforth", 169 | status: "Declined" 170 | }, 171 | { 172 | id: 4, 173 | name: "Peter Horadnia", 174 | email: "horadnia@wxample.com", 175 | product: "Let's Dance", 176 | price: "$43 594.7", 177 | date: "1 Mar 2018", 178 | city: "Hanoverton", 179 | status: "Sent" 180 | } 181 | ] 182 | }; 183 | -------------------------------------------------------------------------------- /src/pages/dashboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dashboard", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "Dashboard.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/dashboard/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | card: { 5 | minHeight: "100%", 6 | display: "flex", 7 | flexDirection: "column", 8 | }, 9 | visitsNumberContainer: { 10 | display: "flex", 11 | alignItems: "center", 12 | flexGrow: 1, 13 | paddingBottom: theme.spacing(1), 14 | }, 15 | progressSection: { 16 | marginBottom: theme.spacing(1), 17 | }, 18 | progressTitle: { 19 | marginBottom: theme.spacing(2), 20 | }, 21 | progress: { 22 | marginBottom: theme.spacing(1), 23 | backgroundColor: theme.palette.primary.main, 24 | }, 25 | pieChartLegendWrapper: { 26 | height: "100%", 27 | display: "flex", 28 | flexDirection: "column", 29 | justifyContent: "center", 30 | alignItems: "flex-end", 31 | marginRight: theme.spacing(1), 32 | }, 33 | legendItemContainer: { 34 | display: "flex", 35 | alignItems: "center", 36 | marginBottom: theme.spacing(1), 37 | }, 38 | fullHeightBody: { 39 | display: "flex", 40 | flexGrow: 1, 41 | flexDirection: "column", 42 | justifyContent: "space-between", 43 | }, 44 | tableWidget: { 45 | overflowX: "auto", 46 | }, 47 | progressBar: { 48 | backgroundColor: theme.palette.warning.main, 49 | }, 50 | performanceLegendWrapper: { 51 | display: "flex", 52 | flexGrow: 1, 53 | alignItems: "center", 54 | marginBottom: theme.spacing(1), 55 | }, 56 | legendElement: { 57 | display: "flex", 58 | alignItems: "center", 59 | marginRight: theme.spacing(2), 60 | }, 61 | legendElementText: { 62 | marginLeft: theme.spacing(1), 63 | }, 64 | serverOverviewElement: { 65 | display: "flex", 66 | alignItems: "center", 67 | maxWidth: "100%", 68 | }, 69 | serverOverviewElementText: { 70 | minWidth: 145, 71 | paddingRight: theme.spacing(2), 72 | }, 73 | serverOverviewElementChartWrapper: { 74 | width: "100%", 75 | }, 76 | mainChartBody: { 77 | overflowX: "auto", 78 | }, 79 | mainChartHeader: { 80 | width: "100%", 81 | display: "flex", 82 | alignItems: "center", 83 | justifyContent: "space-between", 84 | [theme.breakpoints.only("xs")]: { 85 | flexWrap: "wrap", 86 | }, 87 | }, 88 | mainChartHeaderLabels: { 89 | display: "flex", 90 | alignItems: "center", 91 | [theme.breakpoints.only("xs")]: { 92 | order: 3, 93 | width: "100%", 94 | justifyContent: "center", 95 | marginTop: theme.spacing(3), 96 | marginBottom: theme.spacing(2), 97 | }, 98 | }, 99 | mainChartHeaderLabel: { 100 | display: "flex", 101 | alignItems: "center", 102 | marginLeft: theme.spacing(3), 103 | }, 104 | mainChartSelectRoot: { 105 | borderColor: theme.palette.text.hint + "80 !important", 106 | }, 107 | mainChartSelect: { 108 | padding: 10, 109 | paddingRight: 25, 110 | }, 111 | mainChartLegentElement: { 112 | fontSize: "18px !important", 113 | marginLeft: theme.spacing(1), 114 | }, 115 | })); 116 | -------------------------------------------------------------------------------- /src/pages/error/Error.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Grid, Paper, Typography, Button } from "@material-ui/core"; 3 | import { Link } from "react-router-dom"; 4 | import classnames from "classnames"; 5 | 6 | // styles 7 | import useStyles from "./styles"; 8 | 9 | // logo 10 | import logo from "./logo.svg"; 11 | 12 | export default function Error() { 13 | var classes = useStyles(); 14 | 15 | return ( 16 | 17 |
18 | logo 19 | 20 | Material Admin 21 | 22 |
23 | 24 | 29 | 404 30 | 31 | 32 | Oops. Looks like the page you're looking for no longer exists 33 | 34 | 40 | But we're here to bring you back to safety 41 | 42 | 52 | 53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/pages/error/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo_white 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/pages/error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Error", 3 | "version": "0.0.0", 4 | "main": "Error.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/error/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | container: { 5 | height: "100vh", 6 | width: "100vw", 7 | display: "flex", 8 | flexDirection: "column", 9 | justifyContent: "center", 10 | alignItems: "center", 11 | backgroundColor: theme.palette.primary.main, 12 | position: "absolute", 13 | top: 0, 14 | left: 0, 15 | }, 16 | logotype: { 17 | display: "flex", 18 | alignItems: "center", 19 | marginBottom: theme.spacing(12), 20 | [theme.breakpoints.down("sm")]: { 21 | display: "none", 22 | }, 23 | }, 24 | logotypeText: { 25 | fontWeight: 500, 26 | color: "white", 27 | marginLeft: theme.spacing(2), 28 | }, 29 | logotypeIcon: { 30 | width: 70, 31 | marginRight: theme.spacing(2), 32 | }, 33 | paperRoot: { 34 | boxShadow: theme.customShadows.widgetDark, 35 | display: "flex", 36 | flexDirection: "column", 37 | alignItems: "center", 38 | paddingTop: theme.spacing(8), 39 | paddingBottom: theme.spacing(8), 40 | paddingLeft: theme.spacing(6), 41 | paddingRight: theme.spacing(6), 42 | maxWidth: 404, 43 | }, 44 | textRow: { 45 | marginBottom: theme.spacing(10), 46 | textAlign: "center", 47 | }, 48 | errorCode: { 49 | fontSize: 148, 50 | fontWeight: 600, 51 | }, 52 | safetyText: { 53 | fontWeight: 300, 54 | color: theme.palette.text.hint, 55 | }, 56 | backButton: { 57 | boxShadow: theme.customShadows.widget, 58 | textTransform: "none", 59 | fontSize: 22, 60 | }, 61 | })); 62 | -------------------------------------------------------------------------------- /src/pages/icons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Icons", 3 | "version": "0.0.0", 4 | "main": "Icons.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/icons/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | titleBold: { 5 | fontWeight: 600, 6 | }, 7 | iconsBar: { 8 | marginBottom: theme.spacing(4), 9 | borderBottom: "1px solid", 10 | borderBottomColor: theme.palette.text.hint + "80", 11 | }, 12 | tab: { 13 | color: theme.palette.primary.light + "CC", 14 | }, 15 | materailIcon: { 16 | display: "flex", 17 | paddingLeft: `${theme.spacing(4)}px !important`, 18 | paddingRight: `${theme.spacing(4)}px !important`, 19 | color: theme.palette.text.secondary, 20 | fontSize: 24, 21 | overflowX: "hidden", 22 | }, 23 | materialIconText: { 24 | marginLeft: theme.spacing(2), 25 | fontSize: 14, 26 | }, 27 | iconsContainer: { 28 | boxShadow: theme.customShadows.widget, 29 | overflow: "hidden", 30 | paddingBottom: theme.spacing(2), 31 | }, 32 | })); 33 | -------------------------------------------------------------------------------- /src/pages/login/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | Grid, 4 | CircularProgress, 5 | Typography, 6 | Button, 7 | Tabs, 8 | Tab, 9 | TextField, 10 | Fade, 11 | } from "@material-ui/core"; 12 | import { withRouter } from "react-router-dom"; 13 | import classnames from "classnames"; 14 | 15 | // styles 16 | import useStyles from "./styles"; 17 | 18 | // logo 19 | import logo from "./logo.svg"; 20 | import google from "../../images/google.svg"; 21 | 22 | // context 23 | import { useUserDispatch, loginUser } from "../../context/UserContext"; 24 | 25 | function Login(props) { 26 | var classes = useStyles(); 27 | 28 | // global 29 | var userDispatch = useUserDispatch(); 30 | 31 | // local 32 | var [isLoading, setIsLoading] = useState(false); 33 | var [error, setError] = useState(null); 34 | var [activeTabId, setActiveTabId] = useState(0); 35 | var [nameValue, setNameValue] = useState(""); 36 | var [loginValue, setLoginValue] = useState(""); 37 | var [passwordValue, setPasswordValue] = useState(""); 38 | 39 | return ( 40 | 41 |
42 | logo 43 | Material Admin 44 |
45 |
46 |
47 | setActiveTabId(id)} 50 | indicatorColor="primary" 51 | textColor="primary" 52 | centered 53 | > 54 | 55 | 56 | 57 | {activeTabId === 0 && ( 58 | 59 | 60 | Good Morning, User 61 | 62 | 66 |
67 |
68 | or 69 |
70 |
71 | 72 | 73 | Something is wrong with your login or password :( 74 | 75 | 76 | setLoginValue(e.target.value)} 86 | margin="normal" 87 | placeholder="Email Adress" 88 | type="email" 89 | fullWidth 90 | /> 91 | setPasswordValue(e.target.value)} 101 | margin="normal" 102 | placeholder="Password" 103 | type="password" 104 | fullWidth 105 | /> 106 |
107 | {isLoading ? ( 108 | 109 | ) : ( 110 | 130 | )} 131 | 138 |
139 | 140 | )} 141 | {activeTabId === 1 && ( 142 | 143 | 144 | Welcome! 145 | 146 | 147 | Create your account 148 | 149 | 150 | 151 | Something is wrong with your login or password :( 152 | 153 | 154 | setNameValue(e.target.value)} 164 | margin="normal" 165 | placeholder="Full Name" 166 | type="text" 167 | fullWidth 168 | /> 169 | setLoginValue(e.target.value)} 179 | margin="normal" 180 | placeholder="Email Adress" 181 | type="email" 182 | fullWidth 183 | /> 184 | setPasswordValue(e.target.value)} 194 | margin="normal" 195 | placeholder="Password" 196 | type="password" 197 | fullWidth 198 | /> 199 |
200 | {isLoading ? ( 201 | 202 | ) : ( 203 | 227 | )} 228 |
229 |
230 |
231 | or 232 |
233 |
234 | 244 | 245 | )} 246 |
247 | 248 | © 2014-2019 Flatlogic, LLC. All rights reserved. 249 | 250 |
251 | 252 | ); 253 | } 254 | 255 | export default withRouter(Login); 256 | -------------------------------------------------------------------------------- /src/pages/login/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo_white 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/pages/login/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Login", 3 | "version": "0.0.0", 4 | "main": "Login.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/login/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | container: { 5 | height: "100vh", 6 | width: "100vw", 7 | display: "flex", 8 | justifyContent: "center", 9 | alignItems: "center", 10 | position: "absolute", 11 | top: 0, 12 | left: 0, 13 | }, 14 | logotypeContainer: { 15 | backgroundColor: theme.palette.primary.main, 16 | width: "60%", 17 | height: "100%", 18 | display: "flex", 19 | flexDirection: "column", 20 | justifyContent: "center", 21 | alignItems: "center", 22 | [theme.breakpoints.down("md")]: { 23 | width: "50%", 24 | }, 25 | [theme.breakpoints.down("md")]: { 26 | display: "none", 27 | }, 28 | }, 29 | logotypeImage: { 30 | width: 165, 31 | marginBottom: theme.spacing(4), 32 | }, 33 | logotypeText: { 34 | color: "white", 35 | fontWeight: 500, 36 | fontSize: 84, 37 | [theme.breakpoints.down("md")]: { 38 | fontSize: 48, 39 | }, 40 | }, 41 | formContainer: { 42 | width: "40%", 43 | height: "100%", 44 | display: "flex", 45 | flexDirection: "column", 46 | justifyContent: "center", 47 | alignItems: "center", 48 | [theme.breakpoints.down("md")]: { 49 | width: "50%", 50 | }, 51 | }, 52 | form: { 53 | width: 320, 54 | }, 55 | tab: { 56 | fontWeight: 400, 57 | fontSize: 18, 58 | }, 59 | greeting: { 60 | fontWeight: 500, 61 | textAlign: "center", 62 | marginTop: theme.spacing(4), 63 | }, 64 | subGreeting: { 65 | fontWeight: 500, 66 | textAlign: "center", 67 | marginTop: theme.spacing(2), 68 | }, 69 | googleButton: { 70 | marginTop: theme.spacing(6), 71 | boxShadow: theme.customShadows.widget, 72 | backgroundColor: "white", 73 | width: "100%", 74 | textTransform: "none", 75 | }, 76 | googleButtonCreating: { 77 | marginTop: 0, 78 | }, 79 | googleIcon: { 80 | width: 30, 81 | marginRight: theme.spacing(2), 82 | }, 83 | creatingButtonContainer: { 84 | marginTop: theme.spacing(2.5), 85 | height: 46, 86 | display: "flex", 87 | justifyContent: "center", 88 | alignItems: "center", 89 | }, 90 | createAccountButton: { 91 | height: 46, 92 | textTransform: "none", 93 | }, 94 | formDividerContainer: { 95 | marginTop: theme.spacing(4), 96 | marginBottom: theme.spacing(4), 97 | display: "flex", 98 | alignItems: "center", 99 | }, 100 | formDividerWord: { 101 | paddingLeft: theme.spacing(2), 102 | paddingRight: theme.spacing(2), 103 | }, 104 | formDivider: { 105 | flexGrow: 1, 106 | height: 1, 107 | backgroundColor: theme.palette.text.hint + "40", 108 | }, 109 | errorMessage: { 110 | textAlign: "center", 111 | }, 112 | textFieldUnderline: { 113 | "&:before": { 114 | borderBottomColor: theme.palette.primary.light, 115 | }, 116 | "&:after": { 117 | borderBottomColor: theme.palette.primary.main, 118 | }, 119 | "&:hover:before": { 120 | borderBottomColor: `${theme.palette.primary.light} !important`, 121 | }, 122 | }, 123 | textField: { 124 | borderBottomColor: theme.palette.background.light, 125 | }, 126 | formButtons: { 127 | width: "100%", 128 | marginTop: theme.spacing(4), 129 | display: "flex", 130 | justifyContent: "space-between", 131 | alignItems: "center", 132 | }, 133 | forgetButton: { 134 | textTransform: "none", 135 | fontWeight: 400, 136 | }, 137 | loginLoader: { 138 | marginLeft: theme.spacing(4), 139 | }, 140 | copyright: { 141 | marginTop: theme.spacing(4), 142 | whiteSpace: "nowrap", 143 | [theme.breakpoints.up("md")]: { 144 | position: "absolute", 145 | bottom: theme.spacing(2), 146 | }, 147 | }, 148 | })); 149 | -------------------------------------------------------------------------------- /src/pages/maps/Maps.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | withGoogleMap, 4 | withScriptjs, 5 | GoogleMap, 6 | Marker, 7 | } from "react-google-maps"; 8 | 9 | // styles 10 | import useStyles from "./styles"; 11 | 12 | const BasicMap = withScriptjs( 13 | withGoogleMap(() => ( 14 | 21 | 22 | 23 | )), 24 | ); 25 | 26 | export default function Maps() { 27 | var classes = useStyles(); 28 | 29 | return ( 30 |
31 | } 34 | containerElement={
} 35 | mapElement={
} 36 | /> 37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/pages/maps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Maps", 3 | "version": "0.0.0", 4 | "main": "Maps.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/maps/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | mapContainer: { 5 | height: "100%", 6 | margin: -theme.spacing(1) * 3, 7 | }, 8 | })); -------------------------------------------------------------------------------- /src/pages/notifications/Notifications.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Grid } from "@material-ui/core"; 3 | import { Close as CloseIcon } from "@material-ui/icons"; 4 | import { ToastContainer, toast } from "react-toastify"; 5 | import SyntaxHighlighter from "react-syntax-highlighter"; 6 | import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs"; 7 | import classnames from "classnames"; 8 | 9 | // styles 10 | import "react-toastify/dist/ReactToastify.css"; 11 | import useStyles from "./styles"; 12 | 13 | // components 14 | import Widget from "../../components/Widget/Widget"; 15 | import PageTitle from "../../components/PageTitle/PageTitle"; 16 | import Notification from "../../components/Notification"; 17 | import { Typography, Button } from "../../components/Wrappers/Wrappers"; 18 | 19 | const positions = [ 20 | toast.POSITION.TOP_LEFT, 21 | toast.POSITION.TOP_CENTER, 22 | toast.POSITION.TOP_RIGHT, 23 | toast.POSITION.BOTTOM_LEFT, 24 | toast.POSITION.BOTTOM_CENTER, 25 | toast.POSITION.BOTTOM_RIGHT, 26 | ]; 27 | 28 | export default function NotificationsPage(props) { 29 | var classes = useStyles(); 30 | 31 | // local 32 | var [notificationsPosition, setNotificationPosition] = useState(2); 33 | var [errorToastId, setErrorToastId] = useState(null); 34 | 35 | return ( 36 | <> 37 | 38 | 39 | 43 | } 44 | closeOnClick={false} 45 | progressClassName={classes.notificationProgress} 46 | /> 47 | 48 | 49 | 50 | There are few position options available for notifications. You 51 | can click any of them to change notifications position: 52 | 53 |
54 |
55 |
74 | 75 | Click any position 76 | 77 |
78 |
97 |
98 |
99 |
100 | 101 | 102 | 103 | Different types of notifications for lost of use cases. Custom 104 | classes are also supported. 105 | 106 |
107 | 115 | 123 | 131 |
132 |
133 |
134 | 135 | 136 | 137 | Notifications are created with the help of{" "} 138 | 139 | react-toastify 140 | 141 | 142 |
143 | {` 148 | // import needed components, functions and styles 149 | import { ToastContainer, toast } from 'react-toastify'; 150 | import 'react-toastify/dist/ReactToastify.css'; 151 | 152 | const Page = () => { 153 |
154 | 155 | 158 |
159 | }; 160 | `}
161 | 162 | For more API information refer to the library documentation 163 | 164 |
165 |
166 |
167 | 168 | 169 | 177 | 185 | 193 | 201 | 209 | 217 | 218 | 219 | 220 | 221 | 227 | 233 | 239 | 245 | 251 | 257 | 258 | 259 | 260 | 261 | 268 | 275 | 282 | 289 | 296 | 303 | 304 | 305 |
306 | 307 | ); 308 | 309 | // ############################################################# 310 | function sendNotification(componentProps, options) { 311 | return toast( 312 | , 316 | options, 317 | ); 318 | } 319 | 320 | function retryErrorNotification() { 321 | var componentProps = { 322 | type: "message", 323 | message: "Message was sent successfully!", 324 | variant: "contained", 325 | color: "success", 326 | }; 327 | toast.update(errorToastId, { 328 | render: , 329 | type: "success", 330 | }); 331 | setErrorToastId(null); 332 | } 333 | 334 | function handleNotificationCall(notificationType) { 335 | var componentProps; 336 | 337 | if (errorToastId && notificationType === "error") return; 338 | 339 | switch (notificationType) { 340 | case "info": 341 | componentProps = { 342 | type: "feedback", 343 | message: "New user feedback received", 344 | variant: "contained", 345 | color: "primary", 346 | }; 347 | break; 348 | case "error": 349 | componentProps = { 350 | type: "message", 351 | message: "Message was not sent!", 352 | variant: "contained", 353 | color: "secondary", 354 | extraButton: "Resend", 355 | extraButtonClick: retryErrorNotification, 356 | }; 357 | break; 358 | default: 359 | componentProps = { 360 | type: "shipped", 361 | message: "The item was shipped", 362 | variant: "contained", 363 | color: "success", 364 | }; 365 | } 366 | 367 | var toastId = sendNotification(componentProps, { 368 | type: notificationType, 369 | position: positions[notificationsPosition], 370 | progressClassName: classes.progress, 371 | onClose: notificationType === "error" && (() => setErrorToastId(null)), 372 | className: classes.notification, 373 | }); 374 | 375 | if (notificationType === "error") setErrorToastId(toastId); 376 | } 377 | 378 | function changeNotificationPosition(positionId) { 379 | setNotificationPosition(positionId); 380 | } 381 | } 382 | 383 | // ############################################################# 384 | function CloseButton({ closeToast, className }) { 385 | return ; 386 | } 387 | -------------------------------------------------------------------------------- /src/pages/notifications/NotificationsContainer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/core"; 3 | import { compose, withState, withHandlers } from "recompose"; 4 | import { toast } from "react-toastify"; 5 | 6 | import Notification from "../../components/Notification"; 7 | import NotificationsView from "./NotificationsView"; 8 | 9 | const positions = [ 10 | toast.POSITION.TOP_LEFT, 11 | toast.POSITION.TOP_CENTER, 12 | toast.POSITION.TOP_RIGHT, 13 | toast.POSITION.BOTTOM_LEFT, 14 | toast.POSITION.BOTTOM_CENTER, 15 | toast.POSITION.BOTTOM_RIGHT 16 | ]; 17 | 18 | export default compose( 19 | withStyles(theme => ({ 20 | /*progress: { 21 | visibility: "hidden" 22 | }, 23 | notification: { 24 | display: "flex", 25 | alignItems: "center", 26 | background: "transparent", 27 | boxShadow: "none", 28 | overflow: "visible" 29 | }, 30 | notificationComponent: { 31 | paddingRight: theme.spacing.unit * 4 32 | }*/ 33 | })), 34 | withState("notificationsPosition", "setNotificationPosition", 2), 35 | withState("errorToastId", "setErrorToastId", null), 36 | withHandlers({ 37 | sendNotification: props => (componentProps, options) => { 38 | return toast( 39 | , 43 | options 44 | ); 45 | } 46 | }), 47 | withHandlers({ 48 | retryErrorNotification: props => () => { 49 | const componentProps = { 50 | type: "message", 51 | message: "Message was sent successfully!", 52 | variant: "contained", 53 | color: "success", 54 | }; 55 | 56 | toast.update(props.errorToastId, { 57 | render: , 58 | type: "success" 59 | }); 60 | props.setErrorToastId(null); 61 | } 62 | }), 63 | withHandlers({ 64 | handleNotificationCall: props => notificationType => { 65 | let componentProps; 66 | 67 | if (props.errorToastId && notificationType === "error") return; 68 | 69 | switch (notificationType) { 70 | case "info": 71 | componentProps = { 72 | type: "feedback", 73 | message: "New user feedback received", 74 | variant: "contained", 75 | color: "primary" 76 | }; 77 | break; 78 | case "error": 79 | componentProps = { 80 | type: "message", 81 | message: "Message was not sent!", 82 | variant: "contained", 83 | color: "secondary", 84 | extraButton: "Resend", 85 | extraButtonClick: props.retryErrorNotification 86 | }; 87 | break; 88 | default: 89 | componentProps = { 90 | type: "shipped", 91 | message: "The item was shipped", 92 | variant: "contained", 93 | color: "success" 94 | }; 95 | } 96 | 97 | const toastId = props.sendNotification(componentProps, { 98 | type: notificationType, 99 | position: positions[props.notificationsPosition], 100 | progressClassName: props.classes.progress, 101 | onClose: 102 | notificationType === "error" && (() => props.setErrorToastId(null)), 103 | className: props.classes.notification 104 | }); 105 | 106 | if (notificationType === "error") props.setErrorToastId(toastId); 107 | }, 108 | changeNotificationPosition: props => positionId => { 109 | props.setNotificationPosition(positionId); 110 | } 111 | }) 112 | )(NotificationsView); 113 | -------------------------------------------------------------------------------- /src/pages/notifications/NotificationsView.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Grid, withStyles } from '@material-ui/core'; 3 | import { Close as CloseIcon } from '@material-ui/icons'; 4 | import classnames from 'classnames'; 5 | import { ToastContainer } from 'react-toastify'; 6 | import SyntaxHighlighter from 'react-syntax-highlighter'; 7 | import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs'; 8 | import tinycolor from 'tinycolor2'; 9 | import 'react-toastify/dist/ReactToastify.css'; 10 | 11 | import Widget from '../../components/Widget'; 12 | import PageTitle from '../../components/PageTitle'; 13 | import NotificationCustomComponent from '../../components/Notification'; 14 | import { Typography, Button } from '../../components/Wrappers'; 15 | 16 | const CloseButton = ({ closeToast, className }) => ( 17 | 21 | ); 22 | 23 | const NotificationsPage = ({ classes, ...props}) => ( 24 | 25 | 26 | 27 | } closeOnClick={false} progressClassName={classes.notificationProgress} /> 28 | 29 | 30 | There are few position options available for notifications. You can click any of them to change notifications position: 31 |
32 |
33 |
37 | Click any position 38 |
39 |
43 |
44 |
45 |
46 | 47 | 48 | Different types of notifications for lost of use cases. Custom classes are also supported. 49 |
50 | 51 | 52 | 53 |
54 |
55 |
56 | 57 | 58 | Notifications are created with the help of react-toastify 59 |
60 | {` 61 | // import needed components, functions and styles 62 | import { ToastContainer, toast } from 'react-toastify'; 63 | import 'react-toastify/dist/ReactToastify.css'; 64 | 65 | const Page = () => { 66 |
67 | 68 | 71 |
72 | }; 73 | `}
74 | For more API information refer to the library documentation 75 |
76 |
77 |
78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
109 |
110 | ); 111 | 112 | const styles = (theme) => ({ 113 | layoutContainer: { 114 | height: 200, 115 | display: 'flex', 116 | flexDirection: 'column', 117 | justifyContent: 'space-between', 118 | alignItems: 'center', 119 | marginTop: theme.spacing.unit * 2, 120 | border: '1px dashed', 121 | borderColor: theme.palette.primary.main, 122 | position: 'relative', 123 | }, 124 | layoutText: { 125 | color: tinycolor(theme.palette.background.light).darken().toHexString(), 126 | }, 127 | layoutButtonsRow: { 128 | width: '100%', 129 | display: 'flex', 130 | justifyContent: 'space-between', 131 | }, 132 | layoutButton: { 133 | backgroundColor: theme.palette.background.light, 134 | width: 125, 135 | height: 50, 136 | outline: 'none', 137 | border: 'none', 138 | }, 139 | layoutButtonActive: { 140 | backgroundColor: tinycolor(theme.palette.background.light).darken().toHexString(), 141 | }, 142 | buttonsContainer: { 143 | display: 'flex', 144 | flexDirection: 'column', 145 | alignItems: 'flex-start', 146 | marginTop: theme.spacing.unit * 2, 147 | }, 148 | notificationCallButton: { 149 | color: 'white', 150 | marginBottom: theme.spacing.unit, 151 | textTransform: 'none', 152 | }, 153 | codeContainer: { 154 | display: 'flex', 155 | flexDirection: 'column', 156 | marginTop: theme.spacing.unit * 2, 157 | }, 158 | codeComponent: { 159 | flexGrow: 1, 160 | }, 161 | notificationItem: { 162 | marginTop: theme.spacing.unit * 2, 163 | }, 164 | notificationCloseButton: { 165 | position: 'absolute', 166 | right: theme.spacing.unit * 2, 167 | }, 168 | toastsContainer: { 169 | width: 400, 170 | marginTop: theme.spacing.unit * 6, 171 | right: 0, 172 | } 173 | }); 174 | 175 | export default withStyles(styles, { withTheme: true})(NotificationsPage); 176 | -------------------------------------------------------------------------------- /src/pages/notifications/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Notifications", 3 | "version": "0.0.0", 4 | "main": "Notifications.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/notifications/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | import tinycolor from "tinycolor2"; 3 | 4 | export default makeStyles(theme => ({ 5 | layoutContainer: { 6 | height: 200, 7 | display: "flex", 8 | flexDirection: "column", 9 | justifyContent: "space-between", 10 | alignItems: "center", 11 | marginTop: theme.spacing(2), 12 | border: "1px dashed", 13 | borderColor: theme.palette.primary.main, 14 | position: "relative", 15 | }, 16 | layoutText: { 17 | color: tinycolor(theme.palette.background.light) 18 | .darken() 19 | .toHexString(), 20 | }, 21 | layoutButtonsRow: { 22 | width: "100%", 23 | display: "flex", 24 | justifyContent: "space-between", 25 | }, 26 | layoutButton: { 27 | backgroundColor: theme.palette.background.light, 28 | width: 125, 29 | height: 50, 30 | outline: "none", 31 | border: "none", 32 | }, 33 | layoutButtonActive: { 34 | backgroundColor: tinycolor(theme.palette.background.light) 35 | .darken() 36 | .toHexString(), 37 | }, 38 | buttonsContainer: { 39 | display: "flex", 40 | flexDirection: "column", 41 | alignItems: "flex-start", 42 | marginTop: theme.spacing(2), 43 | }, 44 | notificationCallButton: { 45 | color: "white", 46 | marginBottom: theme.spacing(1), 47 | textTransform: "none", 48 | }, 49 | codeContainer: { 50 | display: "flex", 51 | flexDirection: "column", 52 | marginTop: theme.spacing(2), 53 | }, 54 | codeComponent: { 55 | flexGrow: 1, 56 | }, 57 | notificationItem: { 58 | marginTop: theme.spacing(2), 59 | }, 60 | notificationCloseButton: { 61 | position: "absolute", 62 | right: theme.spacing(2), 63 | }, 64 | toastsContainer: { 65 | width: 400, 66 | marginTop: theme.spacing(6), 67 | right: 0, 68 | }, 69 | progress: { 70 | visibility: "hidden", 71 | }, 72 | notification: { 73 | display: "flex", 74 | alignItems: "center", 75 | background: "transparent", 76 | boxShadow: "none", 77 | overflow: "visible", 78 | }, 79 | notificationComponent: { 80 | paddingRight: theme.spacing(4), 81 | }, 82 | })); 83 | -------------------------------------------------------------------------------- /src/pages/tables/Tables.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Grid } from "@material-ui/core"; 3 | import MUIDataTable from "mui-datatables"; 4 | 5 | // components 6 | import PageTitle from "../../components/PageTitle"; 7 | import Widget from "../../components/Widget"; 8 | import Table from "../dashboard/components/Table/Table"; 9 | 10 | // data 11 | import mock from "../dashboard/mock"; 12 | 13 | const datatableData = [ 14 | ["Joe James", "Example Inc.", "Yonkers", "NY"], 15 | ["John Walsh", "Example Inc.", "Hartford", "CT"], 16 | ["Bob Herm", "Example Inc.", "Tampa", "FL"], 17 | ["James Houston", "Example Inc.", "Dallas", "TX"], 18 | ["Prabhakar Linwood", "Example Inc.", "Hartford", "CT"], 19 | ["Kaui Ignace", "Example Inc.", "Yonkers", "NY"], 20 | ["Esperanza Susanne", "Example Inc.", "Hartford", "CT"], 21 | ["Christian Birgitte", "Example Inc.", "Tampa", "FL"], 22 | ["Meral Elias", "Example Inc.", "Hartford", "CT"], 23 | ["Deep Pau", "Example Inc.", "Yonkers", "NY"], 24 | ["Sebastiana Hani", "Example Inc.", "Dallas", "TX"], 25 | ["Marciano Oihana", "Example Inc.", "Yonkers", "NY"], 26 | ["Brigid Ankur", "Example Inc.", "Dallas", "TX"], 27 | ["Anna Siranush", "Example Inc.", "Yonkers", "NY"], 28 | ["Avram Sylva", "Example Inc.", "Hartford", "CT"], 29 | ["Serafima Babatunde", "Example Inc.", "Tampa", "FL"], 30 | ["Gaston Festus", "Example Inc.", "Tampa", "FL"], 31 | ]; 32 | 33 | export default function Tables() { 34 | return ( 35 | <> 36 | 37 | 38 | 39 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/pages/tables/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tables", 3 | "version": "0.0.0", 4 | "main": "Tables.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/typography/Typography.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Grid } from "@material-ui/core"; 3 | 4 | // styles 5 | import useStyles from "./styles"; 6 | 7 | // components 8 | import PageTitle from "../../components/PageTitle"; 9 | import Widget from "../../components/Widget"; 10 | import { Typography } from "../../components/Wrappers"; 11 | 12 | export default function TypographyPage() { 13 | var classes = useStyles(); 14 | 15 | return ( 16 | <> 17 | 18 | 19 | 20 | 21 |
22 | 23 | h1. Heading 24 | 25 | 26 | h2. Heading 27 | 28 | 29 | h3. Heading 30 | 31 | 32 | h4. Heading 33 | 34 | 35 | h5. Heading 36 | 37 | h6. Heading 38 |
39 |
40 |
41 | 42 | 43 |
44 | 45 | h1. Heading 46 | 47 | 48 | h2. Heading 49 | 50 | 55 | h3. Heading 56 | 57 | 58 | h4. Heading 59 | 60 | 66 | h5. Heading 67 | 68 | 69 | h6. Heading 70 | 71 |
72 |
73 |
74 | 75 | 76 |
77 | Basic text 78 | 79 | Basic light text 80 | 81 | 82 | Basic medium text 83 | 84 | 85 | Basic bold text 86 | 87 | 88 | BASIC UPPERCASE TEXT 89 | 90 | 91 | basic lowercase text 92 | 93 | 94 | Basic Capitalized Text 95 | 96 | 97 | Basic Cursive Text 98 | 99 |
100 |
101 |
102 | 103 | 104 |
105 | 106 | Heading Typography SM Font Size 107 | 108 | 109 | Heading Typography Regular Font Size 110 | 111 | 112 | Heading Typography MD Font Size 113 | 114 | 115 | Heading Typography XL Font Size 116 | 117 | 118 | Heading Typography XXL Font Size 119 | 120 |
121 |
122 |
123 |
124 | 125 | ); 126 | } 127 | -------------------------------------------------------------------------------- /src/pages/typography/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Typography", 3 | "version": "0.0.0", 4 | "main": "Typography.js", 5 | "private": true 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/typography/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from "@material-ui/styles"; 2 | 3 | export default makeStyles(theme => ({ 4 | dashedBorder: { 5 | border: "1px dashed", 6 | borderColor: theme.palette.primary.main, 7 | padding: theme.spacing(2), 8 | paddingTop: theme.spacing(4), 9 | paddingBottom: theme.spacing(4), 10 | marginTop: theme.spacing(1), 11 | }, 12 | text: { 13 | marginBottom: theme.spacing(2), 14 | }, 15 | })); 16 | -------------------------------------------------------------------------------- /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 http://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 http://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 http://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/themes/default.js: -------------------------------------------------------------------------------- 1 | import tinycolor from "tinycolor2"; 2 | 3 | const primary = "#536DFE"; 4 | const secondary = "#FF5C93"; 5 | const warning = "#FFC260"; 6 | const success = "#3CD4A0"; 7 | const info = "#9013FE"; 8 | 9 | const lightenRate = 7.5; 10 | const darkenRate = 15; 11 | 12 | export default { 13 | palette: { 14 | primary: { 15 | main: primary, 16 | light: tinycolor(primary) 17 | .lighten(lightenRate) 18 | .toHexString(), 19 | dark: tinycolor(primary) 20 | .darken(darkenRate) 21 | .toHexString(), 22 | }, 23 | secondary: { 24 | main: secondary, 25 | light: tinycolor(secondary) 26 | .lighten(lightenRate) 27 | .toHexString(), 28 | dark: tinycolor(secondary) 29 | .darken(darkenRate) 30 | .toHexString(), 31 | contrastText: "#FFFFFF", 32 | }, 33 | warning: { 34 | main: warning, 35 | light: tinycolor(warning) 36 | .lighten(lightenRate) 37 | .toHexString(), 38 | dark: tinycolor(warning) 39 | .darken(darkenRate) 40 | .toHexString(), 41 | }, 42 | success: { 43 | main: success, 44 | light: tinycolor(success) 45 | .lighten(lightenRate) 46 | .toHexString(), 47 | dark: tinycolor(success) 48 | .darken(darkenRate) 49 | .toHexString(), 50 | }, 51 | info: { 52 | main: info, 53 | light: tinycolor(info) 54 | .lighten(lightenRate) 55 | .toHexString(), 56 | dark: tinycolor(info) 57 | .darken(darkenRate) 58 | .toHexString(), 59 | }, 60 | text: { 61 | primary: "#4A4A4A", 62 | secondary: "#6E6E6E", 63 | hint: "#B9B9B9", 64 | }, 65 | background: { 66 | default: "#F6F7FF", 67 | light: "#F3F5FF", 68 | }, 69 | }, 70 | customShadows: { 71 | widget: 72 | "0px 3px 11px 0px #E8EAFC, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A", 73 | widgetDark: 74 | "0px 3px 18px 0px #4558A3B3, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A", 75 | widgetWide: 76 | "0px 12px 33px 0px #E8EAFC, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A", 77 | }, 78 | overrides: { 79 | MuiBackdrop: { 80 | root: { 81 | backgroundColor: "#4A4A4A1A", 82 | }, 83 | }, 84 | MuiMenu: { 85 | paper: { 86 | boxShadow: 87 | "0px 3px 11px 0px #E8EAFC, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A", 88 | }, 89 | }, 90 | MuiSelect: { 91 | icon: { 92 | color: "#B9B9B9", 93 | }, 94 | }, 95 | MuiListItem: { 96 | root: { 97 | "&$selected": { 98 | backgroundColor: "#F3F5FF !important", 99 | "&:focus": { 100 | backgroundColor: "#F3F5FF", 101 | }, 102 | }, 103 | }, 104 | button: { 105 | "&:hover, &:focus": { 106 | backgroundColor: "#F3F5FF", 107 | }, 108 | }, 109 | }, 110 | MuiTouchRipple: { 111 | child: { 112 | backgroundColor: "white", 113 | }, 114 | }, 115 | MuiTableRow: { 116 | root: { 117 | height: 56, 118 | }, 119 | }, 120 | MuiTableCell: { 121 | root: { 122 | borderBottom: "1px solid rgba(224, 224, 224, .5)", 123 | }, 124 | head: { 125 | fontSize: "0.95rem", 126 | }, 127 | body: { 128 | fontSize: "0.95rem", 129 | }, 130 | }, 131 | }, 132 | }; 133 | -------------------------------------------------------------------------------- /src/themes/index.js: -------------------------------------------------------------------------------- 1 | import defaultTheme from "./default"; 2 | 3 | import { createMuiTheme } from "@material-ui/core"; 4 | 5 | const overrides = { 6 | typography: { 7 | h1: { 8 | fontSize: "3rem", 9 | }, 10 | h2: { 11 | fontSize: "2rem", 12 | }, 13 | h3: { 14 | fontSize: "1.64rem", 15 | }, 16 | h4: { 17 | fontSize: "1.5rem", 18 | }, 19 | h5: { 20 | fontSize: "1.285rem", 21 | }, 22 | h6: { 23 | fontSize: "1.142rem", 24 | }, 25 | }, 26 | }; 27 | 28 | export default { 29 | default: createMuiTheme({ ...defaultTheme, ...overrides }), 30 | }; 31 | --------------------------------------------------------------------------------