├── .babelrc ├── .bitmap ├── .circleci └── config.yml ├── .eslintrc.js ├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bit.json ├── docs ├── Account Ownership Diagram Template.png ├── Design Document.vsdx ├── Document_2018-07-16_21-57-33.png ├── components │ ├── ReadMe.md │ └── notifications │ │ └── ReadMe.md ├── development │ ├── Async.md │ ├── Dev code block.md │ ├── ReadMe.md │ ├── Run and Package.md │ └── TypeScript.md ├── environments │ ├── Azure Service Fabric.md │ ├── Docker.md │ ├── GZIP and SSL.md │ ├── IIS.md │ ├── NodeJs.md │ └── ReadMe.md ├── index.md └── styles │ ├── LESS.md │ ├── ReadMe.md │ └── SCSS.md ├── package.json ├── postcss.config.js ├── src ├── actions │ ├── Dashboard │ │ ├── DashboardActions.js │ │ ├── DashboardReducer.js │ │ └── index.js │ ├── MessageBox │ │ ├── MessageBoxActions.js │ │ ├── MessageBoxReducer.js │ │ └── index.js │ ├── Notifications │ │ ├── NotificationActions.js │ │ ├── NotificationReducer.js │ │ └── index.js │ └── Users │ │ ├── UserActions.js │ │ ├── UserReducer.js │ │ └── index.js ├── api │ ├── chartApi.js │ ├── generalApi.js │ ├── index.js │ └── userApi.js ├── assets │ ├── img │ │ ├── apple-icon.png │ │ ├── cover.jpeg │ │ ├── faces │ │ │ └── marc.jpg │ │ ├── favicon.png │ │ ├── mask.png │ │ ├── new_logo.png │ │ ├── react_logo.svg │ │ ├── sidebar-1.jpg │ │ ├── sidebar-2.jpg │ │ ├── sidebar-3.jpg │ │ ├── sidebar-4.jpg │ │ └── tim_80x80.png │ ├── jss │ │ ├── material-dashboard-react.jsx │ │ └── material-dashboard-react │ │ │ ├── cardImagesStyles.jsx │ │ │ ├── checkboxAdnRadioStyle.jsx │ │ │ ├── dropdownStyle.jsx │ │ │ └── tooltipStyle.jsx │ ├── less │ │ └── material-dashboard-react.less │ └── scss │ │ └── material-dashboard-react.scss ├── commons │ ├── checks.js.ignore │ ├── commonControlRenderers.jsx │ ├── commonFuncs.ts │ ├── constants.js │ ├── exceptionService.js │ └── guard.js ├── components │ ├── Card │ │ ├── Card.jsx │ │ ├── CardAvatar.jsx │ │ ├── CardBody.jsx │ │ ├── CardFooter.jsx │ │ ├── CardHeader.jsx │ │ ├── CardIcon.jsx │ │ ├── cardAvatarStyle.jsx │ │ ├── cardBodyStyle.jsx │ │ ├── cardFooterStyle.jsx │ │ ├── cardHeaderStyle.jsx │ │ ├── cardIconStyle.jsx │ │ └── cardStyle.jsx │ ├── CustomButtons │ │ ├── Button.jsx │ │ └── buttonStyle.jsx │ ├── CustomInput │ │ ├── CustomInput.jsx │ │ └── customInputStyle.jsx │ ├── CustomTabs │ │ ├── CustomTabs.jsx │ │ └── customTabsStyle.jsx │ ├── Footer │ │ ├── Footer.jsx │ │ └── footerStyle.jsx │ ├── Grid │ │ └── GridItem.jsx │ ├── Header │ │ ├── Header.jsx │ │ ├── HeaderLinks.jsx │ │ ├── headerLinksStyle.jsx │ │ └── headerStyle.jsx │ ├── MessageBox │ │ ├── MessageBoxType.jsx │ │ ├── helper.jsx │ │ ├── index.jsx │ │ └── jss.jsx │ ├── Notification │ │ ├── NotificationCenter.jsx │ │ ├── NotificationGroup.jsx │ │ ├── NotificationGroupStyle.js │ │ ├── NotificationItem.jsx │ │ ├── NotificationItemPropTypes.js │ │ ├── NotificationItemStyle.js │ │ ├── NotificationPanel.jsx │ │ ├── NotificationPanelStyle.js │ │ ├── NotificationPopup.jsx │ │ ├── NotificationPopupItem.jsx │ │ ├── NotificationPopupItemStyle.js │ │ ├── NotificationStatus.js │ │ ├── NotificationType.js │ │ ├── helper.jsx │ │ ├── index.js │ │ └── jss.js │ ├── Sidebar │ │ ├── Sidebar.jsx │ │ └── sidebarStyle.jsx │ ├── Snackbar │ │ ├── Snackbar.jsx │ │ ├── SnackbarContent.jsx │ │ └── snackbarContentStyle.jsx │ ├── Table │ │ ├── ActionCell.jsx │ │ ├── Table.jsx │ │ └── tableStyle.jsx │ ├── Tasks │ │ ├── Tasks.jsx │ │ └── tasksStyle.jsx │ ├── Typography │ │ ├── Danger.jsx │ │ ├── Info.jsx │ │ ├── Muted.jsx │ │ ├── Primary.jsx │ │ ├── Quote.jsx │ │ ├── Success.jsx │ │ ├── Warning.jsx │ │ └── typographyStyle.jsx │ └── User │ │ ├── UserForm.jsx │ │ ├── UserTable.jsx │ │ ├── UserView.jsx │ │ └── userFormStyles.js ├── favicon.ico ├── index.html.ejs ├── index.jsx ├── layouts │ ├── Dashboard.jsx │ ├── ExceptionHandler.jsx │ └── dashboardStyle.jsx ├── reducers │ └── index.js ├── routes │ ├── dashboard.jsx │ └── index.jsx ├── settings │ ├── dev.js │ ├── index.js │ └── prd.js └── views │ ├── Dashboard │ ├── Dashboard.jsx │ └── dashboardStyle.jsx │ ├── Icons │ ├── Icons.jsx │ └── iconsStyle.jsx │ ├── Maps │ └── Maps.jsx │ ├── MessageBox │ └── index.jsx │ ├── Notifications │ └── Notifications.jsx │ ├── TableList │ └── TableList.jsx │ ├── Typography │ └── Typography.jsx │ ├── UpgradeToPro │ └── UpgradeToPro.jsx │ ├── UserProfile │ ├── UserEditTransition.jsx │ ├── UserListing.jsx │ └── UserProfile.jsx │ └── loaders │ ├── ComponentLoader.tsx │ └── index.ts ├── tests ├── _dummy_data │ ├── charts.jsx │ ├── general.jsx │ ├── simulator.js │ └── users.js ├── commons │ ├── commonFuncs.test.ts │ └── guard.test.ts └── components │ ├── MessageBox │ ├── __snapshots__ │ │ └── index.test.tsx.snap │ └── index.test.tsx │ ├── User │ ├── UserForm.test.tsx.ignore │ ├── UserTable.test.tsx.ignore │ └── __snapshots__ │ │ └── UserTable.test.tsx.snap │ └── notification │ ├── NotificationPopup.test.tsx │ ├── NotificationPopupItem.test.tsx │ ├── __snapshots__ │ ├── NotificationPopup.test.tsx.snap │ ├── NotificationPopupItem.test.tsx.snap │ └── index.test.tsx.snap │ └── index.test.tsx ├── tsconfig.json └── tsconfig.test.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/env", 5 | { 6 | "modules": false, 7 | "useBuiltIns": "entry" 8 | } 9 | ], 10 | "@babel/react" 11 | ], 12 | "plugins": [ 13 | ["@babel/transform-runtime", { "useESModules": true }], 14 | "@babel/plugin-syntax-dynamic-import", 15 | "@babel/plugin-syntax-import-meta", 16 | ["@babel/plugin-proposal-decorators", { "legacy": true }], 17 | ["@babel/plugin-proposal-class-properties", { "loose": true }], 18 | "@babel/plugin-proposal-json-strings", 19 | "@babel/plugin-transform-async-to-generator", 20 | "react-loadable/babel", 21 | [ 22 | "import", 23 | { 24 | "libraryName": "@material-ui/core", 25 | "libraryDirectory": "", 26 | "camel2DashComponentName": false 27 | }, 28 | "material-core" 29 | ], 30 | [ 31 | "import", 32 | { 33 | "libraryName": "@material-ui/icons", 34 | "libraryDirectory": "", 35 | "camel2DashComponentName": false 36 | }, 37 | "material-icon" 38 | ], 39 | [ 40 | "transform-imports", 41 | { 42 | "material-ui": { 43 | "transform": "@material-ui/core/${member}", 44 | "preventFullImport": true 45 | } 46 | } 47 | ], 48 | "babel-plugin-lodash", 49 | "react-hot-loader/babel", //Enables React code to work with HMR. 50 | [ 51 | "module-resolver", 52 | { 53 | "root": ["./src"] 54 | } 55 | ] 56 | ], 57 | "env": { 58 | "node": { 59 | "presets": [["@babel/preset-env"], "@babel/react"], 60 | "plugins": [ 61 | ["@babel/transform-runtime"], 62 | ["@babel/plugin-transform-modules-commonjs", { "spec": true }] 63 | ] 64 | }, 65 | "test": { 66 | "presets": [["@babel/preset-env"], "@babel/react"], 67 | "plugins": [ 68 | ["@babel/transform-runtime"], 69 | ["@babel/plugin-transform-modules-commonjs", { "spec": true }] 70 | ] 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /.bitmap: -------------------------------------------------------------------------------- 1 | /* THIS IS A BIT-AUTO-GENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. */ 2 | 3 | { 4 | "version": "13.0.4" 5 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:latest 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | #Check out code 21 | - checkout 22 | 23 | # Download and cache dependencies 24 | - restore_cache: 25 | keys: 26 | - v1-dependencies-{{ checksum "package.json" }} 27 | # fallback to using the latest cache if no exact match is found 28 | - v1-dependencies- 29 | 30 | - run: npm install 31 | - run: npm install codecov bundlesize 32 | - run: npm rebuild node-sass 33 | 34 | - save_cache: 35 | paths: 36 | - node_modules 37 | key: v1-dependencies-{{ checksum "package.json" }} 38 | 39 | # Build 40 | - run: npm run git-webpack && npm run build 41 | # run test bundle size 42 | #- run: 43 | # name: run test-bundle 44 | # command: npm run test-bundle 45 | #environment: 46 | # BUNDLESIZE_GITHUB_TOKEN: dca74b5808660e8e1b09dccbe38b3b79b21bef6c 47 | # run tests 48 | #- run: npm run git-jest && npm run test-ci 49 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | node: true, 5 | commonjs: true, 6 | es6: true, 7 | jest: true 8 | }, 9 | extends: 'eslint:recommended', 10 | parser: 'babel-eslint', 11 | parserOptions: { 12 | ecmaVersion: 6, 13 | sourceType: 'module', 14 | legacyDecotators: true, 15 | ecmaFeatures: { 16 | jsx: true 17 | } 18 | }, 19 | plugins: ['react'], 20 | rules: { 21 | indent: 'off', 22 | 'linebreak-style': ['warn', 'unix'], 23 | quotes: ['error', 'single'], 24 | semi: ['error', 'always'], 25 | 'no-unused-vars': 'off', 26 | experimentalDecorators: true 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | # http://git-scm.com/docs/gitattributes#_end_of_line_conversion 3 | * text=auto 4 | 5 | # For the following file types, normalize line endings to LF on 6 | # checkin and prevent conversion to CRLF when they are checked out 7 | # (this is required in order to prevent newline related issues like, 8 | # for example, after the build script is run) 9 | .* text eol=lf 10 | *.html text eol=lf 11 | *.css text eol=lf 12 | *.less text eol=lf 13 | *.styl text eol=lf 14 | *.scss text eol=lf 15 | *.sass text eol=lf 16 | *.sss text eol=lf 17 | *.js text eol=lf 18 | *.jsx text eol=lf 19 | *.json text eol=lf 20 | *.md text eol=lf 21 | *.mjs text eol=lf 22 | *.sh text eol=lf 23 | *.svg text eol=lf 24 | *.txt text eol=lf 25 | *.xml text eol=lf 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sm-react-docker-nginx"] 2 | path = sm-react-docker-nginx 3 | url = https://github.com/baoduy/sm-react-docker-nginx 4 | [submodule "sm-react-service-fabric"] 5 | path = sm-react-service-fabric 6 | url = https://github.com/baoduy/sm-react-service-fabric 7 | [submodule "sm-react-iis"] 8 | path = sm-react-iis 9 | url = https://github.com/baoduy/sm-react-iis 10 | [submodule "sm-react-node-express"] 11 | path = sm-react-node-express 12 | url = https://github.com/baoduy/sm-react-node-express 13 | [submodule "configs/webpack"] 14 | path = configs/webpack 15 | url = https://github.com/baoduy/sm-webpack-config 16 | [submodule "configs/jest"] 17 | path = configs/jest 18 | url = https://github.com/baoduy/sm-spa-jest-config 19 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributes 2 | 3 | Appreciate your all contribution. 4 | 5 | - If you want to contribute the ideas please create a new issue [here](https://github.com/baoduy/React-MaterialUI-Started-Kit/issues). 6 | 7 | - If you want contribute the new features or fixing the reported issues. Please follow the below steps. 8 | 9 | 1. Fork the code. 10 | 2. Add the new featuures or fixing the existing issues. 11 | 3. Create pull request with the comment details. 12 | 13 | We will review and merge your code accordingly. 14 | 15 | Thanks and Regards 16 | Duy 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Viktor Persson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Material Starter Kit 2 | 3 | [![CircleCI](https://circleci.com/gh/baoduy/React-MaterialUI-Starter-Kit.svg?style=svg)](https://circleci.com/gh/baoduy/React-MaterialUI-Starter-Kit) 4 | [![codecov](https://codecov.io/gh/baoduy/React-MaterialUI-Started-Kit/branch/develop/graph/badge.svg)](https://codecov.io/gh/baoduy/React-MaterialUI-Started-Kit) [![Greenkeeper badge](https://badges.greenkeeper.io/baoduy/React-MaterialUI-Starter-Kit.svg)](https://greenkeeper.io/) 5 | [![Open Source Helpers](https://www.codetriage.com/baoduy/react-materialui-started-kit/badges/users.svg)](https://www.codetriage.com/baoduy/react-materialui-started-kit) 6 | [![PeerDependencies](https://img.shields.io/david/peer/baoduy/React-MaterialUI-Started-Kit.svg)](https://david-dm.org/baoduy/React-MaterialUI-Started-Kit?type=peer) 7 | [![Dependencies](https://img.shields.io/david/baoduy/React-MaterialUI-Started-Kit.svg)](https://david-dm.org/baoduy/React-MaterialUI-Started-Kit) 8 | [![DevDependencies](https://img.shields.io/david/dev/baoduy/React-MaterialUI-Started-Kit.svg)](https://david-dm.org/baoduy/React-MaterialUI-Started-Kit?type=develop) 9 | [![Netlify Status](https://api.netlify.com/api/v1/badges/d65b2073-05b2-4f0c-80b4-595b560b57f3/deploy-status)](https://app.netlify.com/sites/pedantic-hypatia-cf3a12/deploys) 10 | 11 | ## Documentation 12 | 13 | Refer [here](/docs/index.md) for details 14 | 15 | ## Demo 16 | 17 | [react.drunkcoding.net](http://react.drunkcoding.net) 18 | -------------------------------------------------------------------------------- /bit.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": {}, 3 | "dependencies": {}, 4 | "componentsDefaultDirectory": "components/{namespace}/{name}", 5 | "packageManager": "npm" 6 | } -------------------------------------------------------------------------------- /docs/Account Ownership Diagram Template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/docs/Account Ownership Diagram Template.png -------------------------------------------------------------------------------- /docs/Design Document.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/docs/Design Document.vsdx -------------------------------------------------------------------------------- /docs/Document_2018-07-16_21-57-33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/docs/Document_2018-07-16_21-57-33.png -------------------------------------------------------------------------------- /docs/components/ReadMe.md: -------------------------------------------------------------------------------- 1 | # New Compoments 2 | 3 | 1. **Message Box and Notification**: Allow to show Info, Confirm, Success and Error message and notification. Refer to the MessageBox in Views folder so sample that using Redux store to manage the state. 4 | -------------------------------------------------------------------------------- /docs/components/notifications/ReadMe.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/docs/components/notifications/ReadMe.md -------------------------------------------------------------------------------- /docs/development/Async.md: -------------------------------------------------------------------------------- 1 | # async/await 2 | 3 | Thanks to `babel-regenerator-runtime` to make the developler life bester as providing the async transform tool to transform the async and await functions to Promise. 4 | It make the code clearer and easier to read. 5 | 6 | ```javascript 7 | //Transform from 8 | function getDataFromServer() { 9 | return Promise((resolve, reject) => { 10 | loadDataFromServer() 11 | .then(data => resolve(data)) 12 | .catch(error => reject(error)); 13 | }); 14 | } 15 | 16 | //To 17 | async function getDataFromServer() { 18 | try { 19 | return await loadDataFromServer(); 20 | } catch (error) { 21 | throw error; 22 | } 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/development/Dev code block.md: -------------------------------------------------------------------------------- 1 | # webpack-remove-block-loader 2 | 3 | The `webpack-remove-block-loader` is Webpack loader to strip blocks of code marked by special comment tags. Useful for removing code that you don't want in your production webpack bundle (e.g. verbose console warnings, etc). 4 | 5 | Example: the code in side `/* PrdDeletion:start */` and `/* PrdDeletion:end */` will be removed in PRD bundle. 6 | 7 | ```javascript 8 | /* devblock:start */ 9 | console.log(`base URL is ${base}`); 10 | /* devblock:end */ 11 | } 12 | ``` 13 | 14 | ## Uninstall Webpack Strip Block 15 | 16 | 1. Run the _npm uninstall —save-dev_ for all below packages: 17 | 18 | - webpack-strip-block 19 | 20 | 2. Remove the below LESS config blog from Webpack config in **configs\webpack\common.js** file: 21 | 22 | ```json 23 | { 24 | "test": /\.(js|jsx|ts|tsx)$/, 25 | //exclude: /node_modules/, 26 | "use": [ 27 | { 28 | "loader": 'webpack-remove-block-loader', 29 | "options": { 30 | "active": !devMode, 31 | } 32 | } 33 | ] 34 | }, 35 | ``` 36 | 37 | 3. Remove all code blocks which starting with `/* develblock:start */` and end with `/* develblock:end */` 38 | -------------------------------------------------------------------------------- /docs/development/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | 1. [Run and Packaging](Run and Package) 4 | 2. [async and await](Async) 5 | 3. [Typescript](TypeScript) 6 | 4. [Dev code block](Dev code block): Allows to write some code which running in DEV only. This block code will be remove when bunlding for PRD. 7 | -------------------------------------------------------------------------------- /docs/development/Run and Package.md: -------------------------------------------------------------------------------- 1 | # Run & Packaging 2 | 3 | ## Development 4 | 5 | `npm run start-dev` 6 | 7 | - Build app continously (HMR enabled) 8 | - App served @ `http://localhost:8080` 9 | 10 | ## Production 11 | 12 | `npm run start-prod` 13 | 14 | - Build app once (HMR disabled) 15 | - App served @ `http://localhost:3000` 16 | 17 | ## Webpack Bundle Analysis 18 | 19 | `npm run start-analysis` 20 | 21 | - Analysis served @ `http://localhost:8888` 22 | -------------------------------------------------------------------------------- /docs/development/TypeScript.md: -------------------------------------------------------------------------------- 1 | # Typescript 2 | 3 | This project allows to develop some commonds javascript with Typescript which helping to minimize the common issue of coding. 4 | You can find the Typescript sample in \*\*src\commons\commonFuncs.ts, and you also able to import the js modules to the Typescript file as well. 5 | 6 | ## Uninstall Typescript 7 | 8 | 1. Run the _npm uninstall —save-dev_ for all below packages: 9 | - awesome-typescript-loader 10 | - typescript 11 | - and all packages have name started with `@types`. 12 | 2. Remove the below LESS config blog from Webpack config in **configs\webpack\common.js** file: 13 | 14 | ```json 15 | { 16 | test: /\.tsx?$/, 17 | loader: 'awesome-typescript-loader' 18 | }, 19 | ``` 20 | 21 | 3. Remove the **tsconfig.json** and **tsconfig.test.json** files. 22 | 4. Convert all .ts and .tsx files to .js and .jsx. 23 | -------------------------------------------------------------------------------- /docs/environments/Azure Service Fabric.md: -------------------------------------------------------------------------------- 1 | # Azure Service Fabric Support 2 | 3 | All stuffs in `sm-react-service-fabric` folder are using for **[Azure Service Fabric](https://azure.microsoft.com/en-us/services/service-fabric/)** hosting purpose. 4 | 5 | The C# project inside this folder will copy all files from dist folder to wwwroot folder and host them as a static side in Azure Service Fabric. 6 | 7 | Currently, I'm using .Net Core 2.1 to make the project is flexible enough to host on any platforms. 8 | 9 | When build the Service Fabric application it will copy all files in `dist` folder to `wwwroot` folder. So ensure you run the `npm build` before deploy the Service Fabric app. 10 | 11 | However if you are not using Service Fabric just simply remove the submodule **sm-react-service-fabric** There is no impact to the application. 12 | -------------------------------------------------------------------------------- /docs/environments/Docker.md: -------------------------------------------------------------------------------- 1 | # Docker Support 2 | 3 | 1. Build Image `docker build`. 4 | 2. Build and Tag the image `docker image build . -f sm-react-docker-nginx/Dockerfile -t [YOUR_ID]/react-materialui-started-kit:latest`. 5 | Example `docker image build . -f sm-react-docker-nginx/Dockerfile -t baoduy2412/react-materialui-started-kit:latest`. 6 | 3. Push inage to Docker hub `docker push [YOUR_ID]/react-materialui-started-kit:latest` 7 | Example `docker push baoduy2412/react-materialui-started-kit:latest`. 8 | 9 | The application will running port 80 and 443 in Docker. 10 | The image can be found in Docker hub [here](https://hub.docker.com/r/baoduy2412/react-materialui-started-kit/). 11 | 12 | However if you are not using Docker just simply remove the submodule **sm-react-docker-nginx** There is no impact to the application. 13 | -------------------------------------------------------------------------------- /docs/environments/GZIP and SSL.md: -------------------------------------------------------------------------------- 1 | ### GZIP and SSL 2 | 3 | The **GZIP** and **SSL** had been applied for all hosting environments above. 4 | -------------------------------------------------------------------------------- /docs/environments/IIS.md: -------------------------------------------------------------------------------- 1 | # IIS Support 2 | 3 | The `Web.config` file in **sm-react-iis** had been added for IIS hosting purpose. This file should be copy along with all files in dist folder when hosting in IIS. 4 | 5 | However if you are not hosting this app in IIS just simply remove the submodule **sm-react-iis** There is no impact to the application. 6 | -------------------------------------------------------------------------------- /docs/environments/NodeJs.md: -------------------------------------------------------------------------------- 1 | # Node Js Hosting 2 | 3 | There is a sub module contains the Express.js configuration to host the application in the Node Js environment. 4 | The application will running port 3000 and 3001 in NodeJs and the port is configurable in the Js file. 5 | 6 | If you are not using Node Js hosting just simply remove the submodule **sm-react-node-express** There is no impact to the application. 7 | But, please note that the `npm run start-prod` is using this submodule to hosting the dist folder as a static side. Please be considered before removing this module. 8 | -------------------------------------------------------------------------------- /docs/environments/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Environment Deployments 2 | 3 | 1. [Azure Service Fabric](Azure Service Fabric) 4 | 2. [Docker](Docker) 5 | 3. [IIS](IIS) 6 | 4. [NodeJs](NodeJs) 7 | 5. [GZIP and SSL](GZIP and SSL) 8 | -------------------------------------------------------------------------------- /docs/styles/LESS.md: -------------------------------------------------------------------------------- 1 | # LESS 2 | 3 | The project is supporting to develop the stylesheet with LESS. All the less files should be placed under **src\assets\less** folder and the LESS import should be in the **src\index.js** file. 4 | 5 | ## Syntax validation 6 | 7 | There is a command had been added to the package.json file which helps to verify the LESS best practices syntax. 8 | 9 | run `npm run lint` or `npm run lint-less` 10 | 11 | ## Stylesheet Bundling 12 | 13 | When packaging the application for Production all stylesheet files _(CSS, LESS and SCSS)_ will be bundled to a single file named **style.css** by the Webpack and storing in the **dist** folder. 14 | 15 | ## Convert Tools 16 | 17 | - Css to Jss [here](https://github.com/cssinjs/cli) 18 | - Css to LESS [here](http://kronus.me/cn/css2less/) in case you don't like Jss. 19 | 20 | ## Uninstall LESS 21 | 22 | LESS support is one of stylesheet development option which supporting by this Starter kid. If you want to use the other stylesheet development options like CSS or SCSS and want to uninstall all LESS related things then follow steps will show you How to remove LESS from this project. 23 | 24 | 1. Run the _npm uninstall —save-dev_ for all below packages: 25 | - less 26 | - less-loader 27 | - lesshint 28 | 2. Remove the below LESS config blog from Webpack config in **configs\webpack\common.js** file: 29 | 30 | ```json 31 | { 32 | "test": /\.less$/, 33 | "use": [ 34 | devMode ? "style-loader" : ExtractCssChunks.loader, 35 | { 36 | "loader": "css-loader", 37 | "options": { "url": false, "sourceMap": false } 38 | }, 39 | "postcss-loader", 40 | { 41 | "loader": "less-loader", 42 | "options": { 43 | "relativeUrls": false, 44 | "sourceMap": false 45 | } 46 | } 47 | ] 48 | } 49 | ``` 50 | 51 | 3. Remove the **less** folder from **scr\asserts** 52 | 4. Remove the LESS scripts at in **package.json** file. 53 | 54 | ```json 55 | "scripts": { 56 | //This is for LESS compilation checking purpose. Remove it if not inuse. 57 | "build-less": "lessc ./src/assets/less/material-dashboard-react.less ./src/assets/less/material-dashboard-react.css", 58 | //Remove the **lint-less** fron this command 59 | "lint": "npm-run-all lint-js lint-less", 60 | //Remove this line 61 | "lint-less": "lesshint ./src/assets/less", 62 | } 63 | ``` 64 | 65 | ## Samples 66 | 67 | There is a sample LESS file named **material-dashboard-react.less** had been added into the project under **scr\asserts** folder which importing to the application in the **index.jsx** file. 68 | 69 | ```javascript 70 | //Style-sheets 71 | import './assets/less/material-dashboard-react.less'; 72 | ``` 73 | -------------------------------------------------------------------------------- /docs/styles/ReadMe.md: -------------------------------------------------------------------------------- 1 | # The stylesheet development tools. 2 | 3 | 1. [LESS](LESS) 4 | 1. [SCSS](scss) 5 | -------------------------------------------------------------------------------- /docs/styles/SCSS.md: -------------------------------------------------------------------------------- 1 | # SCSS 2 | 3 | The project is support to develop the stylesheet with SCSS. All the scss files should be placed under **src\assets\scss** folder and the SCSS import should be in the **src\index.js** file. 4 | 5 | ## Syntax validation 6 | 7 | There is a command had been added to the package.json file which helping to verify the SCSS best practices syntax. 8 | before running below command ensure the sass-lint is installed globally ny command `sass-lint-install`. 9 | 10 | run `npm run lint` or `npm run lint-scss` 11 | 12 | ## Convert Tools 13 | 14 | - LESS to SCSS [here](http://less2scss.awk5.com/). 15 | 16 | ## Stylesheet Bundling 17 | 18 | When packaging the application for Production all stylesheet files _(css, LESS and SCSS)_ will be bundled to a single file named **style.css** by the Webpack and storing in the **dist** folder. 19 | 20 | ## Uninstall SCSS 21 | 22 | SCSS support is one of stylesheet development option which supporting by this Starter kid. If you want to using the other stylesheet development options like CSS or SCSS and want to uninstall all SCSS related things then follow steps will show you How to remove SCSS from this project. 23 | 24 | 1. Run the _npm uninstall —save-dev_ for all below packages: 25 | - node-sass 26 | - sass-loader 27 | 2. Remove the below SCSS config blog from Webpack config in **configs\webpack\common.js** file: 28 | 29 | ```json 30 | { 31 | "test": /\.scss$/, 32 | "use": [ 33 | devMode ? "style-loader" : ExtractCssChunks.loader, 34 | "css-loader", 35 | "postcss-loader", 36 | "sass-loader" 37 | ] 38 | } 39 | ``` 40 | 41 | 3. Remove the **scss** folder from **scr\asserts** 42 | 4. Remove the SCSS scripts at in **package.json** file. 43 | 44 | ```json 45 | "scripts": { 46 | //Remove **lint-scss** from this command 47 | "lint": "npm-run-all lint-js lint-scss", 48 | //Remove this line 49 | "lint-scss": "sass-lint 'src/**/*.scss' -v -q", 50 | //Remove this line 51 | "sass-lint-install": "npm install -g sass-lint", 52 | } 53 | ``` 54 | 55 | ## Samples 56 | 57 | There is an sample SCSS file named **material-dashboard-react.scss** had been added into the project under **scr\asserts** folder which importing to the application in the **index.jsx** file. 58 | 59 | ```javascript 60 | //Style-sheets 61 | import './assets/scss/material-dashboard-react.scss'; 62 | ``` 63 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('cssnano')({ 4 | preset: 'default' 5 | }) 6 | ] 7 | }; 8 | -------------------------------------------------------------------------------- /src/actions/Dashboard/DashboardActions.js: -------------------------------------------------------------------------------- 1 | import { makeThunkAsyncActionCreator } from 'redux-toolbelt-thunk'; 2 | import Api from '../../api'; 3 | 4 | export const getGeneral = makeThunkAsyncActionCreator( 5 | 'FETCH_USER', 6 | Api.GeneralApi.getGeneral 7 | ); 8 | 9 | export const getChartData = makeThunkAsyncActionCreator( 10 | 'FETCH_USER', 11 | Api.ChartApi.getChartData 12 | ); 13 | -------------------------------------------------------------------------------- /src/actions/Dashboard/DashboardReducer.js: -------------------------------------------------------------------------------- 1 | import { makeAsyncReducer } from 'redux-toolbelt'; 2 | import * as actions from './DashboardActions'; 3 | 4 | const chartsReducer = makeAsyncReducer(actions.getChartData); 5 | const generalReducer = makeAsyncReducer(actions.getGeneral); 6 | 7 | export default { 8 | chartsReducer, 9 | generalReducer 10 | }; 11 | -------------------------------------------------------------------------------- /src/actions/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | import * as Actions from './DashboardActions'; 2 | import Reducer from './DashboardReducer'; 3 | 4 | export { Actions, Reducer }; 5 | export default Actions; 6 | -------------------------------------------------------------------------------- /src/actions/MessageBox/MessageBoxActions.js: -------------------------------------------------------------------------------- 1 | import { makeActionCreator } from 'redux-toolbelt'; 2 | //import { makeThunkAsyncActionCreator } from 'redux-toolbelt-thunk'; 3 | import dayjs from 'dayjs'; 4 | import { NotificationStatus } from '../../components/Notification'; 5 | import { newGuid } from '../../commons/commonFuncs'; 6 | 7 | //Moved the type of all actions on top so it will be eaiser if need refectoring the names. 8 | export const TYPES = { 9 | Shown: '@MessageBox/Shown', 10 | Hided: '@MessageBox/Hided' 11 | }; 12 | 13 | export function showMessage(type, message, handler) { 14 | return dispatch => { 15 | const callback = event => { 16 | try { 17 | if (handler) handler(event); 18 | } finally { 19 | dispatch({ 20 | type: TYPES.Hided 21 | }); 22 | } 23 | }; 24 | 25 | dispatch({ 26 | type: TYPES.Shown, 27 | payload: { 28 | id: newGuid(), 29 | type, 30 | message, 31 | actionHandler: callback 32 | } 33 | }); 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/actions/MessageBox/MessageBoxReducer.js: -------------------------------------------------------------------------------- 1 | import { TYPES } from './MessageBoxActions'; 2 | 3 | export default function MessageBoxReducer(state = {}, action) { 4 | switch (action.type) { 5 | case TYPES.Shown: 6 | return Object.assign(action.payload, { 7 | open: true 8 | }); 9 | case TYPES.Hided: 10 | return Object.assign({}, state, { 11 | message: '', 12 | handler: undefined, 13 | open: false 14 | }); 15 | default: 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/actions/MessageBox/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './MessageBoxActions'; 2 | 3 | export { default as Reducer } from './MessageBoxReducer'; 4 | export default actions; 5 | -------------------------------------------------------------------------------- /src/actions/Notifications/NotificationActions.js: -------------------------------------------------------------------------------- 1 | import { makeActionCreator } from 'redux-toolbelt'; 2 | //import { makeThunkAsyncActionCreator } from 'redux-toolbelt-thunk'; 3 | import dayjs from 'dayjs'; 4 | import { NotificationStatus } from '../../components/Notification'; 5 | import { newGuid } from '../../commons/commonFuncs'; 6 | 7 | //Moved the type of all actions on top so it will be eaiser if need refectoring the names. 8 | const TYPES = { 9 | AddedOrUpdated: '@Notification/AddedOrUpdated', 10 | Deleted: '@Notification/Deleted' 11 | }; 12 | 13 | /** 14 | * Create new notification item 15 | * 16 | * @export changeStatus 17 | * @param {NotificationType} type [Default is INFO] the type of the notification 18 | * @param {string} message the notification message 19 | * @param {string?} title the notification message 20 | * @param {string?} group group notification by group name. 21 | * @param {func?} onClick the click action of notification. 22 | * @returns {NotificationItemPropTypes} 23 | */ 24 | export function newNotification(type, message, title, group, onClick) { 25 | if (typeof title === 'function' && !onClick) { 26 | onClick = title; 27 | title = ''; 28 | } 29 | 30 | if (typeof group === 'function' && !onClick) { 31 | onClick = group; 32 | group = ''; 33 | } 34 | 35 | return { 36 | id: newGuid(), 37 | type, 38 | message, 39 | title, 40 | group, 41 | createdOn: dayjs(), 42 | onClick, 43 | status: NotificationStatus.NEW 44 | }; 45 | } 46 | 47 | /** 48 | * Add or Update Notifications to Redux Store. 49 | * 50 | * @export addOrUpdateNotification 51 | * @param {Array of NotificationItemPropTypes} items 52 | * @returns 53 | */ 54 | export const addOrUpdateNotifications = makeActionCreator(TYPES.AddedOrUpdated); 55 | 56 | /** 57 | * Delete the Notitications 58 | * 59 | * @export deleteNotifications 60 | * @param {Array of NotificationItemPropTypes} items 61 | * @returns 62 | */ 63 | export const deleteNotifications = makeActionCreator(TYPES.Deleted); 64 | 65 | /** 66 | * Create notification Item by calling newNotification and then call addOrUpdateNotification to add to Redux Store. 67 | * 68 | * @export notify 69 | * @param {NotificationType} type 70 | * @param {string} message 71 | * @param {string?} title 72 | * @param {string?} group 73 | * @param {func?} onClick 74 | * @returns 75 | */ 76 | export function notify(type, message, title, group, onClick) { 77 | return addOrUpdateNotifications( 78 | newNotification(type, message, title, group, onClick) 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /src/actions/Notifications/NotificationReducer.js: -------------------------------------------------------------------------------- 1 | import { makeReducer } from 'redux-toolbelt'; 2 | import { isArray } from 'lodash'; 3 | import { 4 | addOrUpdateNotifications, 5 | deleteNotifications 6 | } from './NotificationActions'; 7 | import linq from 'linq'; 8 | import { Merge } from '../../commons/commonFuncs'; 9 | 10 | export default makeReducer( 11 | { 12 | [addOrUpdateNotifications]: (state, { payload }) => { 13 | return Merge(state, isArray(payload) ? payload : [payload]); 14 | }, 15 | [deleteNotifications]: (state, { payload }) => { 16 | const deleteItems = isArray(payload) ? payload : [payload]; 17 | 18 | return linq 19 | .from(state) 20 | .where(i => !deleteItems.find(n => n.id === i.id)) 21 | .toArray(); 22 | } 23 | }, 24 | { defaultState: [] } 25 | ); 26 | -------------------------------------------------------------------------------- /src/actions/Notifications/index.js: -------------------------------------------------------------------------------- 1 | import * as Actions from './NotificationActions'; 2 | import Reducer from './NotificationReducer'; 3 | 4 | export { Actions, Reducer }; 5 | export default Actions; 6 | -------------------------------------------------------------------------------- /src/actions/Users/UserActions.js: -------------------------------------------------------------------------------- 1 | import * as api from '../../api/userApi'; 2 | export const TYPES = { 3 | Request: '@User/Request', 4 | GetAllUsers: '@User/GetAllUsers', 5 | GetUserById: '@User/GetUserById', 6 | SaveUser: '@User/GetUserById', 7 | DeleteUser: '@User/DeleteUser' 8 | }; 9 | 10 | function getUserSuccess(users) { 11 | return { 12 | type: TYPES.GetAllUsers, 13 | payload: users 14 | }; 15 | } 16 | export const getAllUsers = () => { 17 | return dispatch => { 18 | dispatch({ 19 | type: TYPES.Request 20 | }); 21 | return api.getAllUsers().then(users => { 22 | dispatch(getUserSuccess(users)); 23 | }); 24 | }; 25 | }; 26 | 27 | function saveUserSuccess(user) { 28 | return { 29 | type: TYPES.SaveUser, 30 | payload: user 31 | }; 32 | } 33 | export const saveUser = user => { 34 | return dispatch => { 35 | dispatch({ 36 | type: TYPES.Request 37 | }); 38 | return api.saveUser(user).then(response => { 39 | dispatch(saveUserSuccess(response)); 40 | }); 41 | }; 42 | }; 43 | 44 | function deleteUserSuccess(id) { 45 | return { 46 | type: TYPES.DeleteUser, 47 | payload: id 48 | }; 49 | } 50 | export const deleteUser = id => { 51 | return dispatch => { 52 | dispatch({ 53 | type: TYPES.Request 54 | }); 55 | return api.deleteUser(id).then(response => { 56 | let payload = null; 57 | if (response) { 58 | payload = id; 59 | } 60 | dispatch(deleteUserSuccess(payload)); 61 | }); 62 | }; 63 | }; -------------------------------------------------------------------------------- /src/actions/Users/UserReducer.js: -------------------------------------------------------------------------------- 1 | import { 2 | TYPES 3 | } from './UserActions'; 4 | import { 5 | Merge 6 | } from '../../commons/commonFuncs'; 7 | import { 8 | isArray 9 | } from 'lodash'; 10 | // const getUser = makeAsyncReducer(getUserById, { 11 | // defaultData: {} 12 | // }); 13 | // const getUsers = makeAsyncReducer(getAllUsers, { 14 | // defaultData: [] 15 | // }); 16 | // export default composeReducers({ 17 | // UserById: getUser, 18 | // AllUsers: getUsers 19 | // }); 20 | //export default getUsers; 21 | export default (state = { 22 | data: [] 23 | }, action) => { 24 | switch (action.type) { 25 | case TYPES.GetAllUsers: 26 | return { ...state, 27 | data: [...action.payload], 28 | isLoading: false 29 | }; 30 | case TYPES.SaveUser: 31 | return { ...state, 32 | data: Merge(state.data, isArray(action.payload) ? action.payload : [action.payload]), 33 | isLoading: false 34 | }; 35 | case TYPES.DeleteUser: 36 | const oldUsers = [...state.data]; 37 | return { 38 | ...state, 39 | data: oldUsers.filter(i => i.id !== action.payload), 40 | isLoading: false 41 | }; 42 | case TYPES.Request: 43 | return { 44 | ...state, 45 | isLoading: true 46 | }; 47 | default: 48 | return state; 49 | } 50 | }; -------------------------------------------------------------------------------- /src/actions/Users/index.js: -------------------------------------------------------------------------------- 1 | import * as Actions from './UserActions'; 2 | import Reducer from './UserReducer'; 3 | 4 | 5 | export { 6 | Actions, 7 | Reducer 8 | }; 9 | export default Actions; -------------------------------------------------------------------------------- /src/api/chartApi.js: -------------------------------------------------------------------------------- 1 | import charts from '../../tests/_dummy_data/charts'; 2 | import simulator from '../../tests/_dummy_data/simulator'; 3 | 4 | //Calling Api and return data 5 | //This will use timeout to simulate server call. 6 | export function getChartData() { 7 | return simulator(charts); 8 | } 9 | -------------------------------------------------------------------------------- /src/api/generalApi.js: -------------------------------------------------------------------------------- 1 | import general from '../../tests/_dummy_data/general'; 2 | import simulator from '../../tests/_dummy_data/simulator'; 3 | 4 | //This will use timeout to simulate server call. 5 | export function getGeneral() { 6 | return simulator(general); 7 | } 8 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | import * as GeneralApi from './generalApi'; 2 | import * as ChartApi from './chartApi'; 3 | 4 | export default { 5 | GeneralApi, 6 | ChartApi 7 | }; 8 | -------------------------------------------------------------------------------- /src/api/userApi.js: -------------------------------------------------------------------------------- 1 | import urljoin from 'url-join'; 2 | import * as evnConfig from '../settings'; 3 | import axios from 'axios'; 4 | 5 | const Root = urljoin(evnConfig.webService, 'api/users/'); 6 | 7 | export const getAllUsers = () => 8 | axios.get(Root).then(response => response.data); 9 | 10 | export const getUserById = id => 11 | axios.get(urljoin(Root, `${id}`)).then(response => response.data); 12 | 13 | export const saveUser = user => 14 | axios.post(Root, user).then(response => response.data); 15 | 16 | export const deleteUser = id => 17 | axios.delete(urljoin(Root, `${id}`)).then(response => response.data); 18 | 19 | export const isUsernameOrEmailExist = (usernameOrEmail, id) => 20 | axios 21 | .get(urljoin(Root, 'IsEmailOrUserNameExist', usernameOrEmail, `${id}`)) 22 | .then(response => response.data); 23 | -------------------------------------------------------------------------------- /src/assets/img/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/apple-icon.png -------------------------------------------------------------------------------- /src/assets/img/cover.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/cover.jpeg -------------------------------------------------------------------------------- /src/assets/img/faces/marc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/faces/marc.jpg -------------------------------------------------------------------------------- /src/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/favicon.png -------------------------------------------------------------------------------- /src/assets/img/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/mask.png -------------------------------------------------------------------------------- /src/assets/img/new_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/new_logo.png -------------------------------------------------------------------------------- /src/assets/img/react_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/img/sidebar-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/sidebar-1.jpg -------------------------------------------------------------------------------- /src/assets/img/sidebar-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/sidebar-2.jpg -------------------------------------------------------------------------------- /src/assets/img/sidebar-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/sidebar-3.jpg -------------------------------------------------------------------------------- /src/assets/img/sidebar-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/sidebar-4.jpg -------------------------------------------------------------------------------- /src/assets/img/tim_80x80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/assets/img/tim_80x80.png -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/cardImagesStyles.jsx: -------------------------------------------------------------------------------- 1 | const cardImagesStyles = { 2 | cardImgTop: { 3 | width: "100%", 4 | borderTopLeftRadius: "calc(.25rem - 1px)", 5 | borderTopRightRadius: "calc(.25rem - 1px)" 6 | }, 7 | cardImgBottom: { 8 | width: "100%", 9 | borderBottomRightRadius: "calc(.25rem - 1px)", 10 | borderBottomLeftRadius: "calc(.25rem - 1px)" 11 | }, 12 | cardImgOverlay: { 13 | position: "absolute", 14 | top: "0", 15 | right: "0", 16 | bottom: "0", 17 | left: "0", 18 | padding: "1.25rem" 19 | }, 20 | cardImg: { 21 | width: "100%", 22 | borderRadius: "calc(.25rem - 1px)" 23 | } 24 | }; 25 | 26 | export default cardImagesStyles; 27 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/checkboxAdnRadioStyle.jsx: -------------------------------------------------------------------------------- 1 | import { primaryColor } from "../material-dashboard-react.jsx"; 2 | 3 | const checkboxAdnRadioStyle = { 4 | checked: { 5 | color: primaryColor + "!important" 6 | }, 7 | checkedIcon: { 8 | width: "20px", 9 | height: "20px", 10 | border: "1px solid rgba(0, 0, 0, .54)", 11 | borderRadius: "3px" 12 | }, 13 | uncheckedIcon: { 14 | width: "0px", 15 | height: "0px", 16 | padding: "10px", 17 | border: "1px solid rgba(0, 0, 0, .54)", 18 | borderRadius: "3px" 19 | }, 20 | radio: { 21 | color: primaryColor + "!important" 22 | }, 23 | radioChecked: { 24 | width: "20px", 25 | height: "20px", 26 | border: "1px solid " + primaryColor, 27 | borderRadius: "50%" 28 | }, 29 | radioUnchecked: { 30 | width: "0px", 31 | height: "0px", 32 | padding: "10px", 33 | border: "1px solid rgba(0, 0, 0, .54)", 34 | borderRadius: "50%" 35 | } 36 | }; 37 | 38 | export default checkboxAdnRadioStyle; 39 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/dropdownStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | primaryBoxShadow, 4 | defaultFont 5 | } from "../material-dashboard-react.jsx"; 6 | 7 | const dropdownStyle = theme => ({ 8 | buttonLink: { 9 | [theme.breakpoints.down("md")]: { 10 | display: "flex", 11 | marginLeft: "30px", 12 | width: "auto" 13 | } 14 | }, 15 | links: { 16 | width: "20px", 17 | height: "20px", 18 | zIndex: "4", 19 | [theme.breakpoints.down("md")]: { 20 | display: "block", 21 | width: "30px", 22 | height: "30px", 23 | color: "#a9afbb", 24 | marginRight: "15px" 25 | } 26 | }, 27 | linkText: { 28 | zIndex: "4", 29 | ...defaultFont, 30 | fontSize: "14px" 31 | }, 32 | popperClose: { 33 | pointerEvents: "none" 34 | }, 35 | pooperResponsive: { 36 | [theme.breakpoints.down("md")]: { 37 | zIndex: "1640", 38 | position: "static", 39 | float: "none", 40 | width: "auto", 41 | marginTop: "0", 42 | backgroundColor: "transparent", 43 | border: "0", 44 | WebkitBoxShadow: "none", 45 | boxShadow: "none", 46 | color: "black" 47 | } 48 | }, 49 | dropdown: { 50 | borderRadius: "3px", 51 | border: "0", 52 | boxShadow: "0 2px 5px 0 rgba(0, 0, 0, 0.26)", 53 | top: "100%", 54 | zIndex: "1000", 55 | minWidth: "160px", 56 | padding: "5px 0", 57 | margin: "2px 0 0", 58 | fontSize: "14px", 59 | textAlign: "left", 60 | listStyle: "none", 61 | backgroundColor: "#fff", 62 | WebkitBackgroundClip: "padding-box", 63 | backgroundClip: "padding-box" 64 | }, 65 | dropdownItem: { 66 | ...defaultFont, 67 | fontSize: "13px", 68 | padding: "10px 20px", 69 | margin: "0 5px", 70 | borderRadius: "2px", 71 | WebkitTransition: "all 150ms linear", 72 | MozTransition: "all 150ms linear", 73 | OTransition: "all 150ms linear", 74 | MsTransition: "all 150ms linear", 75 | transition: "all 150ms linear", 76 | display: "block", 77 | clear: "both", 78 | fontWeight: "400", 79 | lineHeight: "1.42857143", 80 | color: "#333", 81 | whiteSpace: "nowrap", 82 | height: "unset", 83 | "&:hover": { 84 | backgroundColor: primaryColor, 85 | color: "#FFFFFF", 86 | ...primaryBoxShadow 87 | } 88 | } 89 | }); 90 | 91 | export default dropdownStyle; 92 | -------------------------------------------------------------------------------- /src/assets/jss/material-dashboard-react/tooltipStyle.jsx: -------------------------------------------------------------------------------- 1 | const tooltipStyle = { 2 | tooltip: { 3 | padding: "10px 15px", 4 | minWidth: "130px", 5 | color: "#555555", 6 | lineHeight: "1.7em", 7 | background: "#FFFFFF", 8 | border: "none", 9 | borderRadius: "3px", 10 | boxShadow: 11 | "0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2)", 12 | maxWidth: "200px", 13 | textAlign: "center", 14 | fontFamily: "'Helvetica Neue',Helvetica,Arial,sans-serif", 15 | fontSize: "12px", 16 | fontStyle: "normal", 17 | fontWeight: "400", 18 | textShadow: "none", 19 | textTransform: "none", 20 | letterSpacing: "normal", 21 | wordBreak: "normal", 22 | wordSpacing: "normal", 23 | wordWrap: "normal", 24 | whiteSpace: "normal", 25 | lineBreak: "auto" 26 | } 27 | }; 28 | export default tooltipStyle; 29 | -------------------------------------------------------------------------------- /src/commons/checks.js.ignore: -------------------------------------------------------------------------------- 1 | export const isNull = item => item === null; 2 | export const isUndefined = item => item === undefined; 3 | export const isEmpty = item => item === '' || item === {} || item === []; 4 | export const isFunction = item => typeof item === 'function'; 5 | export const isString = item => typeof item === 'string'; 6 | export const isArray = item => Array.isArray(item); 7 | -------------------------------------------------------------------------------- /src/commons/commonControlRenderers.jsx: -------------------------------------------------------------------------------- 1 | import TextField from '@material-ui/core/TextField'; 2 | import FormControl from '@material-ui/core/FormControl'; 3 | 4 | export const renderTextField = ({ 5 | input, 6 | label, 7 | meta: { touched, error, invalid }, 8 | formControlProps = {}, 9 | ...custom 10 | }) => { 11 | return ( 12 | 13 | 21 | 22 | ); 23 | }; 24 | 25 | export const renderInputFile = ({ 26 | input, 27 | label, 28 | meta: { touched, error, invalid }, 29 | fileRef, 30 | onChangeFile, 31 | ...custom 32 | }) => { 33 | return ( 34 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/commons/commonFuncs.ts: -------------------------------------------------------------------------------- 1 | /*eslint no-console: ["off", { allow: ["warn", "error"] }] */ 2 | 3 | import linq from 'linq'; 4 | import uuidv4 from 'uuid/v4'; 5 | import Default from './constants.js'; 6 | //correct URL for Reserved proxy 7 | //Get the millisecond of current time. 8 | 9 | export const GetBaseUrl = () => { 10 | const key = 'BASE_URL'; 11 | 12 | const base: string = 13 | window.sessionStorage.getItem(key) || 14 | document.getElementsByTagName('base')[0].getAttribute('href') || 15 | '/'; 16 | 17 | window.sessionStorage.setItem(key, base); 18 | 19 | /* devblock:start */ 20 | console.log(`base URL is ${base}`); 21 | /* devblock:end */ 22 | 23 | return base; 24 | }; 25 | 26 | export const newGuid = () => uuidv4(); 27 | 28 | /** 29 | *getImgSrc for both normal hosting and Reverse proxy 30 | * 31 | * @export 32 | * @param {string} url the relative image url 33 | * @returns real url 34 | */ 35 | export function getImgSrc(url: string) { 36 | //if (typeof url !== 'string') return url; 37 | const base = GetBaseUrl(); 38 | 39 | return !base || base === '/' || url.indexOf(base) >= 0 40 | ? url 41 | : `${base}/${url}`; 42 | } 43 | 44 | /** 45 | *Merge second array to first array if existed then update. 46 | * 47 | * @export 48 | * @param {Array} [fistArray=[]] 49 | * @param {Array} [secondArray=[]] 50 | * @param {Function} [selector=i => i.id] 51 | */ 52 | export function Merge( 53 | fistArray = [], 54 | secondArray = [], 55 | selector = (i: any) => i.id 56 | ) { 57 | const firstQuery = linq.from(fistArray); 58 | const secondQuery = linq.from(secondArray); 59 | 60 | const news = secondQuery.where(i => 61 | firstQuery.all(f => selector(f) !== selector(i)) 62 | ); 63 | 64 | const exists = secondQuery.where(i => 65 | firstQuery.any(f => selector(f) === selector(i)) 66 | ); 67 | 68 | return firstQuery 69 | .select(i => { 70 | const found = exists.firstOrDefault(e => selector(e) === selector(i)); 71 | return found ? Object.assign({}, i, found) : i; 72 | }) 73 | .union(news) 74 | .toArray(); 75 | } 76 | export function getAvatar(avatar: string): string { 77 | const tmp = avatar 78 | ? avatar.includes('data:image') 79 | ? avatar 80 | : `data:image/png;base64,${avatar}` 81 | : Default.DefaultAvatar; 82 | return tmp; 83 | } 84 | 85 | export function isValidEmail(email: string): boolean { 86 | if (email && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email)) { 87 | return false; 88 | } 89 | return true; 90 | } 91 | 92 | export function convertToFormData(object: any): FormData | undefined { 93 | if (!object) return undefined; 94 | 95 | const formData = new FormData(); 96 | for (var key in object) formData.append(key, object[key]); 97 | 98 | return formData; 99 | } 100 | -------------------------------------------------------------------------------- /src/commons/constants.js: -------------------------------------------------------------------------------- 1 | const DefaultDateFormat = ''; 2 | const DefaultAvatar = 3 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAG2YAABzjgAA+swAAIT6AAB5gQAA/RMAADBtAAASKQ0eJk4AAAMAUExURevr7Jydn5manJucnuPj5JiZm9vb3PT09MvLzMLCw5GSlPr6+qeoqdXV1tHR0rW1trW2uKusrbi4urO0tLq6vKiqq7e4uLW2tqGipLm6ury8vr6+vr+/wLu8vM7OztDQ0cjJytjY2dTU1aipqqqrrMXGxqanqKmqq6Wmp9LT062usKSlprS0ta2trra2t6ytrqWmqK2ur66vsK6ur7CwsbOztK+vsK+wsbKys7GxsqOkpqOkpZ6foampq6qqrJ2eoKenqaysraKjpZqbnbi4uaKjpKSlp5eYmqioqqurrKChory8vKGio6amqKCho7Gys7CxsrGysrKzs5+gop+gobS1tbq6u7e3uLCxsbKztLi4uJWWmJaXmba3t7i5ub29vZWXmZSWmLq6upOVl5SVl7m5up6foLq7u7u7vJqcnZOUlpeZm5aYmpKUlry8vZKTlpGTlZCRlJCRk6ytr9bX197e3q+vsb2+v8DAweDh4bOztebm5ujp6e/v756eoP///4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouLi4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2dnZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+vr7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT09TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl5ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///zfARREAAAeqSURBVHja7N3xVxJZFMBxQAQVilzPSTslBxcDdwdtBQRBDQUFBYNMDTcoNLAMM7QD23JozvvX94cJZBhQZ+bdO2/26F/Q59wv8K48JwP5n/wY7iH3kHvIPYT6D89bLBazfiF8Y+T09PT09MOHzc3Nd+8yeoTwb99+/PjkSbfj8eN83qYPSCOTOTs7O3veEP69UkcegEIdMpPJCI6zz5/f9p9HPp9//97KMsTwLZO5o2Nh4RmrkHrpmxzHwsICzyLEWSq1IXd1TE6aWYNYSqVSqSRzHpOTDsdDliC2ksBQ4HA4zOxA6iURRJ7DccizAbE9fVpyNXhlXTkcjsNDHxMQKyGEWBV35Tg89Pl8rKTVUtHVoc/n89mYgPBK3686Dt8EIcQwMzOjKUTF+1XHMbG0tLT095s3y8vL77WCqHq/Ehy+Lsdybm9aC4irRKGrLkcut7cXiUTQIS1588jfPo/cXiQSibxGhtTpdyU4kCElyq/zXMdx9AwT0qI+j1zbEQwiQmyUX+fXXR0Fg0EXGsQK1dXRUTAYDIbRIJBdBYNhNIgFsqtgOBx2IkFguwqHQyEcSAu2q3AICwLcVSjEh1oYEPCuQgbix4A4obsK+QmPAwHtKhTy+3+gvEacsF2F/H5/wIAIAevK7/cHAhgQgPNuzzwCgcAiFgSoK3/bgQWhuUdJuwosYkGgu1pcnDdgQCju5wPmMY8CAeyqPY/5+To8BKErPAjY50fb8QIHAnIu6Z7HCxwI0Lmk24ECge8KB4LQ1YuNDYS3X4SuNjbGESAIXW2M40DAuxofH0c4ayF0hQNB6AoJAt8VDgShq/GdHQwIYFfteezMIkCgzrsiBwZkhPp+LulqFgXyB/X9XDoPFEgTviscCE99P+/+/PjlwIAQ6vu5dB4vX2JAALvaaTu8OBCoc0m7q5deHAjYuaQzD68fAwJ3Luk4vC4MyAh4V17vAwxIE7orr9fLY0AIdFde7xpBg8B09cuBCAHtyrvmxoLAdrXm/h0DUgfvas3tdvPQEBdCV2632+3eHgWFwL9ftR12+yYg5CfIfi7pyu3ettvtdkAI2B4lnYfdbndBQiD28wGO9XUwiAFmP5e8zu0CxGOFgrgQu1pf93hcUJA84HlX/DoXIDkoCNB+3r8rj8fjgYIA7ef9u4KGoHXl8XheAUKAzyXdXXlezcFBwM8lXV29moODoHY1BwfB7QoSgtoVIARuj+rXFTAEtCvxPJKPoCBm1K6SyZ9gx3jorrodc0kObh/B7CrJzQJCELviZA1EJsQAtp9LuwKFENA9SjwPbhoSYgbbzwVGl0PeQGT/gg5sP+/tipuFhRCkruQORD5kFPa8qzAsJb/Ehj3v/uqKM8NDSANmPxd11cD5fgS8K5Q/3yOE2L7l9vYWALpKPuI4jsP7M3DhB+JcovyBbmoeJ0K/K45oASG0u0py09pAND7v0oM0qe7nSU4zCKG7R2kJofw5yGsFcVLcz1UOROXztah2pSWE6vkqatUO0qS2n3McFyXaQYiP2h6l0qH6qYCU9nMuGmUBon6P4qLRh9pCrJS6UjsQ9Q+cXKKwR3HRaPQfrSGESlfRFaI5pK7284OKg8aDiyl0tcIEpKV4j6LooPIoaYParlZ4NiBE2X5+7fiXMAIh6rpaIcxAeDVdUXHQem78A/n7ecfRYAlCphXsURTnQfG/JJC/RwkO9iCy9yjBEWcNoqyrlTiTEPldxZmEKOgqHt9iDyL782MlHo9vsQeRtZ9fO5iDyNyjOg72IIq62tqaYg0ib4+6drAJkd3VFJMQBV1NTe2yC5HTFZOQO+9R3fPYZQ4iu6spwcEq5PY9StTV7m6WLYjtVFlXu9nsCDMQi9+ruKtsNptOp9N/WTWHmJ/I2s8l88im0+n0/v7+Qc6mHcSiZI8a4Dg4OFhdVXGnRjHks9I9StpVx7G6uvrpU8yJCHmudD+/cR6CIxaLxR5gQJT/3ufOjljMaDQ6ASHN18q+P5fRVcdhNBqNQ9tWAEhL+ffnCuZhNBqNQ0NDQ0N+qpCmyu85lTu+fEkkEnYLDcio6u/PFXbVcSQSiYTJ5FQFeU7le0518xAcJpPJNKMMUqL0/Tk1h2l4eHh4RB6kPkHlHvKA827HsS/bMXx+nvqtdUcItfvt9OcxfH6eSqVSqa8/boU06d2/AnR8LRaLN0Pq9O6339aVckfqa7FYLB7fBKF0DxlhHsXj42PjQEgIqqspCEehUBgA8dG5347S1fFxoVAo2PpCWrrqqlAoFE5OWn0gBr11VTg5Oan0gdC63w45j1Svo2KVQujdQ8bq6qRSqVQkkAlq95DxuqpUKmUJhNb9dsyuKpVyeVQC0WNX5XK52gOx6LKrcrlabYghE5Tut+N2VS5Xq3YxhNo9ZNyuqtXqhQhipXS/HburavVCDKkzvUcN7qp60QPZ1GtXFxc1EYTV/fwaMmgetV4Iu/v5jV3VarW6CML2fj64q1qt9mc3hPX9fPA8ape9ED3sUX3mcXl5KYboY4/q5xgTQdjfz/t31QvRb1djYz0QvXYlhbC/n/ftqgeinz1KMo8eiG72KKlDBNHPHtXbVT+ILrsaG7vqgei0q6ur770QfXZ19V2A/DcAHpmC7NcZ4/wAAAAASUVORK5CYII='; 4 | export default { 5 | DefaultDateFormat, 6 | DefaultAvatar 7 | }; 8 | -------------------------------------------------------------------------------- /src/commons/exceptionService.js: -------------------------------------------------------------------------------- 1 | export function getErrorMessage(error, info) { 2 | if (typeof error === 'string') return error; 3 | return 'There is an error had been uncured. Please contact your App Support for details.'; 4 | } 5 | -------------------------------------------------------------------------------- /src/commons/guard.js: -------------------------------------------------------------------------------- 1 | //Some basic validation and exception 2 | import { 3 | isNull, 4 | isUndefined, 5 | isEmpty, 6 | isFunction, 7 | isString, 8 | isArray 9 | } from 'lodash'; 10 | 11 | export function argumentNotNull(object, name) { 12 | if (isNull(object) || isUndefined(object)) 13 | throw `${name || 'Argument'} must not be null or undefined.`; 14 | } 15 | 16 | export function argumentNotEmpty(object, name) { 17 | argumentNotNull(object, name); 18 | if (isEmpty(object)) throw `${name || 'Argument'} must not be empty.`; 19 | } 20 | 21 | export function argumentIsFunc(func, name) { 22 | if (!isFunction(func)) throw `${name || 'Argument'} is must be a Function.`; 23 | } 24 | 25 | export function argumentIsString(object, name) { 26 | if (!isString(object)) throw `${name || 'Argument'} is must be a String.`; 27 | } 28 | 29 | export function argumentIsStringAndNotEmpty(object, name) { 30 | argumentNotEmpty(object, name); 31 | argumentIsString(object.name); 32 | } 33 | 34 | export function argumentIsArray(object, name) { 35 | if (!isArray(object)) throw `${name || 'Argument'} is must be an Array.`; 36 | } 37 | 38 | export function argumentIsArrayAndNotEmpty(object, name) { 39 | argumentIsArray(object, name); 40 | if (isEmpty(object)) throw `${name || 'Argument'} is must not be empty.`; 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Card/Card.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import withStyles from '@material-ui/core/styles/withStyles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import cardStyle from './cardStyle.jsx'; 12 | 13 | function Card({ ...props }) { 14 | const { 15 | classes, 16 | className, 17 | children, 18 | plain, 19 | profile, 20 | chart, 21 | ...rest 22 | } = props; 23 | const cardClasses = classNames({ 24 | [classes.card]: true, 25 | [classes.cardPlain]: plain, 26 | [classes.cardProfile]: profile, 27 | [classes.cardChart]: chart, 28 | [className]: className !== undefined 29 | }); 30 | return ( 31 |
32 | {children} 33 |
34 | ); 35 | } 36 | 37 | Card.propTypes = { 38 | classes: PropTypes.object.isRequired, 39 | className: PropTypes.string, 40 | plain: PropTypes.bool, 41 | profile: PropTypes.bool, 42 | chart: PropTypes.bool 43 | }; 44 | 45 | export default withStyles(cardStyle)(Card); 46 | -------------------------------------------------------------------------------- /src/components/Card/CardAvatar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import withStyles from '@material-ui/core/styles/withStyles'; 8 | // @material-ui/icons 9 | // core components 10 | 11 | import cardAvatarStyle from './cardAvatarStyle.jsx'; 12 | 13 | function CardAvatar({ ...props }) { 14 | const { classes, children, className, plain, profile, ...rest } = props; 15 | const cardAvatarClasses = classNames({ 16 | [classes.cardAvatar]: true, 17 | [classes.cardAvatarProfile]: profile, 18 | [classes.cardAvatarPlain]: plain, 19 | [className]: className !== undefined 20 | }); 21 | return ( 22 |
23 | {children} 24 |
25 | ); 26 | } 27 | 28 | CardAvatar.propTypes = { 29 | children: PropTypes.node.isRequired, 30 | className: PropTypes.string, 31 | profile: PropTypes.bool, 32 | plain: PropTypes.bool 33 | }; 34 | 35 | export default withStyles(cardAvatarStyle)(CardAvatar); 36 | -------------------------------------------------------------------------------- /src/components/Card/CardBody.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import withStyles from '@material-ui/core/styles/withStyles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import cardBodyStyle from './cardBodyStyle.jsx'; 12 | 13 | function CardBody({ ...props }) { 14 | const { classes, className, children, plain, profile, ...rest } = props; 15 | const cardBodyClasses = classNames({ 16 | [classes.cardBody]: true, 17 | [classes.cardBodyPlain]: plain, 18 | [classes.cardBodyProfile]: profile, 19 | [className]: className !== undefined 20 | }); 21 | return ( 22 |
23 | {children} 24 |
25 | ); 26 | } 27 | 28 | CardBody.propTypes = { 29 | classes: PropTypes.object.isRequired, 30 | className: PropTypes.string, 31 | plain: PropTypes.bool, 32 | profile: PropTypes.bool 33 | }; 34 | 35 | export default withStyles(cardBodyStyle)(CardBody); 36 | -------------------------------------------------------------------------------- /src/components/Card/CardFooter.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import withStyles from "@material-ui/core/styles/withStyles"; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import cardFooterStyle from "./cardFooterStyle.jsx"; 12 | 13 | function CardFooter({ ...props }) { 14 | const { 15 | classes, 16 | className, 17 | children, 18 | plain, 19 | profile, 20 | stats, 21 | chart, 22 | ...rest 23 | } = props; 24 | const cardFooterClasses = classNames({ 25 | [classes.cardFooter]: true, 26 | [classes.cardFooterPlain]: plain, 27 | [classes.cardFooterProfile]: profile, 28 | [classes.cardFooterStats]: stats, 29 | [classes.cardFooterChart]: chart, 30 | [className]: className !== undefined 31 | }); 32 | return ( 33 |
34 | {children} 35 |
36 | ); 37 | } 38 | 39 | CardFooter.propTypes = { 40 | classes: PropTypes.object.isRequired, 41 | className: PropTypes.string, 42 | plain: PropTypes.bool, 43 | profile: PropTypes.bool, 44 | stats: PropTypes.bool, 45 | chart: PropTypes.bool 46 | }; 47 | 48 | export default withStyles(cardFooterStyle)(CardFooter); 49 | -------------------------------------------------------------------------------- /src/components/Card/CardHeader.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | // @material-ui/core components 7 | import withStyles from "@material-ui/core/styles/withStyles"; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import cardHeaderStyle from "./cardHeaderStyle.jsx"; 12 | 13 | function CardHeader({ ...props }) { 14 | const { 15 | classes, 16 | className, 17 | children, 18 | color, 19 | plain, 20 | stats, 21 | icon, 22 | ...rest 23 | } = props; 24 | const cardHeaderClasses = classNames({ 25 | [classes.cardHeader]: true, 26 | [classes[color + "CardHeader"]]: color, 27 | [classes.cardHeaderPlain]: plain, 28 | [classes.cardHeaderStats]: stats, 29 | [classes.cardHeaderIcon]: icon, 30 | [className]: className !== undefined 31 | }); 32 | return ( 33 |
34 | {children} 35 |
36 | ); 37 | } 38 | 39 | CardHeader.propTypes = { 40 | classes: PropTypes.object.isRequired, 41 | className: PropTypes.string, 42 | color: PropTypes.oneOf([ 43 | "warning", 44 | "success", 45 | "danger", 46 | "info", 47 | "primary", 48 | "rose" 49 | ]), 50 | plain: PropTypes.bool, 51 | stats: PropTypes.bool, 52 | icon: PropTypes.bool 53 | }; 54 | 55 | export default withStyles(cardHeaderStyle)(CardHeader); 56 | -------------------------------------------------------------------------------- /src/components/Card/CardIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // nodejs library that concatenates classes 3 | import classNames from 'classnames'; 4 | // nodejs library to set properties for components 5 | import PropTypes from 'prop-types'; 6 | // @material-ui/core components 7 | import withStyles from '@material-ui/core/styles/withStyles'; 8 | // @material-ui/icons 9 | 10 | // core components 11 | import cardIconStyle from './cardIconStyle.jsx'; 12 | 13 | function CardIcon({ ...props }) { 14 | const { classes, className, children, color, ...rest } = props; 15 | const cardIconClasses = classNames({ 16 | [classes.cardIcon]: true, 17 | [classes[color + 'CardHeader']]: color, 18 | [className]: className !== undefined 19 | }); 20 | return ( 21 |
22 | {children} 23 |
24 | ); 25 | } 26 | 27 | CardIcon.propTypes = { 28 | classes: PropTypes.object.isRequired, 29 | className: PropTypes.string, 30 | color: PropTypes.oneOf([ 31 | 'warning', 32 | 'success', 33 | 'danger', 34 | 'info', 35 | 'primary', 36 | 'rose' 37 | ]) 38 | }; 39 | 40 | export default withStyles(cardIconStyle)(CardIcon); 41 | -------------------------------------------------------------------------------- /src/components/Card/cardAvatarStyle.jsx: -------------------------------------------------------------------------------- 1 | const cardAvatarStyle = { 2 | cardAvatar: { 3 | '&$cardAvatarProfile img': { 4 | width: '100%', 5 | height: 'auto' 6 | } 7 | }, 8 | cardAvatarProfile: { 9 | maxWidth: '130px', 10 | maxHeight: '130px', 11 | margin: '-50px auto 0', 12 | borderRadius: '50%', 13 | overflow: 'hidden', 14 | padding: '0', 15 | boxShadow: 16 | '0 16px 38px -12px rgba(0, 0, 0, 0.56), 0 4px 25px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2)', 17 | '&$cardAvatarPlain': { 18 | marginTop: '0' 19 | } 20 | }, 21 | cardAvatarPlain: {} 22 | }; 23 | 24 | export default cardAvatarStyle; 25 | -------------------------------------------------------------------------------- /src/components/Card/cardBodyStyle.jsx: -------------------------------------------------------------------------------- 1 | const cardBodyStyle = { 2 | cardBody: { 3 | padding: "0.9375rem 20px", 4 | flex: "1 1 auto", 5 | WebkitBoxFlex: "1", 6 | position: "relative" 7 | }, 8 | cardBodyPlain: { 9 | paddingLeft: "5px", 10 | paddingRight: "5px" 11 | }, 12 | cardBodyProfile: { 13 | marginTop: "15px" 14 | } 15 | }; 16 | 17 | export default cardBodyStyle; 18 | -------------------------------------------------------------------------------- /src/components/Card/cardFooterStyle.jsx: -------------------------------------------------------------------------------- 1 | const cardFooterStyle = { 2 | cardFooter: { 3 | padding: "0", 4 | paddingTop: "10px", 5 | margin: "0 15px 10px", 6 | borderRadius: "0", 7 | justifyContent: "space-between", 8 | alignItems: "center", 9 | display: "flex", 10 | backgroundColor: "transparent", 11 | border: "0" 12 | }, 13 | cardFooterProfile: { 14 | marginTop: "-15px" 15 | }, 16 | cardFooterPlain: { 17 | paddingLeft: "5px", 18 | paddingRight: "5px", 19 | backgroundColor: "transparent" 20 | }, 21 | cardFooterStats: { 22 | borderTop: "1px solid #eee", 23 | marginTop: "20px", 24 | "& svg": { 25 | position: "relative", 26 | top: "4px", 27 | marginRight: "3px", 28 | marginLeft: "3px", 29 | width: "16px", 30 | height: "16px" 31 | } 32 | }, 33 | cardFooterChart: { 34 | borderTop: "1px solid #eee" 35 | } 36 | }; 37 | 38 | export default cardFooterStyle; 39 | -------------------------------------------------------------------------------- /src/components/Card/cardHeaderStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | warningCardHeader, 3 | successCardHeader, 4 | dangerCardHeader, 5 | infoCardHeader, 6 | primaryCardHeader, 7 | roseCardHeader 8 | } from "../../assets/jss/material-dashboard-react.jsx"; 9 | const cardHeaderStyle = { 10 | cardHeader: { 11 | padding: "0.75rem 1.25rem", 12 | marginBottom: "0", 13 | borderBottom: "none", 14 | background: "transparent", 15 | zIndex: "3 !important", 16 | "&$cardHeaderPlain,&$cardHeaderIcon,&$cardHeaderStats,&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 17 | margin: "0 15px", 18 | padding: "0", 19 | position: "relative", 20 | color: "#FFFFFF" 21 | }, 22 | "&:first-child": { 23 | borderRadius: "calc(.25rem - 1px) calc(.25rem - 1px) 0 0" 24 | }, 25 | "&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 26 | "&:not($cardHeaderIcon)": { 27 | borderRadius: "3px", 28 | marginTop: "-20px", 29 | padding: "15px" 30 | } 31 | }, 32 | "&$cardHeaderStats svg": { 33 | fontSize: "36px", 34 | lineHeight: "56px", 35 | textAlign: "center", 36 | width: "36px", 37 | height: "36px", 38 | margin: "10px 10px 4px" 39 | }, 40 | "&$cardHeaderStats i": { 41 | fontSize: "36px", 42 | lineHeight: "56px", 43 | width: "56px", 44 | height: "56px", 45 | textAlign: "center" 46 | }, 47 | "&$cardHeaderStats$cardHeaderIcon": { 48 | textAlign: "right" 49 | } 50 | }, 51 | cardHeaderPlain: { 52 | marginLeft: "0px !important", 53 | marginRight: "0px !important" 54 | }, 55 | cardHeaderStats: { 56 | "& $cardHeaderIcon": { 57 | textAlign: "right" 58 | }, 59 | "& h1,& h2,& h3,& h4,& h5,& h6": { 60 | margin: "0 !important" 61 | } 62 | }, 63 | cardHeaderIcon: { 64 | "&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 65 | background: "transparent", 66 | boxShadow: "none" 67 | }, 68 | "& i": { 69 | width: "33px", 70 | height: "33px", 71 | textAlign: "center", 72 | lineHeight: "33px" 73 | }, 74 | "& svg": { 75 | width: "24px", 76 | height: "24px", 77 | textAlign: "center", 78 | lineHeight: "33px", 79 | margin: "5px 4px 0px" 80 | } 81 | }, 82 | warningCardHeader: { 83 | color: "#FFFFFF", 84 | "&:not($cardHeaderIcon)": { 85 | ...warningCardHeader 86 | } 87 | }, 88 | successCardHeader: { 89 | color: "#FFFFFF", 90 | "&:not($cardHeaderIcon)": { 91 | ...successCardHeader 92 | } 93 | }, 94 | dangerCardHeader: { 95 | color: "#FFFFFF", 96 | "&:not($cardHeaderIcon)": { 97 | ...dangerCardHeader 98 | } 99 | }, 100 | infoCardHeader: { 101 | color: "#FFFFFF", 102 | "&:not($cardHeaderIcon)": { 103 | ...infoCardHeader 104 | } 105 | }, 106 | primaryCardHeader: { 107 | color: "#FFFFFF", 108 | "&:not($cardHeaderIcon)": { 109 | ...primaryCardHeader 110 | } 111 | }, 112 | roseCardHeader: { 113 | color: "#FFFFFF", 114 | "&:not($cardHeaderIcon)": { 115 | ...roseCardHeader 116 | } 117 | } 118 | }; 119 | 120 | export default cardHeaderStyle; 121 | -------------------------------------------------------------------------------- /src/components/Card/cardIconStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | warningCardHeader, 3 | successCardHeader, 4 | dangerCardHeader, 5 | infoCardHeader, 6 | primaryCardHeader, 7 | roseCardHeader 8 | } from "../../assets/jss/material-dashboard-react.jsx"; 9 | const cardIconStyle = { 10 | cardIcon: { 11 | "&$warningCardHeader,&$successCardHeader,&$dangerCardHeader,&$infoCardHeader,&$primaryCardHeader,&$roseCardHeader": { 12 | borderRadius: "3px", 13 | backgroundColor: "#999", 14 | padding: "15px", 15 | marginTop: "-20px", 16 | marginRight: "15px", 17 | float: "left" 18 | } 19 | }, 20 | warningCardHeader, 21 | successCardHeader, 22 | dangerCardHeader, 23 | infoCardHeader, 24 | primaryCardHeader, 25 | roseCardHeader 26 | }; 27 | 28 | export default cardIconStyle; 29 | -------------------------------------------------------------------------------- /src/components/Card/cardStyle.jsx: -------------------------------------------------------------------------------- 1 | const cardStyle = { 2 | card: { 3 | border: "0", 4 | marginBottom: "30px", 5 | marginTop: "30px", 6 | borderRadius: "6px", 7 | color: "rgba(0, 0, 0, 0.87)", 8 | background: "#fff", 9 | width: "100%", 10 | boxShadow: "0 1px 4px 0 rgba(0, 0, 0, 0.14)", 11 | position: "relative", 12 | display: "flex", 13 | flexDirection: "column", 14 | minWidth: "0", 15 | wordWrap: "break-word", 16 | fontSize: ".875rem" 17 | }, 18 | cardPlain: { 19 | background: "transparent", 20 | boxShadow: "none" 21 | }, 22 | cardProfile: { 23 | marginTop: "30px", 24 | textAlign: "center" 25 | }, 26 | cardChart: { 27 | "& p": { 28 | marginTop: "0px", 29 | paddingTop: "0px" 30 | } 31 | } 32 | }; 33 | 34 | export default cardStyle; 35 | -------------------------------------------------------------------------------- /src/components/CustomButtons/Button.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // nodejs library that concatenates classes 3 | import classNames from "classnames"; 4 | // nodejs library to set properties for components 5 | import PropTypes from "prop-types"; 6 | 7 | // material-ui components 8 | import withStyles from "@material-ui/core/styles/withStyles"; 9 | import Button from "@material-ui/core/Button"; 10 | 11 | import buttonStyle from "./buttonStyle.jsx"; 12 | function RegularButton({ ...props }) { 13 | const { 14 | classes, 15 | color, 16 | round, 17 | children, 18 | disabled, 19 | simple, 20 | size, 21 | block, 22 | link, 23 | justIcon, 24 | className, 25 | muiClasses, 26 | ...rest 27 | } = props; 28 | const btnClasses = classNames({ 29 | [classes.button]: true, 30 | [classes[size]]: size, 31 | [classes[color]]: color, 32 | [classes.round]: round, 33 | [classes.disabled]: disabled, 34 | [classes.simple]: simple, 35 | [classes.block]: block, 36 | [classes.link]: link, 37 | [classes.justIcon]: justIcon, 38 | [className]: className 39 | }); 40 | return ( 41 | 44 | ); 45 | } 46 | 47 | RegularButton.propTypes = { 48 | classes: PropTypes.object.isRequired, 49 | color: PropTypes.oneOf([ 50 | "primary", 51 | "info", 52 | "success", 53 | "warning", 54 | "danger", 55 | "rose", 56 | "white", 57 | "transparent" 58 | ]), 59 | size: PropTypes.oneOf(["sm", "lg"]), 60 | simple: PropTypes.bool, 61 | round: PropTypes.bool, 62 | disabled: PropTypes.bool, 63 | block: PropTypes.bool, 64 | link: PropTypes.bool, 65 | justIcon: PropTypes.bool, 66 | className: PropTypes.string, 67 | // use this to pass the classes props from Material-UI 68 | muiClasses: PropTypes.object 69 | }; 70 | 71 | export default withStyles(buttonStyle)(RegularButton); 72 | -------------------------------------------------------------------------------- /src/components/CustomInput/CustomInput.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from "classnames"; 3 | import PropTypes from "prop-types"; 4 | // @material-ui/core components 5 | import withStyles from "@material-ui/core/styles/withStyles"; 6 | import FormControl from "@material-ui/core/FormControl"; 7 | import InputLabel from "@material-ui/core/InputLabel"; 8 | import Input from "@material-ui/core/Input"; 9 | // @material-ui/icons 10 | import Clear from "@material-ui/icons/Clear"; 11 | import Check from "@material-ui/icons/Check"; 12 | // core components 13 | import customInputStyle from "./customInputStyle"; 14 | import FormHelperText from "@material-ui/core/FormHelperText"; 15 | 16 | function CustomInput({ ...props }) { 17 | const { 18 | classes, 19 | formControlProps, 20 | labelText, 21 | id, 22 | labelProps, 23 | inputProps, 24 | error, 25 | errorText, 26 | success 27 | } = props; 28 | 29 | const labelClasses = classNames({ 30 | [" " + classes.labelRootError]: error, 31 | [" " + classes.labelRootSuccess]: success && !error 32 | }); 33 | const underlineClasses = classNames({ 34 | [classes.underlineError]: error, 35 | [classes.underlineSuccess]: success && !error, 36 | [classes.underline]: true 37 | }); 38 | const marginTop = classNames({ 39 | [classes.marginTop]: labelText === undefined 40 | }); 41 | return ( 42 | 46 | {labelText !== undefined ? ( 47 | 52 | {labelText} 53 | 54 | ) : null} 55 | 64 | {error ? ( 65 |
66 | 67 | {errorText} 68 |
69 | ) : success ? ( 70 | 71 | ) : null} 72 |
73 | ); 74 | } 75 | 76 | CustomInput.propTypes = { 77 | classes: PropTypes.object.isRequired, 78 | labelText: PropTypes.node, 79 | labelProps: PropTypes.object, 80 | id: PropTypes.string, 81 | inputProps: PropTypes.object, 82 | formControlProps: PropTypes.object, 83 | error: PropTypes.bool, 84 | success: PropTypes.bool 85 | }; 86 | 87 | export default withStyles(customInputStyle)(CustomInput); 88 | -------------------------------------------------------------------------------- /src/components/CustomInput/customInputStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | dangerColor, 4 | successColor, 5 | defaultFont 6 | } from "../../assets/jss/material-dashboard-react.jsx"; 7 | 8 | const customInputStyle = { 9 | disabled: { 10 | "&:before": { 11 | backgroundColor: "transparent !important" 12 | } 13 | }, 14 | underline: { 15 | "&:hover:not($disabled):before,&:before": { 16 | borderColor: "#D2D2D2 !important", 17 | borderWidth: "1px !important" 18 | }, 19 | "&:after": { 20 | borderColor: primaryColor 21 | } 22 | }, 23 | underlineError: { 24 | "&:after": { 25 | borderColor: dangerColor 26 | } 27 | }, 28 | underlineSuccess: { 29 | "&:after": { 30 | borderColor: successColor 31 | } 32 | }, 33 | labelRoot: { 34 | ...defaultFont, 35 | color: "#AAAAAA !important", 36 | fontWeight: "400", 37 | fontSize: "14px", 38 | lineHeight: "1.42857" 39 | }, 40 | labelRootError: { 41 | color: dangerColor 42 | }, 43 | labelRootSuccess: { 44 | color: successColor 45 | }, 46 | feedback: { 47 | position: "absolute", 48 | top: "18px", 49 | right: "0", 50 | zIndex: "2", 51 | display: "block", 52 | width: "24px", 53 | height: "24px", 54 | textAlign: "center", 55 | pointerEvents: "none" 56 | }, 57 | marginTop: { 58 | marginTop: "16px" 59 | }, 60 | formControl: { 61 | paddingBottom: "10px", 62 | margin: "27px 0 0 0", 63 | position: "relative" 64 | } 65 | }; 66 | 67 | export default customInputStyle; 68 | -------------------------------------------------------------------------------- /src/components/CustomTabs/CustomTabs.jsx: -------------------------------------------------------------------------------- 1 | // core components 2 | import Card from '../Card/Card.jsx'; 3 | import CardBody from '../Card/CardBody.jsx'; 4 | import CardHeader from '../Card/CardHeader.jsx'; 5 | import LinearProgress from '@material-ui/core/LinearProgress'; 6 | // nodejs library to set properties for components 7 | import PropTypes from 'prop-types'; 8 | import React from 'react'; 9 | import Tab from '@material-ui/core/Tab'; 10 | import Tabs from '@material-ui/core/Tabs'; 11 | // nodejs library that concatenates classes 12 | import classNames from 'classnames'; 13 | import customTabsStyle from './customTabsStyle.jsx'; 14 | // material-ui components 15 | import withStyles from '@material-ui/core/styles/withStyles'; 16 | 17 | class CustomTabs extends React.Component { 18 | state = { 19 | value: 0 20 | }; 21 | 22 | handleChange = (event, value) => { 23 | this.setState({ value }); 24 | }; 25 | 26 | render() { 27 | const { 28 | classes, 29 | headerColor, 30 | plainTabs, 31 | tabs, 32 | title, 33 | rtlActive, 34 | loading 35 | } = this.props; 36 | const cardTitle = classNames({ 37 | [classes.cardTitle]: true, 38 | [classes.cardTitleRTL]: rtlActive 39 | }); 40 | return ( 41 | 42 | 43 | {title !== undefined ? ( 44 |
{title}
45 | ) : null} 46 | 56 | {tabs.map((prop, key) => { 57 | let icon = {}; 58 | if (prop.tabIcon) { 59 | icon = { 60 | icon: 61 | }; 62 | } 63 | return ( 64 | 76 | ); 77 | })} 78 | 79 | {loading && } 80 |
81 | 82 | {tabs.map((prop, key) => { 83 | if (key === this.state.value) { 84 | return
{prop.tabContent}
; 85 | } 86 | return null; 87 | })} 88 |
89 |
90 | ); 91 | } 92 | } 93 | 94 | CustomTabs.propTypes = { 95 | classes: PropTypes.object.isRequired, 96 | headerColor: PropTypes.oneOf([ 97 | 'warning', 98 | 'success', 99 | 'danger', 100 | 'info', 101 | 'primary' 102 | ]), 103 | title: PropTypes.string, 104 | tabs: PropTypes.arrayOf( 105 | PropTypes.shape({ 106 | tabName: PropTypes.string.isRequired, 107 | tabIcon: PropTypes.func, 108 | tabContent: PropTypes.node.isRequired 109 | }) 110 | ), 111 | rtlActive: PropTypes.bool, 112 | plainTabs: PropTypes.bool 113 | }; 114 | 115 | export default withStyles(customTabsStyle)(CustomTabs); 116 | -------------------------------------------------------------------------------- /src/components/CustomTabs/customTabsStyle.jsx: -------------------------------------------------------------------------------- 1 | const customTabsStyle = { 2 | cardTitle: { 3 | float: "left", 4 | padding: "10px 10px 10px 0px", 5 | lineHeight: "24px" 6 | }, 7 | cardTitleRTL: { 8 | float: "right", 9 | padding: "10px 0px 10px 10px !important" 10 | }, 11 | displayNone: { 12 | display: "none !important" 13 | }, 14 | tabsRoot: { 15 | minHeight: "unset !important" 16 | }, 17 | tabRootButton: { 18 | minHeight: "unset !important", 19 | minWidth: "unset !important", 20 | width: "unset !important", 21 | height: "unset !important", 22 | maxWidth: "unset !important", 23 | maxHeight: "unset !important", 24 | padding: "10px 15px", 25 | borderRadius: "3px", 26 | lineHeight: "24px", 27 | border: "0 !important", 28 | color: "#fff !important", 29 | marginLeft: "4px", 30 | "&:last-child": { 31 | marginLeft: "0px" 32 | } 33 | }, 34 | tabLabelContainer: { 35 | padding: "0px" 36 | }, 37 | tabLabel: { 38 | fontWeight: "500", 39 | fontSize: "12px" 40 | }, 41 | tabSelected: { 42 | backgroundColor: "rgba(255, 255, 255, 0.2)", 43 | transition: "0.2s background-color 0.1s" 44 | }, 45 | tabWrapper: { 46 | display: "inline-block", 47 | minHeight: "unset !important", 48 | minWidth: "unset !important", 49 | width: "unset !important", 50 | height: "unset !important", 51 | maxWidth: "unset !important", 52 | maxHeight: "unset !important", 53 | "& > svg": { 54 | verticalAlign: "middle", 55 | margin: "-1px 5px 0 0" 56 | } 57 | } 58 | }; 59 | 60 | export default customTabsStyle; 61 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | import ListItem from "@material-ui/core/ListItem"; 6 | import List from "@material-ui/core/List"; 7 | // core components 8 | import footerStyle from "./footerStyle"; 9 | 10 | function Footer({ ...props }) { 11 | const { classes } = props; 12 | return ( 13 | 49 | ); 50 | } 51 | 52 | Footer.propTypes = { 53 | classes: PropTypes.object.isRequired 54 | }; 55 | 56 | export default withStyles(footerStyle)(Footer); 57 | -------------------------------------------------------------------------------- /src/components/Footer/footerStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | container, 4 | primaryColor 5 | } from "../../assets/jss/material-dashboard-react.jsx"; 6 | 7 | const footerStyle = { 8 | block: { 9 | color: "inherit", 10 | padding: "15px", 11 | textTransform: "uppercase", 12 | borderRadius: "3px", 13 | textDecoration: "none", 14 | position: "relative", 15 | display: "block", 16 | ...defaultFont, 17 | fontWeight: "500", 18 | fontSize: "12px" 19 | }, 20 | left: { 21 | float: "left!important", 22 | display: "block" 23 | }, 24 | right: { 25 | padding: "15px 0", 26 | margin: "0", 27 | fontSize: "14px", 28 | float: "right!important" 29 | }, 30 | footer: { 31 | bottom: "0", 32 | borderTop: "1px solid #e7e7e7", 33 | padding: "15px 0", 34 | ...defaultFont 35 | }, 36 | container, 37 | a: { 38 | color: primaryColor, 39 | textDecoration: "none", 40 | backgroundColor: "transparent" 41 | }, 42 | list: { 43 | marginBottom: "0", 44 | padding: "0", 45 | marginTop: "0" 46 | }, 47 | inlineBlock: { 48 | display: "inline-block", 49 | paddingTop: "0px", 50 | width: "auto" 51 | } 52 | }; 53 | export default footerStyle; 54 | -------------------------------------------------------------------------------- /src/components/Grid/GridItem.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // @material-ui/core components 3 | import withStyles from "@material-ui/core/styles/withStyles"; 4 | import Grid from "@material-ui/core/Grid"; 5 | 6 | const style = { 7 | grid: { 8 | padding: "0 15px !important" 9 | } 10 | }; 11 | 12 | function GridItem({ ...props }) { 13 | const { classes, children, ...rest } = props; 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | } 20 | 21 | export default withStyles(style)(GridItem); 22 | -------------------------------------------------------------------------------- /src/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | // @material-ui/core components 5 | import withStyles from '@material-ui/core/styles/withStyles'; 6 | import AppBar from '@material-ui/core/AppBar'; 7 | import Toolbar from '@material-ui/core/Toolbar'; 8 | import IconButton from '@material-ui/core/IconButton'; 9 | import Hidden from '@material-ui/core/Hidden'; 10 | // @material-ui/icons 11 | import Menu from '@material-ui/icons/Menu'; 12 | // core components 13 | import HeaderLinks from './HeaderLinks'; 14 | 15 | import headerStyle from './headerStyle.jsx'; 16 | 17 | function Header({ 18 | classes, 19 | color, 20 | routes, 21 | location, 22 | path, 23 | handleDrawerToggle, 24 | ...others 25 | }) { 26 | function makeBrand() { 27 | let name; 28 | routes.map(prop => { 29 | if (path === location.pathname) { 30 | name = prop.navbarName; 31 | } 32 | return null; 33 | }); 34 | return name; 35 | } 36 | 37 | const appBarClasses = classNames({ 38 | [' ' + classes[color]]: color 39 | }); 40 | return ( 41 | 42 | 43 |
44 | {/* Here we create navbar brand, based on route name */} 45 | {makeBrand()} 46 |
47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | ); 63 | } 64 | 65 | Header.propTypes = { 66 | classes: PropTypes.object.isRequired, 67 | color: PropTypes.oneOf(['primary', 'info', 'success', 'warning', 'danger']), 68 | notificationBackgroundImage: PropTypes.string, 69 | notifications: PropTypes.array, 70 | onNotificationChange: PropTypes.func.isRequired, 71 | onNotificationDelete: PropTypes.func.isRequired 72 | }; 73 | 74 | export default withStyles(headerStyle)(Header); 75 | -------------------------------------------------------------------------------- /src/components/Header/HeaderLinks.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | //import { Manager, Target, Popper } from "react-popper"; 3 | // @material-ui/core components 4 | import withStyles from '@material-ui/core/styles/withStyles'; 5 | import Hidden from '@material-ui/core/Hidden'; 6 | // @material-ui/icons 7 | import Person from '@material-ui/icons/Person'; 8 | import Dashboard from '@material-ui/icons/Dashboard'; 9 | import Search from '@material-ui/icons/Search'; 10 | // core components 11 | import CustomInput from '../CustomInput/CustomInput.jsx'; 12 | import Button from '../CustomButtons/Button.jsx'; 13 | 14 | import NotificationCenter from '../../components/Notification'; 15 | import headerLinksStyle from './headerLinksStyle'; 16 | 17 | function HeaderLinks({ 18 | classes, 19 | notifications, 20 | onNotificationChange, 21 | onNotificationDelete, 22 | notificationBackgroundImage, 23 | ...others 24 | }) { 25 | return ( 26 |
27 |
28 | 39 | 42 |
43 | 55 | 959 ? 'transparent' : 'white', 59 | justIcon: window.innerWidth > 959, 60 | simple: !(window.innerWidth > 959), 61 | classes: { 62 | button: classes.buttonLink, 63 | icon: classes.icons 64 | } 65 | }} 66 | items={notifications} 67 | onChange={onNotificationChange} 68 | onDelete={onNotificationDelete} 69 | image={notificationBackgroundImage} 70 | /> 71 | 83 |
84 | ); 85 | } 86 | 87 | export default withStyles(headerLinksStyle)(HeaderLinks); 88 | -------------------------------------------------------------------------------- /src/components/Header/headerLinksStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | dangerColor 4 | } from "../../assets/jss/material-dashboard-react"; 5 | 6 | import dropdownStyle from "../../assets/jss/material-dashboard-react/dropdownStyle"; 7 | 8 | const headerLinksStyle = theme => ({ 9 | ...dropdownStyle(theme), 10 | search: { 11 | "& > div": { 12 | marginTop: "0" 13 | }, 14 | [theme.breakpoints.down("sm")]: { 15 | margin: "10px 15px !important", 16 | float: "none !important", 17 | paddingTop: "1px", 18 | paddingBottom: "1px", 19 | padding: "0!important", 20 | width: "60%", 21 | marginTop: "40px", 22 | "& input": { 23 | color: "#FFFFFF" 24 | } 25 | } 26 | }, 27 | linkText: { 28 | zIndex: "4", 29 | ...defaultFont, 30 | fontSize: "14px", 31 | margin: "0px" 32 | }, 33 | buttonLink: { 34 | [theme.breakpoints.down("sm")]: { 35 | display: "flex", 36 | margin: "10px 15px 0", 37 | width: "-webkit-fill-available", 38 | "& svg": { 39 | width: "24px", 40 | height: "30px", 41 | marginRight: "15px", 42 | marginLeft: "-15px" 43 | }, 44 | "& > span": { 45 | justifyContent: "flex-start" 46 | } 47 | } 48 | }, 49 | searchButton: { 50 | [theme.breakpoints.down("sm")]: { 51 | top: "-50px !important", 52 | marginRight: "22px", 53 | float: "right" 54 | } 55 | }, 56 | margin: { 57 | zIndex: "4", 58 | margin: "0" 59 | }, 60 | searchIcon: { 61 | width: "17px", 62 | zIndex: "4" 63 | }, 64 | notifications: { 65 | zIndex: "4", 66 | [theme.breakpoints.up("md")]: { 67 | position: "absolute", 68 | top: "2px", 69 | border: "1px solid #FFF", 70 | right: "4px", 71 | fontSize: "9px", 72 | background: dangerColor, 73 | color: "#FFFFFF", 74 | minWidth: "16px", 75 | height: "16px", 76 | borderRadius: "10px", 77 | textAlign: "center", 78 | lineHeight: "16px", 79 | verticalAlign: "middle", 80 | display: "block" 81 | }, 82 | [theme.breakpoints.down("sm")]: { 83 | ...defaultFont, 84 | fontSize: "14px", 85 | marginRight: "8px" 86 | } 87 | }, 88 | manager: { 89 | [theme.breakpoints.down("sm")]: { 90 | width: "100%" 91 | }, 92 | display: "inline-block" 93 | }, 94 | searchWrapper: { 95 | [theme.breakpoints.down("sm")]: { 96 | width: "-webkit-fill-available", 97 | margin: "10px 15px 0" 98 | }, 99 | display: "inline-block" 100 | } 101 | }); 102 | 103 | export default headerLinksStyle; 104 | -------------------------------------------------------------------------------- /src/components/Header/headerStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | container, 3 | defaultFont, 4 | primaryColor, 5 | defaultBoxShadow, 6 | infoColor, 7 | successColor, 8 | warningColor, 9 | dangerColor 10 | } from "../../assets/jss/material-dashboard-react.jsx"; 11 | 12 | const headerStyle = theme => ({ 13 | appBar: { 14 | backgroundColor: "transparent", 15 | boxShadow: "none", 16 | borderBottom: "0", 17 | marginBottom: "0", 18 | position: "absolute", 19 | width: "100%", 20 | paddingTop: "10px", 21 | zIndex: "1029", 22 | color: "#555555", 23 | border: "0", 24 | borderRadius: "3px", 25 | padding: "10px 0", 26 | transition: "all 150ms ease 0s", 27 | minHeight: "50px", 28 | display: "block" 29 | }, 30 | container: { 31 | ...container, 32 | minHeight: "50px" 33 | }, 34 | flex: { 35 | flex: 1 36 | }, 37 | title: { 38 | ...defaultFont, 39 | lineHeight: "30px", 40 | fontSize: "18px", 41 | borderRadius: "3px", 42 | textTransform: "none", 43 | color: "inherit", 44 | "&:hover,&:focus": { 45 | background: "transparent" 46 | } 47 | }, 48 | appResponsive: { 49 | top: "8px" 50 | }, 51 | primary: { 52 | backgroundColor: primaryColor, 53 | color: "#FFFFFF", 54 | ...defaultBoxShadow 55 | }, 56 | info: { 57 | backgroundColor: infoColor, 58 | color: "#FFFFFF", 59 | ...defaultBoxShadow 60 | }, 61 | success: { 62 | backgroundColor: successColor, 63 | color: "#FFFFFF", 64 | ...defaultBoxShadow 65 | }, 66 | warning: { 67 | backgroundColor: warningColor, 68 | color: "#FFFFFF", 69 | ...defaultBoxShadow 70 | }, 71 | danger: { 72 | backgroundColor: dangerColor, 73 | color: "#FFFFFF", 74 | ...defaultBoxShadow 75 | } 76 | }); 77 | 78 | export default headerStyle; 79 | -------------------------------------------------------------------------------- /src/components/MessageBox/MessageBoxType.jsx: -------------------------------------------------------------------------------- 1 | const MessageBoxType = { 2 | SUCCESS: 'success', 3 | DANGER: 'danger', 4 | WARNING: 'warning', 5 | INFO: 'info', 6 | CONFIRM: 'confirm' 7 | }; 8 | 9 | export default MessageBoxType; 10 | -------------------------------------------------------------------------------- /src/components/MessageBox/helper.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import MessageBoxType from "./MessageBoxType"; 4 | import SvgIcon from "@material-ui/core/SvgIcon"; 5 | 6 | export function getTitleClasses(type, classes) { 7 | switch (type) { 8 | case MessageBoxType.CONFIRM: 9 | return classes.confirm; 10 | case MessageBoxType.DANGER: 11 | return classes.danger; 12 | case MessageBoxType.WARNING: 13 | return classes.warning; 14 | case MessageBoxType.SUCCESS: 15 | return classes.success; 16 | case MessageBoxType.INFO: 17 | default: 18 | return classes.info; 19 | } 20 | } 21 | 22 | export function getTitle(type) { 23 | switch (type) { 24 | case MessageBoxType.CONFIRM: 25 | return "Confirmation"; 26 | case MessageBoxType.DANGER: 27 | return "Error"; 28 | case MessageBoxType.WARNING: 29 | return "Warning"; 30 | case MessageBoxType.SUCCESS: 31 | return "Successfully"; 32 | case MessageBoxType.INFO: 33 | default: 34 | return "Information"; 35 | } 36 | } 37 | 38 | export function getIcon(type, classes) { 39 | const style = { 40 | fontSize: 100, 41 | float: "left", 42 | paddingTop: "5px" 43 | }; 44 | switch (type) { 45 | case MessageBoxType.CONFIRM: 46 | return ( 47 | 48 | 49 | 50 | ); 51 | case MessageBoxType.DANGER: 52 | return ( 53 | 54 | 55 | 56 | ); 57 | case MessageBoxType.WARNING: 58 | return ( 59 | 60 | 61 | 62 | ); 63 | case MessageBoxType.SUCCESS: 64 | return ( 65 | 66 | 67 | 68 | ); 69 | case MessageBoxType.INFO: 70 | default: 71 | return ( 72 | 73 | 74 | 75 | ); 76 | } 77 | } 78 | 79 | export function getActions(type, handler, classes, okText, cancelText) { 80 | const okButton = ( 81 | 92 | ); 93 | 94 | const cancelButton = ( 95 | 104 | ); 105 | 106 | switch (type) { 107 | case MessageBoxType.CONFIRM: 108 | return [cancelButton, okButton]; 109 | case MessageBoxType.DANGER: 110 | return [okButton]; 111 | case MessageBoxType.WARNING: 112 | return [okButton]; 113 | case MessageBoxType.SUCCESS: 114 | return [okButton]; 115 | case MessageBoxType.INFO: 116 | default: 117 | return [okButton]; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/components/MessageBox/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Grid from '@material-ui/core/Grid'; 4 | import GridItem from '../Grid/GridItem.jsx'; 5 | import Dialog from '@material-ui/core/Dialog'; 6 | import DialogActions from '@material-ui/core/DialogActions'; 7 | import DialogContent from '@material-ui/core/DialogContent'; 8 | import Typography from '@material-ui/core/Typography'; 9 | import { withStyles } from '@material-ui/core/styles'; 10 | import MessageBoxStyle from './jss'; 11 | import MessageBoxType from './MessageBoxType'; 12 | import * as helper from './helper'; 13 | 14 | function MessageBox({ 15 | type, 16 | open, 17 | title, 18 | message, 19 | actionHandler, 20 | icon, 21 | okText, 22 | cancelText, 23 | classes, 24 | ...other 25 | }) { 26 | let finalIcon = icon; 27 | if (icon === true) finalIcon = helper.getIcon(type, classes); 28 | 29 | return ( 30 | 38 | 43 | {title || helper.getTitle(type)} 44 | 45 | 46 | 53 | {finalIcon && ( 54 | 55 | {finalIcon} 56 | 57 | )} 58 | 59 | 60 |

{message}

61 |
62 |
63 |
64 | 65 | {helper.getActions(type, actionHandler, classes, okText, cancelText)} 66 | 67 |
68 | ); 69 | } 70 | 71 | MessageBox.defaultProps = { 72 | type: MessageBoxType.INFO, 73 | okText: 'Ok', 74 | cancelText: 'Cancel', 75 | open: true, 76 | icon: true 77 | }; 78 | 79 | MessageBox.propTypes = { 80 | //The type of Message box 81 | type: PropTypes.oneOf([ 82 | MessageBoxType.CONFIRM, 83 | MessageBoxType.DANGER, 84 | MessageBoxType.INFO, 85 | MessageBoxType.SUCCESS, 86 | MessageBoxType.WARNING 87 | ]), 88 | //The title of message box 89 | title: PropTypes.string, 90 | //show/hide message box 91 | open: PropTypes.bool.isRequired, 92 | //the message 93 | message: PropTypes.string, 94 | //the text of Ok button 95 | okText: PropTypes.string, 96 | //the text of cancel button 97 | cancelText: PropTypes.string, 98 | //buttons action handler. 99 | actionHandler: PropTypes.func, 100 | //The icon of notification. set to false to hide the default icon. 101 | icon: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]) 102 | }; 103 | 104 | export default withStyles(MessageBoxStyle)(MessageBox); 105 | -------------------------------------------------------------------------------- /src/components/MessageBox/jss.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | infoColor, 4 | successColor, 5 | warningColor, 6 | dangerColor 7 | } from "../../assets/jss/material-dashboard-react.jsx"; 8 | 9 | const titleType = { 10 | paddingTop: "12px", 11 | paddingBottom: "12px", 12 | paddingLeft: "25px", 13 | paddingRight: "25px", 14 | color: "white", 15 | fontWeight: "600" 16 | }; 17 | 18 | const MessageBoxStyle = { 19 | dialog: { 20 | minWidth: "40%", 21 | maxWidth: "65%" 22 | }, 23 | dialogContent: { padding: "1px !important", margin: "1px !important" }, 24 | content: { 25 | fontSize: "20px", 26 | display: "table" 27 | }, 28 | button: { 29 | minWidth: "80px" 30 | }, 31 | confirm: { 32 | backgroundColor: primaryColor, 33 | ...titleType 34 | }, 35 | confirmIcon: { 36 | color: primaryColor 37 | }, 38 | info: { 39 | backgroundColor: infoColor, 40 | ...titleType 41 | }, 42 | infoIcon: { 43 | color: infoColor 44 | }, 45 | success: { 46 | backgroundColor: successColor, 47 | ...titleType 48 | }, 49 | successIcon: { 50 | color: successColor 51 | }, 52 | warning: { 53 | backgroundColor: warningColor, 54 | ...titleType 55 | }, 56 | warningIcon: { 57 | color: warningColor 58 | }, 59 | danger: { 60 | backgroundColor: dangerColor, 61 | ...titleType 62 | }, 63 | dangerIcon: { 64 | color: dangerColor 65 | } 66 | }; 67 | 68 | export default MessageBoxStyle; 69 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationGroup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | List, 5 | Collapse, 6 | ListItem, 7 | ListItemAvatar, 8 | Avatar, 9 | ListItemText, 10 | Divider, 11 | IconButton, 12 | Tooltip 13 | } from '@material-ui/core'; 14 | import withStyles from '@material-ui/core/styles/withStyles'; 15 | import CloseIcon from '@material-ui/icons/Close'; 16 | import classnames from 'classnames'; 17 | 18 | import NotificationGroupStyle from './NotificationGroupStyle'; 19 | import NotificationItem from './NotificationItem'; 20 | import NotificationItemPropTypes from './NotificationItemPropTypes'; 21 | //helper 22 | import { getUnreadItems } from './helper'; 23 | 24 | @withStyles(NotificationGroupStyle) 25 | export default class NotificationGroup extends React.PureComponent { 26 | constructor(props, context) { 27 | super(props, context); 28 | 29 | this.state = { open: props.open }; 30 | } 31 | 32 | onClick = () => this.setState(p => ({ open: !p.open })); 33 | 34 | onItemClose = item => { 35 | const { onClose } = this.props; 36 | if (onClose) onClose([item]); 37 | }; 38 | 39 | onClose = () => { 40 | const { onClose, items } = this.props; 41 | if (onClose) onClose(items); 42 | }; 43 | 44 | render() { 45 | const { title, items, classes } = this.props; 46 | const unReadItems = getUnreadItems(items); 47 | 48 | return ( 49 | 50 | 58 | 59 | 0 63 | ? classes.avatarNew 64 | : this.state.open 65 | ? classes.avatarHighlight 66 | : '' 67 | )} 68 | > 69 | {unReadItems.length > 0 ? unReadItems.length : items.length} 70 | 71 | 72 | 76 | {this.state.open && ( 77 | 82 | 83 | 84 | 85 | 86 | )} 87 | 88 | 89 | 90 | 91 | {items.map((n, i) => ( 92 | 93 | { 96 | this.onItemClose(n); 97 | //stop event bubbling on click 98 | e.preventDefault(); 99 | e.stopPropagation(); 100 | return false; 101 | }} 102 | /> 103 | 104 | 105 | ))} 106 | 107 | 108 | 109 | 110 | ); 111 | } 112 | } 113 | 114 | NotificationGroup.defaultProps = { 115 | badgeColor: 'primary', 116 | open: false 117 | }; 118 | 119 | NotificationGroup.propTypes = { 120 | //the title of notification 121 | title: PropTypes.string, 122 | items: PropTypes.arrayOf(PropTypes.shape(NotificationItemPropTypes)) 123 | .isRequired, 124 | //Close handler. 125 | onClose: PropTypes.func, 126 | open: PropTypes.bool, 127 | badgeColor: PropTypes.oneOf([ 128 | 'inherit', 129 | 'primary', 130 | 'secondary', 131 | 'default', 132 | 'error' 133 | ]) 134 | }; 135 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationGroupStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | infoColor, 4 | warningColor 5 | } from "../../assets/jss/material-dashboard-react"; 6 | 7 | import { closeButton, tooltip } from "./jss"; 8 | 9 | const NotificationGroupStyle = theme => ({ 10 | root: { 11 | width: "auto", 12 | transition: "all 300ms linear", 13 | margin: "5px 5px 0", 14 | borderRadius: "3px", 15 | position: "relative", 16 | padding: "10px 10px", 17 | borderBottom: "1px solid rgba(180, 180, 180, 0.3)" 18 | }, 19 | title: { 20 | fontSize: theme.typography.pxToRem(13), 21 | fontWeight: 600, 22 | color: "white", 23 | textTransform: "uppercase" 24 | }, 25 | ...closeButton, 26 | ...tooltip(theme), 27 | avatar: { 28 | width: "24px", 29 | height: "24px", 30 | fontSize: theme.typography.pxToRem(13), 31 | backgroundColor: "transparent", 32 | border: "1px solid rgba(180, 180, 180, 0.3)" 33 | }, 34 | avatarHighlight: { 35 | backgroundColor: primaryColor 36 | }, 37 | avatarNew: { 38 | backgroundColor: warningColor 39 | }, 40 | summary: { 41 | paddingRight: 0 42 | }, 43 | details: { 44 | margin: 0, 45 | padding: 0, 46 | width: "100%" 47 | }, 48 | blue: { 49 | border: 0, 50 | backgroundColor: infoColor, 51 | boxShadow: 52 | "0 12px 20px -10px rgba(0,188,212,.28), 0 4px 20px 0 rgba(0,0,0,.12), 0 7px 8px -5px rgba(0,188,212,.2)", 53 | "&:hover": { 54 | backgroundColor: infoColor, 55 | boxShadow: 56 | "0 12px 20px -10px rgba(0,188,212,.28), 0 4px 20px 0 rgba(0,0,0,.12), 0 7px 8px -5px rgba(0,188,212,.2)" 57 | } 58 | } 59 | }); 60 | 61 | export default NotificationGroupStyle; 62 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import NotificationType from './NotificationType'; 4 | import NotificationItemStyle from './NotificationItemStyle'; 5 | import dayjs from 'dayjs'; 6 | import { ListItem, IconButton, Tooltip, Grid } from '@material-ui/core'; 7 | import withStyles from '@material-ui/core/styles/withStyles'; 8 | import CloseIcon from '@material-ui/icons/Close'; 9 | import * as helper from './helper'; 10 | import NotificationItemPropTypes from './NotificationItemPropTypes'; 11 | import NotificationStatus from './NotificationStatus'; 12 | import classnames from 'classnames'; 13 | 14 | function defaultFormatDate(date) { 15 | //convert Date to dayjs 16 | if (date instanceof Date) date = dayjs(date); 17 | //Check if it is nothing then just return out 18 | if (!date || !dayjs.isDayjs(date)) return date; 19 | const now = dayjs(); 20 | 21 | //Less than 1 minutes => now 22 | if (now.diff(date, 'minutes') <= 1) return 'now'; 23 | 24 | //Less than 5hours => hours ago 25 | if (now.diff(date, 'hours') <= 5) return date.format('h') + ' ago'; 26 | 27 | //If today then => Today hh:mm 28 | if (now.diff(date, 'days') <= 1) return 'today ' + date.format('HH:mm'); 29 | 30 | //If Yesterday then => yesterday 31 | const diff = now.diff(date, 'days'); 32 | if (diff > 1 && diff <= 2) return 'yesterday'; 33 | 34 | return `${diff} days`; 35 | } 36 | 37 | function NotificationItem({ 38 | title, 39 | message, 40 | type, 41 | classes, 42 | status, 43 | onClose, 44 | onClick, 45 | formatDate, 46 | createdOn 47 | }) { 48 | const Icon = helper.getIcon(type); 49 | const isNew = 50 | status === NotificationStatus.NEW || status === NotificationStatus.NOTIFIED; 51 | 52 | return ( 53 | 58 | 59 | 60 | 61 | 62 | 63 | {title} 64 | 65 | 66 | {formatDate(createdOn)} 67 | 68 | 69 | {message} 70 | 71 | 72 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ); 85 | } 86 | 87 | NotificationItem.defaultProps = { 88 | type: NotificationType.INFO, 89 | icon: true, 90 | formatDate: defaultFormatDate 91 | }; 92 | 93 | NotificationItem.propTypes = { 94 | ...NotificationItemPropTypes, 95 | //The function to format dayjs object to string 96 | formatDate: PropTypes.func 97 | }; 98 | 99 | export default withStyles(NotificationItemStyle)(NotificationItem); 100 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationItemPropTypes.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import NotificationType from './NotificationType'; 3 | import NotificationStatus from './NotificationStatus'; 4 | 5 | const NotificationItemPropTypes = { 6 | id: PropTypes.any.isRequired, 7 | //The Tye of notification 8 | type: PropTypes.oneOf([ 9 | NotificationType.CONFIRM, 10 | NotificationType.DANGER, 11 | NotificationType.INFO, 12 | NotificationType.SUCCESS, 13 | NotificationType.WARNING 14 | ]), 15 | //The message of notification 16 | message: PropTypes.string.isRequired, 17 | //the title of notification 18 | title: PropTypes.string, 19 | //Close handler. 20 | onClose: PropTypes.func, 21 | //click event handler. 22 | onClick: PropTypes.func, 23 | //The icon of notification. set to false to hide the default icon. 24 | icon: PropTypes.oneOfType([PropTypes.element, PropTypes.bool]), 25 | //Created date of Notification 26 | createdOn: PropTypes.object, 27 | //status of notification 28 | status: PropTypes.oneOf([ 29 | NotificationStatus.NEW, 30 | NotificationStatus.DELETED, 31 | NotificationStatus.NOTIFIED, 32 | NotificationStatus.READ 33 | ]).isRequired 34 | }; 35 | 36 | export default NotificationItemPropTypes; 37 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationItemStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | infoColor, 4 | successColor, 5 | warningColor, 6 | dangerColor 7 | } from '../../assets/jss/material-dashboard-react'; 8 | import { closeButton, tooltip } from './jss'; 9 | 10 | const marginRight = 0; 11 | 12 | const NotificationItemStyle = theme => ({ 13 | root: { 14 | width: 'auto', 15 | transition: 'all 300ms linear', 16 | margin: '5px 5px 0', 17 | borderRadius: '3px', 18 | position: 'relative', 19 | padding: '10px 10px', 20 | border: '1px solid rgba(180, 180, 180, 0.3)' 21 | }, 22 | highlight: { 23 | border: `1px solid ${warningColor}` 24 | }, 25 | date: { 26 | textAlign: 'right', 27 | fontSize: theme.typography.pxToRem(10), 28 | fontWeight: 400, 29 | color: 'white' 30 | }, 31 | title: { 32 | fontSize: theme.typography.pxToRem(13), 33 | fontWeight: 550, 34 | color: 'white', 35 | paddingLeft: '10px' 36 | }, 37 | message: { 38 | fontSize: theme.typography.pxToRem(12), 39 | textAlign: 'justify', 40 | color: 'white' 41 | }, 42 | ...closeButton, 43 | ...tooltip(theme), 44 | 45 | success: { 46 | color: successColor, 47 | marginRight 48 | }, 49 | danger: { 50 | color: dangerColor, 51 | marginRight 52 | }, 53 | warning: { 54 | color: warningColor, 55 | marginRight 56 | }, 57 | info: { 58 | color: infoColor, 59 | marginRight 60 | }, 61 | confirm: { 62 | color: primaryColor, 63 | marginRight 64 | } 65 | }); 66 | 67 | export default NotificationItemStyle; 68 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationPanel.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Drawer, List } from '@material-ui/core'; 4 | import withStyles from '@material-ui/core/styles/withStyles'; 5 | import NotificationPanelStyle from './NotificationPanelStyle'; 6 | import MessageIcon from '@material-ui/icons/Message'; 7 | import NotificationGroup from './NotificationGroup'; 8 | import NotificationItemPropTypes from './NotificationItemPropTypes'; 9 | //helper 10 | import { getGroupItems } from './helper'; 11 | 12 | function defaultGroupComponent({ classes, onClose, items }) { 13 | return ( 14 | 15 | {items.map((g, i) => ( 16 | 22 | ))} 23 | 24 | ); 25 | } 26 | 27 | function defaultBackgroundComponent({ image, classes }) { 28 | if (!image) return null; 29 | return ( 30 |
34 | ); 35 | } 36 | 37 | function defaultTitleComponent({ classes, title }) { 38 | if (!title) return null; 39 | 40 | return ( 41 |
42 | 43 | {title} 44 |
45 | ); 46 | } 47 | 48 | function NotificationPanel({ 49 | GroupComponent, 50 | BackgroundComponent, 51 | TitleComponent, 52 | position, 53 | open, 54 | onPanelClose, 55 | onItemClose, 56 | classes, 57 | items, 58 | ...others 59 | }) { 60 | const groups = getGroupItems(items); 61 | 62 | return ( 63 | onPanelClose(items)} 68 | classes={{ 69 | paper: classes.drawerPaper 70 | }} 71 | ModalProps={{ 72 | //keepMounted: true // Better open performance on mobile. 73 | BackdropProps: { 74 | invisible: true 75 | } 76 | }} 77 | > 78 | 79 |
80 | 81 | 87 |
88 |
89 | ); 90 | } 91 | 92 | NotificationPanel.defaultProps = { 93 | GroupComponent: defaultGroupComponent, 94 | BackgroundComponent: defaultBackgroundComponent, 95 | TitleComponent: defaultTitleComponent, 96 | position: 'right', 97 | open: false 98 | }; 99 | 100 | NotificationPanel.propTypes = { 101 | GroupComponent: PropTypes.func, 102 | BackgroundComponent: PropTypes.func, 103 | TitleComponent: PropTypes.func, 104 | items: PropTypes.arrayOf(PropTypes.shape(NotificationItemPropTypes)) 105 | .isRequired, 106 | position: PropTypes.oneOf(['left', 'top', 'right', 'bottom']), 107 | open: PropTypes.bool, 108 | onPanelClose: PropTypes.func.isRequired, 109 | onItemClose: PropTypes.func.isRequired, 110 | //The background image of Notification Panel 111 | image: PropTypes.string, 112 | title: PropTypes.string 113 | }; 114 | 115 | export default withStyles(NotificationPanelStyle)(NotificationPanel); 116 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationPanelStyle.js: -------------------------------------------------------------------------------- 1 | import { 2 | drawerWidth, 3 | transition, 4 | boxShadow, 5 | defaultFont 6 | } from "../../assets/jss/material-dashboard-react"; 7 | 8 | const width = drawerWidth + 30; 9 | 10 | const NotificationPanelStyle = () => ({ 11 | drawerPaper: { 12 | position: "fixed", 13 | zIndex: "1", 14 | top: "0", 15 | ...boxShadow, 16 | transform: `translate3d(${width}px, 0, 0)`, 17 | ...transition, 18 | width: width 19 | }, 20 | background: { 21 | position: "absolute", 22 | zIndex: "0", 23 | height: "100%", 24 | width: "100%", 25 | display: "block", 26 | top: "0", 27 | left: "0", 28 | backgroundSize: "cover", 29 | backgroundPosition: "center center", 30 | "&:after": { 31 | position: "absolute", 32 | zIndex: "3", 33 | width: "100%", 34 | height: "100%", 35 | content: "''", 36 | display: "block", 37 | background: "#000", 38 | opacity: ".8" 39 | } 40 | }, 41 | containWrapper: { 42 | position: "relative", 43 | height: "calc(100vh - 75px)", 44 | overflow: "auto", 45 | zIndex: "10000", 46 | overflowScrolling: "touch", 47 | display: "block", 48 | top: "0", 49 | left: "0" 50 | }, 51 | logo: { 52 | position: "relative", 53 | padding: "15px 15px", 54 | zIndex: "4", 55 | "&:after": { 56 | content: "''", 57 | position: "absolute", 58 | bottom: "0", 59 | height: "1px", 60 | right: "15px", 61 | width: "calc(100% - 30px)", 62 | backgroundColor: "rgba(180, 180, 180, 0.3)" 63 | } 64 | }, 65 | logoText: { 66 | ...defaultFont, 67 | textTransform: "uppercase", 68 | padding: "5px 0 0 40px", 69 | display: "block", 70 | fontSize: "18px", 71 | textAlign: "left", 72 | fontWeight: "400", 73 | lineHeight: "30px", 74 | textDecoration: "none", 75 | backgroundColor: "transparent", 76 | "&,&:hover": { 77 | color: "#FFFFFF" 78 | } 79 | }, 80 | icon: { 81 | width: "35px", 82 | top: "22px", 83 | position: "absolute", 84 | verticalAlign: "middle", 85 | border: "0", 86 | color: "#00acc1" 87 | } 88 | }); 89 | 90 | export default NotificationPanelStyle; 91 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationPopup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import NotificationPopupItem from './NotificationPopupItem'; 4 | 5 | function defaultPopupItemComponent(props) { 6 | return ; 7 | } 8 | 9 | //Render the Popup of the list NotificationPopupItem 10 | export default function NotificationPopup({ 11 | PopupItemComponent, 12 | items, 13 | displayIn, 14 | subsequentDelay, 15 | onClose, 16 | ...others 17 | }) { 18 | let count = items.length - 1; 19 | 20 | return ( 21 | 22 | {items.map((p, i) => { 23 | return ( 24 | { 29 | if (p.onClose) p.onClose(e); 30 | if (onClose) onClose(e); 31 | }} 32 | displayIn={displayIn + count-- * subsequentDelay} 33 | autoClose={i === 0} 34 | /> 35 | ); 36 | })} 37 | 38 | ); 39 | } 40 | 41 | NotificationPopup.defaultProps = { 42 | //Using default defaultPopupItemComponent render 43 | PopupItemComponent: defaultPopupItemComponent, 44 | displayIn: 3000, 45 | subsequentDelay: 1000, 46 | items: [] 47 | }; 48 | 49 | NotificationPopup.propTypes = { 50 | //This property allow to customize the NotificationPopupItem. 51 | PopupItemComponent: PropTypes.func, 52 | items: PropTypes.arrayOf(PropTypes.shape(NotificationPopupItem.propTypes)), 53 | displayIn: PropTypes.number, 54 | subsequentDelay: PropTypes.number, 55 | onClose: PropTypes.func 56 | }; 57 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationPopupItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Snackbar from '../Snackbar/Snackbar'; 4 | import NotificationType from './NotificationType'; 5 | import NotificationPopupItemStyle from './NotificationPopupItemStyle'; 6 | import withStyles from '@material-ui/core/styles/withStyles'; 7 | import * as helper from './helper'; 8 | import NotificationItemPropTypes from './NotificationItemPropTypes'; 9 | 10 | @withStyles(NotificationPopupItemStyle) 11 | export default class NotificationPopupItem extends React.PureComponent { 12 | constructor(props, context) { 13 | super(props, context); 14 | this._timeout = undefined; 15 | } 16 | 17 | setCallbackTimeout = props => { 18 | const { onClose, open, autoClose, displayIn } = props; 19 | 20 | if (!onClose || open !== true || autoClose !== true || displayIn <= 0) 21 | return; 22 | 23 | if (this._timeout) return; 24 | this._timeout = setTimeout(this.onClosing, displayIn); 25 | }; 26 | 27 | componentDidMount() { 28 | this.setCallbackTimeout(this.props); 29 | } 30 | 31 | componentWillReceiveProps(nextProps) { 32 | this.setCallbackTimeout(nextProps); 33 | } 34 | 35 | //Handling the internal closing event and call onClose with id 36 | onClosing = () => { 37 | const { onClose, id } = this.props; 38 | onClose({ id }); 39 | this._timeout = undefined; 40 | }; 41 | 42 | render() { 43 | const { id, type, icon, title, message, classes, place, open } = this.props; 44 | 45 | return ( 46 | 56 | {title &&
{title}
} 57 | {message} 58 | 59 | } 60 | /> 61 | ); 62 | } 63 | } 64 | 65 | NotificationPopupItem.defaultProps = { 66 | place: 'tr', //Top right 67 | autoClose: true, 68 | type: NotificationType.INFO, 69 | open: true, 70 | icon: true 71 | }; 72 | 73 | NotificationPopupItem.propTypes = { 74 | ...NotificationItemPropTypes, 75 | //The number of second will be displayed. 76 | displayIn: PropTypes.number, 77 | //Enable timeout to call onClose after displayIn second automatically. 78 | autoClose: PropTypes.bool 79 | }; 80 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationPopupItemStyle.js: -------------------------------------------------------------------------------- 1 | const NotificationItemStyle = theme => ({ 2 | title: { fontWeight: 'bold' } 3 | // icon: { 4 | // fontSize: 20 5 | // }, 6 | // message: { 7 | // color: "white", 8 | // display: "flex", 9 | // alignItems: "center", 10 | // whiteSpace: "pre", 11 | // textTransform: "none" 12 | // } 13 | }); 14 | 15 | export default NotificationItemStyle; 16 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationStatus.js: -------------------------------------------------------------------------------- 1 | const NotificationStatus = { 2 | NEW: "NEW", 3 | NOTIFIED: "NOTIFIED", 4 | READ: "READ" 5 | }; 6 | 7 | export default NotificationStatus; 8 | -------------------------------------------------------------------------------- /src/components/Notification/NotificationType.js: -------------------------------------------------------------------------------- 1 | const NotificationType = { 2 | SUCCESS: "success", 3 | DANGER: "danger", 4 | WARNING: "warning", 5 | INFO: "info", 6 | CONFIRM: "confirm" 7 | }; 8 | 9 | export default NotificationType; 10 | -------------------------------------------------------------------------------- /src/components/Notification/helper.jsx: -------------------------------------------------------------------------------- 1 | import NotificationType from './NotificationType'; 2 | import confirmIcon from '@material-ui/icons/Help'; 3 | import errorIcon from '@material-ui/icons/Error'; 4 | import infoIcon from '@material-ui/icons/Info'; 5 | import warningIcon from '@material-ui/icons/Warning'; 6 | import successIcon from '@material-ui/icons/CheckCircle'; 7 | import linq from 'linq'; 8 | import NotificationStatus from './NotificationStatus'; 9 | import * as guard from '../../commons/guard'; 10 | 11 | export function getColor(type) { 12 | switch (type) { 13 | case NotificationType.CONFIRM: 14 | return 'primary'; 15 | case NotificationType.DANGER: 16 | return 'danger'; 17 | case NotificationType.WARNING: 18 | return 'warning'; 19 | case NotificationType.SUCCESS: 20 | return 'success'; 21 | case NotificationType.INFO: 22 | default: 23 | return 'info'; 24 | } 25 | } 26 | 27 | export function getIcon(type) { 28 | switch (type) { 29 | case NotificationType.CONFIRM: 30 | return confirmIcon; 31 | case NotificationType.DANGER: 32 | return errorIcon; 33 | case NotificationType.WARNING: 34 | return warningIcon; 35 | case NotificationType.SUCCESS: 36 | return successIcon; 37 | case NotificationType.INFO: 38 | default: 39 | return infoIcon; 40 | } 41 | } 42 | 43 | /** 44 | * Get Unread Notification Items 45 | * 46 | * @export getUnreadItems 47 | * @param {NotificationItemPropTypes} items 48 | * @returns items which status is in [NEW,NOTIFIED] 49 | */ 50 | export function getUnreadItems(items) { 51 | if (!items) return items; 52 | guard.argumentIsArray(items, 'Notification Items'); 53 | if (items.length <= 0) return items; 54 | 55 | return linq 56 | .from(items) 57 | .where( 58 | i => 59 | i.status === NotificationStatus.NEW || 60 | i.status === NotificationStatus.NOTIFIED 61 | ) 62 | .toArray(); 63 | } 64 | /** 65 | * Get Notification Items for Popup component 66 | * 67 | * @export getItemsForPopup 68 | * @param {NotificationItemPropTypes} items 69 | * @returns items which status is in [NEW,NOTIFIED], orderByDescending by i.createdOn and mark open based on NEW status 70 | */ 71 | export function getItemsForPopup(items) { 72 | const unreadItems = getUnreadItems(items); 73 | if (unreadItems.length <= 0) return unreadItems; 74 | 75 | return ( 76 | linq 77 | .from(unreadItems) 78 | .orderByDescending(i => i.createdOn) 79 | //Tell the popup that only show the NEW status once. 80 | .select(i => ({ ...i, open: i.status === NotificationStatus.NEW })) 81 | .toArray() 82 | ); 83 | } 84 | /** 85 | * Group notification by group 86 | * 87 | * @export getGroupItems 88 | * @param {NotificationItemPropTypes} items 89 | * @returns groups orderByDescending by i.createdOn and grouped by i.group 90 | */ 91 | export function getGroupItems(items) { 92 | if (!items) return items; 93 | guard.argumentIsArray(items, 'Notification Items'); 94 | if (items.length <= 0) return items; 95 | 96 | return linq 97 | .from(items) 98 | .groupBy(i => i.group || '') 99 | .select(g => ({ 100 | title: g.key(), 101 | items: g.orderByDescending(i => i.createdOn.valueOf()).toArray() 102 | })) 103 | .orderBy(i => i.title) 104 | .toArray(); 105 | } 106 | -------------------------------------------------------------------------------- /src/components/Notification/index.js: -------------------------------------------------------------------------------- 1 | import NotificationCenter from './NotificationCenter'; 2 | import * as NotificationCenterStyle from './jss'; 3 | import NotificationStatus from './NotificationStatus'; 4 | import NotificationType from './NotificationType'; 5 | 6 | export { 7 | NotificationCenter, 8 | NotificationCenterStyle, 9 | NotificationStatus, 10 | NotificationType 11 | }; 12 | export default NotificationCenter; 13 | -------------------------------------------------------------------------------- /src/components/Notification/jss.js: -------------------------------------------------------------------------------- 1 | import { 2 | primaryColor, 3 | grayColor 4 | } from '../../assets/jss/material-dashboard-react'; 5 | 6 | const closeButton = { 7 | iconButton: { 8 | width: '24px', 9 | height: '24px' 10 | }, 11 | close: { 12 | width: '11px', 13 | height: '11px', 14 | color: 'white' 15 | } 16 | }; 17 | 18 | const tooltip = theme => ({ 19 | tooltip: { 20 | background: theme.palette.common.white, 21 | color: theme.palette.text.primary, 22 | boxShadow: theme.shadows[1], 23 | fontSize: 11 24 | }, 25 | arrowPopper: { 26 | '&[x-placement*="bottom"] $arrowArrow': { 27 | top: 0, 28 | left: 0, 29 | marginTop: '-0.9em', 30 | width: '3em', 31 | height: '1em', 32 | '&::before': { 33 | borderWidth: '0 1em 1em 1em', 34 | borderColor: `transparent transparent ${ 35 | theme.palette.common.white 36 | } transparent` 37 | } 38 | }, 39 | '&[x-placement*="top"] $arrowArrow': { 40 | bottom: 0, 41 | left: 0, 42 | marginBottom: '-0.9em', 43 | width: '3em', 44 | height: '1em', 45 | '&::before': { 46 | borderWidth: '1em 1em 0 1em', 47 | borderColor: `${ 48 | theme.palette.grey[700] 49 | } transparent transparent transparent` 50 | } 51 | }, 52 | '&[x-placement*="right"] $arrowArrow': { 53 | left: 0, 54 | marginLeft: '-0.9em', 55 | height: '3em', 56 | width: '1em', 57 | '&::before': { 58 | borderWidth: '1em 1em 1em 0', 59 | borderColor: `transparent ${ 60 | theme.palette.grey[700] 61 | } transparent transparent` 62 | } 63 | }, 64 | '&[x-placement*="left"] $arrowArrow': { 65 | right: 0, 66 | marginRight: '-0.9em', 67 | height: '3em', 68 | width: '1em', 69 | '&::before': { 70 | borderWidth: '1em 0 1em 1em', 71 | borderColor: `transparent transparent transparent ${ 72 | theme.palette.grey[700] 73 | }` 74 | } 75 | } 76 | }, 77 | arrowArrow: { 78 | position: 'absolute', 79 | fontSize: 7, 80 | width: '3em', 81 | height: '3em', 82 | '&::before': { 83 | content: '""', 84 | margin: 'auto', 85 | display: 'block', 86 | width: 0, 87 | height: 0, 88 | borderStyle: 'solid' 89 | } 90 | } 91 | }); 92 | 93 | const NotificationCenterStyle = theme => ({ 94 | iconActive: { 95 | color: primaryColor 96 | }, 97 | badge: { 98 | width: '20px', 99 | height: '20px', 100 | fontSize: theme.typography.pxToRem(10) 101 | }, 102 | colorSecondary: { 103 | backgroundColor: grayColor 104 | }, 105 | ...tooltip(theme) 106 | }); 107 | 108 | export { closeButton, tooltip, NotificationCenterStyle }; 109 | export default NotificationCenterStyle; 110 | -------------------------------------------------------------------------------- /src/components/Snackbar/Snackbar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from "classnames"; 3 | import PropTypes from "prop-types"; 4 | // @material-ui/core components 5 | import withStyles from "@material-ui/core/styles/withStyles"; 6 | import Snack from "@material-ui/core/Snackbar"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | // @material-ui/icons 9 | import Close from "@material-ui/icons/Close"; 10 | // core components 11 | import snackbarContentStyle from "./snackbarContentStyle"; 12 | 13 | function getMessage() {} 14 | function Snackbar({ 15 | classes, 16 | message, 17 | color, 18 | close, 19 | place, 20 | open, 21 | onClose, 22 | onClick, 23 | ...others 24 | }) { 25 | let action = []; 26 | const messageClasses = classNames({ 27 | [classes.iconMessage]: others.icon !== undefined 28 | }); 29 | 30 | if (close !== undefined) { 31 | action = [ 32 | 39 | 40 | 41 | ]; 42 | } 43 | 44 | return ( 45 | 58 | {others.icon !== undefined ? ( 59 | typeof others.icon === "function" ? ( 60 | 61 | ) : ( 62 |
{others.icon}
63 | ) 64 | ) : null} 65 | {message} 66 |
67 | } 68 | action={action} 69 | ContentProps={{ 70 | classes: { 71 | root: classes.root + " " + classes[color], 72 | message: classes.message 73 | } 74 | }} 75 | /> 76 | ); 77 | } 78 | 79 | Snackbar.propTypes = { 80 | classes: PropTypes.object.isRequired, 81 | message: PropTypes.node.isRequired, 82 | //The color of the Snackbar 83 | color: PropTypes.oneOf(["info", "success", "warning", "danger", "primary"]), 84 | //Show/Hide close button 85 | close: PropTypes.bool, 86 | icon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), 87 | //The position the Snackbar:top-left, top-right, top-center, buttom-right, buttom-left,buttom-center. 88 | place: PropTypes.oneOf(["tl", "tr", "tc", "br", "bl", "bc"]), 89 | open: PropTypes.bool, 90 | onClose: PropTypes.func, 91 | onClick: PropTypes.func 92 | }; 93 | 94 | export default withStyles(snackbarContentStyle)(Snackbar); 95 | -------------------------------------------------------------------------------- /src/components/Snackbar/SnackbarContent.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import classNames from "classnames"; 4 | // @material-ui/core components 5 | import withStyles from "@material-ui/core/styles/withStyles"; 6 | import Snack from "@material-ui/core/SnackbarContent"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | // @material-ui/icons 9 | import Close from "@material-ui/icons/Close"; 10 | // core components 11 | import snackbarContentStyle from "./snackbarContentStyle.jsx"; 12 | 13 | function SnackbarContent({ ...props }) { 14 | const { classes, message, color, close, icon } = props; 15 | let action = []; 16 | const messageClasses = classNames({ 17 | [classes.iconMessage]: icon !== undefined 18 | }); 19 | if (close !== undefined) { 20 | action = [ 21 | 27 | 28 | 29 | ]; 30 | } 31 | return ( 32 | 35 | {icon !== undefined ? : null} 36 | {message} 37 | 38 | } 39 | classes={{ 40 | root: classes.root + " " + classes[color], 41 | message: classes.message 42 | }} 43 | action={action} 44 | /> 45 | ); 46 | } 47 | 48 | SnackbarContent.propTypes = { 49 | classes: PropTypes.object.isRequired, 50 | message: PropTypes.node.isRequired, 51 | color: PropTypes.oneOf(["info", "success", "warning", "danger", "primary"]), 52 | close: PropTypes.bool, 53 | icon: PropTypes.func 54 | }; 55 | 56 | export default withStyles(snackbarContentStyle)(SnackbarContent); 57 | -------------------------------------------------------------------------------- /src/components/Snackbar/snackbarContentStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryBoxShadow, 4 | infoBoxShadow, 5 | successBoxShadow, 6 | warningBoxShadow, 7 | dangerBoxShadow, 8 | roseBoxShadow, 9 | pointer 10 | } from "../../assets/jss/material-dashboard-react"; 11 | 12 | const snackbarContentStyle = { 13 | pointer: { 14 | ...pointer 15 | }, 16 | root: { 17 | ...defaultFont, 18 | flexWrap: "unset", 19 | position: "relative", 20 | padding: "20px 15px", 21 | lineHeight: "20px", 22 | marginBottom: "20px", 23 | fontSize: "14px", 24 | backgroundColor: "white", 25 | color: "#555555", 26 | borderRadius: "3px", 27 | boxShadow: 28 | "0 12px 20px -10px rgba(255, 255, 255, 0.28), 0 4px 20px 0px rgba(0, 0, 0, 0.12), 0 7px 8px -5px rgba(255, 255, 255, 0.2)" 29 | }, 30 | top20: { 31 | top: "20px" 32 | }, 33 | top40: { 34 | top: "40px" 35 | }, 36 | info: { 37 | backgroundColor: "#00d3ee", 38 | color: "#ffffff", 39 | ...infoBoxShadow 40 | }, 41 | success: { 42 | backgroundColor: "#5cb860", 43 | color: "#ffffff", 44 | ...successBoxShadow 45 | }, 46 | warning: { 47 | backgroundColor: "#ffa21a", 48 | color: "#ffffff", 49 | ...warningBoxShadow 50 | }, 51 | danger: { 52 | backgroundColor: "#f55a4e", 53 | color: "#ffffff", 54 | ...dangerBoxShadow 55 | }, 56 | primary: { 57 | backgroundColor: "#af2cc5", 58 | color: "#ffffff", 59 | ...primaryBoxShadow 60 | }, 61 | rose: { 62 | backgroundColor: "#eb3573", 63 | color: "#ffffff", 64 | ...roseBoxShadow 65 | }, 66 | message: { 67 | padding: "0", 68 | display: "block", 69 | maxWidth: "89%" 70 | }, 71 | close: { 72 | width: "11px", 73 | height: "11px" 74 | }, 75 | iconButton: { 76 | width: "24px", 77 | height: "24px" 78 | }, 79 | icon: { 80 | display: "block", 81 | left: "15px", 82 | position: "absolute", 83 | top: "50%", 84 | marginTop: "-15px", 85 | width: "30px", 86 | height: "30px" 87 | }, 88 | infoIcon: { 89 | color: "#00d3ee" 90 | }, 91 | successIcon: { 92 | color: "#5cb860" 93 | }, 94 | warningIcon: { 95 | color: "#ffa21a" 96 | }, 97 | dangerIcon: { 98 | color: "#f55a4e" 99 | }, 100 | primaryIcon: { 101 | color: "#af2cc5" 102 | }, 103 | roseIcon: { 104 | color: "#eb3573" 105 | }, 106 | iconMessage: { 107 | paddingLeft: "50px", 108 | display: "block" 109 | } 110 | }; 111 | 112 | export default snackbarContentStyle; 113 | -------------------------------------------------------------------------------- /src/components/Table/ActionCell.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TableCell from '@material-ui/core/TableCell'; 3 | import Tooltip from '@material-ui/core/Tooltip'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import PropTypes from 'prop-types'; 6 | import withStyles from '@material-ui/core/styles/withStyles'; 7 | import classNames from 'classnames'; 8 | import tableStyle from './tableStyle'; 9 | 10 | const ActionCell = ({ actionColumns, classes, rowData }) => { 11 | let cell = null; 12 | if (actionColumns.length > 0) { 13 | cell = ( 14 | 15 | {actionColumns.map((obj, key) => ( 16 | 17 | obj.Callback(rowData)}> 18 | 24 | 25 | 26 | ))} 27 | 28 | ); 29 | } 30 | return cell; 31 | }; 32 | ActionCell.defaultProps = { 33 | actionColumns: [] 34 | }; 35 | ActionCell.propTypes = { 36 | actionColumns: PropTypes.arrayOf( 37 | PropTypes.shape({ 38 | Callback: PropTypes.func, 39 | Icon: PropTypes.any.isRequired, 40 | Tooltip: PropTypes.string, 41 | Color: PropTypes.oneOf([ 42 | 'warning', 43 | 'primary', 44 | 'danger', 45 | 'success', 46 | 'info', 47 | 'rose', 48 | 'gray' 49 | ]) 50 | }) 51 | ) 52 | }; 53 | export default withStyles(tableStyle)(ActionCell); 54 | -------------------------------------------------------------------------------- /src/components/Table/Table.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | // @material-ui/core components 4 | import withStyles from '@material-ui/core/styles/withStyles'; 5 | import Table from '@material-ui/core/Table'; 6 | import TableHead from '@material-ui/core/TableHead'; 7 | import TableRow from '@material-ui/core/TableRow'; 8 | import TableBody from '@material-ui/core/TableBody'; 9 | import TableCell from '@material-ui/core/TableCell'; 10 | // core components 11 | import tableStyle from './tableStyle'; 12 | import ActionCell from './ActionCell'; 13 | 14 | function CustomTable({ ...props }) { 15 | const { 16 | classes, 17 | tableHead, 18 | tableData, 19 | tableHeaderColor, 20 | actionColumns 21 | } = props; 22 | return ( 23 |
24 | 25 | {tableHead !== undefined ? ( 26 | 27 | 28 | {tableHead.map((prop, key) => { 29 | return ( 30 | 34 | {prop} 35 | 36 | ); 37 | })} 38 | {actionColumns.length > 0 && ( 39 | 48 | Action 49 | 50 | )} 51 | 52 | 53 | ) : null} 54 | 55 | {tableData.map((prop, key) => { 56 | return ( 57 | 58 | {prop.map((item, key) => { 59 | return ( 60 | 61 | {item} 62 | 63 | ); 64 | })} 65 | 66 | 67 | ); 68 | })} 69 | 70 |
71 |
72 | ); 73 | } 74 | 75 | CustomTable.defaultProps = { 76 | tableHeaderColor: 'gray', 77 | actionColumns: [] 78 | }; 79 | 80 | CustomTable.propTypes = { 81 | classes: PropTypes.object.isRequired, 82 | tableHeaderColor: PropTypes.oneOf([ 83 | 'warning', 84 | 'primary', 85 | 'danger', 86 | 'success', 87 | 'info', 88 | 'rose', 89 | 'gray' 90 | ]), 91 | tableHead: PropTypes.arrayOf(PropTypes.string), 92 | tableData: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)) 93 | }; 94 | 95 | export default withStyles(tableStyle)(CustomTable); 96 | -------------------------------------------------------------------------------- /src/components/Table/tableStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | warningColor, 3 | primaryColor, 4 | dangerColor, 5 | successColor, 6 | infoColor, 7 | roseColor, 8 | grayColor, 9 | defaultFont 10 | } from '../../assets/jss/material-dashboard-react.jsx'; 11 | 12 | const tableStyle = theme => ({ 13 | warningTableHeader: { 14 | color: warningColor 15 | }, 16 | primaryTableHeader: { 17 | color: primaryColor 18 | }, 19 | dangerTableHeader: { 20 | color: dangerColor 21 | }, 22 | successTableHeader: { 23 | color: successColor 24 | }, 25 | infoTableHeader: { 26 | color: infoColor 27 | }, 28 | roseTableHeader: { 29 | color: roseColor 30 | }, 31 | grayTableHeader: { 32 | color: grayColor 33 | }, 34 | table: { 35 | marginBottom: '0', 36 | width: '100%', 37 | maxWidth: '100%', 38 | backgroundColor: 'transparent', 39 | borderSpacing: '0', 40 | borderCollapse: 'collapse' 41 | }, 42 | tableHeadCell: { 43 | color: 'inherit', 44 | ...defaultFont, 45 | fontSize: '1em' 46 | }, 47 | tableCell: { 48 | ...defaultFont, 49 | lineHeight: '1.42857143', 50 | padding: '12px 8px', 51 | verticalAlign: 'middle' 52 | }, 53 | tableResponsive: { 54 | width: '100%', 55 | marginTop: theme.spacing.unit * 3, 56 | overflowX: 'auto' 57 | }, 58 | tableActionButtonIcon: { 59 | width: '17px', 60 | height: '17px' 61 | }, 62 | alignCenter: { 63 | textAlign: 'center' 64 | }, 65 | cellHidden: { 66 | display: 'none' 67 | } 68 | }); 69 | 70 | export default tableStyle; 71 | -------------------------------------------------------------------------------- /src/components/Tasks/Tasks.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | // @material-ui/core components 4 | import withStyles from '@material-ui/core/styles/withStyles'; 5 | import Checkbox from '@material-ui/core/Checkbox'; 6 | import Tooltip from '@material-ui/core/Tooltip'; 7 | import IconButton from '@material-ui/core/IconButton'; 8 | import Table from '@material-ui/core/Table'; 9 | import TableRow from '@material-ui/core/TableRow'; 10 | import TableBody from '@material-ui/core/TableBody'; 11 | import TableCell from '@material-ui/core/TableCell'; 12 | // @material-ui/icons 13 | import Edit from '@material-ui/icons/Edit'; 14 | import Close from '@material-ui/icons/Close'; 15 | import Check from '@material-ui/icons/Check'; 16 | // core components 17 | import tasksStyle from './tasksStyle.jsx'; 18 | 19 | class Tasks extends React.Component { 20 | state = { 21 | checked: this.props.checkedIndexes 22 | }; 23 | handleToggle = value => () => { 24 | const { checked } = this.state; 25 | const currentIndex = checked.indexOf(value); 26 | const newChecked = [...checked]; 27 | 28 | if (currentIndex === -1) { 29 | newChecked.push(value); 30 | } else { 31 | newChecked.splice(currentIndex, 1); 32 | } 33 | 34 | this.setState({ 35 | checked: newChecked 36 | }); 37 | }; 38 | render() { 39 | const { classes, tasksIndexes, tasks } = this.props; 40 | return ( 41 | 42 | 43 | {tasksIndexes.map(value => ( 44 | 45 | 46 | } 51 | icon={} 52 | classes={{ 53 | checked: classes.checked 54 | }} 55 | /> 56 | 57 | 58 | {tasks[value]} 59 | 60 | 61 | 67 | 71 | 76 | 77 | 78 | 84 | 88 | 93 | 94 | 95 | 96 | 97 | ))} 98 | 99 |
100 | ); 101 | } 102 | } 103 | 104 | Tasks.propTypes = { 105 | classes: PropTypes.object.isRequired, 106 | tasksIndexes: PropTypes.arrayOf(PropTypes.number), 107 | tasks: PropTypes.arrayOf(PropTypes.node) 108 | }; 109 | 110 | export default withStyles(tasksStyle)(Tasks); 111 | -------------------------------------------------------------------------------- /src/components/Tasks/tasksStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryColor, 4 | dangerColor 5 | } from "../../assets/jss/material-dashboard-react"; 6 | import tooltipStyle from "../../assets/jss/material-dashboard-react/tooltipStyle"; 7 | import checkboxAdnRadioStyle from "../../assets/jss/material-dashboard-react/checkboxAdnRadioStyle"; 8 | 9 | const tasksStyle = { 10 | ...tooltipStyle, 11 | ...checkboxAdnRadioStyle, 12 | table: { 13 | marginBottom: "0", 14 | overflow: "visible" 15 | }, 16 | tableRow: { 17 | position: "relative", 18 | borderBottom: "1px solid #dddddd" 19 | }, 20 | tableActions: { 21 | display: "flex", 22 | border: "none", 23 | padding: "12px 8px !important", 24 | verticalAlign: "middle" 25 | }, 26 | tableCell: { 27 | ...defaultFont, 28 | padding: "8px", 29 | verticalAlign: "middle", 30 | border: "none", 31 | lineHeight: "1.42857143", 32 | fontSize: "14px" 33 | }, 34 | tableActionButton: { 35 | width: "27px", 36 | height: "27px" 37 | }, 38 | tableActionButtonIcon: { 39 | width: "17px", 40 | height: "17px" 41 | }, 42 | edit: { 43 | backgroundColor: "transparent", 44 | color: primaryColor, 45 | boxShadow: "none" 46 | }, 47 | close: { 48 | backgroundColor: "transparent", 49 | color: dangerColor, 50 | boxShadow: "none" 51 | } 52 | }; 53 | export default tasksStyle; 54 | -------------------------------------------------------------------------------- /src/components/Typography/Danger.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | // core components 6 | import typographyStyle from "./typographyStyle.jsx"; 7 | 8 | function Danger({ ...props }) { 9 | const { classes, children } = props; 10 | return ( 11 |
12 | {children} 13 |
14 | ); 15 | } 16 | 17 | Danger.propTypes = { 18 | classes: PropTypes.object.isRequired 19 | }; 20 | 21 | export default withStyles(typographyStyle)(Danger); 22 | -------------------------------------------------------------------------------- /src/components/Typography/Info.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | // core components 6 | import typographyStyle from "./typographyStyle.jsx"; 7 | 8 | function Info({ ...props }) { 9 | const { classes, children } = props; 10 | return ( 11 |
12 | {children} 13 |
14 | ); 15 | } 16 | 17 | Info.propTypes = { 18 | classes: PropTypes.object.isRequired 19 | }; 20 | 21 | export default withStyles(typographyStyle)(Info); 22 | -------------------------------------------------------------------------------- /src/components/Typography/Muted.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | // core components 6 | import typographyStyle from "./typographyStyle.jsx"; 7 | 8 | function Muted({ ...props }) { 9 | const { classes, children } = props; 10 | return ( 11 |
12 | {children} 13 |
14 | ); 15 | } 16 | 17 | Muted.propTypes = { 18 | classes: PropTypes.object.isRequired 19 | }; 20 | 21 | export default withStyles(typographyStyle)(Muted); 22 | -------------------------------------------------------------------------------- /src/components/Typography/Primary.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | // core components 6 | import typographyStyle from "./typographyStyle.jsx"; 7 | 8 | function Primary({ ...props }) { 9 | const { classes, children } = props; 10 | return ( 11 |
12 | {children} 13 |
14 | ); 15 | } 16 | 17 | Primary.propTypes = { 18 | classes: PropTypes.object.isRequired 19 | }; 20 | 21 | export default withStyles(typographyStyle)(Primary); 22 | -------------------------------------------------------------------------------- /src/components/Typography/Quote.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | // core components 6 | import typographyStyle from "./typographyStyle.jsx"; 7 | 8 | function Quote({ ...props }) { 9 | const { classes, text, author } = props; 10 | return ( 11 |
12 |

