박선진

add front-end template code

going to fix it suitably for my project
Showing 436 changed files with 4816 additions and 0 deletions
1 +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 +
3 +# dependencies
4 +/node_modules
5 +
6 +# testing
7 +/coverage
8 +
9 +# production
10 +/build
11 +
12 +# misc
13 +.DS_Store
14 +.env.local
15 +.env.development.local
16 +.env.test.local
17 +.env.production.local
18 +
19 +npm-debug.log*
20 +yarn-debug.log*
21 +yarn-error.log*
22 +
23 +.idea/
This diff could not be displayed because it is too large.
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 +};
...\ No newline at end of file ...\ No newline at end of file
1 +'use strict';
2 +
3 +const fs = require('fs');
4 +const path = require('path');
5 +const paths = require('./paths');
6 +
7 +// Make sure that including paths.js after env.js will read .env variables.
8 +delete require.cache[require.resolve('./paths')];
9 +
10 +const NODE_ENV = process.env.NODE_ENV;
11 +if (!NODE_ENV) {
12 + throw new Error(
13 + 'The NODE_ENV environment variable is required but was not specified.'
14 + );
15 +}
16 +
17 +// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18 +var dotenvFiles = [
19 + `${paths.dotenv}.${NODE_ENV}.local`,
20 + `${paths.dotenv}.${NODE_ENV}`,
21 + // Don't include `.env.local` for `test` environment
22 + // since normally you expect tests to produce the same
23 + // results for everyone
24 + NODE_ENV !== 'test' && `${paths.dotenv}.local`,
25 + paths.dotenv,
26 +].filter(Boolean);
27 +
28 +// Load environment variables from .env* files. Suppress warnings using silent
29 +// if this file is missing. dotenv will never modify any environment variables
30 +// that have already been set. Variable expansion is supported in .env files.
31 +// https://github.com/motdotla/dotenv
32 +// https://github.com/motdotla/dotenv-expand
33 +dotenvFiles.forEach(dotenvFile => {
34 + if (fs.existsSync(dotenvFile)) {
35 + require('dotenv-expand')(
36 + require('dotenv').config({
37 + path: dotenvFile,
38 + })
39 + );
40 + }
41 +});
42 +
43 +// We support resolving modules according to `NODE_PATH`.
44 +// This lets you use absolute paths in imports inside large monorepos:
45 +// https://github.com/facebook/create-react-app/issues/253.
46 +// It works similar to `NODE_PATH` in Node itself:
47 +// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
48 +// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
49 +// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
50 +// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
51 +// We also resolve them to make sure all tools using them work consistently.
52 +const appDirectory = fs.realpathSync(process.cwd());
53 +process.env.NODE_PATH = (process.env.NODE_PATH || '')
54 + .split(path.delimiter)
55 + .filter(folder => folder && !path.isAbsolute(folder))
56 + .map(folder => path.resolve(appDirectory, folder))
57 + .join(path.delimiter);
58 +
59 +// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
60 +// injected into the application via DefinePlugin in Webpack configuration.
61 +const REACT_APP = /^REACT_APP_/i;
62 +
63 +function getClientEnvironment(publicUrl) {
64 + const raw = Object.keys(process.env)
65 + .filter(key => REACT_APP.test(key))
66 + .reduce(
67 + (env, key) => {
68 + env[key] = process.env[key];
69 + return env;
70 + },
71 + {
72 + // Useful for determining whether we’re running in production mode.
73 + // Most importantly, it switches React into the correct mode.
74 + NODE_ENV: process.env.NODE_ENV || 'development',
75 + // Useful for resolving the correct path to static assets in `public`.
76 + // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
77 + // This should only be used as an escape hatch. Normally you would put
78 + // images into the `src` and `import` them in code to get their paths.
79 + PUBLIC_URL: publicUrl,
80 + }
81 + );
82 + // Stringify all values so we can feed into Webpack DefinePlugin
83 + const stringified = {
84 + 'process.env': Object.keys(raw).reduce((env, key) => {
85 + env[key] = JSON.stringify(raw[key]);
86 + return env;
87 + }, {}),
88 + };
89 +
90 + return { raw, stringified };
91 +}
92 +
93 +module.exports = getClientEnvironment;
1 +'use strict';
2 +
3 +// This is a custom Jest transformer turning style imports into empty objects.
4 +// http://facebook.github.io/jest/docs/en/webpack.html
5 +
6 +module.exports = {
7 + process() {
8 + return 'module.exports = {};';
9 + },
10 + getCacheKey() {
11 + // The output is always the same.
12 + return 'cssTransform';
13 + },
14 +};
1 +'use strict';
2 +
3 +const path = require('path');
4 +
5 +// This is a custom Jest transformer turning file imports into filenames.
6 +// http://facebook.github.io/jest/docs/en/webpack.html
7 +
8 +module.exports = {
9 + process(src, filename) {
10 + const assetFilename = JSON.stringify(path.basename(filename));
11 +
12 + if (filename.match(/\.svg$/)) {
13 + return `module.exports = {
14 + __esModule: true,
15 + default: ${assetFilename},
16 + ReactComponent: (props) => ({
17 + $$typeof: Symbol.for('react.element'),
18 + type: 'svg',
19 + ref: null,
20 + key: null,
21 + props: Object.assign({}, props, {
22 + children: ${assetFilename}
23 + })
24 + }),
25 + };`;
26 + }
27 +
28 + return `module.exports = ${assetFilename};`;
29 + },
30 +};
1 +'use strict';
2 +
3 +const path = require('path');
4 +const fs = require('fs');
5 +const url = require('url');
6 +
7 +// Make sure any symlinks in the project folder are resolved:
8 +// https://github.com/facebook/create-react-app/issues/637
9 +const appDirectory = fs.realpathSync(process.cwd());
10 +const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 +
12 +const envPublicUrl = process.env.PUBLIC_URL;
13 +
14 +function ensureSlash(inputPath, needsSlash) {
15 + const hasSlash = inputPath.endsWith('/');
16 + if (hasSlash && !needsSlash) {
17 + return inputPath.substr(0, inputPath.length - 1);
18 + } else if (!hasSlash && needsSlash) {
19 + return `${inputPath}/`;
20 + } else {
21 + return inputPath;
22 + }
23 +}
24 +
25 +const getPublicUrl = appPackageJson =>
26 + envPublicUrl || require(appPackageJson).homepage;
27 +
28 +// We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 +// "public path" at which the app is served.
30 +// Webpack needs to know it to put the right <script> hrefs into HTML even in
31 +// single-page apps that may serve index.html for nested URLs like /todos/42.
32 +// We can't use a relative path in HTML because we don't want to load something
33 +// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
34 +function getServedPath(appPackageJson) {
35 + const publicUrl = getPublicUrl(appPackageJson);
36 + const servedUrl =
37 + envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
38 + return ensureSlash(servedUrl, true);
39 +}
40 +
41 +// config after eject: we're in ./config/
42 +module.exports = {
43 + dotenv: resolveApp('.env'),
44 + appPath: resolveApp('.'),
45 + appBuild: resolveApp('build'),
46 + appPublic: resolveApp('public'),
47 + appHtml: resolveApp('public/index.html'),
48 + appIndexJs: resolveApp('src/index.js'),
49 + appPackageJson: resolveApp('package.json'),
50 + appSrc: resolveApp('src'),
51 + yarnLockFile: resolveApp('yarn.lock'),
52 + testsSetup: resolveApp('src/setupTests.js'),
53 + proxySetup: resolveApp('src/setupProxy.js'),
54 + appNodeModules: resolveApp('node_modules'),
55 + publicUrl: getPublicUrl(resolveApp('package.json')),
56 + servedPath: getServedPath(resolveApp('package.json')),
57 +};
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
1 +'use strict';
2 +
3 +const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
4 +const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
5 +const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
6 +const ignoredFiles = require('react-dev-utils/ignoredFiles');
7 +const config = require('./webpack.config.dev');
8 +const paths = require('./paths');
9 +const fs = require('fs');
10 +
11 +const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
12 +const host = process.env.HOST || '0.0.0.0';
13 +
14 +module.exports = function(proxy, allowedHost) {
15 + return {
16 + // WebpackDevServer 2.4.3 introduced a security fix that prevents remote
17 + // websites from potentially accessing local content through DNS rebinding:
18 + // https://github.com/webpack/webpack-dev-server/issues/887
19 + // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
20 + // However, it made several existing use cases such as development in cloud
21 + // environment or subdomains in development significantly more complicated:
22 + // https://github.com/facebook/create-react-app/issues/2271
23 + // https://github.com/facebook/create-react-app/issues/2233
24 + // While we're investigating better solutions, for now we will take a
25 + // compromise. Since our WDS configuration only serves files in the `public`
26 + // folder we won't consider accessing them a vulnerability. However, if you
27 + // use the `proxy` feature, it gets more dangerous because it can expose
28 + // remote code execution vulnerabilities in backends like Django and Rails.
29 + // So we will disable the host check normally, but enable it if you have
30 + // specified the `proxy` setting. Finally, we let you override it if you
31 + // really know what you're doing with a special environment variable.
32 + disableHostCheck:
33 + !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
34 + // Enable gzip compression of generated files.
35 + compress: true,
36 + // Silence WebpackDevServer's own logs since they're generally not useful.
37 + // It will still show compile warnings and errors with this setting.
38 + clientLogLevel: 'none',
39 + // By default WebpackDevServer serves physical files from current directory
40 + // in addition to all the virtual build products that it serves from memory.
41 + // This is confusing because those files won’t automatically be available in
42 + // production build folder unless we copy them. However, copying the whole
43 + // project directory is dangerous because we may expose sensitive files.
44 + // Instead, we establish a convention that only files in `public` directory
45 + // get served. Our build script will copy `public` into the `build` folder.
46 + // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
47 + // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
48 + // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
49 + // Note that we only recommend to use `public` folder as an escape hatch
50 + // for files like `favicon.ico`, `manifest.json`, and libraries that are
51 + // for some reason broken when imported through Webpack. If you just want to
52 + // use an image, put it in `src` and `import` it from JavaScript instead.
53 + contentBase: paths.appPublic,
54 + // By default files from `contentBase` will not trigger a page reload.
55 + watchContentBase: true,
56 + // Enable hot reloading server. It will provide /sockjs-node/ endpoint
57 + // for the WebpackDevServer client so it can learn when the files were
58 + // updated. The WebpackDevServer client is included as an entry point
59 + // in the Webpack development configuration. Note that only changes
60 + // to CSS are currently hot reloaded. JS changes will refresh the browser.
61 + hot: true,
62 + // It is important to tell WebpackDevServer to use the same "root" path
63 + // as we specified in the config. In development, we always serve from /.
64 + publicPath: config.output.publicPath,
65 + // WebpackDevServer is noisy by default so we emit custom message instead
66 + // by listening to the compiler events with `compiler.hooks[...].tap` calls above.
67 + quiet: true,
68 + // Reportedly, this avoids CPU overload on some systems.
69 + // https://github.com/facebook/create-react-app/issues/293
70 + // src/node_modules is not ignored to support absolute imports
71 + // https://github.com/facebook/create-react-app/issues/1065
72 + watchOptions: {
73 + ignored: ignoredFiles(paths.appSrc),
74 + },
75 + // Enable HTTPS if the HTTPS environment variable is set to 'true'
76 + https: protocol === 'https',
77 + host,
78 + overlay: false,
79 + historyApiFallback: {
80 + // Paths with dots should still use the history fallback.
81 + // See https://github.com/facebook/create-react-app/issues/387.
82 + disableDotRule: true,
83 + },
84 + public: allowedHost,
85 + proxy,
86 + before(app, server) {
87 + if (fs.existsSync(paths.proxySetup)) {
88 + // This registers user provided middleware for proxy reasons
89 + require(paths.proxySetup)(app);
90 + }
91 +
92 + // This lets us fetch source contents from webpack for the error overlay
93 + app.use(evalSourceMapMiddleware(server));
94 + // This lets us open files from the runtime error overlay.
95 + app.use(errorOverlayMiddleware());
96 +
97 + // This service worker file is effectively a 'no-op' that will reset any
98 + // previous service worker registered for the same host:port combination.
99 + // We do this in development to avoid hitting the production cache if
100 + // it used the same host and port.
101 + // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
102 + app.use(noopServiceWorkerMiddleware());
103 + },
104 + };
105 +};
1 +{
2 + "name": "sing-app-react",
3 + "version": "5.0.0",
4 + "private": true,
5 + "scripts": {
6 + "build": "node scripts/build.js",
7 + "start": "node scripts/start.js",
8 + "test": "node scripts/test.js"
9 + },
10 + "browserslist": [
11 + ">0.2%",
12 + "not dead",
13 + "not ie <= 11",
14 + "not op_mini all"
15 + ],
16 + "eslintConfig": {
17 + "extends": "react-app"
18 + },
19 + "jest": {
20 + "collectCoverageFrom": [
21 + "src/**/*.{js,jsx}"
22 + ],
23 + "moduleFileExtensions": [
24 + "web.js",
25 + "js",
26 + "json",
27 + "web.jsx",
28 + "jsx",
29 + "node"
30 + ],
31 + "moduleNameMapper": {
32 + "^react-native$": "react-native-web",
33 + "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
34 + },
35 + "resolver": "jest-pnp-resolver",
36 + "setupFiles": [
37 + "react-app-polyfill/jsdom"
38 + ],
39 + "testEnvironment": "jsdom",
40 + "testMatch": [
41 + "<rootDir>/src/**/__tests__/**/*.{js,jsx}",
42 + "<rootDir>/src/**/?(*.)(spec|test).{js,jsx}"
43 + ],
44 + "testURL": "http://localhost",
45 + "transform": {
46 + "^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
47 + "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
48 + "^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
49 + },
50 + "transformIgnorePatterns": [
51 + "[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$",
52 + "^.+\\.module\\.(css|sass|scss)$"
53 + ]
54 + },
55 + "dependencies": {
56 + "animate.css": "3.7.0",
57 + "awesome-bootstrap-checkbox": "1.0.1",
58 + "axios": "^0.18.0",
59 + "bootstrap": "4.0.0",
60 + "bootstrap-slider": "^10.2.1",
61 + "bootstrap_calendar": "https://github.com/xero/bootstrap_calendar.git#1.0.1",
62 + "classnames": "^2.2.6",
63 + "draft-js": "^0.10.5",
64 + "easy-pie-chart": "^2.1.7",
65 + "flot": "^0.8.0-alpha",
66 + "flot.dashes": "https://github.com/cquartier/flot.dashes.git",
67 + "font-awesome": "4.7.0",
68 + "formsy-react": "0.19.5",
69 + "fullcalendar": "^3.9.0",
70 + "glyphicons-halflings": "^1.9.1",
71 + "govpredict-morris": "0.5.1",
72 + "jasny-bootstrap": "^3.1.3",
73 + "jquery": "^3.3.1",
74 + "jquery-mapael": "^2.2.0",
75 + "jquery-sparkline": "^2.4.0",
76 + "jquery-ui": "1.12.1",
77 + "jquery.animate-number": "0.0.14",
78 + "jquery.flot-orderBars": "https://github.com/emmerich/flot-orderBars.git",
79 + "jquery.flot.animator": "https://github.com/Codicode/flotanimator.git#3c256c0183d713fd3bf41d04417873928eb1a751",
80 + "jsonwebtoken": "^8.5.1",
81 + "jvectormap": "2.0.4",
82 + "line-awesome": "github:icons8/line-awesome",
83 + "messenger": "git+https://github.com/HubSpot/messenger.git#v1.4.2",
84 + "metrojs": "0.9.77",
85 + "moment": "^2.22.2",
86 + "nvd3": "1.8.6",
87 + "rc-color-picker": "^1.2.6",
88 + "rc-hammerjs": "0.6.9",
89 + "react": "^16.5.2",
90 + "react-animated-number": "^0.4.4",
91 + "react-app-polyfill": "^0.1.3",
92 + "react-autosize-textarea": "^5.0.0",
93 + "react-bootstrap-slider": "^2.1.5",
94 + "react-bootstrap-table": "4.1.5",
95 + "react-datetime": "^2.16.1",
96 + "react-dev-utils": "^6.0.5",
97 + "react-dom": "^16.5.2",
98 + "react-draft-wysiwyg": "1.10.12",
99 + "react-dropzone": "^6.2.4",
100 + "react-flot": "^1.3.0",
101 + "react-google-maps": "^9.4.5",
102 + "react-images": "^0.5.19",
103 + "react-maskedinput": "^4.0.1",
104 + "react-mde": "2.3.4",
105 + "react-redux": "^5.0.7",
106 + "react-router": "^4.3.1",
107 + "react-router-dom": "^4.3.1",
108 + "react-router-hash-link": "^1.2.1",
109 + "react-scrollspy": "^3.3.5",
110 + "react-select2-wrapper": "^1.0.4-beta6",
111 + "react-shuffle": "2.1.0",
112 + "react-slick": "^0.23.1",
113 + "react-sortable-hoc": "^0.8.3",
114 + "react-sortable-tree": "^2.2.0",
115 + "react-sparklines": "^1.7.0",
116 + "react-syntax-highlighter": "^10.1.2",
117 + "react-table": "6.7.6",
118 + "react-tagsinput": "^3.19.0",
119 + "react-toastify": "^5.1.1",
120 + "react-transition-group": "^2.5.2",
121 + "reactstrap": "7.1.0",
122 + "redux": "^4.0.1",
123 + "redux-thunk": "^2.3.0",
124 + "rickshaw": "1.6.6",
125 + "showdown": "1.8.6",
126 + "skycons": "^1.0.0",
127 + "widgster": "^1.0.0"
128 + },
129 + "devDependencies": {
130 + "@babel/core": "7.4.4",
131 + "@babel/plugin-proposal-class-properties": "7.4.4",
132 + "@babel/plugin-proposal-optional-chaining": "^7.2.0",
133 + "@svgr/webpack": "4.2.0",
134 + "babel-core": "7.0.0-bridge.0",
135 + "babel-eslint": "10.0.1",
136 + "babel-jest": "24.8.0",
137 + "babel-loader": "8.0.5",
138 + "babel-plugin-named-asset-import": "1.0.0-next.103",
139 + "babel-preset-react-app": "9.0.0",
140 + "bfj": "6.1.1",
141 + "bundle-loader": "0.5.6",
142 + "case-sensitive-paths-webpack-plugin": "2.2.0",
143 + "chalk": "2.4.2",
144 + "css-loader": "2.1.1",
145 + "dotenv": "8.0.0",
146 + "dotenv-expand": "5.1.0",
147 + "eslint": "5.16.0",
148 + "eslint-config-react-app": "4.0.1",
149 + "eslint-loader": "2.1.1",
150 + "eslint-plugin-flowtype": "3.8.1",
151 + "eslint-plugin-import": "2.17.2",
152 + "eslint-plugin-jsx-a11y": "6.2.1",
153 + "eslint-plugin-react": "7.13.0",
154 + "eslint-plugin-react-hooks": "1.6.0",
155 + "expose-loader": "0.7.5",
156 + "file-loader": "3.0.1",
157 + "fs-extra": "7.0.1",
158 + "html-webpack-plugin": "4.0.0-alpha.2",
159 + "identity-obj-proxy": "3.0.0",
160 + "imports-loader": "0.8.0",
161 + "jest": "24.8.0",
162 + "jest-pnp-resolver": "1.2.1",
163 + "jest-resolve": "24.8.0",
164 + "lodash": "4.17.11",
165 + "mini-css-extract-plugin": "0.6.0",
166 + "node-sass": "4.12.0",
167 + "optimize-css-assets-webpack-plugin": "5.0.1",
168 + "pnp-webpack-plugin": "1.4.3",
169 + "postcss-flexbugs-fixes": "4.1.0",
170 + "postcss-loader": "3.0.0",
171 + "postcss-preset-env": "6.6.0",
172 + "postcss-safe-parser": "4.0.1",
173 + "resolve": "1.10.1",
174 + "sass-loader": "7.1.0",
175 + "style-loader": "0.23.0",
176 + "terser-webpack-plugin": "1.2.3",
177 + "url-loader": "1.1.2",
178 + "webpack": "4.31.0",
179 + "webpack-dev-server": "3.3.1",
180 + "webpack-manifest-plugin": "2.0.4",
181 + "webpack-raphael": "2.1.4",
182 + "workbox-webpack-plugin": "4.3.1"
183 + },
184 + "resolutions": {
185 + "jquery": "3.3.1"
186 + }
187 +}
1 +<?php sleep(10) ?>
2 +<h3 class="text-center no-margin animated bounceInDown">Sign up, <del>it's <strong>free</strong></del> and get <strong>$50 now!</strong></h3>
3 +<p class="lead text-muted text-center">
4 + Faith makes it possible to achieve that which man's mind can conceive and believe.
5 +</p>
6 +<form>
7 +
8 + <div class="form-group">
9 + <label for="exampleInputEmail1"><i class="fa fa-circle text-warning"></i> &nbsp; Email address</label>
10 + <input type="email" class="form-control input-transparent" id="exampleInputEmail1"
11 + placeholder="Enter email">
12 + </div>
13 + <div class="form-group">
14 + <label for="pswd"><i class="fa fa-circle text-danger"></i> &nbsp; Password</label>
15 + <input class="form-control" id="pswd" type="text" placeholder="Min 8 characters">
16 + </div>
17 + <p>
18 + To make a widget automatically load it's content you just need to set
19 + <strong>data-widgster-autoload</strong> attribute and provide an url.
20 + </p>
21 + <pre><code>data-widgster-load="server/ajax_widget.php"
22 +data-widgster-autoload="true"</code></pre>
23 + <p>
24 + <strong>data-widgster-autoload</strong> may be set to an integer value. If set, for example, to
25 + 2000 will refresh widget every 2 seconds.
26 + </p>
27 + <div class="clearfix">
28 + <div class="btn-toolbar float-right">
29 + <button type="submit" class="btn btn-transparent">Cancel</button>
30 + <button type="submit" class="btn btn-success animated wobble">&nbsp;Submit&nbsp;</button>
31 + </div>
32 + </div>
33 +</form>
1 +<!-- demo latetency <?php sleep(2) ?> -->
2 +<p class="text-muted">Simulating latency with tiny php block on the server-side.</p>
3 +<p>A timestamp this widget was created: Apr 24, 19:07:07</p>
4 +<p>A timestamp this widget was updated: <?php echo date("M j, H:i:s") ?></p>
...\ No newline at end of file ...\ No newline at end of file
1 +<!-- <?php sleep(2) //just some delay to simulate latency ?> -->
2 +<ul class="news-list stretchable">
3 + <li class="animated fadeInDown bg-warning-light">
4 + <span class="icon bg-warning text-white">
5 + <i class="fa fa-lock"></i>
6 + </span>
7 + <div class="news-item-info">
8 + <h5 class="name no-margin mb-xs"><a href="#">Just now! Check update time</a></h5>
9 + <p class="fs-mini">
10 + Check this news item timestamp. There is a small server part that generates current timestamp so it
11 + would be easier for you to see ajax widgets in action
12 + </p>
13 + <time class="help-block"><?php echo date("M j, H:i:s")?></time>
14 + </div>
15 + </li>
16 + <li>
17 + <span class="icon bg-danger text-white">
18 + <i class="fa fa-star"></i>
19 + </span>
20 + <div class="news-item-info">
21 + <h5 class="name no-margin mb-xs"><a href="#">First Human Colony on Mars</a></h5>
22 + <p class="fs-mini">
23 + First 700 people will take part in building first human settlement outside of Earth.
24 + That's awesome, right?
25 + </p>
26 + <time class="help-block">Mar 20, 18:46</time>
27 + </div>
28 + </li>
29 + <li>
30 + <span class="icon bg-info text-white">
31 + <i class="fa fa-microphone"></i>
32 + </span>
33 + <div class="news-item-info">
34 + <h5 class="name no-margin mb-xs"><a href="#">Light Blue reached $300</a></h5>
35 + <p class="fs-mini">
36 + Light Blue Inc. shares just hit $300 price. "This was inevitable. It should
37 + have happen sooner or later" - says NYSE expert.
38 + </p>
39 + <time class="help-block">Sep 25, 11:59</time>
40 + </div>
41 + </li>
42 + <li>
43 + <span class="icon bg-success text-white">
44 + <i class="fa fa-eye"></i>
45 + </span>
46 + <div class="news-item-info">
47 + <h5 class="name no-margin mb-xs"><a href="#">No more spying</a></h5>
48 + <p class="fs-mini">
49 + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
50 + incididunt ut labore et dolore magna aliqua.
51 + </p>
52 + <time class="help-block">Mar 20, 18:46</time>
53 + </div>
54 + </li>
55 +</ul>
1 +<?php sleep(2) ?>
2 +<div class="list-group list-group-lg">
3 + <a href="#" class="list-group-item animated fadeInDown bg-warning-light">
4 + <span class="thumb-sm mr">
5 + <img class="rounded-circle" src="/images/people/a6.jpg" alt="...">
6 + </span>
7 + <div>
8 + <h6 class="no-margin"><strong>Jenny Wilington</strong></h6>
9 + <small>just now</small>
10 + </div>
11 + <i class="fa fa-circle ml-auto text-success mt-sm"></i>
12 + </a>
13 + <a href="#" class="list-group-item ">
14 + <span class="thumb-sm mr">
15 + <img class="rounded-circle" src="/images/people/a1.jpg" alt="...">
16 + </span>
17 + <div>
18 + <h6 class="m-0">Maikel Basso</h6>
19 + <small class="text-muted">about 2 mins ago</small>
20 + </div>
21 + <i class="fa fa-circle ml-auto text-danger"></i>
22 + </a>
23 + <a href="#" class="list-group-item">
24 + <span class="thumb-sm mr">
25 + <img class="rounded-circle" src="/images/people/a2.jpg" alt="...">
26 + </span>
27 + <div>
28 + <h6 class="m-0">Ianus Arendse</h6>
29 + <small class="text-muted">about 42 mins ago</small>
30 + </div>
31 + <i class="fa fa-circle ml-auto text-info"></i>
32 + </a>
33 + <a href="#" class="list-group-item">
34 + <span class="thumb-sm mr">
35 + <img class="rounded-circle" src="/images/people/a3.jpg" alt="...">
36 + </span>
37 + <div>
38 + <h6 class="m-0">Valdemar Landau</h6>
39 + <small class="text-muted">one hour ago</small>
40 + </div>
41 + <i class="fa fa-circle ml-auto text-success"></i>
42 + </a>
43 + <a href="#" class="list-group-item mb-n-md">
44 + <span class="thumb-sm mr">
45 + <img class="rounded-circle" src="/images/people/a4.jpg" alt="...">
46 + </span>
47 + <div>
48 + <h6 class="m-0">Rick Teagan</h6>
49 + <small class="text-muted">3 hours ago</small>
50 + </div>
51 + <i class="fa fa-circle ml-auto text-warning"></i>
52 + </a>
53 +</div>
1 +<!DOCTYPE html>
2 +<html lang="en">
3 + <head>
4 + <meta charset="utf-8">
5 + <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.png">
6 + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7 + <meta name="theme-color" content="#000000">
8 + <!--
9 + manifest.json provides metadata used when your web app is added to the
10 + homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
11 + -->
12 + <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
13 + <!--
14 + Notice the use of %PUBLIC_URL% in the tags above.
15 + It will be replaced with the URL of the `public` folder during the build.
16 + Only files inside the `public` folder can be referenced from the HTML.
17 +
18 + Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
19 + work correctly both with client-side routing and a non-root public URL.
20 + Learn how to configure a non-root public URL by running `npm run build`.
21 + -->
22 + <title>Sing App React Dashboard - Admin Template built with React</title>
23 + <meta name="description" content="React Admin Dashboard Template built with Bootstrap, Redux and React Router by Flatlogic. Over 40 unique pages, hundreds of components and theme support.">
24 + <meta name="keywords" content="react admin, react dashboard, react admin template, react theme, react dashboard template, react dashboard template">
25 + <meta name="author" content="Flatlogic LLC.">
26 +
27 + <!-- Global site tag (gtag.js) - Google Analytics -->
28 + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-36759672-9"></script>
29 + <script>
30 + window.dataLayer = window.dataLayer || [];
31 + function gtag(){dataLayer.push(arguments);}
32 + gtag('js', new Date());
33 +
34 + gtag('config', 'UA-36759672-9');
35 + </script>
36 +
37 + <!-- Yandex.Metrika counter --> <script type="text/javascript" > (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)}) (window, document, "script", "https://cdn.jsdelivr.net/npm/yandex-metrica-watch/tag.js", "ym"); ym(48020168, "init", { id:48020168, clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true, trackHash:true, ecommerce:"dataLayer" }); </script> <!-- /Yandex.Metrika counter -->
38 + </head>
39 + <body>
40 + <noscript>
41 + You need to enable JavaScript to run this app.
42 + </noscript>
43 + <div id="root"></div>
44 + <!--
45 + This HTML file is a template.
46 + If you open it directly in the browser, you will see an empty page.
47 +
48 + You can add webfonts, meta tags, or analytics to this file.
49 + The build step will place the bundled scripts into the <body> tag.
50 +
51 + To begin the development, run `npm start` or `yarn start`.
52 + To create a production bundle, use `npm run build` or `yarn build`.
53 + -->
54 + </body>
55 +</html>
1 +{
2 + "short_name": "Sing App 5.0.0 - Isomorphic React Dashboard",
3 + "name": "Create React App Sample",
4 + "icons": [
5 + {
6 + "src": "/favicon.png",
7 + "sizes": "64x64 32x32 24x24 16x16",
8 + "type": "image/x-icon"
9 + }
10 + ],
11 + "start_url": ".",
12 + "display": "standalone",
13 + "theme_color": "#000000",
14 + "background_color": "#ffffff"
15 +}
1 +'use strict';
2 +
3 +// Do this as the first thing so that any code reading it knows the right env.
4 +process.env.BABEL_ENV = 'production';
5 +process.env.NODE_ENV = 'production';
6 +
7 +// Makes the script crash on unhandled rejections instead of silently
8 +// ignoring them. In the future, promise rejections that are not handled will
9 +// terminate the Node.js process with a non-zero exit code.
10 +process.on('unhandledRejection', err => {
11 + throw err;
12 +});
13 +
14 +// Ensure environment variables are read.
15 +require('../config/env');
16 +
17 +
18 +const path = require('path');
19 +const chalk = require('chalk');
20 +const fs = require('fs-extra');
21 +const webpack = require('webpack');
22 +const bfj = require('bfj');
23 +const config = require('../config/webpack.config.prod');
24 +const paths = require('../config/paths');
25 +const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
26 +const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
27 +const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
28 +const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
29 +const printBuildError = require('react-dev-utils/printBuildError');
30 +
31 +const measureFileSizesBeforeBuild =
32 + FileSizeReporter.measureFileSizesBeforeBuild;
33 +const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
34 +const useYarn = fs.existsSync(paths.yarnLockFile);
35 +
36 +// These sizes are pretty large. We'll warn for bundles exceeding them.
37 +const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
38 +const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
39 +
40 +const isInteractive = process.stdout.isTTY;
41 +
42 +// Warn and crash if required files are missing
43 +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
44 + process.exit(1);
45 +}
46 +
47 +// Process CLI arguments
48 +const argv = process.argv.slice(2);
49 +const writeStatsJson = argv.indexOf('--stats') !== -1;
50 +
51 +// We require that you explictly set browsers and do not fall back to
52 +// browserslist defaults.
53 +const { checkBrowsers } = require('react-dev-utils/browsersHelper');
54 +checkBrowsers(paths.appPath, isInteractive)
55 + .then(() => {
56 + // First, read the current file sizes in build directory.
57 + // This lets us display how much they changed later.
58 + return measureFileSizesBeforeBuild(paths.appBuild);
59 + })
60 + .then(previousFileSizes => {
61 + // Remove all content but keep the directory so that
62 + // if you're in it, you don't end up in Trash
63 + fs.emptyDirSync(paths.appBuild);
64 + // Merge with the public folder
65 + copyPublicFolder();
66 + // Start the webpack build
67 + return build(previousFileSizes);
68 + })
69 + .then(
70 + ({ stats, previousFileSizes, warnings }) => {
71 + if (warnings.length) {
72 + console.log(chalk.yellow('Compiled with warnings.\n'));
73 + console.log(warnings.join('\n\n'));
74 + console.log(
75 + '\nSearch for the ' +
76 + chalk.underline(chalk.yellow('keywords')) +
77 + ' to learn more about each warning.'
78 + );
79 + console.log(
80 + 'To ignore, add ' +
81 + chalk.cyan('// eslint-disable-next-line') +
82 + ' to the line before.\n'
83 + );
84 + } else {
85 + console.log(chalk.green('Compiled successfully.\n'));
86 + }
87 +
88 + console.log('File sizes after gzip:\n');
89 + printFileSizesAfterBuild(
90 + stats,
91 + previousFileSizes,
92 + paths.appBuild,
93 + WARN_AFTER_BUNDLE_GZIP_SIZE,
94 + WARN_AFTER_CHUNK_GZIP_SIZE
95 + );
96 + console.log();
97 +
98 + const appPackage = require(paths.appPackageJson);
99 + const publicUrl = paths.publicUrl;
100 + const publicPath = config.output.publicPath;
101 + const buildFolder = path.relative(process.cwd(), paths.appBuild);
102 + printHostingInstructions(
103 + appPackage,
104 + publicUrl,
105 + publicPath,
106 + buildFolder,
107 + useYarn
108 + );
109 + },
110 + err => {
111 + console.log(chalk.red('Failed to compile.\n'));
112 + printBuildError(err);
113 + process.exit(1);
114 + }
115 + )
116 + .catch(err => {
117 + if (err && err.message) {
118 + console.log(err.message);
119 + }
120 + process.exit(1);
121 + });
122 +
123 +// Create the production build and print the deployment instructions.
124 +function build(previousFileSizes) {
125 + console.log('Creating an optimized production build...');
126 +
127 + let compiler = webpack(config);
128 + return new Promise((resolve, reject) => {
129 + compiler.run((err, stats) => {
130 + let messages;
131 + if (err) {
132 + if (!err.message) {
133 + return reject(err);
134 + }
135 + messages = formatWebpackMessages({
136 + errors: [err.message],
137 + warnings: [],
138 + });
139 + } else {
140 + messages = formatWebpackMessages(
141 + stats.toJson({ all: false, warnings: true, errors: true })
142 + );
143 + }
144 + if (messages.errors.length) {
145 + // Only keep the first error. Others are often indicative
146 + // of the same problem, but confuse the reader with noise.
147 + if (messages.errors.length > 1) {
148 + messages.errors.length = 1;
149 + }
150 + return reject(new Error(messages.errors.join('\n\n')));
151 + }
152 + if (
153 + process.env.CI &&
154 + (typeof process.env.CI !== 'string' ||
155 + process.env.CI.toLowerCase() !== 'false') &&
156 + messages.warnings.length
157 + ) {
158 + console.log(
159 + chalk.yellow(
160 + '\nTreating warnings as errors because process.env.CI = true.\n' +
161 + 'Most CI servers set it automatically.\n'
162 + )
163 + );
164 + return reject(new Error(messages.warnings.join('\n\n')));
165 + }
166 +
167 + const resolveArgs = {
168 + stats,
169 + previousFileSizes,
170 + warnings: messages.warnings,
171 + };
172 + if (writeStatsJson) {
173 + return bfj
174 + .write(paths.appBuild + '/bundle-stats.json', stats.toJson())
175 + .then(() => resolve(resolveArgs))
176 + .catch(error => reject(new Error(error)));
177 + }
178 +
179 + return resolve(resolveArgs);
180 + });
181 + });
182 +}
183 +
184 +function copyPublicFolder() {
185 + fs.copySync(paths.appPublic, paths.appBuild, {
186 + dereference: true,
187 + filter: file => file !== paths.appHtml,
188 + });
189 +}
1 +'use strict';
2 +
3 +// Do this as the first thing so that any code reading it knows the right env.
4 +process.env.BABEL_ENV = 'development';
5 +process.env.NODE_ENV = 'development';
6 +
7 +// Makes the script crash on unhandled rejections instead of silently
8 +// ignoring them. In the future, promise rejections that are not handled will
9 +// terminate the Node.js process with a non-zero exit code.
10 +process.on('unhandledRejection', err => {
11 + throw err;
12 +});
13 +
14 +// Ensure environment variables are read.
15 +require('../config/env');
16 +
17 +
18 +const fs = require('fs');
19 +const chalk = require('chalk');
20 +const webpack = require('webpack');
21 +const WebpackDevServer = require('webpack-dev-server');
22 +const clearConsole = require('react-dev-utils/clearConsole');
23 +const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
24 +const {
25 + choosePort,
26 + createCompiler,
27 + prepareProxy,
28 + prepareUrls,
29 +} = require('react-dev-utils/WebpackDevServerUtils');
30 +const openBrowser = require('react-dev-utils/openBrowser');
31 +const paths = require('../config/paths');
32 +const config = require('../config/webpack.config.dev');
33 +const createDevServerConfig = require('../config/webpackDevServer.config');
34 +
35 +const useYarn = fs.existsSync(paths.yarnLockFile);
36 +const isInteractive = process.stdout.isTTY;
37 +
38 +// Warn and crash if required files are missing
39 +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
40 + process.exit(1);
41 +}
42 +
43 +// Tools like Cloud9 rely on this.
44 +const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
45 +const HOST = process.env.HOST || '0.0.0.0';
46 +
47 +if (process.env.HOST) {
48 + console.log(
49 + chalk.cyan(
50 + `Attempting to bind to HOST environment variable: ${chalk.yellow(
51 + chalk.bold(process.env.HOST)
52 + )}`
53 + )
54 + );
55 + console.log(
56 + `If this was unintentional, check that you haven't mistakenly set it in your shell.`
57 + );
58 + console.log(
59 + `Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`
60 + );
61 + console.log();
62 +}
63 +
64 +// We require that you explictly set browsers and do not fall back to
65 +// browserslist defaults.
66 +const { checkBrowsers } = require('react-dev-utils/browsersHelper');
67 +checkBrowsers(paths.appPath, isInteractive)
68 + .then(() => {
69 + // We attempt to use the default port but if it is busy, we offer the user to
70 + // run on a different port. `choosePort()` Promise resolves to the next free port.
71 + return choosePort(HOST, DEFAULT_PORT);
72 + })
73 + .then(port => {
74 + if (port == null) {
75 + // We have not found a port.
76 + return;
77 + }
78 + const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
79 + const appName = require(paths.appPackageJson).name;
80 + const urls = prepareUrls(protocol, HOST, port);
81 + // Create a webpack compiler that is configured with custom messages.
82 + const compiler = createCompiler(webpack, config, appName, urls, useYarn);
83 + // Load proxy config
84 + const proxySetting = require(paths.appPackageJson).proxy;
85 + const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
86 + // Serve webpack assets generated by the compiler over a web server.
87 + const serverConfig = createDevServerConfig(
88 + proxyConfig,
89 + urls.lanUrlForConfig
90 + );
91 + const devServer = new WebpackDevServer(compiler, serverConfig);
92 + // Launch WebpackDevServer.
93 + devServer.listen(port, HOST, err => {
94 + if (err) {
95 + return console.log(err);
96 + }
97 + if (isInteractive) {
98 + clearConsole();
99 + }
100 + console.log(chalk.cyan('Starting the development server...\n'));
101 + openBrowser(urls.localUrlForBrowser);
102 + });
103 +
104 + ['SIGINT', 'SIGTERM'].forEach(function(sig) {
105 + process.on(sig, function() {
106 + devServer.close();
107 + process.exit();
108 + });
109 + });
110 + })
111 + .catch(err => {
112 + if (err && err.message) {
113 + console.log(err.message);
114 + }
115 + process.exit(1);
116 + });
1 +'use strict';
2 +
3 +// Do this as the first thing so that any code reading it knows the right env.
4 +process.env.BABEL_ENV = 'test';
5 +process.env.NODE_ENV = 'test';
6 +process.env.PUBLIC_URL = '';
7 +
8 +// Makes the script crash on unhandled rejections instead of silently
9 +// ignoring them. In the future, promise rejections that are not handled will
10 +// terminate the Node.js process with a non-zero exit code.
11 +process.on('unhandledRejection', err => {
12 + throw err;
13 +});
14 +
15 +// Ensure environment variables are read.
16 +require('../config/env');
17 +
18 +
19 +const jest = require('jest');
20 +const execSync = require('child_process').execSync;
21 +let argv = process.argv.slice(2);
22 +
23 +function isInGitRepository() {
24 + try {
25 + execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 + return true;
27 + } catch (e) {
28 + return false;
29 + }
30 +}
31 +
32 +function isInMercurialRepository() {
33 + try {
34 + execSync('hg --cwd . root', { stdio: 'ignore' });
35 + return true;
36 + } catch (e) {
37 + return false;
38 + }
39 +}
40 +
41 +// Watch unless on CI, in coverage mode, or explicitly running all tests
42 +if (
43 + !process.env.CI &&
44 + argv.indexOf('--coverage') === -1 &&
45 + argv.indexOf('--watchAll') === -1
46 +) {
47 + // https://github.com/facebook/create-react-app/issues/5210
48 + const hasSourceControl = isInGitRepository() || isInMercurialRepository();
49 + argv.push(hasSourceControl ? '--watch' : '--watchAll');
50 +}
51 +
52 +
53 +jest.run(argv);
1 +# Action creators
2 +
3 +Action Creators should go there
1 +export const DISMISS_ALERT = 'DISMISS_ALERT';
2 +
3 +export function dismissAlert(id) {
4 + return {
5 + type: DISMISS_ALERT,
6 + id,
7 + };
8 +}
1 +import axios from 'axios';
2 +
3 +export const RECEIVED_DATA_SUCCESS = 'RECEIVED_DATA_SUCCESS';
4 +export const RECEIVING_DATA = 'RECEIVING_DATA';
5 +
6 +export function receiveDataRequest() {
7 + return (dispatch) => {
8 + dispatch(receivingData());
9 + axios.get('/analytics').then(res => {
10 + dispatch(receiveDataSuccess(res.data));
11 + })
12 + };
13 +}
14 +
15 +export function receiveDataSuccess(payload) {
16 + return {
17 + type: RECEIVED_DATA_SUCCESS,
18 + payload
19 + }
20 +}
21 +
22 +export function receivingData() {
23 + return {
24 + type: RECEIVING_DATA
25 + }
26 +}
27 +
28 +
29 +
30 +
1 +export const CHANGE_THEME = 'CHANGE_THEME';
2 +
3 +export function changeTheme(payload) {
4 + return {
5 + type: CHANGE_THEME,
6 + payload,
7 + };
8 +}
9 +
10 +
1 +/* eslint-disable import/prefer-default-export */
2 +
3 +export const TOGGLE_SIDEBAR = 'TOGGLE_SIDEBAR';
4 +export const OPEN_SIDEBAR = 'OPEN_SIDEBAR';
5 +export const CLOSE_SIDEBAR = 'CLOSE_SIDEBAR';
6 +export const CHANGE_ACTIVE_SIDEBAR_ITEM = 'CHANGE_ACTIVE_SIDEBAR_ITEM';
7 +
8 +export function toggleSidebar() {
9 + return {
10 + type: TOGGLE_SIDEBAR,
11 + };
12 +}
13 +
14 +export function openSidebar() {
15 + return {
16 + type: OPEN_SIDEBAR,
17 + };
18 +}
19 +
20 +export function closeSidebar() {
21 + return {
22 + type: CLOSE_SIDEBAR,
23 + };
24 +}
25 +
26 +export function changeActiveSidebarItem(activeItem) {
27 + return {
28 + type: CHANGE_ACTIVE_SIDEBAR_ITEM,
29 + activeItem,
30 + };
31 +}
1 +import axios from 'axios';
2 +import { toast } from 'react-toastify';
3 +
4 +export const RECEIVED_PRODUCTS = 'RECEIVED_PRODUCTS';
5 +export const RECEIVING_PRODUCTS = 'RECEIVING_PRODUCTS';
6 +export const RECEIVED_PRODUCT = 'RECEIVED_PRODUCT';
7 +export const RECEIVING_PRODUCT = 'RECEIVING_PRODUCT';
8 +export const UPDATED_PRODUCT = 'UPDATED_PRODUCT';
9 +export const UPDATING_PRODUCT = 'UPDATING_PRODUCT';
10 +export const DELETED_PRODUCT = 'DELETED_PRODUCT';
11 +export const DELETING_PRODUCT = 'DELETING_PRODUCT';
12 +export const RECEIVED_IMAGES = 'RECEIVED_IMAGES';
13 +
14 +export function getProductsRequest() {
15 + return (dispatch) => {
16 + dispatch(receivingProducts());
17 + axios.get('/products').then(res => {
18 + dispatch(receiveProducts(res.data));
19 + })
20 + };
21 +}
22 +
23 +export function loadProductRequest(id) {
24 + return (dispatch) => {
25 + dispatch(receivingProduct());
26 + axios.get('/products/' + id).then(res => {
27 + dispatch(receiveProduct(res.data));
28 + })
29 + };
30 +}
31 +
32 +export function updateProductRequest(product) {
33 + return (dispatch) => {
34 + dispatch(updatingProduct());
35 + axios.put('/products/' + product.id, product).then(res => {
36 + dispatch(updateProduct(res.data));
37 + toast.success("Product has been Updated!");
38 + })
39 + };
40 +}
41 +
42 +export function createProductRequest(payload) {
43 + return (dispatch) => {
44 + dispatch(updatingProduct());
45 + axios.post('/products', payload.product).then(res => {
46 + dispatch(updateProduct(res.data));
47 + payload.history.push('/app/ecommerce/management');
48 + toast.success("Product has been Created!");
49 + })
50 + };
51 +}
52 +
53 +export function deleteProductRequest(payload) {
54 + return (dispatch) => {
55 + dispatch(deletingProduct(payload));
56 + axios.delete('/products/' + payload.id).then(res => {
57 + dispatch(deleteProduct({id: payload.id}));
58 + if (payload.history.location.pathname !== '/app/ecommerce/management') {
59 + payload.history.push('/app/ecommerce/management');
60 + }
61 + toast.success("Product has been Deleted!");
62 + })
63 + };
64 +}
65 +
66 +export function getProductsImagesRequest(payload) {
67 + return (dispatch) => {
68 + axios.get('/products/images-list').then(res => {
69 + dispatch(receiveProductImages(res.data));
70 + if (!payload.img && res.data.length) {
71 + dispatch(updateProduct({id: payload.id, img: res.data[0]}));
72 + }
73 + })
74 + };
75 +}
76 +
77 +export function receiveProductImages(payload) {
78 + return {
79 + type: RECEIVED_IMAGES,
80 + payload
81 + }
82 +}
83 +
84 +
85 +export function receiveProducts(payload) {
86 + return {
87 + type: RECEIVED_PRODUCTS,
88 + payload
89 + }
90 +}
91 +
92 +export function receivingProducts() {
93 + return {
94 + type: RECEIVING_PRODUCTS
95 + }
96 +}
97 +
98 +export function receiveProduct(payload) {
99 + return {
100 + type: RECEIVED_PRODUCT,
101 + payload
102 + }
103 +}
104 +
105 +export function receivingProduct() {
106 + return {
107 + type: RECEIVING_PRODUCT
108 + }
109 +}
110 +
111 +export function updateProduct(payload) {
112 + return {
113 + type: UPDATED_PRODUCT,
114 + payload
115 + }
116 +}
117 +
118 +export function updatingProduct() {
119 + return {
120 + type: UPDATING_PRODUCT
121 + }
122 +}
123 +
124 +export function deleteProduct(payload) {
125 + return {
126 + type: DELETED_PRODUCT,
127 + payload
128 + }
129 +}
130 +
131 +export function deletingProduct(payload) {
132 + return {
133 + type: DELETING_PRODUCT,
134 + payload
135 + }
136 +}
137 +
138 +
1 +import axios from 'axios';
2 +import { toast } from 'react-toastify';
3 +
4 +export const REGISTER_REQUEST = 'REGISTER_REQUEST';
5 +export const REGISTER_SUCCESS = 'REGISTER_SUCCESS';
6 +export const REGISTER_FAILURE = 'REGISTER_FAILURE';
7 +
8 +function requestRegister() {
9 + return {
10 + type: REGISTER_REQUEST,
11 + };
12 +}
13 +
14 +export function receiveRegister() {
15 + return {
16 + type: REGISTER_SUCCESS
17 + };
18 +}
19 +
20 +export function registerError(payload) {
21 + return {
22 + type: REGISTER_FAILURE,
23 + payload,
24 + };
25 +}
26 +
27 +export function registerUser(payload) {
28 + return (dispatch) => {
29 + dispatch(requestRegister());
30 + debugger;
31 + const creds = payload.creds;
32 + if (creds.email.length > 0 && creds.password.length > 0) {
33 + axios.post("/user/signup", creds).then(res => {
34 + dispatch(receiveRegister());
35 + toast.success("You've been registered successfully");
36 + payload.history.push('/login');
37 + }).catch(err => {
38 + dispatch(registerError(err.response.data));
39 + })
40 +
41 + } else {
42 + dispatch(registerError('Something was wrong. Try again'));
43 + }
44 + };
45 +}
1 +import axios from 'axios';
2 +import config from '../config';
3 +import jwt from "jsonwebtoken";
4 +
5 +export const LOGIN_REQUEST = 'LOGIN_REQUEST';
6 +export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
7 +export const LOGIN_FAILURE = 'LOGIN_FAILURE';
8 +export const LOGOUT_REQUEST = 'LOGOUT_REQUEST';
9 +export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
10 +export const LOGOUT_FAILURE = 'LOGOUT_FAILURE';
11 +
12 +function requestLogin() {
13 + return {
14 + type: LOGIN_REQUEST,
15 + };
16 +}
17 +
18 +export function receiveLogin() {
19 + return {
20 + type: LOGIN_SUCCESS
21 + };
22 +}
23 +
24 +function loginError(payload) {
25 + return {
26 + type: LOGIN_FAILURE,
27 + payload,
28 + };
29 +}
30 +
31 +function requestLogout() {
32 + return {
33 + type: LOGOUT_REQUEST,
34 + };
35 +}
36 +
37 +export function receiveLogout() {
38 + return {
39 + type: LOGOUT_SUCCESS,
40 + };
41 +}
42 +
43 +// Logs the user out
44 +export function logoutUser() {
45 + return (dispatch) => {
46 + dispatch(requestLogout());
47 + localStorage.removeItem('token');
48 + localStorage.removeItem('user');
49 + document.cookie = 'token=;expires=Thu, 01 Jan 1970 00:00:01 GMT;';
50 + axios.defaults.headers.common['Authorization'] = "";
51 + dispatch(receiveLogout());
52 + };
53 +}
54 +
55 +export function receiveToken(token) {
56 + return (dispatch) => {
57 + let user = jwt.decode(token).user;
58 + delete user.id;
59 + localStorage.setItem('token', token);
60 + localStorage.setItem('user', JSON.stringify(user));
61 + axios.defaults.headers.common['Authorization'] = "Bearer " + token;
62 + dispatch(receiveLogin());
63 + }
64 +}
65 +
66 +export function loginUser(creds) {
67 + return (dispatch) => {
68 + dispatch(requestLogin());
69 + if (creds.social) {
70 + window.location.href = config.baseURLApi + "/user/signin/" + creds.social + (process.env.NODE_ENV === "production" ? "?app=sing-app-react" : "");
71 + }
72 + else if (creds.email.length > 0 && creds.password.length > 0) {
73 + axios.post("/user/signin/local", creds).then(res => {
74 + const token = res.data.token;
75 + dispatch(receiveToken(token));
76 + }).catch(err => {
77 + dispatch(loginError(err.response.data));
78 + })
79 +
80 + } else {
81 + dispatch(loginError('Something was wrong. Try again'));
82 + }
83 + };
84 +}
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 +/* eslint-disable */
8 +import ErrorPage from '../pages/error';
9 +/* eslint-enable */
10 +
11 +import '../styles/theme.scss';
12 +import LayoutComponent from '../components/Layout';
13 +import DocumentationLayoutComponent from '../documentation/DocumentationLayout';
14 +import Login from '../pages/login';
15 +import Register from '../pages/register';
16 +import { logoutUser } from '../actions/user';
17 +
18 +const PrivateRoute = ({dispatch, component, ...rest }) => {
19 + if (!Login.isAuthenticated(localStorage.getItem('token'))) {
20 + dispatch(logoutUser());
21 + return (<Redirect to="/login"/>)
22 + } else {
23 + return ( // eslint-disable-line
24 + <Route {...rest} render={props => (React.createElement(component, props))}/>
25 + );
26 + }
27 +};
28 +
29 +const CloseButton = ({closeToast}) => <i onClick={closeToast} className="la la-close notifications-close"/>
30 +
31 +class App extends React.PureComponent {
32 + render() {
33 + return (
34 + <div>
35 + <ToastContainer
36 + autoClose={5000}
37 + hideProgressBar
38 + closeButton={<CloseButton/>}
39 + />
40 + <HashRouter>
41 + <Switch>
42 + <Route path="/" exact render={() => <Redirect to="/app/main"/>}/>
43 + <Route path="/app" exact render={() => <Redirect to="/app/main"/>}/>
44 + <PrivateRoute path="/app" dispatch={this.props.dispatch} component={LayoutComponent}/>
45 + <Route path="/documentation" exact
46 + render={() => <Redirect to="/documentation/getting-started/overview"/>}/>
47 + <Route path="/documentation" component={DocumentationLayoutComponent}/>
48 + <Route path="/register" exact component={Register}/>
49 + <Route path="/login" exact component={Login}/>
50 + <Route path="/error" exact component={ErrorPage}/>
51 + <Redirect from="*" to="/app/main/analytics"/>
52 + </Switch>
53 + </HashRouter>
54 + </div>
55 +
56 + );
57 + }
58 +}
59 +
60 +const mapStateToProps = state => ({
61 + isAuthenticated: state.auth.isAuthenticated,
62 +});
63 +
64 +export default connect(mapStateToProps)(App);
1 +import React from 'react';
2 +import PropTypes from 'prop-types';
3 +import { withRouter } from 'react-router-dom';
4 +import { ListGroup, ListGroupItem, InputGroup, InputGroupAddon, Input, InputGroupText } from 'reactstrap';
5 +import $ from 'jquery';
6 +
7 +import * as a1 from '../../images/people/a1.jpg';
8 +import * as a2 from '../../images/people/a2.jpg';
9 +import * as a3 from '../../images/people/a3.jpg';
10 +import * as a4 from '../../images/people/a4.jpg';
11 +import * as a5 from '../../images/people/a5.jpg';
12 +import * as a6 from '../../images/people/a6.jpg';
13 +import * as avatar from '../../images/avatar.png';
14 +
15 +import s from './Chat.module.scss';
16 +
17 +class Chat extends React.Component {
18 + static propTypes = {
19 + chatOpen: PropTypes.bool,
20 + };
21 +
22 + static defaultProps = {
23 + chatOpen: false,
24 + };
25 +
26 + constructor(props) {
27 + super(props);
28 +
29 + this.handleChangeContacts = this.handleChangeContacts.bind(this);
30 + this.openMessages = this.openMessages.bind(this);
31 + this.filterConversations = this.filterConversations.bind(this);
32 + this.filterMessages = this.filterMessages.bind(this);
33 + this.addMessage = this.addMessage.bind(this);
34 +
35 + this.state = {
36 + todayConversations: [{
37 + name: 'Chris Gray',
38 + status: 'success',
39 + lastMessage: 'Hey! What\'s up? So many times since we',
40 + image: a2,
41 + messages: [{
42 + id: 0,
43 + text: 'Hey! What\'s up?',
44 + }, {
45 + id: 1,
46 + text: 'Are you there?',
47 + }, {
48 + id: 2,
49 + text: 'Let me know when you come back.',
50 + }, {
51 + id: 3,
52 + text: 'I am here!',
53 + fromMe: true,
54 + }],
55 + }, {
56 + name: 'Jamey Brownlow',
57 + status: 'gray-light',
58 + lastMessage: 'Good news coming tonight. Seems they agreed to proceed',
59 + image: avatar,
60 + }, {
61 + name: 'Livia Walsh',
62 + status: 'danger',
63 + lastMessage: 'Check out my latest email plz!',
64 + image: a1,
65 + }, {
66 + name: 'Jaron Fitzroy',
67 + status: 'gray-light',
68 + lastMessage: 'What about summer break?',
69 + image: avatar,
70 + }, {
71 + name: 'Mike Lewis',
72 + status: 'success',
73 + lastMessage: 'Just ain\'t sure about the weekend now. 90% I\'ll make it.',
74 + image: a4,
75 + }],
76 + lastWeekConversations: [{
77 + name: 'Freda Edison',
78 + status: 'gray-light',
79 + lastMessage: 'Hey what\'s up? Me and Monica going for a lunch somewhere. Wanna join?',
80 + image: a6,
81 + }, {
82 + name: 'Livia Walsh',
83 + status: 'success',
84 + lastMessage: 'Check out my latest email plz!',
85 + image: a5,
86 + }, {
87 + name: 'Jaron Fitzroy',
88 + status: 'warning',
89 + lastMessage: 'What about summer break?',
90 + image: a3,
91 + }, {
92 + name: 'Mike Lewis',
93 + status: 'gray-light',
94 + lastMessage: 'Just ain\'t sure about the weekend now. 90% I\'ll make it.',
95 + image: avatar,
96 + }],
97 + chatMessageOpened: true,
98 + conversation: Object,
99 + searchValue: '',
100 + };
101 + }
102 +
103 + openMessages(conversation, e) {
104 + this.setState({
105 + conversation,
106 + chatMessageOpened: false,
107 + });
108 + $(e.currentTarget).removeClass('active').find('.badge').remove();
109 + }
110 +
111 + addMessage(e) {
112 + if (e.key === 'Enter') {
113 + const value = {
114 + text: e.target.value,
115 + fromMe: true,
116 + };
117 +
118 + this.setState({
119 + conversation: Object.assign({}, this.state.conversation, {
120 + messages: [
121 + ...this.state.conversation.messages || [],
122 + value,
123 + ],
124 + }),
125 + });
126 +
127 + e.target.value = ''; // eslint-disable-line
128 + }
129 + }
130 +
131 + handleChangeContacts(event) {
132 + this.setState({ searchValue: event.target.value });
133 + }
134 +
135 + filterConversations(item) {
136 + const isFindName = item.name.toLowerCase()
137 + .indexOf(this.state.searchValue.toLowerCase()) !== -1;
138 + const isFindMessage = item.lastMessage.toLowerCase()
139 + .indexOf(this.state.searchValue.toLowerCase()) !== -1;
140 + return isFindName || isFindMessage;
141 + }
142 +
143 + filterMessages(item) {
144 + return item.text.toLowerCase().indexOf(this.state.searchValue.toLowerCase()) !== -1;
145 + }
146 +
147 + render() {
148 + return (
149 + <aside className={[s.root, this.props.chatOpen ? s.chatOpen : ''].join(' ')}>
150 + <header className={s.chatHeader}>
151 + <h4 className={s.chatTitle}>Contacts</h4>
152 + <div className="input-group input-group-transparent">
153 + <InputGroup size="sm">
154 + <Input placeholder="Search..." value={this.state.searchValue} onChange={this.handleChangeContacts} />
155 + <InputGroupAddon addonType="append">
156 + <InputGroupText>
157 + <i className="fa fa-search"/>
158 + </InputGroupText>
159 + </InputGroupAddon>
160 + </InputGroup>
161 + </div>
162 + </header>
163 + <div className={[s.chatPanel, s.chatContacts, this.state.chatMessageOpened ? s.chatMessageOpen : ''].join(' ')}>
164 + <h5 className={s.navTitle}>TODAY</h5>
165 + <ListGroup id="chat-sidebar-user-group" className={s.chatSidebarUserGroup}>
166 + {this.state.todayConversations
167 + .filter(this.filterConversations)
168 + .map(item =>
169 + <ListGroupItem
170 + key={item.name}
171 + onClick={e => this.openMessages(item, e)}
172 + >
173 + <i className={['fa fa-circle float-right', `text-${item.status}`].join(' ')} />
174 + <span className="thumb-sm float-left mr">
175 + <img className="rounded-circle" src={item.image} alt="..." />
176 + </span>
177 + <div>
178 + <h6 className={s.messageSender}>{item.name}</h6>
179 + <p className={s.messagePreview}>{item.lastMessage}</p>
180 + </div>
181 + </ListGroupItem>,
182 + )}
183 + </ListGroup>
184 +
185 + <h5 className={s.navTitle}>LAST WEEK</h5>
186 + <ListGroup className={s.chatSidebarUserGroup}>
187 + {this.state.lastWeekConversations
188 + .filter(this.filterConversations)
189 + .map(item =>
190 + <ListGroupItem
191 + key={item.name}
192 + onClick={e => this.openMessages(item, e)}
193 + >
194 + <i className={['fa fa-circle float-right', `text-${item.status}`].join(' ')} />
195 + <span className="thumb-sm pull-left mr">
196 + <img className="rounded-circle" src={item.image} alt="..." />
197 + </span>
198 + <div>
199 + <h6 className={s.messageSender}>{item.name}</h6>
200 + <p className={s.messagePreview}>{item.lastMessage}</p>
201 + </div>
202 + </ListGroupItem>,
203 + )}
204 + </ListGroup>
205 + </div>
206 + <div className={[s.chatPanel, s.chatMessages, this.state.chatMessageOpened ? '' : s.chatMessageOpen].join(' ')}>
207 + <h6 className={s.messagesTitle}>
208 + {/* eslint-disable */}
209 + <a onClick={() => this.setState({ chatMessageOpened: true })}>
210 + <i className="fa fa-angle-left mr-xs" />
211 + {this.state.conversation.name}
212 + </a>
213 + {/* eslint-disable */}
214 + </h6>
215 + <ListGroup>
216 + {this.state.conversation.messages &&
217 + this.state.conversation.messages
218 + .filter(this.filterMessages)
219 + .map(item => <ListGroupItem key={item.id} className={[item.fromMe ? s.fromMe : '', s.messageItem]}>
220 + <span className="thumb-sm">
221 + <img className="rounded-circle"
222 + src={item.fromMe ? avatar : this.state.conversation.image} alt="..."/>
223 + </span>
224 + <div className={s.messageBody}>{item.text}</div>
225 + </ListGroupItem>,
226 + )}
227 + </ListGroup>
228 +
229 + <footer className={[s.chatFooter, 'form-group'].join(' ')}>
230 + <input className="form-control fs-mini" onKeyPress={this.addMessage} type="text"
231 + placeholder="Type your message"/>
232 + </footer>
233 + </div>
234 + </aside>
235 + );
236 + }
237 +}
238 +
239 +export default withRouter(Chat);
1 +@import '../../styles/app';
2 +
3 +.root {
4 + position: fixed;
5 + overflow: hidden;
6 + top: 0;
7 + bottom: 0;
8 + right: -$chat-sidebar-width-open;
9 + width: $chat-sidebar-width-open;
10 + border-left: $sidebar-border;
11 + background-color: var(--chat-sidebar-bg-color);
12 + color: #aaa;
13 + transition: right 0.3s ease-in-out;
14 +
15 + @include media-breakpoint-down(md) {
16 + right: -($chat-sidebar-width-open + 25px);
17 + width: $chat-sidebar-width-open + 25px;
18 + }
19 +
20 + &.chatOpen {
21 + right: 0;
22 + }
23 +
24 + .chatHeader {
25 + position: absolute;
26 + width: 100%;
27 + top: 0;
28 + z-index: 3;
29 + padding: 10px;
30 + background-color: var(--chat-sidebar-bg-color);
31 +
32 + input {
33 + padding: 0.6rem 0.85rem;
34 + line-height: 1.5;
35 + }
36 +
37 + .chatTitle {
38 + margin: 10px;
39 + text-transform: uppercase;
40 + font-size: 15px;
41 + font-weight: 400;
42 + }
43 + }
44 +
45 + .chatPanel {
46 + position: absolute;
47 + top: 0;
48 + bottom: 0;
49 + width: 100%;
50 +
51 + :global .list-group-item {
52 + border: 0;
53 + padding: 10px 20px;
54 + z-index: 1;
55 +
56 + .cirle {
57 + font-size: 11px;
58 + line-height: 37px;
59 + margin-left: auto;
60 + }
61 + }
62 +
63 + .navTitle {
64 + margin: 35px 10px 5px 20px;
65 + font-size: 14px;
66 + }
67 +
68 + .navTitle:first-child {
69 + margin-top: 0;
70 + }
71 +
72 + .messageSender {
73 + text-overflow: ellipsis;
74 + color: var(--sidebar-color);
75 + }
76 +
77 + .messagePreview {
78 + margin: 0;
79 + width: 100px;
80 + white-space: nowrap;
81 + overflow: hidden;
82 + text-overflow: ellipsis;
83 + font-size: 85%;
84 + color: #999;
85 + }
86 + }
87 +
88 + .chatContacts {
89 + padding-top: 105px;
90 + left: -100%;
91 + transition: left 0.2s ease-in-out;
92 +
93 + :global .list-group {
94 + margin-top: 10px;
95 +
96 + :global .list-group-item {
97 + cursor: pointer;
98 + }
99 + }
100 +
101 + &.chatMessageOpen {
102 + left: 0;
103 + overflow-y: auto;
104 +
105 + @include scroll-bar(rgba($white, 0.3));
106 + }
107 + }
108 +
109 + .chatMessages {
110 + padding-top: 100px;
111 + right: -100%;
112 + transition: right 0.2s ease-in-out;
113 + background-color: var(--sidebar-bg-color);
114 +
115 + :global .list-group {
116 + position: absolute;
117 + top: 134px; // header height
118 + bottom: 47px; // footer height
119 + width: 100%;
120 + padding-top: 0.5rem;
121 + overflow-y: auto;
122 +
123 + @include scroll-bar(rgba($white, 0.3));
124 +
125 + :global .list-group-item {
126 + align-items: flex-start;
127 + }
128 +
129 + :global .thumb-sm {
130 + float: left;
131 + }
132 + }
133 +
134 + .messageBody {
135 + position: relative;
136 + margin-left: 50px;
137 + padding: 10px;
138 + font-size: 13px;
139 + font-weight: $font-weight-normal;
140 + background-color: $gray-200;
141 + color: $text-color;
142 + border-radius: 0.25rem;
143 +
144 + &::before {
145 + right: 100%;
146 + top: 8px;
147 + content: '';
148 + height: 0;
149 + width: 0;
150 + position: absolute;
151 + border: 10px solid rgba(0, 0, 0, 0);
152 + border-right-color: $gray-200;
153 + }
154 + }
155 +
156 + .fromMe {
157 + flex-direction: row-reverse;
158 +
159 + :global .thumb-sm {
160 + float: right;
161 + }
162 +
163 + .messageBody {
164 + margin-left: 0;
165 + margin-right: 50px;
166 + background-color: theme-color('warning');
167 + color: $gray-800;
168 +
169 + &::before {
170 + right: auto;
171 + left: 100%;
172 + border-right-color: rgba(0, 0, 0, 0);
173 + border-left-color: theme-color('warning');
174 + }
175 + }
176 + }
177 +
178 + .chatFooter {
179 + position: absolute;
180 + z-index: 1;
181 + bottom: 0;
182 + width: 100%;
183 + margin-bottom: 0;
184 + padding: 10px;
185 + background-color: $white;
186 + }
187 +
188 + &.chatMessageOpen {
189 + right: 0;
190 + }
191 +
192 + .messagesTitle {
193 + margin-bottom: 0;
194 +
195 + a {
196 + display: block;
197 + margin-top: -7px;
198 + padding: 17px 16px;
199 + }
200 + }
201 + }
202 +
203 + .chatSidebarUserGroup {
204 + :global .list-group-item {
205 + transition: $transition-base;
206 + }
207 +
208 + :global .list-group-item.active {
209 + background: rgba($white, 0.1);
210 + }
211 +
212 + :global .list-group-item.active h6 {
213 + color: theme-color('warning');
214 + font-weight: $badge-font-weight;
215 + }
216 +
217 + :global .list-group-item:hover {
218 + background: var(--sidebar-action-bg);
219 + }
220 +
221 + :global .badge {
222 + margin: 8px 5px 0 0;
223 + padding: 3px 5px;
224 + }
225 +
226 + :global .fa {
227 + margin-top: 11px;
228 + }
229 + }
230 +
231 + .messageItem {
232 + &:hover {
233 + background: transparent;
234 + }
235 + }
236 +}
1 +{
2 + "name": "Chat",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Chat.js"
6 +}
1 +import React from 'react';
2 +import { connect } from 'react-redux';
3 +import PropTypes from 'prop-types';
4 +import { withRouter } from 'react-router';
5 +import {
6 + Navbar,
7 + Nav,
8 + Dropdown,
9 + NavItem,
10 + NavLink,
11 + Badge,
12 + DropdownToggle,
13 + DropdownMenu,
14 + DropdownItem,
15 + UncontrolledTooltip,
16 + InputGroupAddon,
17 + InputGroup,
18 + Input,
19 + Form,
20 + FormGroup,
21 +} from 'reactstrap';
22 +import $ from 'jquery';
23 +
24 +import Notifications from '../Notifications';
25 +import { logoutUser } from '../../actions/user';
26 +import { toggleSidebar, openSidebar, closeSidebar, changeActiveSidebarItem } from '../../actions/navigation';
27 +
28 +import a5 from '../../images/people/a5.jpg';
29 +import a6 from '../../images/people/a6.jpg';
30 +
31 +import s from './Header.module.scss'; // eslint-disable-line css-modules/no-unused-class
32 +
33 +class Header extends React.Component {
34 + static propTypes = {
35 + sidebarOpened: PropTypes.bool.isRequired,
36 + sidebarStatic: PropTypes.bool.isRequired,
37 + chatToggle: PropTypes.func.isRequired,
38 + dispatch: PropTypes.func.isRequired,
39 + location: PropTypes.shape({
40 + pathname: PropTypes.string,
41 + }).isRequired,
42 + };
43 +
44 + constructor(props) {
45 + super(props);
46 +
47 + this.toggleMenu = this.toggleMenu.bind(this);
48 + this.switchSidebar = this.switchSidebar.bind(this);
49 + this.toggleNotifications = this.toggleNotifications.bind(this);
50 + this.toggleSidebar = this.toggleSidebar.bind(this);
51 + this.doLogout = this.doLogout.bind(this);
52 +
53 + this.state = {
54 + menuOpen: false,
55 + notificationsOpen: false,
56 + notificationsTabSelected: 1,
57 + };
58 + }
59 + componentDidMount() {
60 + if (window.innerWidth > 576) {
61 + setTimeout(() => {
62 + const $chatNotification = $('#chat-notification');
63 + $chatNotification.removeClass('hide').addClass('animated fadeIn')
64 + .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', () => {
65 + $chatNotification.removeClass('animated fadeIn');
66 + setTimeout(() => {
67 + $chatNotification.addClass('animated fadeOut')
68 + .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd' +
69 + ' oanimationend animationend', () => {
70 + $chatNotification.addClass('hide');
71 + });
72 + }, 6000);
73 + });
74 + $chatNotification.siblings('#toggle-chat')
75 + .append('<i class="chat-notification-sing animated bounceIn"></i>');
76 + }, 4000);
77 + }
78 +
79 + $('#search-input').on('blur focus', (e) => {
80 + $('#search-input').parents('.input-group')[e.type === 'focus' ? 'addClass' : 'removeClass']('focus');
81 + });
82 + }
83 +
84 + toggleNotifications() {
85 + this.setState({
86 + notificationsOpen: !this.state.notificationsOpen,
87 + });
88 + }
89 +
90 + doLogout() {
91 + this.props.dispatch(logoutUser());
92 + }
93 +
94 + // collapse/uncolappse
95 + switchSidebar() {
96 + if (this.props.sidebarOpened) {
97 + this.props.dispatch(closeSidebar());
98 + this.props.dispatch(changeActiveSidebarItem(null));
99 + } else {
100 + const paths = this.props.location.pathname.split('/');
101 + paths.pop();
102 + this.props.dispatch(openSidebar());
103 + this.props.dispatch(changeActiveSidebarItem(paths.join('/')));
104 + }
105 + }
106 +
107 + // static/non-static
108 + toggleSidebar() {
109 + this.props.dispatch(toggleSidebar());
110 + if (this.props.sidebarStatic) {
111 + localStorage.setItem('staticSidebar', 'false');
112 + this.props.dispatch(changeActiveSidebarItem(null));
113 + } else {
114 + localStorage.setItem('staticSidebar', 'true');
115 + const paths = this.props.location.pathname.split('/');
116 + paths.pop();
117 + this.props.dispatch(changeActiveSidebarItem(paths.join('/')));
118 + }
119 + }
120 +
121 + toggleMenu() {
122 + this.setState({
123 + menuOpen: !this.state.menuOpen,
124 + });
125 + }
126 + render() {
127 + const user = JSON.parse(localStorage.getItem('user') || {});
128 +
129 + const firstUserLetter = (user.name|| user.email || "P")[0].toUpperCase();
130 +
131 + return (
132 + <Navbar className={`${s.root} d-print-none`}>
133 + <Nav>
134 + <NavItem>
135 + <NavLink className="d-md-down-none ml-5" id="toggleSidebar" onClick={this.toggleSidebar}>
136 + <i className="la la-bars" />
137 + </NavLink>
138 + <UncontrolledTooltip placement="bottom" target="toggleSidebar">
139 + Turn on/off<br />sidebar<br />collapsing
140 + </UncontrolledTooltip>
141 + <NavLink className="fs-lg d-lg-none" onClick={this.switchSidebar}>
142 + <span className="rounded rounded-lg bg-gray text-white d-md-none"><i className="la la-bars" /></span>
143 + <i className="la la-bars ml-3 d-sm-down-none" />
144 + </NavLink>
145 + </NavItem>
146 + <NavItem className="d-sm-down-none">
147 + <NavLink className="px-2">
148 + <i className="la la-refresh" />
149 + </NavLink>
150 + </NavItem>
151 + <NavItem className="d-sm-down-none">
152 + <NavLink className="px-2">
153 + <i className="la la-times" />
154 + </NavLink>
155 + </NavItem>
156 +
157 + </Nav>
158 +
159 + <Form className="d-sm-down-none ml-5" inline>
160 + <FormGroup>
161 + <InputGroup className="input-group-no-border">
162 + <InputGroupAddon addonType="prepend">
163 + <i className="la la-search" />
164 + </InputGroupAddon>
165 + <Input id="search-input" placeholder="Search Dashboard" />
166 + </InputGroup>
167 + </FormGroup>
168 + </Form>
169 +
170 + <NavLink className={`${s.navbarBrand} d-md-none`}>
171 + <i className="fa fa-circle text-gray mr-n-sm" />
172 + <i className="fa fa-circle text-warning" />
173 + &nbsp;
174 + sing
175 + &nbsp;
176 + <i className="fa fa-circle text-warning mr-n-sm" />
177 + <i className="fa fa-circle text-gray" />
178 + </NavLink>
179 +
180 + <Nav className="ml-auto">
181 + <Dropdown nav isOpen={this.state.notificationsOpen} toggle={this.toggleNotifications} id="basic-nav-dropdown" className={`${s.notificationsMenu} d-sm-down-none`}>
182 + <DropdownToggle nav caret>
183 + <span className={`${s.avatar} rounded-circle thumb-sm float-left mr-2`}>
184 + {user.avatar || user.email === "admin@flatlogic.com" ? (
185 + <img src={user.avatar || a5} alt="..."/>
186 + ) : (
187 + <span>{firstUserLetter}</span>
188 + )}
189 + </span>
190 + <span className="small">{user.name || user.email || "Philip smith"}</span>
191 + <span className="ml-1 circle bg-warning text-white fw-bold">13</span>
192 + </DropdownToggle>
193 + <DropdownMenu right className={`${s.notificationsWrapper} py-0 animated animated-fast fadeInUp`}>
194 + <Notifications />
195 + </DropdownMenu>
196 + </Dropdown>
197 + <Dropdown nav isOpen={this.state.menuOpen} toggle={this.toggleMenu} className="d-sm-down-none">
198 + <DropdownToggle nav>
199 + <i className="la la-cog" />
200 + </DropdownToggle>
201 + <DropdownMenu right className="super-colors">
202 + <DropdownItem><i className="la la-user" /> My Account</DropdownItem>
203 + <DropdownItem divider />
204 + <DropdownItem href="/calendar">Calendar</DropdownItem>
205 + <DropdownItem href="/inbox">Inbox &nbsp;&nbsp;<Badge color="danger" pill className="animated bounceIn">9</Badge></DropdownItem>
206 + <DropdownItem divider />
207 + <DropdownItem onClick={this.doLogout}><i className="la la-sign-out" /> Log Out</DropdownItem>
208 + </DropdownMenu>
209 + </Dropdown>
210 + <NavItem>
211 + <NavLink className="d-sm-down-none mr-5" id="toggle-chat" onClick={this.props.chatToggle}>
212 + <i className="la la-globe" />
213 + </NavLink>
214 + <div id="chat-notification" className={`${s.chatNotification} hide `}>
215 + <div className={s.chatNotificationInner}>
216 + <h6 className={`${s.title} d-flex`}>
217 + <span className="thumb-xs">
218 + <img src={a6} alt="" className="rounded-circle mr-xs float-left" />
219 + </span>
220 + Jess Smith
221 + </h6>
222 + <p className={s.text}>Hi there! <br /> This is a completely new version of Sing App <br /> built with <strong className="text-primary">React JS</strong> </p>
223 + </div>
224 + </div>
225 + </NavItem>
226 + <NavItem className="fs-lg d-md-none">
227 + <NavLink href="#" onClick={this.props.chatToggle}>
228 + <span className="rounded rounded-lg bg-gray text-white"><i className="la la-globe" /></span>
229 + </NavLink>
230 + </NavItem>
231 + </Nav>
232 + </Navbar>
233 + );
234 + }
235 +}
236 +
237 +function mapStateToProps(store) {
238 + return {
239 + sidebarOpened: store.navigation.sidebarOpened,
240 + sidebarStatic: store.navigation.sidebarStatic,
241 + };
242 +}
243 +
244 +export default withRouter(connect(mapStateToProps)(Header));
245 +
1 +@import '../../styles/app';
2 +
3 +.root {
4 + z-index: 100;
5 + background: var(--navbar-bg);
6 + box-shadow: var(--navbar-shadow);
7 +
8 + @include media-breakpoint-down(sm) {
9 + padding: 7px 0;
10 + }
11 +
12 + :global {
13 + .input-group {
14 + overflow: hidden;
15 + }
16 +
17 + .input-group-prepend {
18 + background-color: #fff;
19 + transition: background-color ease-in-out 0.15s;
20 + border-top-left-radius: 0.3rem;
21 + border-bottom-left-radius: 0.3rem;
22 + display: flex;
23 + justify-content: center;
24 + align-items: center;
25 + width: 35px;
26 + }
27 +
28 + .focus .input-group-prepend {
29 + background: #f8f9fa;
30 + }
31 + }
32 +}
33 +
34 +.logo {
35 + font-size: 16px;
36 +}
37 +
38 +.navbarForm {
39 + padding: 6px 0 6px 1rem;
40 + margin-left: 10px;
41 + display: inline-block;
42 + top: 2px;
43 + width: auto;
44 +
45 + .inputAddon {
46 + position: relative;
47 + display: inline;
48 + border: none;
49 + background-color: #fff;
50 + transition: background-color ease-in-out 0.15s;
51 + }
52 +
53 + input {
54 + border: none;
55 + padding: 0.6rem 0.85rem 0.6rem 0;
56 + display: inline !important;
57 + width: 250px !important;
58 + top: 2px;
59 + }
60 +}
61 +
62 +.chatNotification {
63 + position: absolute;
64 + right: 35px;
65 + top: 50px;
66 + z-index: 20;
67 + margin-top: 3px;
68 + padding: 5px 0;
69 + cursor: pointer;
70 +
71 + &::before {
72 + content: ' ';
73 + position: absolute;
74 + top: 0;
75 + right: 18px;
76 + width: 0;
77 + height: 0;
78 + border-left: 5px solid transparent;
79 + border-right: 5px solid transparent;
80 + border-bottom: 5px solid $gray-800;
81 + }
82 +
83 + .chatNotificationInner {
84 + min-width: 120px;
85 + padding: 8px;
86 + font-size: 12px;
87 + border-radius: 0.25rem;
88 + text-decoration: none;
89 + background-color: $gray-800;
90 + color: #fff;
91 + }
92 +
93 + .text {
94 + margin-top: 5px;
95 + margin-bottom: 0;
96 + color: $gray-600;
97 + }
98 +
99 + .title {
100 + margin: 0;
101 + font-weight: 600;
102 + line-height: 28px;
103 + font-size: 0.875rem;
104 +
105 + span {
106 + margin-right: 7px;
107 + }
108 + }
109 +}
110 +
111 +.navbarBrand {
112 + position: absolute;
113 + left: 0;
114 + right: 0;
115 + top: 0;
116 + bottom: 0;
117 + display: flex;
118 + justify-content: center;
119 + align-items: center;
120 + font-weight: 700;
121 + font-size: 1.25rem;
122 + pointer-events: none;
123 +
124 + i {
125 + font-size: 10px;
126 + }
127 +}
128 +
129 +.notificationsMenu {
130 + :global .dropdown-menu {
131 + left: auto !important;
132 + right: 0 !important;
133 + top: $navbar-height !important;
134 + }
135 +}
136 +
137 +.notificationsWrapper {
138 + width: min-content;
139 +}
140 +
141 +.avatar {
142 + display: flex;
143 + align-items: center;
144 + justify-content: center;
145 + overflow: hidden;
146 + height: 40px;
147 + width: 40px;
148 + background: $warning;
149 + font-weight: 600;
150 + font-size: 18px;
151 +}
1 +{
2 + "name": "Header",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Header.js"
6 +}
1 +import React, { Component } from 'react';
2 +import cx from 'classnames';
3 +import { Button } from 'reactstrap';
4 +import PropTypes from 'prop-types';
5 +import { connect } from 'react-redux';
6 +import { DashboardThemes } from '../../reducers/layout';
7 +import { changeTheme } from '../../actions/layout';
8 +
9 +import Widget from '../Widget';
10 +
11 +import s from './Helper.module.scss'; // eslint-disable-line
12 +import themeDark from '../../images/theme-dark.png';
13 +import themeLight from '../../images/theme-light.png';
14 +
15 +class Helper extends Component {
16 + static propTypes = {
17 + dispatch: PropTypes.func.isRequired,
18 + dashboardTheme: PropTypes.string
19 + };
20 +
21 + static defaultProps = {
22 + dashboardTheme: DashboardThemes.DARK
23 + };
24 +
25 + state = { isOpened: false };
26 +
27 + toggle = () => {
28 + this.setState(prevState => ({
29 + isOpened: !prevState.isOpened,
30 + }));
31 + };
32 +
33 + changeTheme = (state) => {
34 + this.props.dispatch(changeTheme(state));
35 + };
36 +
37 + render() {
38 + const { isOpened } = this.state;
39 + return (
40 + <div className={cx(s.themeHelper, { [s.themeHelperOpened]: isOpened })}>
41 + <Widget
42 + className={s.themeHelperContent}
43 + bodyClass="mt-3"
44 + title={
45 + <header className={cx(s.themeHelperHeader, 'd-flex p-0')}>
46 + <Button color="warning" className={s.themeHelperBtn} onClick={this.toggle}>
47 + <div className={cx(s.themeHelperSpinner, 'text-white')}>
48 + <i className="la la-cog" />
49 + <i className="la la-cog" />
50 + </div>
51 + </Button>
52 + <h6>Theme</h6>
53 + </header>
54 + }
55 + >
56 + <div className={s.themeSwitcher}>
57 + <div className={cx(s.theme, "mb-3")}>
58 + <input checked={this.props.dashboardTheme === DashboardThemes.LIGHT} onClick={() => this.changeTheme(DashboardThemes.LIGHT)} type="radio" id="css-light" value="option2" name="theme-variant" aria-label="Sing Light" readOnly/>
59 + <label htmlFor="css-light">
60 + <img className={s.themeImage} src={themeLight} alt="light theme"/>
61 + </label>
62 + </div>
63 + <div className={s.theme}>
64 + <input checked={this.props.dashboardTheme === DashboardThemes.DARK} onClick={() => this.changeTheme(DashboardThemes.DARK)} type="radio" id="css-dark" value="option1" name="theme-variant" aria-label="Single Dark" readOnly/>
65 + <label htmlFor="css-dark">
66 + <img className={s.themeImage} src={themeDark} alt="dark theme"/>
67 + </label>
68 + </div>
69 + </div>
70 + <div className="mt-4">
71 + <Button
72 + href="https://flatlogic.com/admin-dashboards/sing-app-react"
73 + target="_blank"
74 + className="btn-rounded-f btn-block fs-mini"
75 + color="warning"
76 + >
77 + <span className="text-white">Purchase</span>
78 + </Button>
79 + <Button
80 + href="http://demo.flatlogic.com/sing-app/documentation/"
81 + target="_blank"
82 + className="btn-rounded-f btn-block fs-mini text-white"
83 + >
84 + Documentation
85 + </Button>
86 + </div>
87 + <div className="d-flex justify-content-between mt-lg">
88 + <Button
89 + href="https://flatlogic.com/contact"
90 + target="_blank"
91 + className="btn-outline-default btn-rounded-f fs-mini text-muted px-2"
92 + >
93 + <i className="glyphicon glyphicon-headphones mr-xs" />
94 + Support
95 + </Button>
96 + <Button
97 + href="https://github.com/flatlogic/sing-app"
98 + target="_blank"
99 + className="btn-outline-default btn-rounded-f fs-mini text-muted px-2"
100 + >
101 + <i className="fa fa-github mr-xs" />
102 + Github
103 + </Button>
104 + </div>
105 + <div className="mt-lg d-flex flex-column align-items-center theme-helper__sharing">
106 + <span className="fs-sm">
107 + Thank you for sharing!
108 + </span>
109 + <div className="d-flex justify-content-center text-light mt-2">
110 + <a
111 + target="_blank"
112 + rel="noopener noreferrer"
113 + href="https://twitter.com/intent/tweet?text=Amazing%20dashboard%20built%20with%20NodeJS,%20React%20and%20Bootstrap!&url=https://github.com/flatlogic/react-dashboard&via=flatlogic"
114 + >
115 + <i className="fa fa-twitter pr-1" />
116 + </a>
117 + <a
118 + href="https://www.facebook.com/search/top/?q=flatlogic%20llc"
119 + target="_blank"
120 + rel="noopener noreferrer"
121 + >
122 + <i className="fa fa-facebook pl-1" />
123 + </a>
124 + </div>
125 + </div>
126 + </Widget>
127 + </div>
128 + );
129 + }
130 +}
131 +
132 +function mapStateToProps(store) {
133 + return {
134 + dashboardTheme: store.layout.dashboardTheme,
135 + };
136 +}
137 +
138 +export default connect(mapStateToProps)(Helper);
1 +@import '../../styles/app';
2 +
3 +.themeHelper {
4 + width: $sidebar-width-open;
5 + position: fixed;
6 + right: -$sidebar-width-open;
7 + top: $navbar-height * 1.5;
8 + z-index: 100;
9 +
10 + @include transition(right $sidebar-transition-time ease-in-out);
11 +
12 + & :global {
13 + .abc-radio-secondary input[type="radio"]:not(:checked)+label::before {
14 + background-color: #798892;
15 + }
16 +
17 + .abc-radio-warning input[type="radio"]:not(:checked)+label::before {
18 + background-color: theme-color('warning');
19 + }
20 + }
21 +
22 + .themeSwitcher {
23 + display: flex;
24 + flex-direction: column;
25 + align-items: center;
26 +
27 + .theme {
28 + position: relative;
29 +
30 + &,
31 + & > label {
32 + width: 100%;
33 + height: max-content;
34 + }
35 +
36 + & > input {
37 + position: absolute;
38 + width: 0;
39 + height: 0;
40 + padding: 0;
41 + margin: 0;
42 + pointer-events: none;
43 + opacity: 0;
44 + }
45 +
46 + & > label {
47 + margin: 0;
48 + border: 1px solid $input-border-color;
49 + padding: 3px;
50 + border-radius: $border-radius;
51 + transition: background-color .2s ease-in-out;
52 + cursor: pointer;
53 + display: block;
54 +
55 + &:hover {
56 + background-color: $gray-200;
57 + }
58 + }
59 +
60 + & > input:checked + label {
61 + background-color: $gray-300;
62 + }
63 +
64 + .themeImage {
65 + width: 100%;
66 + }
67 + }
68 + }
69 +
70 + &.themeHelperOpened {
71 + right: 0;
72 + }
73 +
74 + .themeHelperBtn {
75 + position: absolute;
76 + width: $sidebar-width-open / 4;
77 + transform: translateX(-76px);
78 + margin-top: -($widget-padding-vertical);
79 + cursor: pointer;
80 + z-index: 200;
81 + border-radius: 50% 0 0 50%;
82 + padding: $spacer * 0.8 $spacer / 2 $spacer * 0.8 $spacer;
83 + &,
84 + &:not(.active) {
85 + box-shadow: $widget-shadow-designated !important;
86 + }
87 +
88 + i {
89 + animation-duration: 6500ms;
90 + animation-iteration-count: infinite;
91 + animation-timing-function: linear;
92 + }
93 +
94 + i:first-of-type {
95 + animation-name: spin;
96 + margin-right: -$spacer * 1.15;
97 + vertical-align: text-bottom;
98 + }
99 +
100 + i:last-of-type {
101 + animation-name: spin-reverse;
102 + vertical-align: $font-size-sm;
103 + }
104 + }
105 +
106 + .themeHelperSpinner {
107 + font-size: $font-size-lg * 1.4;
108 + line-height: initial;
109 + }
110 +
111 + .themeHelperHeader {
112 + padding-top: 0;
113 +
114 + h6 {
115 + margin: auto;
116 + }
117 + }
118 +
119 + .themeHelperContent {
120 + box-shadow: $widget-shadow-designated;
121 + border-radius: 0;
122 + }
123 +
124 + .themeHelperSharing {
125 + font-size: $font-size-lg;
126 + cursor: pointer;
127 + }
128 +
129 + :global .glyphicon {
130 + vertical-align: top;
131 + }
132 +
133 + @keyframes spin {
134 + 0% {
135 + transform: rotate(0deg);
136 + }
137 +
138 + 50% {
139 + transform: rotate(360deg);
140 + }
141 + }
142 +
143 + @keyframes spin-reverse {
144 + 0% {
145 + transform: rotate(0deg);
146 + }
147 +
148 + 50% {
149 + transform: rotate(-360deg);
150 + }
151 + }
152 +}
1 +{
2 + "name": "Helper",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Helper.js"
6 +}
1 +import React from 'react';
2 +import PropTypes from 'prop-types';
3 +import Formsy from 'formsy-react';
4 +
5 +Formsy.addValidationRule('isRange', (values, value, array) => (value >= array[0] && value <= array[1]));
6 +
7 +class InputValidation extends React.Component {
8 + /* eslint-disable */
9 + static propTypes = {
10 + trigger: PropTypes.string,
11 + type: PropTypes.string,
12 + className: PropTypes.string,
13 + name: PropTypes.string,
14 + id: PropTypes.string,
15 + placeholder: PropTypes.string,
16 + setValue: PropTypes.func,
17 + isFormSubmitted: PropTypes.func,
18 + getErrorMessage: PropTypes.func,
19 + showRequired: PropTypes.func,
20 + };
21 + /* eslint-enable */
22 +
23 + static defaultProps = {
24 + trigger: null,
25 + type: 'text',
26 + className: '',
27 + name: '',
28 + id: '',
29 + placeholder: '',
30 + };
31 +
32 + constructor() {
33 + super();
34 + this.changeValue = this.changeValue.bind(this);
35 + }
36 +
37 + changeValue(event) {
38 + this.props.setValue(event.currentTarget.value);
39 + }
40 +
41 + render() {
42 + const errorMessageObj = (this.props.isFormSubmitted() || this.props.trigger) ? this.props.getErrorMessage() : null;
43 + const required = (this.props.isFormSubmitted() && this.props.showRequired()) ?
44 + <span className={'help-block text-danger'}>This value is required.</span> : null;
45 + const errorMsg = [];
46 + if (errorMessageObj) {
47 + Object.keys(errorMessageObj).forEach((type) => {
48 + errorMsg.push(errorMessageObj[type]);
49 + });
50 + }
51 + const errorList = errorMsg.map((msg, index) =>
52 + <span key={`msg-err-${index.toString()}`} className={'help-block text-danger'}>{msg}</span>,
53 + );
54 +
55 + return (
56 + <div className={this.props.className}>
57 + <input
58 + type={this.props.type}
59 + name={this.props.name}
60 + id={this.props.id}
61 + className={'form-control'}
62 + onBlur={this.changeValue}
63 + placeholder={this.props.placeholder}
64 + />
65 + {required}
66 + {errorList}
67 + </div>
68 + );
69 + }
70 +}
71 +
72 +export default Formsy.HOC(InputValidation);
1 +{
2 + "name": "InputValidation",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./InputValidation.js"
6 +}
This diff is collapsed. Click to expand it.
1 +@import '../../styles/app';
2 +
3 +.root {
4 + height: 100%;
5 + position: relative;
6 + left: 0;
7 + transition: left $sidebar-transition-time ease-in-out;
8 +
9 + &.chatOpen {
10 + left: -($chat-sidebar-width-open);
11 +
12 + @include media-breakpoint-down(md) {
13 + left: -($chat-sidebar-width-open + 25px);
14 + }
15 + }
16 +}
17 +
18 +.wrap {
19 + position: relative;
20 + min-height: 100vh;
21 + display: flex;
22 + margin-left: 50px;
23 + flex-direction: column;
24 + left: $sidebar-width-open - $sidebar-width-closed;
25 + right: 0;
26 + transition: left $sidebar-transition-time ease-in-out;
27 +
28 + @media (max-width: breakpoint-max(sm)) {
29 + margin-left: 0;
30 + left: $sidebar-width-open;
31 + }
32 +}
33 +
34 +.sidebarClose div.wrap {
35 + left: 0;
36 +}
37 +
38 +.sidebarStatic .wrap {
39 + transition: none;
40 + left: 0;
41 + margin-left: $sidebar-width-open;
42 +}
43 +
44 +.content {
45 + position: relative;
46 + flex-grow: 1;
47 + // 20px for footer height
48 + padding: $content-padding $content-padding ($content-padding + 20px);
49 + background-color: $body-bg;
50 +
51 + @media (max-width: breakpoint-max(sm)) {
52 + padding: 20px 15px;
53 + }
54 +
55 + // hammers disallows text selection, allowing it for large screens
56 + @media (min-width: breakpoint-min(sm)) {
57 + user-select: auto !important;
58 + }
59 +}
60 +
61 +.contentFooter {
62 + position: absolute;
63 + bottom: 15px;
64 + color: $text-muted;
65 +}
1 +/* eslint-env mocha */
2 +/* eslint-disable padded-blocks, no-unused-expressions */
3 +
4 +import React from 'react';
5 +import { expect } from 'chai';
6 +import { render } from 'enzyme';
7 +import configureStore from 'redux-mock-store';
8 +import thunk from 'redux-thunk';
9 +import App from '../App';
10 +import Layout from './Layout';
11 +
12 +const middlewares = [thunk];
13 +const mockStore = configureStore(middlewares);
14 +const initialState = {};
15 +
16 +describe('Layout', () => {
17 + it('renders children correctly', () => {
18 + const store = mockStore(initialState);
19 +
20 + const wrapper = render(
21 + <App context={{ insertCss: () => {}, store }}>
22 + <Layout>
23 + <div className="child" />
24 + </Layout>
25 + </App>,
26 + );
27 + expect(wrapper.find('div.child').length).to.eq(1);
28 + });
29 +
30 +});
1 +{
2 + "name": "Layout",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Layout.js"
6 +}
1 +import React from 'react';
2 +import PropTypes from 'prop-types';
3 +import cx from 'classnames';
4 +import s from './Loader.module.scss';
5 +
6 +class Loader extends React.Component {
7 + static propTypes = {
8 + size: PropTypes.number.isRequired
9 + };
10 +
11 + static defaultProps = {
12 + size: 21
13 + };
14 +
15 + render() {
16 + return (
17 + <div className={cx(s.root, this.props.className)}>
18 + <i className="la la-spinner la-spin" style={{fontSize: this.props.size}}/>
19 + </div>
20 + );
21 + }
22 +}
23 +
24 +export default Loader;
1 +@import "../../styles/app";
2 +
3 +.root {
4 + width: 100%;
5 + height: 100%;
6 + display: flex;
7 + align-items: center;
8 + justify-content: center;
9 +
10 + i {
11 + font-size: 21px;
12 + margin: 0 $spacer;
13 + }
14 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "name": "Loader",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Loader.js"
6 +}
1 +import React from 'react';
2 +import {
3 + ButtonGroup,
4 + Button,
5 +} from 'reactstrap';
6 +import classnames from 'classnames';
7 +import NotificationsDemo from './notifications-demo/Notifications';
8 +import NewNotificationsDemo from './notifications-demo/NewNotifications';
9 +import MessagesDemo from './notifications-demo/Messages';
10 +import ProgressDemo from './notifications-demo/Progress';
11 +
12 +import s from './Notifications.module.scss';
13 +
14 +class Notifications extends React.Component {
15 + constructor(props) {
16 + super(props);
17 +
18 + this.state = {
19 + notificationsTabSelected: 1,
20 + newNotifications: null,
21 + isLoad: false,
22 + };
23 + }
24 +
25 + changeNotificationsTab(tab) {
26 + this.setState({
27 + notificationsTabSelected: tab,
28 + newNotifications: null,
29 + });
30 + }
31 +
32 + loadNotifications() {
33 + this.setState({
34 + isLoad: true,
35 + });
36 +
37 + setTimeout(() => {
38 + this.setState({
39 + newNotifications: (<NewNotificationsDemo />),
40 + isLoad: false,
41 + });
42 + }, 1500);
43 + }
44 +
45 + render() {
46 + let notificationsTab;
47 +
48 + switch (this.state.notificationsTabSelected) {
49 + case 1:
50 + notificationsTab = (<NotificationsDemo />);
51 + break;
52 + case 2:
53 + notificationsTab = (<MessagesDemo />);
54 + break;
55 + case 3:
56 + notificationsTab = (<ProgressDemo />);
57 + break;
58 + default:
59 + notificationsTab = (<NotificationsDemo />);
60 + break;
61 + }
62 + return (
63 + <section className={`${s.notifications} card navbar-notifications`}>
64 + <header className={[s.cardHeader, 'card-header'].join(' ')}>
65 + <div className="text-center mb-sm">
66 + <strong>You have 13 notifications</strong>
67 + </div>
68 + <ButtonGroup id="notification-buttons">
69 + <Button color="secondary" onClick={() => this.changeNotificationsTab(1)} active={this.state.notificationsTabSelected === 1}>Notifications</Button>
70 + <Button color="secondary" onClick={() => this.changeNotificationsTab(2)} active={this.state.notificationsTabSelected === 2}>Messages</Button>
71 + <Button color="secondary" onClick={() => this.changeNotificationsTab(3)} active={this.state.notificationsTabSelected === 3}>Progress</Button>
72 + </ButtonGroup>
73 + </header>
74 + {this.state.newNotifications || notificationsTab}
75 + <footer className={[s.cardFooter, 'text-sm', 'card-footer'].join(' ')}>
76 + <Button
77 + color="link"
78 + className={classnames({ disabled: this.state.isLoad }, s.btnNotificationsReload, 'btn-xs', 'float-right', 'py-0')}
79 + onClick={() => this.loadNotifications()}
80 + id="load-notifications-btn"
81 + >
82 + {this.state.isLoad ? <span><i className="la la-refresh la-spin" /> Loading...</span> : <i className="la la-refresh" />}
83 + </Button>
84 + <span className="fs-mini">Synced at: 21 Apr 2014 18:36</span>
85 + </footer>
86 + </section>
87 + );
88 + }
89 +}
90 +
91 +export default Notifications;
1 +@import '../../styles/app';
2 +
3 +.notifications {
4 + @include media-breakpoint-up(md) {
5 + width: 333px;
6 + }
7 +
8 + height: 100%;
9 + border: none;
10 +}
11 +
12 +.cardHeader {
13 + border-radius: 0;
14 +}
15 +
16 +.cardFooter {
17 + padding-top: 14px;
18 + padding-bottom: 14px;
19 +}
20 +
21 +.btnNotificationsReload {
22 + color: $navbar-link-color;
23 + outline: none;
24 +
25 + i::before {
26 + top: 2px;
27 + }
28 +}
1 +@import '../../../styles/app';
2 +
3 +.listGroup {
4 + display: block;
5 + height: 320px;
6 + overflow-y: scroll;
7 +
8 + .listGroupItem:first-child {
9 + border: none;
10 + }
11 +}
12 +
13 +.listGroupItem {
14 + transition: background-color 0.15s ease-in-out;
15 + text-decoration: none;
16 + color: $gray-700;
17 + border-left: none;
18 + border-right: none;
19 + display: block;
20 +
21 + :global .progress {
22 + transition: background 0.15s ease-in-out;
23 +
24 + &:hover {
25 + background: $black;
26 + }
27 + }
28 +
29 + &:hover {
30 + background-color: $list-group-hover-bg;
31 +
32 + :global .progress {
33 + background: $white !important;
34 + }
35 + }
36 +
37 + &:first-child {
38 + border-top: none;
39 + border-top-right-radius: 0;
40 + border-top-left-radius: 0;
41 + }
42 +
43 + &:last-child {
44 + border-bottom: none;
45 + border-bottom-right-radius: 0;
46 + border-bottom-left-radius: 0;
47 + }
48 +}
49 +
50 +.notificationIcon {
51 + margin-right: 1rem;
52 + float: left;
53 +
54 + @include clearfix;
55 +}
1 +import React from 'react';
2 +import {
3 + ListGroup,
4 + ListGroupItem,
5 +} from 'reactstrap';
6 +
7 +import a1 from '../../../images/people/a1.jpg';
8 +import a2 from '../../../images/people/a2.jpg';
9 +import a4 from '../../../images/people/a4.jpg';
10 +import a6 from '../../../images/people/a6.jpg';
11 +import avatar from '../../../images/avatar.png';
12 +
13 +import s from './ListGroup.module.scss'; // eslint-disable-line
14 +
15 +class MessagesDemo extends React.Component {
16 + render() {
17 + return (
18 + <ListGroup className={[s.listGroup, 'thin-scroll'].join(' ')}>
19 + <ListGroupItem className={[s.listGroupItem, 'bg-warning-light'].join(' ')}>
20 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
21 + <img className="rounded-circle" src={a2} alt="..." />
22 + <i className="status status-bottom bg-success" />
23 + </span>
24 + <time className="text-link help float-right">10 sec ago</time>
25 + <h6 className="m-0 fw-bold mb-1">Chris Gray</h6>
26 + <p className="deemphasize text-ellipsis m-0">Hey! What&apos;s up? So many times since we</p>
27 + </ListGroupItem>
28 + <ListGroupItem className={s.listGroupItem}>
29 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
30 + <img className="rounded-circle" src={avatar} alt="..." />
31 + <i className="status status-bottom bg-success" />
32 + </span>
33 + <time className="text-link help float-right">2 min ago</time>
34 + <h6 className="m-0 mb-1">Jamey Brownlow</h6>
35 + <p className="deemphasize text-ellipsis m-0">Good news coming tonight. Seems they agreed to proceed</p>
36 + </ListGroupItem>
37 + <ListGroupItem className={s.listGroupItem}>
38 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
39 + <img className="rounded-circle" src={a1} alt="..." />
40 + <i className="status status-bottom bg-warning" />
41 + </span>
42 + <time className="text-link help float-right">9 min ago</time>
43 + <h6 className="m-0 mb-1">Livia Walsh</h6>
44 + <p className="deemphasize text-ellipsis m-0">Check out my latest email plz!</p>
45 + </ListGroupItem>
46 + <ListGroupItem className={s.listGroupItem}>
47 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
48 + <img className="rounded-circle" src={avatar} alt="..." />
49 + <i className="status status-bottom bg-danger" />
50 + </span>
51 + <time className="text-link help float-right">12:56 AM</time>
52 + <h6 className="m-0 mb-1">Jaron Fitzroy</h6>
53 + <p className="deemphasize text-ellipsis m-0">What about summer break?</p>
54 + </ListGroupItem>
55 + <ListGroupItem className={s.listGroupItem}>
56 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
57 + <img className="rounded-circle" src={a4} alt="..." />
58 + <i className="status status-bottom bg-gray-light" />
59 + </span>
60 + <time className="text-link help float-right">Yesterday</time>
61 + <h6 className="m-0 mb-1">Mike Lewis</h6>
62 + <p className="deemphasize text-ellipsis m-0">Just ain&apos;t sure about the weekend now. 90% I&apos;ll make it.</p>
63 + </ListGroupItem>
64 + <ListGroupItem className={s.listGroupItem}>
65 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
66 + <img className="rounded-circle" src={a6} alt="..." />
67 + <i className="status status-bottom bg-success" />
68 + </span>
69 + <time className="text-link help float-right">Apr 23</time>
70 + <h6 className="m-0 mb-1">Freda Edison</h6>
71 + <p className="deemphasize text-ellipsis m-0">Hey what&apos;s up? Me and Monica going for a lunch somewhere. Wanna join?</p>
72 + </ListGroupItem>
73 + </ListGroup>
74 + );
75 + }
76 +}
77 +
78 +export default MessagesDemo;
1 +import React from 'react';
2 +import {
3 + ListGroup,
4 + ListGroupItem,
5 + Button,
6 +} from 'reactstrap';
7 +
8 +import s from './ListGroup.module.scss';
9 +
10 +import a3 from '../../../images/people/a3.jpg';
11 +import a5 from '../../../images/people/a5.jpg';
12 +import a6 from '../../../images/people/a6.jpg';
13 +
14 +class NewNotificationsDemo extends React.Component {
15 + render() {
16 + return (
17 + <ListGroup className={[s.listGroup, 'thin-scroll'].join(' ')}>
18 + <ListGroupItem className={`${s.listGroupItem} bg-attention`}>
19 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
20 + <i className="fa fa-check text-success fa-lg" />
21 + </span>
22 + <p className="m-0 overflow-hidden">
23 + 2 issues require your approval.
24 + {/* eslint-disable */}
25 + &nbsp;<a href="#">The Search Project</a> completed on time!
26 + {/* eslint-enable */}
27 + <time className="help-block m-0">
28 + just now
29 + </time>
30 + </p>
31 + </ListGroupItem>
32 + <ListGroupItem className={`${s.listGroupItem} bg-attention`}>
33 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
34 + <img className="rounded-circle" src={a6} alt="..." />
35 + </span>
36 + <p className="m-0 overflow-hidden">
37 + <button className="btn-link">Jeniffer Willington</button>has just endorsed you with 50 points!
38 + <time className="help-block m-0">
39 + 30 sec ago
40 + </time>
41 + </p>
42 + </ListGroupItem>
43 + <ListGroupItem className={s.listGroupItem}>
44 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
45 + <img className="rounded-circle" src={a3} alt="..." />
46 + </span>
47 + <p className="m-0 overflow-hidden">
48 + 1 new user just signed up! Check out
49 + {/* eslint-disable */}
50 + &nbsp;<a href="#">Monica Smith</a>'s account.
51 + {/* eslint-enable */}
52 + <time className="help-block m-0">
53 + 2 mins ago
54 + </time>
55 + </p>
56 + </ListGroupItem>
57 + <ListGroupItem className={s.listGroupItem}>
58 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
59 + <i className="glyphicon glyphicon-upload fa-lg" />
60 + </span>
61 + <p className="text-ellipsis m-0">
62 + 2.1.0-pre-alpha just released.
63 + <time className="help-block m-0">
64 + 5h ago
65 + </time>
66 + </p>
67 + </ListGroupItem>
68 + <ListGroupItem className={s.listGroupItem}>
69 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
70 + <i className="fa fa-bolt fa-lg" />
71 + </span>
72 + <p className="text-ellipsis m-0 overflow-hidden">
73 + Server load limited.
74 + <time className="help-block m-0">
75 + 7h ago
76 + </time>
77 + </p>
78 + </ListGroupItem>
79 + <ListGroupItem className={s.listGroupItem}>
80 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
81 + <img className="rounded-circle" src={a5} alt="..." />
82 + </span>
83 + <p className="m-0 overflow-hidden">
84 + {/* eslint-disable */}
85 + User <a href="#">Jeff</a> registered
86 + {/* eslint-enable */}
87 + &nbsp;&nbsp;
88 + <Button size="xs" color="success" className="mr-1">Allow</Button>
89 + <Button size="xs" color="danger">Deny</Button>
90 + <time className="help-block m-0">
91 + 12:18 AM
92 + </time>
93 + </p>
94 + </ListGroupItem>
95 + <ListGroupItem className={s.listGroupItem}>
96 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
97 + <i className="fa fa-shield fa-lg" />
98 + </span>
99 + <p className="m-0 overflow-hidden">
100 + {/* eslint-disable */}
101 + Instructions for changing your Envato Account password. Please
102 + check your account <a href="#">security page</a>.
103 + {/* eslint-enable */}
104 + <time className="help-block m-0">
105 + 12:18 AM
106 + </time>
107 + </p>
108 + </ListGroupItem>
109 + <ListGroupItem className={s.listGroupItem}>
110 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
111 + <span className="rounded bg-primary rounded-lg">
112 + <i className="fa fa-facebook text-white" />
113 + </span>
114 + </span>
115 + <p className="text-ellipsis m-0">
116 + New <strong>76</strong> facebook likes received.
117 + <time className="help-block m-0">
118 + 15 Apr 2014
119 + </time>
120 + </p>
121 + </ListGroupItem>
122 + <ListGroupItem className={s.listGroupItem}>
123 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
124 + <span className="circle circle-lg bg-gray-dark">
125 + <i className="fa fa-circle-o text-white" />
126 + </span>
127 + </span>
128 + <p className="text-ellipsis m-0">
129 + Dark matter detected.
130 + <time className="help-block m-0">
131 + 15 Apr 2014
132 + </time>
133 + </p>
134 + </ListGroupItem>
135 + </ListGroup>
136 + );
137 + }
138 +}
139 +
140 +export default NewNotificationsDemo;
1 +import React from 'react';
2 +import {
3 + ListGroup,
4 + ListGroupItem,
5 + Button,
6 +} from 'reactstrap';
7 +
8 +import s from './ListGroup.module.scss';
9 +
10 +import a3 from '../../../images/people/a3.jpg';
11 +import a5 from '../../../images/people/a5.jpg';
12 +
13 +class NotificationsDemo extends React.Component {
14 + render() {
15 + return (
16 + <ListGroup className={[s.listGroup, 'thin-scroll'].join(' ')}>
17 + <ListGroupItem className={s.listGroupItem}>
18 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
19 + <img className="rounded-circle" src={a3} alt="..." />
20 + </span>
21 + <p className="m-0 overflow-hidden">
22 + 1 new user just signed up! Check out
23 + {/* eslint-disable */}
24 + &nbsp;<a href="#">Monica Smith</a>'s account.
25 + {/* eslint-enable */}
26 + <time className="help-block m-0">
27 + 2 mins ago
28 + </time>
29 + </p>
30 + </ListGroupItem>
31 + <ListGroupItem className={s.listGroupItem}>
32 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
33 + <i className="glyphicon glyphicon-upload fa-lg" />
34 + </span>
35 + <p className="text-ellipsis m-0">
36 + 2.1.0-pre-alpha just released.
37 + <time className="help-block m-0">
38 + 5h ago
39 + </time>
40 + </p>
41 + </ListGroupItem>
42 + <ListGroupItem className={s.listGroupItem}>
43 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
44 + <i className="fa fa-bolt fa-lg" />
45 + </span>
46 + <p className="text-ellipsis m-0 overflow-hidden">
47 + Server load limited.
48 + <time className="help-block m-0">
49 + 7h ago
50 + </time>
51 + </p>
52 + </ListGroupItem>
53 + <ListGroupItem className={s.listGroupItem}>
54 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
55 + <img className="rounded-circle" src={a5} alt="..." />
56 + </span>
57 + <p className="m-0 overflow-hidden">
58 + {/* eslint-disable */}
59 + User <a href="#">Jeff</a> registered
60 + {/* eslint-enable */}
61 + &nbsp;&nbsp;
62 + <Button size="xs" color="success" className="mr-1">Allow</Button>
63 + <Button size="xs" color="danger">Deny</Button>
64 + <time className="help-block m-0">
65 + 12:18 AM
66 + </time>
67 + </p>
68 + </ListGroupItem>
69 + <ListGroupItem className={s.listGroupItem}>
70 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
71 + <i className="fa fa-shield fa-lg" />
72 + </span>
73 + <p className="m-0 overflow-hidden">
74 + {/* eslint-disable */}
75 + Instructions for changing your Envato Account password. Please
76 + check your account <a href="#">security page</a>.
77 + {/* eslint-enable */}
78 + <time className="help-block m-0">
79 + 12:18 AM
80 + </time>
81 + </p>
82 + </ListGroupItem>
83 + <ListGroupItem className={s.listGroupItem}>
84 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
85 + <span className="rounded bg-primary rounded-lg">
86 + <i className="fa fa-facebook text-white" />
87 + </span>
88 + </span>
89 + <p className="text-ellipsis m-0">
90 + New <strong>76</strong> facebook likes received.
91 + <time className="help-block m-0">
92 + 15 Apr 2014
93 + </time>
94 + </p>
95 + </ListGroupItem>
96 + <ListGroupItem className={s.listGroupItem}>
97 + <span className={[s.notificationIcon, 'thumb-sm'].join(' ')}>
98 + <span className="circle circle-lg bg-gray-dark">
99 + <i className="fa fa-circle-o text-white" />
100 + </span>
101 + </span>
102 + <p className="text-ellipsis m-0">
103 + Dark matter detected.
104 + <time className="help-block m-0">
105 + 15 Apr 2014
106 + </time>
107 + </p>
108 + </ListGroupItem>
109 + </ListGroup>
110 + );
111 + }
112 +}
113 +
114 +export default NotificationsDemo;
1 +import React from 'react';
2 +import {
3 + ListGroup,
4 + ListGroupItem,
5 + Progress,
6 + UncontrolledTooltip,
7 +} from 'reactstrap';
8 +
9 +import s from './ListGroup.module.scss'; // eslint-disable-line
10 +
11 +class ProgressDemo extends React.Component {
12 + render() {
13 + return (
14 + <ListGroup className={[s.listGroup, 'thin-scroll'].join(' ')}>
15 + <ListGroupItem className={s.listGroupItem}>
16 + <span className="text-muted float-right">60%</span>
17 + <h6 className="m-0 mb-1 text-gray">
18 + <strong>Urgent:</strong>
19 + &nbsp;Rails 4.1.0 upgrade
20 + </h6>
21 + <Progress className={['m-0'].join(' ')} color="primary" value="60" />
22 + <span className="help-block">3 notes added by James 2h ago...</span>
23 + </ListGroupItem>
24 + <ListGroupItem className={s.listGroupItem}>
25 + <span className="text-muted float-right">83%</span>
26 + <h6 className="m-0 mb-1 text-gray">
27 + <strong>Primary:</strong>
28 + &nbsp;Sing Web App
29 + </h6>
30 + <Progress className={['progress-sm', 'm-0'].join(' ')} color="success" value="83" />
31 + <span className="help-block">verifying stable probability status</span>
32 + </ListGroupItem>
33 + <ListGroupItem className={s.listGroupItem}>
34 + <span className="text-muted float-right">44%</span>
35 + <h6 className="m-0 mb-1">
36 + <span className="circle bg-gray-dark text-warning" id="TooltipQuestion">
37 + <i className="fa fa-question" />
38 + </span>
39 + <UncontrolledTooltip placement="bottom" target="TooltipQuestion">
40 + 2 issues require your attention
41 + </UncontrolledTooltip>
42 + &nbsp;
43 + Finish The Road to Hell Song
44 + </h6>
45 + <Progress className={['progress-sm', 'm-0'].join(' ')} color="gray-dark" value="44" />
46 + <span className="help-block">last update: 2h ago</span>
47 + </ListGroupItem>
48 + <ListGroupItem className={s.listGroupItem}>
49 + <span className="text-muted float-right">86%</span>
50 + <h6 className="m-0 mb-1 deemphasize text-gray">
51 + Complete project planning
52 + </h6>
53 + <Progress className={['progress-xs', 'm-0'].join(' ')} color="danger" value="86" />
54 + <span className="help-block">no, no way this is not working...</span>
55 + </ListGroupItem>
56 + <ListGroupItem className={s.listGroupItem}>
57 + <span className="text-muted float-right">100%</span>
58 + <h6 className="m-0 mb-1 deemphasize text-gray">
59 + <strong>Completed:</strong>
60 + &nbsp;Instruct newbies on coding standards
61 + </h6>
62 + <Progress className={['progress-xs', 'm-0'].join(' ')} color="primary" value="100" />
63 + <span className="help-block">last update: April 22, 2014 2:36 pm</span>
64 + </ListGroupItem>
65 + </ListGroup>
66 + );
67 + }
68 +}
69 +
70 +export default ProgressDemo;
1 +{
2 + "name": "Notifications",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Notifications"
6 +}
1 +import React, { Component } from 'react';
2 +import PropTypes from 'prop-types';
3 +import { NavLink, withRouter } from 'react-router-dom';
4 +import { Collapse, Badge } from 'reactstrap';
5 +import { Route } from 'react-router';
6 +import classnames from 'classnames';
7 +
8 +import s from './LinksGroup.module.scss';
9 +
10 +class LinksGroup extends Component {
11 + /* eslint-disable */
12 + static propTypes = {
13 + header: PropTypes.node.isRequired,
14 + link: PropTypes.string.isRequired,
15 + childrenLinks: PropTypes.array,
16 + iconName: PropTypes.string,
17 + className: PropTypes.string,
18 + badge: PropTypes.string,
19 + label: PropTypes.string,
20 + activeItem: PropTypes.string,
21 + isHeader: PropTypes.bool,
22 + index: PropTypes.string,
23 + deep: PropTypes.number,
24 + onActiveSidebarItemChange: PropTypes.func,
25 + labelColor: PropTypes.string,
26 + exact: PropTypes.bool
27 + };
28 + /* eslint-enable */
29 +
30 + static defaultProps = {
31 + link: '',
32 + childrenLinks: null,
33 + className: '',
34 + isHeader: false,
35 + deep: 0,
36 + activeItem: '',
37 + label: '',
38 + exact: true
39 + };
40 +
41 + constructor(props) {
42 + super(props);
43 + this.togglePanelCollapse = this.togglePanelCollapse.bind(this);
44 +
45 + this.state = {
46 + headerLinkWasClicked: true,
47 + };
48 + }
49 +
50 + togglePanelCollapse(link) {
51 + this.props.onActiveSidebarItemChange(link);
52 + this.setState({
53 + headerLinkWasClicked: !this.state.headerLinkWasClicked ||
54 + (this.props.activeItem && !this.props.activeItem.includes(this.props.index)),
55 + });
56 + }
57 +
58 + render() {
59 + const isOpen = this.props.activeItem &&
60 + this.props.activeItem.includes(this.props.index) &&
61 + this.state.headerLinkWasClicked;
62 +
63 + const {exact} = this.props.exact;
64 +
65 + if (!this.props.childrenLinks) {
66 + if (this.props.isHeader) {
67 + return (
68 + <li className={classnames('link-wrapper', s.headerLink, this.props.className)}>
69 + <NavLink
70 + to={this.props.link}
71 + activeClassName={s.headerLinkActive}
72 + exact={exact}
73 + target={this.props.target}
74 + >
75 + <span className={classnames('icon', s.icon)}>
76 + <i className={`fi ${this.props.iconName}`} />
77 + </span>
78 + {this.props.header} {this.props.label && <sup className={`${s.headerLabel} text-${this.props.labelColor || 'warning'}`}>{this.props.label}</sup>}
79 + {this.props.badge && <Badge className={s.badge} color="warning" pill>9</Badge>}
80 + </NavLink>
81 + </li>
82 + );
83 + }
84 + return (
85 + <li>
86 + <NavLink
87 + to={this.props.link}
88 + activeClassName={s.headerLinkActive}
89 + style={{ paddingLeft: `${26 + (10 * (this.props.deep - 1))}px` }}
90 + onClick={(e) => {
91 + // able to go to link is not available(for Demo)
92 + if (this.props.link.includes('menu')) {
93 + e.preventDefault();
94 + }
95 + }}
96 + exact={exact}
97 + >
98 + {this.props.header} {this.props.label && <sup className={`${s.headerLabel} text-${this.props.labelColor || 'warning'}`}>{this.props.label}</sup>}
99 + </NavLink>
100 + </li>
101 + );
102 + }
103 + /* eslint-disable */
104 + return (
105 + <Route
106 + path={this.props.link}
107 + children={(params) => {
108 + const { match } = params;
109 + return (
110 + <li className={classnames('link-wrapper', { [s.headerLink]: this.props.isHeader }, this.props.className)}>
111 + <a className={classnames({ [s.headerLinkActive]: match }, { [s.collapsed]: isOpen }, "d-flex")}
112 + style={{ paddingLeft: `${this.props.deep == 0 ? 50 : 26 + 10 * (this.props.deep - 1)}px` }}
113 + onClick={() => this.togglePanelCollapse(this.props.link)}
114 + >
115 + {this.props.isHeader ?
116 + <span className={classnames('icon', s.icon)}>
117 + <i className={`fi ${this.props.iconName}`} />
118 + </span> : null
119 + }
120 + {this.props.header} {this.props.label && <sup className={`${s.headerLabel} text-${this.props.labelColor || 'warning'} ml-1`}>{this.props.label}</sup>}
121 + <b className={['fa fa-angle-left', s.caret].join(' ')} />
122 + </a>
123 + {/* eslint-enable */}
124 + <Collapse className={s.panel} isOpen={isOpen}>
125 + <ul>
126 + {this.props.childrenLinks &&
127 + this.props.childrenLinks.map((child, ind) =>
128 + <LinksGroup
129 + onActiveSidebarItemChange={this.props.onActiveSidebarItemChange}
130 + activeItem={this.props.activeItem}
131 + header={child.header}
132 + link={child.link}
133 + index={child.index}
134 + childrenLinks={child.childrenLinks}
135 + deep={this.props.deep + 1}
136 + key={ind} // eslint-disable-line
137 + />,
138 + )}
139 + </ul>
140 + </Collapse>
141 + </li>
142 + );
143 + }}
144 + />
145 + );
146 + }
147 +}
148 +
149 +export default withRouter(LinksGroup);
1 +@import '../../../styles/app';
2 +
3 +.headerLink {
4 + overflow-x: hidden;
5 +
6 + @media (min-width: map_get($grid-breakpoints, lg)) and (min-height: $screen-lg-height), (max-width: map_get($grid-breakpoints, md) - 1px) {
7 + font-size: 13px;
8 + }
9 +
10 + a {
11 + display: block;
12 + color: var(--sidebar-color);
13 + text-decoration: none;
14 + cursor: pointer;
15 +
16 + &:hover {
17 + text-decoration: none;
18 + color: inherit;
19 + }
20 + }
21 +
22 + &:last-child > a {
23 + border-bottom: 1px solid $sidebar-item-border-color;
24 + }
25 +
26 + > a {
27 + position: relative;
28 + align-items: center;
29 + padding-left: 50px;
30 + line-height: 35px;
31 + border-top: 1px solid $sidebar-item-border-color;
32 +
33 + &:hover {
34 + background-color: var(--sidebar-item-hover-bg-color);
35 + }
36 +
37 + > i {
38 + margin-right: 7px;
39 + }
40 +
41 + @media (min-width: map_get($grid-breakpoints, lg)) and (min-height: $screen-lg-height), (max-width: map_get($grid-breakpoints, md) - 1px) {
42 + line-height: 55px;
43 + }
44 + }
45 +
46 + .icon {
47 + font-size: $font-size-larger;
48 + display: block;
49 + position: absolute;
50 + top: 3px;
51 + left: 11px;
52 + width: 28px;
53 + height: 28px;
54 + line-height: 28px;
55 + text-align: center;
56 + opacity: .7;
57 +
58 + @media (min-width: map_get($grid-breakpoints, lg)) and (min-height: $screen-lg-height), (max-width: map_get($grid-breakpoints, md) - 1px) {
59 + top: 12px;
60 + }
61 + }
62 +
63 + .badge {
64 + float: right;
65 + line-height: 8px;
66 + margin-top: 9px;
67 + margin-right: 15px;
68 +
69 + @media (min-width: map_get($grid-breakpoints, lg)) and (min-height: $screen-lg-height), (max-width: map_get($grid-breakpoints, md) - 1px) {
70 + margin-top: 16px;
71 + }
72 + }
73 +
74 + a.headerLinkActive {
75 + color: $sidebar-item-active-color;
76 + font-weight: $font-weight-normal;
77 +
78 + &:hover {
79 + color: $sidebar-item-active-color;
80 + }
81 +
82 + .icon {
83 + border-radius: 50%;
84 + background-color: $sidebar-item-active-color;
85 + opacity: 1;
86 +
87 + i {
88 + color: var(--sidebar-bg-color);
89 + }
90 + }
91 + }
92 +}
93 +
94 +.headerLabel {
95 + font-weight: 600;
96 +}
97 +
98 +.collapsed .caret {
99 + transform: rotate(-90deg);
100 +}
101 +
102 +.caret {
103 + display: flex;
104 + align-items: center;
105 + margin-left: auto;
106 + margin-right: 15px;
107 +
108 + @include transition(transform 0.3s ease-in-out);
109 +}
110 +
111 +.panel {
112 + border: none;
113 + box-shadow: none;
114 + margin: 0;
115 + border-radius: 0;
116 + background-color: var(--sidebar-panel-bg-color);
117 +
118 + a.headerLinkActive {
119 + font-weight: $font-weight-semi-bold;
120 + color: var(--sidebar-color);
121 +
122 + &:hover {
123 + color: var(--sidebar-color);
124 + }
125 + }
126 +
127 + ul {
128 + background: var(--sidebar-action-bg);
129 + padding: $spacer;
130 +
131 + li {
132 + list-style: none;
133 + }
134 +
135 + a {
136 + padding: 10px 20px 10px 26px;
137 + font-size: $font-size-mini;
138 +
139 + &:hover {
140 + background-color: var(--sidebar-item-hover-bg-color);
141 + }
142 + }
143 + }
144 +}
145 +
This diff is collapsed. Click to expand it.
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 + border-right: $sidebar-border;
10 + background-color: var(--sidebar-bg-color);
11 + color: var(--sidebar-color);
12 + overflow-y: auto;
13 +
14 + @include scroll-bar($sidebar-scrollbar-bg);
15 +}
16 +
17 +.logo {
18 + margin: 15px 0;
19 + font-size: 18px;
20 + width: 100%;
21 + font-weight: $font-weight-thin;
22 + text-align: center;
23 + transition: width $sidebar-transition-time ease-in-out;
24 +
25 + a {
26 + color: var(--logo-color);
27 + padding: 0 5px;
28 + text-decoration: none;
29 + white-space: nowrap;
30 + }
31 +}
32 +
33 +.sidebarClose .logo {
34 + width: 50px;
35 +}
36 +
37 +.staticSidebar .logo {
38 + width: 100%;
39 + transition: none;
40 +}
41 +
42 +.nav {
43 + padding: 30px 0 10px;
44 + overflow-y: auto;
45 + overflow-x: hidden;
46 +}
47 +
48 +.navTitle {
49 + margin: 35px 0 5px 11px;
50 + font-size: $font-size-larger;
51 + transition: opacity $sidebar-transition-time ease-in-out;
52 + color: var(--sidebar-nav-title-color);
53 +
54 + @media (min-width: breakpoint-min(lg)) {
55 + opacity: 1;
56 + }
57 +}
58 +
59 +.sidebarClose .navTitle {
60 + opacity: 0;
61 +}
62 +
63 +.staticSidebar .navTitle {
64 + opacity: 1;
65 + transition: none;
66 +}
67 +
68 +.actionLink {
69 + color: #aaa;
70 + float: right;
71 + margin-right: 15px;
72 + margin-top: -1px;
73 +}
74 +
75 +.labelName {
76 + opacity: 1;
77 + transition: opacity $sidebar-transition-time ease-in-out;
78 +}
79 +
80 +.sidebarClose .labelName {
81 + opacity: 0;
82 +}
83 +
84 +.staticSidebar .labelName {
85 + transition: none;
86 + opacity: 1;
87 +}
88 +
89 +.glyphiconSm {
90 + font-size: 9px;
91 +}
92 +
93 +.sidebarLabels {
94 + list-style-type: none;
95 + padding: 11px;
96 + padding-right: 15px;
97 +
98 + > li + li {
99 + margin-top: 10px;
100 + }
101 +
102 + li > a {
103 + font-size: $font-size-mini;
104 + color: var(--sidebar-color);
105 + text-decoration: none;
106 +
107 + > i {
108 + font-size: 11px;
109 + vertical-align: 1px;
110 + transition: margin-left $sidebar-transition-time ease-in-out;
111 + }
112 + }
113 +}
114 +
115 +.sidebarClose {
116 + .sidebarLabels > li > a > i {
117 + margin-left: 8px;
118 + transition: margin-left $sidebar-transition-time ease-in-out;
119 + }
120 +}
121 +
122 +.staticSidebar {
123 + .sidebarLabels > li > a > i {
124 + transition: none;
125 + margin-left: 0;
126 + }
127 +}
128 +
129 +.sidebarAlerts {
130 + margin-bottom: $spacer * 2;
131 + transition: opacity $sidebar-transition-time ease-in-out;
132 + opacity: 1;
133 +}
134 +
135 +.sidebarClose .sidebarAlerts {
136 + opacity: 0;
137 +}
138 +
139 +.staticSidebar .sidebarAlerts {
140 + opacity: 1;
141 + transition: none;
142 +}
143 +
144 +.sidebarAlert {
145 + background: transparent;
146 + margin-bottom: 0;
147 + padding: 0.5rem 11px;
148 + padding-right: 15px;
149 +}
150 +
151 +.sidebarProgress {
152 + background-color: var(--sidebar-progress-bg-color);
153 +}
154 +
155 +.groupTitle {
156 + margin-bottom: 15px;
157 +}
1 +{
2 + "name": "Sidebar",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Sidebar.js"
6 +}
1 +import React from 'react';
2 +import PropTypes from 'prop-types';
3 +import ReactDOM from 'react-dom';
4 +
5 +const Skycons = require('skycons')(window || {});
6 +
7 +class Skycon extends React.Component {
8 + static propTypes = {
9 + color: PropTypes.string.isRequired,
10 + autoPlay: PropTypes.bool,
11 + icon: PropTypes.oneOf([ // eslint-disable-line
12 + 'CLEAR_DAY',
13 + 'CLEAR_NIGHT',
14 + 'PARTLY_CLOUDY_DAY',
15 + 'PARTLY_CLOUDY_NIGHT',
16 + 'CLOUDY',
17 + 'RAIN',
18 + 'SLEET',
19 + 'SNOW',
20 + 'WIND',
21 + 'FOG',
22 + ]),
23 + width: PropTypes.string.isRequired,
24 + height: PropTypes.string.isRequired,
25 + };
26 +
27 + static defaultProps = {
28 + color: null,
29 + autoPlay: true,
30 + width: '100%',
31 + height: '100%',
32 + };
33 +
34 + constructor(props) {
35 + super(props);
36 +
37 + this.state = {
38 + skycons: new Skycons({ color: this.props.color }),
39 + };
40 + }
41 +
42 + componentDidMount() {
43 + const { skycons } = this.state;
44 + skycons.add(ReactDOM.findDOMNode(this), Skycons[this.props.icon]); // eslint-disable-line
45 +
46 + if (this.props.autoPlay) {
47 + skycons.play();
48 + }
49 + }
50 +
51 + componentWillReceiveProps(nextProps) {
52 + this.state.skycons.set(ReactDOM.findDOMNode(this), Skycons[nextProps.icon]); // eslint-disable-line
53 + }
54 +
55 + play() {
56 + this.state.skycons.play();
57 + }
58 +
59 + pause() {
60 + this.state.skycons.pause();
61 + }
62 +
63 + render() {
64 + const {
65 + ...restPops
66 + } = this.props;
67 +
68 + return (
69 + <canvas width={this.props.width} height={this.props.height} {...restPops} />
70 + );
71 + }
72 +}
73 +
74 +export default Skycon;
1 +{
2 + "name": "Skycons",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Skycons.js"
6 +}
1 +import React from 'react';
2 +
3 +import $ from 'jquery';
4 +import PropTypes from 'prop-types';
5 +
6 +/* eslint-disable */
7 +import 'imports-loader?jQuery=jquery,this=>window!jquery-sparkline';
8 +/* eslint-enable */
9 +
10 +class Sparklines extends React.Component {
11 + static propTypes = {
12 + data: PropTypes.arrayOf(
13 + PropTypes.arrayOf(PropTypes.number),
14 + ),
15 + options: PropTypes.oneOfType([
16 + PropTypes.arrayOf(
17 + PropTypes.shape({
18 + type: PropTypes.string.isRequired,
19 + }),
20 + ),
21 + PropTypes.shape({
22 + type: PropTypes.string.isRequired,
23 + }),
24 + ]),
25 + };
26 +
27 + static defaultProps = {
28 + data: [],
29 + options: {},
30 + };
31 +
32 + componentDidMount() {
33 + this.initSparkline();
34 + }
35 +
36 + initSparkline() {
37 + const $el = $(this.sparklineRef);
38 +
39 + const model = $.type(this.data) === 'string' ?
40 + this.props.data.replace(/(^,)|(,$)/g, '')
41 + : this.props.data;
42 + const options = this.props.options;
43 +
44 + if ($.type(model) === 'array' && $.type(options) === 'array') {
45 + options.forEach((singleOptions, i) => {
46 + if (i === 0) {
47 + $el.sparkline(model[i], singleOptions);
48 + } else { // set composite for next calls
49 + $el.sparkline(model[i], $.extend({ composite: true }, singleOptions));
50 + }
51 + });
52 + } else {
53 + const data = $.type(model) === 'array' ? model : model.split(',');
54 + $el.sparkline(data, options);
55 + }
56 + }
57 +
58 + render() {
59 + return (
60 + <div
61 + className="sparkline" ref={(r) => {
62 + this.sparklineRef = r;
63 + }}
64 + />
65 + );
66 + }
67 +}
68 +
69 +export default Sparklines;
1 +{
2 + "name": "Sparklines",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Sparklines.js"
6 +}
1 +import React from 'react';
2 +import PropTypes from 'prop-types';
3 +import jQuery from 'jquery';
4 +import { UncontrolledTooltip } from 'reactstrap';
5 +import 'imports-loader?window.jQuery=jquery,this=>window!widgster'; // eslint-disable-line
6 +import s from './Widget.module.scss';
7 +import Loader from '../Loader'; // eslint-disable-line css-modules/no-unused-class
8 +
9 +class Widget extends React.Component {
10 + static propTypes = {
11 + title: PropTypes.node,
12 + className: PropTypes.string,
13 + children: PropTypes.oneOfType([
14 + PropTypes.arrayOf(PropTypes.node),
15 + PropTypes.node,
16 + ]),
17 + close: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
18 + fullscreen: PropTypes.bool,
19 + collapse: PropTypes.bool,
20 + refresh: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
21 + settings: PropTypes.bool,
22 + settingsInverse: PropTypes.bool,
23 + tooltipPlacement: PropTypes.string,
24 + showTooltip: PropTypes.bool,
25 + bodyClass: PropTypes.string,
26 + customControls: PropTypes.node,
27 + options: PropTypes.object, //eslint-disable-line,
28 + fetchingData: PropTypes.bool
29 + };
30 +
31 + static defaultProps = {
32 + title: null,
33 + className: '',
34 + children: [],
35 + close: false,
36 + fullscreen: false,
37 + collapse: false,
38 + refresh: false,
39 + settings: false,
40 + settingsInverse: false,
41 + tooltipPlacement: 'bottom',
42 + showTooltip: false,
43 + bodyClass: '',
44 + customControls: null,
45 + options: {},
46 + fetchingData: false
47 + };
48 +
49 + constructor(prop) {
50 + super(prop);
51 + this.state = {
52 + randomId: Math.floor(Math.random() * 100),
53 + };
54 + }
55 +
56 + componentDidMount() {
57 + const options = this.props.options;
58 + options.bodySelector = '.widget-body';
59 + jQuery(this.el).widgster(options);
60 + }
61 +
62 + render() {
63 + const {
64 + title,
65 + className,
66 + children,
67 + close,
68 + fullscreen,
69 + collapse,
70 + refresh,
71 + settings,
72 + settingsInverse,
73 + tooltipPlacement,
74 + showTooltip,
75 + bodyClass,
76 + customControls,
77 + fetchingData,
78 + options, //eslint-disable-line
79 + ...attributes
80 + } = this.props;
81 + const randomId = this.state.randomId;
82 + const mainControls = !!(close || fullscreen || collapse || refresh || settings || settingsInverse);
83 + return (
84 + <section
85 + className={['widget', s.widget, className].join(' ')}
86 + ref={(widget) => { this.el = widget; }} {...attributes}
87 + >
88 + {
89 + title && (
90 + typeof title === 'string'
91 + ? <h5 className={s.title}>{title}</h5>
92 + : <header className={s.title}>{title}</header>
93 + )
94 + }
95 + {
96 + !customControls && mainControls && (
97 + <div className={`${s.widgetControls} widget-controls`}>
98 + {settings && (
99 + <button><i className="la la-cog" /></button>
100 + )}
101 + {settingsInverse && (
102 + <button className={`bg-gray-transparent ${s.inverse}`}><i
103 + className="la la-cog text-white"
104 + /></button>
105 + )}
106 + {refresh && (
107 + <button data-widgster="load" id={`reloadId-${randomId}`}>
108 + {typeof refresh === 'string' ?
109 + <strong className="text-gray-light">{refresh}</strong> :
110 + <i className="la la-refresh" />}
111 + {showTooltip && (
112 + <UncontrolledTooltip
113 + placement={tooltipPlacement}
114 + target={`reloadId-${randomId}`}
115 + >Reload</UncontrolledTooltip>
116 + )}
117 + </button>
118 + )}
119 + {fullscreen && (
120 + <button data-widgster="fullscreen" id={`fullscreenId-${randomId}`}>
121 + <i className="glyphicon glyphicon-resize-full" />
122 + {showTooltip && (
123 + <UncontrolledTooltip
124 + placement={tooltipPlacement}
125 + target={`fullscreenId-${randomId}`}
126 + >Fullscreen</UncontrolledTooltip>
127 + )}
128 + </button>
129 + )}
130 + {fullscreen && (
131 + <button data-widgster="restore" id={`restoreId-${randomId}`}>
132 + <i className="glyphicon glyphicon-resize-small" />
133 + {showTooltip && (
134 + <UncontrolledTooltip
135 + placement={tooltipPlacement}
136 + target={`restoreId-${randomId}`}
137 + >Restore</UncontrolledTooltip>
138 + )}
139 + </button>
140 + )}
141 + {collapse && (
142 + <span>
143 + <button data-widgster="collapse" id={`collapseId-${randomId}`}>
144 + <i className="la la-angle-down" />
145 + {showTooltip && (
146 + <UncontrolledTooltip
147 + placement={tooltipPlacement}
148 + target={`collapseId-${randomId}`}
149 + >Collapse</UncontrolledTooltip>
150 + )}
151 + </button>
152 + </span>
153 + )}
154 + {collapse && (
155 + <span>
156 + <button data-widgster="expand" id={`expandId-${randomId}`}>
157 + <i className="la la-angle-up" />
158 + {showTooltip && (
159 + <UncontrolledTooltip
160 + placement={tooltipPlacement}
161 + target={`expandId-${randomId}`}
162 + >Expand</UncontrolledTooltip>
163 + )}
164 + </button>
165 + </span>
166 + )}
167 +
168 + {close && (
169 + <button data-widgster="close" id={`closeId-${randomId}`}>
170 + {typeof close === 'string' ?
171 + <strong className="text-gray-light">{close}</strong> :
172 + <i className="la la-remove" />}
173 + {showTooltip && (
174 + <UncontrolledTooltip
175 + placement={tooltipPlacement}
176 + target={`closeId-${randomId}`}
177 + >Close</UncontrolledTooltip>
178 + )}
179 + </button>
180 + )}
181 + </div>
182 + )}
183 + {
184 + customControls && (
185 + <div className={`${s.widgetControls} widget-controls`}>
186 + {customControls}
187 + </div>
188 + )
189 + }
190 + <div className={`${s.widgetBody} widget-body ${bodyClass}`}>
191 + {fetchingData ? <Loader className={s.widgetLoader} size={40}/> : children}
192 + </div>
193 + </section>
194 + );
195 + }
196 +}
197 +
198 +export default Widget;
1 +@import '../../styles/app';
2 +
3 +.title {
4 + margin-top: 0;
5 + color: $widget-title-color;
6 +
7 + @include clearfix();
8 +}
9 +
10 +:global .widget.collapsed {
11 + min-height: unset;
12 +}
13 +
14 +.widget {
15 + display: block;
16 + position: relative;
17 + margin-bottom: $grid-gutter-width;
18 + padding: $widget-padding-vertical $widget-padding-horizontal;
19 + background: $widget-bg-color;
20 + border-radius: $widget-border-radius;
21 + box-shadow: var(--widget-shadow);
22 + min-height: 150px;
23 +
24 + > header {
25 + margin: (-$widget-padding-vertical) (-$widget-padding-horizontal);
26 + padding: $widget-padding-vertical $widget-padding-horizontal;
27 +
28 + h1,
29 + h2,
30 + h3,
31 + h4,
32 + h5,
33 + h6 {
34 + margin: 0;
35 + }
36 + }
37 +
38 + :global {
39 + .loader {
40 + position: absolute;
41 + top: 0;
42 + bottom: 0;
43 + left: 0;
44 + right: 0;
45 +
46 + .spinner {
47 + position: absolute;
48 + top: 50%;
49 + width: 100%; //ie fix
50 + margin-top: -10px;
51 + font-size: 20px;
52 + text-align: center;
53 + }
54 + }
55 +
56 + .widget-body.p-0 {
57 + margin: $widget-padding-vertical (-$widget-padding-horizontal) (-$widget-padding-vertical);
58 +
59 + + footer {
60 + margin-top: $widget-padding-vertical;
61 + }
62 + }
63 + }
64 +
65 + &:global.bg-transparent {
66 + box-shadow: none;
67 + }
68 +}
69 +
70 +.widgetBody {
71 + @include clearfix();
72 +
73 + > footer {
74 + margin: $spacer/2 (-$widget-padding-horizontal) (-$widget-padding-vertical);
75 + padding: 10px 20px;
76 + }
77 +}
78 +
79 +.widgetControls + .widgetBody {
80 + margin-top: $widget-padding-vertical;
81 +}
82 +
83 +.widgetControls,
84 +:global(.widget-controls) {
85 + position: absolute;
86 + z-index: 1;
87 + top: 0;
88 + right: 0;
89 + padding: 14px;
90 + font-size: $font-size-sm;
91 +
92 + button {
93 + padding: 1px 4px;
94 + border-radius: 4px;
95 + color: rgba($black, 0.4);
96 + background: transparent;
97 + border: none;
98 +
99 + @include transition(color 0.15s ease-in-out);
100 +
101 + &:hover {
102 + color: rgba($black, 0.1);
103 + text-decoration: none;
104 + }
105 +
106 + &:active,
107 + &:focus {
108 + outline: none;
109 + }
110 +
111 + :global {
112 + .la {
113 + position: relative;
114 + top: 2px;
115 + }
116 +
117 + .glyphicon {
118 + font-size: 0.7rem;
119 + }
120 + }
121 + }
122 +}
123 +
124 +.inverse {
125 + top: 2px;
126 + position: relative;
127 + margin-left: 3px;
128 +
129 + :global {
130 + .glyphicon {
131 + vertical-align: baseline;
132 + }
133 + }
134 +}
135 +
136 +:global {
137 + .widget-image {
138 + position: relative;
139 + overflow: hidden;
140 + margin: (-$widget-padding-vertical) (-$widget-padding-horizontal);
141 + border-radius: $border-radius;
142 +
143 + > img {
144 + max-width: 100%;
145 + border-radius: $border-radius $border-radius 0 0;
146 + transition: transform 0.15s ease;
147 + }
148 +
149 + &:hover > img {
150 + transform: scale(1.1, 1.1);
151 + }
152 +
153 + .title {
154 + position: absolute;
155 + top: 0;
156 + left: 0;
157 + margin: 20px;
158 + }
159 +
160 + .info {
161 + position: absolute;
162 + top: 0;
163 + right: 0;
164 + margin: 20px;
165 + }
166 + }
167 +
168 + .widget-footer-bottom {
169 + position: absolute;
170 + bottom: 0;
171 + width: 100%;
172 + }
173 +
174 + .widget-sm {
175 + height: 230px;
176 + }
177 +
178 + .widget-md {
179 + height: 373px;
180 + }
181 +
182 + .widget-padding-md {
183 + padding: $widget-padding-vertical $widget-padding-horizontal;
184 + }
185 +
186 + .widget-padding-lg {
187 + padding: $widget-padding-vertical*2 $widget-padding-horizontal*2;
188 + }
189 +
190 + .widget-body-container {
191 + position: relative;
192 + height: 100%;
193 + }
194 +
195 + .widget-top-overflow,
196 + .widget-middle-overflow {
197 + position: relative;
198 + margin: 0 (-$widget-padding-horizontal);
199 +
200 + > img {
201 + max-width: 100%;
202 + }
203 + }
204 +
205 + .widget-top-overflow {
206 + margin-top: (-$widget-padding-vertical);
207 + border-top-left-radius: $border-radius;
208 + border-top-right-radius: $border-radius;
209 + overflow: hidden;
210 +
211 + > img {
212 + border-top-left-radius: $border-radius;
213 + border-top-right-radius: $border-radius;
214 + }
215 +
216 + > .btn-toolbar {
217 + position: absolute;
218 + top: 0;
219 + right: 0;
220 + z-index: 1;
221 + margin-right: $widget-padding-horizontal;
222 +
223 + @include media-breakpoint-up(md) {
224 + top: auto;
225 + bottom: 0;
226 + }
227 + }
228 + }
229 +
230 + .widget-icon {
231 + opacity: 0.5;
232 + font-size: 42px;
233 + height: 60px;
234 + line-height: 45px;
235 + display: inline-block;
236 + }
237 +}
238 +
239 +.widgetLoader {
240 + position: absolute;
241 + top: 0;
242 + left: 0;
243 +}
1 +{
2 + "name": "Widget",
3 + "version": "0.0.0",
4 + "private": true,
5 + "main": "./Widget.js"
6 +}
1 +const hostApi = process.env.NODE_ENV === "development" ? "http://localhost" : "https://flatlogic-node-backend.herokuapp.com";
2 +const portApi = process.env.NODE_ENV === "development" ? 8080 : "";
3 +const baseURLApi = `${hostApi}${portApi ? `:${portApi}` : ``}`;
4 +
5 +export default {
6 + hostApi,
7 + portApi,
8 + baseURLApi
9 +};
...\ No newline at end of file ...\ No newline at end of file
1 +import React, { Component } from 'react';
2 +import PropTypes from 'prop-types';
3 +
4 +class Bundle extends Component {
5 + static propTypes = {
6 + load: PropTypes.func.isRequired,
7 + children: PropTypes.func.isRequired,
8 + };
9 +
10 + static generateBundle = loadModule => () => (
11 + /* eslint-disable */
12 + <Bundle load={loadModule}>
13 + {Mod => Mod ? <Mod /> : <div style={{ textAlign: 'center', paddingTop: '35vh' }}>Loading</div>}
14 + </Bundle>
15 + /* eslint-enable */
16 + );
17 +
18 +
19 + state = {
20 + // short for "module" but that's a keyword in js, so "mod"
21 + mod: null,
22 + };
23 +
24 + componentWillMount() {
25 + this.load(this.props);
26 + }
27 +
28 + componentWillReceiveProps(nextProps) {
29 + if (nextProps.load !== this.props.load) {
30 + this.load(nextProps);
31 + }
32 + }
33 +
34 + load(props) {
35 + this.setState({
36 + mod: null,
37 + });
38 + props.load((mod) => {
39 + this.setState({
40 + // handle both es imports and cjs
41 + mod: mod.default ? mod.default : mod,
42 + });
43 + });
44 + }
45 +
46 + render() {
47 + return this.props.children(this.state.mod);
48 + }
49 +}
50 +
51 +export default Bundle;
1 +const config = {
2 + name: 'sing',
3 + title: 'Sing Dashboard App built with React JS by Flatlogic',
4 + version: '3.8.0',
5 + settings: {
6 + screens: {
7 + 'xs-max': 543,
8 + 'sm-min': 544,
9 + 'sm-max': 767,
10 + 'md-min': 768,
11 + 'md-max': 991,
12 + 'lg-min': 992,
13 + 'lg-max': 1199,
14 + 'xl-min': 1200,
15 + },
16 + navCollapseTimeout: 2500,
17 + },
18 +};
19 +
20 +export default function isScreen(size) {
21 + const screenPx = window.innerWidth;
22 + return (screenPx >= config.settings.screens[`${size}-min`] || size === 'xs')
23 + && (screenPx <= config.settings.screens[`${size}-max`] || size === 'xl');
24 +}
1 +import React, { Component } from 'react';
2 +import hoistStatics from 'hoist-non-react-statics';
3 +import { updateMeta } from '../DOMUtils';
4 +import { defaultMeta } from '../config';
5 +
6 +function withMeta(ComposedComponent) {
7 + class WithMeta extends Component {
8 + componentWillMount() {
9 + if (ComposedComponent.meta) {
10 + Object.keys(ComposedComponent.meta).forEach((metaKey) => {
11 + if (metaKey === 'title') {
12 + document.title = `${ComposedComponent.meta[metaKey]} - ${defaultMeta[metaKey]}`;
13 + return;
14 + }
15 + updateMeta(metaKey, ComposedComponent.meta[metaKey]);
16 + });
17 + }
18 + }
19 +
20 + componentWillUnmount() {
21 + Object.keys(defaultMeta).forEach((metaKey) => {
22 + if (metaKey === 'title') {
23 + document.title = defaultMeta[metaKey];
24 + return;
25 + }
26 + updateMeta(metaKey, defaultMeta[metaKey]);
27 + });
28 + }
29 +
30 + render() {
31 + return <ComposedComponent {...this.props} />;
32 + }
33 + }
34 +
35 + WithMeta.ComposedComponent = ComposedComponent;
36 + return hoistStatics(WithMeta, ComposedComponent);
37 +}
38 +
39 +export default withMeta;
1 +import React from 'react';
2 +import { connect } from 'react-redux';
3 +import PropTypes from 'prop-types';
4 +import classnames from 'classnames';
5 +import { Link } from 'react-router-dom';
6 +import { withRouter } from 'react-router';
7 +import {
8 + Navbar,
9 + Nav,
10 + NavItem,
11 + NavLink,
12 +} from 'reactstrap';
13 +
14 +import { openSidebar, closeSidebar, changeActiveSidebarItem } from '../actions/navigation';
15 +
16 +import s from '../components/Header/Header.module.scss'; // eslint-disable-line css-modules/no-unused-class
17 +import sd from './styles.module.scss';
18 +
19 +import twitterLogo from '../images/documentation/twitter-logo.svg';
20 +import dribbleLogo from '../images/documentation/dribble-logo.svg';
21 +import facebookLogo from '../images/documentation/facebook-logo.svg';
22 +import instagramLogo from '../images/documentation/instagram-logo.svg';
23 +import linkedinLogo from '../images/documentation/linkedin-logo.svg';
24 +import githubLogo from '../images/documentation/github-logo.svg';
25 +
26 +class Header extends React.Component {
27 + static propTypes = {
28 + sidebarOpened: PropTypes.bool.isRequired,
29 + sidebarStatic: PropTypes.bool.isRequired,
30 + chatToggle: PropTypes.func.isRequired,
31 + dispatch: PropTypes.func.isRequired,
32 + location: PropTypes.shape({
33 + pathname: PropTypes.string,
34 + }).isRequired,
35 + };
36 +
37 + constructor(props) {
38 + super(props);
39 +
40 + this.toggleMenu = this.toggleMenu.bind(this);
41 + this.switchSidebar = this.switchSidebar.bind(this);
42 +
43 + this.state = {
44 + menuOpen: false,
45 + notificationsOpen: false,
46 + notificationsTabSelected: 1,
47 + };
48 + }
49 +
50 + // collapse/uncolappse
51 + switchSidebar() {
52 + if (this.props.sidebarOpened) {
53 + this.props.dispatch(closeSidebar());
54 + this.props.dispatch(changeActiveSidebarItem(null));
55 + } else {
56 + const paths = this.props.location.pathname.split('/');
57 + paths.pop();
58 + this.props.dispatch(openSidebar());
59 + this.props.dispatch(changeActiveSidebarItem(paths.join('/')));
60 + }
61 + }
62 +
63 + toggleMenu() {
64 + this.setState({
65 + menuOpen: !this.state.menuOpen,
66 + });
67 + }
68 + render() {
69 + return (
70 + <Navbar className={classnames(s.root, sd.header, 'd-print-none')}>
71 + <div className="container">
72 + <div className="row w-100 d-flex align-items-center">
73 + <Nav>
74 + <NavItem>
75 + <NavLink className="fs-lg d-lg-none d-md-none" onClick={this.switchSidebar}>
76 + <span className="rounded rounded-lg text-white d-md-none"><i className="la la-bars" /></span>
77 + <i className="la la-bars ml-3 d-sm-down-none" />
78 + </NavLink>
79 + </NavItem>
80 + <NavItem>
81 + <NavLink className={classnames(s.logo, 'd-sm-down-none px-4')} href={'/documentation'}>
82 + <span className={'fw-semi-bold'}>Sing App React</span> &nbsp; Documentation
83 + </NavLink>
84 + </NavItem>
85 + </Nav>
86 +
87 + <NavLink className={`${s.navbarBrand} d-md-none text-muted`}>
88 + <i className="fa fa-circle text-gray mr-n-sm" />
89 + <i className="fa fa-circle text-warning" />
90 + &nbsp;
91 + documentation
92 + &nbsp;
93 + <i className="fa fa-circle text-warning mr-n-sm" />
94 + <i className="fa fa-circle text-muted" />
95 + </NavLink>
96 +
97 + <Nav className="ml-auto">
98 + <NavItem className="d-flex alight-items-center d-md-down-none">
99 + <NavLink href="https://twitter.com/flatlogic" className="mr-1">
100 + <img src={twitterLogo} alt="twitter" />
101 + </NavLink>
102 + <NavLink href="https://dribbble.com/flatlogic" className="mr-1">
103 + <img src={dribbleLogo} alt="dribble" />
104 + </NavLink>
105 + <NavLink href="https://dribbble.com/flatlogic" className="mr-1">
106 + <img src={facebookLogo} alt="facebook" />
107 + </NavLink>
108 + <NavLink href="https://instagram.com/flatlogiccom/" className="mr-1">
109 + <img src={instagramLogo} alt="instagram" />
110 + </NavLink>
111 + <NavLink href="https://www.linkedin.com/company/flatlogic/" className="mr-1">
112 + <img src={linkedinLogo} alt="linkedin" />
113 + </NavLink>
114 + <NavLink href="https://github.com/flatlogic" className="mr-3">
115 + <img src={githubLogo} alt="github" />
116 + </NavLink>
117 + </NavItem>
118 + <NavItem className="d-flex align-items-center">
119 + <NavItem className="d-md-down-none">
120 + <Link to="/" className="btn btn-default">
121 + Live Preview
122 + </Link>
123 + </NavItem>
124 + <NavLink href="https://flatlogic.com/admin-dashboards/sing-app-react" target="_blank" className="mr-1">
125 + <button className="btn btn-warning text-gray fw-semi-bold">
126 + Buy Now
127 + </button>
128 + </NavLink>
129 + </NavItem>
130 + </Nav>
131 + </div>
132 + </div>
133 + </Navbar>
134 + );
135 + }
136 +}
137 +
138 +function mapStateToProps(store) {
139 + return {
140 + sidebarOpened: store.navigation.sidebarOpened,
141 + sidebarStatic: store.navigation.sidebarStatic,
142 + };
143 +}
144 +
145 +export default withRouter(connect(mapStateToProps)(Header));
146 +
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
No preview for this file type
No preview for this file type
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.