cdn frontend

master
Tudor Stanciu 2021-07-03 12:15:26 +03:00
commit 98539899c1
106 changed files with 53556 additions and 0 deletions

1
.eslintcache Normal file

File diff suppressed because one or more lines are too long

24
.gitignore vendored Normal file
View File

@ -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*

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"tabWidth": 2,
"singleQuote": false,
"semi": true,
"trailingComma": "all"
}

21
LICENSE.txt Normal file
View File

@ -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.

106
README.md Normal file
View File

@ -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).

67
changelog.md Normal file
View File

@ -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

25
docs/asset-manifest.json Normal file
View File

@ -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"
]
}

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

1
docs/index.html Normal file
View File

@ -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>

15
docs/manifest.json Normal file
View File

@ -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"
}

View File

@ -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"
}
]);

39
docs/service-worker.js Normal file
View File

@ -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: [/^\/_/,/\/[^\/?]+\.[^\/]+$/],
});

6
docs/static/css/2.637eb612.chunk.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3
docs/static/js/2.af6eef2b.chunk.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -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.
*/

File diff suppressed because one or more lines are too long

2
docs/static/js/main.fc3e922e.chunk.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

47
docs/static/media/google.09aea0f5.svg vendored Normal file
View File

@ -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

13
docs/static/media/logo.3d432ca2.svg vendored Normal file
View File

@ -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

17658
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

57
package.json Normal file
View File

@ -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"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

44
public/index.html Normal file
View File

@ -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>

15
public/manifest.json Normal file
View File

@ -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"
}

76
src/components/App.js Normal file
View File

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

View File

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

View File

@ -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);

View File

@ -0,0 +1,6 @@
{
"name": "Header",
"version": "0.0.0",
"private": true,
"main": "Header.js"
}

View File

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

View File

@ -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);

View File

@ -0,0 +1,6 @@
{
"name": "Layout",
"version": "1.0.0",
"private": true,
"main": "Layout.js"
}

View File

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

View File

@ -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];
}

View File

@ -0,0 +1,6 @@
{
"name": "Notification",
"version": "0.0.0",
"private": true,
"main": "Notification.js"
}

View File

@ -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",
},
},
}));

View File

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

View File

@ -0,0 +1,6 @@
{
"name": "PageTitle",
"version": "0.0.0",
"private": true,
"main": "PageTitle.js"
}

View File

@ -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,
},
},
}));

View File

@ -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);

View File

@ -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);

View File

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

View File

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

View File

@ -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",
},
}));

View File

@ -0,0 +1,6 @@
{
"name": "Sidebar",
"version": "0.0.0",
"private": true,
"main": "Sidebar.js"
}

View File

@ -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",
},
},
}));

View File

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

View File

@ -0,0 +1,6 @@
{
"name": "UserAvatar",
"version": "0.0.0",
"private": true,
"main": "UserAvatar.js"
}

View File

@ -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",
},
}));

View File

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

View File

@ -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);

View File

@ -0,0 +1,6 @@
{
"name": "Widget",
"version": "0.0.0",
"private": true,
"main": "Widget.js"
}

View File

@ -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'
}
}));

View File

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

View File

@ -0,0 +1,7 @@
{
"name": "Wrappers",
"version": "0.0.0",
"private": true,
"main": "Wrappers.js"
}

View File

@ -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",
});
}

View File

@ -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");
}

47
src/images/google.svg Normal file
View File

@ -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

27
src/index.js Normal file
View File

@ -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();

234
src/pages/charts/Charts.js Normal file
View File

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

View File

@ -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],
};
}

View File

@ -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,
},
};
}

View File

@ -0,0 +1,6 @@
{
"name": "Charts",
"version": "1.0.0",
"private": true,
"main": "Charts.js"
}

View File

@ -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 }} >
&nbsp;{name}&nbsp;
</Typography>
<Typography color="text" colorBrightness="secondary">
&nbsp;{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;
}

View File

@ -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"}>
&nbsp;{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 }));
}

View File

@ -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'
}
}));

View File

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

184
src/pages/dashboard/mock.js Normal file
View File

@ -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;

View File

@ -0,0 +1,6 @@
{
"name": "Dashboard",
"version": "1.0.0",
"private": true,
"main": "Dashboard.js"
}

View File

@ -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',
}
}));

55
src/pages/error/Error.js Normal file
View File

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

13
src/pages/error/logo.svg Normal file
View File

@ -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

View File

@ -0,0 +1,6 @@
{
"name": "Error",
"version": "0.0.0",
"main": "Error.js",
"private": true
}

61
src/pages/error/styles.js Normal file
View File

@ -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,
},
}));

13600
src/pages/icons/Icons.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
{
"name": "Icons",
"version": "0.0.0",
"main": "Icons.js",
"private": true
}

32
src/pages/icons/styles.js Normal file
View File

@ -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),
},
}));

255
src/pages/login/Login.js Normal file
View File

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

13
src/pages/login/logo.svg Normal file
View File

@ -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

View File

@ -0,0 +1,6 @@
{
"name": "Login",
"version": "0.0.0",
"main": "Login.js",
"private": true
}

148
src/pages/login/styles.js Normal file
View File

@ -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),
},
},
}));

39
src/pages/maps/Maps.js Normal file
View File

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

View File

@ -0,0 +1,6 @@
{
"name": "Maps",
"version": "0.0.0",
"main": "Maps.js",
"private": true
}

8
src/pages/maps/styles.js Normal file
View File

@ -0,0 +1,8 @@
import { makeStyles } from "@material-ui/styles";
export default makeStyles(theme => ({
mapContainer: {
height: "100%",
margin: -theme.spacing(1) * 3,
},
}));

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -0,0 +1,6 @@
{
"name": "Notifications",
"version": "0.0.0",
"main": "Notifications.js",
"private": true
}

View File

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

View File

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

View File

@ -0,0 +1,6 @@
{
"name": "Tables",
"version": "0.0.0",
"main": "Tables.js",
"private": true
}

View File

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

Some files were not shown because too many files have changed in this diff Show More