cdn frontend
|
@ -0,0 +1,24 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.idea/
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"tabWidth": 2,
|
||||
"singleQuote": false,
|
||||
"semi": true,
|
||||
"trailingComma": "all"
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2019 Flatlogic LLC.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,106 @@
|
|||
# React Material Admin — Material-UI Dashboard Template
|
||||
|
||||
Built with [React](https://facebook.github.io/react/), [Material-UI](https://material-ui.com), [React Router](https://reacttraining.com/react-router/).
|
||||
**No jQuery and Bootstrap!**
|
||||
|
||||
**This version uses React 16.14.0, React Router v5, MaterialUI v4, built with React Hooks and React Context (No Redux)**
|
||||
|
||||
[View Demo](https://flatlogic.com/templates/react-material-admin/demo) | [Download](https://github.com/flatlogic/react-material-admin/archive/master.zip) | [More templates](https://flatlogic.com/templates) | [Support forum](https://flatlogic.com/forum)
|
||||
|
||||
[![image](https://user-images.githubusercontent.com/24964748/55800639-df780300-5adc-11e9-84b7-7c2437088516.png)](https://flatlogic.com/admin-dashboards/react-material-admin/demo)
|
||||
|
||||
## Full Version
|
||||
|
||||
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.
|
||||
|
||||
## Features
|
||||
|
||||
- React (**16.14.0**)
|
||||
- React Hooks
|
||||
- React Context
|
||||
- **No jQuery and Bootstrap!**
|
||||
- Mobile friendly layout (responsive)
|
||||
- Create-react-app under the hood
|
||||
- React Router v5
|
||||
- Material-UI v4
|
||||
- Modular Architecture
|
||||
- CSS-in-JS styles
|
||||
- Webpack build
|
||||
- Stylish, clean, responsive layout
|
||||
- Authentication
|
||||
|
||||
## Pages
|
||||
|
||||
We have implemented some basic pages, so you can see our template in action.
|
||||
|
||||
- Dashboard
|
||||
- Typography
|
||||
- Tables
|
||||
- Notifications
|
||||
- Charts
|
||||
- Icons
|
||||
- Maps
|
||||
- Login
|
||||
- Error
|
||||
|
||||
## Quick Start
|
||||
|
||||
#### 1. Get the latest version
|
||||
|
||||
You can start by cloning the latest version of React Dashboard on your
|
||||
local machine by running:
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/flatlogic/react-material-admin.git MyApp
|
||||
$ cd MyApp
|
||||
```
|
||||
|
||||
#### 2. Run `yarn install`
|
||||
|
||||
This will install both run-time project dependencies and developer tools listed
|
||||
in [package.json](package.json) file.
|
||||
|
||||
#### 3. Run `yarn start`
|
||||
|
||||
Runs the app in the development mode.
|
||||
|
||||
Open http://localhost:3000 to view it in the browser. Whenever you modify any of the source files inside the `/src` folder,
|
||||
the module bundler ([Webpack](http://webpack.github.io/)) will recompile the
|
||||
app on the fly and refresh all the connected browsers.
|
||||
|
||||
#### 4. Run `yarn build`
|
||||
|
||||
Builds the app for production to the build folder.
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.
|
||||
Your app is ready to be deployed!
|
||||
|
||||
## Support
|
||||
|
||||
For any additional information please go to our [**support forum**](https://flatlogic.com/forum) and raise your questions or feedback provide there. We highly appreciate your participation!
|
||||
|
||||
## How can I support developers?
|
||||
|
||||
- Star our GitHub repo :star:
|
||||
- [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).
|
||||
- Create pull requests, submit bugs, suggest new features or documentation updates :wrench:
|
||||
- Follow [@flatlogic on Twitter](https://twitter.com/flatlogic).
|
||||
- Subscribe to Flatlogic newsletter at [flatlogic.com](https://flatlogic.com/)
|
||||
- Like our page on [Facebook](https://www.facebook.com/flatlogic/) :thumbsup:
|
||||
|
||||
## More from Flatlogic
|
||||
|
||||
- [React Native Starter](https://github.com/flatlogic/react-native-starter) - 🚀 A powerful react native starter template that bootstraps development of your mobile application
|
||||
- [Sing App](https://github.com/flatlogic/sing-app) - 💥 Free and open-source admin dashboard template built with Bootstrap 4
|
||||
- [Awesome Bootstrap Checkboxes & Radios](https://github.com/flatlogic/awesome-bootstrap-checkbox) - ✅ Pure css way to make inputs look prettier
|
||||
- [React Dashboard](https://github.com/flatlogic/react-dashboard) - 🔥 React Dashboard - isomorphic admin dashboard template with GraphQL
|
||||
- [Light Blue Dashboard](https://github.com/flatlogic/light-blue-dashboard) - 💦 Free and open-source admin dashboard template built with Bootstrap
|
||||
|
||||
## Premium themes
|
||||
|
||||
Looking for premium themes and templates? Check out more [admin dashboard templates at flatlogic.com](https://flatlogic.com/admin-dashboards).
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/flatlogic/react-material-dashboard/blob/master/LICENSE.txt).
|
|
@ -0,0 +1,67 @@
|
|||
# Changelog
|
||||
|
||||
# [1.3.2]
|
||||
|
||||
### Updated
|
||||
- Packages updated
|
||||
- Edited README.md
|
||||
|
||||
# [1.3.1]
|
||||
|
||||
### Updated
|
||||
- Added link to flatlogic on login and register pages
|
||||
|
||||
# [1.3.0]
|
||||
|
||||
### Updated
|
||||
- Update packages
|
||||
|
||||
### Fixed
|
||||
- Header button on md adjusting
|
||||
- Dashboard page make more responsive
|
||||
- Notification page fix
|
||||
- Tables page fix TableBody paddings
|
||||
|
||||
# [1.2.3]
|
||||
|
||||
### Updated
|
||||
- Fixed security vulnerabilities in dependencies
|
||||
|
||||
# [1.2.2]
|
||||
|
||||
### Updated
|
||||
- Packages updated
|
||||
|
||||
# [1.2.1]
|
||||
|
||||
### Updated
|
||||
- Packages updated
|
||||
|
||||
### Fixed
|
||||
- Sign up name type of input
|
||||
- Dot component size prop
|
||||
- Performance errors
|
||||
|
||||
# [1.2.0]
|
||||
|
||||
### Updated
|
||||
- Packages update
|
||||
- Link to Full version
|
||||
|
||||
### Fixed
|
||||
- User login state improvements
|
||||
|
||||
## [1.1.0]
|
||||
|
||||
### New Feactures
|
||||
|
||||
- React v16.8.6
|
||||
- React Router v5
|
||||
- new React Hooks
|
||||
- Material UI v4.3
|
||||
|
||||
Bug fixes
|
||||
|
||||
## [1.0.0]
|
||||
|
||||
Initial version of the project
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"files": {
|
||||
"main.js": "./static/js/main.fc3e922e.chunk.js",
|
||||
"main.js.map": "./static/js/main.fc3e922e.chunk.js.map",
|
||||
"runtime-main.js": "./static/js/runtime-main.4862f1b0.js",
|
||||
"runtime-main.js.map": "./static/js/runtime-main.4862f1b0.js.map",
|
||||
"static/css/2.637eb612.chunk.css": "./static/css/2.637eb612.chunk.css",
|
||||
"static/js/2.af6eef2b.chunk.js": "./static/js/2.af6eef2b.chunk.js",
|
||||
"static/js/2.af6eef2b.chunk.js.map": "./static/js/2.af6eef2b.chunk.js.map",
|
||||
"index.html": "./index.html",
|
||||
"precache-manifest.9910cad913c1899724ce0a968a9630a0.js": "./precache-manifest.9910cad913c1899724ce0a968a9630a0.js",
|
||||
"service-worker.js": "./service-worker.js",
|
||||
"static/css/2.637eb612.chunk.css.map": "./static/css/2.637eb612.chunk.css.map",
|
||||
"static/js/2.af6eef2b.chunk.js.LICENSE.txt": "./static/js/2.af6eef2b.chunk.js.LICENSE.txt",
|
||||
"static/media/font-awesome.min.css": "./static/media/fontawesome-webfont.fee66e71.woff",
|
||||
"static/media/google.svg": "./static/media/google.09aea0f5.svg",
|
||||
"static/media/logo.svg": "./static/media/logo.3d432ca2.svg"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.4862f1b0.js",
|
||||
"static/css/2.637eb612.chunk.css",
|
||||
"static/js/2.af6eef2b.chunk.js",
|
||||
"static/js/main.fc3e922e.chunk.js"
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="./manifest.json"/><title>React Material Admin</title><meta name="description" content="React Material Admin is a React Template built with Material-UI"><meta name="keywords" content="react material, material ui admin, react template, react material admin, react material dashboard"><meta name="author" content="Flatlogic LLC."><link href="./static/css/2.637eb612.chunk.css" rel="stylesheet"></head><body style="font-family:Roboto,sans-serif"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,a,i=r[0],l=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(c&&c(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./";var i=this["webpackJsonpreact-material-admin"]=this["webpackJsonpreact-material-admin"]||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var c=l;t()}([])</script><script src="./static/js/2.af6eef2b.chunk.js"></script><script src="./static/js/main.fc3e922e.chunk.js"></script></body></html>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"short_name": "React Material Admin",
|
||||
"name": "React Material Admin is a React Template built with Material-UI",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#536DFE",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
||||
{
|
||||
"revision": "e5348224acfeb02d7e11ea3c1731985e",
|
||||
"url": "./index.html"
|
||||
},
|
||||
{
|
||||
"revision": "b12e029207b94b413809",
|
||||
"url": "./static/css/2.637eb612.chunk.css"
|
||||
},
|
||||
{
|
||||
"revision": "b12e029207b94b413809",
|
||||
"url": "./static/js/2.af6eef2b.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "7d02737b1000ff1e4aedf1ef380f6e7b",
|
||||
"url": "./static/js/2.af6eef2b.chunk.js.LICENSE.txt"
|
||||
},
|
||||
{
|
||||
"revision": "41a71acbec3f321f5a82",
|
||||
"url": "./static/js/main.fc3e922e.chunk.js"
|
||||
},
|
||||
{
|
||||
"revision": "f6f2b74c08f9ec6d372d",
|
||||
"url": "./static/js/runtime-main.4862f1b0.js"
|
||||
},
|
||||
{
|
||||
"revision": "674f50d287a8c48dc19ba404d20fe713",
|
||||
"url": "./static/media/fontawesome-webfont.674f50d2.eot"
|
||||
},
|
||||
{
|
||||
"revision": "912ec66d7572ff821749319396470bde",
|
||||
"url": "./static/media/fontawesome-webfont.912ec66d.svg"
|
||||
},
|
||||
{
|
||||
"revision": "af7ae505a9eed503f8b8e6982036873e",
|
||||
"url": "./static/media/fontawesome-webfont.af7ae505.woff2"
|
||||
},
|
||||
{
|
||||
"revision": "b06871f281fee6b241d60582ae9369b9",
|
||||
"url": "./static/media/fontawesome-webfont.b06871f2.ttf"
|
||||
},
|
||||
{
|
||||
"revision": "fee66e712a8a08eef5805a46892932ad",
|
||||
"url": "./static/media/fontawesome-webfont.fee66e71.woff"
|
||||
},
|
||||
{
|
||||
"revision": "09aea0f59807f6f4f66af7f5719cba9e",
|
||||
"url": "./static/media/google.09aea0f5.svg"
|
||||
},
|
||||
{
|
||||
"revision": "3d432ca2badb7e0130b379f9162b99b1",
|
||||
"url": "./static/media/logo.3d432ca2.svg"
|
||||
}
|
||||
]);
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Welcome to your Workbox-powered service worker!
|
||||
*
|
||||
* You'll need to register this file in your web app and you should
|
||||
* disable HTTP caching for this file too.
|
||||
* See https://goo.gl/nhQhGp
|
||||
*
|
||||
* The rest of the code is auto-generated. Please don't update this file
|
||||
* directly; instead, make changes to your Workbox build configuration
|
||||
* and re-run your build process.
|
||||
* See https://goo.gl/2aRDsh
|
||||
*/
|
||||
|
||||
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
|
||||
|
||||
importScripts(
|
||||
"./precache-manifest.9910cad913c1899724ce0a968a9630a0.js"
|
||||
);
|
||||
|
||||
self.addEventListener('message', (event) => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
||||
workbox.core.clientsClaim();
|
||||
|
||||
/**
|
||||
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
|
||||
* requests for URLs in the manifest.
|
||||
* See https://goo.gl/S9QRab
|
||||
*/
|
||||
self.__precacheManifest = [].concat(self.__precacheManifest || []);
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
|
||||
|
||||
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("./index.html"), {
|
||||
|
||||
blacklist: [/^\/_/,/\/[^\/?]+\.[^\/]+$/],
|
||||
});
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* $script.js JS loader & dependency manager
|
||||
* https://github.com/ded/script.js
|
||||
* (c) Dustin Diaz 2014 | License MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
Copyright (c) 2017 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/*!
|
||||
* ApexCharts v3.20.0
|
||||
* (c) 2018-2020 Juned Chhipa
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*! decimal.js-light v2.5.0 https://github.com/MikeMcl/decimal.js-light/LICENCE */
|
||||
|
||||
/*! svg.draggable.js - v2.2.2 - 2019-01-08
|
||||
* https://github.com/svgdotjs/svg.draggable.js
|
||||
* Copyright (c) 2019 Wout Fierens; Licensed MIT */
|
||||
|
||||
/*! svg.filter.js - v2.0.2 - 2016-02-24
|
||||
* https://github.com/wout/svg.filter.js
|
||||
* Copyright (c) 2016 Wout Fierens; Licensed MIT */
|
||||
|
||||
/**
|
||||
* A better abstraction over CSS.
|
||||
*
|
||||
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
||||
* @website https://github.com/cssinjs/jss
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** @license React v0.19.1
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**!
|
||||
* @fileOverview Kickass library to create and place poppers near their reference elements.
|
||||
* @version 1.16.1-lts
|
||||
* @license
|
||||
* Copyright (c) 2016 Federico Zivolo and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
|
@ -0,0 +1,2 @@
|
|||
!function(e){function r(r){for(var n,a,i=r[0],l=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(c&&c(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./";var i=this["webpackJsonpreact-material-admin"]=this["webpackJsonpreact-material-admin"]||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var c=l;t()}([]);
|
||||
//# sourceMappingURL=runtime-main.4862f1b0.js.map
|
After Width: | Height: | Size: 434 KiB |
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path style="fill:#FBBB00;" d="M113.47,309.408L95.648,375.94l-65.139,1.378C11.042,341.211,0,299.9,0,256
|
||||
c0-42.451,10.324-82.483,28.624-117.732h0.014l57.992,10.632l25.404,57.644c-5.317,15.501-8.215,32.141-8.215,49.456
|
||||
C103.821,274.792,107.225,292.797,113.47,309.408z"/>
|
||||
<path style="fill:#518EF8;" d="M507.527,208.176C510.467,223.662,512,239.655,512,256c0,18.328-1.927,36.206-5.598,53.451
|
||||
c-12.462,58.683-45.025,109.925-90.134,146.187l-0.014-0.014l-73.044-3.727l-10.338-64.535
|
||||
c29.932-17.554,53.324-45.025,65.646-77.911h-136.89V208.176h138.887L507.527,208.176L507.527,208.176z"/>
|
||||
<path style="fill:#28B446;" d="M416.253,455.624l0.014,0.014C372.396,490.901,316.666,512,256,512
|
||||
c-97.491,0-182.252-54.491-225.491-134.681l82.961-67.91c21.619,57.698,77.278,98.771,142.53,98.771
|
||||
c28.047,0,54.323-7.582,76.87-20.818L416.253,455.624z"/>
|
||||
<path style="fill:#F14336;" d="M419.404,58.936l-82.933,67.896c-23.335-14.586-50.919-23.012-80.471-23.012
|
||||
c-66.729,0-123.429,42.957-143.965,102.724l-83.397-68.276h-0.014C71.23,56.123,157.06,0,256,0
|
||||
C318.115,0,375.068,22.126,419.404,58.936z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="131px" height="106px" viewBox="0 0 131 106" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
|
||||
<title>logo_white</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="logo_white" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="Group-39" transform="translate(12.000000, 16.000000)" stroke-width="16.3476923">
|
||||
<path d="M4,72.4669231 L52.2318841,0" id="Line-10" stroke="#FFFFFF"></path>
|
||||
<path d="M1.10416667,74.69 L105.551432,74.69" id="Line-10" stroke="#FBC02D"></path>
|
||||
<path d="M52.2318841,0 L105.551432,74.69" id="Line-10" stroke="#FF5252"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 858 B |
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"name": "react-material-admin",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"homepage": "./",
|
||||
"resolutions": {
|
||||
"websocket-extensions": "^0.1.4",
|
||||
"node-forge": "^0.10.0",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/styles": "^4.11.3",
|
||||
"@mdi/js": "^5.9.55",
|
||||
"@mdi/react": "^1.4.0",
|
||||
"apexcharts": "^3.24.0",
|
||||
"classnames": "^2.2.6",
|
||||
"font-awesome": "^4.7.0",
|
||||
"mui-datatables": "^3.7.4",
|
||||
"react": "^16.14.0",
|
||||
"react-apexcharts": "^1.3.7",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-google-maps": "^9.4.5",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.1",
|
||||
"react-syntax-highlighter": "^15.4.3",
|
||||
"react-toastify": "^7.0.3",
|
||||
"recharts": "^2.0.4",
|
||||
"tinycolor2": "^1.4.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/helper-call-delegate": "^7.11.4",
|
||||
"prettier": "^2.1.1"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React Material Admin</title>
|
||||
<meta name="description" content="React Material Admin is a React Template built with Material-UI">
|
||||
<meta name="keywords" content="react material, material ui admin, react template, react material admin, react material dashboard">
|
||||
<meta name="author" content="Flatlogic LLC.">
|
||||
</head>
|
||||
<body style="font-family: 'Roboto', sans-serif;">
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"short_name": "React Material Admin",
|
||||
"name": "React Material Admin is a React Template built with Material-UI",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#536DFE",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import React from "react";
|
||||
import { HashRouter, Route, Switch, Redirect } from "react-router-dom";
|
||||
|
||||
// components
|
||||
import Layout from "./Layout";
|
||||
|
||||
// pages
|
||||
import Error from "../pages/error";
|
||||
import Login from "../pages/login";
|
||||
|
||||
// context
|
||||
import { useUserState } from "../context/UserContext";
|
||||
|
||||
export default function App() {
|
||||
// global
|
||||
var { isAuthenticated } = useUserState();
|
||||
|
||||
return (
|
||||
<HashRouter>
|
||||
<Switch>
|
||||
<Route exact path="/" render={() => <Redirect to="/app/dashboard" />} />
|
||||
<Route
|
||||
exact
|
||||
path="/app"
|
||||
render={() => <Redirect to="/app/dashboard" />}
|
||||
/>
|
||||
<PrivateRoute path="/app" component={Layout} />
|
||||
<PublicRoute path="/login" component={Login} />
|
||||
<Route component={Error} />
|
||||
</Switch>
|
||||
</HashRouter>
|
||||
);
|
||||
|
||||
// #######################################################################
|
||||
|
||||
function PrivateRoute({ component, ...rest }) {
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props =>
|
||||
isAuthenticated ? (
|
||||
React.createElement(component, props)
|
||||
) : (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/login",
|
||||
state: {
|
||||
from: props.location,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function PublicRoute({ component, ...rest }) {
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props =>
|
||||
isAuthenticated ? (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
React.createElement(component, props)
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
IconButton,
|
||||
InputBase,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Fab,
|
||||
Link
|
||||
} from "@material-ui/core";
|
||||
import {
|
||||
Menu as MenuIcon,
|
||||
MailOutline as MailIcon,
|
||||
NotificationsNone as NotificationsIcon,
|
||||
Person as AccountIcon,
|
||||
Search as SearchIcon,
|
||||
Send as SendIcon,
|
||||
ArrowBack as ArrowBackIcon,
|
||||
} from "@material-ui/icons";
|
||||
import classNames from "classnames";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import { Badge, Typography, Button } from "../Wrappers";
|
||||
import Notification from "../Notification/Notification";
|
||||
import UserAvatar from "../UserAvatar/UserAvatar";
|
||||
|
||||
// context
|
||||
import {
|
||||
useLayoutState,
|
||||
useLayoutDispatch,
|
||||
toggleSidebar,
|
||||
} from "../../context/LayoutContext";
|
||||
import { useUserDispatch, signOut } from "../../context/UserContext";
|
||||
|
||||
const messages = [
|
||||
{
|
||||
id: 0,
|
||||
variant: "warning",
|
||||
name: "Jane Hew",
|
||||
message: "Hey! How is it going?",
|
||||
time: "9:32",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
variant: "success",
|
||||
name: "Lloyd Brown",
|
||||
message: "Check out my new Dashboard",
|
||||
time: "9:18",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
variant: "primary",
|
||||
name: "Mark Winstein",
|
||||
message: "I want rearrange the appointment",
|
||||
time: "9:15",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
variant: "secondary",
|
||||
name: "Liana Dutti",
|
||||
message: "Good news from sale department",
|
||||
time: "9:09",
|
||||
},
|
||||
];
|
||||
|
||||
const notifications = [
|
||||
{ id: 0, color: "warning", message: "Check out this awesome ticket" },
|
||||
{
|
||||
id: 1,
|
||||
color: "success",
|
||||
type: "info",
|
||||
message: "What is the best way to get ...",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
color: "secondary",
|
||||
type: "notification",
|
||||
message: "This is just a simple notification",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
color: "primary",
|
||||
type: "e-commerce",
|
||||
message: "12 new orders has arrived today",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Header(props) {
|
||||
var classes = useStyles();
|
||||
|
||||
// global
|
||||
var layoutState = useLayoutState();
|
||||
var layoutDispatch = useLayoutDispatch();
|
||||
var userDispatch = useUserDispatch();
|
||||
|
||||
// local
|
||||
var [mailMenu, setMailMenu] = useState(null);
|
||||
var [isMailsUnread, setIsMailsUnread] = useState(true);
|
||||
var [notificationsMenu, setNotificationsMenu] = useState(null);
|
||||
var [isNotificationsUnread, setIsNotificationsUnread] = useState(true);
|
||||
var [profileMenu, setProfileMenu] = useState(null);
|
||||
var [isSearchOpen, setSearchOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<AppBar position="fixed" className={classes.appBar}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={() => toggleSidebar(layoutDispatch)}
|
||||
className={classNames(
|
||||
classes.headerMenuButtonSandwich,
|
||||
classes.headerMenuButtonCollapse,
|
||||
)}
|
||||
>
|
||||
{layoutState.isSidebarOpened ? (
|
||||
<ArrowBackIcon
|
||||
classes={{
|
||||
root: classNames(
|
||||
classes.headerIcon,
|
||||
classes.headerIconCollapse,
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MenuIcon
|
||||
classes={{
|
||||
root: classNames(
|
||||
classes.headerIcon,
|
||||
classes.headerIconCollapse,
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
<Typography variant="h6" weight="medium" className={classes.logotype}>
|
||||
React Material Admin
|
||||
</Typography>
|
||||
<div className={classes.grow} />
|
||||
<Button component={Link} href="https://flatlogic.com/templates/react-material-admin-full" variant={"outlined"} color={"secondary"} className={classes.purchaseBtn}>Unlock full version</Button>
|
||||
<div
|
||||
className={classNames(classes.search, {
|
||||
[classes.searchFocused]: isSearchOpen,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classNames(classes.searchIcon, {
|
||||
[classes.searchIconOpened]: isSearchOpen,
|
||||
})}
|
||||
onClick={() => setSearchOpen(!isSearchOpen)}
|
||||
>
|
||||
<SearchIcon classes={{ root: classes.headerIcon }} />
|
||||
</div>
|
||||
<InputBase
|
||||
placeholder="Search…"
|
||||
classes={{
|
||||
root: classes.inputRoot,
|
||||
input: classes.inputInput,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-haspopup="true"
|
||||
aria-controls="mail-menu"
|
||||
onClick={e => {
|
||||
setNotificationsMenu(e.currentTarget);
|
||||
setIsNotificationsUnread(false);
|
||||
}}
|
||||
className={classes.headerMenuButton}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={isNotificationsUnread ? notifications.length : null}
|
||||
color="warning"
|
||||
>
|
||||
<NotificationsIcon classes={{ root: classes.headerIcon }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-haspopup="true"
|
||||
aria-controls="mail-menu"
|
||||
onClick={e => {
|
||||
setMailMenu(e.currentTarget);
|
||||
setIsMailsUnread(false);
|
||||
}}
|
||||
className={classes.headerMenuButton}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={isMailsUnread ? messages.length : null}
|
||||
color="secondary"
|
||||
>
|
||||
<MailIcon classes={{ root: classes.headerIcon }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-haspopup="true"
|
||||
color="inherit"
|
||||
className={classes.headerMenuButton}
|
||||
aria-controls="profile-menu"
|
||||
onClick={e => setProfileMenu(e.currentTarget)}
|
||||
>
|
||||
<AccountIcon classes={{ root: classes.headerIcon }} />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="mail-menu"
|
||||
open={Boolean(mailMenu)}
|
||||
anchorEl={mailMenu}
|
||||
onClose={() => setMailMenu(null)}
|
||||
MenuListProps={{ className: classes.headerMenuList }}
|
||||
className={classes.headerMenu}
|
||||
classes={{ paper: classes.profileMenu }}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
<div className={classes.profileMenuUser}>
|
||||
<Typography variant="h4" weight="medium">
|
||||
New Messages
|
||||
</Typography>
|
||||
<Typography
|
||||
className={classes.profileMenuLink}
|
||||
component="a"
|
||||
color="secondary"
|
||||
>
|
||||
{messages.length} New Messages
|
||||
</Typography>
|
||||
</div>
|
||||
{messages.map(message => (
|
||||
<MenuItem key={message.id} className={classes.messageNotification}>
|
||||
<div className={classes.messageNotificationSide}>
|
||||
<UserAvatar color={message.variant} name={message.name} />
|
||||
<Typography size="sm" color="text" colorBrightness="secondary">
|
||||
{message.time}
|
||||
</Typography>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
classes.messageNotificationSide,
|
||||
classes.messageNotificationBodySide,
|
||||
)}
|
||||
>
|
||||
<Typography weight="medium" gutterBottom>
|
||||
{message.name}
|
||||
</Typography>
|
||||
<Typography color="text" colorBrightness="secondary">
|
||||
{message.message}
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
))}
|
||||
<Fab
|
||||
variant="extended"
|
||||
color="primary"
|
||||
aria-label="Add"
|
||||
className={classes.sendMessageButton}
|
||||
>
|
||||
Send New Message
|
||||
<SendIcon className={classes.sendButtonIcon} />
|
||||
</Fab>
|
||||
</Menu>
|
||||
<Menu
|
||||
id="notifications-menu"
|
||||
open={Boolean(notificationsMenu)}
|
||||
anchorEl={notificationsMenu}
|
||||
onClose={() => setNotificationsMenu(null)}
|
||||
className={classes.headerMenu}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
{notifications.map(notification => (
|
||||
<MenuItem
|
||||
key={notification.id}
|
||||
onClick={() => setNotificationsMenu(null)}
|
||||
className={classes.headerMenuItem}
|
||||
>
|
||||
<Notification {...notification} typographyVariant="inherit" />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
<Menu
|
||||
id="profile-menu"
|
||||
open={Boolean(profileMenu)}
|
||||
anchorEl={profileMenu}
|
||||
onClose={() => setProfileMenu(null)}
|
||||
className={classes.headerMenu}
|
||||
classes={{ paper: classes.profileMenu }}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
<div className={classes.profileMenuUser}>
|
||||
<Typography variant="h4" weight="medium">
|
||||
John Smith
|
||||
</Typography>
|
||||
<Typography
|
||||
className={classes.profileMenuLink}
|
||||
component="a"
|
||||
color="primary"
|
||||
href="https://flatlogic.com"
|
||||
>
|
||||
Flalogic.com
|
||||
</Typography>
|
||||
</div>
|
||||
<MenuItem
|
||||
className={classNames(
|
||||
classes.profileMenuItem,
|
||||
classes.headerMenuItem,
|
||||
)}
|
||||
>
|
||||
<AccountIcon className={classes.profileMenuIcon} /> Profile
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className={classNames(
|
||||
classes.profileMenuItem,
|
||||
classes.headerMenuItem,
|
||||
)}
|
||||
>
|
||||
<AccountIcon className={classes.profileMenuIcon} /> Tasks
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className={classNames(
|
||||
classes.profileMenuItem,
|
||||
classes.headerMenuItem,
|
||||
)}
|
||||
>
|
||||
<AccountIcon className={classes.profileMenuIcon} /> Messages
|
||||
</MenuItem>
|
||||
<div className={classes.profileMenuUser}>
|
||||
<Typography
|
||||
className={classes.profileMenuLink}
|
||||
color="primary"
|
||||
onClick={() => signOut(userDispatch, props.history)}
|
||||
>
|
||||
Sign Out
|
||||
</Typography>
|
||||
</div>
|
||||
</Menu>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,456 @@
|
|||
import React from "react";
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
IconButton,
|
||||
InputBase,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Fab,
|
||||
withStyles
|
||||
} from "@material-ui/core";
|
||||
import {
|
||||
Menu as MenuIcon,
|
||||
MailOutline as MailIcon,
|
||||
NotificationsNone as NotificationsIcon,
|
||||
Person as AccountIcon,
|
||||
Search as SearchIcon,
|
||||
Send as SendIcon,
|
||||
ArrowBack as ArrowBackIcon
|
||||
} from "@material-ui/icons";
|
||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||
import classNames from "classnames";
|
||||
|
||||
import { Badge, Typography } from "../Wrappers";
|
||||
import Notification from "../Notification";
|
||||
import UserAvatar from "../UserAvatar";
|
||||
|
||||
const messages = [
|
||||
{
|
||||
id: 0,
|
||||
variant: "warning",
|
||||
name: "Jane Hew",
|
||||
message: "Hey! How is it going?",
|
||||
time: "9:32"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
variant: "success",
|
||||
name: "Lloyd Brown",
|
||||
message: "Check out my new Dashboard",
|
||||
time: "9:18"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
variant: "primary",
|
||||
name: "Mark Winstein",
|
||||
message: "I want rearrange the appointment",
|
||||
time: "9:15"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
variant: "secondary",
|
||||
name: "Liana Dutti",
|
||||
message: "Good news from sale department",
|
||||
time: "9:09"
|
||||
}
|
||||
];
|
||||
|
||||
const notifications = [
|
||||
{ id: 0, color: "warning", message: "Check out this awesome ticket" },
|
||||
{
|
||||
id: 1,
|
||||
color: "success",
|
||||
type: "info",
|
||||
message: "What is the best way to get ..."
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
color: "secondary",
|
||||
type: "notification",
|
||||
message: "This is just a simple notification"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
color: "primary",
|
||||
type: "e-commerce",
|
||||
message: "12 new orders has arrived today"
|
||||
}
|
||||
];
|
||||
|
||||
const Header = ({ classes, isSidebarOpened, toggleSidebar, ...props }) => (
|
||||
<AppBar position="fixed" className={classes.appBar}>
|
||||
<Toolbar className={classes.toolbar}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={toggleSidebar}
|
||||
className={classNames(
|
||||
classes.headerMenuButton,
|
||||
classes.headerMenuButtonCollapse
|
||||
)}
|
||||
>
|
||||
{isSidebarOpened ? (
|
||||
<ArrowBackIcon
|
||||
classes={{
|
||||
root: classNames(classes.headerIcon, classes.headerIconCollapse)
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MenuIcon
|
||||
classes={{
|
||||
root: classNames(classes.headerIcon, classes.headerIconCollapse)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
<Typography variant="h6" weight="medium" className={classes.logotype}>React Material Admin</Typography>
|
||||
<div className={classes.grow} />
|
||||
<div
|
||||
className={classNames(classes.search, {
|
||||
[classes.searchFocused]: props.isSearchOpen
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classNames(classes.searchIcon, {
|
||||
[classes.searchIconOpened]: props.isSearchOpen
|
||||
})}
|
||||
onClick={props.toggleSearch}
|
||||
>
|
||||
<SearchIcon classes={{ root: classes.headerIcon }} />
|
||||
</div>
|
||||
<InputBase
|
||||
placeholder="Search…"
|
||||
classes={{
|
||||
root: classes.inputRoot,
|
||||
input: classes.inputInput
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-haspopup="true"
|
||||
aria-controls="mail-menu"
|
||||
onClick={props.openNotificationsMenu}
|
||||
className={classes.headerMenuButton}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={
|
||||
props.isNotificationsUnread ? notifications.length : null
|
||||
}
|
||||
colortheme="warning"
|
||||
>
|
||||
<NotificationsIcon classes={{ root: classes.headerIcon }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-haspopup="true"
|
||||
aria-controls="mail-menu"
|
||||
onClick={props.openMailMenu}
|
||||
className={classes.headerMenuButton}
|
||||
>
|
||||
<Badge
|
||||
badgeContent={props.isMailsUnread ? messages.length : null}
|
||||
color="secondary"
|
||||
>
|
||||
<MailIcon classes={{ root: classes.headerIcon }} />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-haspopup="true"
|
||||
color="inherit"
|
||||
className={classes.headerMenuButton}
|
||||
aria-controls="profile-menu"
|
||||
onClick={props.openProfileMenu}
|
||||
>
|
||||
<AccountIcon classes={{ root: classes.headerIcon }} />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="mail-menu"
|
||||
open={Boolean(props.mailMenu)}
|
||||
anchorEl={props.mailMenu}
|
||||
onClose={props.closeMailMenu}
|
||||
MenuListProps={{ className: classes.headerMenuList }}
|
||||
className={classes.headerMenu}
|
||||
classes={{ paper: classes.profileMenu }}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
<div className={classes.profileMenuUser}>
|
||||
<Typography variant="h4" weight="medium">
|
||||
New Messages
|
||||
</Typography>
|
||||
<Typography
|
||||
className={classes.profileMenuLink}
|
||||
component="a"
|
||||
color="secondary"
|
||||
>
|
||||
{messages.length} New Messages
|
||||
</Typography>
|
||||
</div>
|
||||
{messages.map(message => (
|
||||
<MenuItem key={message.id} className={classes.messageNotification}>
|
||||
<div className={classes.messageNotificationSide}>
|
||||
<UserAvatar color={message.variant} name={message.name} />
|
||||
<Typography size="sm" color="textSecondary">
|
||||
{message.time}
|
||||
</Typography>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
classes.messageNotificationSide,
|
||||
classes.messageNotificationBodySide
|
||||
)}
|
||||
>
|
||||
<Typography weight="medium" gutterBottom>
|
||||
{message.name}
|
||||
</Typography>
|
||||
<Typography color="textSecondary">{message.message}</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
))}
|
||||
<Fab
|
||||
variant="extended"
|
||||
color="primary"
|
||||
aria-label="Add"
|
||||
className={classes.sendMessageButton}
|
||||
>
|
||||
Send New Message
|
||||
<SendIcon className={classes.sendButtonIcon} />
|
||||
</Fab>
|
||||
</Menu>
|
||||
<Menu
|
||||
id="notifications-menu"
|
||||
open={Boolean(props.notificationsMenu)}
|
||||
anchorEl={props.notificationsMenu}
|
||||
onClose={props.closeNotificationsMenu}
|
||||
className={classes.headerMenu}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
{notifications.map(notification => (
|
||||
<MenuItem
|
||||
key={notification.id}
|
||||
onClick={props.closeNotificationsMenu}
|
||||
className={classes.headerMenuItem}
|
||||
>
|
||||
<Notification {...notification} typographyVariant="inherit" />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
<Menu
|
||||
id="profile-menu"
|
||||
open={Boolean(props.profileMenu)}
|
||||
anchorEl={props.profileMenu}
|
||||
onClose={props.closeProfileMenu}
|
||||
className={classes.headerMenu}
|
||||
classes={{ paper: classes.profileMenu }}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
<div className={classes.profileMenuUser}>
|
||||
<Typography variant="h4" weight="medium">
|
||||
John Smith
|
||||
</Typography>
|
||||
<Typography
|
||||
className={classes.profileMenuLink}
|
||||
component="a"
|
||||
color="primary"
|
||||
href="https://flatlogic.com"
|
||||
>
|
||||
Flalogic.com
|
||||
</Typography>
|
||||
</div>
|
||||
<MenuItem
|
||||
className={classNames(
|
||||
classes.profileMenuItem,
|
||||
classes.headerMenuItem
|
||||
)}
|
||||
>
|
||||
<AccountIcon className={classes.profileMenuIcon} /> Profile
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className={classNames(
|
||||
classes.profileMenuItem,
|
||||
classes.headerMenuItem
|
||||
)}
|
||||
>
|
||||
<AccountIcon className={classes.profileMenuIcon} /> Tasks
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className={classNames(
|
||||
classes.profileMenuItem,
|
||||
classes.headerMenuItem
|
||||
)}
|
||||
>
|
||||
<AccountIcon className={classes.profileMenuIcon} /> Messages
|
||||
</MenuItem>
|
||||
<div className={classes.profileMenuUser}>
|
||||
<Typography
|
||||
className={classes.profileMenuLink}
|
||||
color="primary"
|
||||
onClick={props.signOut}
|
||||
>
|
||||
Sign Out
|
||||
</Typography>
|
||||
</div>
|
||||
</Menu>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
|
||||
const styles = theme => ({
|
||||
logotype: {
|
||||
color: "white",
|
||||
marginLeft: theme.spacing.unit * 2.5,
|
||||
marginRight: theme.spacing.unit * 2.5,
|
||||
fontWeight: 500,
|
||||
fontSize: 18,
|
||||
whiteSpace: "nowrap",
|
||||
[theme.breakpoints.down("xs")]: {
|
||||
display: "none"
|
||||
}
|
||||
},
|
||||
appBar: {
|
||||
width: "100vw",
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
})
|
||||
},
|
||||
toolbar: {
|
||||
paddingLeft: theme.spacing.unit * 2,
|
||||
paddingRight: theme.spacing.unit * 2
|
||||
},
|
||||
hide: {
|
||||
display: "none"
|
||||
},
|
||||
grow: {
|
||||
flexGrow: 1
|
||||
},
|
||||
search: {
|
||||
position: "relative",
|
||||
borderRadius: 25,
|
||||
paddingLeft: theme.spacing.unit * 2.5,
|
||||
width: 36,
|
||||
backgroundColor: fade(theme.palette.common.black, 0),
|
||||
transition: theme.transitions.create(["background-color", "width"]),
|
||||
"&:hover": {
|
||||
cursor: "pointer",
|
||||
backgroundColor: fade(theme.palette.common.black, 0.08)
|
||||
}
|
||||
},
|
||||
searchFocused: {
|
||||
backgroundColor: fade(theme.palette.common.black, 0.08),
|
||||
width: "100%",
|
||||
[theme.breakpoints.up("md")]: {
|
||||
width: 250
|
||||
}
|
||||
},
|
||||
searchIcon: {
|
||||
width: 36,
|
||||
right: 0,
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
transition: theme.transitions.create("right"),
|
||||
"&:hover": {
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
searchIconOpened: {
|
||||
right: theme.spacing.unit * 1.25
|
||||
},
|
||||
inputRoot: {
|
||||
color: "inherit",
|
||||
width: "100%"
|
||||
},
|
||||
inputInput: {
|
||||
height: 36,
|
||||
padding: 0,
|
||||
paddingRight: 36 + theme.spacing.unit * 1.25,
|
||||
width: "100%"
|
||||
},
|
||||
messageContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
},
|
||||
headerMenu: {
|
||||
marginTop: theme.spacing.unit * 7
|
||||
},
|
||||
headerMenuList: {
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
},
|
||||
headerMenuItem: {
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: "white"
|
||||
}
|
||||
},
|
||||
headerMenuButton: {
|
||||
marginLeft: theme.spacing.unit * 2,
|
||||
padding: theme.spacing.unit / 2
|
||||
},
|
||||
headerMenuButtonCollapse: {
|
||||
marginRight: theme.spacing.unit * 2
|
||||
},
|
||||
headerIcon: {
|
||||
fontSize: 28,
|
||||
color: "rgba(255, 255, 255, 0.35)"
|
||||
},
|
||||
headerIconCollapse: {
|
||||
color: "white"
|
||||
},
|
||||
profileMenu: {
|
||||
minWidth: 265
|
||||
},
|
||||
profileMenuUser: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: theme.spacing.unit * 2
|
||||
},
|
||||
profileMenuItem: {
|
||||
color: theme.palette.text.hint
|
||||
},
|
||||
profileMenuIcon: {
|
||||
marginRight: theme.spacing.unit * 2,
|
||||
color: theme.palette.text.hint
|
||||
},
|
||||
profileMenuLink: {
|
||||
fontSize: 16,
|
||||
textDecoration: "none",
|
||||
"&:hover": {
|
||||
cursor: "pointer"
|
||||
}
|
||||
},
|
||||
messageNotification: {
|
||||
height: "auto",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: theme.palette.background.light
|
||||
}
|
||||
},
|
||||
messageNotificationSide: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
marginRight: theme.spacing.unit * 2
|
||||
},
|
||||
messageNotificationBodySide: {
|
||||
alignItems: "flex-start",
|
||||
marginRight: 0
|
||||
},
|
||||
sendMessageButton: {
|
||||
margin: theme.spacing.unit * 4,
|
||||
marginTop: theme.spacing.unit * 2,
|
||||
marginBottom: theme.spacing.unit * 2,
|
||||
textTransform: "none"
|
||||
},
|
||||
sendButtonIcon: {
|
||||
marginLeft: theme.spacing.unit * 2
|
||||
}
|
||||
});
|
||||
|
||||
export default withStyles(styles)(Header);
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Header",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "Header.js"
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
logotype: {
|
||||
color: "white",
|
||||
marginLeft: theme.spacing(2.5),
|
||||
marginRight: theme.spacing(2.5),
|
||||
fontWeight: 500,
|
||||
fontSize: 18,
|
||||
whiteSpace: "nowrap",
|
||||
[theme.breakpoints.down("xs")]: {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
appBar: {
|
||||
width: "100vw",
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
},
|
||||
toolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
hide: {
|
||||
display: "none",
|
||||
},
|
||||
grow: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
search: {
|
||||
position: "relative",
|
||||
borderRadius: 25,
|
||||
paddingLeft: theme.spacing(2.5),
|
||||
width: 36,
|
||||
backgroundColor: fade(theme.palette.common.black, 0),
|
||||
transition: theme.transitions.create(["background-color", "width"]),
|
||||
"&:hover": {
|
||||
cursor: "pointer",
|
||||
backgroundColor: fade(theme.palette.common.black, 0.08),
|
||||
},
|
||||
},
|
||||
searchFocused: {
|
||||
backgroundColor: fade(theme.palette.common.black, 0.08),
|
||||
width: "100%",
|
||||
[theme.breakpoints.up("md")]: {
|
||||
width: 250,
|
||||
},
|
||||
},
|
||||
searchIcon: {
|
||||
width: 36,
|
||||
right: 0,
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
transition: theme.transitions.create("right"),
|
||||
"&:hover": {
|
||||
cursor: "pointer",
|
||||
},
|
||||
},
|
||||
searchIconOpened: {
|
||||
right: theme.spacing(1.25),
|
||||
},
|
||||
inputRoot: {
|
||||
color: "inherit",
|
||||
width: "100%",
|
||||
},
|
||||
inputInput: {
|
||||
height: 36,
|
||||
padding: 0,
|
||||
paddingRight: 36 + theme.spacing(1.25),
|
||||
width: "100%",
|
||||
},
|
||||
messageContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
headerMenu: {
|
||||
marginTop: theme.spacing(7),
|
||||
},
|
||||
headerMenuList: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
headerMenuItem: {
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
// color: "white",
|
||||
},
|
||||
},
|
||||
headerMenuButton: {
|
||||
marginLeft: theme.spacing(2),
|
||||
padding: theme.spacing(0.5),
|
||||
},
|
||||
headerMenuButtonSandwich: {
|
||||
marginLeft: 9,
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
marginLeft: 0
|
||||
},
|
||||
padding: theme.spacing(0.5),
|
||||
},
|
||||
headerMenuButtonCollapse: {
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
headerIcon: {
|
||||
fontSize: 28,
|
||||
color: "rgba(255, 255, 255, 0.35)",
|
||||
},
|
||||
headerIconCollapse: {
|
||||
color: "white",
|
||||
},
|
||||
profileMenu: {
|
||||
minWidth: 265,
|
||||
},
|
||||
profileMenuUser: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
profileMenuItem: {
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
profileMenuIcon: {
|
||||
marginRight: theme.spacing(2),
|
||||
color: theme.palette.text.hint,
|
||||
'&:hover': {
|
||||
color: theme.palette.primary.main,
|
||||
}
|
||||
},
|
||||
profileMenuLink: {
|
||||
fontSize: 16,
|
||||
textDecoration: "none",
|
||||
"&:hover": {
|
||||
cursor: "pointer",
|
||||
},
|
||||
},
|
||||
messageNotification: {
|
||||
height: "auto",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
},
|
||||
},
|
||||
messageNotificationSide: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
messageNotificationBodySide: {
|
||||
alignItems: "flex-start",
|
||||
marginRight: 0,
|
||||
},
|
||||
sendMessageButton: {
|
||||
margin: theme.spacing(4),
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
textTransform: "none",
|
||||
},
|
||||
sendButtonIcon: {
|
||||
marginLeft: theme.spacing(2),
|
||||
},
|
||||
purchaseBtn: {
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
display: 'none'
|
||||
},
|
||||
marginRight: theme.spacing(3)
|
||||
}
|
||||
}));
|
|
@ -0,0 +1,150 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Route,
|
||||
Switch,
|
||||
Redirect,
|
||||
withRouter,
|
||||
} from "react-router-dom";
|
||||
import classnames from "classnames";
|
||||
import {Box, IconButton, Link} from '@material-ui/core'
|
||||
import Icon from '@mdi/react'
|
||||
|
||||
//icons
|
||||
import {
|
||||
mdiFacebook as FacebookIcon,
|
||||
mdiTwitter as TwitterIcon,
|
||||
mdiGithub as GithubIcon,
|
||||
} from '@mdi/js'
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import Header from "../Header";
|
||||
import Sidebar from "../Sidebar";
|
||||
|
||||
// pages
|
||||
import Dashboard from "../../pages/dashboard";
|
||||
import Typography from "../../pages/typography";
|
||||
import Notifications from "../../pages/notifications";
|
||||
import Maps from "../../pages/maps";
|
||||
import Tables from "../../pages/tables";
|
||||
import Icons from "../../pages/icons";
|
||||
import Charts from "../../pages/charts";
|
||||
|
||||
// context
|
||||
import { useLayoutState } from "../../context/LayoutContext";
|
||||
|
||||
function Layout(props) {
|
||||
var classes = useStyles();
|
||||
|
||||
// global
|
||||
var layoutState = useLayoutState();
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<>
|
||||
<Header history={props.history} />
|
||||
<Sidebar />
|
||||
<div
|
||||
className={classnames(classes.content, {
|
||||
[classes.contentShift]: layoutState.isSidebarOpened,
|
||||
})}
|
||||
>
|
||||
<div className={classes.fakeToolbar} />
|
||||
<Switch>
|
||||
<Route path="/app/dashboard" component={Dashboard} />
|
||||
<Route path="/app/typography" component={Typography} />
|
||||
<Route path="/app/tables" component={Tables} />
|
||||
<Route path="/app/notifications" component={Notifications} />
|
||||
<Route
|
||||
exact
|
||||
path="/app/ui"
|
||||
render={() => <Redirect to="/app/ui/icons" />}
|
||||
/>
|
||||
<Route path="/app/ui/maps" component={Maps} />
|
||||
<Route path="/app/ui/icons" component={Icons} />
|
||||
<Route path="/app/ui/charts" component={Charts} />
|
||||
</Switch>
|
||||
<Box
|
||||
mt={5}
|
||||
width={"100%"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<div>
|
||||
<Link
|
||||
color={'primary'}
|
||||
href={'https://flatlogic.com/'}
|
||||
target={'_blank'}
|
||||
className={classes.link}
|
||||
>
|
||||
Flatlogic
|
||||
</Link>
|
||||
<Link
|
||||
color={'primary'}
|
||||
href={'https://flatlogic.com/about'}
|
||||
target={'_blank'}
|
||||
className={classes.link}
|
||||
>
|
||||
About Us
|
||||
</Link>
|
||||
<Link
|
||||
color={'primary'}
|
||||
href={'https://flatlogic.com/blog'}
|
||||
target={'_blank'}
|
||||
className={classes.link}
|
||||
>
|
||||
Blog
|
||||
</Link>
|
||||
</div>
|
||||
<div>
|
||||
<Link
|
||||
href={'https://www.facebook.com/flatlogic'}
|
||||
target={'_blank'}
|
||||
>
|
||||
<IconButton aria-label="facebook">
|
||||
<Icon
|
||||
path={FacebookIcon}
|
||||
size={1}
|
||||
color="#6E6E6E99"
|
||||
/>
|
||||
</IconButton>
|
||||
</Link>
|
||||
<Link
|
||||
href={'https://twitter.com/flatlogic'}
|
||||
target={'_blank'}
|
||||
>
|
||||
<IconButton aria-label="twitter">
|
||||
<Icon
|
||||
path={TwitterIcon}
|
||||
size={1}
|
||||
color="#6E6E6E99"
|
||||
/>
|
||||
</IconButton>
|
||||
</Link>
|
||||
<Link
|
||||
href={'https://github.com/flatlogic'}
|
||||
target={'_blank'}
|
||||
>
|
||||
<IconButton
|
||||
aria-label="github"
|
||||
style={{marginRight: -12}}
|
||||
>
|
||||
<Icon
|
||||
path={GithubIcon}
|
||||
size={1}
|
||||
color="#6E6E6E99"
|
||||
/>
|
||||
</IconButton>
|
||||
</Link>
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter(Layout);
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Layout",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "Layout.js"
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
root: {
|
||||
display: "flex",
|
||||
maxWidth: "100vw",
|
||||
overflowX: "hidden",
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing(3),
|
||||
width: `calc(100vw - 240px)`,
|
||||
minHeight: "100vh",
|
||||
},
|
||||
contentShift: {
|
||||
width: `calc(100vw - ${240 + theme.spacing(6)}px)`,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
fakeToolbar: {
|
||||
...theme.mixins.toolbar,
|
||||
},
|
||||
link: {
|
||||
'&:not(:first-child)': {
|
||||
paddingLeft: 15
|
||||
}
|
||||
}
|
||||
}));
|
|
@ -0,0 +1,117 @@
|
|||
import React from "react";
|
||||
import { Button } from "@material-ui/core";
|
||||
import {
|
||||
NotificationsNone as NotificationsIcon,
|
||||
ThumbUp as ThumbUpIcon,
|
||||
ShoppingCart as ShoppingCartIcon,
|
||||
LocalOffer as TicketIcon,
|
||||
BusinessCenter as DeliveredIcon,
|
||||
SmsFailed as FeedbackIcon,
|
||||
DiscFull as DiscIcon,
|
||||
Email as MessageIcon,
|
||||
Report as ReportIcon,
|
||||
Error as DefenceIcon,
|
||||
AccountBox as CustomerIcon,
|
||||
Done as ShippedIcon,
|
||||
Publish as UploadIcon,
|
||||
} from "@material-ui/icons";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
import classnames from "classnames";
|
||||
import tinycolor from "tinycolor2";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import { Typography } from "../Wrappers";
|
||||
|
||||
const typesIcons = {
|
||||
"e-commerce": <ShoppingCartIcon />,
|
||||
notification: <NotificationsIcon />,
|
||||
offer: <TicketIcon />,
|
||||
info: <ThumbUpIcon />,
|
||||
message: <MessageIcon />,
|
||||
feedback: <FeedbackIcon />,
|
||||
customer: <CustomerIcon />,
|
||||
shipped: <ShippedIcon />,
|
||||
delivered: <DeliveredIcon />,
|
||||
defence: <DefenceIcon />,
|
||||
report: <ReportIcon />,
|
||||
upload: <UploadIcon />,
|
||||
disc: <DiscIcon />,
|
||||
};
|
||||
|
||||
export default function Notification({ variant, ...props }) {
|
||||
var classes = useStyles();
|
||||
var theme = useTheme();
|
||||
|
||||
const icon = getIconByType(props.type);
|
||||
const iconWithStyles = React.cloneElement(icon, {
|
||||
classes: {
|
||||
root: classes.notificationIcon,
|
||||
},
|
||||
style: {
|
||||
color:
|
||||
variant !== "contained" &&
|
||||
theme.palette[props.color] &&
|
||||
theme.palette[props.color].main,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(classes.notificationContainer, props.className, {
|
||||
[classes.notificationContained]: variant === "contained",
|
||||
[classes.notificationContainedShadowless]: props.shadowless,
|
||||
})}
|
||||
style={{
|
||||
backgroundColor:
|
||||
variant === "contained" &&
|
||||
theme.palette[props.color] &&
|
||||
theme.palette[props.color].main,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={classnames(classes.notificationIconContainer, {
|
||||
[classes.notificationIconContainerContained]: variant === "contained",
|
||||
[classes.notificationIconContainerRounded]: variant === "rounded",
|
||||
})}
|
||||
style={{
|
||||
backgroundColor:
|
||||
variant === "rounded" &&
|
||||
theme.palette[props.color] &&
|
||||
tinycolor(theme.palette[props.color].main)
|
||||
.setAlpha(0.15)
|
||||
.toRgbString(),
|
||||
}}
|
||||
>
|
||||
{iconWithStyles}
|
||||
</div>
|
||||
<div className={classes.messageContainer}>
|
||||
<Typography
|
||||
className={classnames({
|
||||
[classes.containedTypography]: variant === "contained",
|
||||
})}
|
||||
variant={props.typographyVariant}
|
||||
size={variant !== "contained" && !props.typographyVariant && "md"}
|
||||
>
|
||||
{props.message}
|
||||
</Typography>
|
||||
{props.extraButton && props.extraButtonClick && (
|
||||
<Button
|
||||
onClick={props.extraButtonClick}
|
||||
disableRipple
|
||||
className={classes.extraButton}
|
||||
>
|
||||
{props.extraButton}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ####################################################################
|
||||
function getIconByType(type = "offer") {
|
||||
return typesIcons[type];
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Notification",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "Notification.js"
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
notificationContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
notificationContained: {
|
||||
borderRadius: 45,
|
||||
height: 45,
|
||||
boxShadow: theme.customShadows.widgetDark,
|
||||
},
|
||||
notificationContainedShadowless: {
|
||||
boxShadow: "none",
|
||||
},
|
||||
notificationIconContainer: {
|
||||
minWidth: 45,
|
||||
height: 45,
|
||||
borderRadius: 45,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: 24,
|
||||
},
|
||||
notificationIconContainerContained: {
|
||||
fontSize: 18,
|
||||
color: "#FFFFFF80",
|
||||
},
|
||||
notificationIconContainerRounded: {
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
containedTypography: {
|
||||
color: "white",
|
||||
},
|
||||
messageContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
flexGrow: 1,
|
||||
},
|
||||
extraButton: {
|
||||
color: "white",
|
||||
"&:hover, &:focus": {
|
||||
background: "transparent",
|
||||
},
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,20 @@
|
|||
import React from "react";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import { Typography } from "../Wrappers";
|
||||
|
||||
export default function PageTitle(props) {
|
||||
var classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.pageTitleContainer}>
|
||||
<Typography className={classes.typo} variant="h1" size="sm">
|
||||
{props.title}
|
||||
</Typography>
|
||||
{props.button && props.button}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "PageTitle",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "PageTitle.js"
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
pageTitleContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: theme.spacing(4),
|
||||
marginTop: theme.spacing(5),
|
||||
},
|
||||
typo: {
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
button: {
|
||||
boxShadow: theme.customShadows.widget,
|
||||
textTransform: "none",
|
||||
"&:active": {
|
||||
boxShadow: theme.customShadows.widgetWide,
|
||||
},
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,156 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Drawer, IconButton, List } from "@material-ui/core";
|
||||
import {
|
||||
Home as HomeIcon,
|
||||
NotificationsNone as NotificationsIcon,
|
||||
FormatSize as TypographyIcon,
|
||||
FilterNone as UIElementsIcon,
|
||||
BorderAll as TableIcon,
|
||||
QuestionAnswer as SupportIcon,
|
||||
LibraryBooks as LibraryIcon,
|
||||
HelpOutline as FAQIcon,
|
||||
ArrowBack as ArrowBackIcon,
|
||||
} from "@material-ui/icons";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import SidebarLink from "./components/SidebarLink/SidebarLink";
|
||||
import Dot from "./components/Dot";
|
||||
|
||||
// context
|
||||
import {
|
||||
useLayoutState,
|
||||
useLayoutDispatch,
|
||||
toggleSidebar,
|
||||
} from "../../context/LayoutContext";
|
||||
|
||||
const structure = [
|
||||
{ id: 0, label: "Dashboard", link: "/app/dashboard", icon: <HomeIcon /> },
|
||||
{
|
||||
id: 1,
|
||||
label: "Typography",
|
||||
link: "/app/typography",
|
||||
icon: <TypographyIcon />,
|
||||
},
|
||||
{ id: 2, label: "Tables", link: "/app/tables", icon: <TableIcon /> },
|
||||
{
|
||||
id: 3,
|
||||
label: "Notifications",
|
||||
link: "/app/notifications",
|
||||
icon: <NotificationsIcon />,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
label: "UI Elements",
|
||||
link: "/app/ui",
|
||||
icon: <UIElementsIcon />,
|
||||
children: [
|
||||
{ label: "Icons", link: "/app/ui/icons" },
|
||||
{ label: "Charts", link: "/app/ui/charts" },
|
||||
{ label: "Maps", link: "/app/ui/maps" },
|
||||
],
|
||||
},
|
||||
{ id: 5, type: "divider" },
|
||||
{ id: 6, type: "title", label: "HELP" },
|
||||
{ id: 7, label: "Library", link: "https://flatlogic.com/templates", icon: <LibraryIcon /> },
|
||||
{ id: 8, label: "Support", link: "https://flatlogic.com/forum", icon: <SupportIcon /> },
|
||||
{ id: 9, label: "FAQ", link: "https://flatlogic.com/forum", icon: <FAQIcon /> },
|
||||
{ id: 10, type: "divider" },
|
||||
{ id: 11, type: "title", label: "PROJECTS" },
|
||||
{
|
||||
id: 12,
|
||||
label: "My recent",
|
||||
link: "",
|
||||
icon: <Dot size="small" color="warning" />,
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
label: "Starred",
|
||||
link: "",
|
||||
icon: <Dot size="small" color="primary" />,
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
label: "Background",
|
||||
link: "",
|
||||
icon: <Dot size="small" color="secondary" />,
|
||||
},
|
||||
];
|
||||
|
||||
function Sidebar({ location }) {
|
||||
var classes = useStyles();
|
||||
var theme = useTheme();
|
||||
|
||||
// global
|
||||
var { isSidebarOpened } = useLayoutState();
|
||||
var layoutDispatch = useLayoutDispatch();
|
||||
|
||||
// local
|
||||
var [isPermanent, setPermanent] = useState(true);
|
||||
|
||||
useEffect(function() {
|
||||
window.addEventListener("resize", handleWindowWidthChange);
|
||||
handleWindowWidthChange();
|
||||
return function cleanup() {
|
||||
window.removeEventListener("resize", handleWindowWidthChange);
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
variant={isPermanent ? "permanent" : "temporary"}
|
||||
className={classNames(classes.drawer, {
|
||||
[classes.drawerOpen]: isSidebarOpened,
|
||||
[classes.drawerClose]: !isSidebarOpened,
|
||||
})}
|
||||
classes={{
|
||||
paper: classNames({
|
||||
[classes.drawerOpen]: isSidebarOpened,
|
||||
[classes.drawerClose]: !isSidebarOpened,
|
||||
}),
|
||||
}}
|
||||
open={isSidebarOpened}
|
||||
>
|
||||
<div className={classes.toolbar} />
|
||||
<div className={classes.mobileBackButton}>
|
||||
<IconButton onClick={() => toggleSidebar(layoutDispatch)}>
|
||||
<ArrowBackIcon
|
||||
classes={{
|
||||
root: classNames(classes.headerIcon, classes.headerIconCollapse),
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
<List className={classes.sidebarList}>
|
||||
{structure.map(link => (
|
||||
<SidebarLink
|
||||
key={link.id}
|
||||
location={location}
|
||||
isSidebarOpened={isSidebarOpened}
|
||||
{...link}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
// ##################################################################
|
||||
function handleWindowWidthChange() {
|
||||
var windowWidth = window.innerWidth;
|
||||
var breakpointWidth = theme.breakpoints.values.md;
|
||||
var isSmallScreen = windowWidth < breakpointWidth;
|
||||
|
||||
if (isSmallScreen && isPermanent) {
|
||||
setPermanent(false);
|
||||
} else if (!isSmallScreen && !isPermanent) {
|
||||
setPermanent(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Sidebar);
|
|
@ -0,0 +1,140 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Drawer,
|
||||
IconButton,
|
||||
List,
|
||||
withStyles } from "@material-ui/core";
|
||||
import {
|
||||
Home as HomeIcon,
|
||||
NotificationsNone as NotificationsIcon,
|
||||
FormatSize as TypographyIcon,
|
||||
FilterNone as UIElementsIcon,
|
||||
BorderAll as TableIcon,
|
||||
QuestionAnswer as SupportIcon,
|
||||
LibraryBooks as LibraryIcon,
|
||||
HelpOutline as FAQIcon,
|
||||
ArrowBack as ArrowBackIcon,
|
||||
} from "@material-ui/icons";
|
||||
import classNames from 'classnames';
|
||||
|
||||
import SidebarLink from './components/SidebarLink/SidebarLinkContainer';
|
||||
import Dot from './components/Dot';
|
||||
|
||||
const structure = [
|
||||
{ id: 0, label: 'Dashboard', link: '/app/dashboard', icon: <HomeIcon /> },
|
||||
{ id: 1, label: 'Typography', link: '/app/typography', icon: <TypographyIcon /> },
|
||||
{ id: 2, label: 'Tables', link: '/app/tables', icon: <TableIcon /> },
|
||||
{ id: 3, label: 'Notifications', link: '/app/notifications', icon: <NotificationsIcon />},
|
||||
{
|
||||
id: 4,
|
||||
label: 'UI Elements',
|
||||
link: '/app/ui',
|
||||
icon: <UIElementsIcon />,
|
||||
children: [
|
||||
{ label: 'Icons', link: '/app/ui/icons' },
|
||||
{ label: 'Charts', link: '/app/ui/charts' },
|
||||
{ label: 'Maps', link: '/app/ui/maps' },
|
||||
],
|
||||
},
|
||||
{ id: 5, type: 'divider' },
|
||||
{ id: 6, type: 'title', label: 'HELP' },
|
||||
{ id: 7, label: 'Library', link: 'https://flatlogic.com/templates', icon: <LibraryIcon /> },
|
||||
{ id: 8, label: 'Support', link: 'https://flatlogic.com/forum/', icon: <SupportIcon /> },
|
||||
{ id: 9, label: 'FAQ', link: 'https://flatlogic.com/forum/', icon: <FAQIcon />},
|
||||
{ id: 10, type: 'divider' },
|
||||
{ id: 11, type: 'title', label: 'PROJECTS' },
|
||||
{ id: 12, label: 'My recent', link: '', icon: <Dot size="small" color="secondary" /> },
|
||||
{ id: 13, label: 'Starred', link: '', icon: <Dot size="small" color="primary" /> },
|
||||
{ id: 14, label: 'Background', link: '', icon: <Dot size="small" color="secondary" /> },
|
||||
];
|
||||
|
||||
const SidebarView = ({ classes, theme, toggleSidebar, isSidebarOpened, isPermanent, location }) => {
|
||||
return (
|
||||
<Drawer
|
||||
variant={isPermanent ? 'permanent' : 'temporary'}
|
||||
className={classNames(classes.drawer, {
|
||||
[classes.drawerOpen]: isSidebarOpened,
|
||||
[classes.drawerClose]: !isSidebarOpened,
|
||||
})}
|
||||
classes={{
|
||||
paper: classNames(classes.drawer, {
|
||||
[classes.drawerOpen]: isSidebarOpened,
|
||||
[classes.drawerClose]: !isSidebarOpened,
|
||||
}),
|
||||
}}
|
||||
open={isSidebarOpened}
|
||||
>
|
||||
<div className={classes.mobileBackButton}>
|
||||
<IconButton
|
||||
onClick={toggleSidebar}
|
||||
>
|
||||
<ArrowBackIcon classes={{ root: classNames(classes.headerIcon, classes.headerIconCollapse) }} />
|
||||
</IconButton>
|
||||
</div>
|
||||
<List className={classes.sidebarList}>
|
||||
{structure.map(link => <SidebarLink key={link.id} location={location} isSidebarOpened={isSidebarOpened} {...link} />)}
|
||||
</List>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
const styles = theme => ({
|
||||
menuButton: {
|
||||
marginLeft: 12,
|
||||
marginRight: 36,
|
||||
},
|
||||
hide: {
|
||||
display: 'none',
|
||||
},
|
||||
drawer: {
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
whiteSpace: 'nowrap',
|
||||
top: theme.spacing.unit * 8,
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
top: 0,
|
||||
}
|
||||
},
|
||||
drawerOpen: {
|
||||
width: drawerWidth,
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
drawerClose: {
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
overflowX: 'hidden',
|
||||
width: theme.spacing.unit * 7 + 40,
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
width: drawerWidth,
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
...theme.mixins.toolbar,
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
display: 'none',
|
||||
}
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing.unit * 3,
|
||||
},
|
||||
mobileBackButton: {
|
||||
marginTop: theme.spacing.unit * .5,
|
||||
marginLeft: theme.spacing.unit * 3,
|
||||
[theme.breakpoints.only("sm")]: {
|
||||
marginTop: theme.spacing.unit * .625,
|
||||
},
|
||||
[theme.breakpoints.up("md")]: {
|
||||
display: 'none',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(SidebarView);
|
|
@ -0,0 +1,40 @@
|
|||
import React from "react";
|
||||
import { makeStyles, useTheme } from "@material-ui/styles";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
var useStyles = makeStyles(theme => ({
|
||||
dotBase: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
backgroundColor: theme.palette.text.hint,
|
||||
borderRadius: "50%",
|
||||
transition: theme.transitions.create("background-color"),
|
||||
},
|
||||
dotSmall: {
|
||||
width: 5,
|
||||
height: 5
|
||||
},
|
||||
dotLarge: {
|
||||
width: 11,
|
||||
height: 11,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Dot({ size, color }) {
|
||||
var classes = useStyles();
|
||||
var theme = useTheme();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(classes.dotBase, {
|
||||
[classes.dotLarge]: size === "large",
|
||||
[classes.dotSmall]: size === "small",
|
||||
})}
|
||||
style={{
|
||||
backgroundColor:
|
||||
color && theme.palette[color] && theme.palette[color].main,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
Collapse,
|
||||
Divider,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import { Inbox as InboxIcon } from "@material-ui/icons";
|
||||
import { Link } from "react-router-dom";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import Dot from "../Dot";
|
||||
|
||||
export default function SidebarLink({
|
||||
link,
|
||||
icon,
|
||||
label,
|
||||
children,
|
||||
location,
|
||||
isSidebarOpened,
|
||||
nested,
|
||||
type,
|
||||
}) {
|
||||
var classes = useStyles();
|
||||
|
||||
// local
|
||||
var [isOpen, setIsOpen] = useState(false);
|
||||
var isLinkActive =
|
||||
link &&
|
||||
(location.pathname === link || location.pathname.indexOf(link) !== -1);
|
||||
|
||||
if (type === "title")
|
||||
return (
|
||||
<Typography
|
||||
className={classnames(classes.linkText, classes.sectionTitle, {
|
||||
[classes.linkTextHidden]: !isSidebarOpened,
|
||||
})}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
if (type === "divider") return <Divider className={classes.divider} />;
|
||||
if (link && link.includes('http')) {
|
||||
return (
|
||||
<ListItem
|
||||
button
|
||||
className={classes.link}
|
||||
classes={{
|
||||
root: classnames(classes.linkRoot, {
|
||||
[classes.linkActive]: isLinkActive && !nested,
|
||||
[classes.linkNested]: nested,
|
||||
}),
|
||||
}}
|
||||
disableRipple
|
||||
>
|
||||
<a className={classes.externalLink} href={link}>
|
||||
<ListItemIcon
|
||||
className={classnames(classes.linkIcon, {
|
||||
[classes.linkIconActive]: isLinkActive,
|
||||
})}
|
||||
>
|
||||
{nested ? <Dot color={isLinkActive && "primary"} /> : icon}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
classes={{
|
||||
primary: classnames(classes.linkText, {
|
||||
[classes.linkTextActive]: isLinkActive,
|
||||
[classes.linkTextHidden]: !isSidebarOpened,
|
||||
}),
|
||||
}}
|
||||
primary={label}
|
||||
/>
|
||||
</a>
|
||||
</ListItem>
|
||||
)
|
||||
}
|
||||
if (!children)
|
||||
return (
|
||||
<ListItem
|
||||
button
|
||||
component={link && Link}
|
||||
to={link}
|
||||
className={classes.link}
|
||||
classes={{
|
||||
root: classnames(classes.linkRoot, {
|
||||
[classes.linkActive]: isLinkActive && !nested,
|
||||
[classes.linkNested]: nested,
|
||||
}),
|
||||
}}
|
||||
disableRipple
|
||||
>
|
||||
<ListItemIcon
|
||||
className={classnames(classes.linkIcon, {
|
||||
[classes.linkIconActive]: isLinkActive,
|
||||
})}
|
||||
>
|
||||
{nested ? <Dot color={isLinkActive && "primary"} /> : icon}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
classes={{
|
||||
primary: classnames(classes.linkText, {
|
||||
[classes.linkTextActive]: isLinkActive,
|
||||
[classes.linkTextHidden]: !isSidebarOpened,
|
||||
}),
|
||||
}}
|
||||
primary={label}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListItem
|
||||
button
|
||||
component={link && Link}
|
||||
onClick={toggleCollapse}
|
||||
className={classes.link}
|
||||
to={link}
|
||||
disableRipple
|
||||
>
|
||||
<ListItemIcon
|
||||
className={classnames(classes.linkIcon, {
|
||||
[classes.linkIconActive]: isLinkActive,
|
||||
})}
|
||||
>
|
||||
{icon ? icon : <InboxIcon />}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
classes={{
|
||||
primary: classnames(classes.linkText, {
|
||||
[classes.linkTextActive]: isLinkActive,
|
||||
[classes.linkTextHidden]: !isSidebarOpened,
|
||||
}),
|
||||
}}
|
||||
primary={label}
|
||||
/>
|
||||
</ListItem>
|
||||
{children && (
|
||||
<Collapse
|
||||
in={isOpen && isSidebarOpened}
|
||||
timeout="auto"
|
||||
unmountOnExit
|
||||
className={classes.nestedList}
|
||||
>
|
||||
<List component="div" disablePadding>
|
||||
{children.map(childrenLink => (
|
||||
<SidebarLink
|
||||
key={childrenLink && childrenLink.link}
|
||||
location={location}
|
||||
isSidebarOpened={isSidebarOpened}
|
||||
classes={classes}
|
||||
nested
|
||||
{...childrenLink}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
// ###########################################################
|
||||
|
||||
function toggleCollapse(e) {
|
||||
if (isSidebarOpened) {
|
||||
e.preventDefault();
|
||||
setIsOpen(!isOpen);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
link: {
|
||||
textDecoration: "none",
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
},
|
||||
},
|
||||
externalLink: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
textDecoration: 'none'
|
||||
},
|
||||
linkActive: {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
},
|
||||
linkNested: {
|
||||
paddingLeft: 0,
|
||||
"&:hover, &:focus": {
|
||||
backgroundColor: "#FFFFFF",
|
||||
},
|
||||
},
|
||||
linkIcon: {
|
||||
marginRight: theme.spacing(1),
|
||||
color: theme.palette.text.secondary + "99",
|
||||
transition: theme.transitions.create("color"),
|
||||
width: 24,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
},
|
||||
linkIconActive: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
linkText: {
|
||||
padding: 0,
|
||||
color: theme.palette.text.secondary + "CC",
|
||||
transition: theme.transitions.create(["opacity", "color"]),
|
||||
fontSize: 16,
|
||||
},
|
||||
linkTextActive: {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
linkTextHidden: {
|
||||
opacity: 0,
|
||||
},
|
||||
nestedList: {
|
||||
paddingLeft: theme.spacing(2) + 30,
|
||||
},
|
||||
sectionTitle: {
|
||||
marginLeft: theme.spacing(4.5),
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
divider: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(4),
|
||||
height: 1,
|
||||
backgroundColor: "#D8D8D880",
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Sidebar",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "Sidebar.js"
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
menuButton: {
|
||||
marginLeft: 12,
|
||||
marginRight: 36,
|
||||
},
|
||||
hide: {
|
||||
display: "none",
|
||||
},
|
||||
drawer: {
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
drawerOpen: {
|
||||
width: drawerWidth,
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
},
|
||||
drawerClose: {
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
overflowX: "hidden",
|
||||
width: theme.spacing(7) + 40,
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
width: drawerWidth,
|
||||
},
|
||||
},
|
||||
toolbar: {
|
||||
...theme.mixins.toolbar,
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing(3),
|
||||
},
|
||||
/* sidebarList: {
|
||||
marginTop: theme.spacing(6),
|
||||
}, */
|
||||
mobileBackButton: {
|
||||
marginTop: theme.spacing(0.5),
|
||||
marginLeft: 18,
|
||||
[theme.breakpoints.only("sm")]: {
|
||||
marginTop: theme.spacing(0.625),
|
||||
},
|
||||
[theme.breakpoints.up("md")]: {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,27 @@
|
|||
import React from "react";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import { Typography } from "../Wrappers";
|
||||
|
||||
export default function UserAvatar({ color = "primary", ...props }) {
|
||||
var classes = useStyles();
|
||||
var theme = useTheme();
|
||||
|
||||
var letters = props.name
|
||||
.split(" ")
|
||||
.map(word => word[0])
|
||||
.join("");
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes.avatar}
|
||||
style={{ backgroundColor: theme.palette[color].main }}
|
||||
>
|
||||
<Typography className={classes.text}>{letters}</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "UserAvatar",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "UserAvatar.js"
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(() => ({
|
||||
avatar: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "50%",
|
||||
},
|
||||
text: {
|
||||
color: "white",
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,96 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
Paper,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import { MoreVert as MoreIcon } from "@material-ui/icons";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
export default function Widget({
|
||||
children,
|
||||
title,
|
||||
noBodyPadding,
|
||||
bodyClass,
|
||||
disableWidgetMenu,
|
||||
header,
|
||||
noHeaderPadding,
|
||||
headerClass,
|
||||
style,
|
||||
noWidgetShadow,
|
||||
...props
|
||||
}) {
|
||||
var classes = useStyles();
|
||||
|
||||
// local
|
||||
var [moreButtonRef, setMoreButtonRef] = useState(null);
|
||||
var [isMoreMenuOpen, setMoreMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={classes.widgetWrapper} style={style && {...style}}>
|
||||
<Paper className={classes.paper} classes={{ root: classnames(classes.widgetRoot, {
|
||||
[classes.noWidgetShadow]: noWidgetShadow
|
||||
}) }}>
|
||||
<div className={classnames(classes.widgetHeader, {
|
||||
[classes.noPadding]: noHeaderPadding,
|
||||
[headerClass]: headerClass
|
||||
})}>
|
||||
{header ? (
|
||||
header
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Typography variant="h5" color="textSecondary" noWrap>
|
||||
{title}
|
||||
</Typography>
|
||||
{!disableWidgetMenu && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
classes={{ root: classes.moreButton }}
|
||||
aria-owns="widget-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={() => setMoreMenuOpen(true)}
|
||||
buttonRef={setMoreButtonRef}
|
||||
>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classnames(classes.widgetBody, {
|
||||
[classes.noPadding]: noBodyPadding,
|
||||
[bodyClass]: bodyClass,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Paper>
|
||||
<Menu
|
||||
id="widget-menu"
|
||||
open={isMoreMenuOpen}
|
||||
anchorEl={moreButtonRef}
|
||||
onClose={() => setMoreMenuOpen(false)}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
<MenuItem>
|
||||
<Typography>Edit</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Typography>Copy</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Typography>Delete</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Typography>Print</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
import React from "react";
|
||||
import classnames from "classnames";
|
||||
import {
|
||||
Paper,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
withStyles
|
||||
} from "@material-ui/core";
|
||||
import { MoreVert as MoreIcon } from "@material-ui/icons";
|
||||
import Typography from "@material-ui/core/es/Typography/Typography";
|
||||
|
||||
const Widget = ({
|
||||
classes,
|
||||
children,
|
||||
title,
|
||||
noBodyPadding,
|
||||
bodyClass,
|
||||
className,
|
||||
disableWidgetMenu,
|
||||
...props
|
||||
}) => (
|
||||
<div className={classes.widgetWrapper}>
|
||||
<Paper className={classes.paper} classes={{ root: classes.widgetRoot }}>
|
||||
<div className={classes.widgetHeader}>
|
||||
{props.header ? (
|
||||
props.header
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Typography variant="h5" color="textSecondary">
|
||||
{title}
|
||||
</Typography>
|
||||
{!disableWidgetMenu && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
classes={{ root: classes.moreButton }}
|
||||
aria-owns="widget-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={() => props.setMoreMenuOpen(true)}
|
||||
buttonRef={props.setMoreButtonRef}
|
||||
>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classnames(classes.widgetBody, {
|
||||
[classes.noPadding]: noBodyPadding,
|
||||
[bodyClass]: bodyClass
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Paper>
|
||||
<Menu
|
||||
id="widget-menu"
|
||||
open={props.isMoreMenuOpen}
|
||||
anchorEl={props.moreButtonRef}
|
||||
onClose={() => props.setMoreMenuOpen(false)}
|
||||
disableAutoFocusItem
|
||||
>
|
||||
<MenuItem>
|
||||
<Typography>Edit</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Typography>Copy</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Typography>Delete</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Typography>Print</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
const styles = theme => ({
|
||||
widgetWrapper: {
|
||||
display: "flex",
|
||||
minHeight: "100%"
|
||||
},
|
||||
widgetHeader: {
|
||||
padding: theme.spacing.unit * 3,
|
||||
paddingBottom: theme.spacing.unit,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
},
|
||||
widgetRoot: {
|
||||
boxShadow: theme.customShadows.widget
|
||||
},
|
||||
widgetBody: {
|
||||
paddingBottom: theme.spacing.unit * 3,
|
||||
paddingRight: theme.spacing.unit * 3,
|
||||
paddingLeft: theme.spacing.unit * 3
|
||||
},
|
||||
noPadding: {
|
||||
padding: 0
|
||||
},
|
||||
paper: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
overflow: "hidden"
|
||||
},
|
||||
moreButton: {
|
||||
margin: -theme.spacing.unit,
|
||||
padding: 0,
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: theme.palette.text.hint,
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: "rgba(255, 255, 255, 0.35)"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(Widget);
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Widget",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "Widget.js"
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
widgetWrapper: {
|
||||
display: "flex",
|
||||
minHeight: "100%",
|
||||
},
|
||||
widgetHeader: {
|
||||
padding: theme.spacing(3),
|
||||
paddingBottom: theme.spacing(1),
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
},
|
||||
widgetRoot: {
|
||||
boxShadow: theme.customShadows.widget,
|
||||
},
|
||||
widgetBody: {
|
||||
paddingBottom: theme.spacing(3),
|
||||
paddingRight: theme.spacing(3),
|
||||
paddingLeft: theme.spacing(3),
|
||||
},
|
||||
noPadding: {
|
||||
padding: 0,
|
||||
},
|
||||
paper: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
overflow: "auto",
|
||||
},
|
||||
moreButton: {
|
||||
margin: -theme.spacing(1),
|
||||
padding: 0,
|
||||
width: 40,
|
||||
height: 40,
|
||||
color: theme.palette.text.hint,
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: "rgba(255, 255, 255, 0.35)",
|
||||
},
|
||||
},
|
||||
noWidgetShadow: {
|
||||
boxShadow: 'none'
|
||||
}
|
||||
}));
|
|
@ -0,0 +1,181 @@
|
|||
import React from "react";
|
||||
import {
|
||||
withStyles,
|
||||
Badge as BadgeBase,
|
||||
Typography as TypographyBase,
|
||||
Button as ButtonBase,
|
||||
} from "@material-ui/core";
|
||||
import { useTheme, makeStyles } from "@material-ui/styles";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
var useStyles = makeStyles(theme => ({
|
||||
badge: {
|
||||
fontWeight: 600,
|
||||
height: 16,
|
||||
minWidth: 16,
|
||||
},
|
||||
}));
|
||||
|
||||
function Badge({ children, colorBrightness, color, ...props }) {
|
||||
var classes = useStyles();
|
||||
var theme = useTheme();
|
||||
var Styled = createStyled({
|
||||
badge: {
|
||||
backgroundColor: getColor(color, theme, colorBrightness),
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
{styledProps => (
|
||||
<BadgeBase
|
||||
classes={{
|
||||
badge: classnames(classes.badge, styledProps.classes.badge),
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</BadgeBase>
|
||||
)}
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
|
||||
function Typography({
|
||||
children,
|
||||
weight,
|
||||
size,
|
||||
colorBrightness,
|
||||
color,
|
||||
...props
|
||||
}) {
|
||||
var theme = useTheme();
|
||||
|
||||
return (
|
||||
<TypographyBase
|
||||
style={{
|
||||
color: getColor(color, theme, colorBrightness),
|
||||
fontWeight: getFontWeight(weight),
|
||||
fontSize: getFontSize(size, props.variant, theme),
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</TypographyBase>
|
||||
);
|
||||
}
|
||||
|
||||
function Button({ children, color, className, ...props }) {
|
||||
var theme = useTheme();
|
||||
|
||||
var Styled = createStyled({
|
||||
root: {
|
||||
color: getColor(color, theme),
|
||||
},
|
||||
contained: {
|
||||
backgroundColor: getColor(color, theme),
|
||||
boxShadow: theme.customShadows.widget,
|
||||
color: `${color ? "white" : theme.palette.text.primary} !important`,
|
||||
"&:hover": {
|
||||
backgroundColor: getColor(color, theme, "light"),
|
||||
boxShadow: theme.customShadows.widgetWide,
|
||||
},
|
||||
"&:active": {
|
||||
boxShadow: theme.customShadows.widgetWide,
|
||||
},
|
||||
},
|
||||
outlined: {
|
||||
color: getColor(color, theme),
|
||||
borderColor: getColor(color, theme),
|
||||
},
|
||||
select: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: "#fff",
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Styled>
|
||||
{({ classes }) => (
|
||||
<ButtonBase
|
||||
classes={{
|
||||
contained: classes.contained,
|
||||
root: classes.root,
|
||||
outlined: classes.outlined,
|
||||
}}
|
||||
{...props}
|
||||
className={classnames(
|
||||
{
|
||||
[classes.select]: props.select,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</ButtonBase>
|
||||
)}
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
|
||||
export { Badge, Typography, Button };
|
||||
|
||||
// ########################################################################
|
||||
|
||||
function getColor(color, theme, brigtness = "main") {
|
||||
if (color && theme.palette[color] && theme.palette[color][brigtness]) {
|
||||
return theme.palette[color][brigtness];
|
||||
}
|
||||
}
|
||||
|
||||
function getFontWeight(style) {
|
||||
switch (style) {
|
||||
case "light":
|
||||
return 300;
|
||||
case "medium":
|
||||
return 500;
|
||||
case "bold":
|
||||
return 600;
|
||||
default:
|
||||
return 400;
|
||||
}
|
||||
}
|
||||
|
||||
function getFontSize(size, variant = "", theme) {
|
||||
var multiplier;
|
||||
|
||||
switch (size) {
|
||||
case "sm":
|
||||
multiplier = 0.8;
|
||||
break;
|
||||
case "md":
|
||||
multiplier = 1.5;
|
||||
break;
|
||||
case "xl":
|
||||
multiplier = 2;
|
||||
break;
|
||||
case "xxl":
|
||||
multiplier = 3;
|
||||
break;
|
||||
default:
|
||||
multiplier = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
var defaultSize =
|
||||
variant && theme.typography[variant]
|
||||
? theme.typography[variant].fontSize
|
||||
: theme.typography.fontSize + "px";
|
||||
|
||||
return `calc(${defaultSize} * ${multiplier})`;
|
||||
}
|
||||
|
||||
function createStyled(styles, options) {
|
||||
var Styled = function(props) {
|
||||
const { children, ...other } = props;
|
||||
return children(other);
|
||||
};
|
||||
|
||||
return withStyles(styles, options)(Styled);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "Wrappers",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "Wrappers.js"
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import React from "react";
|
||||
|
||||
var LayoutStateContext = React.createContext();
|
||||
var LayoutDispatchContext = React.createContext();
|
||||
|
||||
function layoutReducer(state, action) {
|
||||
switch (action.type) {
|
||||
case "TOGGLE_SIDEBAR":
|
||||
return { ...state, isSidebarOpened: !state.isSidebarOpened };
|
||||
default: {
|
||||
throw new Error(`Unhandled action type: ${action.type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function LayoutProvider({ children }) {
|
||||
var [state, dispatch] = React.useReducer(layoutReducer, {
|
||||
isSidebarOpened: true,
|
||||
});
|
||||
return (
|
||||
<LayoutStateContext.Provider value={state}>
|
||||
<LayoutDispatchContext.Provider value={dispatch}>
|
||||
{children}
|
||||
</LayoutDispatchContext.Provider>
|
||||
</LayoutStateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function useLayoutState() {
|
||||
var context = React.useContext(LayoutStateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useLayoutState must be used within a LayoutProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
function useLayoutDispatch() {
|
||||
var context = React.useContext(LayoutDispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useLayoutDispatch must be used within a LayoutProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
export { LayoutProvider, useLayoutState, useLayoutDispatch, toggleSidebar };
|
||||
|
||||
// ###########################################################
|
||||
function toggleSidebar(dispatch) {
|
||||
dispatch({
|
||||
type: "TOGGLE_SIDEBAR",
|
||||
});
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import React from "react";
|
||||
|
||||
var UserStateContext = React.createContext();
|
||||
var UserDispatchContext = React.createContext();
|
||||
|
||||
function userReducer(state, action) {
|
||||
switch (action.type) {
|
||||
case "LOGIN_SUCCESS":
|
||||
return { ...state, isAuthenticated: true };
|
||||
case "SIGN_OUT_SUCCESS":
|
||||
return { ...state, isAuthenticated: false };
|
||||
default: {
|
||||
throw new Error(`Unhandled action type: ${action.type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function UserProvider({ children }) {
|
||||
var [state, dispatch] = React.useReducer(userReducer, {
|
||||
isAuthenticated: !!localStorage.getItem("id_token"),
|
||||
});
|
||||
|
||||
return (
|
||||
<UserStateContext.Provider value={state}>
|
||||
<UserDispatchContext.Provider value={dispatch}>
|
||||
{children}
|
||||
</UserDispatchContext.Provider>
|
||||
</UserStateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function useUserState() {
|
||||
var context = React.useContext(UserStateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useUserState must be used within a UserProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
function useUserDispatch() {
|
||||
var context = React.useContext(UserDispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useUserDispatch must be used within a UserProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
export { UserProvider, useUserState, useUserDispatch, loginUser, signOut };
|
||||
|
||||
// ###########################################################
|
||||
|
||||
function loginUser(dispatch, login, password, history, setIsLoading, setError) {
|
||||
setError(false);
|
||||
setIsLoading(true);
|
||||
|
||||
if (!!login && !!password) {
|
||||
setTimeout(() => {
|
||||
localStorage.setItem('id_token', 1)
|
||||
setError(null)
|
||||
setIsLoading(false)
|
||||
dispatch({ type: 'LOGIN_SUCCESS' })
|
||||
|
||||
history.push('/app/dashboard')
|
||||
}, 2000);
|
||||
} else {
|
||||
dispatch({ type: "LOGIN_FAILURE" });
|
||||
setError(true);
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function signOut(dispatch, history) {
|
||||
localStorage.removeItem("id_token");
|
||||
dispatch({ type: "SIGN_OUT_SUCCESS" });
|
||||
history.push("/login");
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path style="fill:#FBBB00;" d="M113.47,309.408L95.648,375.94l-65.139,1.378C11.042,341.211,0,299.9,0,256
|
||||
c0-42.451,10.324-82.483,28.624-117.732h0.014l57.992,10.632l25.404,57.644c-5.317,15.501-8.215,32.141-8.215,49.456
|
||||
C103.821,274.792,107.225,292.797,113.47,309.408z"/>
|
||||
<path style="fill:#518EF8;" d="M507.527,208.176C510.467,223.662,512,239.655,512,256c0,18.328-1.927,36.206-5.598,53.451
|
||||
c-12.462,58.683-45.025,109.925-90.134,146.187l-0.014-0.014l-73.044-3.727l-10.338-64.535
|
||||
c29.932-17.554,53.324-45.025,65.646-77.911h-136.89V208.176h138.887L507.527,208.176L507.527,208.176z"/>
|
||||
<path style="fill:#28B446;" d="M416.253,455.624l0.014,0.014C372.396,490.901,316.666,512,256,512
|
||||
c-97.491,0-182.252-54.491-225.491-134.681l82.961-67.91c21.619,57.698,77.278,98.771,142.53,98.771
|
||||
c28.047,0,54.323-7.582,76.87-20.818L416.253,455.624z"/>
|
||||
<path style="fill:#F14336;" d="M419.404,58.936l-82.933,67.896c-23.335-14.586-50.919-23.012-80.471-23.012
|
||||
c-66.729,0-123.429,42.957-143.965,102.724l-83.397-68.276h-0.014C71.23,56.123,157.06,0,256,0
|
||||
C318.115,0,375.068,22.126,419.404,58.936z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,27 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { ThemeProvider } from "@material-ui/styles";
|
||||
import { CssBaseline } from "@material-ui/core";
|
||||
|
||||
import Themes from "./themes";
|
||||
import App from "./components/App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
import { LayoutProvider } from "./context/LayoutContext";
|
||||
import { UserProvider } from "./context/UserContext";
|
||||
|
||||
ReactDOM.render(
|
||||
<LayoutProvider>
|
||||
<UserProvider>
|
||||
<ThemeProvider theme={Themes.default}>
|
||||
<CssBaseline />
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</UserProvider>
|
||||
</LayoutProvider>,
|
||||
document.getElementById("root"),
|
||||
);
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: http://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
|
@ -0,0 +1,234 @@
|
|||
import React, { useState } from "react";
|
||||
import { Button, Grid } from "@material-ui/core";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
import {
|
||||
CartesianGrid,
|
||||
Legend,
|
||||
Line,
|
||||
LineChart,
|
||||
Pie,
|
||||
PieChart,
|
||||
ResponsiveContainer,
|
||||
Sector,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
|
||||
// components
|
||||
import Widget from "../../components/Widget/Widget";
|
||||
import ApexLineChart from "./components/ApexLineChart";
|
||||
import ApexHeatmap from "./components/ApexHeatmap";
|
||||
import PageTitle from "../../components/PageTitle/PageTitle";
|
||||
|
||||
const lineChartData = [
|
||||
{
|
||||
name: "Page A",
|
||||
uv: 4000,
|
||||
pv: 2400,
|
||||
amt: 2400,
|
||||
},
|
||||
{
|
||||
name: "Page B",
|
||||
uv: 3000,
|
||||
pv: 1398,
|
||||
amt: 2210,
|
||||
},
|
||||
{
|
||||
name: "Page C",
|
||||
uv: 2000,
|
||||
pv: 9800,
|
||||
amt: 2290,
|
||||
},
|
||||
{
|
||||
name: "Page D",
|
||||
uv: 2780,
|
||||
pv: 3908,
|
||||
amt: 2000,
|
||||
},
|
||||
{
|
||||
name: "Page E",
|
||||
uv: 1890,
|
||||
pv: 4800,
|
||||
amt: 2181,
|
||||
},
|
||||
{
|
||||
name: "Page F",
|
||||
uv: 2390,
|
||||
pv: 3800,
|
||||
amt: 2500,
|
||||
},
|
||||
{
|
||||
name: "Page G",
|
||||
uv: 3490,
|
||||
pv: 4300,
|
||||
amt: 2100,
|
||||
},
|
||||
];
|
||||
|
||||
const pieChartData = [
|
||||
{ name: "Group A", value: 400 },
|
||||
{ name: "Group B", value: 300 },
|
||||
{ name: "Group C", value: 300 },
|
||||
{ name: "Group D", value: 200 },
|
||||
];
|
||||
|
||||
export default function Charts(props) {
|
||||
var theme = useTheme();
|
||||
|
||||
// local
|
||||
var [activeIndex, setActiveIndexId] = useState(0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title="Charts Page - Data Display" button={
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
color="secondary"
|
||||
>
|
||||
Latest Reports
|
||||
</Button>
|
||||
} />
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Widget title="Apex Line Chart" upperTitle noBodyPadding>
|
||||
<ApexLineChart />
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Widget title="Apex Heatmap" upperTitle noBodyPadding>
|
||||
<ApexHeatmap />
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={8}>
|
||||
<Widget title="Simple Line Chart" noBodyPadding upperTitle>
|
||||
<ResponsiveContainer width="100%" height={350}>
|
||||
<LineChart
|
||||
width={500}
|
||||
height={300}
|
||||
data={lineChartData}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 20,
|
||||
bottom: 5,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="pv"
|
||||
stroke={theme.palette.primary.main}
|
||||
activeDot={{ r: 8 }}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="uv"
|
||||
stroke={theme.palette.secondary.main}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<Widget title="Pie Chart with Tooltips" noBodyPadding upperTitle>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<PieChart width={200} height={300}>
|
||||
<Pie
|
||||
activeIndex={activeIndex}
|
||||
activeShape={renderActiveShape}
|
||||
data={pieChartData}
|
||||
innerRadius={60}
|
||||
outerRadius={80}
|
||||
fill={theme.palette.primary.main}
|
||||
dataKey="value"
|
||||
onMouseEnter={(e, id) => setActiveIndexId(id)}
|
||||
/>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</Widget>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// ################################################################
|
||||
|
||||
function renderActiveShape(props) {
|
||||
var RADIAN = Math.PI / 180;
|
||||
var {
|
||||
cx,
|
||||
cy,
|
||||
midAngle,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
fill,
|
||||
payload,
|
||||
percent,
|
||||
value,
|
||||
} = props;
|
||||
var sin = Math.sin(-RADIAN * midAngle);
|
||||
var cos = Math.cos(-RADIAN * midAngle);
|
||||
var sx = cx + (outerRadius + 10) * cos;
|
||||
var sy = cy + (outerRadius + 10) * sin;
|
||||
var mx = cx + (outerRadius + 30) * cos;
|
||||
var my = cy + (outerRadius + 30) * sin;
|
||||
var ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||
var ey = my;
|
||||
var textAnchor = cos >= 0 ? "start" : "end";
|
||||
|
||||
return (
|
||||
<g>
|
||||
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>
|
||||
{payload.name}
|
||||
</text>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
innerRadius={innerRadius}
|
||||
outerRadius={outerRadius}
|
||||
startAngle={startAngle}
|
||||
endAngle={endAngle}
|
||||
fill={fill}
|
||||
/>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
startAngle={startAngle}
|
||||
endAngle={endAngle}
|
||||
innerRadius={outerRadius + 6}
|
||||
outerRadius={outerRadius + 10}
|
||||
fill={fill}
|
||||
/>
|
||||
<path
|
||||
d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
|
||||
stroke={fill}
|
||||
fill="none"
|
||||
/>
|
||||
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" />
|
||||
<text
|
||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||
y={ey}
|
||||
textAnchor={textAnchor}
|
||||
fill="#333"
|
||||
>{`PV ${value}`}</text>
|
||||
<text
|
||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||
y={ey}
|
||||
dy={18}
|
||||
textAnchor={textAnchor}
|
||||
fill="#999"
|
||||
>
|
||||
{`(Rate ${(percent * 100).toFixed(2)}%)`}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
import React from "react";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
import ApexCharts from "react-apexcharts";
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: "Metric1",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric2",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric3",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric4",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric5",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric6",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric7",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric8",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Metric9",
|
||||
data: generateData(18, {
|
||||
min: 0,
|
||||
max: 90,
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
export default function ApexLineChart() {
|
||||
var theme = useTheme();
|
||||
|
||||
return (
|
||||
<ApexCharts
|
||||
options={themeOptions(theme)}
|
||||
series={series}
|
||||
type="heatmap"
|
||||
height={350}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// ##################################################################
|
||||
function generateData(count, yrange) {
|
||||
var i = 0;
|
||||
var series = [];
|
||||
while (i < count) {
|
||||
var x = "w" + (i + 1).toString();
|
||||
var y =
|
||||
Math.floor(Math.random() * (yrange.max - yrange.min + 1)) + yrange.min;
|
||||
|
||||
series.push({
|
||||
x: x,
|
||||
y: y,
|
||||
});
|
||||
i++;
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
function themeOptions(theme) {
|
||||
return {
|
||||
chart: {
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
colors: [theme.palette.primary.main],
|
||||
};
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import React from "react";
|
||||
import ApexCharts from "react-apexcharts";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: "series1",
|
||||
data: [31, 40, 28, 51, 42, 109, 100],
|
||||
},
|
||||
{
|
||||
name: "series2",
|
||||
data: [11, 32, 45, 32, 34, 52, 41],
|
||||
},
|
||||
];
|
||||
|
||||
export default function ApexLineChart() {
|
||||
var theme = useTheme();
|
||||
|
||||
return (
|
||||
<ApexCharts
|
||||
options={themeOptions(theme)}
|
||||
series={series}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// ############################################################
|
||||
function themeOptions(theme) {
|
||||
return {
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
},
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
categories: [
|
||||
"2018-09-19T00:00:00",
|
||||
"2018-09-19T01:30:00",
|
||||
"2018-09-19T02:30:00",
|
||||
"2018-09-19T03:30:00",
|
||||
"2018-09-19T04:30:00",
|
||||
"2018-09-19T05:30:00",
|
||||
"2018-09-19T06:30:00",
|
||||
],
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: "dd/MM/yy HH:mm",
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
colors: [theme.palette.primary.light, theme.palette.success.light],
|
||||
},
|
||||
colors: [theme.palette.primary.main, theme.palette.success.main],
|
||||
chart: {
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Charts",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "Charts.js"
|
||||
}
|
|
@ -0,0 +1,467 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
Grid,
|
||||
LinearProgress,
|
||||
Select,
|
||||
OutlinedInput,
|
||||
MenuItem,
|
||||
Button
|
||||
} from "@material-ui/core";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
ComposedChart,
|
||||
AreaChart,
|
||||
LineChart,
|
||||
Line,
|
||||
Area,
|
||||
PieChart,
|
||||
Pie,
|
||||
Cell,
|
||||
YAxis,
|
||||
XAxis,
|
||||
} from "recharts";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import mock from "./mock";
|
||||
import Widget from "../../components/Widget";
|
||||
import PageTitle from "../../components/PageTitle";
|
||||
import { Typography } from "../../components/Wrappers";
|
||||
import Dot from "../../components/Sidebar/components/Dot";
|
||||
import Table from "./components/Table/Table";
|
||||
import BigStat from "./components/BigStat/BigStat";
|
||||
|
||||
const mainChartData = getMainChartData();
|
||||
const PieChartData = [
|
||||
{ name: "Group A", value: 400, color: "primary" },
|
||||
{ name: "Group B", value: 300, color: "secondary" },
|
||||
{ name: "Group C", value: 300, color: "warning" },
|
||||
{ name: "Group D", value: 200, color: "success" },
|
||||
];
|
||||
|
||||
export default function Dashboard(props) {
|
||||
var classes = useStyles();
|
||||
var theme = useTheme();
|
||||
|
||||
// local
|
||||
var [mainChartState, setMainChartState] = useState("monthly");
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title="Dashboard" button={<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
color="secondary"
|
||||
>
|
||||
Latest Reports
|
||||
</Button>} />
|
||||
<Grid container spacing={4}>
|
||||
<Grid item lg={3} md={4} sm={6} xs={12}>
|
||||
<Widget
|
||||
title="Visits Today"
|
||||
upperTitle
|
||||
bodyClass={classes.fullHeightBody}
|
||||
className={classes.card}
|
||||
>
|
||||
<div className={classes.visitsNumberContainer}>
|
||||
<Grid container item alignItems={"center"}>
|
||||
<Grid item xs={6}>
|
||||
<Typography size="xl" weight="medium" noWrap>
|
||||
12, 678
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<LineChart
|
||||
width={100}
|
||||
height={30}
|
||||
data={[
|
||||
{ value: 10 },
|
||||
{ value: 15 },
|
||||
{ value: 10 },
|
||||
{ value: 17 },
|
||||
{ value: 18 },
|
||||
]}
|
||||
>
|
||||
<Line
|
||||
type="natural"
|
||||
dataKey="value"
|
||||
stroke={theme.palette.success.main}
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
/>
|
||||
</LineChart>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justify="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Grid item xs={4}>
|
||||
<Typography color="text" colorBrightness="secondary" noWrap>
|
||||
Registrations
|
||||
</Typography>
|
||||
<Typography size="md">860</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Typography color="text" colorBrightness="secondary" noWrap>
|
||||
Sign Out
|
||||
</Typography>
|
||||
<Typography size="md">32</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Typography color="text" colorBrightness="secondary" noWrap>
|
||||
Rate
|
||||
</Typography>
|
||||
<Typography size="md">3.25%</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item lg={3} md={8} sm={6} xs={12}>
|
||||
<Widget
|
||||
title="App Performance"
|
||||
upperTitle
|
||||
className={classes.card}
|
||||
bodyClass={classes.fullHeightBody}
|
||||
>
|
||||
<div className={classes.performanceLegendWrapper}>
|
||||
<div className={classes.legendElement}>
|
||||
<Dot color="warning" />
|
||||
<Typography
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classes.legendElementText}
|
||||
>
|
||||
Integration
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.legendElement}>
|
||||
<Dot color="primary" />
|
||||
<Typography
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classes.legendElementText}
|
||||
>
|
||||
SDK
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.progressSection}>
|
||||
<Typography
|
||||
size="md"
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classes.progressSectionTitle}
|
||||
>
|
||||
Integration
|
||||
</Typography>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={77}
|
||||
classes={{ barColorPrimary: classes.progressBarPrimary }}
|
||||
className={classes.progress}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Typography
|
||||
size="md"
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classes.progressSectionTitle}
|
||||
>
|
||||
SDK
|
||||
</Typography>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={73}
|
||||
classes={{ barColorPrimary: classes.progressBarWarning }}
|
||||
className={classes.progress}
|
||||
/>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item lg={3} md={8} sm={6} xs={12}>
|
||||
<Widget
|
||||
title="Server Overview"
|
||||
upperTitle
|
||||
className={classes.card}
|
||||
bodyClass={classes.fullHeightBody}
|
||||
>
|
||||
<div className={classes.serverOverviewElement}>
|
||||
<Typography
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classes.serverOverviewElementText}
|
||||
noWrap
|
||||
>
|
||||
60% / 37°С / 3.3 Ghz
|
||||
</Typography>
|
||||
<div className={classes.serverOverviewElementChartWrapper}>
|
||||
<ResponsiveContainer height={50} width="99%">
|
||||
<AreaChart data={getRandomData(10)}>
|
||||
<Area
|
||||
type="natural"
|
||||
dataKey="value"
|
||||
stroke={theme.palette.secondary.main}
|
||||
fill={theme.palette.secondary.light}
|
||||
strokeWidth={2}
|
||||
fillOpacity="0.25"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.serverOverviewElement}>
|
||||
<Typography
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classes.serverOverviewElementText}
|
||||
noWrap
|
||||
>
|
||||
54% / 31°С / 3.3 Ghz
|
||||
</Typography>
|
||||
<div className={classes.serverOverviewElementChartWrapper}>
|
||||
<ResponsiveContainer height={50} width="99%">
|
||||
<AreaChart data={getRandomData(10)}>
|
||||
<Area
|
||||
type="natural"
|
||||
dataKey="value"
|
||||
stroke={theme.palette.primary.main}
|
||||
fill={theme.palette.primary.light}
|
||||
strokeWidth={2}
|
||||
fillOpacity="0.25"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.serverOverviewElement}>
|
||||
<Typography
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classes.serverOverviewElementText}
|
||||
noWrap
|
||||
>
|
||||
57% / 21°С / 3.3 Ghz
|
||||
</Typography>
|
||||
<div className={classes.serverOverviewElementChartWrapper}>
|
||||
<ResponsiveContainer height={50} width="99%">
|
||||
<AreaChart data={getRandomData(10)}>
|
||||
<Area
|
||||
type="natural"
|
||||
dataKey="value"
|
||||
stroke={theme.palette.warning.main}
|
||||
fill={theme.palette.warning.light}
|
||||
strokeWidth={2}
|
||||
fillOpacity="0.25"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item lg={3} md={4} sm={6} xs={12}>
|
||||
<Widget title="Revenue Breakdown" upperTitle className={classes.card}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<ResponsiveContainer width="100%" height={144}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={PieChartData}
|
||||
innerRadius={30}
|
||||
outerRadius={40}
|
||||
dataKey="value"
|
||||
>
|
||||
{PieChartData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={theme.palette[entry.color].main}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<div className={classes.pieChartLegendWrapper}>
|
||||
{PieChartData.map(({ name, value, color }, index) => (
|
||||
<div key={color} className={classes.legendItemContainer}>
|
||||
<Dot color={color} />
|
||||
<Typography style={{ whiteSpace: "nowrap", fontSize: 12 }} >
|
||||
{name}
|
||||
</Typography>
|
||||
<Typography color="text" colorBrightness="secondary">
|
||||
{value}
|
||||
</Typography>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Widget
|
||||
bodyClass={classes.mainChartBody}
|
||||
header={
|
||||
<div className={classes.mainChartHeader}>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
>
|
||||
Daily Line Chart
|
||||
</Typography>
|
||||
<div className={classes.mainChartHeaderLabels}>
|
||||
<div className={classes.mainChartHeaderLabel}>
|
||||
<Dot color="warning" />
|
||||
<Typography className={classes.mainChartLegentElement}>
|
||||
Tablet
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.mainChartHeaderLabel}>
|
||||
<Dot color="primary" />
|
||||
<Typography className={classes.mainChartLegentElement}>
|
||||
Mobile
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.mainChartHeaderLabel}>
|
||||
<Dot color="secondary" />
|
||||
<Typography className={classes.mainChartLegentElement}>
|
||||
Desktop
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<Select
|
||||
value={mainChartState}
|
||||
onChange={e => setMainChartState(e.target.value)}
|
||||
input={
|
||||
<OutlinedInput
|
||||
labelWidth={0}
|
||||
classes={{
|
||||
notchedOutline: classes.mainChartSelectRoot,
|
||||
input: classes.mainChartSelect,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
autoWidth
|
||||
>
|
||||
<MenuItem value="daily">Daily</MenuItem>
|
||||
<MenuItem value="weekly">Weekly</MenuItem>
|
||||
<MenuItem value="monthly">Monthly</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<ResponsiveContainer width="100%" minWidth={500} height={350}>
|
||||
<ComposedChart
|
||||
margin={{ top: 0, right: -15, left: -15, bottom: 0 }}
|
||||
data={mainChartData}
|
||||
>
|
||||
<YAxis
|
||||
ticks={[0, 2500, 5000, 7500]}
|
||||
tick={{ fill: theme.palette.text.hint + "80", fontSize: 14 }}
|
||||
stroke={theme.palette.text.hint + "80"}
|
||||
tickLine={false}
|
||||
/>
|
||||
<XAxis
|
||||
tickFormatter={i => i + 1}
|
||||
tick={{ fill: theme.palette.text.hint + "80", fontSize: 14 }}
|
||||
stroke={theme.palette.text.hint + "80"}
|
||||
tickLine={false}
|
||||
/>
|
||||
<Area
|
||||
type="natural"
|
||||
dataKey="desktop"
|
||||
fill={theme.palette.background.light}
|
||||
strokeWidth={0}
|
||||
activeDot={false}
|
||||
/>
|
||||
<Line
|
||||
type="natural"
|
||||
dataKey="mobile"
|
||||
stroke={theme.palette.primary.main}
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
activeDot={false}
|
||||
/>
|
||||
<Line
|
||||
type="linear"
|
||||
dataKey="tablet"
|
||||
stroke={theme.palette.warning.main}
|
||||
strokeWidth={2}
|
||||
dot={{
|
||||
stroke: theme.palette.warning.dark,
|
||||
strokeWidth: 2,
|
||||
fill: theme.palette.warning.main,
|
||||
}}
|
||||
/>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</Widget>
|
||||
</Grid>
|
||||
{mock.bigStat.map(stat => (
|
||||
<Grid item md={4} sm={6} xs={12} key={stat.product}>
|
||||
<BigStat {...stat} />
|
||||
</Grid>
|
||||
))}
|
||||
<Grid item xs={12}>
|
||||
<Widget
|
||||
title="Support Requests"
|
||||
upperTitle
|
||||
noBodyPadding
|
||||
bodyClass={classes.tableWidget}
|
||||
>
|
||||
<Table data={mock.table} />
|
||||
</Widget>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// #######################################################################
|
||||
function getRandomData(length, min, max, multiplier = 10, maxDiff = 10) {
|
||||
var array = new Array(length).fill();
|
||||
let lastValue;
|
||||
|
||||
return array.map((item, index) => {
|
||||
let randomValue = Math.floor(Math.random() * multiplier + 1);
|
||||
|
||||
while (
|
||||
randomValue <= min ||
|
||||
randomValue >= max ||
|
||||
(lastValue && randomValue - lastValue > maxDiff)
|
||||
) {
|
||||
randomValue = Math.floor(Math.random() * multiplier + 1);
|
||||
}
|
||||
|
||||
lastValue = randomValue;
|
||||
|
||||
return { value: randomValue };
|
||||
});
|
||||
}
|
||||
|
||||
function getMainChartData() {
|
||||
var resultArray = [];
|
||||
var tablet = getRandomData(31, 3500, 6500, 7500, 1000);
|
||||
var desktop = getRandomData(31, 1500, 7500, 7500, 1500);
|
||||
var mobile = getRandomData(31, 1500, 7500, 7500, 1500);
|
||||
|
||||
for (let i = 0; i < tablet.length; i++) {
|
||||
resultArray.push({
|
||||
tablet: tablet[i].value,
|
||||
desktop: desktop[i].value,
|
||||
mobile: mobile[i].value,
|
||||
});
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
import React, { useState } from "react";
|
||||
import { Grid, Select, MenuItem, Input } from "@material-ui/core";
|
||||
import { ArrowForward as ArrowForwardIcon } from "@material-ui/icons";
|
||||
import { useTheme } from "@material-ui/styles";
|
||||
import { BarChart, Bar } from "recharts";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import Widget from "../../../../components/Widget";
|
||||
import { Typography } from "../../../../components/Wrappers";
|
||||
|
||||
export default function BigStat(props) {
|
||||
var { product, total, color, registrations, bounce } = props;
|
||||
var classes = useStyles();
|
||||
var theme = useTheme();
|
||||
|
||||
// local
|
||||
var [value, setValue] = useState("daily");
|
||||
|
||||
return (
|
||||
<Widget
|
||||
header={
|
||||
<div className={classes.title}>
|
||||
<Typography variant="h5">{product}</Typography>
|
||||
|
||||
<Select
|
||||
value={value}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
input={
|
||||
<Input
|
||||
disableUnderline
|
||||
classes={{ input: classes.selectInput }}
|
||||
/>
|
||||
}
|
||||
className={classes.select}
|
||||
>
|
||||
<MenuItem value="daily">Daily</MenuItem>
|
||||
<MenuItem value="weekly">Weekly</MenuItem>
|
||||
<MenuItem value="monthly">Monthly</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
}
|
||||
upperTitle
|
||||
bodyClass={classes.bodyWidgetOverflow}
|
||||
>
|
||||
<div className={classes.totalValueContainer}>
|
||||
<div className={classes.totalValue}>
|
||||
<Typography size="xxl" color="text" colorBrightness="secondary">
|
||||
{total[value]}
|
||||
</Typography>
|
||||
<Typography color={total.percent.profit ? "success" : "secondary"}>
|
||||
{total.percent.profit ? "+" : "-"}
|
||||
{total.percent.value}%
|
||||
</Typography>
|
||||
</div>
|
||||
<BarChart width={150} height={70} data={getRandomData()}>
|
||||
<Bar
|
||||
dataKey="value"
|
||||
fill={theme.palette[color].main}
|
||||
radius={10}
|
||||
barSize={10}
|
||||
/>
|
||||
</BarChart>
|
||||
</div>
|
||||
<div className={classes.bottomStatsContainer}>
|
||||
<div className={classnames(classes.statCell, classes.borderRight)}>
|
||||
<Grid container alignItems="center">
|
||||
<Typography variant="h6">{registrations[value].value}</Typography>
|
||||
<ArrowForwardIcon
|
||||
className={classnames(classes.profitArrow, {
|
||||
[!registrations[value].profit]: classes.profitArrowDanger,
|
||||
})}
|
||||
/>
|
||||
</Grid>
|
||||
<Typography size="sm" color="text" colorBrightness="secondary">
|
||||
Registrations
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.statCell}>
|
||||
<Grid container alignItems="center">
|
||||
<Typography variant="h6">{bounce[value].value}%</Typography>
|
||||
<ArrowForwardIcon
|
||||
className={classnames(classes.profitArrow, {
|
||||
[!registrations[value].profit]: classes.profitArrowDanger,
|
||||
})}
|
||||
/>
|
||||
</Grid>
|
||||
<Typography size="sm" color="text" colorBrightness="secondary">
|
||||
Bounce Rate
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classnames(classes.statCell, classes.borderRight)}>
|
||||
<Grid container alignItems="center">
|
||||
<Typography variant="h6">
|
||||
{registrations[value].value * 10}
|
||||
</Typography>
|
||||
<ArrowForwardIcon
|
||||
className={classnames(classes.profitArrow, {
|
||||
[classes.profitArrowDanger]: !registrations[value].profit,
|
||||
})}
|
||||
/>
|
||||
</Grid>
|
||||
<Typography size="sm" color="text" colorBrightness="secondary">
|
||||
Views
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
// #######################################################################
|
||||
|
||||
function getRandomData() {
|
||||
return Array(7)
|
||||
.fill()
|
||||
.map(() => ({ value: Math.floor(Math.random() * 10) + 1 }));
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
title: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
bottomStatsContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
margin: theme.spacing(1) * -2,
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
statCell: {
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
totalValueContainer: {
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
totalValue: {
|
||||
display: "flex",
|
||||
alignItems: "baseline",
|
||||
},
|
||||
profitArrow: {
|
||||
transform: "rotate(-45deg)",
|
||||
fill: theme.palette.success.main,
|
||||
},
|
||||
profitArrowDanger: {
|
||||
transform: "rotate(45deg)",
|
||||
fill: theme.palette.secondary.main,
|
||||
},
|
||||
selectInput: {
|
||||
padding: 10,
|
||||
paddingRight: 25,
|
||||
"&:focus": {
|
||||
backgroundColor: "white",
|
||||
},
|
||||
},
|
||||
bodyWidgetOverflow: {
|
||||
overflow: 'auto'
|
||||
}
|
||||
}));
|
|
@ -0,0 +1,49 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Table,
|
||||
TableRow,
|
||||
TableHead,
|
||||
TableBody,
|
||||
TableCell,
|
||||
Chip
|
||||
} from "@material-ui/core";
|
||||
import useStyles from "../../styles";
|
||||
|
||||
const states = {
|
||||
sent: "success",
|
||||
pending: "warning",
|
||||
declined: "secondary",
|
||||
};
|
||||
|
||||
export default function TableComponent({ data }) {
|
||||
const classes = useStyles();
|
||||
var keys = Object.keys(data[0]).map(i => i.toUpperCase());
|
||||
keys.shift(); // delete "id" key
|
||||
|
||||
return (
|
||||
<Table className="mb-0">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{keys.map(key => (
|
||||
<TableCell key={key}>{key}</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{data.map(({ id, name, email, product, price, date, city, status }) => (
|
||||
<TableRow key={id}>
|
||||
<TableCell className="pl-3 fw-normal">{name}</TableCell>
|
||||
<TableCell>{email}</TableCell>
|
||||
<TableCell>{product}</TableCell>
|
||||
<TableCell>{price}</TableCell>
|
||||
<TableCell>{date}</TableCell>
|
||||
<TableCell>{city}</TableCell>
|
||||
<TableCell>
|
||||
<Chip label={status} classes={{root: classes[states[status.toLowerCase()]]}}/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
const mock = {
|
||||
tasks: [
|
||||
{
|
||||
id: 0,
|
||||
type: "Meeting",
|
||||
title: "Meeting with Andrew Piker",
|
||||
time: "9:00"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
type: "Call",
|
||||
title: "Call with HT Company",
|
||||
time: "12:00"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: "Meeting",
|
||||
title: "Meeting with Zoe Alison",
|
||||
time: "14:00"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: "Interview",
|
||||
title: "Interview with HR",
|
||||
time: "15:00"
|
||||
}
|
||||
],
|
||||
bigStat: [
|
||||
{
|
||||
product: "Light Blue",
|
||||
total: {
|
||||
monthly: 4232,
|
||||
weekly: 1465,
|
||||
daily: 199,
|
||||
percent: { value: 3.7, profit: false }
|
||||
},
|
||||
color: "primary",
|
||||
registrations: {
|
||||
monthly: { value: 830, profit: false },
|
||||
weekly: { value: 215, profit: true },
|
||||
daily: { value: 33, profit: true }
|
||||
},
|
||||
bounce: {
|
||||
monthly: { value: 4.5, profit: false },
|
||||
weekly: { value: 3, profit: true },
|
||||
daily: { value: 3.25, profit: true }
|
||||
}
|
||||
},
|
||||
{
|
||||
product: "Sing App",
|
||||
total: {
|
||||
monthly: 754,
|
||||
weekly: 180,
|
||||
daily: 27,
|
||||
percent: { value: 2.5, profit: true }
|
||||
},
|
||||
color: "warning",
|
||||
registrations: {
|
||||
monthly: { value: 32, profit: true },
|
||||
weekly: { value: 8, profit: true },
|
||||
daily: { value: 2, profit: false }
|
||||
},
|
||||
bounce: {
|
||||
monthly: { value: 2.5, profit: true },
|
||||
weekly: { value: 4, profit: false },
|
||||
daily: { value: 4.5, profit: false }
|
||||
}
|
||||
},
|
||||
{
|
||||
product: "RNS",
|
||||
total: {
|
||||
monthly: 1025,
|
||||
weekly: 301,
|
||||
daily: 44,
|
||||
percent: { value: 3.1, profit: true }
|
||||
},
|
||||
color: "secondary",
|
||||
registrations: {
|
||||
monthly: { value: 230, profit: true },
|
||||
weekly: { value: 58, profit: false },
|
||||
daily: { value: 15, profit: false }
|
||||
},
|
||||
bounce: {
|
||||
monthly: { value: 21.5, profit: false },
|
||||
weekly: { value: 19.35, profit: false },
|
||||
daily: { value: 10.1, profit: true }
|
||||
}
|
||||
}
|
||||
],
|
||||
notifications: [
|
||||
{
|
||||
id: 0,
|
||||
icon: "thumbs-up",
|
||||
color: "primary",
|
||||
content:
|
||||
'Ken <span className="fw-semi-bold">accepts</span> your invitation'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
icon: "file",
|
||||
color: "success",
|
||||
content: "Report from LT Company"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: "envelope",
|
||||
color: "danger",
|
||||
content: '4 <span className="fw-semi-bold">Private</span> Mails'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: "comment",
|
||||
color: "success",
|
||||
content: '3 <span className="fw-semi-bold">Comments</span> to your Post'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: "cog",
|
||||
color: "light",
|
||||
content: 'New <span className="fw-semi-bold">Version</span> of RNS app'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
icon: "bell",
|
||||
color: "info",
|
||||
content:
|
||||
'15 <span className="fw-semi-bold">Notifications</span> from Social Apps'
|
||||
}
|
||||
],
|
||||
table: [
|
||||
{
|
||||
id: 0,
|
||||
name: "Mark Otto",
|
||||
email: "ottoto@wxample.com",
|
||||
product: "ON the Road",
|
||||
price: "$25 224.2",
|
||||
date: "11 May 2017",
|
||||
city: "Otsego",
|
||||
status: "Sent"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: "Jacob Thornton",
|
||||
email: "thornton@wxample.com",
|
||||
product: "HP Core i7",
|
||||
price: "$1 254.2",
|
||||
date: "4 Jun 2017",
|
||||
city: "Fivepointville",
|
||||
status: "Sent"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Larry the Bird",
|
||||
email: "bird@wxample.com",
|
||||
product: "Air Pro",
|
||||
price: "$1 570.0",
|
||||
date: "27 Aug 2017",
|
||||
city: "Leadville North",
|
||||
status: "Pending"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Joseph May",
|
||||
email: "josephmay@wxample.com",
|
||||
product: "Version Control",
|
||||
price: "$5 224.5",
|
||||
date: "19 Feb 2018",
|
||||
city: "Seaforth",
|
||||
status: "Declined"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Peter Horadnia",
|
||||
email: "horadnia@wxample.com",
|
||||
product: "Let's Dance",
|
||||
price: "$43 594.7",
|
||||
date: "1 Mar 2018",
|
||||
city: "Hanoverton",
|
||||
status: "Sent"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default mock;
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Dashboard",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "Dashboard.js"
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
card: {
|
||||
minHeight: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
visitsNumberContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexGrow: 1,
|
||||
paddingBottom: theme.spacing(1),
|
||||
},
|
||||
progressSection: {
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
progressTitle: {
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
progress: {
|
||||
marginBottom: theme.spacing(1),
|
||||
backgroundColor: 'rgb(236, 236, 236)',
|
||||
},
|
||||
pieChartLegendWrapper: {
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
legendItemContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
fullHeightBody: {
|
||||
display: "flex",
|
||||
flexGrow: 1,
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
tableWidget: {
|
||||
overflowX: "auto",
|
||||
},
|
||||
progressBarPrimary: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
progressBarWarning: {
|
||||
backgroundColor: theme.palette.warning.main,
|
||||
},
|
||||
performanceLegendWrapper: {
|
||||
display: "flex",
|
||||
flexGrow: 1,
|
||||
alignItems: "center",
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
legendElement: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
legendElementText: {
|
||||
marginLeft: theme.spacing(1),
|
||||
},
|
||||
serverOverviewElement: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
maxWidth: "100%",
|
||||
},
|
||||
serverOverviewElementText: {
|
||||
minWidth: 145,
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
serverOverviewElementChartWrapper: {
|
||||
width: "100%",
|
||||
},
|
||||
mainChartBody: {
|
||||
overflowX: "auto",
|
||||
},
|
||||
mainChartHeader: {
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
[theme.breakpoints.only("xs")]: {
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
},
|
||||
mainChartHeaderLabels: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
[theme.breakpoints.only("xs")]: {
|
||||
order: 3,
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
marginTop: theme.spacing(3),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
mainChartHeaderLabel: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginLeft: theme.spacing(3),
|
||||
},
|
||||
mainChartSelectRoot: {
|
||||
borderColor: theme.palette.text.hint + "80 !important",
|
||||
},
|
||||
mainChartSelect: {
|
||||
padding: 10,
|
||||
paddingRight: 25,
|
||||
},
|
||||
mainChartLegentElement: {
|
||||
fontSize: "18px !important",
|
||||
marginLeft: theme.spacing(1),
|
||||
},
|
||||
success: {
|
||||
backgroundColor: theme.palette.success.main,
|
||||
color: '#fff',
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: theme.palette.warning.main,
|
||||
color: '#fff',
|
||||
},
|
||||
secondary: {
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
color: '#fff',
|
||||
}
|
||||
}));
|
|
@ -0,0 +1,55 @@
|
|||
import React from "react";
|
||||
import { Grid, Paper, Typography, Button } from "@material-ui/core";
|
||||
import { Link } from "react-router-dom";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// logo
|
||||
import logo from "./logo.svg";
|
||||
|
||||
export default function Error() {
|
||||
var classes = useStyles();
|
||||
|
||||
return (
|
||||
<Grid container className={classes.container}>
|
||||
<div className={classes.logotype}>
|
||||
<img className={classes.logotypeIcon} src={logo} alt="logo" />
|
||||
<Typography variant="h3" color="white" className={classes.logotypeText}>
|
||||
Material Admin
|
||||
</Typography>
|
||||
</div>
|
||||
<Paper classes={{ root: classes.paperRoot }}>
|
||||
<Typography
|
||||
variant="h1"
|
||||
color="primary"
|
||||
className={classnames(classes.textRow, classes.errorCode)}
|
||||
>
|
||||
404
|
||||
</Typography>
|
||||
<Typography variant="h5" color="primary" className={classes.textRow}>
|
||||
Oops. Looks like the page you're looking for no longer exists
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
color="text"
|
||||
colorBrightness="secondary"
|
||||
className={classnames(classes.textRow, classes.safetyText)}
|
||||
>
|
||||
But we're here to bring you back to safety
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
component={Link}
|
||||
to="/"
|
||||
size="large"
|
||||
className={classes.backButton}
|
||||
>
|
||||
Back to Home
|
||||
</Button>
|
||||
</Paper>
|
||||
</Grid>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="131px" height="106px" viewBox="0 0 131 106" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
|
||||
<title>logo_white</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="logo_white" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="Group-39" transform="translate(12.000000, 16.000000)" stroke-width="16.3476923">
|
||||
<path d="M4,72.4669231 L52.2318841,0" id="Line-10" stroke="#FFFFFF"></path>
|
||||
<path d="M1.10416667,74.69 L105.551432,74.69" id="Line-10" stroke="#FBC02D"></path>
|
||||
<path d="M52.2318841,0 L105.551432,74.69" id="Line-10" stroke="#FF5252"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 858 B |
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Error",
|
||||
"version": "0.0.0",
|
||||
"main": "Error.js",
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
container: {
|
||||
height: "100vh",
|
||||
width: "100vw",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
},
|
||||
logotype: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: theme.spacing(12),
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
logotypeText: {
|
||||
fontWeight: 500,
|
||||
color: "white",
|
||||
marginLeft: theme.spacing(2),
|
||||
},
|
||||
logotypeIcon: {
|
||||
width: 70,
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
paperRoot: {
|
||||
boxShadow: theme.customShadows.widgetDark,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
paddingTop: theme.spacing(8),
|
||||
paddingBottom: theme.spacing(8),
|
||||
paddingLeft: theme.spacing(6),
|
||||
paddingRight: theme.spacing(6),
|
||||
maxWidth: 404,
|
||||
},
|
||||
textRow: {
|
||||
marginBottom: theme.spacing(10),
|
||||
textAlign: "center",
|
||||
},
|
||||
errorCode: {
|
||||
fontSize: 148,
|
||||
fontWeight: 600,
|
||||
},
|
||||
safetyText: {
|
||||
fontWeight: 300,
|
||||
color: theme.palette.text.hint,
|
||||
},
|
||||
backButton: {
|
||||
boxShadow: theme.customShadows.widget,
|
||||
textTransform: "none",
|
||||
fontSize: 22,
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Icons",
|
||||
"version": "0.0.0",
|
||||
"main": "Icons.js",
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
titleBold: {
|
||||
fontWeight: 600,
|
||||
},
|
||||
iconsBar: {
|
||||
marginBottom: theme.spacing(4),
|
||||
borderBottom: "1px solid",
|
||||
borderBottomColor: theme.palette.text.hint + "80",
|
||||
},
|
||||
tab: {
|
||||
color: theme.palette.primary.light + "CC",
|
||||
},
|
||||
materailIcon: {
|
||||
display: "flex",
|
||||
paddingLeft: `${theme.spacing(4)}px !important`,
|
||||
paddingRight: `${theme.spacing(4)}px !important`,
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 24,
|
||||
overflowX: "hidden",
|
||||
},
|
||||
materialIconText: {
|
||||
marginLeft: theme.spacing(2),
|
||||
fontSize: 14,
|
||||
},
|
||||
iconsContainer: {
|
||||
boxShadow: theme.customShadows.widget,
|
||||
overflow: "hidden",
|
||||
paddingBottom: theme.spacing(2),
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,255 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
Grid,
|
||||
CircularProgress,
|
||||
Typography,
|
||||
Button,
|
||||
Tabs,
|
||||
Tab,
|
||||
TextField,
|
||||
Fade,
|
||||
} from "@material-ui/core";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// logo
|
||||
import logo from "./logo.svg";
|
||||
import google from "../../images/google.svg";
|
||||
|
||||
// context
|
||||
import { useUserDispatch, loginUser } from "../../context/UserContext";
|
||||
|
||||
function Login(props) {
|
||||
var classes = useStyles();
|
||||
|
||||
// global
|
||||
var userDispatch = useUserDispatch();
|
||||
|
||||
// local
|
||||
var [isLoading, setIsLoading] = useState(false);
|
||||
var [error, setError] = useState(null);
|
||||
var [activeTabId, setActiveTabId] = useState(0);
|
||||
var [nameValue, setNameValue] = useState("");
|
||||
var [loginValue, setLoginValue] = useState("admin@flatlogic.com");
|
||||
var [passwordValue, setPasswordValue] = useState("password");
|
||||
|
||||
return (
|
||||
<Grid container className={classes.container}>
|
||||
<div className={classes.logotypeContainer}>
|
||||
<img src={logo} alt="logo" className={classes.logotypeImage} />
|
||||
<Typography className={classes.logotypeText}>Material Admin</Typography>
|
||||
</div>
|
||||
<div className={classes.formContainer}>
|
||||
<div className={classes.form}>
|
||||
<Tabs
|
||||
value={activeTabId}
|
||||
onChange={(e, id) => setActiveTabId(id)}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
centered
|
||||
>
|
||||
<Tab label="Login" classes={{ root: classes.tab }} />
|
||||
<Tab label="New User" classes={{ root: classes.tab }} />
|
||||
</Tabs>
|
||||
{activeTabId === 0 && (
|
||||
<React.Fragment>
|
||||
<Typography variant="h1" className={classes.greeting}>
|
||||
Good Morning, User
|
||||
</Typography>
|
||||
<Button size="large" className={classes.googleButton}>
|
||||
<img src={google} alt="google" className={classes.googleIcon} />
|
||||
Sign in with Google
|
||||
</Button>
|
||||
<div className={classes.formDividerContainer}>
|
||||
<div className={classes.formDivider} />
|
||||
<Typography className={classes.formDividerWord}>or</Typography>
|
||||
<div className={classes.formDivider} />
|
||||
</div>
|
||||
<Fade in={error}>
|
||||
<Typography color="secondary" className={classes.errorMessage}>
|
||||
Something is wrong with your login or password :(
|
||||
</Typography>
|
||||
</Fade>
|
||||
<TextField
|
||||
id="email"
|
||||
InputProps={{
|
||||
classes: {
|
||||
underline: classes.textFieldUnderline,
|
||||
input: classes.textField,
|
||||
},
|
||||
}}
|
||||
value={loginValue}
|
||||
onChange={e => setLoginValue(e.target.value)}
|
||||
margin="normal"
|
||||
placeholder="Email Adress"
|
||||
type="email"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
InputProps={{
|
||||
classes: {
|
||||
underline: classes.textFieldUnderline,
|
||||
input: classes.textField,
|
||||
},
|
||||
}}
|
||||
value={passwordValue}
|
||||
onChange={e => setPasswordValue(e.target.value)}
|
||||
margin="normal"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
fullWidth
|
||||
/>
|
||||
<div className={classes.formButtons}>
|
||||
{isLoading ? (
|
||||
<CircularProgress size={26} className={classes.loginLoader} />
|
||||
) : (
|
||||
<Button
|
||||
disabled={
|
||||
loginValue.length === 0 || passwordValue.length === 0
|
||||
}
|
||||
onClick={() =>
|
||||
loginUser(
|
||||
userDispatch,
|
||||
loginValue,
|
||||
passwordValue,
|
||||
props.history,
|
||||
setIsLoading,
|
||||
setError,
|
||||
)
|
||||
}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="large"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
color="primary"
|
||||
size="large"
|
||||
className={classes.forgetButton}
|
||||
>
|
||||
Forget Password
|
||||
</Button>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{activeTabId === 1 && (
|
||||
<React.Fragment>
|
||||
<Typography variant="h1" className={classes.greeting}>
|
||||
Welcome!
|
||||
</Typography>
|
||||
<Typography variant="h2" className={classes.subGreeting}>
|
||||
Create your account
|
||||
</Typography>
|
||||
<Fade in={error}>
|
||||
<Typography color="secondary" className={classes.errorMessage}>
|
||||
Something is wrong with your login or password :(
|
||||
</Typography>
|
||||
</Fade>
|
||||
<TextField
|
||||
id="name"
|
||||
InputProps={{
|
||||
classes: {
|
||||
underline: classes.textFieldUnderline,
|
||||
input: classes.textField,
|
||||
},
|
||||
}}
|
||||
value={nameValue}
|
||||
onChange={e => setNameValue(e.target.value)}
|
||||
margin="normal"
|
||||
placeholder="Full Name"
|
||||
type="text"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
id="email"
|
||||
InputProps={{
|
||||
classes: {
|
||||
underline: classes.textFieldUnderline,
|
||||
input: classes.textField,
|
||||
},
|
||||
}}
|
||||
value={loginValue}
|
||||
onChange={e => setLoginValue(e.target.value)}
|
||||
margin="normal"
|
||||
placeholder="Email Adress"
|
||||
type="email"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
InputProps={{
|
||||
classes: {
|
||||
underline: classes.textFieldUnderline,
|
||||
input: classes.textField,
|
||||
},
|
||||
}}
|
||||
value={passwordValue}
|
||||
onChange={e => setPasswordValue(e.target.value)}
|
||||
margin="normal"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
fullWidth
|
||||
/>
|
||||
<div className={classes.creatingButtonContainer}>
|
||||
{isLoading ? (
|
||||
<CircularProgress size={26} />
|
||||
) : (
|
||||
<Button
|
||||
onClick={() =>
|
||||
loginUser(
|
||||
userDispatch,
|
||||
loginValue,
|
||||
passwordValue,
|
||||
props.history,
|
||||
setIsLoading,
|
||||
setError,
|
||||
)
|
||||
}
|
||||
disabled={
|
||||
loginValue.length === 0 ||
|
||||
passwordValue.length === 0 ||
|
||||
nameValue.length === 0
|
||||
}
|
||||
size="large"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
className={classes.createAccountButton}
|
||||
>
|
||||
Create your account
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.formDividerContainer}>
|
||||
<div className={classes.formDivider} />
|
||||
<Typography className={classes.formDividerWord}>or</Typography>
|
||||
<div className={classes.formDivider} />
|
||||
</div>
|
||||
<Button
|
||||
size="large"
|
||||
className={classnames(
|
||||
classes.googleButton,
|
||||
classes.googleButtonCreating,
|
||||
)}
|
||||
>
|
||||
<img src={google} alt="google" className={classes.googleIcon} />
|
||||
Sign in with Google
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
<Typography color="primary" className={classes.copyright}>
|
||||
© 2014-{new Date().getFullYear()} <a style={{ textDecoration: 'none', color: 'inherit' }} href="https://flatlogic.com" rel="noopener noreferrer" target="_blank">Flatlogic</a>, LLC. All rights reserved.
|
||||
</Typography>
|
||||
</div>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRouter(Login);
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="131px" height="106px" viewBox="0 0 131 106" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
|
||||
<title>logo_white</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="logo_white" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="Group-39" transform="translate(12.000000, 16.000000)" stroke-width="16.3476923">
|
||||
<path d="M4,72.4669231 L52.2318841,0" id="Line-10" stroke="#FFFFFF"></path>
|
||||
<path d="M1.10416667,74.69 L105.551432,74.69" id="Line-10" stroke="#FBC02D"></path>
|
||||
<path d="M52.2318841,0 L105.551432,74.69" id="Line-10" stroke="#FF5252"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 858 B |
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Login",
|
||||
"version": "0.0.0",
|
||||
"main": "Login.js",
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
container: {
|
||||
height: "100vh",
|
||||
width: "100vw",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
},
|
||||
logotypeContainer: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
width: "60%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "50%",
|
||||
},
|
||||
[theme.breakpoints.down("md")]: {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
logotypeImage: {
|
||||
width: 165,
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
logotypeText: {
|
||||
color: "white",
|
||||
fontWeight: 500,
|
||||
fontSize: 84,
|
||||
[theme.breakpoints.down("md")]: {
|
||||
fontSize: 48,
|
||||
},
|
||||
},
|
||||
formContainer: {
|
||||
width: "40%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "50%",
|
||||
},
|
||||
},
|
||||
form: {
|
||||
width: 320,
|
||||
},
|
||||
tab: {
|
||||
fontWeight: 400,
|
||||
fontSize: 18,
|
||||
},
|
||||
greeting: {
|
||||
fontWeight: 500,
|
||||
textAlign: "center",
|
||||
marginTop: theme.spacing(4),
|
||||
},
|
||||
subGreeting: {
|
||||
fontWeight: 500,
|
||||
textAlign: "center",
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
googleButton: {
|
||||
marginTop: theme.spacing(6),
|
||||
boxShadow: theme.customShadows.widget,
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
textTransform: "none",
|
||||
},
|
||||
googleButtonCreating: {
|
||||
marginTop: 0,
|
||||
},
|
||||
googleIcon: {
|
||||
width: 30,
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
creatingButtonContainer: {
|
||||
marginTop: theme.spacing(2.5),
|
||||
height: 46,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
createAccountButton: {
|
||||
height: 46,
|
||||
textTransform: "none",
|
||||
},
|
||||
formDividerContainer: {
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(4),
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
formDividerWord: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
formDivider: {
|
||||
flexGrow: 1,
|
||||
height: 1,
|
||||
backgroundColor: theme.palette.text.hint + "40",
|
||||
},
|
||||
errorMessage: {
|
||||
textAlign: "center",
|
||||
},
|
||||
textFieldUnderline: {
|
||||
"&:before": {
|
||||
borderBottomColor: theme.palette.primary.light,
|
||||
},
|
||||
"&:after": {
|
||||
borderBottomColor: theme.palette.primary.main,
|
||||
},
|
||||
"&:hover:before": {
|
||||
borderBottomColor: `${theme.palette.primary.light} !important`,
|
||||
},
|
||||
},
|
||||
textField: {
|
||||
borderBottomColor: theme.palette.background.light,
|
||||
},
|
||||
formButtons: {
|
||||
width: "100%",
|
||||
marginTop: theme.spacing(4),
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
},
|
||||
forgetButton: {
|
||||
textTransform: "none",
|
||||
fontWeight: 400,
|
||||
},
|
||||
loginLoader: {
|
||||
marginLeft: theme.spacing(4),
|
||||
},
|
||||
copyright: {
|
||||
marginTop: theme.spacing(4),
|
||||
whiteSpace: "nowrap",
|
||||
[theme.breakpoints.up("md")]: {
|
||||
position: "absolute",
|
||||
bottom: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,39 @@
|
|||
import React from "react";
|
||||
import {
|
||||
withGoogleMap,
|
||||
withScriptjs,
|
||||
GoogleMap,
|
||||
Marker,
|
||||
} from "react-google-maps";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
const BasicMap = withScriptjs(
|
||||
withGoogleMap(() => (
|
||||
<GoogleMap
|
||||
defaultZoom={12}
|
||||
defaultCenter={{
|
||||
lat: parseFloat(-37.813179),
|
||||
lng: parseFloat(144.950259),
|
||||
}}
|
||||
>
|
||||
<Marker position={{ lat: -37.813179, lng: 144.950259 }} />
|
||||
</GoogleMap>
|
||||
)),
|
||||
);
|
||||
|
||||
export default function Maps() {
|
||||
var classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.mapContainer}>
|
||||
<BasicMap
|
||||
googleMapURL="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=AIzaSyB7OXmzfQYua_1LEhRdqsoYzyJOPh9hGLg"
|
||||
loadingElement={<div style={{ height: "inherit", width: "inherit" }} />}
|
||||
containerElement={<div style={{ height: "100%" }} />}
|
||||
mapElement={<div style={{ height: "100%" }} />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Maps",
|
||||
"version": "0.0.0",
|
||||
"main": "Maps.js",
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
mapContainer: {
|
||||
height: "100%",
|
||||
margin: -theme.spacing(1) * 3,
|
||||
},
|
||||
}));
|
|
@ -0,0 +1,392 @@
|
|||
import React, { useState } from "react";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import { Close as CloseIcon } from "@material-ui/icons";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs";
|
||||
import classnames from "classnames";
|
||||
|
||||
// styles
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import Widget from "../../components/Widget/Widget";
|
||||
import PageTitle from "../../components/PageTitle/PageTitle";
|
||||
import Notification from "../../components/Notification";
|
||||
import { Typography, Button } from "../../components/Wrappers/Wrappers";
|
||||
|
||||
const positions = [
|
||||
toast.POSITION.TOP_LEFT,
|
||||
toast.POSITION.TOP_CENTER,
|
||||
toast.POSITION.TOP_RIGHT,
|
||||
toast.POSITION.BOTTOM_LEFT,
|
||||
toast.POSITION.BOTTOM_CENTER,
|
||||
toast.POSITION.BOTTOM_RIGHT,
|
||||
];
|
||||
|
||||
export default function NotificationsPage(props) {
|
||||
var classes = useStyles();
|
||||
|
||||
// local
|
||||
var [notificationsPosition, setNotificationPosition] = useState(2);
|
||||
var [errorToastId, setErrorToastId] = useState(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title="Notifications" />
|
||||
<Grid container spacing={4}>
|
||||
<ToastContainer
|
||||
className={classes.toastsContainer}
|
||||
closeButton={
|
||||
<CloseButton className={classes.notificationCloseButton} />
|
||||
}
|
||||
closeOnClick={false}
|
||||
progressClassName={classes.notificationProgress}
|
||||
/>
|
||||
<Grid item xs={12}>
|
||||
<Widget disableWidgetMenu>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={4}>
|
||||
<Widget title="Layout Options" noWidgetShadow disableWidgetMenu noBodyPadding noHeaderPadding style={{paddingRight: 15}} headerClass={classes.widgetHeader}>
|
||||
<Typography>
|
||||
There are few position options available for notifications. You
|
||||
can click any of them to change notifications position:
|
||||
</Typography>
|
||||
<div className={classes.layoutContainer}>
|
||||
<div className={classes.layoutButtonsRow}>
|
||||
<button
|
||||
onClick={() => changeNotificationPosition(0)}
|
||||
className={classnames(classes.layoutButton, {
|
||||
[classes.layoutButtonActive]: notificationsPosition === 0,
|
||||
})}
|
||||
/>
|
||||
<button
|
||||
onClick={() => changeNotificationPosition(1)}
|
||||
className={classnames(classes.layoutButton, {
|
||||
[classes.layoutButtonActive]: notificationsPosition === 1,
|
||||
})}
|
||||
/>
|
||||
<button
|
||||
onClick={() => changeNotificationPosition(2)}
|
||||
className={classnames(classes.layoutButton, {
|
||||
[classes.layoutButtonActive]: notificationsPosition === 2,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<Typography className={classes.layoutText} size="md">
|
||||
Click any position
|
||||
</Typography>
|
||||
<div className={classes.layoutButtonsRow}>
|
||||
<button
|
||||
onClick={() => changeNotificationPosition(3)}
|
||||
className={classnames(classes.layoutButton, {
|
||||
[classes.layoutButtonActive]: notificationsPosition === 3,
|
||||
})}
|
||||
/>
|
||||
<button
|
||||
onClick={() => changeNotificationPosition(4)}
|
||||
className={classnames(classes.layoutButton, {
|
||||
[classes.layoutButtonActive]: notificationsPosition === 4,
|
||||
})}
|
||||
/>
|
||||
<button
|
||||
onClick={() => changeNotificationPosition(5)}
|
||||
className={classnames(classes.layoutButton, {
|
||||
[classes.layoutButtonActive]: notificationsPosition === 5,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Widget title="Notifications Types" disableWidgetMenu noBodyPadding noWidgetShadow noHeaderPadding style={{paddingRight: 15}} headerClass={classes.widgetHeader}>
|
||||
<Typography>
|
||||
Different types of notifications for lost of use cases. Custom
|
||||
classes are also supported.
|
||||
</Typography>
|
||||
<div className={classes.buttonsContainer}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => handleNotificationCall("info")}
|
||||
className={classnames(classes.notificationCallButton)}
|
||||
>
|
||||
Info Message
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={() => handleNotificationCall("error")}
|
||||
className={classnames(classes.notificationCallButton)}
|
||||
>
|
||||
Error + Retry Message
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="success"
|
||||
onClick={() => handleNotificationCall("success")}
|
||||
className={classnames(classes.notificationCallButton)}
|
||||
>
|
||||
Success Message
|
||||
</Button>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Widget title="Usage" disableWidgetMenu noBodyPadding noWidgetShadow noHeaderPadding style={{paddingRight: 15}} headerClass={classes.widgetHeader}>
|
||||
<Typography>
|
||||
Notifications are created with the help of{" "}
|
||||
<a href="https://github.com/fkhadra/react-toastify">
|
||||
react-toastify
|
||||
</a>
|
||||
</Typography>
|
||||
<div className={classes.codeContainer}>
|
||||
<SyntaxHighlighter
|
||||
className={classes.codeComponent}
|
||||
language="javascript"
|
||||
style={docco}
|
||||
>{`
|
||||
// import needed components, functions and styles
|
||||
import { ToastContainer, toast } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
const Page = () => {
|
||||
<div>
|
||||
<ToastContainer />
|
||||
<button onClick={() => toast('Toast Message')}>
|
||||
show notification
|
||||
</button>
|
||||
</div>
|
||||
};
|
||||
`}</SyntaxHighlighter>
|
||||
<Typography variant="caption">
|
||||
For more API information refer to the library documentation
|
||||
</Typography>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Notification Types Examples" disableWidgetMenu>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
shadowless
|
||||
type="message"
|
||||
message="Thanks for Checking out Messenger"
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
shadowless
|
||||
type="feedback"
|
||||
message="New user feedback received"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
shadowless
|
||||
type="customer"
|
||||
message="New customer is registered"
|
||||
variant="contained"
|
||||
color="success"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
shadowless
|
||||
type="shipped"
|
||||
message="The order was shipped"
|
||||
variant="contained"
|
||||
color="warning"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
shadowless
|
||||
type="delivered"
|
||||
message="The order was delivered"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
shadowless
|
||||
type="defence"
|
||||
message="5 Defence alerts"
|
||||
variant="contained"
|
||||
color="info"
|
||||
/>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Notification Types Examples" disableWidgetMenu>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="report"
|
||||
message="New report has been received"
|
||||
color="secondary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="feedback"
|
||||
message="New user feedback received"
|
||||
color="primary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="shipped"
|
||||
message="The item was shipped"
|
||||
color="success"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="message"
|
||||
message="The new message from user @nahawaii"
|
||||
color="warning"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="upload"
|
||||
message="Your file is ready to upload"
|
||||
color="primary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="disc"
|
||||
message="The disc is full"
|
||||
color="info"
|
||||
/>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Notification Types Examples" disableWidgetMenu>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="report"
|
||||
message="New report has been received"
|
||||
variant="rounded"
|
||||
color="secondary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="feedback"
|
||||
message="New user feedback received"
|
||||
variant="rounded"
|
||||
color="primary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="shipped"
|
||||
message="The item was shipped"
|
||||
variant="rounded"
|
||||
color="success"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="message"
|
||||
message="The new message from user @nahawaii"
|
||||
variant="rounded"
|
||||
color="warning"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="upload"
|
||||
message="Your file is ready to upload"
|
||||
variant="rounded"
|
||||
color="primary"
|
||||
/>
|
||||
<Notification
|
||||
className={classes.notificationItem}
|
||||
type="disc"
|
||||
message="The disc is full"
|
||||
variant="rounded"
|
||||
color="info"
|
||||
/>
|
||||
</Widget>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
// #############################################################
|
||||
function sendNotification(componentProps, options) {
|
||||
return toast(
|
||||
<Notification
|
||||
{...componentProps}
|
||||
className={classes.notificationComponent}
|
||||
/>,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
function retryErrorNotification() {
|
||||
var componentProps = {
|
||||
type: "message",
|
||||
message: "Message was sent successfully!",
|
||||
variant: "contained",
|
||||
color: "success",
|
||||
};
|
||||
toast.update(errorToastId, {
|
||||
render: <Notification {...componentProps} />,
|
||||
type: "success",
|
||||
});
|
||||
setErrorToastId(null);
|
||||
}
|
||||
|
||||
function handleNotificationCall(notificationType) {
|
||||
var componentProps;
|
||||
|
||||
if (errorToastId && notificationType === "error") return;
|
||||
|
||||
switch (notificationType) {
|
||||
case "info":
|
||||
componentProps = {
|
||||
type: "feedback",
|
||||
message: "New user feedback received",
|
||||
variant: "contained",
|
||||
color: "primary",
|
||||
};
|
||||
break;
|
||||
case "error":
|
||||
componentProps = {
|
||||
type: "message",
|
||||
message: "Message was not sent!",
|
||||
variant: "contained",
|
||||
color: "secondary",
|
||||
extraButton: "Resend",
|
||||
extraButtonClick: retryErrorNotification,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
componentProps = {
|
||||
type: "shipped",
|
||||
message: "The item was shipped",
|
||||
variant: "contained",
|
||||
color: "success",
|
||||
};
|
||||
}
|
||||
|
||||
var toastId = sendNotification(componentProps, {
|
||||
type: notificationType,
|
||||
position: positions[notificationsPosition],
|
||||
progressClassName: classes.progress,
|
||||
onClose: notificationType === "error" && (() => setErrorToastId(null)),
|
||||
className: classes.notification,
|
||||
});
|
||||
|
||||
if (notificationType === "error") setErrorToastId(toastId);
|
||||
}
|
||||
|
||||
function changeNotificationPosition(positionId) {
|
||||
setNotificationPosition(positionId);
|
||||
}
|
||||
}
|
||||
|
||||
// #############################################################
|
||||
function CloseButton({ closeToast, className }) {
|
||||
return <CloseIcon className={className} onClick={closeToast} />;
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
import React from "react";
|
||||
import { withStyles } from "@material-ui/core";
|
||||
import { compose, withState, withHandlers } from "recompose";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
import Notification from "../../components/Notification";
|
||||
import NotificationsView from "./NotificationsView";
|
||||
|
||||
const positions = [
|
||||
toast.POSITION.TOP_LEFT,
|
||||
toast.POSITION.TOP_CENTER,
|
||||
toast.POSITION.TOP_RIGHT,
|
||||
toast.POSITION.BOTTOM_LEFT,
|
||||
toast.POSITION.BOTTOM_CENTER,
|
||||
toast.POSITION.BOTTOM_RIGHT
|
||||
];
|
||||
|
||||
export default compose(
|
||||
withStyles(theme => ({
|
||||
/*progress: {
|
||||
visibility: "hidden"
|
||||
},
|
||||
notification: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
background: "transparent",
|
||||
boxShadow: "none",
|
||||
overflow: "visible"
|
||||
},
|
||||
notificationComponent: {
|
||||
paddingRight: theme.spacing.unit * 4
|
||||
}*/
|
||||
})),
|
||||
withState("notificationsPosition", "setNotificationPosition", 2),
|
||||
withState("errorToastId", "setErrorToastId", null),
|
||||
withHandlers({
|
||||
sendNotification: props => (componentProps, options) => {
|
||||
return toast(
|
||||
<Notification
|
||||
{...componentProps}
|
||||
className={props.classes.notificationComponent}
|
||||
/>,
|
||||
options
|
||||
);
|
||||
}
|
||||
}),
|
||||
withHandlers({
|
||||
retryErrorNotification: props => () => {
|
||||
const componentProps = {
|
||||
type: "message",
|
||||
message: "Message was sent successfully!",
|
||||
variant: "contained",
|
||||
color: "success",
|
||||
};
|
||||
|
||||
toast.update(props.errorToastId, {
|
||||
render: <Notification {...componentProps} />,
|
||||
type: "success"
|
||||
});
|
||||
props.setErrorToastId(null);
|
||||
}
|
||||
}),
|
||||
withHandlers({
|
||||
handleNotificationCall: props => notificationType => {
|
||||
let componentProps;
|
||||
|
||||
if (props.errorToastId && notificationType === "error") return;
|
||||
|
||||
switch (notificationType) {
|
||||
case "info":
|
||||
componentProps = {
|
||||
type: "feedback",
|
||||
message: "New user feedback received",
|
||||
variant: "contained",
|
||||
color: "primary"
|
||||
};
|
||||
break;
|
||||
case "error":
|
||||
componentProps = {
|
||||
type: "message",
|
||||
message: "Message was not sent!",
|
||||
variant: "contained",
|
||||
color: "secondary",
|
||||
extraButton: "Resend",
|
||||
extraButtonClick: props.retryErrorNotification
|
||||
};
|
||||
break;
|
||||
default:
|
||||
componentProps = {
|
||||
type: "shipped",
|
||||
message: "The item was shipped",
|
||||
variant: "contained",
|
||||
color: "success"
|
||||
};
|
||||
}
|
||||
|
||||
const toastId = props.sendNotification(componentProps, {
|
||||
type: notificationType,
|
||||
position: positions[props.notificationsPosition],
|
||||
progressClassName: props.classes.progress,
|
||||
onClose:
|
||||
notificationType === "error" && (() => props.setErrorToastId(null)),
|
||||
className: props.classes.notification
|
||||
});
|
||||
|
||||
if (notificationType === "error") props.setErrorToastId(toastId);
|
||||
},
|
||||
changeNotificationPosition: props => positionId => {
|
||||
props.setNotificationPosition(positionId);
|
||||
}
|
||||
})
|
||||
)(NotificationsView);
|
|
@ -0,0 +1,175 @@
|
|||
import React from 'react';
|
||||
import { Grid, withStyles } from '@material-ui/core';
|
||||
import { Close as CloseIcon } from '@material-ui/icons';
|
||||
import classnames from 'classnames';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
import Widget from '../../components/Widget';
|
||||
import PageTitle from '../../components/PageTitle';
|
||||
import NotificationCustomComponent from '../../components/Notification';
|
||||
import { Typography, Button } from '../../components/Wrappers';
|
||||
|
||||
const CloseButton = ({ closeToast, className }) => (
|
||||
<CloseIcon
|
||||
className={className}
|
||||
onClick={closeToast}
|
||||
/>
|
||||
);
|
||||
|
||||
const NotificationsPage = ({ classes, ...props}) => (
|
||||
<React.Fragment>
|
||||
<PageTitle title="Notifications" />
|
||||
<Grid container spacing={32}>
|
||||
<ToastContainer className={classes.toastsContainer} closeButton={<CloseButton className={classes.notificationCloseButton} />} closeOnClick={false} progressClassName={classes.notificationProgress} />
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Layout Options" disableWidgetMenu>
|
||||
<Typography>There are few position options available for notifications. You can click any of them to change notifications position:</Typography>
|
||||
<div className={classes.layoutContainer}>
|
||||
<div className={classes.layoutButtonsRow}>
|
||||
<button onClick={() => props.changeNotificationPosition(0)} className={classnames(classes.layoutButton, { [classes.layoutButtonActive]: props.notificationsPosition === 0})} />
|
||||
<button onClick={() => props.changeNotificationPosition(1)} className={classnames(classes.layoutButton, { [classes.layoutButtonActive]: props.notificationsPosition === 1})} />
|
||||
<button onClick={() => props.changeNotificationPosition(2)} className={classnames(classes.layoutButton, { [classes.layoutButtonActive]: props.notificationsPosition === 2})} />
|
||||
</div>
|
||||
<Typography className={classes.layoutText} size="md">Click any position</Typography>
|
||||
<div className={classes.layoutButtonsRow}>
|
||||
<button onClick={() => props.changeNotificationPosition(3)} className={classnames(classes.layoutButton, { [classes.layoutButtonActive]: props.notificationsPosition === 3})} />
|
||||
<button onClick={() => props.changeNotificationPosition(4)} className={classnames(classes.layoutButton, { [classes.layoutButtonActive]: props.notificationsPosition === 4})} />
|
||||
<button onClick={() => props.changeNotificationPosition(5)} className={classnames(classes.layoutButton, { [classes.layoutButtonActive]: props.notificationsPosition === 5})} />
|
||||
</div>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Notifications Types" disableWidgetMenu>
|
||||
<Typography>Different types of notifications for lost of use cases. Custom classes are also supported.</Typography>
|
||||
<div className={classes.buttonsContainer}>
|
||||
<Button variant="contained" colortheme="primary" onClick={() => props.handleNotificationCall('info')} className={classnames(classes.notificationCallButton)}>Info Message</Button>
|
||||
<Button variant="contained" colortheme="warning" onClick={() => props.handleNotificationCall('error')} className={classnames(classes.notificationCallButton)}>Error + Retry Message</Button>
|
||||
<Button variant="contained" colortheme="success" onClick={() => props.handleNotificationCall('success')} className={classnames(classes.notificationCallButton)}>Success Message</Button>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Usage" disableWidgetMenu>
|
||||
<Typography>Notifications are created with the help of <a href="https://github.com/fkhadra/react-toastify">react-toastify</a></Typography>
|
||||
<div className={classes.codeContainer}>
|
||||
<SyntaxHighlighter className={classes.codeComponent} language='javascript' style={docco}>{`
|
||||
// import needed components, functions and styles
|
||||
import { ToastContainer, toast } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
const Page = () => {
|
||||
<div>
|
||||
<ToastContainer />
|
||||
<button onClick={() => toast('Toast Message')}>
|
||||
show notification
|
||||
</button>
|
||||
</div>
|
||||
};
|
||||
`}</SyntaxHighlighter>
|
||||
<Typography variant="caption">For more API information refer to the library documentation</Typography>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Notification Types Examples" disableWidgetMenu>
|
||||
<NotificationCustomComponent className={classes.notificationItem} shadowless type="message" message="Thanks for Checking out Messenger" variant="contained" color="secondary"/>
|
||||
<NotificationCustomComponent className={classes.notificationItem} shadowless type="feedback" message="New user feedback received" variant="contained" color="primary"/>
|
||||
<NotificationCustomComponent className={classes.notificationItem} shadowless type="customer" message="New customer is registered" variant="contained" color="success" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} shadowless type="shipped" message="The order was shipped" variant="contained" color="warning" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} shadowless type="delivered" message="The order was delivered" variant="contained" color="primary" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} shadowless type="defence" message="5 Defence alerts" variant="contained" color="info" />
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Notification Types Examples" disableWidgetMenu>
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="report" message="New report has been received" color="secondary" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="feedback" message="New user feedback received" color="primary" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="shipped" message="The item was shipped" color="success" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="message" message="The new message from user @nahawaii" color="warning" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="upload" message="Your file is ready to upload" color="primary" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="disc" message="The disc is full" color="info" />
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} lg={4}>
|
||||
<Widget title="Notification Types Examples" disableWidgetMenu>
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="report" message="New report has been received" variant="rounded" color="secondary" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="feedback" message="New user feedback received" variant="rounded" color="primary" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="shipped" message="The item was shipped" variant="rounded" color="success" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="message" message="The new message from user @nahawaii" variant="rounded" color="warning" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="upload" message="Your file is ready to upload" variant="rounded" color="primary" />
|
||||
<NotificationCustomComponent className={classes.notificationItem} type="disc" message="The disc is full" variant="rounded" color="info" />
|
||||
</Widget>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const styles = (theme) => ({
|
||||
layoutContainer: {
|
||||
height: 200,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginTop: theme.spacing.unit * 2,
|
||||
border: '1px dashed',
|
||||
borderColor: theme.palette.primary.main,
|
||||
position: 'relative',
|
||||
},
|
||||
layoutText: {
|
||||
color: tinycolor(theme.palette.background.light).darken().toHexString(),
|
||||
},
|
||||
layoutButtonsRow: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
layoutButton: {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
width: 125,
|
||||
height: 50,
|
||||
outline: 'none',
|
||||
border: 'none',
|
||||
},
|
||||
layoutButtonActive: {
|
||||
backgroundColor: tinycolor(theme.palette.background.light).darken().toHexString(),
|
||||
},
|
||||
buttonsContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
marginTop: theme.spacing.unit * 2,
|
||||
},
|
||||
notificationCallButton: {
|
||||
color: 'white',
|
||||
marginBottom: theme.spacing.unit,
|
||||
textTransform: 'none',
|
||||
},
|
||||
codeContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
marginTop: theme.spacing.unit * 2,
|
||||
},
|
||||
codeComponent: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
notificationItem: {
|
||||
marginTop: theme.spacing.unit * 2,
|
||||
},
|
||||
notificationCloseButton: {
|
||||
position: 'absolute',
|
||||
right: theme.spacing.unit * 2,
|
||||
},
|
||||
toastsContainer: {
|
||||
width: 400,
|
||||
marginTop: theme.spacing.unit * 6,
|
||||
right: 0,
|
||||
}
|
||||
});
|
||||
|
||||
export default withStyles(styles, { withTheme: true})(NotificationsPage);
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Notifications",
|
||||
"version": "0.0.0",
|
||||
"main": "Notifications.js",
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import { makeStyles } from "@material-ui/styles";
|
||||
import tinycolor from "tinycolor2";
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
layoutContainer: {
|
||||
height: 200,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginTop: theme.spacing(2),
|
||||
border: "1px dashed",
|
||||
borderColor: theme.palette.primary.main,
|
||||
position: "relative",
|
||||
},
|
||||
layoutText: {
|
||||
color: tinycolor(theme.palette.background.light)
|
||||
.darken()
|
||||
.toHexString(),
|
||||
},
|
||||
layoutButtonsRow: {
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
layoutButton: {
|
||||
backgroundColor: theme.palette.background.light,
|
||||
width: 125,
|
||||
height: 50,
|
||||
outline: "none",
|
||||
border: "none",
|
||||
},
|
||||
layoutButtonActive: {
|
||||
backgroundColor: tinycolor(theme.palette.background.light)
|
||||
.darken()
|
||||
.toHexString(),
|
||||
},
|
||||
buttonsContainer: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
notificationCallButton: {
|
||||
color: "white",
|
||||
marginBottom: theme.spacing(1),
|
||||
textTransform: "none",
|
||||
},
|
||||
codeContainer: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
codeComponent: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
notificationItem: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
notificationCloseButton: {
|
||||
position: "absolute",
|
||||
right: theme.spacing(2),
|
||||
},
|
||||
toastsContainer: {
|
||||
width: 400,
|
||||
marginTop: theme.spacing(6),
|
||||
right: 0,
|
||||
},
|
||||
progress: {
|
||||
visibility: "hidden",
|
||||
},
|
||||
notification: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
background: "transparent",
|
||||
boxShadow: "none",
|
||||
overflow: "visible",
|
||||
},
|
||||
notificationComponent: {
|
||||
paddingRight: theme.spacing(4),
|
||||
},
|
||||
widgetHeader: {
|
||||
paddingBottom: 8
|
||||
}
|
||||
}));
|
|
@ -0,0 +1,64 @@
|
|||
import React from "react";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import { makeStyles } from "@material-ui/styles";
|
||||
import MUIDataTable from "mui-datatables";
|
||||
|
||||
// components
|
||||
import PageTitle from "../../components/PageTitle";
|
||||
import Widget from "../../components/Widget";
|
||||
import Table from "../dashboard/components/Table/Table";
|
||||
|
||||
// data
|
||||
import mock from "../dashboard/mock";
|
||||
|
||||
const datatableData = [
|
||||
["Joe James", "Example Inc.", "Yonkers", "NY"],
|
||||
["John Walsh", "Example Inc.", "Hartford", "CT"],
|
||||
["Bob Herm", "Example Inc.", "Tampa", "FL"],
|
||||
["James Houston", "Example Inc.", "Dallas", "TX"],
|
||||
["Prabhakar Linwood", "Example Inc.", "Hartford", "CT"],
|
||||
["Kaui Ignace", "Example Inc.", "Yonkers", "NY"],
|
||||
["Esperanza Susanne", "Example Inc.", "Hartford", "CT"],
|
||||
["Christian Birgitte", "Example Inc.", "Tampa", "FL"],
|
||||
["Meral Elias", "Example Inc.", "Hartford", "CT"],
|
||||
["Deep Pau", "Example Inc.", "Yonkers", "NY"],
|
||||
["Sebastiana Hani", "Example Inc.", "Dallas", "TX"],
|
||||
["Marciano Oihana", "Example Inc.", "Yonkers", "NY"],
|
||||
["Brigid Ankur", "Example Inc.", "Dallas", "TX"],
|
||||
["Anna Siranush", "Example Inc.", "Yonkers", "NY"],
|
||||
["Avram Sylva", "Example Inc.", "Hartford", "CT"],
|
||||
["Serafima Babatunde", "Example Inc.", "Tampa", "FL"],
|
||||
["Gaston Festus", "Example Inc.", "Tampa", "FL"],
|
||||
];
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
tableOverflow: {
|
||||
overflow: 'auto'
|
||||
}
|
||||
}))
|
||||
|
||||
export default function Tables() {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<>
|
||||
<PageTitle title="Tables" />
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={12}>
|
||||
<MUIDataTable
|
||||
title="Employee List"
|
||||
data={datatableData}
|
||||
columns={["Name", "Company", "City", "State"]}
|
||||
options={{
|
||||
filterType: "checkbox",
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Widget title="Material-UI Table" upperTitle noBodyPadding bodyClass={classes.tableOverflow}>
|
||||
<Table data={mock.table} />
|
||||
</Widget>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Tables",
|
||||
"version": "0.0.0",
|
||||
"main": "Tables.js",
|
||||
"private": true
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
import React from "react";
|
||||
import { Grid } from "@material-ui/core";
|
||||
|
||||
// styles
|
||||
import useStyles from "./styles";
|
||||
|
||||
// components
|
||||
import PageTitle from "../../components/PageTitle";
|
||||
import Widget from "../../components/Widget";
|
||||
import { Typography } from "../../components/Wrappers";
|
||||
|
||||
export default function TypographyPage() {
|
||||
var classes = useStyles();
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle title="Typography" />
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Widget title="Headings" disableWidgetMenu>
|
||||
<div className={classes.dashedBorder}>
|
||||
<Typography variant="h1" className={classes.text}>
|
||||
h1. Heading
|
||||
</Typography>
|
||||
<Typography variant="h2" className={classes.text}>
|
||||
h2. Heading
|
||||
</Typography>
|
||||
<Typography variant="h3" className={classes.text}>
|
||||
h3. Heading
|
||||
</Typography>
|
||||
<Typography variant="h4" className={classes.text}>
|
||||
h4. Heading
|
||||
</Typography>
|
||||
<Typography variant="h5" className={classes.text}>
|
||||
h5. Heading
|
||||
</Typography>
|
||||
<Typography variant="h6">h6. Heading</Typography>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Widget title="Typography Colors" disableWidgetMenu>
|
||||
<div className={classes.dashedBorder}>
|
||||
<Typography variant="h1" color="primary" className={classes.text}>
|
||||
h1. Heading
|
||||
</Typography>
|
||||
<Typography variant="h2" color="success" className={classes.text}>
|
||||
h2. Heading
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h3"
|
||||
color="secondary"
|
||||
className={classes.text}
|
||||
>
|
||||
h3. Heading
|
||||
</Typography>
|
||||
<Typography variant="h4" color="warning" className={classes.text}>
|
||||
h4. Heading
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
color="primary"
|
||||
colorBrightness="light"
|
||||
className={classes.text}
|
||||
>
|
||||
h5. Heading
|
||||
</Typography>
|
||||
<Typography variant="h6" color="info">
|
||||
h6. Heading
|
||||
</Typography>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Widget title="Basic Text Settings" disableWidgetMenu>
|
||||
<div className={classes.dashedBorder}>
|
||||
<Typography className={classes.text}>Basic text</Typography>
|
||||
<Typography className={classes.text} weight="light">
|
||||
Basic light text
|
||||
</Typography>
|
||||
<Typography className={classes.text} weight="medium">
|
||||
Basic medium text
|
||||
</Typography>
|
||||
<Typography className={classes.text} weight="bold">
|
||||
Basic bold text
|
||||
</Typography>
|
||||
<Typography className={classes.text}>
|
||||
BASIC UPPERCASE TEXT
|
||||
</Typography>
|
||||
<Typography className={classes.text}>
|
||||
basic lowercase text
|
||||
</Typography>
|
||||
<Typography className={classes.text}>
|
||||
Basic Capitalized Text
|
||||
</Typography>
|
||||
<Typography>
|
||||
<i>Basic Cursive Text</i>
|
||||
</Typography>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Widget title="Text Size" disableWidgetMenu>
|
||||
<div className={classes.dashedBorder}>
|
||||
<Typography className={classes.text} size="sm">
|
||||
Heading Typography SM Font Size
|
||||
</Typography>
|
||||
<Typography className={classes.text}>
|
||||
Heading Typography Regular Font Size
|
||||
</Typography>
|
||||
<Typography className={classes.text} size="md">
|
||||
Heading Typography MD Font Size
|
||||
</Typography>
|
||||
<Typography className={classes.text} size="xl">
|
||||
Heading Typography XL Font Size
|
||||
</Typography>
|
||||
<Typography className={classes.text} size="xxl">
|
||||
Heading Typography XXL Font Size
|
||||
</Typography>
|
||||
</div>
|
||||
</Widget>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|