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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|