├── .env ├── .gitignore ├── README.md ├── babel.config.js ├── changelog.md ├── config-overrides.js ├── database.sqlite ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── screenshot.png ├── server ├── config.js ├── server.js └── start.js ├── src ├── actions │ ├── navigation.js │ ├── posts.js │ ├── runtime.js │ └── user.js ├── components │ ├── App.js │ ├── Footer │ │ ├── Footer.js │ │ ├── Footer.module.scss │ │ └── package.json │ ├── Header │ │ ├── Header.js │ │ ├── Header.module.scss │ │ └── package.json │ ├── Icon │ │ ├── Icon.js │ │ ├── icons │ │ │ ├── components.svg │ │ │ ├── dashboard.svg │ │ │ ├── index.js │ │ │ ├── logo.svg │ │ │ ├── mail.svg │ │ │ ├── notification.svg │ │ │ ├── notifications.svg │ │ │ ├── settings.svg │ │ │ ├── tables.svg │ │ │ └── typography.svg │ │ └── package.json │ ├── Layout │ │ ├── Layout.js │ │ ├── Layout.module.scss │ │ └── package.json │ ├── Sidebar │ │ ├── LinksGroup │ │ │ ├── LinksGroup.js │ │ │ └── LinksGroup.module.scss │ │ ├── Sidebar.js │ │ ├── Sidebar.module.scss │ │ └── package.json │ └── Widget │ │ ├── Widget.js │ │ ├── Widget.module.scss │ │ └── package.json ├── config.js ├── constants │ └── index.js ├── data │ ├── models │ │ ├── Post.js │ │ ├── User.js │ │ ├── UserClaim.js │ │ ├── UserLogin.js │ │ ├── UserProfile.js │ │ └── index.js │ ├── mutations │ │ └── posts.js │ ├── queries │ │ ├── me.js │ │ ├── news.js │ │ └── posts.js │ ├── schema.js │ ├── sequelize.js │ └── types │ │ ├── NewsItemType.js │ │ ├── PostType.js │ │ └── UserType.js ├── fonts │ └── glyphicons │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 ├── images │ ├── photo.jpg │ └── tables │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ └── 5.jpg ├── index.js ├── pages │ ├── about │ │ └── About.js │ ├── buttons │ │ ├── Buttons.js │ │ └── package.json │ ├── charts │ │ ├── Charts.js │ │ ├── charts │ │ │ ├── BarChart.js │ │ │ ├── LineChart.js │ │ │ ├── PercentAreaChart.js │ │ │ └── PieChart.js │ │ └── package.json │ ├── dashboard │ │ ├── Dashboard.js │ │ ├── Dashboard.module.scss │ │ ├── mock.js │ │ └── package.json │ ├── error │ │ ├── ErrorPage.js │ │ ├── ErrorPage.scss │ │ └── package.json │ ├── google │ │ ├── Google.js │ │ ├── Google.module.scss │ │ └── package.json │ ├── icons │ │ ├── Icons.js │ │ ├── Icons.module.scss │ │ └── package.json │ ├── login │ │ ├── Login.js │ │ ├── Login.module.scss │ │ └── package.json │ ├── notFound │ │ ├── NotFound.js │ │ ├── NotFound.module.scss │ │ └── package.json │ ├── notifications │ │ ├── Notifications.js │ │ ├── Notifications.module.scss │ │ └── package.json │ ├── posts │ │ ├── Posts.js │ │ ├── list │ │ │ ├── PostList.js │ │ │ ├── PostList.module.scss │ │ │ └── mock.js │ │ ├── new │ │ │ ├── PostNew.js │ │ │ └── PostNew.module.scss │ │ └── package.json │ ├── privacy │ │ ├── Privacy.js │ │ └── package.json │ ├── profile │ │ ├── Profile.js │ │ ├── Profile.module.scss │ │ └── package.json │ ├── register │ │ ├── Register.js │ │ └── package.json │ ├── tables │ │ ├── Static.module.scss │ │ ├── Tables.js │ │ └── package.json │ └── typography │ │ ├── Typography.js │ │ └── package.json ├── reducers │ ├── auth.js │ ├── index.js │ ├── navigation.js │ ├── posts.js │ └── runtime.js ├── serviceWorker.js ├── static.json └── styles │ ├── _general.scss │ ├── _mixins.scss │ ├── _overrides.scss │ ├── _utils.scss │ ├── _variables.scss │ ├── app.scss │ └── theme.scss ├── table.png └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_NODE_ENV=development 2 | REACT_APP_PORT=5000 3 | REACT_APP_BACKEND=false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Editors and IDEs 26 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## React Dashboard — "[isomorphic](http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/)" admin dashboard template 2 | built with [React](https://facebook.github.io/react/), [Bootstrap](http://getbootstrap.com/), [React Router](https://reacttraining.com/react-router/), 3 | [Redux](http://redux.js.org/) and [GraphQL](http://graphql.org/) based on 4 | [Create React App](https://github.com/facebook/create-react-app) and latest industry best practices. 5 | 6 | [View Demo](https://flatlogic.com/admin-dashboards/react-dashboard/demo) | [Download](https://github.com/flatlogic/react-dashboard.git) | [More templates](https://flatlogic.com/templates) | [Support forum](https://flatlogic.com/forum) 7 | 8 | [![react-dashboard](screenshot.png)](https://flatlogic.com/admin-dashboards/react-dashboard/demo) 9 | 10 | This seed project is like a free version of a template you can find on [Themeforest](https://themeforest.net/category/site-templates/admin-templates) or [Wrapbootstrap](https://wrapbootstrap.com/themes/admin), with working backend integration, to get you started on your next [business software](https://flatlogic.com/) development. 11 | 12 | 13 | ## Features 14 | * React 15 | * Mobile friendly layout (responsive) 16 | * React Router 17 | * Bootstrap3 18 | * GraphQL 19 | * Nodejs backend inegration 20 | * Sass styles 21 | * Stylish, clean, responsive layout 22 | * Lots of utility css classes for rapid development (flatlogic css set) 23 | * Authentication 24 | * CRUD operations examples 25 | 26 | ## Quick Start 27 | 28 | #### 1. Get the latest version 29 | 30 | You can start by cloning the latest version of React Dashboard on your 31 | local machine by running: 32 | 33 | ```shell 34 | $ git clone -o react-dashboard -b master --single-branch \ 35 | https://github.com/flatlogic/react-dashboard.git MyApp 36 | $ cd MyApp 37 | ``` 38 | 39 | #### 2. Run `yarn install` 40 | 41 | This will install both run-time project dependencies and developer tools listed 42 | in [package.json](../package.json) file. 43 | 44 | #### 3. Run `yarn dev` 45 | 46 | This command will start the app with simultaneously with express server, 47 | set up your database, start local server XAMPP, opensever, or other tool 48 | to start database, connect to it in file 49 | ```shell 50 | src > data > sequelize.js. 51 | ``` 52 | Also go to 53 | ```shell 54 | src > data > schema.js 55 | ``` 56 | and enable mutation. This preparation 57 | will enable to realize CRUD operations locally 58 | 59 | ### 4. How to create db 60 | 61 | Create db. For instance name it "sequelize" and add posts table to it, 62 | your table should have same structure as you can see on the screenshot 63 |
64 | ![table structure](table.png) 65 | 66 | > [http://localhost:3000/](http://localhost:3000/) — Node.js server
67 | > [http://localhost:3000/graphql](http://localhost:3000/graphql) — GraphQL server and IDE
68 | 69 | #### 5. Run `yarn build` 70 | 71 | Builds the app for production to the build folder. 72 | It correctly bundles React in production mode and optimizes the build for the best performance. 73 | 74 | The build is minified and the filenames include the hashes. 75 | Your app is ready to be deployed! 76 | 77 | Now you can open your web app in a browser, on mobile devices and start 78 | hacking. Whenever you modify any of the source files inside the `/src` folder, 79 | the module bundler ([Webpack](http://webpack.github.io/)) will recompile the 80 | app on the fly and refresh all the connected browsers. 81 | 82 | For more info please refer to [getting started](./docs/getting-started.md) guide to download and run the project (Node.js >= 6.5) 83 | 84 | ## Support 85 | For any additional information please go to our [**support forum**](https://flatlogic.com/forum) and raise your questions or feedback provide there. We highly appreciate your participation! 86 | 87 | ## How can I support developers? 88 | - Star our GitHub repo :star: 89 | - [Tweet about it](https://twitter.com/intent/tweet?text=Amazing%20dashboard%20built%20with%20NodeJS,%20React%20and%20Bootstrap!&url=https://github.com/flatlogic/react-dashboard&via=flatlogic). 90 | - Create pull requests, submit bugs, suggest new features or documentation updates :wrench: 91 | - Follow [@flatlogic on Twitter](https://twitter.com/flatlogic). 92 | - Subscribe to Flatlogic newsletter at [flatlogic.com](https://flatlogic.com/) 93 | - Like our page on [Facebook](https://www.facebook.com/flatlogic/) :thumbsup: 94 | 95 | ## Premium themes 96 | Looking for premium themes and templates? Check out more [admin dashboard templates at flatlogic.com](https://flatlogic.com/admin-dashboards). 97 | 98 | ## License 99 | 100 | [MIT](https://github.com/flatlogic/react-dashboard/blob/master/LICENSE.txt) and another [MIT](https://github.com/flatlogic/react-dashboard/blob/master/LICENSE-react-starter-kit.txt) from RSK. 101 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "presets": [ 3 | "react-app" 4 | ], 5 | "plugins": [ 6 | "@babel/plugin-proposal-class-properties", 7 | "@babel/plugin-proposal-optional-chaining" 8 | ] 9 | }; -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.5.0] - 14/09/2023 4 | 5 | - Added react-scripts version 5 along with webpack version 5 support. 6 | - Added support for node.js version > 20 7 | - Updated React.js from 16 to 18 version 8 | - Removed old webpack configs. 9 | - Removed unnecessary dev dependencies. 10 | - Replaced deprecated glyphicons-halflings icon library with bootstrap-icons 11 | 12 | ## [1.4.2] - 22/12/2023 13 | 14 | - Updated dependencies 15 | 16 | ## [1.4.1] 17 | 18 | ### Updated 19 | - Added link to flatlogic on login page 20 | 21 | ## [1.4.0] 22 | 23 | ### Updated 24 | - Update libs, fixed text visibility 25 | 26 | ## [1.3.0] 27 | 28 | ### Updated 29 | - Update libs 30 | 31 | ## [1.2.0] 32 | 33 | ### Updated 34 | - Update libs 35 | - Merge PR 36 | 37 | ## [1.1.0] 38 | 39 | ### Updated 40 | 41 | Following libs have beed updated to the recent versions: 42 | - React - 16.7.1 43 | - React-router - 4.3.1 44 | - Reactstrap - 7.1.0 45 | 46 | ## [1.0.0] 47 | 48 | ### New Features 49 | 50 | - Shadow added to image 51 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack") 2 | const path = require("path") 3 | module.exports = function override(config) { 4 | const fallback = config.resolve.fallback || {} 5 | Object.assign(fallback, { 6 | crypto: require.resolve("crypto-browserify"), 7 | stream: require.resolve("stream-browserify"), 8 | assert: require.resolve("assert"), 9 | http: require.resolve("stream-http"), 10 | https: require.resolve("https-browserify"), 11 | os: require.resolve("os-browserify"), 12 | url: require.resolve("url"), 13 | vm: require.resolve("vm-browserify"), 14 | }) 15 | config.resolve.fallback = fallback 16 | config.plugins = (config.plugins || []).concat([ 17 | new webpack.ProvidePlugin({ 18 | process: "process/browser", 19 | Buffer: ["buffer", "Buffer"], 20 | }), 21 | ]) 22 | const modules = config.resolve.modules 23 | config.resolve.modules = [...modules, path.resolve(__dirname, "src")] 24 | config.module.rules.push({ 25 | test: /\.m?js/, 26 | resolve: { 27 | fullySpecified: false, 28 | }, 29 | }) 30 | return config 31 | } 32 | -------------------------------------------------------------------------------- /database.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/database.sqlite -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-dashboard-new", 3 | "version": "1.5.0", 4 | "private": true, 5 | "dependencies": { 6 | "@typescript-eslint/eslint-plugin": "^2.34.0", 7 | "@typescript-eslint/parser": "^2.34.0", 8 | "assert": "^2.1.0", 9 | "awesome-bootstrap-checkbox": "^1.0.1", 10 | "axios": "^0.19.0", 11 | "babel-eslint": "10.1.0", 12 | "babel-jest": "^24.9.0", 13 | "babel-loader": "8.0.6", 14 | "babel-plugin-named-asset-import": "^0.3.8", 15 | "babel-preset-react-app": "^9.0.2", 16 | "bootstrap": "^4.5.2", 17 | "bootstrap-icons": "^1.11.3", 18 | "buffer": "^6.0.3", 19 | "camelcase": "^5.2.0", 20 | "classnames": "^2.3.2", 21 | "cookie-parser": "^1.4.6", 22 | "cross-env": "^6.0.3", 23 | "crypto-browserify": "^3.12.0", 24 | "css-loader": "2.1.1", 25 | "dotenv": "^8.2.0", 26 | "dotenv-expand": "5.1.0", 27 | "express-graphql": "^0.9.0", 28 | "express-jwt": "^5.3.1", 29 | "file-loader": "3.0.1", 30 | "font-awesome": "^4.7.0", 31 | "fs-extra": "7.0.1", 32 | "graphql": "^14.7.0", 33 | "graphql-relay": "^0.6.0", 34 | "graphql-sequelize": "^9.5.1", 35 | "helmet": "^3.23.3", 36 | "https-browserify": "^1.0.0", 37 | "identity-obj-proxy": "3.0.0", 38 | "is-wsl": "^1.1.0", 39 | "jest": "24.9.0", 40 | "jest-environment-jsdom-fourteen": "0.1.0", 41 | "jest-resolve": "24.9.0", 42 | "jest-watch-typeahead": "0.4.0", 43 | "jsonwebtoken": "^8.5.1", 44 | "mini-css-extract-plugin": "0.8.0", 45 | "mysql2": "^2.2.5", 46 | "normalize.css": "^8.0.1", 47 | "npm-run-all": "^4.1.5", 48 | "os-browserify": "^0.3.0", 49 | "postcss-flexbugs-fixes": "4.1.0", 50 | "postcss-loader": "3.0.0", 51 | "postcss-normalize": "7.0.1", 52 | "postcss-preset-env": "6.7.0", 53 | "postcss-safe-parser": "4.0.1", 54 | "process": "^0.11.10", 55 | "prop-types": "^15.8.1", 56 | "react": "^18.0.0", 57 | "react-app-polyfill": "^1.0.4", 58 | "react-app-rewired": "^2.2.1", 59 | "react-bootstrap-icons": "^1.11.4", 60 | "react-dev-utils": "^9.1.0", 61 | "react-dom": "^18.0.0", 62 | "react-google-maps": "^9.4.5", 63 | "react-redux": "^7.2.1", 64 | "react-router": "^5.2.0", 65 | "react-router-dom": "^5.2.0", 66 | "react-scripts": "^5.0.1", 67 | "react-sparklines": "^1.7.0", 68 | "react-toastify": "^5.4.1", 69 | "reactstrap": "^8.6.0", 70 | "recharts": "^1.8.5", 71 | "redux": "^4.0.4", 72 | "redux-thunk": "^2.3.0", 73 | "resolve": "1.22.8", 74 | "resolve-url-loader": "2", 75 | "sass": "^1.77.8", 76 | "sass-loader": "7.2.0", 77 | "semver": "6.3.0", 78 | "sequelize": "^5.22.3", 79 | "sqlite3": "^4.1.0", 80 | "stream-browserify": "^3.0.0", 81 | "stream-http": "^3.2.0", 82 | "style-loader": "1.0.0", 83 | "ts-pnp": "1.2.0", 84 | "url": "^0.11.4", 85 | "url-loader": "2.1.0", 86 | "uuid": "^3.3.3", 87 | "vm-browserify": "^1.1.2" 88 | }, 89 | "scripts": { 90 | "start": "react-app-rewired --openssl-legacy-provider start", 91 | "build": "cross-env PUBLIC_URL='/react-dashboard' react-app-rewired --openssl-legacy-provider build", 92 | "test": "react-app-rewired --openssl-legacy-provider test", 93 | "server": "cross-env PORT=5000 node --openssl-legacy-provider server/start.js", 94 | "dev": "cross-env NODE_ENV=development run-p server start" 95 | }, 96 | "proxy": "http://localhost:5000", 97 | "eslintConfig": { 98 | "extends": "react-app" 99 | }, 100 | "browserslist": { 101 | "production": [ 102 | ">0.2%", 103 | "not dead", 104 | "not op_mini all" 105 | ], 106 | "development": [ 107 | "last 1 chrome version", 108 | "last 1 firefox version", 109 | "last 1 safari version" 110 | ] 111 | }, 112 | "engines": { 113 | "node": "^20.15" 114 | }, 115 | "devDependencies": { 116 | "@babel/cli": "^7.23.4", 117 | "@babel/node": "^7.22.19", 118 | "@babel/plugin-proposal-class-properties": "^7.18.6", 119 | "@babel/plugin-proposal-optional-chaining": "^7.21.0", 120 | "@babel/polyfill": "^7.12.1", 121 | "@babel/preset-env": "^7.11.5" 122 | } 123 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/screenshot.png -------------------------------------------------------------------------------- /server/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Node.js app 3 | port: process.env.PORT || 3000, 4 | 5 | // API Gateway 6 | api: { 7 | // API URL to be used in the client-side code 8 | clientUrl: process.env.API_CLIENT_URL || '', 9 | // API URL to be used in the server-side code 10 | serverUrl: 11 | process.env.API_SERVER_URL || 12 | `http://localhost:${process.env.PORT || 3000}`, 13 | }, 14 | 15 | // Database 16 | databaseUrl: process.env.DATABASE_URL || 'sqlite:database.sqlite', 17 | 18 | // Web analytics 19 | analytics: { 20 | // https://analytics.google.com/ 21 | googleTrackingId: process.env.GOOGLE_TRACKING_ID, // UA-XXXXX-X 22 | }, 23 | 24 | defaultMeta: { 25 | title: 'React Dashboard', 26 | description: 27 | 'React Dashboard Starter project based on react-router 4, redux, graphql, bootstrap', 28 | }, 29 | 30 | // Authentication 31 | auth: { 32 | jwt: { secret: process.env.JWT_SECRET || 'React Dashboard' }, 33 | 34 | // https://developers.facebook.com/ 35 | facebook: { 36 | id: process.env.FACEBOOK_APP_ID || '186244551745631', 37 | secret: 38 | process.env.FACEBOOK_APP_SECRET || 'a970ae3240ab4b9b8aae0f9f0661c6fc', 39 | }, 40 | 41 | // https://cloud.google.com/console/project 42 | google: { 43 | id: 44 | process.env.GOOGLE_CLIENT_ID || 45 | '251410730550-ahcg0ou5mgfhl8hlui1urru7jn5s12km.apps.googleusercontent.com', 46 | secret: process.env.GOOGLE_CLIENT_SECRET || 'Y8yR9yZAhm9jQ8FKAL8QIEcd', 47 | }, 48 | 49 | // https://apps.twitter.com/ 50 | twitter: { 51 | key: process.env.TWITTER_CONSUMER_KEY || 'Ie20AZvLJI2lQD5Dsgxgjauns', 52 | secret: 53 | process.env.TWITTER_CONSUMER_SECRET || 54 | 'KTZ6cxoKnEakQCeSpZlaUCJWGAlTEBJj0y2EMkUBujA7zWSvaQ', 55 | }, 56 | }, 57 | }; 58 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import helmet from 'helmet'; 3 | import jwt from 'jsonwebtoken'; 4 | import cookieParser from 'cookie-parser'; 5 | import expressJwt from 'express-jwt'; 6 | import expressGraphQL from 'express-graphql'; 7 | import schema from '../src/data/schema' 8 | import dotenv from 'dotenv'; 9 | import config from './config'; 10 | 11 | 12 | const app = express(); 13 | app.use(helmet()); 14 | app.use(cookieParser()); 15 | app.use(express.json()) 16 | app.use(express.urlencoded({extended: true})) 17 | 18 | dotenv.config(); 19 | app.use( 20 | expressJwt({ 21 | secret: config.auth.jwt.secret, 22 | credentialsRequired: false, 23 | getToken: req => req.cookies.id_token, 24 | }), 25 | ); 26 | 27 | app.post('/login', (req, res) => { 28 | // replace with real database check in production 29 | // const user = graphql.find(req.login, req.password); 30 | let user = false; 31 | const login = req.body.login; // eslint-disable-line 32 | const password = req.body.password; // eslint-disable-line 33 | if (login === 'user' && password === 'password') { 34 | user = { user, login }; 35 | } 36 | 37 | if (user) { 38 | const expiresIn = 60 * 60 * 24 * 180; // 180 days 39 | const token = jwt.sign(user, config.auth.jwt.secret, { expiresIn }); 40 | res.cookie('id_token', token, { 41 | maxAge: 1000 * expiresIn, 42 | httpOnly: false, 43 | }); 44 | res.json({ id_token: token }); 45 | } else { 46 | res.status(401).json({ message: 'To login use user: "user", password: "password".' }); 47 | } 48 | }); 49 | 50 | app.use( 51 | '/graphql', 52 | expressJwt({ 53 | secret: config.auth.jwt.secret, 54 | getToken: req => req.cookies.id_token, 55 | }), 56 | expressGraphQL(req => ({ 57 | schema, 58 | graphiql: process.env.REACT_APP_NODE_ENV, 59 | rootValue: { request: req }, 60 | pretty: process.env.REACT_APP_NODE_ENV, 61 | })), 62 | ); 63 | 64 | const PORT = process.env.REACT_APP_PORT || 5000; 65 | 66 | const server = app.listen(PORT, console.log(`Server running in ${process.env.REACT_APP_NODE_ENV} mode on port ${PORT}`)) 67 | 68 | process.on('unhandledRejection', (err, promise) => { 69 | console.log(`Unhandled Rejection: ${err.message}`) 70 | // Close server 71 | server.close(() => process.exit(1)) 72 | }) -------------------------------------------------------------------------------- /server/start.js: -------------------------------------------------------------------------------- 1 | require("@babel/register")({ 2 | presets: ["@babel/preset-env"], 3 | "plugins": [ 4 | ["@babel/transform-runtime"] 5 | ] 6 | }); 7 | 8 | module.exports = require('./server.js') -------------------------------------------------------------------------------- /src/actions/navigation.js: -------------------------------------------------------------------------------- 1 | import { TOGGLE_SIDEBAR, OPEN_SIDEBAR, CLOSE_SIDEBAR } from '../constants'; 2 | 3 | export function toggleSidebar() { 4 | return { 5 | type: TOGGLE_SIDEBAR, 6 | }; 7 | } 8 | 9 | export function openSidebar() { 10 | return { 11 | type: OPEN_SIDEBAR, 12 | }; 13 | } 14 | 15 | export function closeSidebar() { 16 | return { 17 | type: CLOSE_SIDEBAR, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/actions/posts.js: -------------------------------------------------------------------------------- 1 | export const CREATE_POST_INITIAL = 'CREATE_POST_INITIAL'; 2 | export const CREATE_POST_REQUEST = 'CREATE_POST_REQUEST'; 3 | export const CREATE_POST_SUCCESS = 'CREATE_POST_SUCCESS'; 4 | export const CREATE_POST_FAILURE = 'CREATE_POST_FAILURE'; 5 | export const FETCH_POSTS_REQUEST = 'FETCH_POSTS_REQUEST'; 6 | export const FETCH_POSTS_SUCCESS = 'FETCH_POSTS_SUCCESS'; 7 | export const FETCH_POSTS_FAILURE = 'FETCH_POSTS_FAILURE'; 8 | 9 | function createPostInitial() { 10 | return { 11 | type: CREATE_POST_INITIAL, 12 | isFetching: false, 13 | }; 14 | } 15 | 16 | function requestCreatePost(post) { 17 | return { 18 | type: CREATE_POST_REQUEST, 19 | isFetching: true, 20 | post, 21 | }; 22 | } 23 | 24 | function createPostSuccess(post) { 25 | return { 26 | type: CREATE_POST_SUCCESS, 27 | isFetching: false, 28 | post, 29 | }; 30 | } 31 | 32 | function createPostError(message) { 33 | return { 34 | type: CREATE_POST_FAILURE, 35 | isFetching: false, 36 | message, 37 | }; 38 | } 39 | 40 | function requestFetchPosts() { 41 | return { 42 | type: FETCH_POSTS_REQUEST, 43 | isFetching: true, 44 | }; 45 | } 46 | 47 | function fetchPostsSuccess(posts) { 48 | return { 49 | type: FETCH_POSTS_SUCCESS, 50 | isFetching: false, 51 | posts, 52 | }; 53 | } 54 | 55 | function fetchPostsError(message) { 56 | return { 57 | type: FETCH_POSTS_FAILURE, 58 | isFetching: false, 59 | message, 60 | }; 61 | } 62 | 63 | export function createPost(postData) { 64 | const config = { 65 | method: 'post', 66 | headers: { 67 | Accept: 'application/json', 68 | 'Content-Type': 'application/json', 69 | }, 70 | body: JSON.stringify({ 71 | query: `mutation { 72 | addPost(title: "${postData.title}", content: "${ 73 | postData.content 74 | }"){ 75 | id, 76 | title, 77 | content 78 | } 79 | }`, 80 | }), 81 | credentials: 'include', 82 | }; 83 | 84 | return dispatch => { 85 | // We dispatch requestCreatePost to kickoff the call to the API 86 | dispatch(requestCreatePost(postData)); 87 | if(process.env.NODE_ENV === "development") { 88 | return fetch('/graphql', config) 89 | .then(response => response.json().then(post => ({ post, response }))) 90 | .then(({ post, response }) => { 91 | if (!response.ok) { 92 | // If there was a problem, we want to 93 | // dispatch the error condition 94 | dispatch(createPostError(post.message)); 95 | return Promise.reject(post); 96 | } 97 | // Dispatch the success action 98 | dispatch(createPostSuccess(post)); 99 | setTimeout(() => { 100 | dispatch(createPostInitial()); 101 | }, 5000); 102 | return Promise.resolve(post); 103 | }) 104 | .catch(err => console.error('Error: ', err)); 105 | } else { 106 | dispatch(createPostError('')); 107 | return Promise.reject(); 108 | } 109 | }; 110 | } 111 | 112 | export function fetchPosts() { 113 | const config = { 114 | method: 'post', 115 | headers: { 116 | Accept: 'application/json', 117 | 'Content-Type': 'application/json', 118 | }, 119 | body: JSON.stringify({ 120 | query: '{posts{id,title,content,updatedAt}}', 121 | }), 122 | credentials: 'include', 123 | }; 124 | 125 | return dispatch => { 126 | dispatch(requestFetchPosts()); 127 | 128 | return fetch('/graphql', config) 129 | .then(response => 130 | response.json().then(responseJson => ({ 131 | posts: responseJson.data.posts, 132 | responseJson, 133 | })), 134 | ) 135 | .then(({ posts, responseJson }) => { 136 | if (!responseJson.data.posts) { 137 | // If there was a problem, we want to 138 | // dispatch the error condition 139 | dispatch(fetchPostsError(posts.message)); 140 | return Promise.reject(posts); 141 | } 142 | // Dispatch the success action 143 | dispatch(fetchPostsSuccess(posts)); 144 | return Promise.resolve(posts); 145 | }) 146 | .catch(err => console.error('Error: ', err)); 147 | }; 148 | } 149 | -------------------------------------------------------------------------------- /src/actions/runtime.js: -------------------------------------------------------------------------------- 1 | import { SET_RUNTIME_VARIABLE } from '../constants'; 2 | 3 | export function setRuntimeVariable({ name, value }) { 4 | return { 5 | type: SET_RUNTIME_VARIABLE, 6 | payload: { 7 | name, 8 | value, 9 | }, 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/actions/user.js: -------------------------------------------------------------------------------- 1 | import appConfig from '../config'; 2 | 3 | export const LOGIN_REQUEST = 'LOGIN_REQUEST'; 4 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; 5 | export const LOGIN_FAILURE = 'LOGIN_FAILURE'; 6 | export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'; 7 | export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; 8 | export const LOGOUT_FAILURE = 'LOGOUT_FAILURE'; 9 | 10 | function requestLogin(creds) { 11 | return { 12 | type: LOGIN_REQUEST, 13 | isFetching: true, 14 | isAuthenticated: false, 15 | creds, 16 | }; 17 | } 18 | 19 | export function receiveLogin(user) { 20 | return { 21 | type: LOGIN_SUCCESS, 22 | isFetching: false, 23 | isAuthenticated: true, 24 | id_token: user.id_token, 25 | }; 26 | } 27 | 28 | function loginError(message) { 29 | return { 30 | type: LOGIN_FAILURE, 31 | isFetching: false, 32 | isAuthenticated: false, 33 | message, 34 | }; 35 | } 36 | 37 | function requestLogout() { 38 | return { 39 | type: LOGOUT_REQUEST, 40 | isFetching: true, 41 | isAuthenticated: true, 42 | }; 43 | } 44 | 45 | export function receiveLogout() { 46 | return { 47 | type: LOGOUT_SUCCESS, 48 | isFetching: false, 49 | isAuthenticated: false, 50 | }; 51 | } 52 | 53 | // Logs the user out 54 | export function logoutUser() { 55 | return dispatch => { 56 | dispatch(requestLogout()); 57 | localStorage.removeItem('id_token'); 58 | document.cookie = 'id_token=;expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 59 | dispatch(receiveLogout()); 60 | }; 61 | } 62 | 63 | export function loginUser(creds) { 64 | const config = { 65 | method: 'POST', 66 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 67 | credentials: 'include', 68 | body: `login=${creds.login}&password=${creds.password}`, 69 | }; 70 | 71 | return dispatch => { 72 | // We dispatch requestLogin to kickoff the call to the API 73 | dispatch(requestLogin(creds)); 74 | if(process.env.NODE_ENV === "development") { 75 | return fetch('/login', config) 76 | .then(response => response.json().then(user => ({ user, response }))) 77 | .then(({ user, response }) => { 78 | if (!response.ok) { 79 | // If there was a problem, we want to 80 | // dispatch the error condition 81 | dispatch(loginError(user.message)); 82 | return Promise.reject(user); 83 | } 84 | // in posts create new action and check http status, if malign logout 85 | // If login was successful, set the token in local storage 86 | localStorage.setItem('id_token', user.id_token); 87 | // Dispatch the success action 88 | dispatch(receiveLogin(user)); 89 | return Promise.resolve(user); 90 | }) 91 | .catch(err => console.error('Error: ', err)); 92 | } else { 93 | localStorage.setItem('id_token', appConfig.id_token); 94 | dispatch(receiveLogin({id_token: appConfig.id_token})) 95 | } 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Switch, Route, Redirect } from 'react-router'; 4 | import { HashRouter } from 'react-router-dom'; 5 | import { ToastContainer } from 'react-toastify'; 6 | 7 | import ErrorPage from '../pages/error'; 8 | 9 | import '../styles/theme.scss'; 10 | import LayoutComponent from '../components/Layout'; 11 | //import DocumentationLayoutComponent from '../documentation/DocumentationLayout'; 12 | import Login from '../pages/login'; 13 | import Register from '../pages/register'; 14 | import { logoutUser } from '../actions/user'; 15 | 16 | const PrivateRoute = ({dispatch, component, ...rest }) => { 17 | if (!Login.isAuthenticated(localStorage.getItem('id_token'))) { 18 | dispatch(logoutUser()); 19 | return () 20 | } else { 21 | return ( // eslint-disable-line 22 | (React.createElement(component, props))}/> 23 | ); 24 | } 25 | }; 26 | 27 | const CloseButton = ({closeToast}) => 28 | 29 | class App extends React.PureComponent { 30 | render() { 31 | return ( 32 |
33 | } 37 | /> 38 | 39 | 40 | }/> 41 | }/> 42 | 43 | }/> 45 | {/* */} 46 | 47 | 48 | 49 | 50 | 51 |
52 | 53 | ); 54 | } 55 | } 56 | 57 | const mapStateToProps = state => ({ 58 | isAuthenticated: state.auth.isAuthenticated, 59 | }); 60 | 61 | export default connect(mapStateToProps)(App); 62 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import cx from 'classnames'; 13 | import { Link } from 'react-router-dom'; 14 | 15 | import s from './Footer.module.scss'; 16 | 17 | class Footer extends React.Component { 18 | static propTypes = { 19 | className: PropTypes.string, 20 | }; 21 | 22 | static defaultProps = { 23 | className: '', 24 | }; 25 | 26 | render() { 27 | return ( 28 |
29 |
30 | © {new Date().getFullYear()}  Flatlogic LLC 31 | · 32 | Terms of Service 33 | · 34 | Privacy Policy 35 | · 36 | Support 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default Footer; 44 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.module.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../styles/app'; 11 | 12 | .root { 13 | position: absolute; 14 | bottom: 0; 15 | left: 0; 16 | right: 0; 17 | font-size: $font-size-small - 1; 18 | } 19 | 20 | .container { 21 | margin: 0 auto; 22 | padding: 7px $content-padding-horizontal; 23 | } 24 | 25 | .spacer { 26 | display: inline-block; 27 | padding: 0 5px; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Footer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Footer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Footer.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { connect } from 'react-redux'; 11 | import cx from 'classnames'; 12 | import React from 'react'; 13 | import PropTypes from 'prop-types'; 14 | import { 15 | Navbar, 16 | Nav, 17 | NavItem, 18 | Button, 19 | Dropdown, 20 | DropdownToggle, 21 | DropdownMenu, 22 | DropdownItem, 23 | Input, 24 | InputGroup, 25 | InputGroupAddon, 26 | } from 'reactstrap'; 27 | import { NavLink } from 'react-router-dom'; 28 | 29 | import Icon from '../Icon'; 30 | 31 | import photo from '../../images/photo.jpg'; 32 | import { logoutUser } from '../../actions/user'; 33 | import s from './Header.module.scss'; 34 | 35 | class Header extends React.Component { 36 | static propTypes = { 37 | sidebarToggle: PropTypes.func, 38 | dispatch: PropTypes.func.isRequired, 39 | }; 40 | 41 | static defaultProps = { 42 | sidebarToggle: () => {}, 43 | }; 44 | 45 | state = { isOpen: false }; 46 | 47 | toggleDropdown = () => { 48 | this.setState(prevState => ({ 49 | isOpen: !prevState.isOpen, 50 | })); 51 | } 52 | 53 | doLogout = () => { 54 | this.props.dispatch(logoutUser()); 55 | } 56 | 57 | render() { 58 | const {isOpen} = this.state; 59 | return ( 60 | 61 | 78 | 115 | 116 | ); 117 | } 118 | } 119 | 120 | function mapStateToProps(state) { 121 | return { 122 | init: state.runtime.initialNow, 123 | }; 124 | } 125 | export default connect(mapStateToProps)(Header); 126 | -------------------------------------------------------------------------------- /src/components/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../styles/app'; 11 | 12 | .root { 13 | background: $navbar-bg-color; 14 | 15 | :global { 16 | .input-group { 17 | background-color: $gray-100; 18 | 19 | @include media-breakpoint-down(xs) { 20 | display: none; 21 | } 22 | 23 | input { 24 | background: transparent; 25 | border: none; 26 | width: 250px; 27 | 28 | @include media-breakpoint-down(md) { 29 | width: 100px; 30 | } 31 | 32 | @include media-breakpoint-down(sm) { 33 | width: 175px; 34 | } 35 | 36 | &::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ 37 | color: $gray-300; 38 | opacity: 1; /* Firefox */ 39 | } 40 | 41 | &:-ms-input-placeholder { /* Internet Explorer 10-11 */ 42 | color: $gray-300; 43 | } 44 | 45 | &::-ms-input-placeholder { /* Microsoft Edge */ 46 | color: $gray-300; 47 | } 48 | 49 | &:hover, 50 | &:active, 51 | &:focus { 52 | border: none; 53 | outline: none; 54 | box-shadow: none; 55 | } 56 | } 57 | 58 | .input-group-append { 59 | display: flex; 60 | align-items: center; 61 | justify-content: center; 62 | 63 | i { 64 | font-size: 1.25rem; 65 | color: theme-color('light'); 66 | } 67 | } 68 | } 69 | 70 | .dropdown-menu { 71 | border: none; 72 | margin-top: 0.7rem; 73 | } 74 | 75 | .dropdown-item { 76 | &:active { 77 | background-color: $gray-200; 78 | color: $text-color; 79 | } 80 | 81 | &:focus { 82 | outline: none; 83 | } 84 | 85 | a { 86 | display: block; 87 | color: $text-color; 88 | 89 | &:hover { 90 | color: $text-color; 91 | text-decoration: none; 92 | cursor: default; 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | .arrow { 100 | color: $gray-400; 101 | transition: 0.3s; 102 | right: 0; 103 | } 104 | 105 | .arrowActive { 106 | transform: rotate(180deg); 107 | } 108 | 109 | .adminPhoto { 110 | width: 40px; 111 | height: 40px; 112 | } 113 | 114 | .headerIcon { 115 | display: flex; 116 | align-items: center; 117 | position: relative; 118 | 119 | @include media-breakpoint-down(sm) { 120 | display: none; 121 | } 122 | 123 | :global .btn { 124 | background: $white; 125 | border: none; 126 | 127 | &:hover, 128 | &:active, 129 | &:focus, 130 | &:focus:active { 131 | background: $white; 132 | box-shadow: none !important; 133 | outline: none; 134 | } 135 | } 136 | 137 | &:nth-child(n + 2) { 138 | margin-left: 0.5rem; 139 | } 140 | 141 | &:nth-child(2) { 142 | span { 143 | right: -1px; 144 | } 145 | } 146 | 147 | img { 148 | max-height: 1.65rem; 149 | width: 1.65rem; 150 | } 151 | 152 | span { 153 | position: absolute; 154 | display: flex; 155 | justify-content: center; 156 | align-items: center; 157 | top: 12px; 158 | right: -6px; 159 | color: $white; 160 | font-size: 0.55rem; 161 | width: 15px; 162 | height: 15px; 163 | border-radius: 50%; 164 | background-color: theme-color('danger'); 165 | } 166 | } 167 | 168 | .sidebarToggler { 169 | display: flex; 170 | 171 | @include media-breakpoint-up(md) { 172 | display: none !important; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/components/Header/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Header", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Header.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Icon/Icon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import icons from './icons'; 5 | 6 | const Icon = ({glyph}) => ( 7 | icons[glyph] ? {`${glyph}-icon`} : null 8 | ); 9 | 10 | Icon.propTypes = { 11 | glyph: PropTypes.string.isRequired 12 | }; 13 | 14 | export default Icon; -------------------------------------------------------------------------------- /src/components/Icon/icons/components.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/Icon/icons/dashboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/components/Icon/icons/index.js: -------------------------------------------------------------------------------- 1 | import components from './components.svg'; 2 | import dashboard from './dashboard.svg'; 3 | import notifications from './notifications.svg'; 4 | import typography from './typography.svg'; 5 | import tables from './tables.svg'; 6 | import logo from './logo.svg'; 7 | import settings from './settings.svg'; 8 | import notification from './notification.svg'; 9 | import mail from './mail.svg'; 10 | 11 | export default { 12 | components, 13 | dashboard, 14 | notifications, 15 | typography, 16 | tables, 17 | logo, 18 | settings, 19 | notification, 20 | mail, 21 | } -------------------------------------------------------------------------------- /src/components/Icon/icons/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 13 | 14 | 15 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/components/Icon/icons/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 13 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/Icon/icons/notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/Icon/icons/notifications.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/Icon/icons/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/Icon/icons/tables.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/Icon/icons/typography.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 12 | 14 | 16 | 17 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/components/Icon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Icon", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Icon.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import cx from 'classnames'; 12 | import { Switch, Route, withRouter } from 'react-router'; 13 | 14 | import s from './Layout.module.scss'; 15 | import Header from '../Header'; 16 | import Footer from '../Footer'; 17 | import Sidebar from '../Sidebar'; 18 | 19 | // Dashboard component is loaded directly as an example of server side rendering 20 | import Dashboard from '../../pages/dashboard' 21 | import Buttons from '../../pages/buttons' 22 | import Charts from '../../pages/charts' 23 | import Maps from '../../pages/google' 24 | import NotFound from '../../pages/notFound' 25 | import Icons from '../../pages/icons' 26 | import Typography from '../../pages/typography' 27 | import Tables from '../../pages/tables' 28 | import Notifications from '../../pages/notifications' 29 | import Posts from '../../pages/posts' 30 | import Profile from '../../pages/profile' 31 | import Privacy from '../../pages/privacy' 32 | 33 | class Layout extends React.Component { 34 | constructor(props) { 35 | super(props); 36 | 37 | this.state = { 38 | sidebarOpen: false, 39 | }; 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 | 46 |
49 |
51 | this.setState({ 52 | sidebarOpen: !this.state.sidebarOpen, 53 | }) 54 | } 55 | /> 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 |
73 |
74 |
75 | ); 76 | } 77 | } 78 | 79 | export default withRouter(Layout); 80 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/app'; 2 | 3 | .root { 4 | height: 100%; 5 | } 6 | 7 | .wrap { 8 | min-height: 100%; 9 | position: absolute; 10 | left: 0; 11 | right: 0; 12 | transition: left 0.3s ease-in-out, right 0.3s ease-in-out; 13 | 14 | @include media-breakpoint-up(md) { 15 | left: $sidebar-width-open; 16 | right: 0; 17 | } 18 | } 19 | 20 | .sidebarOpen { 21 | @include media-breakpoint-down(sm) { 22 | left: $sidebar-width-open; 23 | right: -$sidebar-width-open; 24 | } 25 | } 26 | 27 | .content { 28 | min-height: 100%; 29 | // 20px for footer height 30 | padding: #{$navbar-height + $content-padding-vertical} $content-padding-horizontal ($content-padding-vertical + 20px); 31 | background-color: $body-bg; 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Layout", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Layout.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Sidebar/LinksGroup/LinksGroup.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import cx from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | import { NavLink } from 'react-router-dom'; 5 | import { Collapse } from 'reactstrap'; 6 | import { Route } from 'react-router'; 7 | 8 | import Icon from '../../Icon/Icon'; 9 | 10 | import s from './LinksGroup.module.scss'; 11 | 12 | class LinksGroup extends Component { 13 | /* eslint-disable */ 14 | static propTypes = { 15 | header: PropTypes.node.isRequired, 16 | headerLink: PropTypes.string, 17 | childrenLinks: PropTypes.array, 18 | glyph: PropTypes.string, 19 | className: PropTypes.string, 20 | }; 21 | /* eslint-enable */ 22 | 23 | static defaultProps = { 24 | headerLink: null, 25 | childrenLinks: null, 26 | className: '', 27 | glyph: null, 28 | }; 29 | 30 | constructor(props) { 31 | super(props); 32 | 33 | this.state = { 34 | isOpen: false, 35 | }; 36 | } 37 | 38 | render() { 39 | const { className, childrenLinks, headerLink, header, glyph } = this.props; 40 | const { isOpen } = this.state; 41 | if (!childrenLinks) { 42 | return ( 43 |
  • 44 | 49 |
    50 | {glyph && } 51 | {header} 52 |
    53 |
    54 |
  • 55 | ); 56 | } 57 | /* eslint-disable */ 58 | return ( 59 | { 62 | return ( 63 |
  • 64 | this.setState({isOpen: !isOpen})} 67 | > 68 |
    69 | {glyph && } 70 | {header} 71 |
    72 | 73 |
    74 | {/* eslint-enable */} 75 | 76 |
      77 | {childrenLinks && 78 | childrenLinks.map(child => ( 79 |
    • 80 | 84 | this.setState({ 85 | isOpen: true, 86 | }) 87 | } 88 | activeClassName={s.headerLinkActive} 89 | > 90 | {child.name} 91 | 92 |
    • 93 | ))} 94 |
    95 |
    96 |
  • 97 | ); 98 | }} 99 | /> 100 | ); 101 | } 102 | } 103 | 104 | export default LinksGroup; 105 | -------------------------------------------------------------------------------- /src/components/Sidebar/LinksGroup/LinksGroup.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../../styles/app'; 2 | 3 | .headerLink { 4 | a { 5 | display: flex; 6 | color: $gray-500; 7 | align-items: center; 8 | text-decoration: none; 9 | cursor: pointer; 10 | 11 | &:hover, 12 | &:active, 13 | &:focus { 14 | font-weight: $font-weight-semi-bold; 15 | } 16 | } 17 | 18 | > a { 19 | position: relative; 20 | padding: 0 15px; 21 | line-height: 55px; 22 | justify-content: space-between; 23 | 24 | &:hover { 25 | font-weight: $font-weight-thin; 26 | background-color: $gray-100; 27 | } 28 | 29 | img { 30 | width: 15px; 31 | margin-right: 15px; 32 | } 33 | } 34 | } 35 | 36 | .arrow { 37 | color: $gray-400; 38 | transition: 0.3s; 39 | right: 0; 40 | } 41 | 42 | .arrowActive { 43 | transform: rotate(-90deg); 44 | } 45 | 46 | .headerLinkActive { 47 | span { 48 | color: $brand-danger; 49 | font-weight: $font-weight-semi-bold; 50 | } 51 | } 52 | 53 | .panel { 54 | border: none; 55 | box-shadow: none; 56 | margin: 0; 57 | border-radius: 0; 58 | background: rgba(#000, 0.1); 59 | 60 | div { 61 | padding: 0; 62 | } 63 | 64 | ul { 65 | padding-left: 0; 66 | font-size: $font-size-small; 67 | 68 | li { 69 | padding: 3px 0; 70 | list-style: none; 71 | } 72 | 73 | a { 74 | padding: 4px 20px 4px 44px; 75 | 76 | &:hover, 77 | &:active, 78 | &:focus { 79 | font-weight: $font-weight-semi-bold; 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {withRouter, Link} from 'react-router-dom'; 4 | 5 | import Icon from '../Icon'; 6 | import LinksGroup from './LinksGroup/LinksGroup'; 7 | 8 | import s from './Sidebar.module.scss'; 9 | 10 | const Sidebar = () => ( 11 | 63 | ); 64 | 65 | function mapStateToProps(store) { 66 | return { 67 | sidebarOpened: store.navigation.sidebarOpened, 68 | sidebarStatic: store.navigation.sidebarStatic, 69 | }; 70 | } 71 | 72 | export default withRouter(connect(mapStateToProps)(Sidebar)); 73 | -------------------------------------------------------------------------------- /src/components/Sidebar/Sidebar.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/app'; 2 | 3 | .root { 4 | width: $sidebar-width-open; 5 | position: fixed; 6 | left: 0; 7 | top: 0; 8 | bottom: 0; 9 | background-color: $sidebar-bg-color; 10 | color: $sidebar-color; 11 | } 12 | 13 | .logo { 14 | display: flex; 15 | justify-content: center; 16 | align-items: center; 17 | height: $navbar-height; 18 | width: 100%; 19 | background: $logo-gradient; 20 | 21 | a img { 22 | width: $sidebar-width-open - 60px; 23 | } 24 | } 25 | 26 | .nav { 27 | padding: 10px 0; 28 | overflow-y: auto; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Sidebar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sidebar", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Sidebar.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Widget/Widget.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import cx from 'classnames'; 12 | import PropTypes from 'prop-types'; 13 | 14 | import s from './Widget.module.scss'; 15 | 16 | class Widget extends React.Component { 17 | static propTypes = { 18 | title: PropTypes.node, 19 | className: PropTypes.string, 20 | children: PropTypes.oneOfType([ 21 | PropTypes.arrayOf(PropTypes.node), 22 | PropTypes.node, 23 | ]), 24 | }; 25 | 26 | static defaultProps = { 27 | title: null, 28 | className: '', 29 | children: [], 30 | }; 31 | 32 | render() { 33 | return ( 34 |
    35 | {this.props.title && 36 | (typeof this.props.title === 'string' ? ( 37 |
    {this.props.title}
    38 | ) : ( 39 |
    {this.props.title}
    40 | ))} 41 |
    {this.props.children}
    42 |
    43 | ); 44 | } 45 | } 46 | 47 | export default Widget; 48 | -------------------------------------------------------------------------------- /src/components/Widget/Widget.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/app'; 2 | 3 | .widget { 4 | display: block; 5 | position: relative; 6 | margin-bottom: 30px; 7 | padding: $widget-padding-y $widget-padding-x; 8 | background: $widget-bg; 9 | } 10 | 11 | .title { 12 | margin-top: 0; 13 | margin-bottom: 1.5rem / 2; 14 | color: $widget-title-color; 15 | 16 | @include clearfix(); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Widget/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Widget", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Widget.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | isBackend: process.env.REACT_APP_BACKEND, 3 | id_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjpmYWxzZSwibG9naW4iOiJ1c2VyIiwiaWF0IjoxNTczNzQ4ODI1LCJleHAiOjE2MjA0MDQ4MjV9.Jd1Trqu6izHq2R3uw4enrDlQKG4mzZdipSMdYQD_9JM' 4 | }; -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | export const SET_RUNTIME_VARIABLE = 'SET_RUNTIME_VARIABLE'; 2 | 3 | export const TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR'; 4 | export const OPEN_SIDEBAR = 'OPEN_SIDEBAR'; 5 | export const CLOSE_SIDEBAR = 'CLOSE_SIDEBAR'; 6 | -------------------------------------------------------------------------------- /src/data/models/Post.js: -------------------------------------------------------------------------------- 1 | import DataType from 'sequelize'; 2 | import Model from '../sequelize'; 3 | 4 | const Post = Model.define( 5 | 'Post', 6 | { 7 | id: { 8 | type: DataType.UUID, 9 | defaultValue: DataType.UUIDV1, 10 | primaryKey: true, 11 | }, 12 | 13 | title: { 14 | type: DataType.STRING(255), 15 | defaultValue: false, 16 | validate: { notEmpty: true }, 17 | }, 18 | 19 | content: { 20 | type: DataType.TEXT, 21 | defaultValue: false, 22 | validate: { notEmpty: true }, 23 | }, 24 | }, 25 | { 26 | indexes: [{ fields: ['title'] }], 27 | }, 28 | ); 29 | 30 | export default Post; 31 | -------------------------------------------------------------------------------- /src/data/models/User.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const User = Model.define( 14 | 'User', 15 | { 16 | id: { 17 | type: DataType.UUID, 18 | defaultValue: DataType.UUIDV1, 19 | primaryKey: true, 20 | }, 21 | 22 | email: { 23 | type: DataType.STRING(255), 24 | validate: { isEmail: true }, 25 | }, 26 | 27 | emailConfirmed: { 28 | type: DataType.BOOLEAN, 29 | defaultValue: false, 30 | }, 31 | }, 32 | { 33 | indexes: [{ fields: ['email'] }], 34 | }, 35 | ); 36 | 37 | export default User; 38 | -------------------------------------------------------------------------------- /src/data/models/UserClaim.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const UserClaim = Model.define('UserClaim', { 14 | type: { 15 | type: DataType.STRING, 16 | }, 17 | 18 | value: { 19 | type: DataType.STRING, 20 | }, 21 | }); 22 | 23 | export default UserClaim; 24 | -------------------------------------------------------------------------------- /src/data/models/UserLogin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const UserLogin = Model.define('UserLogin', { 14 | name: { 15 | type: DataType.STRING(50), 16 | primaryKey: true, 17 | }, 18 | 19 | key: { 20 | type: DataType.STRING(100), 21 | primaryKey: true, 22 | }, 23 | }); 24 | 25 | export default UserLogin; 26 | -------------------------------------------------------------------------------- /src/data/models/UserProfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const UserProfile = Model.define('UserProfile', { 14 | userId: { 15 | type: DataType.UUID, 16 | primaryKey: true, 17 | }, 18 | 19 | displayName: { 20 | type: DataType.STRING(100), 21 | }, 22 | 23 | picture: { 24 | type: DataType.STRING(255), 25 | }, 26 | 27 | gender: { 28 | type: DataType.STRING(50), 29 | }, 30 | 31 | location: { 32 | type: DataType.STRING(100), 33 | }, 34 | 35 | website: { 36 | type: DataType.STRING(255), 37 | }, 38 | }); 39 | 40 | export default UserProfile; 41 | -------------------------------------------------------------------------------- /src/data/models/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import sequelize from '../sequelize'; 11 | import User from './User'; 12 | import UserLogin from './UserLogin'; 13 | import UserClaim from './UserClaim'; 14 | import UserProfile from './UserProfile'; 15 | import Post from './Post'; 16 | 17 | User.hasMany(UserLogin, { 18 | foreignKey: 'userId', 19 | as: 'logins', 20 | onUpdate: 'cascade', 21 | onDelete: 'cascade', 22 | }); 23 | 24 | User.hasMany(UserClaim, { 25 | foreignKey: 'userId', 26 | as: 'claims', 27 | onUpdate: 'cascade', 28 | onDelete: 'cascade', 29 | }); 30 | 31 | User.hasOne(UserProfile, { 32 | foreignKey: 'userId', 33 | as: 'profile', 34 | onUpdate: 'cascade', 35 | onDelete: 'cascade', 36 | }); 37 | 38 | function sync(...args) { 39 | return sequelize.sync(...args); 40 | } 41 | 42 | export default { sync }; 43 | export { User, UserLogin, UserClaim, UserProfile, Post }; 44 | -------------------------------------------------------------------------------- /src/data/mutations/posts.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLString as StringType, 3 | GraphQLNonNull as NonNull, 4 | } from 'graphql'; 5 | 6 | import PostType from '../types/PostType'; 7 | import Post from '../models/Post'; 8 | 9 | const addPost = { 10 | type: PostType, 11 | description: 'Add a Post', 12 | args: { 13 | title: { 14 | name: 'Post title', 15 | type: new NonNull(StringType), 16 | }, 17 | content: { 18 | name: 'Post content', 19 | type: new NonNull(StringType), 20 | }, 21 | }, 22 | resolve: (root, { title, content }) => Post.create({ title, content }), 23 | }; 24 | 25 | export default addPost; 26 | -------------------------------------------------------------------------------- /src/data/queries/me.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import UserType from '../types/UserType'; 11 | 12 | const me = { 13 | type: UserType, 14 | resolve({ request }) { 15 | return ( 16 | request.user && { 17 | id: request.user.id, 18 | email: request.user.email, 19 | } 20 | ); 21 | }, 22 | }; 23 | 24 | export default me; 25 | -------------------------------------------------------------------------------- /src/data/queries/news.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { GraphQLList as List } from 'graphql'; 11 | import fetch from 'isomorphic-fetch'; 12 | import NewsItemType from '../types/NewsItemType'; 13 | 14 | // React.js News Feed (RSS) 15 | const url = 16 | 'https://api.rss2json.com/v1/api.json' + 17 | '?rss_url=https%3A%2F%2Freactjsnews.com%2Ffeed.xml'; 18 | 19 | let items = []; 20 | let lastFetchTask; 21 | let lastFetchTime = new Date(1970, 0, 1); 22 | 23 | const news = { 24 | type: new List(NewsItemType), 25 | resolve() { 26 | if (lastFetchTask) { 27 | return lastFetchTask; 28 | } 29 | 30 | if (new Date() - lastFetchTime > 1000 * 60 * 10 /* 10 mins */) { 31 | lastFetchTime = new Date(); 32 | lastFetchTask = fetch(url) 33 | .then(response => response.json()) 34 | .then(data => { 35 | if (data.status === 'ok') { 36 | items = data.items; 37 | } 38 | 39 | lastFetchTask = null; 40 | return items; 41 | }) 42 | .catch(err => { 43 | lastFetchTask = null; 44 | throw err; 45 | }); 46 | 47 | if (items.length) { 48 | return items; 49 | } 50 | 51 | return lastFetchTask; 52 | } 53 | 54 | return items; 55 | }, 56 | }; 57 | 58 | export default news; 59 | -------------------------------------------------------------------------------- /src/data/queries/posts.js: -------------------------------------------------------------------------------- 1 | import { GraphQLList as List } from 'graphql'; 2 | import { resolver } from 'graphql-sequelize'; 3 | 4 | import PostType from '../types/PostType'; 5 | import Post from '../models/Post'; 6 | 7 | const posts = { 8 | type: new List(PostType), 9 | resolve: resolver(Post), 10 | }; 11 | 12 | export default posts; 13 | -------------------------------------------------------------------------------- /src/data/schema.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLSchema as Schema, 3 | GraphQLObjectType as ObjectType, 4 | } from 'graphql'; 5 | 6 | import me from './queries/me'; 7 | import news from './queries/news'; 8 | import posts from './queries/posts'; 9 | import addPost from './mutations/posts'; 10 | 11 | const schemaConfig = { 12 | query: new ObjectType({ 13 | name: 'Query', 14 | fields: { 15 | me, 16 | news, 17 | posts, 18 | }, 19 | }), 20 | }; 21 | 22 | // for demo purposes forbid mutations in production environment, if you want to enable mutation uncomment code below 23 | 24 | // schemaConfig.mutation = new ObjectType({ 25 | // name: 'Mutation', 26 | // fields: { 27 | // addPost, 28 | // }, 29 | // }); 30 | 31 | 32 | 33 | const schema = new Schema(schemaConfig); 34 | 35 | export default schema; 36 | -------------------------------------------------------------------------------- /src/data/sequelize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import Sequelize from 'sequelize'; 11 | import config from '../../server/config'; 12 | 13 | // uncomment this and configure connection to your local db, firts goes db name, username, password. In you db create table posts with id, title, content, createdAt, updatedAt 14 | // const sequelize = new Sequelize('sequelize', 'root', '', { 15 | // host: 'localhost', 16 | // dialect: 'mysql', 17 | // operatorsAliases: false, 18 | // }); 19 | 20 | const sequelize = new Sequelize(config.databaseUrl, { 21 | define: { 22 | freezeTableName: true, 23 | }, 24 | }); 25 | 26 | export default sequelize; 27 | -------------------------------------------------------------------------------- /src/data/types/NewsItemType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { 11 | GraphQLObjectType as ObjectType, 12 | GraphQLString as StringType, 13 | GraphQLNonNull as NonNull, 14 | } from 'graphql'; 15 | 16 | const NewsItemType = new ObjectType({ 17 | name: 'NewsItem', 18 | fields: { 19 | title: { type: new NonNull(StringType) }, 20 | link: { type: new NonNull(StringType) }, 21 | author: { type: StringType }, 22 | pubDate: { type: new NonNull(StringType) }, 23 | content: { type: StringType }, 24 | }, 25 | }); 26 | 27 | export default NewsItemType; 28 | -------------------------------------------------------------------------------- /src/data/types/PostType.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLObjectType as ObjectType, 3 | GraphQLID as ID, 4 | GraphQLString as StringType, 5 | GraphQLNonNull as NonNull, 6 | } from 'graphql'; 7 | 8 | const PostType = new ObjectType({ 9 | name: 'Post', 10 | description: 'A post', 11 | fields: { 12 | id: { 13 | type: new NonNull(ID), 14 | description: 'The id of the post.', 15 | }, 16 | title: { 17 | type: StringType, 18 | description: 'The title of the post.', 19 | }, 20 | content: { 21 | type: StringType, 22 | description: 'The content of the post.', 23 | }, 24 | updatedAt: { 25 | type: StringType, 26 | description: 'The date post was updated', 27 | }, 28 | }, 29 | }); 30 | 31 | export default PostType; 32 | -------------------------------------------------------------------------------- /src/data/types/UserType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { 11 | GraphQLObjectType as ObjectType, 12 | GraphQLID as ID, 13 | GraphQLString as StringType, 14 | GraphQLNonNull as NonNull, 15 | } from 'graphql'; 16 | 17 | const UserType = new ObjectType({ 18 | name: 'User', 19 | fields: { 20 | id: { type: new NonNull(ID) }, 21 | email: { type: StringType }, 22 | }, 23 | }); 24 | 25 | export default UserType; 26 | -------------------------------------------------------------------------------- /src/fonts/glyphicons/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/fonts/glyphicons/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/fonts/glyphicons/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/fonts/glyphicons/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/fonts/glyphicons/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/fonts/glyphicons/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/fonts/glyphicons/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/fonts/glyphicons/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/images/photo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/images/photo.jpg -------------------------------------------------------------------------------- /src/images/tables/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/images/tables/1.jpg -------------------------------------------------------------------------------- /src/images/tables/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/images/tables/2.jpg -------------------------------------------------------------------------------- /src/images/tables/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/images/tables/3.jpg -------------------------------------------------------------------------------- /src/images/tables/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/images/tables/4.jpg -------------------------------------------------------------------------------- /src/images/tables/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flatlogic/react-dashboard/0a45547c50246dc8f3da058196f2d87f10b421c5/src/images/tables/5.jpg -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { createStore, applyMiddleware } from 'redux'; 4 | import { Provider } from 'react-redux' 5 | import ReduxThunk from 'redux-thunk' 6 | import * as serviceWorker from './serviceWorker'; 7 | import axios from 'axios'; 8 | 9 | import App from './components/App'; 10 | import config from './config'; 11 | import reducers from './reducers'; 12 | 13 | axios.defaults.baseURL = config.baseURLApi; 14 | axios.defaults.headers.common['Content-Type'] = "application/json"; 15 | const token = localStorage.getItem('token'); 16 | if (token) { 17 | axios.defaults.headers.common['Authorization'] = "Bearer " + token; 18 | } 19 | 20 | const store = createStore( 21 | reducers, 22 | applyMiddleware(ReduxThunk) 23 | ); 24 | 25 | ReactDOM.render( 26 | 27 | 28 | , 29 | document.getElementById('root') 30 | ); 31 | 32 | // If you want your app to work offline and load faster, you can change 33 | // unregister() to register() below. Note this comes with some pitfalls. 34 | // Learn more about service workers: http://bit.ly/CRA-PWA 35 | serviceWorker.unregister(); 36 | -------------------------------------------------------------------------------- /src/pages/about/About.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | 12 | class About extends React.Component { 13 | 14 | render() { 15 | return ( 16 |
    17 |

    About Us

    18 |
    19 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat 20 | tortor fermentum mi fermentum dignissim. Nullam vel ipsum ut ligula elementum 21 | lobortis. Maecenas aliquam, massa laoreet lacinia pretium, nisi urna venenatis 22 | tortor, nec imperdiet tellus libero efficitur metus. Fusce semper posuere 23 | ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem 24 | pharetra commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat 25 | augue libero, id consectetur tortor bibendum non. Quisque nec fringilla lorem. 26 | Nullam efficitur vulputate mauris, nec maximus leo dignissim id. 27 | 28 | In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. 29 | Morbi pellentesque venenatis felis sed convallis. Nulla varius, nibh vitae 30 | placerat tempus, mauris sem elementum ipsum, eget sollicitudin nisl est vel 31 | purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras pharetra 32 | sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non 33 | scelerisque purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus 34 | posuere placerat. Sed et libero eu nibh tristique mollis a eget lectus. Donec 35 | interdum augue sollicitudin vehicula hendrerit. Vivamus justo orci, molestie 36 | ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu 37 | tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in 38 | consequat elit tellus auctor nulla. Donec placerat elementum diam, vitae 39 | imperdiet lectus luctus at. 40 | 41 | Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam 42 | non quam nisi. Donec rutrum turpis ac diam blandit, id pulvinar mauris 43 | suscipit. Pellentesque tincidunt libero ultricies risus iaculis, sit amet 44 | consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit 45 | ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum 46 | justo sapien, sit amet consequat orci accumsan et. Aliquam ornare ullamcorper 47 | sem sed finibus. Nullam ac lacus pulvinar, egestas felis ut, accumsan est. 48 | 49 | Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem 50 | hendrerit aliquam. Integer eu varius orci. Vestibulum ante ipsum primis in 51 | faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum ante ipsum 52 | primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut at mauris 53 | nibh. Suspendisse maximus ac eros at vestibulum. 54 | 55 | Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas 56 | tortor et dui consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, 57 | vulputate nisl. Aenean congue varius ex, sit amet bibendum odio posuere at. 58 | Nulla facilisi. In finibus, nulla vitae tincidunt ornare, sapien nulla 59 | fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam 60 | enim. 61 | 62 |
    63 |
    64 | ); 65 | } 66 | } 67 | 68 | export default About; 69 | -------------------------------------------------------------------------------- /src/pages/buttons/Buttons.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Row, 4 | Col, 5 | Button, 6 | ButtonGroup, 7 | ButtonToolbar, 8 | ButtonDropdown, 9 | DropdownToggle, 10 | DropdownMenu, 11 | DropdownItem, 12 | } from 'reactstrap'; 13 | 14 | import Widget from '../../components/Widget'; 15 | 16 | class Buttons extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | 20 | this.toggleOne = this.toggleOne.bind(this); 21 | this.toggleTwo = this.toggleTwo.bind(this); 22 | this.toggleThree = this.toggleThree.bind(this); 23 | this.toggleFour = this.toggleFour.bind(this); 24 | 25 | this.onRadioBtnClickOne = this.onRadioBtnClickOne.bind(this); 26 | this.onRadioBtnClickTwo = this.onRadioBtnClickTwo.bind(this); 27 | this.onCheckboxBtnClickOne = this.onCheckboxBtnClickOne.bind(this); 28 | this.onCheckboxBtnClickTwo = this.onCheckboxBtnClickTwo.bind(this); 29 | 30 | this.state = { 31 | dropdownOpenOne: false, 32 | dropdownOpenTwo: false, 33 | dropdownOpenThree: false, 34 | dropdownOpenFour: false, 35 | cSelectedOne: [2], 36 | cSelectedTwo: [1, 3], 37 | rSelectedTwo: 2, 38 | rSelectedOne: null, 39 | }; 40 | } 41 | 42 | onRadioBtnClickOne(rSelectedOne) { 43 | this.setState({ rSelectedOne }); 44 | } 45 | 46 | onRadioBtnClickTwo(rSelectedTwo) { 47 | this.setState({ rSelectedTwo }); 48 | } 49 | 50 | onCheckboxBtnClickOne(selected) { 51 | const index = this.state.cSelectedOne.indexOf(selected); 52 | if (index < 0) { 53 | this.state.cSelectedOne.push(selected); 54 | } else { 55 | this.state.cSelectedOne.splice(index, 1); 56 | } 57 | this.setState({ cSelectedOne: [...this.state.cSelectedOne] }); 58 | } 59 | 60 | onCheckboxBtnClickTwo(selected) { 61 | const index = this.state.cSelectedTwo.indexOf(selected); 62 | if (index < 0) { 63 | this.state.cSelectedTwo.push(selected); 64 | } else { 65 | this.state.cSelectedTwo.splice(index, 1); 66 | } 67 | this.setState({ cSelectedTwo: [...this.state.cSelectedTwo] }); 68 | } 69 | 70 | toggleOne() { 71 | this.setState({ 72 | dropdownOpenOne: !this.state.dropdownOpenOne, 73 | }); 74 | } 75 | 76 | toggleTwo() { 77 | this.setState({ 78 | dropdownOpenTwo: !this.state.dropdownOpenTwo, 79 | }); 80 | } 81 | 82 | toggleThree() { 83 | this.setState({ 84 | dropdownOpenThree: !this.state.dropdownOpenThree, 85 | }); 86 | } 87 | 88 | toggleFour() { 89 | this.setState({ 90 | dropdownOpenFour: !this.state.dropdownOpenFour, 91 | }); 92 | } 93 | 94 | render() { 95 | return ( 96 |
    97 |
      98 |
    1. YOU ARE HERE
    2. 99 |
    3. UI Buttons
    4. 100 |
    101 | 102 |

    Buttons - Styles

    103 | 104 | 105 | {/* Color options */} 106 | 107 | Color Options 109 | } close collapse 110 | > 111 |
    112 |

    113 | Use any of the available button classes to quickly create a styled button. 114 | Semantically distinguishable beauty. 115 |

    116 |

    117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |

    126 |
    127 |
    128 | 129 | 130 | {/* Size variants */} 131 | 132 | Size Variants 134 | } close collapse 135 | > 136 |
    137 |

    138 | Fancy larger or smaller buttons? 139 | Four separate sizes available for all use cases: 140 | from tiny 10px button to large one. 141 |

    142 |

    143 | 144 | 145 | 146 | 147 |

    148 |
    149 |
    150 | 151 | 152 | 153 | Outline Buttons 155 | } close collapse 156 | > 157 |
    158 |

    159 | In need of a button, but not the hefty background colors they bring? 160 | Use outline property to remove all 161 | background images and colors on any button. 162 |

    163 |

    164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 |

    173 |
    174 |
    175 | 176 | 177 | 178 | Rounded Buttons 180 | } close collapse 181 | > 182 |
    183 |

    184 | Use any of the available button properties to quickly create a styled button. 185 | Semantically distinguishable beauty. Use .btn-rounded or .btn-rounded-f. 186 |

    187 |

    188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 |

    197 |
    198 |
    199 | 200 | 201 | {/* Block Buttons */} 202 | 203 | Block Buttons 205 | } close collapse 206 | > 207 |
    208 |

    209 | Create block level buttons - those that span the full width 210 | of a parent— by adding block 211 | to <Button> component. 212 | Great for menu & social buttons. 213 |

    214 | 215 | 218 | 220 | 221 |
    222 |
    223 | 224 | 225 | {/* Disabled Buttons */} 226 | 227 | Disabled Buttons 229 | } close collapse 230 | > 231 |
    232 |

    233 | Make buttons look unclickable by fading them back 50%. 234 | Add the disabled to <Button> component. 235 |

    236 |

    237 | 238 | 239 |

    240 |

    241 | 242 | 243 |

    244 |
    245 |
    246 | 247 | 248 | {/* Buttons Groups */} 249 | 250 | Button Groups 252 | } close collapse 253 | > 254 |
    255 |

    256 | Group a series of buttons together on a single 257 | line with the button group. 258 | Add on optional JavaScript radio and checkbox 259 | style behavior with Bootstrap buttons plugin. 260 |

    261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 |
    285 |
    286 | 287 | 288 | {/* Button Dropdowns */} 289 | {/* todo: check after reactstrap update */} 290 | 291 | Button Dropdowns 293 | } close collapse 294 | > 295 |
    296 |

    297 | Add dropdown menus to nearly anything with 298 | this simple plugin, including the buttons, 299 | navbar, tabs, and pills. 300 | Both solid & segmented dropdown options available. 301 |

    302 | 303 |
    304 | 308 | 309 |   One   310 | 311 | 312 | Action 313 | Another action 314 | Something else here 315 | 316 | Separated link 317 | 318 | 319 | 320 | 321 | 322 |   One   323 | 324 | 325 | Action 326 | Another action 327 | Something else here 328 | 329 | Separated link 330 | 331 | 332 |
    333 |
    334 | 338 | 339 | 340 | 341 | Action 342 | Another action 343 | Something else here 344 | 345 | Separated link 346 | 347 | 348 | 349 | 350 | 351 | 352 | Action 353 | Another action 354 | Something else here 355 | 356 | Separated link 357 | 358 | 359 |
    360 |
    361 |
    362 | 363 |
    364 | 365 | 366 | 367 | Button Options 369 | } close collapse 370 | > 371 | 372 | {/* Checkboxes */} 373 | 374 |

    Button Checkboxes

    375 |

    376 | Do more with buttons. Control button states 377 | or create groups of buttons for more components like 378 | toolbars. 379 | Use ButtonGroup to a group 380 | of checkboxes for checkbox style toggling on 381 | btn-group. 382 |

    383 |
    384 | 385 | 389 | 393 | 397 | 398 |
    399 |
    400 | 401 | 405 | 409 | 413 | 414 |
    415 | 416 | 417 | 418 | {/* Radios */} 419 | 420 |

    Button Radios

    421 |

    422 | Do more with buttons. Control button states 423 | or create groups of buttons for more components like toolbars. 424 | Use ButtonGroup to a group of radio 425 | inputs for radio style toggling on btn-group. 426 |

    427 |
    428 | 429 | 433 | 437 | 441 | 442 |
    443 |
    444 | 445 | 449 | 453 | 457 | 458 |
    459 | 460 | 461 | {/* Buttons with Icons */} 462 | 463 |

    Use with Icons

    464 |

    465 | Fontawesome and Glyph- icons may be used in buttons, 466 | button groups for a toolbar, navigation, or prepended form inputs. 467 | Let your buttons shine! 468 |

    469 |
    470 | 474 | 478 | 482 |
    483 |
    484 | 488 | 492 | 498 |
    499 | 500 |
    501 |
    502 | 503 |
    504 | 505 |
    506 | ); 507 | } 508 | 509 | } 510 | 511 | export default Buttons; 512 | -------------------------------------------------------------------------------- /src/pages/buttons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Buttons", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Buttons.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/charts/Charts.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { Row, Col, Breadcrumb, BreadcrumbItem } from 'reactstrap'; 3 | 4 | import Widget from '../../components/Widget'; 5 | 6 | // Charts 7 | import LineChart from './charts/LineChart'; 8 | import BarChart from './charts/BarChart'; 9 | import PercentAreaChart from './charts/PercentAreaChart'; 10 | import PieChart from './charts/PieChart'; 11 | 12 | const data = [ 13 | {name: 'Page A', uv: 4000, pv: 2400, amt: 2400}, 14 | {name: 'Page B', uv: 3000, pv: 1398, amt: 2210}, 15 | {name: 'Page C', uv: 2000, pv: 9800, amt: 2290}, 16 | {name: 'Page D', uv: 2780, pv: 3908, amt: 2000}, 17 | {name: 'Page E', uv: 1890, pv: 4800, amt: 2181}, 18 | {name: 'Page F', uv: 2390, pv: 3800, amt: 2500}, 19 | {name: 'Page G', uv: 3490, pv: 4300, amt: 2100}, 20 | ]; 21 | 22 | export default class Charts extends PureComponent { 23 | render() { 24 | return( 25 |
    26 | 27 | YOU ARE HERE 28 | Charts 29 | 30 |

    Pretty Charts

    31 | 32 | 33 | Simple Line Chart}> 35 | 36 | 37 | 38 | 39 | Simple Bar Chart}> 41 | 42 | 43 | 44 | 45 | Percent Area Chart}> 47 | 48 | 49 | 50 | 51 | Pie Chart}> 53 | 54 | 55 | 56 | 57 |
    58 | ) 59 | } 60 | } -------------------------------------------------------------------------------- /src/pages/charts/charts/BarChart.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | BarChart, 5 | Bar, 6 | XAxis, 7 | YAxis, 8 | CartesianGrid, 9 | Tooltip, 10 | Legend, 11 | ResponsiveContainer 12 | } from 'recharts'; 13 | 14 | class SimpleBarChart extends PureComponent { 15 | static propTypes = { 16 | data: PropTypes.arrayOf(PropTypes.shape({ 17 | name: PropTypes.string, 18 | uv: PropTypes.number, 19 | pv: PropTypes.number, 20 | })).isRequired, 21 | } 22 | 23 | render () { 24 | return ( 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ); 42 | } 43 | } 44 | 45 | export default SimpleBarChart; 46 | -------------------------------------------------------------------------------- /src/pages/charts/charts/LineChart.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | LineChart, 5 | Line, 6 | XAxis, 7 | YAxis, 8 | CartesianGrid, 9 | Tooltip, 10 | Legend, 11 | ResponsiveContainer 12 | } from 'recharts'; 13 | 14 | class SimpleLineChart extends PureComponent { 15 | static propTypes = { 16 | data: PropTypes.arrayOf(PropTypes.shape({ 17 | name: PropTypes.string, 18 | uv: PropTypes.number, 19 | pv: PropTypes.number, 20 | })).isRequired, 21 | } 22 | 23 | render () { 24 | return ( 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ); 40 | } 41 | } 42 | 43 | export default SimpleLineChart; 44 | -------------------------------------------------------------------------------- /src/pages/charts/charts/PercentAreaChart.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | AreaChart, 5 | Area, 6 | XAxis, 7 | YAxis, 8 | Tooltip, 9 | ResponsiveContainer 10 | } from 'recharts'; 11 | 12 | class PercentAreaChart extends PureComponent { 13 | static propTypes = { 14 | data: PropTypes.arrayOf(PropTypes.shape({ 15 | name: PropTypes.string, 16 | uv: PropTypes.number, 17 | pv: PropTypes.number, 18 | amt: PropTypes.number, 19 | })).isRequired, 20 | } 21 | 22 | getPercent = (value, total) => { 23 | const ratio = total > 0 ? value / total : 0; 24 | 25 | return this.toPercent(ratio, 2); 26 | }; 27 | 28 | toPercent = (decimal, fixed = 0) => `${(decimal * 100).toFixed(fixed)}%`; 29 | 30 | renderTooltipContent = o => { // eslint-disable-line 31 | const { payload, label } = o; 32 | const total = payload.reduce((result, entry) => (result + entry.value), 0); 33 | 34 | return ( 35 |
    36 |

    {`${label} (Total: ${total})`}

    37 |
      38 | { 39 | payload.map((entry, index) => ( 40 | /* eslint-disable */ 41 |
    • 42 | {`${entry.name}: ${entry.value}(${this.getPercent(entry.value, total)})`} 43 |
    • 44 | /* eslint-enable */ 45 | )) 46 | } 47 |
    48 |
    49 | ); 50 | }; 51 | 52 | render () { 53 | return ( 54 | 55 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | ); 68 | } 69 | } 70 | 71 | export default PercentAreaChart; 72 | -------------------------------------------------------------------------------- /src/pages/charts/charts/PieChart.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import { PieChart, Pie, Cell, ResponsiveContainer } from 'recharts'; 3 | 4 | const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042']; 5 | const RADIAN = Math.PI / 180; 6 | const data = [ 7 | {name: 'Group A', value: 400}, {name: 'Group B', value: 300}, 8 | {name: 'Group C', value: 300}, {name: 'Group D', value: 200} 9 | ]; 10 | 11 | class SimplePieChart extends PureComponent { 12 | renderCustomizedLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, percent }) => { 13 | const radius = innerRadius + (outerRadius - innerRadius) * 0.5; 14 | const x = cx + radius * Math.cos(-midAngle * RADIAN); 15 | const y = cy + radius * Math.sin(-midAngle * RADIAN); 16 | 17 | return ( 18 | cx ? 'start' : 'end'} dominantBaseline="central"> 19 | {`${(percent * 100).toFixed(0)}%`} 20 | 21 | ); 22 | } 23 | 24 | render () { 25 | return ( 26 | 27 | 28 | 38 | { 39 | data.map((entry, index) => ) 40 | } 41 | 42 | 43 | 44 | ); 45 | } 46 | } 47 | 48 | export default SimplePieChart; 49 | -------------------------------------------------------------------------------- /src/pages/charts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Charts", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Charts.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/dashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import cx from 'classnames'; 3 | import PropTypes from 'prop-types'; 4 | import { Link } from 'react-router-dom'; 5 | import { connect } from 'react-redux'; 6 | import { 7 | Row, 8 | Col, 9 | Alert, 10 | Button, 11 | ButtonGroup, 12 | Breadcrumb, 13 | BreadcrumbItem, 14 | Progress, 15 | Badge, 16 | ListGroup, 17 | ButtonDropdown, 18 | DropdownMenu, 19 | DropdownToggle, 20 | DropdownItem, 21 | Table 22 | } from 'reactstrap'; 23 | import { mock } from './mock' 24 | 25 | import Widget from '../../components/Widget'; 26 | 27 | import { fetchPosts } from '../../actions/posts'; 28 | import s from './Dashboard.module.scss'; 29 | 30 | class Dashboard extends Component { 31 | /* eslint-disable */ 32 | static propTypes = { 33 | posts: PropTypes.any, 34 | isFetching: PropTypes.bool, 35 | dispatch: PropTypes.func.isRequired, 36 | }; 37 | /* eslint-enable */ 38 | 39 | static defaultProps = { 40 | posts: [], 41 | isFetching: false, 42 | }; 43 | 44 | state = { 45 | isDropdownOpened: false 46 | }; 47 | 48 | componentDidMount() { 49 | if(process.env.NODE_ENV === "development") { 50 | this.props.dispatch(fetchPosts()); 51 | } 52 | } 53 | 54 | formatDate = (str) => { 55 | return str.replace(/,.*$/,""); 56 | } 57 | 58 | toggleDropdown = () => { 59 | this.setState(prevState => ({ 60 | isDropdownOpened: !prevState.isDropdownOpened, 61 | })); 62 | } 63 | 64 | render() { 65 | return ( 66 |
    67 | 68 | YOU ARE HERE 69 | Dashboard 70 | 71 |

    Dashboard

    72 | 73 | 74 | 77 |
    78 | 83 |
    84 |
    85 | {' '} 86 | Users 87 |
    88 |
    89 | } 90 | > 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 108 | 109 | 110 | 111 | 112 | 113 | 116 | 117 | 118 | 119 | 120 | 121 | 124 | 125 | 126 | 127 | 128 | 129 | 132 | 133 | 134 |
    IDUsernameEmailStatus
    1Alicealice@email.com 106 | active 107 |
    2Bobbob@email.com 114 | delayed 115 |
    3Duckduck@email.com 122 | active 123 |
    4Shepherdshepherd@email.com 130 | removed 131 |
    135 | 136 | 137 | 138 | 139 | 143 | Warning: Best check yo 144 | self, you're not looking too good. 145 | 146 | 150 | Success: You successfully 151 | read this important alert message. 152 | 153 | 157 | Info: This alert needs 158 | your attention, but it's not super important. 159 | 160 | 164 | Danger: Change this and 165 | that and try again. 166 | 167 | 170 | or 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 182 |
    183 | Options 184 |
    185 |
    186 | Recent posts{' '} 187 | 188 | 5 189 | 190 |
    191 |

    192 | posts, that have been published recently 193 |

    194 | 195 | } 196 | > 197 | 198 | 199 | {this.props.posts && 200 | this.props.posts.map(post => ( 201 | 202 | 203 | 206 | 207 | ))} 208 | {this.props.posts && 209 | !this.props.posts.length && ( 210 | mock.map(post => ( 211 | 212 | 213 | 216 | 217 | )) 218 | )} 219 | {this.props.isFetching && ( 220 | 221 | 222 | 223 | )} 224 | 225 |
    {this.formatDate(new Date(post.updatedAt).toLocaleString())} 204 | {post.title} 205 |
    {post.updatedAt} 214 | {post.title} 215 |
    Loading...
    226 |
    227 | 228 | View all Posts 13 229 | 230 |
    231 |
    232 | 233 | 234 | 235 | 236 | {' '} 237 | Incoming calls 3 238 | 239 | 240 | {' '} 241 | Notifications 6 242 | 243 | 244 | {' '} 245 | Messages 18 246 | 247 | 248 | {' '} 249 | Visits total 250 | 251 | 252 | Inbox{' '} 253 | 254 | 255 | 256 |
    257 | 258 | 259 | 260 |
    261 | 264 | 267 | 270 | 273 | 276 |
    277 | 278 | 279 | 280 | 281 | 282 | Dropdown 283 | 284 | 285 | 1 286 | 2 287 | 288 | 289 | 290 |

    291 | For more components please checkout{' '} 292 | 297 | reactstrap documentation 298 | 299 |

    300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 |
    308 |
    309 | 310 | ); 311 | } 312 | } 313 | 314 | function mapStateToProps(state) { 315 | return { 316 | isFetching: state.posts.isFetching, 317 | posts: state.posts.posts, 318 | }; 319 | } 320 | 321 | export default connect(mapStateToProps)(Dashboard); 322 | -------------------------------------------------------------------------------- /src/pages/dashboard/Dashboard.module.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../styles/app'; 11 | 12 | .root { 13 | // styles here 14 | } 15 | 16 | .usersTable { 17 | th, 18 | td { 19 | border-color: $white; 20 | } 21 | 22 | thead { 23 | border-bottom: 1px solid $gray-200; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/pages/dashboard/mock.js: -------------------------------------------------------------------------------- 1 | export const mock = [ 2 | { 3 | id: 123325, 4 | updatedAt: '2019-11-14', 5 | title: ' React Native Starter - Mobile Template' 6 | }, 7 | { 8 | id: 56785, 9 | updatedAt: '2019-12-14', 10 | title: 'Light Blue React Node.js - update version 7.0.1' 11 | }, 12 | { 13 | id: 943325, 14 | updatedAt: '2019-15-14', 15 | title: 'Sing App React Node.js - update version 7.0.1' 16 | }, 17 | { 18 | id: 84767, 19 | updatedAt: '2019-10-14', 20 | title: 'Sing App Vue Node.js - update version 5.0.3' 21 | }, 22 | { 23 | id: 889412, 24 | updatedAt: '2019-11-14', 25 | title: 'Light Blue Vue Node.js - update version 3.0.5' 26 | }, 27 | ] -------------------------------------------------------------------------------- /src/pages/dashboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dashboard", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Dashboard.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/error/ErrorPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | 5 | class ErrorPage extends React.Component { 6 | static propTypes = { 7 | error: PropTypes.shape({ 8 | name: PropTypes.string.isRequired, 9 | message: PropTypes.string.isRequired, 10 | stack: PropTypes.string.isRequired, 11 | }).isRequired, 12 | }; 13 | 14 | render() { 15 | return ( 16 |
    17 |

    Error

    18 |

    Sorry, a critical error occurred on this page.

    19 |
    20 | ); 21 | } 22 | } 23 | 24 | export default ErrorPage; 25 | -------------------------------------------------------------------------------- /src/pages/error/ErrorPage.scss: -------------------------------------------------------------------------------- 1 | * { 2 | line-height: 1.2; 3 | margin: 0; 4 | } 5 | 6 | html { 7 | color: #888; 8 | display: table; 9 | font-family: sans-serif; 10 | height: 100%; 11 | text-align: center; 12 | width: 100%; 13 | } 14 | 15 | body { 16 | display: table-cell; 17 | vertical-align: middle; 18 | padding: 2em; 19 | } 20 | 21 | h1 { 22 | color: #555; 23 | font-size: 2em; 24 | font-weight: 400; 25 | } 26 | 27 | p { 28 | margin: 0 auto; 29 | width: 280px; 30 | } 31 | 32 | pre { 33 | text-align: left; 34 | margin-top: 2rem; 35 | } 36 | 37 | @media only screen and (max-width: 280px) { 38 | body, 39 | p { 40 | width: 95%; 41 | } 42 | 43 | h1 { 44 | font-size: 1.5em; 45 | margin: 0 0 0.3em; 46 | } 47 | } -------------------------------------------------------------------------------- /src/pages/error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "error", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./ErrorPage.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/google/Google.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | withGoogleMap, 4 | withScriptjs, 5 | GoogleMap, 6 | Marker, 7 | } from 'react-google-maps'; 8 | 9 | import s from './Google.module.scss'; 10 | 11 | const BasicMap = withScriptjs(withGoogleMap(() => 12 | 16 | 17 | , 18 | )); 19 | 20 | class Maps extends React.Component { 21 | 22 | render() { 23 | return ( 24 |
    25 |
    26 | } 29 | containerElement={
    } 30 | mapElement={
    } 31 | /> 32 |
    33 |
    34 | ); 35 | } 36 | } 37 | 38 | export default Maps; 39 | -------------------------------------------------------------------------------- /src/pages/google/Google.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/app'; 2 | 3 | .MapContainer { 4 | position: absolute; 5 | top: 0; 6 | left: 0; 7 | bottom: 0; 8 | right: 0; 9 | height: 100%; 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/google/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Google", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Google.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/icons/Icons.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/app'; 2 | 3 | .root { 4 | :global { 5 | .icon-list { 6 | margin-top: 1rem; 7 | } 8 | 9 | .icon-list-item { 10 | height: 32px; 11 | font-size: 14px; 12 | line-height: 32px; 13 | 14 | > a { 15 | color: $text-color; 16 | text-decoration: none; 17 | } 18 | 19 | .bi, 20 | .fa, 21 | .fi { 22 | width: 32px; 23 | margin-right: 10px; 24 | } 25 | 26 | &:hover { 27 | .bi, 28 | .fa, 29 | .fi { 30 | font-size: 28px; 31 | top: 2px; 32 | } 33 | 34 | .fa, 35 | .fi { 36 | vertical-align: -5px; 37 | } 38 | 39 | .bi { 40 | vertical-align: -6px; 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/pages/icons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Icons", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Icons.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/login/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { withRouter, Redirect } from 'react-router-dom'; 4 | import { connect } from 'react-redux'; 5 | import { 6 | Alert, 7 | Button, 8 | FormGroup, 9 | Input, 10 | Row, 11 | Col, 12 | Form 13 | } from 'reactstrap'; 14 | import s from './Login.module.scss'; 15 | import Widget from '../../components/Widget'; 16 | import Footer from "../../components/Footer"; 17 | import { loginUser } from '../../actions/user'; 18 | import jwt from 'jsonwebtoken'; 19 | import config from '../../config' 20 | 21 | class Login extends React.Component { 22 | static propTypes = { 23 | dispatch: PropTypes.func.isRequired, 24 | isAuthenticated: PropTypes.bool, 25 | isFetching: PropTypes.bool, 26 | location: PropTypes.any, // eslint-disable-line 27 | errorMessage: PropTypes.string, 28 | }; 29 | 30 | static defaultProps = { 31 | isAuthenticated: false, 32 | isFetching: false, 33 | location: {}, 34 | errorMessage: null, 35 | }; 36 | 37 | static isAuthenticated(token) { 38 | // We check if app runs with backend mode 39 | if (!config.isBackend && token) return true; 40 | if (!token) return; 41 | const date = new Date().getTime() / 1000; 42 | const data = jwt.decode(token); 43 | return date < data.exp; 44 | } 45 | 46 | constructor(props) { 47 | super(props); 48 | 49 | this.state = { 50 | login: 'user', 51 | password: 'password', 52 | }; 53 | } 54 | 55 | changeLogin = (event) => { 56 | this.setState({login: event.target.value}); 57 | } 58 | 59 | changePassword = (event) => { 60 | this.setState({password: event.target.value}); 61 | } 62 | 63 | doLogin = (e) => { 64 | this.props.dispatch( 65 | loginUser({ 66 | login: this.state.login, 67 | password: this.state.password, 68 | }), 69 | ); 70 | e.preventDefault(); 71 | } 72 | 73 | render() { 74 | const {from} = this.props.location.state || { 75 | from: {pathname: '/app'}, 76 | }; 77 | 78 | if (this.props.isAuthenticated) { 79 | // cant access login page while logged in 80 | return ; 81 | } 82 | 83 | return ( 84 |
    85 | 86 | 87 |

    React Dashboard

    88 | 89 |

    Login to your Web App

    90 |

    91 | User your username and password to sign in
    92 | Don't have an account? Sign up now! 93 |

    94 |
    95 | {this.props.errorMessage && ( 96 | 97 | {this.props.errorMessage} 98 | 99 | )} 100 | 101 | 110 | 111 | 112 | 121 | 122 |
    123 | Trouble with account? {/* eslint-disable-line */} 124 |
    125 | 128 | 131 |
    132 |
    133 |
    134 |
    135 | 136 |
    137 |
    138 |
    139 | ); 140 | } 141 | } 142 | 143 | function mapStateToProps(state) { 144 | return { 145 | isFetching: state.auth.isFetching, 146 | isAuthenticated: state.auth.isAuthenticated, 147 | errorMessage: state.auth.errorMessage, 148 | }; 149 | } 150 | 151 | export default withRouter(connect(mapStateToProps)(Login)); 152 | 153 | -------------------------------------------------------------------------------- /src/pages/login/Login.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/app'; 2 | 3 | .root { 4 | padding-top: 25vh; 5 | } 6 | 7 | .widget { 8 | max-width: 360px; 9 | margin: 0 auto; 10 | padding: 30px !important; 11 | 12 | :global .form-control { 13 | font-size: 14px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/pages/login/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "login", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Login.js" 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/notFound/NotFound.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | 12 | import s from './NotFound.module.scss'; 13 | 14 | class NotFound extends React.Component { 15 | render() { 16 | return ( 17 |
    18 |

    404

    19 |

    Sorry, the page you were trying to view does not exist.

    20 |
    21 | ); 22 | } 23 | } 24 | 25 | export default NotFound -------------------------------------------------------------------------------- /src/pages/notFound/NotFound.module.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Flatlogic Dashboards (https://flatlogic.com/admin-dashboards) 3 | * 4 | * Copyright © 2015-present Flatlogic, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../styles/app'; 11 | 12 | .root { 13 | padding-top: 20vh; 14 | text-align: center; 15 | } 16 | 17 | .title { 18 | font-size: 80px; 19 | } 20 | -------------------------------------------------------------------------------- /src/pages/notFound/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Not Found", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./NotFound.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/notifications/Notifications.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Row, Col, Button, 4 | } from 'reactstrap'; 5 | 6 | import { toast } from 'react-toastify'; 7 | import 'react-toastify/dist/ReactToastify.css'; 8 | import uuid from 'uuid/v4' 9 | import Widget from '../../components/Widget'; 10 | import s from './Notifications.module.scss'; 11 | 12 | class Notifications extends React.Component { 13 | 14 | state = { 15 | options: { 16 | position: "top-right", 17 | autoClose: 5000, 18 | closeOnClick: false, 19 | pauseOnHover: false, 20 | draggable: true 21 | } 22 | } 23 | 24 | componentDidMount() { 25 | toast.success('Thanks for checking out Messenger!', { 26 | position: "bottom-right", 27 | autoClose: 5000, 28 | closeOnClick: true, 29 | pauseOnHover: false, 30 | draggable: true 31 | }); 32 | } 33 | 34 | addSuccessNotification = () => toast.success('Showing success message was successful!', this.state.options); 35 | 36 | toggleLocation = (location) => { 37 | this.setState(prevState => ({ 38 | options: { 39 | ...prevState.options, 40 | position: location 41 | } 42 | })); 43 | } 44 | 45 | addInfoNotification = () => { 46 | let id = uuid(); 47 | toast.info( 48 |
    49 | Launching thermonuclear war... 50 | 51 |
    , 52 | {...this.state.options,toastId: id}, 53 | ); 54 | } 55 | 56 | launchNotification = (id) => toast.update(id, { ...this.state.options, render: "Thermonuclear war averted", type: toast.TYPE.SUCCESS }); 57 | 58 | addErrorNotification = () => { 59 | let id = uuid(); 60 | toast.error( 61 |
    62 | Error destroying alien planet 63 | 64 |
    , 65 | {...this.state.options,toastId: id} 66 | ); 67 | } 68 | 69 | retryNotification = (id) => toast.update(id, {...this.state.options, render: 'Alien planet destroyed!', type: toast.TYPE.SUCCESS }); 70 | 71 | render() { 72 | return ( 73 |
    74 |
      75 |
    1. YOU ARE HERE
    2. 76 |
    3. UI Notifications
    4. 77 |
    78 |

    Messages - Notifications 79 |

    80 | 81 | Messenger } close collapse settings> 82 | 83 | 84 |
    Layout options
    85 |

    There are few position options available for notifications. You can click any of 86 | them 87 | to change notifications position:

    88 |
    89 |
    { 91 | this.toggleLocation('top-left'); 92 | }} 93 | /> 94 |
    { 96 | this.toggleLocation('top-right'); 97 | }} 98 | /> 99 |
    { 101 | this.toggleLocation('top-center'); 102 | }} 103 | /> 104 |
    { 106 | this.toggleLocation('bottom-left'); 107 | }} 108 | /> 109 |
    { 111 | this.toggleLocation('bottom-right'); 112 | }} 113 | /> 114 |
    { 116 | this.toggleLocation('bottom-center'); 117 | }} 118 | /> 119 |
    120 | 121 | 122 | 123 |
    Notification Types
    124 |

    Different types of notifications for lost of use cases. Custom classes are also 125 | supported.

    126 |

    128 |

    130 |

    134 | 135 | 136 | 137 |
    Dead Simple Usage
    138 |

    Just few lines of code to instantiate a notifications object. Does not require 139 | passing any options:

    140 |
    {'toast("Thanks for checking out Messenger!");'}
    141 |

    More complex example:

    142 |
    143 |                 {'\ntoast.success( \'There was an explosion while processing your request.\', { \n position: location,\n autoClose: 5000, \n hideProgressBar: false, \n closeOnClick: true,\n pauseOnHover: true, \n draggable: true \n});\n\n'}
    144 |                 
    145 |               
    146 | 147 | 148 | 149 |
    150 | ); 151 | } 152 | } 153 | 154 | export default Notifications; 155 | -------------------------------------------------------------------------------- /src/pages/notifications/Notifications.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../styles/app'; 2 | 3 | .root { 4 | :global { 5 | .location-selector { 6 | width: 100%; 7 | height: 220px; 8 | border: 1px dashed #bbb; 9 | background-color: $white; 10 | position: relative; 11 | } 12 | 13 | .location-selector .bit { 14 | @include transition(background-color 0.15s ease-in-out); 15 | 16 | background-color: $gray-300; 17 | cursor: pointer; 18 | position: absolute; 19 | } 20 | 21 | .location-selector .bit:hover { 22 | background-color: $gray-400; 23 | } 24 | 25 | .location-selector .bit.top, 26 | .location-selector .bit.bottom { 27 | height: 20%; 28 | width: 20%; 29 | margin: 0 40%; 30 | } 31 | 32 | .location-selector .bit.top { 33 | top: 0; 34 | } 35 | 36 | .location-selector .bit.bottom { 37 | bottom: 0; 38 | } 39 | 40 | .location-selector .bit.right, 41 | .location-selector .bit.left { 42 | height: 20%; 43 | width: 20%; 44 | margin-left: 0; 45 | margin-right: 0; 46 | } 47 | 48 | .location-selector .bit.right { 49 | right: 0; 50 | } 51 | 52 | .location-selector .bit.left { 53 | left: 0; 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/pages/notifications/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Notifications", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Notifications.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/pages/posts/Posts.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Switch, Route, withRouter } from 'react-router'; 3 | 4 | import PostList from './list/PostList'; 5 | import PostNew from './new/PostNew'; 6 | 7 | class Posts extends React.Component { 8 | render() { 9 | return ( 10 | 11 | 12 | 13 | 14 | ); 15 | } 16 | } 17 | 18 | export default withRouter(Posts); 19 | -------------------------------------------------------------------------------- /src/pages/posts/list/PostList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Link } from 'react-router-dom'; 4 | import { connect } from 'react-redux'; 5 | import { 6 | Table, 7 | Breadcrumb, 8 | BreadcrumbItem, 9 | } from 'reactstrap'; 10 | import { mock } from './mock' 11 | 12 | import s from './PostList.module.scss'; 13 | import Widget from '../../../components/Widget'; 14 | import { fetchPosts } from '../../../actions/posts'; 15 | 16 | class PostList extends React.Component { 17 | static propTypes = { 18 | dispatch: PropTypes.func.isRequired, 19 | posts: PropTypes.array, // eslint-disable-line 20 | isFetching: PropTypes.bool, 21 | }; 22 | 23 | static defaultProps = { 24 | isFetching: false, 25 | posts: [], 26 | }; 27 | 28 | static meta = { 29 | title: 'Posts list', 30 | description: 'About description', 31 | }; 32 | 33 | componentDidMount() { 34 | if(process.env.NODE_ENV === "development") { 35 | this.props.dispatch(fetchPosts()); 36 | } 37 | } 38 | 39 | formatDate = (str) => { 40 | return str.replace(/,.*$/,"") 41 | } 42 | 43 | render() { 44 | return ( 45 |
    46 | 47 | YOU ARE HERE 48 | Posts 49 | 50 |

    Posts

    51 | 55 |
    56 | 57 | Create new 58 | 59 |
    60 |
    61 | Posts List 62 |
    63 |
    64 | } 65 | > 66 |
    67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {this.props.posts && 77 | this.props.posts.map(post => ( 78 | 79 | 80 | 81 | 82 | 83 | ))} 84 | {this.props.posts && 85 | !this.props.posts.length && ( 86 | mock.map(post => ( 87 | 88 | 89 | 90 | 91 | 92 | )) 93 | )} 94 | {this.props.isFetching && ( 95 | 96 | 97 | 98 | )} 99 | 100 |
    TitleContentLast Updated
    {post.title}{post.content.slice(0, 80)}...{this.formatDate(new Date(post.updatedAt).toLocaleString())}
    {post.title}{post.content.slice(0, 80)}...{post.updatedAt}
    Loading...
    101 |
    102 | 103 |
    104 | ); 105 | } 106 | } 107 | 108 | function mapStateToProps(state) { 109 | return { 110 | isFetching: state.posts.isFetching, 111 | posts: state.posts.posts, 112 | }; 113 | } 114 | 115 | export default connect(mapStateToProps)(PostList); 116 | -------------------------------------------------------------------------------- /src/pages/posts/list/PostList.module.scss: -------------------------------------------------------------------------------- 1 | @import '../../../styles/app'; 2 | 3 | .root { 4 | // styles 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/posts/list/mock.js: -------------------------------------------------------------------------------- 1 | export const mock = [ 2 | { 3 | id: 123325, 4 | updatedAt: '2019-11-14', 5 | title: ' React Native Starter - Mobile Template', 6 | content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.' 7 | }, 8 | { 9 | id: 56785, 10 | updatedAt: '2019-12-14', 11 | title: 'Light Blue React Node.js - update version 7.0.1', 12 | content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.' 13 | }, 14 | { 15 | id: 943325, 16 | updatedAt: '2019-15-14', 17 | title: 'Sing App React Node.js - update version 7.0.1', 18 | content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.' 19 | }, 20 | { 21 | id: 84767, 22 | updatedAt: '2019-10-14', 23 | title: 'Sing App Vue Node.js - update version 5.0.3', 24 | content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.' 25 | }, 26 | { 27 | id: 889412, 28 | updatedAt: '2019-11-14', 29 | title: 'Light Blue Vue Node.js - update version 3.0.5', 30 | content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.' 31 | }, 32 | ] -------------------------------------------------------------------------------- /src/pages/posts/new/PostNew.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | Row, 5 | Col, 6 | Form, 7 | FormGroup, 8 | Input, 9 | Button, 10 | ButtonGroup, 11 | Alert, 12 | Label, 13 | Breadcrumb, 14 | BreadcrumbItem, 15 | } from 'reactstrap'; 16 | import { connect } from 'react-redux'; 17 | 18 | import Widget from '../../../components/Widget'; 19 | 20 | import { createPost } from '../../../actions/posts'; 21 | import s from './PostNew.module.scss'; 22 | 23 | class PostNew extends React.Component { 24 | static propTypes = { 25 | dispatch: PropTypes.func.isRequired, 26 | message: PropTypes.string, 27 | isFetching: PropTypes.bool, 28 | }; 29 | 30 | static defaultProps = { 31 | isFetching: false, 32 | message: null, 33 | }; 34 | 35 | static meta = { 36 | title: 'Create new post', 37 | description: 'About description', 38 | }; 39 | 40 | constructor(props) { 41 | super(props); 42 | 43 | this.state = { 44 | title: '', 45 | content: '', 46 | }; 47 | } 48 | 49 | changeTitle = (event) => { 50 | this.setState({title: event.target.value}); 51 | } 52 | 53 | changeContent = (event) => { 54 | this.setState({content: event.target.value}); 55 | } 56 | 57 | doCreatePost = (e) => { 58 | this.props 59 | .dispatch( 60 | createPost({ 61 | title: this.state.title, 62 | content: this.state.content, 63 | }), 64 | ) 65 | .then(() => 66 | this.setState({ 67 | title: '', 68 | content: '', 69 | }), 70 | ); 71 | e.preventDefault(); 72 | } 73 | 74 | render() { 75 | return ( 76 |
    77 | 78 | YOU ARE HERE 79 | New Post 80 | 81 |

    Create new post

    82 | 83 | 84 | 87 | Add Post Form 88 | 89 | } 90 | > 91 |
    92 | {this.props.message && ( 93 | 94 | {this.props.message} 95 | 96 | )} 97 | 98 | 99 | 107 | 108 | 109 | 110 |