{text}

13 | {author} 14 |
15 | ); 16 | } 17 | 18 | Quote.propTypes = { 19 | classes: PropTypes.object.isRequired, 20 | text: PropTypes.node, 21 | author: PropTypes.node 22 | }; 23 | 24 | export default withStyles(typographyStyle)(Quote); 25 | -------------------------------------------------------------------------------- /src/components/Typography/Success.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | // core components 6 | import typographyStyle from "./typographyStyle.jsx"; 7 | 8 | function Success({ ...props }) { 9 | const { classes, children } = props; 10 | return ( 11 |
12 | {children} 13 |
14 | ); 15 | } 16 | 17 | Success.propTypes = { 18 | classes: PropTypes.object.isRequired 19 | }; 20 | 21 | export default withStyles(typographyStyle)(Success); 22 | -------------------------------------------------------------------------------- /src/components/Typography/Warning.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | // core components 6 | import typographyStyle from "./typographyStyle.jsx"; 7 | 8 | function Warning({ ...props }) { 9 | const { classes, children } = props; 10 | return ( 11 |
12 | {children} 13 |
14 | ); 15 | } 16 | 17 | Warning.propTypes = { 18 | classes: PropTypes.object.isRequired 19 | }; 20 | 21 | export default withStyles(typographyStyle)(Warning); 22 | -------------------------------------------------------------------------------- /src/components/Typography/typographyStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | defaultFont, 3 | primaryColor, 4 | infoColor, 5 | successColor, 6 | warningColor, 7 | dangerColor 8 | } from "../../assets/jss/material-dashboard-react.jsx"; 9 | 10 | const typographyStyle = { 11 | defaultFontStyle: { 12 | ...defaultFont, 13 | fontSize: "14px" 14 | }, 15 | defaultHeaderMargins: { 16 | marginTop: "20px", 17 | marginBottom: "10px" 18 | }, 19 | quote: { 20 | padding: "10px 20px", 21 | margin: "0 0 20px", 22 | fontSize: "17.5px", 23 | borderLeft: "5px solid #eee" 24 | }, 25 | quoteText: { 26 | margin: "0 0 10px", 27 | fontStyle: "italic" 28 | }, 29 | quoteAuthor: { 30 | display: "block", 31 | fontSize: "80%", 32 | lineHeight: "1.42857143", 33 | color: "#777" 34 | }, 35 | mutedText: { 36 | color: "#777" 37 | }, 38 | primaryText: { 39 | color: primaryColor 40 | }, 41 | infoText: { 42 | color: infoColor 43 | }, 44 | successText: { 45 | color: successColor 46 | }, 47 | warningText: { 48 | color: warningColor 49 | }, 50 | dangerText: { 51 | color: dangerColor 52 | } 53 | }; 54 | 55 | export default typographyStyle; 56 | -------------------------------------------------------------------------------- /src/components/User/userFormStyles.js: -------------------------------------------------------------------------------- 1 | export default { 2 | cardCategoryWhite: { 3 | color: 'rgba(255,255,255,.62)', 4 | margin: '0', 5 | fontSize: '14px', 6 | marginTop: '0', 7 | marginBottom: '0' 8 | }, 9 | cardTitleWhite: { 10 | color: '#FFFFFF', 11 | marginTop: '0px', 12 | minHeight: 'auto', 13 | fontWeight: '300', 14 | marginBottom: '3px', 15 | textDecoration: 'none' 16 | }, 17 | appBar: { 18 | position: 'relative' 19 | }, 20 | flex: { 21 | flex: 1 22 | }, 23 | avatar: { 24 | width: '200px', 25 | height: '200px' 26 | }, 27 | hiddenTag: { 28 | display: 'none' 29 | }, 30 | pointer: { 31 | cursor: 'pointer' 32 | }, 33 | formControl: { 34 | paddingBottom: '10px', 35 | margin: '27px 0 0 0', 36 | position: 'relative' 37 | } 38 | }; -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baoduy/React-MaterialUI-Starter-Kit/450262c7cac7fda180c46fe06c8ca24e63407278/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | drunkcoding.net - Hello React! 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | //Sample to import LESS or SCSS 2 | //Style-sheets 3 | import './assets/scss/material-dashboard-react.scss'; 4 | 5 | import { BrowserRouter, Route, Router, Switch } from 'react-router-dom'; 6 | 7 | import ExceptionHandler from './layouts/ExceptionHandler'; 8 | import { GetBaseUrl } from './commons/commonFuncs'; 9 | import Provider from 'react-redux-thunk-store'; 10 | import React from 'react'; 11 | import ReactDOM from 'react-dom'; 12 | import { createBrowserHistory } from 'history'; 13 | import indexRoutes from 'routes/index.jsx'; 14 | import reducers from './reducers'; 15 | 16 | //import './assets/less/material-dashboard-react.less'; 17 | //Sample to import LESS or SCSS 18 | 19 | //indicate whether application is running on PRD or not 20 | const isPrd = process.env.NODE_ENV === 'production'; 21 | 22 | //Update for Reserved proxy 23 | const base = GetBaseUrl(); 24 | const hist = createBrowserHistory({ basename: base }); 25 | 26 | const createRouter = () => { 27 | return ( 28 | 29 | 30 | 31 | {indexRoutes.map((prop, key) => { 32 | return ( 33 | 34 | ); 35 | })} 36 | 37 | 38 | 39 | ); 40 | }; 41 | 42 | //The Global ExceptionHandler will be disabled when running on development mode. 43 | const renderComponent = () => { 44 | ReactDOM.render( 45 | 46 | 47 | {createRouter()} 48 | 49 | , 50 | document.getElementById('root') 51 | ); 52 | }; 53 | 54 | renderComponent(); 55 | 56 | /* devblock:start */ 57 | // Hot Module Replacement API 58 | if (module.hot) { 59 | module.hot.accept('./layouts/Dashboard', () => { 60 | renderComponent(); 61 | }); 62 | } 63 | /* devblock:end */ 64 | -------------------------------------------------------------------------------- /src/layouts/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Switch, Route, Redirect } from 'react-router-dom'; 4 | import { bindActionCreators } from 'redux'; 5 | import { connect } from 'react-redux'; 6 | import 'perfect-scrollbar/css/perfect-scrollbar.css'; 7 | // @material-ui/core components 8 | import withStyles from '@material-ui/core/styles/withStyles'; 9 | // core components 10 | import Header from '../components/Header/Header.jsx'; 11 | import Footer from '../components/Footer/Footer.jsx'; 12 | import Sidebar from '../components/Sidebar/Sidebar.jsx'; 13 | import MessageBox from '../components/MessageBox'; 14 | 15 | import dashboardRoutes from 'routes/dashboard.jsx'; 16 | import dashboardStyle from './dashboardStyle.jsx'; 17 | 18 | //Actions 19 | import NotificationActions from '../actions/Notifications'; 20 | 21 | import { getImgSrc } from '../commons/commonFuncs'; 22 | //Import may not working with Reserved proxy so using require instead. 23 | const image = getImgSrc(require('../assets/img/sidebar-2.jpg')); 24 | const logo = require('../assets/img/react_logo.svg'); 25 | 26 | const switchRoutes = ( 27 | 28 | {dashboardRoutes.map( 29 | (prop, key) => 30 | prop.redirect ? ( 31 | 32 | ) : ( 33 | 34 | ) 35 | )} 36 | 37 | ); 38 | 39 | //Connect component to Redux store. 40 | @connect( 41 | state => ({ 42 | messageBox: state.messageBox || {}, 43 | notifications: state.notifications || [] 44 | }), 45 | dispatch => ({ 46 | actions: bindActionCreators(NotificationActions, dispatch) 47 | }) 48 | ) 49 | class App extends React.Component { 50 | constructor(props, context) { 51 | super(props, context); 52 | 53 | this.state = { 54 | mobileOpen: false 55 | }; 56 | } 57 | 58 | handleDrawerToggle = () => { 59 | this.setState({ mobileOpen: !this.state.mobileOpen }); 60 | }; 61 | getRoute() { 62 | return this.props.location.pathname !== '/maps'; 63 | } 64 | componentDidMount() { 65 | if (navigator.platform.indexOf('Win') <= -1) return; 66 | } 67 | componentDidUpdate(e) { 68 | if (e.history.location.pathname === e.location.pathname) return; 69 | 70 | this.refs.mainPanel.scrollTop = 0; 71 | if (this.state.mobileOpen) this.setState({ mobileOpen: false }); 72 | } 73 | 74 | onNotificationChange = items => { 75 | this.props.actions.addOrUpdateNotifications(items); 76 | }; 77 | 78 | onNotificationDelete = items => { 79 | this.props.actions.deleteNotifications(items); 80 | }; 81 | 82 | render() { 83 | const { classes, notifications, messageBox, ...rest } = this.props; 84 | return ( 85 |
86 | 87 | 88 | 98 |
99 |
108 | {/* On the /maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */} 109 | {this.getRoute() ? ( 110 |
111 |
{switchRoutes}
112 |
113 | ) : ( 114 |
{switchRoutes}
115 | )} 116 | {this.getRoute() ?
: null} 117 |
118 |
119 | ); 120 | } 121 | } 122 | 123 | App.propTypes = { 124 | classes: PropTypes.object.isRequired 125 | }; 126 | 127 | export default withStyles(dashboardStyle)(App); 128 | -------------------------------------------------------------------------------- /src/layouts/ExceptionHandler.jsx: -------------------------------------------------------------------------------- 1 | /*eslint no-console: ["error", { allow: ["warn", "error"] }] */ 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import { bindActionCreators } from 'redux'; 5 | import { connect } from 'react-redux'; 6 | 7 | import NotificationActions from '../actions/Notifications'; 8 | import { getErrorMessage } from '../commons/exceptionService'; 9 | import NotificationType from '../components/Notification/NotificationType'; 10 | 11 | //Connect component to Redux store. 12 | //This one is a component however we are bot put it into components folder 13 | //Because it needs to connect to Redux actions to send the notification. 14 | @connect( 15 | undefined, 16 | dispatch => ({ actions: bindActionCreators(NotificationActions, dispatch) }) 17 | ) 18 | export default class ExceptionHandler extends React.Component { 19 | constructor(props, context) { 20 | super(props, context); 21 | } 22 | 23 | componentWillMount() { 24 | const { global, disabled } = this.props; 25 | if (global !== true || disabled === true) return; 26 | window.addEventListener('error', this.globalErrorHandler); 27 | } 28 | 29 | componentWillUnmount() { 30 | window.removeEventListener('error', this.globalErrorHandler); 31 | } 32 | 33 | /** 34 | * Exception handling for any error throw by any children control. 35 | * 36 | * @memberof ExceptionHandler 37 | */ 38 | globalErrorHandler = event => { 39 | event.preventDefault(); 40 | event.cancelBubble = true; //Stop the Bubble up 41 | 42 | const msg = getErrorMessage(event.error, event); 43 | this.props.actions.notify(NotificationType.DANGER, msg); 44 | }; 45 | 46 | /** 47 | * From 16 onward all exception from children components 48 | * will be catch by parent component which implemented the componentDidCatch(error, info) 49 | * More details here https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html 50 | * 51 | * @param {*} error 52 | * @param {*} info 53 | * @memberof ExceptionHandler 54 | */ 55 | componentDidCatch(error, info) { 56 | const { disabled } = this.props; 57 | if (disabled === true) { 58 | console.error(error); 59 | throw error; 60 | } else { 61 | const msg = getErrorMessage(error, info); 62 | this.props.actions.notify(NotificationType.DANGER, msg); 63 | } 64 | } 65 | 66 | render() { 67 | return this.props.children; 68 | } 69 | } 70 | 71 | ExceptionHandler.propTypes = { 72 | global: PropTypes.bool, 73 | disabled: PropTypes.bool 74 | }; 75 | -------------------------------------------------------------------------------- /src/layouts/dashboardStyle.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | drawerWidth, 3 | transition, 4 | container 5 | } from '../assets/jss/material-dashboard-react.jsx'; 6 | 7 | const appStyle = theme => ({ 8 | wrapper: { 9 | position: 'relative', 10 | top: '0', 11 | height: '100vh' 12 | }, 13 | mainPanel: { 14 | [theme.breakpoints.up('md')]: { 15 | width: `calc(100% - ${drawerWidth}px)` 16 | }, 17 | overflow: 'auto', 18 | position: 'relative', 19 | float: 'right', 20 | ...transition, 21 | maxHeight: '100%', 22 | width: '100%', 23 | overflowScrolling: 'touch' 24 | }, 25 | content: { 26 | marginTop: '70px', 27 | padding: '30px 15px', 28 | minHeight: 'calc(100vh - 123px)' 29 | }, 30 | container, 31 | map: { 32 | marginTop: '70px' 33 | } 34 | }); 35 | 36 | export default appStyle; 37 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { Reducer as dashBoardReducers } from '../actions/Dashboard'; 2 | import { Reducer as MessageBoxReducer } from '../actions/MessageBox'; 3 | import { Reducer as NotificationReducer } from '../actions/Notifications'; 4 | import { Reducer as UserReducer } from '../actions/Users'; 5 | import { reducer as formReducer } from 'redux-form'; 6 | 7 | const reducers = { 8 | charts: dashBoardReducers.chartsReducer, 9 | general: dashBoardReducers.generalReducer, 10 | messageBox: MessageBoxReducer, 11 | notifications: NotificationReducer, 12 | users: UserReducer, 13 | form: formReducer 14 | }; 15 | 16 | export default reducers; 17 | -------------------------------------------------------------------------------- /src/routes/dashboard.jsx: -------------------------------------------------------------------------------- 1 | // @material-ui/icons 2 | import Dashboard from '@material-ui/icons/Dashboard'; 3 | import Person from '@material-ui/icons/Person'; 4 | import ContentPaste from '@material-ui/icons/Assessment'; 5 | import LibraryBooks from '@material-ui/icons/LibraryBooks'; 6 | import BubbleChart from '@material-ui/icons/BubbleChart'; 7 | import LocationOn from '@material-ui/icons/LocationOn'; 8 | import Notifications from '@material-ui/icons/Notifications'; 9 | import MessageBox from '@material-ui/icons/Message'; 10 | import Unarchive from '@material-ui/icons/Unarchive'; 11 | // core components/views 12 | //import DashboardPage from '../views/Dashboard/Dashboard.jsx'; 13 | //import UserProfile from '../views/UserProfile/UserProfile.jsx'; 14 | //import UserListing from '../views/UserProfile/UserListing.jsx'; 15 | //import TableList from '../views/TableList/TableList.jsx'; 16 | //import Typography from '../views/Typography/Typography.jsx'; 17 | //import Icons from '../views/Icons/Icons.jsx'; 18 | //import Maps from '../views/Maps/Maps.jsx'; 19 | //import NotificationsPage from '../views/Notifications/Notifications.jsx'; 20 | //import MessageBoxPage from '../views/MessageBox'; 21 | //import UpgradeToPro from '../views/UpgradeToPro/UpgradeToPro.jsx'; 22 | 23 | import Loader from '../views/loaders'; 24 | 25 | const DashboardPage = Loader(() => 26 | import(/* webpackChunkName: "DashboardView" */ '../views/Dashboard/Dashboard.jsx') 27 | ); 28 | 29 | const UserListing = Loader(() => 30 | import(/* webpackChunkName: "UserListingView" */ '../views/UserProfile/UserListing.jsx') 31 | ); 32 | 33 | const TableList = Loader(() => 34 | import(/* webpackChunkName: "TableListView" */ '../views/TableList/TableList.jsx') 35 | ); 36 | 37 | const Typography = Loader(() => 38 | import(/* webpackChunkName: "TypographyView" */ '../views/Typography/Typography.jsx') 39 | ); 40 | 41 | const Icons = Loader(() => 42 | import(/* webpackChunkName: "IconsView" */ '../views/Icons/Icons.jsx') 43 | ); 44 | 45 | const Maps = Loader(() => 46 | import(/* webpackChunkName: "MapsView" */ '../views/Maps/Maps.jsx') 47 | ); 48 | 49 | const NotificationsPage = Loader(() => 50 | import(/* webpackChunkName: "NotificationsView" */ '../views/Notifications/Notifications.jsx') 51 | ); 52 | 53 | const MessageBoxPage = Loader(() => 54 | import(/* webpackChunkName: "MessageBoxView" */ '../views/MessageBox') 55 | ); 56 | 57 | const UpgradeToPro = Loader(() => 58 | import(/* webpackChunkName: "UpgradeToProView" */ '../views/UpgradeToPro/UpgradeToPro.jsx') 59 | ); 60 | 61 | export default [ 62 | { 63 | path: '/dashboard', 64 | sidebarName: 'Dashboard', 65 | navbarName: 'Material Dashboard', 66 | icon: Dashboard, 67 | component: DashboardPage 68 | }, 69 | { 70 | path: '/users', 71 | sidebarName: 'User Listing', 72 | navbarName: 'User Listing', 73 | icon: Person, 74 | component: UserListing 75 | }, 76 | { 77 | path: '/table', 78 | sidebarName: 'Table List', 79 | navbarName: 'Table List', 80 | icon: ContentPaste, 81 | component: TableList 82 | }, 83 | { 84 | path: '/typography', 85 | sidebarName: 'Typography', 86 | navbarName: 'Typography', 87 | icon: LibraryBooks, 88 | component: Typography 89 | }, 90 | { 91 | path: '/icons', 92 | sidebarName: 'Icons', 93 | navbarName: 'Icons', 94 | icon: BubbleChart, 95 | component: Icons 96 | }, 97 | { 98 | path: '/maps', 99 | sidebarName: 'Maps', 100 | navbarName: 'Map', 101 | icon: LocationOn, 102 | component: Maps 103 | }, 104 | { 105 | path: '/notifications', 106 | sidebarName: 'Notifications', 107 | navbarName: 'Notifications', 108 | icon: Notifications, 109 | component: NotificationsPage 110 | }, 111 | { 112 | path: '/messagebox', 113 | sidebarName: 'Message Box', 114 | navbarName: 'Message Box', 115 | icon: MessageBox, 116 | component: MessageBoxPage 117 | }, 118 | { 119 | path: '/upgrade-to-pro', 120 | sidebarName: 'Upgrade To PRO', 121 | navbarName: 'Upgrade To PRO', 122 | icon: Unarchive, 123 | component: UpgradeToPro 124 | }, 125 | { redirect: true, path: '/', to: '/dashboard', navbarName: 'Redirect' } 126 | ]; 127 | -------------------------------------------------------------------------------- /src/routes/index.jsx: -------------------------------------------------------------------------------- 1 | //import Dashboard from '../layouts/Dashboard.jsx'; 2 | import Loader from '../views/loaders'; 3 | 4 | const Dashboard = Loader(() => 5 | import(/* webpackChunkName: "Dashboard" */ '../layouts/Dashboard.jsx') 6 | ); 7 | 8 | export default [{ path: '/', component: Dashboard }]; 9 | -------------------------------------------------------------------------------- /src/settings/dev.js: -------------------------------------------------------------------------------- 1 | export const webService = 'http://react-api.azurewebsites.net'; 2 | -------------------------------------------------------------------------------- /src/settings/index.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./prd'); 3 | } else { 4 | module.exports = require('./dev'); 5 | } 6 | -------------------------------------------------------------------------------- /src/settings/prd.js: -------------------------------------------------------------------------------- 1 | export const webService = 'http://react-api.azurewebsites.net'; 2 | -------------------------------------------------------------------------------- /src/views/Dashboard/dashboardStyle.jsx: -------------------------------------------------------------------------------- 1 | import { successColor } from "../../assets/jss/material-dashboard-react.jsx"; 2 | 3 | const dashboardStyle = { 4 | successText: { 5 | color: successColor 6 | }, 7 | upArrowCardCategory: { 8 | width: "16px", 9 | height: "16px" 10 | }, 11 | stats: { 12 | color: "#999999", 13 | display: "inline-flex", 14 | fontSize: "12px", 15 | lineHeight: "22px", 16 | "& svg": { 17 | top: "4px", 18 | width: "16px", 19 | height: "16px", 20 | position: "relative", 21 | marginRight: "3px" 22 | } 23 | }, 24 | cardCategory: { 25 | color: "#999999", 26 | margin: "0", 27 | fontSize: "14px", 28 | marginTop: "0", 29 | paddingTop: "10px", 30 | marginBottom: "0" 31 | }, 32 | cardCategoryWhite: { 33 | color: "rgba(255,255,255,.62)", 34 | margin: "0", 35 | fontSize: "14px", 36 | marginTop: "0", 37 | marginBottom: "0" 38 | }, 39 | cardTitle: { 40 | color: "#3C4858", 41 | marginTop: "0px", 42 | minHeight: "auto", 43 | fontWeight: "300", 44 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 45 | marginBottom: "3px", 46 | textDecoration: "none", 47 | "& small": { 48 | color: "#777", 49 | fontSize: "65%", 50 | fontWeight: "400", 51 | lineHeight: "1" 52 | } 53 | }, 54 | cardTitleWhite: { 55 | color: "#FFFFFF", 56 | marginTop: "0px", 57 | minHeight: "auto", 58 | fontWeight: "300", 59 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 60 | marginBottom: "3px", 61 | textDecoration: "none", 62 | "& small": { 63 | color: "#777", 64 | fontSize: "65%", 65 | fontWeight: "400", 66 | lineHeight: "1" 67 | } 68 | } 69 | }; 70 | 71 | export default dashboardStyle; 72 | -------------------------------------------------------------------------------- /src/views/Icons/Icons.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | // @material-ui/core components 4 | import withStyles from "@material-ui/core/styles/withStyles"; 5 | import Grid from "@material-ui/core/Grid"; 6 | import Hidden from "@material-ui/core/Hidden"; 7 | // core components 8 | import GridItem from "../../components/Grid/GridItem.jsx"; 9 | import Card from "../../components/Card/Card.jsx"; 10 | import CardHeader from "../../components/Card/CardHeader.jsx"; 11 | import CardBody from "../../components/Card/CardBody.jsx"; 12 | 13 | import iconsStyle from "./iconsStyle.jsx"; 14 | 15 | function Icons(props) { 16 | const { classes } = props; 17 | return ( 18 | 19 | 20 | 21 | 22 |

Material Design Icons

23 |

24 | Handcrafted by our friends from{" "} 25 | 30 | Google 31 | 32 |

33 |
34 | 35 | 36 | 43 | 44 | 45 | 46 |
47 | The icons are visible on Desktop mode inside an iframe. Since 48 | the iframe is not working on Mobile and Tablets please visit 49 | the icons on their original page on Google. Check the 50 | 55 | Material Icons 56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | ); 65 | } 66 | 67 | Icons.propTypes = { 68 | classes: PropTypes.object.isRequired 69 | }; 70 | 71 | export default withStyles(iconsStyle)(Icons); 72 | -------------------------------------------------------------------------------- /src/views/Icons/iconsStyle.jsx: -------------------------------------------------------------------------------- 1 | import { boxShadow } from "../../assets/jss/material-dashboard-react.jsx"; 2 | 3 | const iconsStyle = { 4 | iframe: { 5 | width: "100%", 6 | height: "500px", 7 | border: "0", 8 | ...boxShadow 9 | }, 10 | iframeContainer: { 11 | margin: "0 -20px 0" 12 | }, 13 | cardCategoryWhite: { 14 | "&,& a,& a:hover,& a:focus": { 15 | color: "rgba(255,255,255,.62)", 16 | margin: "0", 17 | fontSize: "14px", 18 | marginTop: "0", 19 | marginBottom: "0" 20 | }, 21 | "& a,& a:hover,& a:focus": { 22 | color: "#FFFFFF" 23 | } 24 | }, 25 | cardTitleWhite: { 26 | color: "#FFFFFF", 27 | marginTop: "0px", 28 | minHeight: "auto", 29 | fontWeight: "300", 30 | fontFamily: "'Roboto', 'Helvetica', 'Arial', sans-serif", 31 | marginBottom: "3px", 32 | textDecoration: "none", 33 | "& small": { 34 | color: "#777", 35 | fontSize: "65%", 36 | fontWeight: "400", 37 | lineHeight: "1" 38 | } 39 | } 40 | }; 41 | 42 | export default iconsStyle; 43 | -------------------------------------------------------------------------------- /src/views/Maps/Maps.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | withScriptjs, 4 | withGoogleMap, 5 | GoogleMap, 6 | Marker 7 | } from "react-google-maps"; 8 | 9 | const CustomSkinMap = withScriptjs( 10 | withGoogleMap(props => ( 11 | 79 | 80 | 81 | )) 82 | ); 83 | 84 | function Maps({ ...props }) { 85 | return ( 86 | } 89 | containerElement={
} 90 | mapElement={
} 91 | /> 92 | ); 93 | } 94 | 95 | export default Maps; 96 | -------------------------------------------------------------------------------- /src/views/UserProfile/UserEditTransition.jsx: -------------------------------------------------------------------------------- 1 | import Slide from '@material-ui/core/Slide'; 2 | const UserEditTransition = props => { 3 | return ; 4 | }; 5 | export default UserEditTransition; 6 | -------------------------------------------------------------------------------- /src/views/UserProfile/UserListing.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | import UserProfile from './UserProfile'; 4 | import { connect } from 'react-redux'; 5 | import { bindActionCreators } from 'redux'; 6 | import UserActions from '../../actions/Users'; 7 | import MessageBoxActions from '../../actions/MessageBox'; 8 | import MessageBoxType from '../../components/MessageBox/MessageBoxType'; 9 | 10 | import urljoin from 'url-join'; 11 | import UserTable from '../../components/User/UserTable'; 12 | 13 | @connect( 14 | state => { 15 | return { users: state.users.data, isLoading: state.users.isLoading }; 16 | }, 17 | dispatch => { 18 | return { 19 | actions: bindActionCreators(UserActions, dispatch), 20 | messageBoxActions: bindActionCreators(MessageBoxActions, dispatch) 21 | }; 22 | } 23 | ) 24 | class UserListing extends PureComponent { 25 | componentWillMount() { 26 | this.props.actions.getAllUsers(); 27 | } 28 | onEditClick = rowData => { 29 | this.props.history.push(urljoin(this.props.match.url, `${rowData.id}`)); 30 | }; 31 | onDeleteClick = rowData => { 32 | this.props.messageBoxActions.showMessage( 33 | MessageBoxType.CONFIRM, 34 | `User ${rowData.firstName} will be deleted, are you sure?`, 35 | event => { 36 | if (event.currentTarget.value === 'OK') { 37 | this.props.actions.deleteUser(rowData.id); 38 | } 39 | } 40 | ); 41 | }; 42 | onAddClick = () => { 43 | this.props.history.push(urljoin(this.props.match.url, '0')); 44 | }; 45 | render() { 46 | const columns = [ 47 | { 48 | Header: 'Username', 49 | accessor: 'username' 50 | }, 51 | { 52 | Header: 'First Name', 53 | accessor: 'firstName' 54 | }, 55 | { 56 | Header: 'Last Name', 57 | accessor: 'lastName' 58 | }, 59 | { 60 | Header: 'Email', 61 | accessor: 'email' 62 | } 63 | ]; 64 | return ( 65 | 66 | 74 | 75 | 76 | ); 77 | } 78 | } 79 | export default UserListing; 80 | -------------------------------------------------------------------------------- /src/views/UserProfile/UserProfile.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { bindActionCreators } from 'redux'; 4 | import UserActions from '../../actions/Users'; 5 | import Dialog from '@material-ui/core/Dialog'; 6 | import UserEditTransition from './UserEditTransition'; 7 | import UserForm from '../../components/User/UserForm'; 8 | import { reduxForm } from 'redux-form'; 9 | import { isValidEmail, convertToFormData } from '../../commons/commonFuncs'; 10 | import { isUsernameOrEmailExist } from '../../api/userApi'; 11 | 12 | const validate = values => { 13 | const errors = {}; 14 | const requiredFields = ['username', 'email', 'firstName', 'lastName']; 15 | requiredFields.forEach(field => { 16 | if (!values[field]) { 17 | errors[field] = 'Required'; 18 | } 19 | }); 20 | if (!isValidEmail(values.email)) { 21 | errors.email = 'Invalid email address'; 22 | } 23 | return errors; 24 | }; 25 | const asyncValidate = (values, dispatch, props, blurredField) => { 26 | const id = values.id !== undefined ? values.id : 0; 27 | if (blurredField === 'email' && values.email) { 28 | return isUsernameOrEmailExist(values.email, id).then(response => { 29 | if (response) { 30 | throw { email: `${values.email} is already taken` }; 31 | } 32 | }); 33 | } 34 | return isUsernameOrEmailExist(values.username, id).then(response => { 35 | if (response) { 36 | throw { username: `${values.username} is already taken` }; 37 | } 38 | }); 39 | }; 40 | @connect( 41 | (state, props) => { 42 | const id = props.match.params['id']; 43 | let result = state.users.data.find(i => i.id == id); 44 | return { user: result, initialValues: result }; 45 | }, 46 | dispatch => { 47 | return { 48 | actions: bindActionCreators(UserActions, dispatch) 49 | }; 50 | } 51 | ) 52 | @reduxForm({ 53 | form: 'UserProfile', 54 | validate, 55 | asyncValidate, 56 | asyncBlurFields: ['username', 'email'] 57 | }) 58 | class UserProfile extends PureComponent { 59 | constructor(props) { 60 | super(props); 61 | this.state = { 62 | user: props.user || { 63 | id: 0, 64 | username: '', 65 | email: '', 66 | firstName: '', 67 | lastName: '', 68 | avatar: '' 69 | }, 70 | open: props.match.params['id'] != undefined, 71 | file: null, 72 | avatarChanged: false 73 | }; 74 | this.handleClose = this.handleClose.bind(this); 75 | this.onSave = this.onSave.bind(this); 76 | } 77 | handleClose = () => { 78 | this.props.history.push('/users'); 79 | }; 80 | onSave = e => { 81 | const data = convertToFormData(e); 82 | data.append('avatar', this.state.file); 83 | this.props.actions.saveUser(data); 84 | this.handleClose(); 85 | }; 86 | componentWillUpdate(nextProps) { 87 | if (this.props.user !== nextProps.user) { 88 | this.setState((prevState, props) => { 89 | return { 90 | user: { 91 | ...props.user 92 | } 93 | }; 94 | }); 95 | } 96 | } 97 | onChangeAvatar = e => { 98 | let val = e.target.value; 99 | let _this = this; 100 | const file = e.target.files[0]; 101 | if (e.target.type === 'file') { 102 | let reader = new FileReader(); 103 | reader.onload = function(pe) { 104 | val = pe.target.result; 105 | _this.setState((prevState, props) => { 106 | return { 107 | user: { 108 | ...prevState.user, 109 | avatar: val 110 | }, 111 | file: file, 112 | avatarChanged: true 113 | }; 114 | }); 115 | }; 116 | reader.readAsDataURL(file); 117 | } 118 | }; 119 | render() { 120 | return ( 121 | 126 | 134 | 135 | ); 136 | } 137 | } 138 | 139 | export default UserProfile; 140 | -------------------------------------------------------------------------------- /src/views/loaders/ComponentLoader.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import LinearProgress from '@material-ui/core/LinearProgress'; 3 | 4 | const loading = () => ; 5 | export default loading; 6 | -------------------------------------------------------------------------------- /src/views/loaders/index.ts: -------------------------------------------------------------------------------- 1 | import Loadable from 'react-loadable'; 2 | import loading from './ComponentLoader'; 3 | 4 | const Loader = (loader: any) => 5 | Loadable({ 6 | loader, 7 | loading, 8 | timeout: 10000, 9 | /* devblock:start */ 10 | delay: 1000 11 | /* devblock:end */ 12 | }); 13 | 14 | export default Loader; 15 | -------------------------------------------------------------------------------- /tests/_dummy_data/general.jsx: -------------------------------------------------------------------------------- 1 | // ############################## 2 | // // // Tasks for TasksCard - see Dashboard view 3 | // ############################# 4 | 5 | const bugs = [ 6 | "Sign contract for \"What are conference organizers afraid of?\"", 7 | "Lines From Great Russian Literature? Or E-mails From My Boss?", 8 | "Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit", 9 | "Create 4 Invisible User Experiences you Never Knew About" 10 | ]; 11 | const website = [ 12 | "Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit", 13 | "Sign contract for \"What are conference organizers afraid of?\"" 14 | ]; 15 | const server = [ 16 | "Lines From Great Russian Literature? Or E-mails From My Boss?", 17 | "Flooded: One year later, assessing what was lost and what was found when a ravaging rain swept through metro Detroit", 18 | "Sign contract for \"What are conference organizers afraid of?\"" 19 | ]; 20 | 21 | export default { 22 | // these 3 are used to create the tasks lists in TasksCard - Dashboard view 23 | bugs, 24 | website, 25 | server 26 | }; 27 | -------------------------------------------------------------------------------- /tests/_dummy_data/simulator.js: -------------------------------------------------------------------------------- 1 | export default function(data, inSecond = 5) { 2 | return new Promise(resolve => 3 | setTimeout(() => resolve(data), inSecond * 1000) 4 | ); 5 | } 6 | -------------------------------------------------------------------------------- /tests/_dummy_data/users.js: -------------------------------------------------------------------------------- 1 | export const users = [{ 2 | id: 1, 3 | username: 'Tuan', 4 | email: 'xuantuan93@gmail.com', 5 | firstname: 'Tuan', 6 | lastname: 'Nguyen', 7 | avatar: null 8 | }, { 9 | id: 2, 10 | username: 'Steven', 11 | email: 'baoduy2412@outlook.com', 12 | firstname: 'Steven', 13 | lastname: 'Hoang', 14 | avatar: null 15 | }]; -------------------------------------------------------------------------------- /tests/commons/commonFuncs.test.ts: -------------------------------------------------------------------------------- 1 | require('jest-localstorage-mock'); 2 | 3 | import { getImgSrc, newGuid } from '../../src/commons/commonFuncs'; 4 | 5 | window.sessionStorage.setItem('BASE_URL', '/ReactJs'); 6 | 7 | test('2 GUID must be difference', () => { 8 | const g1 = newGuid(); 9 | const g2 = newGuid(); 10 | 11 | expect(g1).not.toBe(g2); 12 | }); 13 | 14 | test('getImgSrc should add basename to url', () => { 15 | expect(getImgSrc('/img/a.png')).toContain('/ReactJs'); 16 | }); 17 | 18 | test('getImgSrc should not add basename to url', () => { 19 | expect(getImgSrc('/ReactJs/img/a.png')).toBe('/ReactJs/img/a.png'); 20 | }); 21 | 22 | test('getImgSrc should return original object', () => { 23 | const url: any = { a: 1 }; 24 | expect(getImgSrc(url)).toBe(url); 25 | }); 26 | 27 | test('getImgSrc should return original string', () => { 28 | const url = '123'; 29 | window.sessionStorage.setItem('BASE_URL', '/'); 30 | 31 | expect(getImgSrc(url)).toBe(url); 32 | }); 33 | 34 | test('getImgSrc should do nothing', () => { 35 | const url = '123'; 36 | window.sessionStorage.setItem('BASE_URL', ''); 37 | 38 | expect(getImgSrc(url)).toBe(url); 39 | }); 40 | -------------------------------------------------------------------------------- /tests/commons/guard.test.ts: -------------------------------------------------------------------------------- 1 | import * as guard from '../../src/commons/guard'; 2 | 3 | describe('Testing Guard common functions', () => { 4 | test('Check Null and Undefined', () => { 5 | expect(() => guard.argumentNotNull(null)).toThrowError(); 6 | expect(() => guard.argumentNotNull(undefined)).toThrowError(); 7 | 8 | expect(() => guard.argumentNotNull('')).not.toThrowError(); 9 | expect(() => guard.argumentNotNull('Duy')).not.toThrowError(); 10 | }); 11 | 12 | test('Check Empty', () => { 13 | expect(() => guard.argumentNotEmpty(null)).toThrowError(); 14 | expect(() => guard.argumentNotEmpty(undefined)).toThrowError(); 15 | expect(() => guard.argumentNotEmpty('')).toThrowError(); 16 | 17 | expect(() => guard.argumentNotEmpty('Duy')).not.toThrowError(); 18 | }); 19 | 20 | test('Check Function', () => { 21 | expect(() => guard.argumentIsFunc(null)).toThrowError(); 22 | expect(() => guard.argumentIsFunc(undefined)).toThrowError(); 23 | expect(() => guard.argumentIsFunc('')).toThrowError(); 24 | 25 | expect(() => guard.argumentIsFunc(() => {})).not.toThrowError(); 26 | }); 27 | 28 | test('Check String', () => { 29 | expect(() => guard.argumentIsString(null)).toThrowError(); 30 | expect(() => guard.argumentIsString(undefined)).toThrowError(); 31 | expect(() => guard.argumentIsString(123)).toThrowError(); 32 | expect(() => guard.argumentIsString(true)).toThrowError(); 33 | 34 | expect(() => guard.argumentIsString('')).not.toThrowError(); 35 | expect(() => guard.argumentIsString('Duy')).not.toThrowError(); 36 | }); 37 | 38 | test('Array Test', () => { 39 | expect(() => guard.argumentIsArray(null)).toThrowError(); 40 | expect(() => guard.argumentIsArray(undefined)).toThrowError(); 41 | expect(() => guard.argumentIsArray(123)).toThrowError(); 42 | expect(() => guard.argumentIsArray(true)).toThrowError(); 43 | 44 | expect(() => guard.argumentIsArray([])).not.toThrowError(); 45 | expect(() => guard.argumentIsArray([1, 2, 3])).not.toThrowError(); 46 | }); 47 | 48 | test('Array Not Empty Test', () => { 49 | expect(() => guard.argumentIsArrayAndNotEmpty([])).toThrowError(); 50 | expect(() => guard.argumentIsArray([1, 2, 3])).not.toThrowError(); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /tests/components/MessageBox/index.test.tsx: -------------------------------------------------------------------------------- 1 | /*eslint no-console: ["off", { allow: ["warn", "error"] }] */ 2 | 3 | import * as React from 'react'; 4 | 5 | import MessageBox from '../../../src/components/MessageBox'; 6 | import Type from '../../../src/components/MessageBox/MessageBoxType'; 7 | import { mount } from 'enzyme'; 8 | 9 | const render = ({ callBack, ...rest }: any = {}) => { 10 | if (!callBack) callBack = () => true; 11 | 12 | const item = mount(); 13 | 14 | if (item.mount) item.mount(); 15 | return item; 16 | }; 17 | 18 | describe(`Testing ${MessageBox.displayName} component`, () => { 19 | test('render without error', () => { 20 | const item = render({ message: 'Hello', open: true }); 21 | expect(item).toMatchSnapshot(); 22 | }); 23 | 24 | test(`render ${Type.INFO}`, () => { 25 | const item = render({ type: Type.INFO, message: 'Hello' }); 26 | expect(item).toMatchSnapshot(); 27 | }); 28 | 29 | test(`render ${Type.CONFIRM}`, () => { 30 | const item = render({ type: Type.CONFIRM, message: 'Hello' }); 31 | expect(item).toMatchSnapshot(); 32 | }); 33 | 34 | test(`render ${Type.DANGER}`, () => { 35 | const item = render({ type: Type.DANGER, message: 'Hello' }); 36 | expect(item).toMatchSnapshot(); 37 | }); 38 | 39 | test(`render ${Type.SUCCESS}`, () => { 40 | const item = render({ type: Type.SUCCESS, message: 'Hello' }); 41 | expect(item).toMatchSnapshot(); 42 | }); 43 | 44 | test(`render ${Type.WARNING}`, () => { 45 | const item = render({ type: Type.WARNING, message: 'Hello' }); 46 | expect(item).toMatchSnapshot(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /tests/components/User/UserForm.test.tsx.ignore: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import UserForm from '../../../src/components/User/UserForm'; 4 | import { mount } from 'enzyme'; 5 | 6 | const render = ({ user, onClose, onSave }: any = {}) => { 7 | const item = mount( 8 | {}} 13 | /> 14 | ); 15 | 16 | if (item.mount) item.mount(); 17 | return item; 18 | }; 19 | describe('Testing UserForm', () => { 20 | const user = { 21 | id: 0, 22 | username: '', 23 | email: '', 24 | firstName: '', 25 | lastName: '', 26 | avatar: null 27 | }; 28 | test('render component', () => { 29 | const item = render({ user: user }); 30 | expect(item).toMatchSnapshot(); 31 | }); 32 | test('on click save', () => { 33 | const onSaveClick = jest.fn(); 34 | const wrapper = render({ onSave: onSaveClick, user: user }); 35 | const btn = wrapper.find('button[type="submit"]'); 36 | btn.simulate('submit'); 37 | expect(onSaveClick).toBeCalled(); 38 | }); 39 | test('on click close', () => { 40 | const onCloseClick = jest.fn(); 41 | const wrapper = render({ onClose: onCloseClick, user: user }); 42 | const btn = wrapper.find('button[type="button"]'); 43 | btn.simulate('click'); 44 | expect(onCloseClick).toBeCalled(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/components/User/UserTable.test.tsx.ignore: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import UserTable from '../../../src/components/User/UserTable'; 4 | import { mount } from 'enzyme'; 5 | 6 | const render = ({ 7 | data, 8 | onEditClick, 9 | onAddClick, 10 | onDeleteClick, 11 | columns, 12 | defaultPageSize 13 | }: any = {}) => { 14 | const item = mount( 15 | 23 | ); 24 | 25 | if (item.mount) item.mount(); 26 | return item; 27 | }; 28 | 29 | describe('Testing UserTable', () => { 30 | test('render component', () => { 31 | const item = render(); 32 | expect(item).toMatchSnapshot(); 33 | }); 34 | 35 | test('on click add', () => { 36 | const onAddClick = jest.fn(); 37 | const wrapper = render({ onAddClick: onAddClick }); 38 | const btn = wrapper.find('button[aria-label="Add"]'); 39 | expect(wrapper).toMatchSnapshot(); 40 | btn.simulate('click'); 41 | expect(onAddClick).toBeCalled(); 42 | }); 43 | 44 | test('on click edit', () => { 45 | const onEditClick = jest.fn(); 46 | const data = [{ name: 'tuan' }]; 47 | const columns = [ 48 | { 49 | Header: 'Name', 50 | accessor: 'name' 51 | } 52 | ]; 53 | const wrapper = render({ 54 | onEditClick: onEditClick, 55 | data: data, 56 | columns: columns 57 | }); 58 | 59 | const btn = wrapper.find('button[title="Edit"]'); 60 | expect(wrapper).toMatchSnapshot(); 61 | if (btn) btn.simulate('click'); 62 | expect(onEditClick).toBeCalled(); 63 | }); 64 | 65 | test('on click delete', () => { 66 | const onDeleteClick = jest.fn(); 67 | const data = [{ name: 'tuan' }]; 68 | const columns = [ 69 | { 70 | Header: 'Name', 71 | accessor: 'name' 72 | } 73 | ]; 74 | const wrapper = render({ 75 | onDeleteClick: onDeleteClick, 76 | data: data, 77 | columns: columns 78 | }); 79 | const btn = wrapper.find('button[title="Delete"]'); 80 | 81 | expect(wrapper).toMatchSnapshot(); 82 | btn.simulate('click'); 83 | expect(onDeleteClick).toBeCalled(); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /tests/components/notification/NotificationPopup.test.tsx: -------------------------------------------------------------------------------- 1 | /*eslint no-console: ["off", { allow: ["warn", "error"] }] */ 2 | 3 | import * as React from 'react'; 4 | 5 | import NotificationPopup from '../../../src/components/Notification/NotificationPopup'; 6 | import Type from '../../../src/components/Notification/NotificationType'; 7 | import { mount } from 'enzyme'; 8 | 9 | const render = ({ callBack, ...rest }: any = {}) => { 10 | if (!callBack) callBack = () => true; 11 | 12 | const item = mount(); 13 | 14 | if (item.mount) item.mount(); 15 | return item; 16 | }; 17 | 18 | describe(`Render NotificationPopup tests`, () => { 19 | test('Render 3 items', () => { 20 | const item = render({ 21 | dataSource: [ 22 | { 23 | type: Type.CONFIRM, 24 | message: 'Hello' 25 | }, 26 | { 27 | type: Type.INFO, 28 | message: 'Hello' 29 | }, 30 | { 31 | type: Type.DANGER, 32 | message: 'Hello' 33 | } 34 | ] 35 | }); 36 | 37 | expect(item).toMatchSnapshot(); 38 | expect(item.find('svg').length).toBe(6); 39 | }); 40 | 41 | test('Error when no DataSource', () => { 42 | const original = console.error; 43 | console.error = jest.fn(); 44 | render(); 45 | 46 | expect(console.error).toHaveBeenCalled(); 47 | console.error = original; 48 | }); 49 | 50 | test('Error when DataSource is wrong type', () => { 51 | const original = console.error; 52 | console.error = jest.fn(); 53 | render({ dataSource: [{}, {}, {}] }); 54 | 55 | expect(console.error).toHaveBeenCalled(); 56 | console.error = original; 57 | }); 58 | 59 | test('onClose called', () => { 60 | const item = render({ 61 | dataSource: [ 62 | { 63 | type: Type.CONFIRM, 64 | message: 'Hello' 65 | }, 66 | { 67 | type: Type.INFO, 68 | message: 'Hello' 69 | }, 70 | { 71 | type: Type.DANGER, 72 | message: 'Hello', 73 | onClose: () => true 74 | } 75 | ], 76 | displayIn: 2, 77 | subsequentDelay: 1 78 | }); 79 | 80 | const p = new Promise((r, j) => { 81 | item.setProps({ onClose: () => r(true) }); 82 | setTimeout(() => j(false), 4000); 83 | }); 84 | 85 | return expect(p).resolves.toBe(true); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /tests/components/notification/__snapshots__/NotificationPopup.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Render NotificationPopup tests Render 3 items 1`] = `""`; 4 | -------------------------------------------------------------------------------- /tests/components/notification/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Render Notification tests Render 3 items 1`] = ` 4 | Array [ 5 | Array [ 6 | , 46 | "", 47 | ], 48 | "", 49 | "", 50 | ] 51 | `; 52 | -------------------------------------------------------------------------------- /tests/components/notification/index.test.tsx: -------------------------------------------------------------------------------- 1 | /*eslint no-console: ["off", { allow: ["warn", "error"] }] */ 2 | 3 | import * as React from 'react'; 4 | 5 | import Notification from '../../../src/components/Notification'; 6 | import Type from '../../../src/components/Notification/NotificationType'; 7 | import { mount } from 'enzyme'; 8 | 9 | const render = ({ callBack, ...rest }: any = {}) => { 10 | if (!callBack) callBack = () => true; 11 | 12 | const item = mount(); 13 | 14 | if (item.mount) item.mount(); 15 | return item; 16 | }; 17 | 18 | describe(`Render Notification tests`, () => { 19 | test('Render 3 items', () => { 20 | const item = render({ 21 | dataSource: [ 22 | { 23 | type: Type.CONFIRM, 24 | message: 'Hello' 25 | }, 26 | { 27 | type: Type.INFO, 28 | message: 'Hello' 29 | }, 30 | { 31 | type: Type.DANGER, 32 | message: 'Hello' 33 | } 34 | ] 35 | }); 36 | 37 | expect(item).toMatchSnapshot(); 38 | expect(item.find('svg').length).toBe(6); 39 | }); 40 | 41 | test('Error when no DataSource', () => { 42 | const original = console.error; 43 | console.error = jest.fn(); 44 | render(); 45 | 46 | expect(console.error).toHaveBeenCalled(); 47 | console.error = original; 48 | }); 49 | 50 | test('Error when DataSource is wrong type', () => { 51 | const original = console.error; 52 | console.error = jest.fn(); 53 | render({ dataSource: [{}, {}, {}] }); 54 | 55 | expect(console.error).toHaveBeenCalled(); 56 | console.error = original; 57 | }); 58 | 59 | test('onClose called', () => { 60 | const item = render({ 61 | dataSource: [ 62 | { 63 | type: Type.CONFIRM, 64 | message: 'Hello' 65 | }, 66 | { 67 | type: Type.INFO, 68 | message: 'Hello' 69 | }, 70 | { 71 | type: Type.DANGER, 72 | message: 'Hello', 73 | onClose: () => true 74 | } 75 | ], 76 | displayIn: 2, 77 | subsequentDelay: 1 78 | }); 79 | 80 | const p = new Promise((r, j) => { 81 | item.setProps({ onClose: () => r(true) }); 82 | setTimeout(() => j(false), 4000); 83 | }); 84 | 85 | return expect(p).resolves.toBe(true); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "es2015", 5 | "target": "es5", 6 | "lib": ["es6", "esnext", "dom"], 7 | //"sourceMap": true, 8 | "allowJs": true, 9 | "jsx": "react", 10 | "moduleResolution": "node", 11 | "rootDir": "src", 12 | //"noEmit": true, 13 | "pretty": true, 14 | "skipLibCheck": true, 15 | "removeComments": false, 16 | "forceConsistentCasingInFileNames": true, 17 | //"emitDeclarationOnly": true, 18 | "allowSyntheticDefaultImports": true, 19 | "noImplicitReturns": true, 20 | "noImplicitThis": true, 21 | "noImplicitAny": true, 22 | "strictNullChecks": true, 23 | "suppressImplicitAnyIndexErrors": true, 24 | "noUnusedLocals": true, 25 | "experimentalDecorators": true, 26 | //"declaration": true, 27 | "strict": true 28 | }, 29 | "include": ["./src/**/*.tsx", "./src/**/*.ts", "tests/*.ts"], 30 | "exclude": [ 31 | "node_modules", 32 | "build", 33 | "dist", 34 | "tests", 35 | "configs", 36 | "scripts", 37 | "acceptance-tests", 38 | "webpack", 39 | "jest", 40 | "src/setupTests.ts", 41 | "sample" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } 7 | --------------------------------------------------------------------------------