Merge branch 'dmgt-ra/feature/eslint' into 'master'

Add ESLint to devicemgt react app

Closes product-iots#291

See merge request entgra/carbon-device-mgt!419
This commit is contained in:
Dharmakeerthi Lasantha 2020-01-16 07:27:30 +00:00
commit c34811f0e7
59 changed files with 9836 additions and 9097 deletions

View File

@ -82,6 +82,16 @@
<arguments>install</arguments> <arguments>install</arguments>
</configuration> </configuration>
</execution> </execution>
<execution>
<id>lint</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run-script lint</arguments>
</configuration>
<phase>generate-resources</phase>
</execution>
<execution> <execution>
<id>prod</id> <id>prod</id>
<goals> <goals>

View File

@ -0,0 +1,325 @@
{
"parser": "babel-eslint",
"plugins": [
"react",
"babel",
"jsx",
"prettier"
],
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"parserOptions": {
"ecmaVersion": 2016,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"settings": {
"react": {
"createClass": "createReactClass",
"pragma": "React",
"version": "16.8.6"
}
},
"env": {
"node": true,
"commonjs": true,
"browser": true,
"jasmine": true,
"es6": true
},
"globals": {
"document": true,
"console": true,
// Only for development purposes
"setTimeout": true,
"window" : true
},
"rules": {
"prettier/prettier": "error",
// Enforce the spacing around the * in generator functions.
"generator-star-spacing": [2, "after"],
// Disallow using variables outside the blocks they are defined (especially
// since only let and const are used, see "no-var").
"block-scoped-var": 2,
// Require camel case names
"camelcase": 2,
// Allow trailing commas for easy list extension. Having them does not
// impair readability, but also not required either.
"comma-dangle": 0,
// Warn about cyclomatic complexity in functions.
"complexity": 1,
// Don't warn for inconsistent naming when capturing this (not so important
// with auto-binding fat arrow functions).
"consistent-this": 0,
// Enforce curly brace conventions for all control statements.
"curly": 2,
// Don't require a default case in switch statements. Avoid being forced to
// add a bogus default when you know all possible cases are handled.
"default-case": 0,
// Encourage the use of dot notation whenever possible.
"dot-notation": 2,
// Allow mixed 'LF' and 'CRLF' as linebreaks.
"linebreak-style": 0,
// Don't enforce the maximum depth that blocks can be nested.
"max-depth": 0,
// Maximum length of a line.
"max-len": [2, 120, 2, { "ignoreStrings": true, "ignoreUrls": true}],
// Maximum depth callbacks can be nested.
"max-nested-callbacks": [2, 3],
// Don't limit the number of parameters that can be used in a function.
"max-params": 0,
// Don't limit the maximum number of statement allowed in a function.
"max-statements": 0,
// Require a capital letter for constructors, only check if all new
// operators are followed by a capital letter. Don't warn when capitalized
// functions are used without the new operator.
"new-cap": [2, {"capIsNew": false}],
// Disallow use of the Array constructor.
"no-array-constructor": 2,
// Allow use of bitwise operators.
"no-bitwise": 0,
// Disallow use of arguments.caller or arguments.callee.
"no-caller": 2,
// Disallow the catch clause parameter name being the same as a variable in
// the outer scope, to avoid confusion.
"no-catch-shadow": 2,
// Disallow assignment in conditional expressions.
"no-cond-assign": 2,
// Allow using the console API.
"no-console": 0,
// Allow using constant expressions in conditions like while (true)
"no-constant-condition": 0,
// Allow use of the continue statement.
"no-continue": 0,
// Disallow control characters in regular expressions.
"no-control-regex": 2,
// Disallow deletion of variables (deleting properties is fine).
"no-delete-var": 2,
// Disallow duplicate arguments in functions.
"no-dupe-args": 2,
// Disallow duplicate keys when creating object literals.
"no-dupe-keys": 2,
// Disallow multiple empty lines
"no-multiple-empty-lines": "error",
// Disallow a duplicate case label.
"no-duplicate-case": 2,
// Disallow else after a return in an if. The else around the second return
// here is useless:
// if (something) { return false; } else { return true; }
"no-else-return": 2,
// Disallow empty statements. This will report an error for:
// try { something(); } catch (e) {}
// but will not report it for:
// try { something(); } catch (e) { /* Silencing the error because ...*/ }
// which is a valid use case.
"no-empty": 2,
// Disallow the use of empty character classes in regular expressions.
"no-empty-character-class": 2,
// Disallow use of labels for anything other then loops and switches.
"no-labels": 2,
// Disallow use of eval(). We have other APIs to evaluate code in content.
"no-eval": 2,
// Disallow assigning to the exception in a catch block.
"no-ex-assign": 2,
// Disallow adding to native types
"no-extend-native": 2,
// Disallow unnecessary function binding.
"no-extra-bind": 2,
// Disallow double-negation boolean casts in a boolean context.
"no-extra-boolean-cast": 2,
// Allow unnecessary parentheses, as they may make the code more readable.
"no-extra-parens": 0,
// Disallow fallthrough of case statements, except if there is a comment.
"no-fallthrough": 2,
// Allow the use of leading or trailing decimal points in numeric literals.
"no-floating-decimal": 0,
// Disallow if as the only statement in an else block.
"no-lonely-if": 2,
// Disallow use of multiline strings (use template strings instead).
"no-multi-str": 2,
// Disallow reassignments of native objects.
"no-native-reassign": 2,
// Disallow nested ternary expressions, they make the code hard to read.
"no-nested-ternary": 2,
// Allow use of new operator with the require function.
"no-new-require": 0,
// Disallow use of octal literals.
"no-octal": 2,
// Allow reassignment of function parameters.
"no-param-reassign": 0,
// Allow string concatenation with __dirname and __filename (not a node env).
"no-path-concat": 0,
// Allow use of unary operators, ++ and --.
"no-plusplus": 0,
// Allow using process.env (not a node environment).
"no-process-env": 0,
// Allow using process.exit (not a node environment).
"no-process-exit": 0,
// Disallow usage of __proto__ property.
"no-proto": 2,
// Disallow declaring the same variable more than once (we use let anyway).
"no-redeclare": 2,
// Disallow multiple spaces in a regular expression literal.
"no-regex-spaces": 2,
// Allow reserved words being used as object literal keys.
"no-reserved-keys": 0,
// Don't restrict usage of specified node modules (not a node environment).
"no-restricted-modules": 0,
// Disallow use of assignment in return statement. It is preferable for a
// single line of code to have only one easily predictable effect.
"no-return-assign": 2,
// Allow use of javascript: urls.
"no-script-url": 0,
// Disallow comparisons where both sides are exactly the same.
"no-self-compare": 2,
// Disallow use of comma operator.
"no-sequences": 2,
// Warn about declaration of variables already declared in the outer scope.
// This isn't an error because it sometimes is useful to use the same name
// in a small helper function rather than having to come up with another
// random name.
// Still, making this a warning can help people avoid being confused.
"no-shadow": 0,
// Require empty line at end of file
"eol-last": "error",
// Disallow shadowing of names such as arguments.
"no-shadow-restricted-names": 2,
"no-space-before-semi": 0,
// Disallow sparse arrays, eg. let arr = [,,2].
// Array destructuring is fine though:
// for (let [, breakpointPromise] of aPromises)
"no-sparse-arrays": 2,
// Allow use of synchronous methods (not a node environment).
"no-sync": 0,
// Allow the use of ternary operators.
"no-ternary": 0,
// Don't allow spaces after end of line
"no-trailing-spaces": "error",
// Disallow throwing literals (eg. throw "error" instead of
// throw new Error("error")).
"no-throw-literal": 2,
// Disallow use of undeclared variables unless mentioned in a /*global */
// block. Note that globals from head.js are automatically imported in tests
// by the import-headjs-globals rule form the mozilla eslint plugin.
"no-undef": 2,
// Allow use of undefined variable.
"no-undefined": 0,
// Disallow the use of Boolean literals in conditional expressions.
"no-unneeded-ternary": 2,
// Disallow unreachable statements after a return, throw, continue, or break
// statement.
"no-unreachable": 2,
// Allow using variables before they are defined.
"no-unused-vars": [2, {"vars": "all", "args": "none"}],
// Disallow global and local variables that arent used, but allow unused function arguments.
"no-use-before-define": 0,
// We use var-only-at-top-level instead of no-var as we allow top level
// vars.
"no-var": 0,
// Allow using TODO/FIXME comments.
"no-warning-comments": 0,
// Disallow use of the with statement.
"no-with": 2,
// Dont require method and property shorthand syntax for object literals.
// We use this in the code a lot, but not consistently, and this seems more
// like something to check at code review time.
"object-shorthand": 0,
// Allow more than one variable declaration per function.
"one-var": 0,
// Single quotes should be used.
"quotes": [2, "single", "avoid-escape"],
// Require use of the second argument for parseInt().
"radix": 2,
// Dont require to sort variables within the same declaration block.
// Anyway, one-var is disabled.
"sort-vars": 0,
"space-after-function-name": 0,
"space-before-function-parentheses": 0,
// Disallow space before function opening parenthesis.
//"space-before-function-paren": [2, "never"],
// Disable the rule that checks if spaces inside {} and [] are there or not.
// Our code is split on conventions, and itd be nice to have 2 rules
// instead, one for [] and one for {}. So, disabling until we write them.
"space-in-brackets": 0,
// Deprecated, will be removed in 1.0.
"space-unary-word-ops": 0,
// Require a space immediately following the // in a line comment.
"spaced-comment": [2, "always"],
// Require "use strict" to be defined globally in the script.
"strict": [2, "global"],
// Disallow comparisons with the value NaN.
"use-isnan": 2,
// Warn about invalid JSDoc comments.
// Disabled for now because of https://github.com/eslint/eslint/issues/2270
// The rule fails on some jsdoc comments like in:
// devtools/client/webconsole/console-output.js
"valid-jsdoc": 0,
// Ensure that the results of typeof are compared against a valid string.
"valid-typeof": 2,
// Allow vars to be declared anywhere in the scope.
"vars-on-top": 0,
// Dont require immediate function invocation to be wrapped in parentheses.
"wrap-iife": 0,
// Don't require regex literals to be wrapped in parentheses (which
// supposedly prevent them from being mistaken for division operators).
"wrap-regex": 0,
// Require for-in loops to have an if statement.
"guard-for-in": 0,
// allow/disallow an empty newline after var statement
"newline-after-var": 0,
// disallow the use of alert, confirm, and prompt
"no-alert": 0,
// disallow the use of deprecated react changes and lifecycle methods
"react/no-deprecated": 0,
// disallow comparisons to null without a type-checking operator
"no-eq-null": 0,
// disallow overwriting functions written as function declarations
"no-func-assign": 0,
// disallow use of eval()-like methods
"no-implied-eval": 0,
// disallow function or variable declarations in nested blocks
"no-inner-declarations": 0,
// disallow invalid regular expression strings in the RegExp constructor
"no-invalid-regexp": 0,
// disallow irregular whitespace outside of strings and comments
"no-irregular-whitespace": 0,
// disallow unnecessary nested blocks
"no-lone-blocks": 0,
// disallow creation of functions within loops
"no-loop-func": 0,
// disallow use of new operator when not part of the assignment or
// comparison
"no-new": 0,
// disallow use of new operator for Function object
"no-new-func": 0,
// disallow use of the Object constructor
"no-new-object": 0,
// disallows creating new instances of String,Number, and Boolean
"no-new-wrappers": 0,
// disallow the use of object properties of the global object (Math and
// JSON) as functions
"no-obj-calls": 0,
// disallow use of octal escape sequences in string literals, such as
// var foo = "Copyright \251";
"no-octal-escape": 0,
// disallow use of undefined when initializing variables
"no-undef-init": 0,
// disallow usage of expressions in statement position
"no-unused-expressions": 0,
// disallow use of void operator
"no-void": 0,
// disallow wrapping of non-IIFE statements in parens
"no-wrap-func": 0,
// require assignment operator shorthand where possible or prohibit it
// entirely
"operator-assignment": 0,
// enforce operators to be placed before or after line breaks
"operator-linebreak": 0,
// disable chacking prop types
"react/prop-types": 0
}
}

View File

@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all",
"parser": "flow"
}

View File

@ -16,14 +16,13 @@
* under the License. * under the License.
*/ */
module.exports = function (api) { module.exports = function(api) {
api.cache(true); api.cache(true);
const presets = [ "@babel/preset-env", const presets = ['@babel/preset-env', '@babel/preset-react'];
"@babel/preset-react" ]; const plugins = ['@babel/plugin-proposal-class-properties'];
const plugins = ["@babel/plugin-proposal-class-properties"];
return { return {
presets, presets,
plugins plugins,
}; };
}; };

View File

@ -8,6 +8,7 @@
"acorn": "^6.2.0", "acorn": "^6.2.0",
"antd": "^3.23.5", "antd": "^3.23.5",
"axios": "^0.18.1", "axios": "^0.18.1",
"babel-eslint": "^9.0.0",
"bizcharts": "^3.5.6", "bizcharts": "^3.5.6",
"bootstrap": "^4.3.1", "bootstrap": "^4.3.1",
"javascript-time-ago": "^2.0.1", "javascript-time-ago": "^2.0.1",
@ -46,6 +47,12 @@
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"chai": "^4.1.2", "chai": "^4.1.2",
"css-loader": "^0.28.11", "css-loader": "^0.28.11",
"eslint": "^5.16.0",
"eslint-config-prettier": "4.3.0",
"eslint-plugin-babel": "5.3.0",
"eslint-plugin-jsx": "0.0.2",
"eslint-plugin-prettier": "3.1.0",
"eslint-plugin-react": "7.14.2",
"express": "^4.17.1", "express": "^4.17.1",
"express-pino-logger": "^4.0.0", "express-pino-logger": "^4.0.0",
"file-loader": "^2.0.0", "file-loader": "^2.0.0",
@ -64,6 +71,7 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pino-colada": "^1.4.5", "pino-colada": "^1.4.5",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",
"prettier": "1.18.1",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-intl": "^2.9.0", "react-intl": "^2.9.0",
@ -84,6 +92,7 @@
"build_prod": "NODE_ENV=production NODE_OPTIONS=--max_old_space_size=4096 webpack -p --display errors-only --hide-modules", "build_prod": "NODE_ENV=production NODE_OPTIONS=--max_old_space_size=4096 webpack -p --display errors-only --hide-modules",
"build_dev": "NODE_ENV=development webpack -d --watch ", "build_dev": "NODE_ENV=development webpack -d --watch ",
"server": "node-env-run server --exec nodemon | pino-colada", "server": "node-env-run server --exec nodemon | pino-colada",
"dev2": "run-p server start" "dev2": "run-p server start",
"lint": "eslint \"src/**/*.js\""
} }
} }

View File

@ -16,153 +16,156 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import "antd/dist/antd.less"; import 'antd/dist/antd.less';
import RouteWithSubRoutes from "./components/RouteWithSubRoutes"; import RouteWithSubRoutes from './components/RouteWithSubRoutes';
import { import { BrowserRouter as Router, Redirect, Switch } from 'react-router-dom';
BrowserRouter as Router, import axios from 'axios';
Redirect, Switch, import { Layout, Spin, Result, notification } from 'antd';
} from 'react-router-dom'; import ConfigContext from './context/ConfigContext';
import axios from "axios";
import {Layout, Spin, Result, message, notification} from "antd";
import ConfigContext from "./context/ConfigContext";
const {Content} = Layout; const { Content } = Layout;
const loadingView = ( const loadingView = (
<Layout> <Layout>
<Content style={{ <Content
padding: '0 0', style={{
paddingTop: 300, padding: '0 0',
backgroundColor: '#fff', paddingTop: 300,
textAlign: 'center' backgroundColor: '#fff',
}}> textAlign: 'center',
<Spin tip="Loading..."/> }}
</Content> >
</Layout> <Spin tip="Loading..." />
</Content>
</Layout>
); );
const errorView = ( const errorView = (
<Result <Result
style={{ style={{
paddingTop: 200 paddingTop: 200,
}} }}
status="500" status="500"
title="Error occurred while loading the configuration" title="Error occurred while loading the configuration"
subTitle="Please refresh your browser window" subTitle="Please refresh your browser window"
/> />
); );
class App extends React.Component { class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
error: false,
config: {},
};
}
constructor(props) { componentDidMount() {
super(props); axios
this.state = { .get(window.location.origin + '/entgra/public/conf/config.json')
loading: true, .then(res => {
error: false, const config = res.data;
config: {} this.checkUserLoggedIn(config);
})
.catch(error => {
this.setState({
loading: false,
error: true,
});
});
}
checkUserLoggedIn = config => {
axios
.post(
window.location.origin + '/entgra-ui-request-handler/user',
'platform=entgra',
)
.then(res => {
config.user = res.data.data;
const pageURL = window.location.pathname;
const lastURLSegment = pageURL.substr(pageURL.lastIndexOf('/') + 1);
if (lastURLSegment === 'login') {
window.location.href = window.location.origin + '/entgra/';
} else {
this.setState({
loading: false,
config: config,
});
} }
} this.getDeviceTypes(config);
})
componentDidMount() { .catch(error => {
axios.get( if (error.hasOwnProperty('response') && error.response.status === 401) {
window.location.origin + "/entgra/public/conf/config.json", const redirectUrl = encodeURI(window.location.href);
).then(res => { const pageURL = window.location.pathname;
const config = res.data; const lastURLSegment = pageURL.substr(pageURL.lastIndexOf('/') + 1);
this.checkUserLoggedIn(config); if (lastURLSegment !== 'login') {
}).catch((error) => { window.location.href =
window.location.origin + `/entgra/login?redirect=${redirectUrl}`;
} else {
this.setState({ this.setState({
loading: false, loading: false,
error: true config: config,
})
});
}
checkUserLoggedIn = (config) => {
axios.post(
window.location.origin + "/entgra-ui-request-handler/user",
"platform=entgra"
).then(res => {
config.user = res.data.data;
const pageURL = window.location.pathname;
const lastURLSegment = pageURL.substr(pageURL.lastIndexOf('/') + 1);
if (lastURLSegment === "login") {
window.location.href = window.location.origin + `/entgra/`;
} else {
this.setState({
loading: false,
config: config
});
}
this.getDeviceTypes(config);
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
const redirectUrl = encodeURI(window.location.href);
const pageURL = window.location.pathname;
const lastURLSegment = pageURL.substr(pageURL.lastIndexOf('/') + 1);
if (lastURLSegment !== "login") {
window.location.href = window.location.origin + `/entgra/login?redirect=${redirectUrl}`;
} else {
this.setState({
loading: false,
config: config
})
}
} else {
this.setState({
loading: false,
error: true
})
}
});
};
getDeviceTypes = (config) => {
axios.get(
window.location.origin + "/entgra-ui-request-handler/invoke/device-mgt/v1.0/device-types",
).then(res => {
config.deviceTypes = JSON.parse(res.data.data);
this.setState({
config: config,
loading: false
}); });
}).catch((error) => { }
} else {
notification["error"]({ this.setState({
message: "There was a problem", loading: false,
duration: 0, error: true,
description:"Error occurred while trying to load device types.", });
}); }
});
};
getDeviceTypes = config => {
axios
.get(
window.location.origin +
'/entgra-ui-request-handler/invoke/device-mgt/v1.0/device-types',
)
.then(res => {
config.deviceTypes = JSON.parse(res.data.data);
this.setState({
config: config,
loading: false,
}); });
}; })
.catch(error => {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load device types.',
});
});
};
render() { render() {
const {loading, error} = this.state; const { loading, error } = this.state;
const applicationView = (
<Router>
<ConfigContext.Provider value={this.state.config}>
<div>
<Switch>
<Redirect exact from="/entgra" to="/entgra/devices" />
{this.props.routes.map(route => (
<RouteWithSubRoutes key={route.path} {...route} />
))}
</Switch>
</div>
</ConfigContext.Provider>
</Router>
);
const abc = this.state.deviceTypes; return (
const applicationView = ( <div>
<Router> {loading && loadingView}
<ConfigContext.Provider value={this.state.config}> {!loading && !error && applicationView}
<div> {error && errorView}
<Switch> </div>
<Redirect exact from="/entgra" to="/entgra/devices"/> );
{this.props.routes.map((route) => ( }
<RouteWithSubRoutes key={route.path} {...route} />
))}
</Switch>
</div>
</ConfigContext.Provider>
</Router>
);
return (
<div>
{loading && loadingView}
{!loading && !error && applicationView}
{error && errorView}
</div>
);
}
} }
export default App; export default App;

View File

@ -16,217 +16,247 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Icon, message, Modal, notification, Popconfirm, Select, Table, Tag, Tooltip, Typography} from "antd"; import {
import TimeAgo from 'javascript-time-ago' Icon,
message,
notification,
Popconfirm,
Table,
Tooltip,
Typography,
} from 'antd';
import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../../context/ConfigContext"; import { withConfigContext } from '../../../context/ConfigContext';
import Moment from "react-moment"; import Moment from 'react-moment';
const {Paragraph, Text} = Typography; const { Paragraph, Text } = Typography;
let config = null; let config = null;
class CertificateTable extends React.Component { class CertificateTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; config = this.props.context;
TimeAgo.addLocale(en); TimeAgo.addLocale(en);
this.state = { this.state = {
data: [], data: [],
pagination: {}, pagination: {},
loading: false,
};
}
componentDidMount() {
this.fetch();
}
// fetch data from api
fetch = (params = {}) => {
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), // calculate the offset
limit: 10,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key])
.join('&');
// send request to the invoker
axios
.get(
window.location.origin +
config.serverConfig.invoker.uri +
'/certificate-mgt/v1.0/admin/certificates' +
encodedExtraParams,
)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
}; data: res.data.data.certificates,
} pagination,
});
componentDidMount() { }
this.fetch(); })
} .catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
//fetch data from api // todo display a popop with error
fetch = (params = {}) => { message.error('You are not logged in');
this.setState({loading: true}); window.location.href = window.location.origin + '/entgra/login';
// get current page } else {
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; notification.error({
message: 'There was a problem',
const extraParams = { duration: 0,
offset: 10 * (currentPage - 1), //calculate the offset description: 'Error occurred while trying to load devices.',
limit: 10, });
requireDeviceInfo: true,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key]).join('&');
//send request to the invoker
axios.get(
window.location.origin + config.serverConfig.invoker.uri +
'/certificate-mgt/v1.0/admin/certificates',
).then(res => {
if (res.status === 200) {
const pagination = {...this.state.pagination};
this.setState({
loading: false,
data: res.data.data.certificates,
pagination
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load devices.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
deleteCertificate = (serialNumber) =>{
axios.delete(
window.location.origin + config.serverConfig.invoker.uri +
'/certificate-mgt/v1.0/admin/certificates/'+ serialNumber,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 200) {
this.fetch();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully deleted the certificate.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to delete the certificate.",
});
}
});
};
columns = [
{
title: 'Serial Number',
dataIndex: 'serialNumber'
},
{
title: 'Username',
dataIndex: 'username'
},
{
title: 'Certificate Version',
dataIndex: 'certificateVersion'
},
{
title: 'Certificate Serial',
dataIndex: 'certificateserial'
},
{
title: 'Not Before',
dataIndex: 'notBefore',
render: notBefore => (
<Moment format="YYYY/MM/DD HH:mm" date={notBefore} />
)
},
{
title: 'Not After',
dataIndex: 'notAfter',
render: notAfter => (
<Moment format="YYYY/MM/DD HH:mm" date={notAfter} />
)
},
{
title: 'Subject',
dataIndex: 'subject',
render: subject => (
<Paragraph style={{marginBottom: 0}} ellipsis={{rows:1, expandable: true}}>{subject}</Paragraph>
)
},
{
title: 'Issuer',
dataIndex: 'issuer',
render: issuer => (
<Paragraph style={{marginBottom: 0}} ellipsis={{rows:1, expandable: true}}>{issuer}</Paragraph>
)
},
{
title: 'Actions',
key: 'actions',
dataIndex: 'serialNumber',
render: (serialNumber) => (
<Tooltip placement="bottom" title={"Remove User"}>
<Popconfirm
placement="top"
title={"Are you sure?"}
onConfirm={() => {this.deleteCertificate(serialNumber)}}
okText="Ok"
cancelText="Cancel">
<a><Text type="danger"><Icon type="delete"/></Text></a>
</Popconfirm>
</Tooltip>
)
} }
];
render() { this.setState({ loading: false });
const {data, pagination, loading} = this.state; });
};
return ( handleTableChange = (pagination, filters, sorter) => {
<div> const pager = { ...this.state.pagination };
<div> pager.current = pagination.current;
<Table this.setState({
columns={this.columns} pagination: pager,
rowKey={record => record.serialNumber} });
dataSource={data} this.fetch({
pagination={{ results: pagination.pageSize,
...pagination, page: pagination.current,
size: "small", sortField: sorter.field,
// position: "top", sortOrder: sorter.order,
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices` ...filters,
// showQuickJumper: true });
}} };
loading={loading}
onChange={this.handleTableChange} deleteCertificate = serialNumber => {
/> axios
</div> .delete(
</div> window.location.origin +
); config.serverConfig.invoker.uri +
} '/certificate-mgt/v1.0/admin/certificates/' +
serialNumber,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.fetch();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully deleted the certificate.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description:
'Error occurred while trying to delete the certificate.',
});
}
});
};
columns = [
{
title: 'Serial Number',
dataIndex: 'serialNumber',
},
{
title: 'Username',
dataIndex: 'username',
},
{
title: 'Certificate Version',
dataIndex: 'certificateVersion',
},
{
title: 'Certificate Serial',
dataIndex: 'certificateserial',
},
{
title: 'Not Before',
dataIndex: 'notBefore',
render: notBefore => (
<Moment format="YYYY/MM/DD HH:mm" date={notBefore} />
),
},
{
title: 'Not After',
dataIndex: 'notAfter',
render: notAfter => <Moment format="YYYY/MM/DD HH:mm" date={notAfter} />,
},
{
title: 'Subject',
dataIndex: 'subject',
render: subject => (
<Paragraph
style={{ marginBottom: 0 }}
ellipsis={{ rows: 1, expandable: true }}
>
{subject}
</Paragraph>
),
},
{
title: 'Issuer',
dataIndex: 'issuer',
render: issuer => (
<Paragraph
style={{ marginBottom: 0 }}
ellipsis={{ rows: 1, expandable: true }}
>
{issuer}
</Paragraph>
),
},
{
title: 'Actions',
key: 'actions',
dataIndex: 'serialNumber',
render: serialNumber => (
<Tooltip placement="bottom" title={'Remove User'}>
<Popconfirm
placement="top"
title={'Are you sure?'}
onConfirm={() => {
this.deleteCertificate(serialNumber);
}}
okText="Ok"
cancelText="Cancel"
>
<a>
<Text type="danger">
<Icon type="delete" />
</Text>
</a>
</Popconfirm>
</Tooltip>
),
},
];
render() {
const { data, pagination, loading } = this.state;
return (
<div>
<div>
<Table
columns={this.columns}
rowKey={record => record.serialNumber}
dataSource={data}
pagination={{
...pagination,
size: 'small',
// position: "top",
showTotal: (total, range) =>
`showing ${range[0]}-${range[1]} of ${total} devices`,
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
/>
</div>
</div>
);
}
} }
export default withConfigContext(CertificateTable); export default withConfigContext(CertificateTable);

View File

@ -16,129 +16,115 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Card, Col, Icon, message, notification, Row, Typography} from "antd"; import { Card, Col, Icon, message, notification, Row } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography;
let config = null;
let apiUrl; let apiUrl;
class DeviceTypesTable extends React.Component { class DeviceTypesTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; TimeAgo.addLocale(en);
TimeAgo.addLocale(en); this.state = {
this.state = { data: [],
data: [], pagination: {},
pagination: {}, loading: false,
selectedRows: [],
};
}
componentDidMount() {
this.fetchUsers();
}
// fetch data from api
fetchUsers = (params = {}) => {
const config = this.props.context;
this.setState({ loading: true });
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/device-types';
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [] data: JSON.parse(res.data.data),
}; pagination,
} });
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load device types.',
});
}
componentDidMount() { this.setState({ loading: false });
this.fetchUsers(); });
} };
//fetch data from api handleTableChange = (pagination, filters, sorter) => {
fetchUsers = (params = {}) => { const pager = { ...this.state.pagination };
const config = this.props.context; pager.current = pagination.current;
this.setState({loading: true}); this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
// get current page render() {
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; const { data } = this.state;
const { Meta } = Card;
const extraParams = { const itemCard = data.map(data => (
offset: 10 * (currentPage - 1), //calculate the offset <Col span={5} key={data.id}>
limit: 10, <Card
}; size="default"
style={{ width: 200 }}
const encodedExtraParams = Object.keys(extraParams) bordered={true}
.map(key => key + '=' + extraParams[key]).join('&'); actions={[
<Icon type="setting" key="setting" />,
apiUrl = window.location.origin + config.serverConfig.invoker.uri + <Icon type="edit" key="edit" />,
config.serverConfig.invoker.deviceMgt + ]}
"/device-types"; >
<Meta
//send request to the invokerss avatar={<Icon type="desktop" key="device-types" />}
axios.get(apiUrl).then(res => { title={data.name}
if (res.status === 200) { />
const pagination = {...this.state.pagination}; </Card>
this.setState({ </Col>
loading: false, ));
data: JSON.parse(res.data.data), return (
pagination, <div style={{ background: '#ECECEC', padding: '20px' }}>
}); <Row gutter={16}>{itemCard}</Row>
} </div>
);
}).catch((error) => { }
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load device types.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
const { Meta } = Card;
const itemCard = data.map((data) =>
<Col span={5} key={data.id}>
<Card
size="default"
style={{ width: 200 }}
bordered={true}
actions={[
<Icon type="setting" key="setting" />,
<Icon type="edit" key="edit" />,]}
>
<Meta
avatar={<Icon type="desktop" key="device-types"/>}
title={data.name}
/>
</Card>
</Col>
);
return (
<div style={{ background: '#ECECEC', padding: '20px' }}>
<Row gutter={16}>
{itemCard}
</Row>
</div>
);
}
} }
export default withConfigContext(DeviceTypesTable); export default withConfigContext(DeviceTypesTable);

View File

@ -1,66 +1,56 @@
import React from 'react'; import React from 'react';
import {Button, Form, Row, Col, Card, Steps, Input, message, Modal, notification, Typography} from "antd"; import { Form, Row, Col, Card, Steps } from 'antd';
import axios from "axios"; import { withConfigContext } from '../../context/ConfigContext';
import {withConfigContext} from "../../context/ConfigContext"; import DeviceType from './DeviceType';
import DeviceType from "./DeviceType"; import EnrollAgent from './EnrollAgent';
import EnrollAgent from "./EnrollAgent"; const { Step } = Steps;
const {Step} = Steps;
class AddDevice extends React.Component { class AddDevice extends React.Component {
constructor(props) {
constructor(props) { super(props);
super(props); this.config = this.props.context;
this.config = this.props.context; this.state = {
this.state = { isAddDeviceModalVisible: false,
isAddDeviceModalVisible: false, current: 0,
current : 0,
}
}; };
}
onClickType = () =>{ onClickType = () => {
this.setState({ this.setState({
current: 1, current: 1,
}) });
}; };
openAddDeviceModal = () =>{ render() {
this.setState({ const { current } = this.state;
isAddDeviceModalVisible : true, return (
}) <div>
}; <Row>
<Col span={16} offset={4}>
<Steps style={{ minHeight: 32 }} current={current}>
<Step key="DeviceType" title="Device Type" />
<Step key="EnrollAgent" title="Enroll Agent" />
<Step key="Result" title="Result" />
</Steps>
</Col>
<Col span={16} offset={4}>
<Card style={{ marginTop: 24 }}>
<div style={{ display: current === 0 ? 'unset' : 'none' }}>
<DeviceType onClickType={this.onClickType} />
</div>
<div style={{ display: current === 1 ? 'unset' : 'none' }}>
<EnrollAgent />
</div>
render() { <div style={{ display: current === 2 ? 'unset' : 'none' }}></div>
const {loading, current, isError, supportedOsVersions, errorText, forbiddenErrors} = this.state; </Card>
const { getFieldDecorator } = this.props.form; </Col>
return ( </Row>
<div> </div>
<Row> );
<Col span={16} offset={4}> }
<Steps style={{minHeight: 32}} current={current}>
<Step key="DeviceType" title="Device Type"/>
<Step key="EnrollAgent" title="Enroll Agent"/>
<Step key="Result" title="Result"/>
</Steps>
</Col>
<Col span={16} offset={4}>
<Card style={{marginTop: 24}}>
<div style={{display: (current === 0 ? 'unset' : 'none')}}>
<DeviceType onClickType={this.onClickType}/>
</div>
<div style={{display: (current === 1 ? 'unset' : 'none')}}>
<EnrollAgent />
</div>
<div style={{display: (current === 2 ? 'unset' : 'none')}}>
</div>
</Card>
</Col>
</Row>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'add-device'})(AddDevice)); export default withConfigContext(
Form.create({ name: 'add-device' })(AddDevice),
);

View File

@ -16,109 +16,118 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import {Button, Tooltip, Popconfirm, Divider} from "antd"; import { Button, Tooltip, Popconfirm, Divider } from 'antd';
class BulkActionBar extends React.Component { class BulkActionBar extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedMultiple: false,
selectedSingle: false,
canDelete: true,
};
}
constructor(props) { // This method checks whether NON-REMOVED devices are selected
super(props); onDeleteDeviceCall = () => {
this.state = { let tempDeleteState;
selectedMultiple: false, for (let i = 0; i < this.props.selectedRows.length; i++) {
selectedSingle: false, if (this.props.selectedRows[i].enrolmentInfo.status != 'REMOVED') {
canDelete: true, tempDeleteState = false;
} break;
}
tempDeleteState = true;
} }
this.setState({ canDelete: tempDeleteState });
};
//This method checks whether NON-REMOVED devices are selected onConfirmDelete = () => {
onDeleteDeviceCall = () => { if (this.state.canDelete) {
let tempDeleteState; this.props.deleteDevice();
for(let i=0; i < this.props.selectedRows.length; i++){ }
if(this.props.selectedRows[i].enrolmentInfo.status != "REMOVED"){ };
tempDeleteState = false;
break; onConfirmDisenroll = () => {
this.props.disenrollDevice();
};
onDeviceGroupCall = () => {
this.props.getGroups();
};
render() {
const isSelected = this.props.selectedRows.length > 0;
const isSelectedSingle = this.props.selectedRows.length == 1;
return (
<div style={{ display: isSelected ? 'inline' : 'none', padding: '11px' }}>
<Tooltip
placement="bottom"
title={'Delete Device'}
autoAdjustOverflow={true}
>
<Popconfirm
placement="topLeft"
title={
this.state.canDelete
? 'Are you sure you want to delete?'
: 'You can only delete disenrolled devices'
} }
tempDeleteState = true; onConfirm={this.onConfirmDelete}
} okText="Ok"
this.setState({canDelete:tempDeleteState}) cancelText="Cancel"
}; >
<Button
onConfirmDelete = () => { type="link"
if (this.state.canDelete) { shape="circle"
this.props.deleteDevice(); icon="delete"
} size={'default'}
}; onClick={this.onDeleteDeviceCall}
disabled={!isSelected}
onConfirmDisenroll = () => { style={{ margin: '2px' }}
this.props.disenrollDevice(); />
}; </Popconfirm>
</Tooltip>
onDeviceGroupCall = () => { <Divider type="vertical" />
this.props.getGroups(); <Tooltip placement="bottom" title={'Disenroll Device'}>
} <Popconfirm
placement="topLeft"
render() { title={'Are you sure?'}
const isSelected = this.props.selectedRows.length > 0; onConfirm={this.onConfirmDisenroll}
const isSelectedSingle = this.props.selectedRows.length == 1; okText="Ok"
disabled={!isSelectedSingle}
return ( cancelText="Cancel"
<div style={{display: isSelected ? "inline" : "none", padding: '11px'}}> >
<Button
<Tooltip type="link"
placement="bottom" shape="circle"
title={"Delete Device"} icon="close"
autoAdjustOverflow={true}> size={'default'}
<Popconfirm style={{
placement="topLeft" display: isSelectedSingle ? 'inline' : 'none',
title={ margin: '2px',
this.state.canDelete ? }}
"Are you sure you want to delete?" : "You can only delete disenrolled devices"} />
onConfirm={this.onConfirmDelete} </Popconfirm>
okText="Ok" </Tooltip>
cancelText="Cancel"> <Divider
type="vertical"
<Button style={{ display: isSelectedSingle ? 'inline-block' : 'none' }}
type="link" />
shape="circle" <Tooltip placement="bottom" title={'Add to group'}>
icon="delete" <Button
size={'default'} type="link"
onClick={this.onDeleteDeviceCall} shape="circle"
disabled={isSelected ? false : true} icon="deployment-unit"
style={{margin: "2px"}}/> size={'default'}
</Popconfirm> onClick={this.onDeviceGroupCall}
</Tooltip> style={{ margin: '2px' }}
<Divider type="vertical"/> />
<Tooltip placement="bottom" title={"Disenroll Device"}> </Tooltip>
<Popconfirm </div>
placement="topLeft" );
title={"Are you sure?"} }
onConfirm={this.onConfirmDisenroll}
okText="Ok"
disabled={isSelectedSingle ? false : true}
cancelText="Cancel">
<Button
type="link"
shape="circle"
icon="close"
size={'default'}
style={{display:isSelectedSingle ? "inline" : "none", margin: "2px"}}/>
</Popconfirm>
</Tooltip>
<Divider type="vertical" style={{display:isSelectedSingle ? "inline-block" : "none"}}/>
<Tooltip placement="bottom" title={"Add to group"}>
<Button
type="link"
shape="circle"
icon="deployment-unit"
size={'default'}
onClick={this.onDeviceGroupCall}
style={{margin: "2px"}}/>
</Tooltip>
</div>
)
}
} }
export default BulkActionBar; export default BulkActionBar;

View File

@ -16,133 +16,126 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Card, Col, Icon, message, notification, Row, Typography} from "antd"; import { Card, Col, Icon, message, notification, Row } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography;
let config = null;
let apiUrl; let apiUrl;
class DeviceType extends React.Component { class DeviceType extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; TimeAgo.addLocale(en);
TimeAgo.addLocale(en); this.state = {
this.state = { data: [],
data: [], pagination: {},
pagination: {}, loading: false,
selectedRows: [],
};
}
componentDidMount() {
this.fetchUsers();
}
onClickCard = data => {
console.log(data);
this.props.onClickType();
};
// fetch data from api
fetchUsers = (params = {}) => {
const config = this.props.context;
this.setState({ loading: true });
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/device-types';
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [] data: JSON.parse(res.data.data),
}; pagination,
} });
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load device types.',
});
}
componentDidMount() { this.setState({ loading: false });
this.fetchUsers(); });
} };
onClickCard = (data) =>{ handleTableChange = (pagination, filters, sorter) => {
console.log(data); const pager = { ...this.state.pagination };
this.props.onClickType(); pager.current = pagination.current;
}; this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
//fetch data from api render() {
fetchUsers = (params = {}) => { const { data } = this.state;
const config = this.props.context; const { Meta } = Card;
this.setState({loading: true}); const itemCard = data.map(data => (
<Col span={5} key={data.id}>
// get current page <Card
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; size="default"
style={{ width: 150 }}
const extraParams = { bordered={true}
offset: 10 * (currentPage - 1), //calculate the offset onClick={this.onClickCard}
limit: 10, cover={
}; <Icon
type="android"
const encodedExtraParams = Object.keys(extraParams) key="device-types"
.map(key => key + '=' + extraParams[key]).join('&'); style={{
color: '#ffffff',
apiUrl = window.location.origin + config.serverConfig.invoker.uri + backgroundColor: '#4b92db',
config.serverConfig.invoker.deviceMgt + fontSize: '100px',
"/device-types"; padding: '20px',
}}
//send request to the invokerss />
axios.get(apiUrl).then(res => { }
if (res.status === 200) { >
const pagination = {...this.state.pagination}; <Meta title={data.name} />
this.setState({ </Card>
loading: false, </Col>
data: JSON.parse(res.data.data), ));
pagination, return (
}); <div>
} <Row gutter={16}>{itemCard}</Row>
</div>
}).catch((error) => { );
if (error.hasOwnProperty("response") && error.response.status === 401) { }
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load device types.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
const { Meta } = Card;
const itemCard = data.map((data) =>
<Col span={5} key={data.id}>
<Card
size="default"
style={{ width: 150 }}
bordered={true}
onClick={this.onClickCard}
cover={<Icon type="android" key="device-types" style={{color:'#ffffff',
backgroundColor:'#4b92db', fontSize: '100px', padding:'20px'}}/>}
>
<Meta
title={data.name}
/>
</Card>
</Col>
);
return (
<div>
<Row gutter={16}>
{itemCard}
</Row>
</div>
);
}
} }
export default withConfigContext(DeviceType); export default withConfigContext(DeviceType);

View File

@ -16,471 +16,523 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Icon, message, Modal, notification, Select, Table, Tag, Tooltip, Typography} from "antd"; import {
import TimeAgo from 'javascript-time-ago' Icon,
message,
Modal,
notification,
Select,
Table,
Tag,
Tooltip,
} from 'antd';
import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
import BulkActionBar from "./BulkActionBar"; import BulkActionBar from './BulkActionBar';
const {Text} = Typography;
let config = null; let config = null;
const columns = [ const columns = [
{ {
title: 'Device', title: 'Device',
dataIndex: 'name', dataIndex: 'name',
width: 100, width: 100,
}, },
{ {
title: 'Type', title: 'Type',
dataIndex: 'type', dataIndex: 'type',
key: 'type', key: 'type',
render: type => { // eslint-disable-next-line react/display-name
const defaultPlatformIcons = config.defaultPlatformIcons; render: type => {
let icon = defaultPlatformIcons.default.icon; const defaultPlatformIcons = config.defaultPlatformIcons;
let color = defaultPlatformIcons.default.color; let icon = defaultPlatformIcons.default.icon;
let theme = defaultPlatformIcons.default.theme; let color = defaultPlatformIcons.default.color;
let theme = defaultPlatformIcons.default.theme;
if (defaultPlatformIcons.hasOwnProperty(type)) { if (defaultPlatformIcons.hasOwnProperty(type)) {
icon = defaultPlatformIcons[type].icon; icon = defaultPlatformIcons[type].icon;
color = defaultPlatformIcons[type].color; color = defaultPlatformIcons[type].color;
theme = defaultPlatformIcons[type].theme; theme = defaultPlatformIcons[type].theme;
} }
return ( return (
<span style={{fontSize: 20, color: color, textAlign: "center"}}> <span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
<Icon type={icon} theme={theme}/> <Icon type={icon} theme={theme} />
</span> </span>
); );
}
// todo add filtering options
}, },
{ // todo add filtering options
title: 'Owner', },
dataIndex: 'enrolmentInfo', {
key: 'owner', title: 'Owner',
render: enrolmentInfo => enrolmentInfo.owner dataIndex: 'enrolmentInfo',
// todo add filtering options key: 'owner',
render: enrolmentInfo => enrolmentInfo.owner,
// todo add filtering options
},
{
title: 'Ownership',
dataIndex: 'enrolmentInfo',
key: 'ownership',
render: enrolmentInfo => enrolmentInfo.ownership,
// todo add filtering options
},
{
title: 'Status',
dataIndex: 'enrolmentInfo',
key: 'status',
// eslint-disable-next-line react/display-name
render: enrolmentInfo => {
const status = enrolmentInfo.status.toLowerCase();
let color = '#f9ca24';
switch (status) {
case 'active':
color = '#badc58';
break;
case 'created':
color = '#6ab04c';
break;
case 'removed':
color = '#ff7979';
break;
case 'inactive':
color = '#f9ca24';
break;
case 'blocked':
color = '#636e72';
break;
}
return <Tag color={color}>{status}</Tag>;
}, },
{ // todo add filtering options
title: 'Ownership', },
dataIndex: 'enrolmentInfo', {
key: 'ownership', title: 'Last Updated',
render: enrolmentInfo => enrolmentInfo.ownership dataIndex: 'enrolmentInfo',
// todo add filtering options key: 'dateOfLastUpdate',
// eslint-disable-next-line react/display-name
render: data => {
const { dateOfLastUpdate } = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return (
<Tooltip title={new Date(dateOfLastUpdate).toString()}>
{timeAgoString}
</Tooltip>
);
}, },
{ // todo add filtering options
title: 'Status', },
dataIndex: 'enrolmentInfo',
key: 'status',
render: (enrolmentInfo) => {
const status = enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "removed":
color = "#ff7979";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
}
return <Tag color={color}>{status}</Tag>;
}
// todo add filtering options
},
{
title: 'Last Updated',
dataIndex: 'enrolmentInfo',
key: 'dateOfLastUpdate',
render: (data) => {
const {dateOfLastUpdate} = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return <Tooltip title={new Date(dateOfLastUpdate).toString()}>{timeAgoString}</Tooltip>;
}
// todo add filtering options
}
]; ];
const getTimeAgo = (time) => { const getTimeAgo = time => {
const timeAgo = new TimeAgo('en-US'); const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time); return timeAgo.format(time);
}; };
class DeviceTable extends React.Component { class DeviceTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; config = this.props.context;
TimeAgo.addLocale(en); TimeAgo.addLocale(en);
this.state = { this.state = {
data: [], data: [],
pagination: {}, pagination: {},
loading: false,
selectedRows: [],
deviceGroups: [],
groupModalVisible: false,
selectedGroupId: [],
selectedRowKeys: [],
};
}
componentDidMount() {
this.fetch();
}
// fetch data from api
fetch = (params = {}) => {
const config = this.props.context;
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), // calculate the offset
limit: 10,
requireDeviceInfo: true,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key])
.join('&');
// send request to the invoker
axios
.get(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/devices?' +
encodedExtraParams,
)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [], data: res.data.data.devices,
deviceGroups: [], pagination,
groupModalVisible: false, });
selectedGroupId: [], }
selectedRowKeys:[] })
}; .catch(error => {
} if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
componentDidMount() { message.error('You are not logged in');
this.fetch(); window.location.href = window.location.origin + '/entgra/login';
} } else {
notification.error({
//fetch data from api message: 'There was a problem',
fetch = (params = {}) => { duration: 0,
const config = this.props.context; description: 'Error occurred while trying to load devices.',
this.setState({loading: true}); });
// get current page
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), //calculate the offset
limit: 10,
requireDeviceInfo: true,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key]).join('&');
//send request to the invoker
axios.get(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/devices?" + encodedExtraParams,
).then(res => {
if (res.status === 200) {
const pagination = {...this.state.pagination};
this.setState({
loading: false,
data: res.data.data.devices,
pagination
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load devices.",
});
}
this.setState({loading: false});
});
};
deleteDevice = () => {
const config = this.props.context;
this.setState({loading: true});
const deviceData = this.state.selectedRows.map(obj => obj.deviceIdentifier);
//send request to the invoker
axios.put(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/admin/devices/permanent-delete",
deviceData,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 200) {
this.fetch();
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to delete devices.",
});
}
this.setState({loading: false});
});
};
disenrollDevice = () => {
const config = this.props.context;
this.setState({loading: true});
const deviceData = this.state.selectedRows[0];
//send request to the invoker
axios.delete(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/devices/type/" + deviceData.type + "/id/" + deviceData.deviceIdentifier,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 200) {
this.fetch();
this.setState({
selectedRowKeys:[]
})
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully dis-enrolled the device.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to dis-enroll devices.",
});
}
this.setState({loading: false});
});
};
addDevicesToGroup = (groupId) => {
const config = this.props.context;
this.setState({loading: true});
let apiUrl;
let deviceData;
if (this.state.selectedRows.length === 1) {
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups/device/assign";
deviceData = {
deviceIdentifier: {
id: this.state.selectedRows[0].deviceIdentifier,
type: this.state.selectedRows[0].type
},
deviceGroupIds: groupId
}
} else if (!groupId[0]){
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups/id/" + groupId + "/devices/add";
deviceData = this.state.selectedRows.map(obj => ({id: obj.deviceIdentifier, type: obj.type}));
} else{
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups/id/" + groupId[0] + "/devices/add";
deviceData = this.state.selectedRows.map(obj => ({id: obj.deviceIdentifier, type: obj.type}));
} }
//send request to the invoker this.setState({ loading: false });
axios.post( });
apiUrl, };
deviceData,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 200) {
this.setState({
loading: false
});
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully added to the device group.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while adding to the device group.",
});
}
this.setState({loading: false}); deleteDevice = () => {
}); const config = this.props.context;
}; this.setState({ loading: true });
getGroups = () => { const deviceData = this.state.selectedRows.map(obj => obj.deviceIdentifier);
this.setState({
groupModalVisible: true
});
//send request to the invoker
axios.get(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups"
).then(res => {
this.setState({deviceGroups: res.data.data.deviceGroups})
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while retrieving device groups.",
});
}
this.setState({loading: false}); // send request to the invoker
}); axios
}; .put(
window.location.origin +
handleOk = e => { config.serverConfig.invoker.uri +
if(this.state.selectedGroupId){ config.serverConfig.invoker.deviceMgt +
this.addDevicesToGroup(this.state.selectedGroupId); '/admin/devices/permanent-delete',
this.setState({ deviceData,
groupModalVisible: false { headers: { 'Content-Type': 'application/json' } },
}); )
}else{ .then(res => {
notification["error"]({ if (res.status === 200) {
message: "There was a problem", this.fetch();
duration: 0, }
description: })
"Please select a group.", .catch(error => {
}); if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to delete devices.',
});
} }
}; this.setState({ loading: false });
});
};
handleCancel = e => { disenrollDevice = () => {
this.setState({ const config = this.props.context;
groupModalVisible: false, this.setState({ loading: true });
});
};
onGroupSelectChange = (value) => { const deviceData = this.state.selectedRows[0];
this.setState({selectedGroupId: value});
};
handleTableChange = (pagination, filters, sorter) => { // send request to the invoker
const pager = {...this.state.pagination}; axios
pager.current = pagination.current; .delete(
this.setState({ window.location.origin +
pagination: pager, config.serverConfig.invoker.uri +
}); config.serverConfig.invoker.deviceMgt +
this.fetch({ '/devices/type/' +
results: pagination.pageSize, deviceData.type +
page: pagination.current, '/id/' +
sortField: sorter.field, deviceData.deviceIdentifier,
sortOrder: sorter.order, { headers: { 'Content-Type': 'application/json' } },
...filters, )
}); .then(res => {
}; if (res.status === 200) {
this.fetch();
onSelectChange = (selectedRowKeys, selectedRows) => { this.setState({
this.setState({ selectedRowKeys: [],
selectedRowKeys, });
selectedRows: selectedRows notification.success({
}); message: 'Done',
}; duration: 4,
description: 'Successfully dis-enrolled the device.',
render() { });
const {data, pagination, loading, selectedRows, selectedRowKeys} = this.state; }
const isSelectedSingle = this.state.selectedRows.length == 1; })
.catch(error => {
let selectedText; if (error.hasOwnProperty('response') && error.response.status === 401) {
if(isSelectedSingle){ // todo display a popop with error
selectedText = "You have selected 1 device" message.error('You are not logged in');
}else{ window.location.href = window.location.origin + '/entgra/login';
selectedText = "You have selected " + this.state.selectedRows.length + " devices" } else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to dis-enroll devices.',
});
} }
const rowSelection = { this.setState({ loading: false });
selectedRowKeys, });
selectedRows, };
onChange: this.onSelectChange,
};
let item = this.state.deviceGroups.map((data) => addDevicesToGroup = groupId => {
<Select.Option const config = this.props.context;
value={data.id} this.setState({ loading: true });
key={data.id}>
{data.name}
</Select.Option>);
return (
<div>
<BulkActionBar
deleteDevice={this.deleteDevice}
getGroups={this.getGroups}
disenrollDevice={this.disenrollDevice}
selectedRows={this.state.selectedRows}/>
<div>
<Table
columns={columns}
rowKey={record => (record.deviceIdentifier + record.enrolmentInfo.owner + record.enrolmentInfo.ownership)}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={rowSelection}
/>
</div>
<Modal let apiUrl;
title="Grouping Devices" let deviceData;
width="350px" if (this.state.selectedRows.length === 1) {
visible={this.state.groupModalVisible} apiUrl =
onOk={this.handleOk} window.location.origin +
onCancel={this.handleCancel} config.serverConfig.invoker.uri +
> config.serverConfig.invoker.deviceMgt +
<p>{selectedText}</p> '/groups/device/assign';
<Select deviceData = {
mode={isSelectedSingle ? "multiple" : "default"} deviceIdentifier: {
showSearch id: this.state.selectedRows[0].deviceIdentifier,
style={{display:"block"}} type: this.state.selectedRows[0].type,
placeholder="Select Group" },
optionFilterProp="children" deviceGroupIds: groupId,
onChange={this.onGroupSelectChange} };
> } else if (!groupId[0]) {
{item} apiUrl =
</Select> window.location.origin +
</Modal> config.serverConfig.invoker.uri +
</div> config.serverConfig.invoker.deviceMgt +
); '/groups/id/' +
groupId +
'/devices/add';
deviceData = this.state.selectedRows.map(obj => ({
id: obj.deviceIdentifier,
type: obj.type,
}));
} else {
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/groups/id/' +
groupId[0] +
'/devices/add';
deviceData = this.state.selectedRows.map(obj => ({
id: obj.deviceIdentifier,
type: obj.type,
}));
} }
// send request to the invoker
axios
.post(apiUrl, deviceData, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
if (res.status === 200) {
this.setState({
loading: false,
});
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully added to the device group.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while adding to the device group.',
});
}
this.setState({ loading: false });
});
};
getGroups = () => {
this.setState({
groupModalVisible: true,
});
// send request to the invoker
axios
.get(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/groups',
)
.then(res => {
this.setState({ deviceGroups: res.data.data.deviceGroups });
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while retrieving device groups.',
});
}
this.setState({ loading: false });
});
};
handleOk = e => {
if (this.state.selectedGroupId) {
this.addDevicesToGroup(this.state.selectedGroupId);
this.setState({
groupModalVisible: false,
});
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Please select a group.',
});
}
};
handleCancel = e => {
this.setState({
groupModalVisible: false,
});
};
onGroupSelectChange = value => {
this.setState({ selectedGroupId: value });
};
handleTableChange = (pagination, filters, sorter) => {
const pager = { ...this.state.pagination };
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
onSelectChange = (selectedRowKeys, selectedRows) => {
this.setState({
selectedRowKeys,
selectedRows: selectedRows,
});
};
render() {
const {
data,
pagination,
loading,
selectedRows,
selectedRowKeys,
} = this.state;
const isSelectedSingle = this.state.selectedRows.length == 1;
let selectedText;
if (isSelectedSingle) {
selectedText = 'You have selected 1 device';
} else {
selectedText =
'You have selected ' + this.state.selectedRows.length + ' devices';
}
const rowSelection = {
selectedRowKeys,
selectedRows,
onChange: this.onSelectChange,
};
let item = this.state.deviceGroups.map(data => (
<Select.Option value={data.id} key={data.id}>
{data.name}
</Select.Option>
));
return (
<div>
<BulkActionBar
deleteDevice={this.deleteDevice}
getGroups={this.getGroups}
disenrollDevice={this.disenrollDevice}
selectedRows={this.state.selectedRows}
/>
<div>
<Table
columns={columns}
rowKey={record =>
record.deviceIdentifier +
record.enrolmentInfo.owner +
record.enrolmentInfo.ownership
}
dataSource={data}
pagination={{
...pagination,
size: 'small',
// position: "top",
showTotal: (total, range) =>
`showing ${range[0]}-${range[1]} of ${total} devices`,
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={rowSelection}
/>
</div>
<Modal
title="Grouping Devices"
width="350px"
visible={this.state.groupModalVisible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<p>{selectedText}</p>
<Select
mode={isSelectedSingle ? 'multiple' : 'default'}
showSearch
style={{ display: 'block' }}
placeholder="Select Group"
optionFilterProp="children"
onChange={this.onGroupSelectChange}
>
{item}
</Select>
</Modal>
</div>
);
}
} }
export default withConfigContext(DeviceTable); export default withConfigContext(DeviceTable);

View File

@ -1,87 +1,97 @@
import React from 'react'; import React from 'react';
import {Collapse, Button, Divider, message, notification , Select} from 'antd'; import { Button, Divider, message, notification } from 'antd';
import TimeAgo from "javascript-time-ago/modules/JavascriptTimeAgo"; import TimeAgo from 'javascript-time-ago/modules/JavascriptTimeAgo';
import en from "javascript-time-ago/locale/en"; import en from 'javascript-time-ago/locale/en';
import axios from "axios"; import axios from 'axios';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const { Option } = Select;
class EnrollAgent extends React.Component { class EnrollAgent extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.config = this.props.context; this.config = this.props.context;
TimeAgo.addLocale(en); TimeAgo.addLocale(en);
this.state = { this.state = {
data: [], data: [],
pagination: {}, pagination: {},
loading: false, loading: false,
selectedRows: [], selectedRows: [],
visibleSelector: {display : 'none'} visibleSelector: { display: 'none' },
};
}
componentDidMount() {
this.getConfigData();
}; };
}
componentDidMount() {
this.getConfigData();
}
onGetEnrollmentQR = () =>{ onGetEnrollmentQR = () => {
this.setState({ this.setState({
visibleSelector: {display : 'block'} visibleSelector: { display: 'block' },
}) });
}; };
getConfigData = () =>{ getConfigData = () => {
axios.get( axios
window.location.origin + this.config.serverConfig.invoker.uri + .get(
"/device-mgt/android/v1.0/configuration" window.location.origin +
).then(res => { this.config.serverConfig.invoker.uri +
let data = res.data.data; '/device-mgt/android/v1.0/configuration',
}).catch((error) => { )
if (error.hasOwnProperty("response") && error.response.status === 401) { .catch(error => {
//todo display a popop with error if (error.hasOwnProperty('response') && error.response.status === 401) {
message.error('You are not logged in'); // todo display a popop with error
window.location.href = window.location.origin + '/entgra/login'; message.error('You are not logged in');
} else { window.location.href = window.location.origin + '/entgra/login';
notification["error"]({ } else {
message: "There was a problem", notification.error({
duration: 0, message: 'There was a problem',
description: duration: 0,
"Error occurred while retrieving device groups.", description: 'Error occurred while retrieving device groups.',
}); });
} }
});
};
}); render() {
}; return (
<div>
<Divider orientation="left">Step 01 - Get your Android Agent.</Divider>
<div>
<p>
The Android agent can be downloaded by using following QR. The
generated QR code can be scanned, and the agent APK downloaded from
the link, and transferred to the device and then installed.
</p>
</div>
<div style={{ margin: '30px' }}>
<Button type="primary" size={'default'}>
Get Android Agent
</Button>
</div>
<Divider orientation="left">
Step 02 - Enroll the Android Agent.
</Divider>
render() { <div>
return ( <p>
<div> {' '}
<Divider orientation="left">Step 01 - Get your Android Agent.</Divider> Your device can be enrolled with Entgra IoTS automatically via QR
<div> code. To enroll first download agent as mentioned in Step 1 then
<p>The Android agent can be downloaded by using following QR. proceed with the ENROLL WITH QR option from the device setup
The generated QR code can be scanned, and the agent APK downloaded from the link, activity. Thereafter select the ownership configuration and scan the
and transferred to the device and then installed.</p> generated QR to complete the process.
</div> </p>
<div style={{ margin:'30px'}}> </div>
<Button type="primary" size={"default"}> <div style={{ margin: '30px' }}>
Get Android Agent <Button
</Button> type="primary"
</div> size={'default'}
<Divider orientation="left">Step 02 - Enroll the Android Agent.</Divider> onClick={this.onGetEnrollmentQR}
>
<div> Enroll Using QR
<p> Your device can be enrolled with Entgra IoTS automatically via QR code. </Button>
To enroll first download agent as mentioned in Step 1 then proceed with the ENROLL WITH </div>
QR option from the device setup activity. Thereafter select the ownership configuration </div>
and scan the generated QR to complete the process.</p> );
</div> }
<div style={{ margin:'30px'}}>
<Button type="primary" size={"default"} onClick={this.onGetEnrollmentQR}>
Enroll Using QR
</Button>
</div>
</div>
);
}
} }
export default withConfigContext(EnrollAgent); export default withConfigContext(EnrollAgent);

View File

@ -16,232 +16,252 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Icon, message, notification, Table, Tag, Tooltip, Typography} from "antd"; import { Icon, message, notification, Table, Tag, Tooltip } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography;
let config = null; let config = null;
let apiUrl; let apiUrl;
const columns = [ const columns = [
{ {
title: 'Device', title: 'Device',
dataIndex: 'name', dataIndex: 'name',
width: 100, width: 100,
}, },
{ {
title: 'Type', title: 'Type',
dataIndex: 'type', dataIndex: 'type',
key: 'type', key: 'type',
render: type => { // eslint-disable-next-line react/display-name
const defaultPlatformIcons = config.defaultPlatformIcons; render: type => {
let icon = defaultPlatformIcons.default.icon; const defaultPlatformIcons = config.defaultPlatformIcons;
let color = defaultPlatformIcons.default.color; let icon = defaultPlatformIcons.default.icon;
let theme = defaultPlatformIcons.default.theme; let color = defaultPlatformIcons.default.color;
let theme = defaultPlatformIcons.default.theme;
if (defaultPlatformIcons.hasOwnProperty(type)) { if (defaultPlatformIcons.hasOwnProperty(type)) {
icon = defaultPlatformIcons[type].icon; icon = defaultPlatformIcons[type].icon;
color = defaultPlatformIcons[type].color; color = defaultPlatformIcons[type].color;
theme = defaultPlatformIcons[type].theme; theme = defaultPlatformIcons[type].theme;
} }
return ( return (
<span style={{fontSize: 20, color: color, textAlign: "center"}}> <span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
<Icon type={icon} theme={theme}/> <Icon type={icon} theme={theme} />
</span> </span>
); );
}
// todo add filtering options
}, },
{ // todo add filtering options
title: 'Owner', },
dataIndex: 'enrolmentInfo', {
key: 'owner', title: 'Owner',
render: enrolmentInfo => enrolmentInfo.owner dataIndex: 'enrolmentInfo',
// todo add filtering options key: 'owner',
render: enrolmentInfo => enrolmentInfo.owner,
// todo add filtering options
},
{
title: 'Ownership',
dataIndex: 'enrolmentInfo',
key: 'ownership',
render: enrolmentInfo => enrolmentInfo.ownership,
// todo add filtering options
},
{
title: 'Status',
dataIndex: 'enrolmentInfo',
key: 'status',
// eslint-disable-next-line react/display-name
render: enrolmentInfo => {
const status = enrolmentInfo.status.toLowerCase();
let color = '#f9ca24';
switch (status) {
case 'active':
color = '#badc58';
break;
case 'created':
color = '#6ab04c';
break;
case 'removed':
color = '#ff7979';
break;
case 'inactive':
color = '#f9ca24';
break;
case 'blocked':
color = '#636e72';
break;
}
return <Tag color={color}>{status}</Tag>;
}, },
{ // todo add filtering options
title: 'Ownership', },
dataIndex: 'enrolmentInfo', {
key: 'ownership', title: 'Last Updated',
render: enrolmentInfo => enrolmentInfo.ownership dataIndex: 'enrolmentInfo',
// todo add filtering options key: 'dateOfLastUpdate',
// eslint-disable-next-line react/display-name
render: data => {
const { dateOfLastUpdate } = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return (
<Tooltip title={new Date(dateOfLastUpdate).toString()}>
{timeAgoString}
</Tooltip>
);
}, },
{ // todo add filtering options
title: 'Status', },
dataIndex: 'enrolmentInfo',
key: 'status',
render: (enrolmentInfo) => {
const status = enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "removed":
color = "#ff7979";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
}
return <Tag color={color}>{status}</Tag>;
}
// todo add filtering options
},
{
title: 'Last Updated',
dataIndex: 'enrolmentInfo',
key: 'dateOfLastUpdate',
render: (data) => {
const {dateOfLastUpdate} = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return <Tooltip title={new Date(dateOfLastUpdate).toString()}>{timeAgoString}</Tooltip>;
}
// todo add filtering options
}
]; ];
const getTimeAgo = (time) => { const getTimeAgo = time => {
const timeAgo = new TimeAgo('en-US'); const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time); return timeAgo.format(time);
}; };
class ReportDeviceTable extends React.Component { class ReportDeviceTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; config = this.props.context;
TimeAgo.addLocale(en); TimeAgo.addLocale(en);
this.state = { this.state = {
data: [], data: [],
pagination: {}, pagination: {},
loading: false,
selectedRows: [],
paramsObj: {},
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows,
});
},
};
componentDidMount() {
this.fetch();
}
// Rerender component when parameters change
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.paramsObject !== this.props.paramsObject) {
this.fetch();
}
}
// fetch data from api
fetch = (params = {}) => {
const config = this.props.context;
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
this.props.paramsObject.offset = 10 * (currentPage - 1); // calculate the offset
this.props.paramsObject.limit = 10;
const encodedExtraParams = Object.keys(this.props.paramsObject)
.map(key => key + '=' + this.props.paramsObject[key])
.join('&');
if (
this.props.paramsObject.from == null &&
this.props.paramsObject.to == null
) {
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/devices?' +
encodedExtraParams;
} else {
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/reports/devices?' +
encodedExtraParams;
}
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [], data: res.data.data.devices,
paramsObj:{} pagination,
}; });
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows
})
} }
}; })
.catch(error => {
componentDidMount() { if (error.hasOwnProperty('response') && error.response.status === 401) {
this.fetch(); // todo display a popop with error
} message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
//Rerender component when parameters change } else {
componentDidUpdate(prevProps, prevState, snapshot) { notification.error({
if(prevProps.paramsObject !== this.props.paramsObject){ message: 'There was a problem',
this.fetch(); duration: 0,
} description: 'Error occurred while trying to load devices.',
} });
//fetch data from api
fetch = (params = {}) => {
const config = this.props.context;
this.setState({loading: true});
// get current page
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
this.props.paramsObject.offset = 10 * (currentPage -1); //calculate the offset
this.props.paramsObject.limit = 10;
const encodedExtraParams = Object.keys(this.props.paramsObject)
.map(key => key + '=' + this.props.paramsObject[key]).join('&');
if(this.props.paramsObject.from==null && this.props.paramsObject.to==null){
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/devices?" + encodedExtraParams;
}else{
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/reports/devices?" + encodedExtraParams;
} }
//send request to the invokerss this.setState({ loading: false });
axios.get(apiUrl).then(res => { });
if (res.status === 200) { };
const pagination = {...this.state.pagination};
this.setState({
loading: false,
data: res.data.data.devices,
pagination,
});
}
}).catch((error) => { handleTableChange = (pagination, filters, sorter) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { const pager = { ...this.state.pagination };
//todo display a popop with error pager.current = pagination.current;
message.error('You are not logged in'); this.setState({
window.location.href = window.location.origin + '/entgra/login'; pagination: pager,
} else { });
notification["error"]({ this.fetch({
message: "There was a problem", results: pagination.pageSize,
duration: 0, page: pagination.current,
description:"Error occurred while trying to load devices.", sortField: sorter.field,
}); sortOrder: sorter.order,
} ...filters,
});
};
this.setState({loading: false}); render() {
}); const { data, pagination, loading } = this.state;
}; return (
<div>
handleTableChange = (pagination, filters, sorter) => { <Table
const pager = {...this.state.pagination}; columns={columns}
pager.current = pagination.current; rowKey={record =>
this.setState({ record.deviceIdentifier +
pagination: pager, record.enrolmentInfo.owner +
}); record.enrolmentInfo.ownership
this.fetch({ }
results: pagination.pageSize, dataSource={data}
page: pagination.current, pagination={{
sortField: sorter.field, ...pagination,
sortOrder: sorter.order, size: 'small',
...filters, // position: "top",
}); showTotal: (total, range) =>
}; `showing ${range[0]}-${range[1]} of ${total} devices`,
// showQuickJumper: true
render() { }}
loading={loading}
const {data, pagination, loading, selectedRows} = this.state; onChange={this.handleTableChange}
return ( rowSelection={this.rowSelection}
<div> />
<Table </div>
columns={columns} );
rowKey={record => (record.deviceIdentifier + record.enrolmentInfo.owner + record.enrolmentInfo.ownership)} }
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
);
}
} }
export default withConfigContext(ReportDeviceTable); export default withConfigContext(ReportDeviceTable);

View File

@ -16,66 +16,64 @@
* under the License. * under the License.
*/ */
import React, {Component, Fragment} from "react"; import React, { Component, Fragment } from 'react';
import { import {
Map, Map,
TileLayer, TileLayer,
Marker, Marker,
Polyline, Popup, Tooltip Polyline,
} from "react-leaflet"; Popup,
import {withConfigContext} from "../../../context/ConfigContext"; Tooltip,
} from 'react-leaflet';
import { withConfigContext } from '../../../context/ConfigContext';
class GeoCustomMap extends Component { class GeoCustomMap extends Component {
constructor(props) {
super(props);
}
constructor(props) { /**
super(props); * Polyline draw for historical locations
} * @param locationData - location data object
* @returns content
*/
polylineMarker = locationData => {
const polyMarkers = locationData.map(locationPoint => {
return [locationPoint.latitude, locationPoint.longitude];
});
/** return (
* Polyline draw for historical locations <div style={{ display: 'none' }}>
* @param locationData - location data object {
* @returns content <Polyline color="green" positions={polyMarkers}>
*/ <Popup>on the way</Popup>
polylineMarker = (locationData) => { </Polyline>
}
</div>
);
};
const polyMarkers = locationData render() {
.map(locationPoint => { const locationData = this.props.locationData;
return [locationPoint.latitude, locationPoint.longitude] const config = this.props.context;
}); const attribution = config.geoMap.attribution;
const url = config.geoMap.url;
return ( const startingPoint = [locationData[0].latitude, locationData[0].longitude];
<div style={{display: "none"}}>{ const zoom = config.geoMap.defaultZoomLevel;
<Polyline color="green" positions={polyMarkers}> return (
<Popup>on the way</Popup> <div style={{ backgroundColor: '#ffffff', borderRadius: 5, padding: 5 }}>
</Polyline> <Map center={startingPoint} zoom={zoom}>
}</div> <TileLayer url={url} attribution={attribution} />
); <Fragment>
}; {this.polylineMarker(locationData)}
<Marker position={startingPoint}>
render() { <Tooltip>Starting Location</Tooltip>
const locationData = this.props.locationData; </Marker>
const config = this.props.context; </Fragment>
const attribution = config.geoMap.attribution; </Map>
const url = config.geoMap.url; </div>
const startingPoint = [locationData[0].latitude, locationData[0].longitude]; );
const zoom = config.geoMap.defaultZoomLevel; }
return (
<div style={{backgroundColor: "#ffffff", borderRadius: 5, padding: 5}}>
<Map center={startingPoint} zoom={zoom}>
<TileLayer
url={url}
attribution={attribution}
/>
<Fragment>
{this.polylineMarker(locationData)}
<Marker position={startingPoint}>
<Tooltip>Starting Location</Tooltip>
</Marker>
</Fragment>
</Map>
</div>
);
}
} }
export default withConfigContext(GeoCustomMap); export default withConfigContext(GeoCustomMap);

View File

@ -16,266 +16,300 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import moment from "moment"; import moment from 'moment';
import {Button, Select, message, notification, Tag, Tooltip, Empty, DatePicker} from "antd"; import {
import axios from "axios"; Button,
import {withConfigContext} from "../../../context/ConfigContext"; Select,
import GeoCustomMap from "../geo-custom-map/GeoCustomMap"; message,
import "./GeoDashboard.css"; notification,
Tag,
Tooltip,
Empty,
DatePicker,
} from 'antd';
import axios from 'axios';
import { withConfigContext } from '../../../context/ConfigContext';
import GeoCustomMap from '../geo-custom-map/GeoCustomMap';
import './GeoDashboard.css';
class GeoDashboard extends React.Component { class GeoDashboard extends React.Component {
constructor(props) {
constructor(props) { super(props);
super(props); let start = moment(
let start = moment( new Date(
new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate(), 0, 0, 0, 0) new Date().getFullYear(),
); new Date().getMonth(),
let end = moment(start) new Date().getDate(),
.add(1, "days") 0,
.subtract(1, "seconds"); 0,
this.state = { 0,
deviceData: [], 0,
selectedDevice: '', ),
locationData: [], );
loading: false, let end = moment(start)
start: start, .add(1, 'days')
end: end, .subtract(1, 'seconds');
buttonTooltip: "Fetch Locations", this.state = {
}; deviceData: [],
} selectedDevice: '',
locationData: [],
componentDidMount() { loading: false,
this.fetchDevices(); start: start,
// this.fetchCurrentLocation(); end: end,
} buttonTooltip: 'Fetch Locations',
/**
* Call back on apply button in the date time picker
* @param startDate - start date
* @param endDate - end date
*/
applyCallback = (dates, dateStrings) => {
console.log("Apply Callback");
this.setState({
start: dateStrings[0],
end: dateStrings[1]
});
}; };
}
/** componentDidMount() {
* Api call handle on fetch location date button this.fetchDevices();
*/ // this.fetchCurrentLocation();
handleApiCall = () => { }
if (this.state.selectedDevice && this.state.start && this.state.end) { /**
const toInMills = moment(this.state.end); * Call back on apply button in the date time picker
const fromInMills = moment(this.state.start); * @param startDate - start date
const deviceType = this.state.selectedDevice.type; * @param endDate - end date
const deviceId = this.state.selectedDevice.deviceIdentifier; */
const config = this.props.context; applyCallback = (dates, dateStrings) => {
this.setState({loading: true}); console.log('Apply Callback');
this.setState({
start: dateStrings[0],
end: dateStrings[1],
});
};
axios.get(window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt /**
+ "/devices/" + deviceType + "/" + deviceId + "/location-history?" + "from=" + fromInMills + "&to=" + * Api call handle on fetch location date button
toInMills,).then(res => { */
if (res.status === 200) { handleApiCall = () => {
const locationData = JSON.parse(res.data.data); if (this.state.selectedDevice && this.state.start && this.state.end) {
this.setState({ const toInMills = moment(this.state.end);
loading: false, const fromInMills = moment(this.state.start);
locationData, const deviceType = this.state.selectedDevice.type;
}); const deviceId = this.state.selectedDevice.deviceIdentifier;
} const config = this.props.context;
}).catch((error) => { this.setState({ loading: true });
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to fetch locations......",
});
}
this.setState({loading: false}); axios
console.log(error); .get(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/devices/' +
deviceType +
'/' +
deviceId +
'/location-history?' +
'from=' +
fromInMills +
'&to=' +
toInMills,
)
.then(res => {
if (res.status === 200) {
const locationData = JSON.parse(res.data.data);
this.setState({
loading: false,
locationData,
}); });
} else { }
notification["error"]({ })
message: "There was a problem", .catch(error => {
duration: 0, if (
description: error.hasOwnProperty('response') &&
"Please provide a date range and a device.", error.response.status === 401
}); ) {
} message.error('You are not logged in');
}; window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description:
'Error occurred while trying to fetch locations......',
});
}
/** this.setState({ loading: false });
* Device dropdown list handler console.log(error);
* @param e - selected device data
*/
handleDeviceList = (e) => {
let selectedDevice = this.state.deviceData[e];
this.setState({selectedDevice})
};
/**
* render fetch location button
*/
fetchLocationButton = () => {
let flag;
let toolTip = "";
if (this.state.selectedDevice === "") {
flag = true;
toolTip = "Please select a Device";
}
return (
<Tooltip placement="rightBottom" title={toolTip}>
<Button disabled={flag}
onClick={this.handleApiCall}>
Fetch Locations
</Button>
</Tooltip>);
};
/**
* fetches device data to populate the dropdown list
*/
fetchDevices = () => {
const config = this.props.context;
this.setState({loading: true});
axios.get(
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt +
"/devices?excludeStatus=REMOVED",).then(res => {
if (res.status === 200) {
this.setState({
loading: false,
deviceData: res.data.data.devices,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load devices.",
});
}
this.setState({loading: false});
}); });
}; } else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Please provide a date range and a device.',
});
}
};
/** /**
* Geo Dashboard controller * Device dropdown list handler
*/ * @param e - selected device data
controllerBar = () => { */
handleDeviceList = e => {
let selectedDevice = this.state.deviceData[e];
this.setState({ selectedDevice });
};
const {RangePicker} = DatePicker; /**
let now = new Date(); * render fetch location button
let start = moment( */
new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0) fetchLocationButton = () => {
); let flag;
let end = moment(start) let toolTip = '';
.add(1, "days") if (this.state.selectedDevice === '') {
.subtract(1, "seconds"); flag = true;
let ranges = { toolTip = 'Please select a Device';
"Today Only": [moment(start), moment(end)], }
"Yesterday Only": [ return (
moment(start).subtract(1, "days"), <Tooltip placement="rightBottom" title={toolTip}>
moment(end).subtract(1, "days") <Button disabled={flag} onClick={this.handleApiCall}>
], Fetch Locations
"3 Days": [moment(start).subtract(3, "days"), moment(end)], </Button>
"5 Days": [moment(start).subtract(5, "days"), moment(end)], </Tooltip>
"1 Week": [moment(start).subtract(7, "days"), moment(end)], );
"2 Weeks": [moment(start).subtract(14, "days"), moment(end)], };
"1 Month": [moment(start).subtract(1, "months"), moment(end)],
};
let {deviceData} = this.state; /**
* fetches device data to populate the dropdown list
*/
fetchDevices = () => {
const config = this.props.context;
this.setState({ loading: true });
return ( axios
<div className="controllerDiv"> .get(
<RangePicker window.location.origin +
ranges={ranges} config.serverConfig.invoker.uri +
style={{marginRight: 20}} config.serverConfig.invoker.deviceMgt +
showTime '/devices?excludeStatus=REMOVED',
format="YYYY-MM-DD HH:mm:ss" )
defaultValue={[this.state.start, this.state.end]} .then(res => {
onChange={this.applyCallback} if (res.status === 200) {
this.setState({
/> loading: false,
deviceData: res.data.data.devices,
<Select });
showSearch }
style={{width: 220, marginRight: 20}} })
placeholder="Select a Device" .catch(error => {
optionFilterProp="children" if (error.hasOwnProperty('response') && error.response.status === 401) {
onChange={this.handleDeviceList} message.error('You are not logged in');
filterOption={(input, option) => window.location.href = window.location.origin + '/entgra/login';
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 } else {
} notification.error({
> message: 'There was a problem',
{deviceData.map((device, index) => duration: 0,
<Select.Option key={index} value={index}> description: 'Error occurred while trying to load devices.',
{device.name + " "}{this.statusTag(device)} });
</Select.Option>)}
</Select>
{this.fetchLocationButton()}
</div>
);
};
/**
* Creates color based tags on device status
* @param device - device object
*/
statusTag = (device) => {
const status = device.enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
} }
return <Tag color={color}>{status}</Tag> this.setState({ loading: false });
});
};
/**
* Geo Dashboard controller
*/
controllerBar = () => {
const { RangePicker } = DatePicker;
let now = new Date();
let start = moment(
new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0),
);
let end = moment(start)
.add(1, 'days')
.subtract(1, 'seconds');
let ranges = {
'Today Only': [moment(start), moment(end)],
'Yesterday Only': [
moment(start).subtract(1, 'days'),
moment(end).subtract(1, 'days'),
],
'3 Days': [moment(start).subtract(3, 'days'), moment(end)],
'5 Days': [moment(start).subtract(5, 'days'), moment(end)],
'1 Week': [moment(start).subtract(7, 'days'), moment(end)],
'2 Weeks': [moment(start).subtract(14, 'days'), moment(end)],
'1 Month': [moment(start).subtract(1, 'months'), moment(end)],
}; };
render() { let { deviceData } = this.state;
const locationData = [...this.state.locationData];
return ( return (
<div className="container"> <div className="controllerDiv">
{this.controllerBar()} <RangePicker
{(locationData.length > 0) ? ranges={ranges}
<GeoCustomMap locationData={locationData}/> style={{ marginRight: 20 }}
: showTime
<Empty/> format="YYYY-MM-DD HH:mm:ss"
} defaultValue={[this.state.start, this.state.end]}
</div> onChange={this.applyCallback}
); />
<Select
showSearch
style={{ width: 220, marginRight: 20 }}
placeholder="Select a Device"
optionFilterProp="children"
onChange={this.handleDeviceList}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >=
0
}
>
{deviceData.map((device, index) => (
<Select.Option key={index} value={index}>
{device.name + ' '}
{this.statusTag(device)}
</Select.Option>
))}
</Select>
{this.fetchLocationButton()}
</div>
);
};
/**
* Creates color based tags on device status
* @param device - device object
*/
statusTag = device => {
const status = device.enrolmentInfo.status.toLowerCase();
let color = '#f9ca24';
switch (status) {
case 'active':
color = '#badc58';
break;
case 'created':
color = '#6ab04c';
break;
case 'inactive':
color = '#f9ca24';
break;
case 'blocked':
color = '#636e72';
break;
} }
return <Tag color={color}>{status}</Tag>;
};
render() {
const locationData = [...this.state.locationData];
return (
<div className="container">
{this.controllerBar()}
{locationData.length > 0 ? (
<GeoCustomMap locationData={locationData} />
) : (
<Empty />
)}
</div>
);
}
} }
export default withConfigContext(GeoDashboard); export default withConfigContext(GeoDashboard);

View File

@ -16,160 +16,159 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import {Button, Form, Input, message, Modal, notification, Typography} from "antd"; import { Button, Form, Input, message, Modal, notification } from 'antd';
import axios from "axios"; import axios from 'axios';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography;
let config = null;
class AddGroup extends React.Component { class AddGroup extends React.Component {
constructor(props) {
super(props);
this.state = {
addModalVisible: false,
name: '',
description: '',
};
}
constructor(props) { onConfirmAdGroup = () => {
super(props); const config = this.props.context;
config = this.props.context;
this.state = { const groupData = {
addModalVisible: false, name: this.state.name,
name:'', description: this.state.description,
description:'', };
// send request to the invoker
axios
.post(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/groups',
groupData,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 201) {
this.props.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully added the group.',
});
} }
} })
.catch(error => {
onConfirmAdGroup = () => { if (error.hasOwnProperty('response') && error.response.status === 401) {
const config = this.props.context; // todo display a popop with error
message.error('You are not logged in');
const groupData = { window.location.href = window.location.origin + '/entgra/login';
name: this.state.name, } else {
description: this.state.description notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to add group.',
});
} }
});
};
//send request to the invoker openAddModal = () => {
axios.post( this.setState({
window.location.origin + config.serverConfig.invoker.uri + addModalVisible: true,
config.serverConfig.invoker.deviceMgt + });
"/groups", };
groupData,
{headers: {'Content-Type': 'application/json'}} handleAddOk = e => {
).then(res => { this.props.form.validateFields(err => {
if (res.status === 201) { if (!err) {
this.props.fetchGroups(); this.onConfirmAdGroup();
notification["success"]({ this.setState({
message: "Done", addModalVisible: false,
duration: 4,
description:
"Successfully added the group.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to add group.",
});
}
}); });
}; }
});
};
openAddModal = () => { handleAddCancel = e => {
this.setState({ this.setState({
addModalVisible:true addModalVisible: false,
}) });
}; };
handleAddOk = e => { onChangeName = e => {
this.props.form.validateFields(err => { this.setState({
if (!err) { name: e.currentTarget.value,
this.onConfirmAdGroup(); });
this.setState({ };
addModalVisible: false,
});
}
});
};
handleAddCancel = e => { onChangeDescription = e => {
this.setState({ this.setState({
addModalVisible: false, description: e.currentTarget.value,
}); });
}; };
onChangeName = (e) => { render() {
this.setState({ const { getFieldDecorator } = this.props.form;
name:e.currentTarget.value return (
}) <div>
}; <div>
<Button
onChangeDescription = (e) => { type="primary"
this.setState({ icon="plus"
description:e.currentTarget.value size={'default'}
}) onClick={this.openAddModal}
}; >
Add Group
render() { </Button>
const { getFieldDecorator } = this.props.form; </div>
return( <div>
<div> <Modal
<div> title="ADD NEW GROUP"
<Button type="primary" icon="plus" size={"default"} onClick={this.openAddModal}> width="40%"
Add Group visible={this.state.addModalVisible}
</Button> onOk={this.handleAddOk}
</div> onCancel={this.handleAddCancel}
<div> footer={[
<Modal <Button key="cancel" onClick={this.handleAddCancel}>
title="ADD NEW GROUP" Cancel
width="40%" </Button>,
visible={this.state.addModalVisible} <Button key="submit" type="primary" onClick={this.handleAddOk}>
onOk={this.handleAddOk} Submit
onCancel={this.handleAddCancel} </Button>,
footer={[ ]}
<Button key="cancel" onClick={this.handleAddCancel}> >
Cancel <div style={{ alignItems: 'center' }}>
</Button>, <p>Create new device group on IoT Server.</p>
<Button key="submit" type="primary" onClick={this.handleAddOk}> <Form labelCol={{ span: 5 }} wrapperCol={{ span: 18 }}>
Submit <Form.Item label="Name" style={{ display: 'block' }}>
</Button>, {getFieldDecorator('name', {
]} rules: [
> {
<div style={{alignItems:"center"}}> required: true,
<p>Create new device group on IoT Server.</p> message: 'Please input group name',
<Form },
labelCol={{ span: 5 }} ],
wrapperCol={{ span: 18 }} })(<Input onChange={this.onChangeName} />)}
> </Form.Item>
<Form.Item label="Name" style={{display:"block"}}> <Form.Item label="Description" style={{ display: 'block' }}>
{getFieldDecorator('name', { {getFieldDecorator('description', {
rules: [ rules: [
{ {
required: true, required: true,
message: 'Please input group name', message: 'Please input group description',
}, },
], ],
})(<Input onChange={this.onChangeName}/>)} })(<Input onChange={this.onChangeDescription} />)}
</Form.Item> </Form.Item>
<Form.Item label="Description" style={{display:"block"}}> </Form>
{getFieldDecorator('description', {
rules: [
{
required: true,
message: 'Please input group description',
},
],
})(<Input onChange={this.onChangeDescription}/>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
</div> </div>
) </Modal>
} </div>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'add-group'})(AddGroup)); export default withConfigContext(Form.create({ name: 'add-group' })(AddGroup));

View File

@ -16,364 +16,380 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import {
Button, Button,
Divider, Divider,
Form, Form,
Icon, Icon,
Input, Input,
message, message,
Modal, Modal,
notification, notification,
Popconfirm, Popconfirm,
Select, Select,
Tooltip, Tooltip,
Typography Typography,
} from "antd"; } from 'antd';
import axios from "axios"; import axios from 'axios';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography; const { Text } = Typography;
let config = null;
class GroupActions extends React.Component { class GroupActions extends React.Component {
constructor(props) {
super(props);
this.state = {
editModalVisible: false,
shareModalVisible: false,
name: this.props.data.name,
description: this.props.data.description,
groupDataObject: {},
rolesData: [],
shareRolesData: [],
};
}
constructor(props) { onConfirmDeleteGroup = () => {
super(props); const config = this.props.context;
config = this.props.context;
this.state = { // send request to the invoker
editModalVisible: false, axios
shareModalVisible: false, .delete(
name:this.props.data.name, window.location.origin +
description:this.props.data.description, config.serverConfig.invoker.uri +
groupDataObject:{}, config.serverConfig.invoker.deviceMgt +
rolesData:[], '/groups/id/' +
shareRolesData:[] this.props.data.id,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully deleted the group.',
});
} }
} })
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to delete group.',
});
}
});
};
onConfirmDeleteGroup = () => { onConfirmUpdateGroup = data => {
const config = this.props.context; const config = this.props.context;
//send request to the invoker // send request to the invoker
axios.delete( axios
window.location.origin + config.serverConfig.invoker.uri + .put(
config.serverConfig.invoker.deviceMgt + window.location.origin +
"/groups/id/" + this.props.data.id, config.serverConfig.invoker.uri +
{headers: {'Content-Type': 'application/json'}} config.serverConfig.invoker.deviceMgt +
'/groups/id/' +
this.props.data.id,
data,
)
.then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully updated the group.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to update group.',
});
}
});
};
).then(res => { fetchUserRoles = (params = {}) => {
if (res.status === 200) { const config = this.props.context;
this.props.fetchGroups();
notification["success"]({ const apiUrl =
message: "Done", window.location.origin +
duration: 4, config.serverConfig.invoker.uri +
description: config.serverConfig.invoker.deviceMgt +
"Successfully deleted the group.", '/roles';
});
} // send request to the invokerss
}).catch((error) => { axios
if (error.hasOwnProperty("response") && error.response.status === 401) { .get(apiUrl)
//todo display a popop with error .then(res => {
message.error('You are not logged in'); if (res.status === 200) {
window.location.href = window.location.origin + '/entgra/login'; this.setState({
} else { rolesData: res.data.data.roles,
notification["error"]({ });
message: "There was a problem", }
duration: 0, })
description: .catch(error => {
"Error occurred while trying to delete group.", if (error.hasOwnProperty('response') && error.response.status === 401) {
}); // todo display a popop with error
} message.error('You are not logged in');
}); window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load roles.',
});
}
});
};
onConfirmShareGroup = data => {
const config = this.props.context;
// send request to the invoker
axios
.post(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/groups/id/' +
this.props.data.id +
'/share',
data,
)
.then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully shared the group.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to share group.',
});
}
});
};
openEditModal = () => {
this.setState({
editModalVisible: true,
});
};
openShareModal = () => {
this.fetchUserRoles();
this.setState({
shareModalVisible: true,
});
};
handleEditOk = e => {
const groupDataObject = {
name: this.state.name,
description: this.state.description,
id: this.props.data.id,
owner: this.props.data.owner,
groupProperties: this.props.data.groupProperties,
}; };
onConfirmUpdateGroup = (data) => { this.setState({ groupDataObject });
const config = this.props.context;
//send request to the invoker this.props.form.validateFields(err => {
axios.put( if (!err) {
window.location.origin + config.serverConfig.invoker.uri + this.onConfirmUpdateGroup(this.state.groupDataObject);
config.serverConfig.invoker.deviceMgt +
"/groups/id/" + this.props.data.id,
data
).then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully updated the group.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to update group.",
});
}
});
};
fetchUserRoles = (params = {}) => {
const config = this.props.context;
const apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/roles";
//send request to the invokerss
axios.get(apiUrl).then(res => {
if (res.status === 200) {
this.setState({
rolesData: res.data.data.roles,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load roles.",
});
}
});
};
onConfirmShareGroup = (data) => {
const config = this.props.context;
//send request to the invoker
axios.post(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups/id/" + this.props.data.id + "/share",
data
).then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully shared the group.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to share group.",
});
}
});
}
openEditModal = () => {
this.setState({ this.setState({
editModalVisible:true editModalVisible: false,
})
};
openShareModal = () => {
this.fetchUserRoles();
this.setState({
shareModalVisible:true
})
}
handleEditOk = e => {
this.state.groupDataObject = {
name:this.state.name,
description:this.state.description,
id:this.props.data.id,
owner:this.props.data.owner,
groupProperties:this.props.data.groupProperties
};
this.props.form.validateFields(err => {
if (!err) {
this.onConfirmUpdateGroup(this.state.groupDataObject);
this.setState({
editModalVisible: false,
});
}
}); });
}; }
});
};
handleEditCancel = e => { handleEditCancel = e => {
this.setState({ this.setState({
editModalVisible: false, editModalVisible: false,
}); });
}; };
handleShareOk = e => { handleShareOk = e => {
this.setState({ this.setState({
shareModalVisible: false, shareModalVisible: false,
}); });
this.onConfirmShareGroup(this.state.shareRolesData); this.onConfirmShareGroup(this.state.shareRolesData);
}; };
handleShareCancel = e => { handleShareCancel = e => {
this.setState({ this.setState({
shareModalVisible: false, shareModalVisible: false,
}); });
}; };
onChangeName = (e) => { onChangeName = e => {
this.setState({ this.setState({
name:e.currentTarget.value name: e.currentTarget.value,
}) });
}; };
onChangeDescription = (e) => { onChangeDescription = e => {
this.setState({ this.setState({
description:e.currentTarget.value description: e.currentTarget.value,
}) });
}; };
handleRolesDropdownChange = (value) => { handleRolesDropdownChange = value => {
this.setState({ this.setState({
shareRolesData:value shareRolesData: value,
}) });
}; };
render() { render() {
const isAdminGroups = this.props.data.id==1 || this.props.data.id==2; const isAdminGroups = this.props.data.id == 1 || this.props.data.id == 2;
const { Option } = Select; const { getFieldDecorator } = this.props.form;
const { getFieldDecorator } = this.props.form; let item = this.state.rolesData.map(data => (
let item = this.state.rolesData.map((data) => <Select.Option value={data} key={data}>
<Select.Option {data}
value={data} </Select.Option>
key={data}> ));
{data} return (
</Select.Option>); <div>
return( <div style={{ display: isAdminGroups ? 'none' : 'inline' }}>
<div> <Tooltip placement="top" title={'Share Group'}>
<div style={{display:isAdminGroups ? "none" : "inline"}}> <a>
<Tooltip placement="top" title={"Share Group"}> <Icon type="share-alt" onClick={this.openShareModal} />
<a><Icon type="share-alt" onClick={this.openShareModal}/></a> </a>
</Tooltip> </Tooltip>
<Divider type="vertical" /> <Divider type="vertical" />
<Tooltip placement="top" title={"Edit Group"}> <Tooltip placement="top" title={'Edit Group'}>
<a><Icon type="edit" onClick={this.openEditModal}/></a> <a>
</Tooltip> <Icon type="edit" onClick={this.openEditModal} />
<Divider type="vertical" /> </a>
<Tooltip placement="bottom" title={"Delete Group"}> </Tooltip>
<Popconfirm <Divider type="vertical" />
placement="top" <Tooltip placement="bottom" title={'Delete Group'}>
title={"Are you sure?"} <Popconfirm
onConfirm={this.onConfirmDeleteGroup} placement="top"
okText="Ok" title={'Are you sure?'}
cancelText="Cancel"> onConfirm={this.onConfirmDeleteGroup}
<a><Text type="danger"><Icon type="delete"/></Text></a> okText="Ok"
</Popconfirm> cancelText="Cancel"
</Tooltip> >
</div> <a>
<div> <Text type="danger">
<Modal <Icon type="delete" />
title="Update Group" </Text>
width="40%" </a>
visible={this.state.editModalVisible} </Popconfirm>
onOk={this.handleEditOk} </Tooltip>
onCancel={this.handleEditCancel} </div>
footer={[ <div>
<Button key="cancel" onClick={this.handleEditCancel}> <Modal
Cancel title="Update Group"
</Button>, width="40%"
<Button key="submit" type="primary" onClick={this.handleEditOk}> visible={this.state.editModalVisible}
Submit onOk={this.handleEditOk}
</Button>, onCancel={this.handleEditCancel}
]} footer={[
> <Button key="cancel" onClick={this.handleEditCancel}>
<div style={{alignItems:"center"}}> Cancel
<p>Enter new name and description for the group</p> </Button>,
<Form <Button key="submit" type="primary" onClick={this.handleEditOk}>
labelCol={{ span: 5 }} Submit
wrapperCol={{ span: 18 }} </Button>,
> ]}
<Form.Item label="Name" style={{display:"block"}}> >
{getFieldDecorator( <div style={{ alignItems: 'center' }}>
'name', <p>Enter new name and description for the group</p>
{ <Form labelCol={{ span: 5 }} wrapperCol={{ span: 18 }}>
initialValue: this.props.data.name, <Form.Item label="Name" style={{ display: 'block' }}>
rules: [ {getFieldDecorator('name', {
{ initialValue: this.props.data.name,
required: true, rules: [
message: 'Please input group name', {
}, required: true,
], message: 'Please input group name',
})(<Input },
onChange={this.onChangeName}/>)} ],
</Form.Item> })(<Input onChange={this.onChangeName} />)}
<Form.Item label="Description" style={{display:"block"}}> </Form.Item>
{getFieldDecorator( <Form.Item label="Description" style={{ display: 'block' }}>
'description', {getFieldDecorator('description', {
{ initialValue: this.props.data.description,
initialValue: this.props.data.description, rules: [
rules: [ {
{ required: true,
required: true, message: 'Please input group description',
message: 'Please input group description', },
}, ],
], })(<Input onChange={this.onChangeDescription} />)}
})(<Input </Form.Item>
onChange={this.onChangeDescription}/>)} </Form>
</Form.Item>
</Form>
</div>
</Modal>
</div>
<div>
<Modal
title="Share Group"
width="500px"
visible={this.state.shareModalVisible}
onOk={this.handleShareOk}
onCancel={this.handleShareCancel}
footer={[
<Button key="new-role" onClick={this.handleShareCancel}>
New Role
</Button>,
<Button key="new-role-selection" onClick={this.handleShareCancel}>
New Role from Selection
</Button>,
<Button key="submit" type="primary" onClick={this.handleShareOk}>
Share
</Button>,
]}
>
<p>Select user role(s)</p>
<Select
mode="multiple"
defaultValue={"admin"}
style={{ width: '100%' }}
onChange={this.handleRolesDropdownChange}>
{item}
</Select>,
</Modal>
</div>
</div> </div>
) </Modal>
} </div>
<div>
<Modal
title="Share Group"
width="500px"
visible={this.state.shareModalVisible}
onOk={this.handleShareOk}
onCancel={this.handleShareCancel}
footer={[
<Button key="new-role" onClick={this.handleShareCancel}>
New Role
</Button>,
<Button key="new-role-selection" onClick={this.handleShareCancel}>
New Role from Selection
</Button>,
<Button key="submit" type="primary" onClick={this.handleShareOk}>
Share
</Button>,
]}
>
<p>Select user role(s)</p>
<Select
mode="multiple"
defaultValue={'admin'}
style={{ width: '100%' }}
onChange={this.handleRolesDropdownChange}
>
{item}
</Select>
,
</Modal>
</div>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'group-actions'})(GroupActions)); export default withConfigContext(
Form.create({ name: 'group-actions' })(GroupActions),
);

View File

@ -16,193 +16,191 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {message, notification, Table, Typography} from "antd"; import { message, notification, Table } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
import GroupActions from "./GroupActions"; import GroupActions from './GroupActions';
import AddGroup from "./AddGroup"; import AddGroup from './AddGroup';
import Filter from "../Utils/Filter/Filter"; import Filter from '../Utils/Filter/Filter';
const {Text} = Typography;
const searchFields = [ const searchFields = [
{ {
name: 'name', name: 'name',
placeholder: 'Name' placeholder: 'Name',
}, },
{ {
name: 'owner', name: 'owner',
placeholder: 'Owner' placeholder: 'Owner',
} },
]; ];
let config = null;
let apiUrl; let apiUrl;
const getTimeAgo = (time) => {
const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time);
};
class GroupsTable extends React.Component { class GroupsTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; TimeAgo.addLocale(en);
TimeAgo.addLocale(en); this.state = {
this.state = { data: [],
data: [], pagination: {},
pagination: {}, loading: false,
selectedRows: [],
};
}
columns = [
{
title: 'Group Name',
dataIndex: 'name',
width: 100,
},
{
title: 'Owner',
dataIndex: 'owner',
key: 'owner',
// render: enrolmentInfo => enrolmentInfo.owner
// todo add filtering options
},
{
title: 'Description',
dataIndex: 'description',
key: 'description',
// render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
},
{
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id, row) => (
<span>
<GroupActions data={row} fetchGroups={this.fetchGroups} />
</span>
),
},
];
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows,
});
},
};
componentDidMount() {
this.fetchGroups();
}
// fetch data from api
fetchGroups = (params = {}, filters = {}) => {
const config = this.props.context;
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), // calculate the offset
limit: 10,
...filters,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key])
.join('&');
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/admin/groups?' +
encodedExtraParams;
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [] data: res.data.data,
}; pagination,
} });
}
columns = [ })
{ .catch(error => {
title: 'Group Name', if (error.hasOwnProperty('response') && error.response.status === 401) {
dataIndex: 'name', // todo display a popop with error
width: 100, message.error('You are not logged in');
}, window.location.href = window.location.origin + '/entgra/login';
{ } else {
title: 'Owner', notification.error({
dataIndex: 'owner', message: 'There was a problem',
key: 'owner', duration: 0,
// render: enrolmentInfo => enrolmentInfo.owner description: 'Error occurred while trying to load device groups.',
// todo add filtering options });
},
{
title: 'Description',
dataIndex: 'description',
key: 'description',
// render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
},
{
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id, row) => (
<span>
<GroupActions data={row} fetchGroups={this.fetchGroups}/>
</span>
),
},
];
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows
})
} }
};
componentDidMount() { this.setState({ loading: false });
this.fetchGroups(); });
} };
//fetch data from api handleTableChange = (pagination, filters, sorter) => {
fetchGroups = (params = {}, filters = {}) => { const pager = { ...this.state.pagination };
const config = this.props.context; pager.current = pagination.current;
this.setState({loading: true}); this.setState({
pagination: pager,
});
this.fetchGroups({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
// get current page render() {
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; const { data, pagination, loading } = this.state;
const extraParams = { return (
offset: 10 * (currentPage - 1), //calculate the offset <div>
limit: 10, <div style={{ background: '#f0f2f5' }}>
...filters <AddGroup
}; fetchGroups={this.fetchGroups}
style={{ marginBottom: '10px' }}
const encodedExtraParams = Object.keys(extraParams) />
.map(key => key + '=' + extraParams[key]).join('&'); </div>
<div style={{ textAlign: 'right' }}>
apiUrl = window.location.origin + config.serverConfig.invoker.uri + <Filter fields={searchFields} callback={this.fetchGroups} />
config.serverConfig.invoker.deviceMgt + </div>
"/admin/groups?" + encodedExtraParams; <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
<Table
//send request to the invokerss columns={this.columns}
axios.get(apiUrl).then(res => { rowKey={record => record.id}
if (res.status === 200) { dataSource={data.deviceGroups}
const pagination = {...this.state.pagination}; pagination={{
this.setState({ ...pagination,
loading: false, size: 'small',
data: res.data.data, total: data.count,
pagination, pageSize: 10,
}); showTotal: (total, range) =>
} `showing ${range[0]}-${range[1]} of ${total} groups`,
}}
}).catch((error) => { loading={loading}
if (error.hasOwnProperty("response") && error.response.status === 401) { onChange={this.handleTableChange}
//todo display a popop with error rowSelection={this.rowSelection}
message.error('You are not logged in'); />
window.location.href = window.location.origin + '/entgra/login'; </div>
} else { </div>
notification["error"]({ );
message: "There was a problem", }
duration: 0,
description:"Error occurred while trying to load device groups.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetchGroups({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
return (
<div>
<div style={{background: '#f0f2f5'}}>
<AddGroup fetchGroups={this.fetchGroups} style={{marginBottom:"10px"}}/>
</div>
<div style={{textAlign: 'right'}}>
<Filter fields={searchFields} callback={this.fetchGroups}/>
</div>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}>
<Table
columns={this.columns}
rowKey={record => (record.id)}
dataSource={data.deviceGroups}
pagination={{
...pagination,
size: "small",
total: data.count,
pageSize: 10,
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
</div>
);
}
} }
export default withConfigContext(GroupsTable); export default withConfigContext(GroupsTable);

View File

@ -1,104 +1,86 @@
import React from 'react'; import React from 'react';
import {Button, Form, Row, Col, Card, Steps, Input, message, Modal, notification, Typography} from "antd"; import { Button, Form, Row, Col, Card, Steps } from 'antd';
import axios from "axios"; import { withConfigContext } from '../../context/ConfigContext';
import {withConfigContext} from "../../context/ConfigContext"; import SelectPlatform from './SelectPlatform';
import DeviceType from "../Devices/DeviceType"; import ConfigureProfile from './ConfigureProfile';
import SelectPlatform from "./SelectPlatform"; const { Step } = Steps;
import ConfigureProfile from "./ConfigureProfile";
const {Step} = Steps;
class AddPolicy extends React.Component { class AddPolicy extends React.Component {
constructor(props) {
constructor(props) { super(props);
super(props); this.config = this.props.context;
this.config = this.props.context; this.state = {
this.state = { isAddDeviceModalVisible: false,
isAddDeviceModalVisible: false, current: 0,
current : 0,
}
}; };
}
onClickType = () =>{ onClickType = () => {
this.setState({ this.setState({
current: 1, current: 1,
}) });
}; };
next() { next() {
const current = this.state.current + 1; const current = this.state.current + 1;
this.setState({ current }); this.setState({ current });
} }
prev() {
const current = this.state.current - 1;
this.setState({ current });
}
openAddDeviceModal = () =>{
this.setState({
isAddDeviceModalVisible : true,
})
};
render() {
const {loading, current, isError, supportedOsVersions, errorText, forbiddenErrors} = this.state;
const { getFieldDecorator } = this.props.form;
return (
<div>
<Row>
<Col span={20} offset={2}>
<Steps style={{minHeight: 32}} current={current}>
<Step key="Platform" title="Select a Platform"/>
<Step key="ProfileConfigure" title="Configure profile"/>
<Step key="PolicyType" title="Select policy type"/>
<Step key="AssignGroups" title="Assign to groups"/>
<Step key="Publish" title="Publish to devices"/>
<Step key="Result" title="Result"/>
</Steps>
</Col>
<Col span={16} offset={4}>
<Card style={{marginTop: 24}}>
<div style={{display: (current === 0 ? 'unset' : 'none')}}>
<SelectPlatform onClickType={this.onClickType}/>
</div>
<div style={{display: (current === 1 ? 'unset' : 'none')}}>
<ConfigureProfile/>
</div>
<div style={{display: (current === 2 ? 'unset' : 'none')}}>
</div>
<div style={{display: (current === 3 ? 'unset' : 'none')}}>
</div>
<div style={{display: (current === 4 ? 'unset' : 'none')}}>
</div>
<div style={{display: (current === 5 ? 'unset' : 'none')}}>
</div>
</Card>
</Col>
<Col span={16} offset={4}>
<div style={{marginTop: 24}}>
{current > 0 && (
<Button style={{ marginRight: 8 }} onClick={() => this.prev()}>
Previous
</Button>
)}
{current < 5 && current > 0 && (
<Button type="primary" onClick={() => this.next()}>
Next
</Button>
)}
{current === 5 && (
<Button type="primary">
Done
</Button>
)}
</div>
</Col>
</Row>
prev() {
const current = this.state.current - 1;
this.setState({ current });
}
render() {
const { current } = this.state;
return (
<div>
<Row>
<Col span={20} offset={2}>
<Steps style={{ minHeight: 32 }} current={current}>
<Step key="Platform" title="Select a Platform" />
<Step key="ProfileConfigure" title="Configure profile" />
<Step key="PolicyType" title="Select policy type" />
<Step key="AssignGroups" title="Assign to groups" />
<Step key="Publish" title="Publish to devices" />
<Step key="Result" title="Result" />
</Steps>
</Col>
<Col span={16} offset={4}>
<Card style={{ marginTop: 24 }}>
<div style={{ display: current === 0 ? 'unset' : 'none' }}>
<SelectPlatform onClickType={this.onClickType} />
</div>
<div style={{ display: current === 1 ? 'unset' : 'none' }}>
<ConfigureProfile />
</div>
<div style={{ display: current === 2 ? 'unset' : 'none' }}></div>
<div style={{ display: current === 3 ? 'unset' : 'none' }}></div>
<div style={{ display: current === 4 ? 'unset' : 'none' }}></div>
<div style={{ display: current === 5 ? 'unset' : 'none' }}></div>
</Card>
</Col>
<Col span={16} offset={4}>
<div style={{ marginTop: 24 }}>
{current > 0 && (
<Button style={{ marginRight: 8 }} onClick={() => this.prev()}>
Previous
</Button>
)}
{current < 5 && current > 0 && (
<Button type="primary" onClick={() => this.next()}>
Next
</Button>
)}
{current === 5 && <Button type="primary">Done</Button>}
</div> </div>
); </Col>
} </Row>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'add-policy'})(AddPolicy)); export default withConfigContext(
Form.create({ name: 'add-policy' })(AddPolicy),
);

View File

@ -1,268 +1,270 @@
import React from 'react'; import React from 'react';
import {Tabs, Row, Col, Switch, Menu,Input, Typography, Form, Checkbox, Select, import {
Tooltip, Icon, Collapse, Alert, Upload, Button,Radio} from "antd"; Tabs,
import {withConfigContext} from "../../context/ConfigContext"; Row,
import "../../pages/Dashboard/Policies/policies.css"; Col,
import jsonResponse from "./configuration"; Switch,
const { Title, Text, Paragraph } = Typography; Input,
Typography,
Form,
Checkbox,
Select,
Tooltip,
Icon,
Alert,
Upload,
Button,
Radio,
} from 'antd';
import { withConfigContext } from '../../context/ConfigContext';
import '../../pages/Dashboard/Policies/policies.css';
import jsonResponse from './configuration';
const { Title, Paragraph } = Typography;
const { TabPane } = Tabs; const { TabPane } = Tabs;
const {Option} = Select; const { Option } = Select;
const {Panel} = Collapse;
const { TextArea } = Input; const { TextArea } = Input;
const policyConfigurationsList = jsonResponse.PolicyConfigurations; const policyConfigurationsList = jsonResponse.PolicyConfigurations;
class ConfigureProfile extends React.Component { class ConfigureProfile extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.config = this.props.context; this.config = this.props.context;
this.policies = policyConfigurationsList.androidPolicy.Policy; this.policies = policyConfigurationsList.androidPolicy.Policy;
this.state = { this.state = {
isDisplayMain: "none", isDisplayMain: 'none',
activeKeys: [] activeKeys: [],
}
}; };
}
componentDidMount() { componentDidMount() {}
onChange = e => {
console.log(`checked = ${e.target.id}`);
};
onChecked = (e, i) => {
if (e) {
this.setState({
isDisplayMain: 'block',
});
} else {
this.setState({
isDisplayMain: 'none',
});
} }
};
onChange = (e) =>{ onClickSwitch = e => {};
console.log(`checked = ${e.target.id}`);
};
onChecked = (e,i) =>{ getPanelItems = panel => {
if(e){ const { getFieldDecorator } = this.props.form;
this.setState({ return panel.map((item, k) => {
isDisplayMain: "block", switch (item._type) {
}); case 'select':
}else{ return (
this.setState({ <Form.Item
isDisplayMain: "none", key={k}
}); label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{ display: 'block' }}
>
{getFieldDecorator(`${item._id}`, {
initialValue: `${item.Optional.Option[0]}`,
})(
<Select>
{item.Optional.Option.map(option => {
return <Option key={option}>{option}</Option>;
})}
</Select>,
)}
</Form.Item>
);
case 'input':
return (
<Form.Item
key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{ display: 'block' }}
>
{getFieldDecorator(`${item._id}`, {
rules: [
{
pattern: new RegExp(`${item.Optional.rules.regex}`),
message: `${item.Optional.rules.validationMsg}`,
},
],
})(<Input placeholder={item.Optional.Placeholder} />)}
</Form.Item>
);
case 'checkbox':
return (
<Form.Item key={k}>
{getFieldDecorator(`${item._id}`, {
valuePropName: 'checked',
initialValue: `${item.Optional.checked}`,
})(
<Checkbox
// checked={item.Optional.checked}
onChange={this.onChange}
>
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
</Checkbox>,
)}
</Form.Item>
);
case 'textArea':
return (
<Form.Item
key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{ display: 'block' }}
>
{getFieldDecorator(`${item._id}`, {})(
<TextArea
placeholder={item.Optional.Placeholder}
rows={item.Optional.Row}
/>,
)}
</Form.Item>
);
case 'radioGroup':
return (
<Form.Item
key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{ display: 'block' }}
>
{getFieldDecorator(`${item._id}`, {})(
<Radio.Group>
{item.Optional.Radio.map(option => {
return (
<Radio key={option} value={option}>
{option}
</Radio>
);
})}
</Radio.Group>,
)}
</Form.Item>
);
case 'title':
return (
<Title key={k} level={4}>
{item.Label}{' '}
</Title>
);
case 'paragraph':
return (
<Paragraph key={k} style={{ marginTop: 20 }}>
{item.Label}{' '}
</Paragraph>
);
case 'alert':
return (
<Alert key={k} description={item.Label} type="warning" showIcon />
);
case 'upload':
return (
<Form.Item
key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
>
{getFieldDecorator('upload', {})(
<Upload>
<Button>
<Icon type="upload" /> Click to upload
</Button>
</Upload>,
)}
</Form.Item>
);
default:
return null;
}
});
};
} render() {
return (
}; <div>
<Tabs tabPosition={'left'} size={'large'}>
onClickSwitch = (e) =>{ {this.policies.map((element, i) => {
return (
}; <TabPane tab={element.Name} key={i}>
{/* <div style={{ height: 800, overflowY: "scroll"}}>*/}
{Object.values(element.Panel).map((panel, j) => {
getPanelItems = (panel)=>{ return (
const { getFieldDecorator } = this.props.form; <div key={j}>
return ( <div>
panel.map((item,k)=>{ <Row>
switch(item._type){ <Col offset={0} span={14}>
case "select": <Title level={4}>{panel.title} </Title>
return( </Col>
<Form.Item key={k} <Col offset={8} span={1}>
label={ <Switch
<span> checkedChildren="ON"
{item.Label}&nbsp; unCheckedChildren="OFF"
<Tooltip title={item.tooltip} placement="right"> id={i}
<Icon type="question-circle-o" /> onClick={this.onClickSwitch}
</Tooltip> onChange={this.onChecked}
</span>
}
style={{display: "block"}}>
{getFieldDecorator(`${item._id}`, {
initialValue: `${item.Optional.Option[0]}`
})(
<Select>
{item.Optional.Option.map((option)=>{
return(
<Option key={option}>{option}</Option>
);
})}
</Select>
)}
</Form.Item>
);
case "input":
return(
<Form.Item key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{display: "block"}}>
{getFieldDecorator(`${item._id}`, {
rules: [
{
pattern: new RegExp(`${item.Optional.rules.regex}`),
message: `${item.Optional.rules.validationMsg}`,
},
],
})(
<Input placeholder={item.Optional.Placeholder}/>
)}
</Form.Item>
);
case "checkbox":
return(
<Form.Item key={k}>
{getFieldDecorator(`${item._id}`, {
valuePropName: 'checked',
initialValue: `${item.Optional.checked}`,
})(
<Checkbox
// checked={item.Optional.checked}
onChange={this.onChange}
>
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
</Checkbox>
)}
</Form.Item>
);
case "textArea":
return(
<Form.Item key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{display: "block"}}>
{getFieldDecorator(`${item._id}`, {
})(
<TextArea placeholder={item.Optional.Placeholder}
rows={item.Optional.Row} />
)}
</Form.Item>
);
case "radioGroup":
return(
<Form.Item key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{display: "block"}}>
{getFieldDecorator(`${item._id}`, {
})(
<Radio.Group>
{item.Optional.Radio.map((option)=>{
return(
<Radio value={option}>{option}</Radio>
);
})}
</Radio.Group>
)}
</Form.Item>
);
case "title":
return(
<Title key={k} level={4}>{item.Label} </Title>
);
case "paragraph":
return(
<Paragraph key={k} style={{marginTop:20}}>{item.Label} </Paragraph>
);
case "alert":
return(
<Alert
key={k}
description={item.Label}
type="warning"
showIcon
/> />
); </Col>
case "upload": </Row>
return( <Row>{panel.description}</Row>
<Form.Item key={k} </div>
label={ <div style={{ display: `${this.state.isDisplayMain}` }}>
<span> <Form>{this.getPanelItems(panel.PanelItem)}</Form>
{item.Label}&nbsp; </div>
<Tooltip title={item.tooltip} placement="right"> </div>
<Icon type="question-circle-o" /> );
</Tooltip> })}
</span> </TabPane>
} );
> })}
{getFieldDecorator('upload', { </Tabs>
</div>
})( );
}
<Upload>
<Button>
<Icon type="upload" /> Click to upload
</Button>
</Upload>,
)}
</Form.Item>
);
default:
return null;
}
})
)
};
render() {
return (
<div>
<Tabs tabPosition={"left"} size={"large"}>
{ this.policies.map((element, i) =>{
return(
<TabPane tab={element.Name} key={i} >
{/*<div style={{ height: 800, overflowY: "scroll"}}>*/}
{ Object.values(element.Panel).map((panel, j)=>{
return(
<div key={j} >
<div>
<Row>
<Col offset={0} span={14}>
<Title level={4}>{panel.title} </Title>
</Col>
<Col offset={8} span={1}>
<Switch
checkedChildren="ON"
unCheckedChildren="OFF"
id={i}
onClick={this.onClickSwitch}
onChange={this.onChecked}
/>
</Col>
</Row>
<Row>{panel.description}</Row>
</div>
<div style={{display: `${this.state.isDisplayMain}`}}>
<Form >
{this.getPanelItems(panel.PanelItem)}
</Form>
</div>
</div>
);
})
}
</TabPane>
)
})
}
</Tabs>
</div>
);
}
} }
export default withConfigContext(Form.create()(ConfigureProfile)); export default withConfigContext(Form.create()(ConfigureProfile));

View File

@ -16,165 +16,162 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {message, notification, Table, Typography} from "antd"; import { message, notification, Table } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography;
let config = null;
let apiUrl; let apiUrl;
const columns = [ const columns = [
{ {
title: 'Policy Name', title: 'Policy Name',
dataIndex: 'policyName', dataIndex: 'policyName',
width: 100, width: 100,
}, },
{ {
title: 'Description', title: 'Description',
dataIndex: 'description', dataIndex: 'description',
key: 'description', key: 'description',
// render: enrolmentInfo => enrolmentInfo.owner // render: enrolmentInfo => enrolmentInfo.owner
// todo add filtering options // todo add filtering options
}, },
{ {
title: 'Compilance', title: 'Compilance',
dataIndex: 'compliance', dataIndex: 'compliance',
key: 'compliance', key: 'compliance',
// render: enrolmentInfo => enrolmentInfo.ownership // render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options // todo add filtering options
}, },
{ {
title: 'Policy Type', title: 'Policy Type',
dataIndex: 'policyType', dataIndex: 'policyType',
key: 'policyType', key: 'policyType',
// render: enrolmentInfo => enrolmentInfo.ownership // render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options // todo add filtering options
} },
]; ];
const getTimeAgo = (time) => {
const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time);
};
class PoliciesTable extends React.Component { class PoliciesTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; TimeAgo.addLocale(en);
TimeAgo.addLocale(en); this.state = {
this.state = { data: [],
data: [], pagination: {},
pagination: {}, loading: false,
selectedRows: [],
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows,
});
},
};
componentDidMount() {
this.fetchGroups();
}
// fetch data from api
fetchGroups = (params = {}) => {
const config = this.props.context;
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), // calculate the offset
limit: 10,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key])
.join('&');
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/policies?' +
encodedExtraParams;
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [] data: res.data.data.policies,
}; pagination,
} });
}
rowSelection = { })
onChange: (selectedRowKeys, selectedRows) => { .catch(error => {
this.setState({ if (error.hasOwnProperty('response') && error.response.status === 401) {
selectedRows: selectedRows // todo display a popop with error
}) message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load policies.',
});
} }
};
componentDidMount() { this.setState({ loading: false });
this.fetchGroups(); });
} };
//fetch data from api handleTableChange = (pagination, filters, sorter) => {
fetchGroups = (params = {}) => { const pager = { ...this.state.pagination };
const config = this.props.context; pager.current = pagination.current;
this.setState({loading: true}); this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
// get current page render() {
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; const { data, pagination, loading } = this.state;
return (
const extraParams = { <div>
offset: 10 * (currentPage - 1), //calculate the offset <Table
limit: 10, columns={columns}
}; rowKey={record => record.id}
dataSource={data}
const encodedExtraParams = Object.keys(extraParams) pagination={{
.map(key => key + '=' + extraParams[key]).join('&'); ...pagination,
size: 'small',
apiUrl = window.location.origin + config.serverConfig.invoker.uri + // position: "top",
config.serverConfig.invoker.deviceMgt + showTotal: (total, range) =>
"/policies?" + encodedExtraParams; `showing ${range[0]}-${range[1]} of ${total} groups`,
// showQuickJumper: true
//send request to the invokerss }}
axios.get(apiUrl).then(res => { loading={loading}
if (res.status === 200) { onChange={this.handleTableChange}
const pagination = {...this.state.pagination}; rowSelection={this.rowSelection}
this.setState({ />
loading: false, </div>
data: res.data.data.policies, );
pagination, }
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load policies.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
return (
<div>
<Table
columns={columns}
rowKey={record => (record.id)}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
);
}
} }
export default withConfigContext(PoliciesTable); export default withConfigContext(PoliciesTable);

View File

@ -16,133 +16,126 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Card, Col, Icon, message, notification, Row, Typography} from "antd"; import { Card, Col, Icon, message, notification, Row } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography;
let config = null;
let apiUrl; let apiUrl;
class SelectPlatform extends React.Component { class SelectPlatform extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; TimeAgo.addLocale(en);
TimeAgo.addLocale(en); this.state = {
this.state = { data: [],
data: [], pagination: {},
pagination: {}, loading: false,
selectedRows: [],
};
}
componentDidMount() {
this.fetchUsers();
}
onClickCard = data => {
console.log(data);
this.props.onClickType();
};
// fetch data from api
fetchUsers = (params = {}) => {
const config = this.props.context;
this.setState({ loading: true });
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/device-types';
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [] data: JSON.parse(res.data.data),
}; pagination,
} });
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load device types.',
});
}
componentDidMount() { this.setState({ loading: false });
this.fetchUsers(); });
} };
onClickCard = (data) =>{ handleTableChange = (pagination, filters, sorter) => {
console.log(data); const pager = { ...this.state.pagination };
this.props.onClickType(); pager.current = pagination.current;
}; this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
//fetch data from api render() {
fetchUsers = (params = {}) => { const { data } = this.state;
const config = this.props.context; const { Meta } = Card;
this.setState({loading: true}); const itemCard = data.map(data => (
<Col span={5} key={data.id}>
// get current page <Card
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; size="default"
style={{ width: 150 }}
const extraParams = { bordered={true}
offset: 10 * (currentPage - 1), //calculate the offset onClick={this.onClickCard}
limit: 10, cover={
}; <Icon
type="android"
const encodedExtraParams = Object.keys(extraParams) key="device-types"
.map(key => key + '=' + extraParams[key]).join('&'); style={{
color: '#ffffff',
apiUrl = window.location.origin + config.serverConfig.invoker.uri + backgroundColor: '#4b92db',
config.serverConfig.invoker.deviceMgt + fontSize: '100px',
"/device-types"; padding: '20px',
}}
//send request to the invokerss />
axios.get(apiUrl).then(res => { }
if (res.status === 200) { >
const pagination = {...this.state.pagination}; <Meta title={data.name} />
this.setState({ </Card>
loading: false, </Col>
data: JSON.parse(res.data.data), ));
pagination, return (
}); <div>
} <Row gutter={16}>{itemCard}</Row>
</div>
}).catch((error) => { );
if (error.hasOwnProperty("response") && error.response.status === 401) { }
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load device types.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
const { Meta } = Card;
const itemCard = data.map((data) =>
<Col span={5} key={data.id}>
<Card
size="default"
style={{ width: 150 }}
bordered={true}
onClick={this.onClickCard}
cover={<Icon type="android" key="device-types" style={{color:'#ffffff',
backgroundColor:'#4b92db', fontSize: '100px', padding:'20px'}}/>}
>
<Meta
title={data.name}
/>
</Card>
</Col>
);
return (
<div>
<Row gutter={16}>
{itemCard}
</Row>
</div>
);
}
} }
export default withConfigContext(SelectPlatform); export default withConfigContext(SelectPlatform);

View File

@ -16,50 +16,47 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { DatePicker } from 'antd'; import { DatePicker } from 'antd';
import moment from 'moment'; import moment from 'moment';
class DateRangePicker extends React.Component { class DateRangePicker extends React.Component {
constructor(props) {
super(props);
}
constructor(props){ // Send updated date range to Reports.js when duration change
super(props); onChange = (dates, dateStrings) => {
} this.props.updateDurationValue(dateStrings[0], dateStrings[1]);
};
//Send updated date range to Reports.js when duration change render() {
onChange = (dates, dateStrings) => { const { RangePicker } = DatePicker;
this.props.updateDurationValue(dateStrings[0],dateStrings[1]); return (
} <RangePicker
ranges={{
render(){ Today: [moment(), moment()],
const { RangePicker } = DatePicker; Yesterday: [
return( moment().subtract(1, 'days'),
<RangePicker moment().subtract(1, 'days'),
ranges={{ ],
'Today': [ 'Last 7 Days': [moment().subtract(6, 'days'), moment()],
moment(), 'Last 30 Days': [moment().subtract(29, 'days'), moment()],
moment()], 'This Month': [moment().startOf('month'), moment().endOf('month')],
'Yesterday': [ 'Last Month': [
moment().subtract(1, 'days'), moment()
moment().subtract(1, 'days')], .subtract(1, 'month')
'Last 7 Days': [ .startOf('month'),
moment().subtract(6, 'days'), moment()
moment()], .subtract(1, 'month')
'Last 30 Days': [ .endOf('month'),
moment().subtract(29, 'days'), ],
moment()], }}
'This Month': [ format="YYYY-MM-DD"
moment().startOf('month'), onChange={this.onChange}
moment().endOf('month')], />
'Last Month': [ );
moment().subtract(1, 'month').startOf('month'), }
moment().subtract(1, 'month').endOf('month')]
}}
format="YYYY-MM-DD"
onChange={this.onChange}
/>
)
}
} }
export default DateRangePicker; export default DateRangePicker;

View File

@ -16,52 +16,53 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { Select } from 'antd'; import { Select } from 'antd';
class Filter extends React.Component { class Filter extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItem: null,
};
}
constructor(props){ // Send updated filter value to Reports.js
super(props); onChange = value => {
const { Option } = Select; this.setState({ selectedItem: value }, () => {
this.state = { if (this.props.dropDownName == 'Device Status') {
selectedItem:null this.props.updateFiltersValue(this.state.selectedItem, 'Device Status');
} else {
this.props.updateFiltersValue(
this.state.selectedItem,
'Device Ownership',
);
}
});
};
render() {
// Dynamically generate dropdown items from dropDownItems array
let item = this.props.dropDownItems.map(data => (
<Select.Option value={data} key={data}>
{data}
</Select.Option>
));
return (
<Select
showSearch
style={{ width: 200 }}
placeholder={this.props.dropDownName}
optionFilterProp="children"
onChange={this.onChange}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
} }
} >
{item}
//Send updated filter value to Reports.js </Select>
onChange = value => { );
this.setState({selectedItem:value},() => { }
if(this.props.dropDownName=="Device Status"){
this.props.updateFiltersValue(this.state.selectedItem,"Device Status");
}else{
this.props.updateFiltersValue(this.state.selectedItem, "Device Ownership");
}
});
}
render(){
//Dynamically generate dropdown items from dropDownItems array
let item = this.props.dropDownItems.map((data) =>
<Select.Option
value={data}
key={data}>
{data}
</Select.Option>);
return(
<Select
showSearch
style={{ width: 200 }}
placeholder={this.props.dropDownName}
optionFilterProp="children"
onChange={this.onChange}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}>
{item}
</Select>
)
}
} }
export default Filter; export default Filter;

View File

@ -16,110 +16,112 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Breadcrumb, Icon, Select, Button, Card } from 'antd';
PageHeader,
Typography,
Breadcrumb,
Icon,
Tag,
Radio, Select, Button, Card,
Row, Col, message, notification
} from "antd";
import {Link} from "react-router-dom"; import { Link } from 'react-router-dom';
import PoliciesTable from "../../../components/Policies/PoliciesTable"; import ReportDeviceTable from '../../../components/Devices/ReportDevicesTable';
import DevicesTable from "../../../components/Devices/DevicesTable"; import PieChart from '../../../components/Reports/Widgets/PieChart';
import DateRangePicker from "../../../components/Reports/DateRangePicker"; import { withConfigContext } from '../../../context/ConfigContext';
import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable";
import PieChart from "../../../components/Reports/Widgets/PieChart";
import axios from "axios";
import CountWidget from "../../../components/Reports/Widgets/CountWidget";
import {withConfigContext} from "../../../context/ConfigContext";
const {Paragraph} = Typography;
const { CheckableTag } = Tag;
const { Option } = Select; const { Option } = Select;
let config = null;
class DeviceStatusReport extends React.Component { class DeviceStatusReport extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
config = this.props.context; const { reportData } = this.props.location;
const { reportData } = this.props.location; this.state = {
this.state = { selectedTags: ['Enrolled'],
selectedTags: ['Enrolled'], paramsObject: {
paramsObject:{ from: reportData.duration[0],
from:reportData.duration[0], to: reportData.duration[1],
to:reportData.duration[1] },
}, statsObject: {},
statsObject:{}, statArray: [
statArray:[{item:"ACTIVE",count:0},{item:"INACTIVE",count:0},{item:"REMOVED",count:0}] { item: 'ACTIVE', count: 0 },
}; { item: 'INACTIVE', count: 0 },
} { item: 'REMOVED', count: 0 },
],
onClickPieChart = (value) => {
console.log(value.data.point.item);
const chartValue = value.data.point.item;
let tempParamObj = this.state.paramsObject;
tempParamObj.status = chartValue;
this.setState({paramsObject:tempParamObj});
console.log(this.state.paramsObject)
}; };
}
render() { onClickPieChart = value => {
const { statArray } = this.state; console.log(value.data.point.item);
const { reportData } = this.props.location; const chartValue = value.data.point.item;
let tempParamObj = this.state.paramsObject;
const params = {...this.state.paramsObject}; tempParamObj.status = chartValue;
return (
<div>
<PageHeader style={{paddingTop: 0}}>
<Breadcrumb style={{paddingBottom: 16}}>
<Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap" style={{marginBottom: '10px'}}>
<h3>Summary of enrollments</h3>
<div style={{marginBottom: '10px'}}>
<Select defaultValue="android" style={{ width: 120 , marginRight:10}}>
<Option value="android">Android</Option>
<Option value="ios">IOS</Option>
<Option value="windows">Windows</Option>
</Select>
<Button onClick={this.onSubmitReport} style={{marginLeft:10}} type="primary">Generate Report</Button>
</div>
</div>
<div> this.setState({ paramsObject: tempParamObj });
<Card console.log(this.state.paramsObject);
bordered={true} };
hoverable={true}
style={{borderRadius: 5, marginBottom: 10, height:window.innerHeight*0.5}}>
<PieChart onClickPieChart={this.onClickPieChart} reportData={reportData}/> render() {
</Card> const { reportData } = this.props.location;
</div>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}> const params = { ...this.state.paramsObject };
<ReportDeviceTable paramsObject={params}/> return (
</div> <div>
</PageHeader> <PageHeader style={{ paddingTop: 0 }}>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item>
</div> <Link to="/entgra">
<Icon type="home" /> Home
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Summary of enrollments</h3>
<div style={{ marginBottom: '10px' }}>
<Select
defaultValue="android"
style={{ width: 120, marginRight: 10 }}
>
<Option value="android">Android</Option>
<Option value="ios">IOS</Option>
<Option value="windows">Windows</Option>
</Select>
<Button
onClick={this.onSubmitReport}
style={{ marginLeft: 10 }}
type="primary"
>
Generate Report
</Button>
</div> </div>
); </div>
}
<div>
<Card
bordered={true}
hoverable={true}
style={{
borderRadius: 5,
marginBottom: 10,
height: window.innerHeight * 0.5,
}}
>
<PieChart
onClickPieChart={this.onClickPieChart}
reportData={reportData}
/>
</Card>
</div>
<div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
<ReportDeviceTable paramsObject={params} />
</div>
</PageHeader>
<div
style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
></div>
</div>
);
}
} }
export default withConfigContext(DeviceStatusReport); export default withConfigContext(DeviceStatusReport);

View File

@ -16,118 +16,88 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Breadcrumb, Icon, Card } from 'antd';
PageHeader,
Typography,
Breadcrumb,
Icon,
Tag,
Radio, Select, Button, Card,
Row, Col, message, notification
} from "antd";
import {Link} from "react-router-dom";
import PoliciesTable from "../../../components/Policies/PoliciesTable";
import DevicesTable from "../../../components/Devices/DevicesTable";
import DateRangePicker from "../../../components/Reports/DateRangePicker";
import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable";
import PieChart from "../../../components/Reports/Widgets/PieChart";
import axios from "axios";
import CountWidget from "../../../components/Reports/Widgets/CountWidget";
import {withConfigContext} from "../../../context/ConfigContext";
const {Paragraph} = Typography;
const { CheckableTag } = Tag;
const { Option } = Select;
let config = null;
import { Link } from 'react-router-dom';
import ReportDeviceTable from '../../../components/Devices/ReportDevicesTable';
import PieChart from '../../../components/Reports/Widgets/PieChart';
import { withConfigContext } from '../../../context/ConfigContext';
class EnrollmentTypeReport extends React.Component { class EnrollmentTypeReport extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
config = this.props.context; const { reportData } = this.props.location;
const { reportData } = this.props.location; this.state = {
this.state = { paramsObject: {
paramsObject:{ from: reportData.duration[0],
from:reportData.duration[0], to: reportData.duration[1],
to:reportData.duration[1] },
}
};
console.log(reportData.duration);
}
setParam = (tempParamObj, type, value) => {
if(type==="status"){
tempParamObj.status = value;
if(tempParamObj.status) {
delete tempParamObj.status;
}
} else if(type=="ownership"){
tempParamObj.ownership = value;
if(value=="ALL" && tempParamObj.ownership) {
delete tempParamObj.ownership;
}
}
}; };
onClickPieChart = (value) => { console.log(reportData.duration);
const chartValue = value.data.point.item; }
let tempParamObj = this.state.paramsObject;
console.log(chartValue) onClickPieChart = value => {
const chartValue = value.data.point.item;
let tempParamObj = this.state.paramsObject;
tempParamObj.ownership = chartValue; console.log(chartValue);
this.setState({paramsObject:tempParamObj}); tempParamObj.ownership = chartValue;
};
render() { this.setState({ paramsObject: tempParamObj });
const { reportData } = this.props.location; };
const params = {...this.state.paramsObject}; render() {
return ( const { reportData } = this.props.location;
<div>
<PageHeader style={{paddingTop: 0}}>
<Breadcrumb style={{paddingBottom: 16}}>
<Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap" style={{marginBottom: '10px'}}>
<h3>Summary of enrollments</h3>
</div> const params = { ...this.state.paramsObject };
return (
<div>
<PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item>
<Link to="/entgra">
<Icon type="home" /> Home
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Summary of enrollments</h3>
</div>
<div> <div>
<Card <Card
bordered={true} bordered={true}
hoverable={true} hoverable={true}
style={{borderRadius: 5, marginBottom: 10, height:window.innerHeight*0.5}}> style={{
borderRadius: 5,
marginBottom: 10,
height: window.innerHeight * 0.5,
}}
>
<PieChart
onClickPieChart={this.onClickPieChart}
reportData={reportData}
/>
</Card>
</div>
<div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
<PieChart onClickPieChart={this.onClickPieChart} reportData={reportData}/> <ReportDeviceTable paramsObject={params} />
</div>
</Card> </PageHeader>
<div
</div> style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
></div>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}> </div>
<ReportDeviceTable paramsObject={params}/> );
</div> }
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
</div>
</div>
);
}
} }
export default withConfigContext(EnrollmentTypeReport); export default withConfigContext(EnrollmentTypeReport);

View File

@ -16,157 +16,111 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Breadcrumb, Icon, Card } from 'antd';
PageHeader,
Typography,
Breadcrumb,
Icon,
Tag,
Radio, Select, Button, Card,
Row, Col, message, notification
} from "antd";
import {Link, Redirect} from "react-router-dom";
import PoliciesTable from "../../../components/Policies/PoliciesTable";
import DevicesTable from "../../../components/Devices/DevicesTable";
import DateRangePicker from "../../../components/Reports/DateRangePicker";
import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable";
import PieChart from "../../../components/Reports/Widgets/PieChart";
import axios from "axios";
import CountWidget from "../../../components/Reports/Widgets/CountWidget";
import {withConfigContext} from "../../../context/ConfigContext";
const {Paragraph} = Typography;
const { CheckableTag } = Tag;
const { Option } = Select;
let config = null;
import { Link, Redirect } from 'react-router-dom';
import ReportDeviceTable from '../../../components/Devices/ReportDevicesTable';
import PieChart from '../../../components/Reports/Widgets/PieChart';
import { withConfigContext } from '../../../context/ConfigContext';
class EnrollmentsVsUnenrollmentsReport extends React.Component { class EnrollmentsVsUnenrollmentsReport extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
config = this.props.context; const { reportData } = this.props.location;
const { reportData } = this.props.location;
this.state = { this.state = {
paramsObject:{ paramsObject: {
from:reportData? reportData.duration[0]: "2019-01-01", from: reportData ? reportData.duration[0] : '2019-01-01',
to:reportData? reportData.duration[1]: "2019-01-01" to: reportData ? reportData.duration[1] : '2019-01-01',
}, },
redirect: false redirect: false,
}; };
this.redirectToHome(); this.redirectToHome();
console.log(this.state.paramsObject); console.log(this.state.paramsObject);
}
redirectToHome = () => {
return <Redirect to="/entgra" />;
};
onClickPieChart = value => {
const chartValue = value.data.point.item;
let tempParamObj = this.state.paramsObject;
console.log(chartValue);
if (chartValue === 'Enrollments') {
tempParamObj.status = 'ACTIVE&status=INACTIVE';
} else {
tempParamObj.status = 'REMOVED';
} }
setParam = (tempParamObj, type, value) => { this.setState({ paramsObject: tempParamObj });
if(type==="status"){ };
tempParamObj.status = value;
if(tempParamObj.status) { render() {
delete tempParamObj.status; const { reportData } = this.props.location;
}
} else if(type=="ownership"){ console.log('======');
tempParamObj.ownership = value; console.log(reportData);
if(value=="ALL" && tempParamObj.ownership) { console.log('======');
delete tempParamObj.ownership;
} let reportDataClone = {
} params: ['ACTIVE'],
duration: ['2020-01-01', '2020-01-01'],
}; };
redirectToHome = () => { const params = { ...this.state.paramsObject };
return <Redirect to="/entgra" /> return (
}; <div>
<div>
{!reportData ? (
<Redirect to="/entgra/reports" />
) : (
<PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item>
<Link to="/entgra">
<Icon type="home" /> Home
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Summary of enrollments</h3>
</div>
setRedirect = (reportData) => { <div>
if(!reportData){ <Card
this.setState({ bordered={true}
redirect: true hoverable={true}
}) style={{
} borderRadius: 5,
}; marginBottom: 10,
height: window.innerHeight * 0.5,
}}
>
<PieChart
onClickPieChart={this.onClickPieChart}
reportData={reportData ? reportData : reportDataClone}
/>
</Card>
</div>
renderRedirect = () => { <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
if (this.state.redirect) { <ReportDeviceTable paramsObject={params} />
return <Redirect to='/entgra' /> </div>
} </PageHeader>
} )}
</div>
onClickPieChart = (value) => { </div>
const chartValue = value.data.point.item; );
let tempParamObj = this.state.paramsObject; }
console.log(chartValue)
// tempParamObj.status = chartValue;
if(chartValue==="Enrollments"){
tempParamObj.status = "ACTIVE&status=INACTIVE"
}else{
tempParamObj.status = "REMOVED"
}
this.setState({paramsObject:tempParamObj});
};
render() {
const { reportData } = this.props.location;
console.log("======")
console.log(reportData)
console.log("======")
let reportDataClone = {
params: ["ACTIVE"],
duration: ["2020-01-01","2020-01-01"]
};
const params = {...this.state.paramsObject};
return (
<div>
<div>{!reportData ? (
<Redirect to='/entgra/reports' />
) : (
<PageHeader style={{paddingTop: 0}}>
<Breadcrumb style={{paddingBottom: 16}}>
<Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap" style={{marginBottom: '10px'}}>
<h3>Summary of enrollments</h3>
</div>
<div>
<Card
bordered={true}
hoverable={true}
style={{borderRadius: 5, marginBottom: 10, height:window.innerHeight*0.5}}>
<PieChart onClickPieChart={this.onClickPieChart} reportData={reportData? reportData : reportDataClone}/>
</Card>
</div>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}>
<ReportDeviceTable paramsObject={params}/>
</div>
</PageHeader>
)}</div>
</div>
);
}
} }
export default withConfigContext(EnrollmentsVsUnenrollmentsReport); export default withConfigContext(EnrollmentsVsUnenrollmentsReport);

View File

@ -1,63 +1,47 @@
import React from "react"; import React from 'react';
import {Card, Col, Icon} from "antd"; import { Card, Col } from 'antd';
import {Link} from "react-router-dom";
class CountWidget extends React.Component { class CountWidget extends React.Component {
constructor(props) {
super(props);
this.routes = props.routes;
this.state = {
statArray: [],
};
}
constructor(props) { componentDidMount() {
super(props); this.setState({ statArray: this.props.statArray });
this.routes = props.routes; console.log('$$$$');
this.state = { console.log(this.props.statArray);
statArray:[] }
}
}
componentDidMount() { render() {
this.setState({statArray:this.props.statArray}) const { statArray } = this.state;
console.log("$$$$")
console.log(this.props.statArray)
}
let card = statArray.map(data => (
<Col key={data.item} span={6}>
<Card
key={data.item}
bordered={true}
hoverable={true}
style={{ borderRadius: 10, marginBottom: 16 }}
>
<div align="center">
<h2>
<b>{data.item}</b>
</h2>
<h1>{data.count}</h1>
{/* <p>{data.duration}</p>*/}
{/* <ReportFilterModal/>*/}
</div>
</Card>
</Col>
));
render() { return <div>{card}</div>;
const countObj = [ }
{item:"All",count:100},
{item:"Enrolled",count:80},
{item:"Unenrolled",count:20}];
const { statArray } = this.state;
let card = statArray.map((data) =>
// <Card
// bordered={true}
// hoverable={true}
// key={data.item}
// style={{borderRadius: 5, marginBottom: 5, width:"100%"}}>
//
// <h3>{data.item} Devices: {data.count}</h3>
//
// </Card>
<Col key={data.item} span={6}>
<Card key={data.item} bordered={true} hoverable={true} style={{borderRadius: 10, marginBottom: 16}}>
<div align='center'>
<h2><b>{data.item}</b></h2>
<h1>{data.count}</h1>
{/*<p>{data.duration}</p>*/}
{/*<ReportFilterModal/>*/}
</div>
</Card>
</Col>
)
return(
<div>
{card}
</div>
)
}
} }
export default CountWidget; export default CountWidget;

View File

@ -1,322 +1,342 @@
import React from "react"; import React from 'react';
import { import {
G2, Chart,
Chart, Geom,
Geom, Axis,
Axis, Tooltip,
Tooltip, Coord,
Coord, Label,
Label, Legend,
Legend, Guide,
View, } from 'bizcharts';
Guide, import DataSet from '@antv/data-set';
Shape, import axios from 'axios';
Facet, import { message, notification } from 'antd';
Util import { withConfigContext } from '../../../context/ConfigContext';
} from "bizcharts";
import DataSet from "@antv/data-set";
import axios from "axios";
import {message, notification} from "antd";
import {withConfigContext} from "../../../context/ConfigContext";
let config = null; let config = null;
class PieChart extends React.Component { class PieChart extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
this.state = {
loading: true,
statArray: [],
};
}
constructor(props) { componentDidMount() {
super(props); const { reportData } = this.props;
config = this.props.context; let params = {
this.state = { status: reportData.params[0],
loading:true, from: reportData.duration[0],
statArray:[] to: reportData.duration[1],
}; };
const urlSet = {
paramsList: reportData.params,
duration: reportData.duration,
};
console.log(urlSet);
if (reportData.params[0] === 'Enrollments') {
this.getEnrollmentsVsUnenrollmentsCount(params, urlSet);
} else if (reportData.params[0] === 'BYOD') {
this.getEnrollmentTypeCount(params, urlSet);
} else {
this.getCount(params, urlSet);
} }
}
componentDidMount() { clicked = () => {
let { statArray } = this.state; console.log('Clicked...!!');
const { reportData } = this.props; };
let params = {
status: reportData.params[0],
from: reportData.duration[0],
to: reportData.duration[1]
};
const urlSet = { onChartChange = data => {
paramsList:reportData.params, this.props.onClickPieChart(data);
duration:reportData.duration };
};
console.log(urlSet) statArray = [];
if(reportData.params[0]==="Enrollments"){ // Call count APIs and get count for given parameters, then create data object to build pie chart
this.getEnrollmentsVsUnenrollmentsCount(params, urlSet) getCount = (params, urlSet) => {
}else if(reportData.params[0]==="BYOD"){ this.setState({ loading: true });
this.getEnrollmentTypeCount(params, urlSet);
}else{ let { statArray } = this.state;
this.getCount(params, urlSet);
console.log(urlSet);
const urlArray = [];
urlSet.paramsList.map(data => {
const paramsObj = {
status: data,
from: urlSet.duration[0],
to: urlSet.duration[1],
};
// console.log(paramsObj)
const encodedExtraParams = Object.keys(paramsObj)
.map(key => key + '=' + paramsObj[key])
.join('&');
const apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/reports/devices/count?' +
encodedExtraParams;
urlArray.push(axios.get(apiUrl, data));
});
console.log(urlArray);
axios
.all(urlArray)
.then(res => {
res.map(response => {
if (response.status === 200) {
let countData = {
item: response.config[0],
// eslint-disable-next-line radix
count: parseInt(response.data.data),
};
statArray.push(countData);
}
});
this.setState({ statArray });
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popup with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to get device count.',
});
} }
} });
};
clicked = () => { // Call count APIs and get count for given parameters, then create data object to build pie chart
console.log("Clicked...!!") getEnrollmentsVsUnenrollmentsCount = (params, urlSet) => {
this.setState({ loading: true });
let { statArray } = this.state;
console.log(urlSet);
const urlArray = [];
urlSet.paramsList.map(data => {
const paramsObj = {
from: urlSet.duration[0],
to: urlSet.duration[1],
};
const encodedExtraParams = Object.keys(paramsObj)
.map(key => key + '=' + paramsObj[key])
.join('&');
let apiUrl;
if (data === 'Enrollments') {
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/reports/devices/count?status=ACTIVE&status=INACTIVE&' +
encodedExtraParams;
} else {
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/reports/devices/count?status=REMOVED&' +
encodedExtraParams;
}
urlArray.push(axios.get(apiUrl, data));
});
console.log(urlArray);
axios
.all(urlArray)
.then(res => {
res.map(response => {
if (response.status === 200) {
let countData = {
item: response.config[0],
// eslint-disable-next-line radix
count: parseInt(response.data.data),
};
statArray.push(countData);
}
});
this.setState({ statArray });
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popup with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to get device count.',
});
}
});
};
// Call count APIs and get count for given parameters, then create data object to build pie chart
getEnrollmentTypeCount = (params, urlSet) => {
this.setState({ loading: true });
let { statArray } = this.state;
console.log(urlSet);
const urlArray = [];
urlSet.paramsList.map(data => {
const paramsObj = {
ownership: data,
from: urlSet.duration[0],
to: urlSet.duration[1],
};
const encodedExtraParams = Object.keys(paramsObj)
.map(key => key + '=' + paramsObj[key])
.join('&');
const apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/reports/devices/count?' +
encodedExtraParams;
urlArray.push(axios.get(apiUrl, data));
});
console.log(urlArray);
axios
.all(urlArray)
.then(res => {
res.map(response => {
if (response.status === 200) {
let countData = {
item: response.config[0],
// eslint-disable-next-line radix
count: parseInt(response.data.data),
};
statArray.push(countData);
}
});
this.setState({ statArray });
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popup with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to get device count.',
});
}
});
};
render() {
const { DataView } = DataSet;
const { Html } = Guide;
const { statArray } = this.state;
const dv = new DataView();
dv.source(statArray).transform({
type: 'percent',
field: 'count',
dimension: 'item',
as: 'percent',
});
const cols = {
percent: {
formatter: val => {
val = val * 100 + '%';
return val;
},
},
}; };
onChartChange = (data) => { return (
this.props.onClickPieChart(data); <div>
}; <Chart
height={window.innerHeight / 2}
statArray = []; data={dv}
scale={cols}
//Call count APIs and get count for given parameters, then create data object to build pie chart padding={[20, 25, 20, 20]}
getCount = (params, urlSet) => { forceFit
onPlotClick={this.onChartChange}
this.setState({loading: true}); animate={true}
>
let { statArray } = this.state; <Coord type={'theta'} radius={0.75} innerRadius={0.6} />
<Axis name="percent" />
console.log(urlSet); <Legend
position="right"
const urlArray = []; offsetY={-window.innerHeight / 2 + 120}
offsetX={-100}
urlSet.paramsList.map((data) => { />
const paramsObj = { <Tooltip
status:data, showTitle={false}
from:urlSet.duration[0], itemTpl='<li><span style="background-color:{color};" class="g2-tooltip-marker"></span>{name}: {value}</li>'
to:urlSet.duration[1] />
} <Guide>
// console.log(paramsObj) <Html
const encodedExtraParams = Object.keys(paramsObj) position={['50%', '50%']}
.map(key => key + '=' + paramsObj[key]).join('&'); html='<div style="color:#8c8c8c;font-size:1.16em;text-align: center;width: 10em;">Total<br><span style="color:#262626;font-size:2.5em">200</span>台</div>'
const apiUrl = window.location.origin + config.serverConfig.invoker.uri + alignX="middle"
config.serverConfig.invoker.deviceMgt + alignY="middle"
"/reports/devices/count?" + encodedExtraParams; />
</Guide>
urlArray.push(axios.get(apiUrl, data)); <div onClick={this.clicked}>
}); <Geom
type="intervalStack"
console.log(urlArray) position="percent"
color="item"
tooltip={[
axios.all(urlArray).then(res => { 'item*percent',
(item, percent) => {
res.map((response) => { percent = percent * 100 + '%';
if(response.status === 200){ return {
let countData = {item:response.config[0], count:parseInt(response.data.data)} name: item,
statArray.push(countData); value: percent,
} };
}) },
this.setState({statArray}) ]}
}).catch((error) => { style={{
if (error.hasOwnProperty("response") && error.response.status === 401) { lineWidth: 1,
//todo display a popup with error stroke: '#fff',
message.error('You are not logged in'); }}
window.location.href = window.location.origin + '/entgra/login'; >
} else { <Label
notification["error"]({ content="percent"
message: "There was a problem", formatter={(val, item) => {
duration: 0, return item.point.item + ': ' + val;
description:"Error occurred while trying to get device count.", }}
}); />
} </Geom>
}); </div>
}; </Chart>
</div>
//Call count APIs and get count for given parameters, then create data object to build pie chart );
getEnrollmentsVsUnenrollmentsCount = (params, urlSet) => { }
this.setState({loading: true});
let { statArray } = this.state;
console.log(urlSet);
const urlArray = [];
urlSet.paramsList.map((data) => {
const paramsObj = {
from:urlSet.duration[0],
to:urlSet.duration[1]
}
const encodedExtraParams = Object.keys(paramsObj)
.map(key => key + '=' + paramsObj[key]).join('&');
let apiUrl;
if(data==="Enrollments"){
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/reports/devices/count?status=ACTIVE&status=INACTIVE&" + encodedExtraParams;
}else{
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/reports/devices/count?status=REMOVED&" + encodedExtraParams;
}
urlArray.push(axios.get(apiUrl, data));
});
console.log(urlArray)
axios.all(urlArray).then(res => {
res.map((response) => {
if(response.status === 200){
let countData = {item:response.config[0], count:parseInt(response.data.data)}
statArray.push(countData);
}
})
this.setState({statArray})
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popup with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to get device count.",
});
}
});
};
//Call count APIs and get count for given parameters, then create data object to build pie chart
getEnrollmentTypeCount = (params, urlSet) => {
this.setState({loading: true});
let { statArray } = this.state;
console.log(urlSet);
const urlArray = [];
urlSet.paramsList.map((data) => {
const paramsObj = {
ownership:data,
from:urlSet.duration[0],
to:urlSet.duration[1]
}
const encodedExtraParams = Object.keys(paramsObj)
.map(key => key + '=' + paramsObj[key]).join('&');
const apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/reports/devices/count?" + encodedExtraParams;
urlArray.push(axios.get(apiUrl, data));
});
console.log(urlArray)
axios.all(urlArray).then(res => {
res.map((response) => {
if(response.status === 200){
let countData = {item:response.config[0], count:parseInt(response.data.data)}
statArray.push(countData);
}
})
this.setState({statArray})
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popup with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to get device count.",
});
}
});
};
render() {
const { DataView } = DataSet;
const { Html } = Guide;
const { statArray , loading} = this.state;
const dv = new DataView();
dv.source(statArray).transform({
type: "percent",
field: "count",
dimension: "item",
as: "percent"
});
const cols = {
percent: {
formatter: val => {
val = val * 100 + "%";
return val;
}
}
};
return (
<div>
<Chart
height={window.innerHeight/2}
data={dv}
scale={cols}
padding={[20, 25, 20, 20]}
forceFit
onPlotClick={this.onChartChange}
animate={true}
>
<Coord type={"theta"} radius={0.75} innerRadius={0.6} />
<Axis name="percent" />
<Legend
position="right"
offsetY={-window.innerHeight / 2 + 120}
offsetX={-100}
/>
<Tooltip
showTitle={false}
itemTpl="<li><span style=&quot;background-color:{color};&quot; class=&quot;g2-tooltip-marker&quot;></span>{name}: {value}</li>"
/>
<Guide>
<Html
position={["50%", "50%"]}
html="<div style=&quot;color:#8c8c8c;font-size:1.16em;text-align: center;width: 10em;&quot;>Total<br><span style=&quot;color:#262626;font-size:2.5em&quot;>200</span>台</div>"
alignX="middle"
alignY="middle"
/>
</Guide>
<div onClick={this.clicked}>
<Geom
type="intervalStack"
position="percent"
color="item"
tooltip={[
"item*percent",
(item, percent) => {
percent = percent * 100 + "%";
return {
name: item,
value: percent
};
}
]}
style={{
lineWidth: 1,
stroke: "#fff"
}}
>
<Label
content="percent"
formatter={(val, item) => {
return item.point.item + ": " + val;
}}/>
</Geom>
</div>
</Chart>
</div>
);
}
} }
export default withConfigContext(PieChart); export default withConfigContext(PieChart);

View File

@ -1,340 +1,392 @@
import React from 'react'; import React from 'react';
import {Button, Form, Input, message, Modal, notification, Select, Tree} from "antd"; import {
import {withConfigContext} from "../../context/ConfigContext"; Button,
import axios from "axios"; Form,
Input,
message,
Modal,
notification,
Select,
Tree,
} from 'antd';
import { withConfigContext } from '../../context/ConfigContext';
import axios from 'axios';
const {Option} = Select; const { Option } = Select;
const {TreeNode} = Tree; const { TreeNode } = Tree;
class AddRole extends React.Component { class AddRole extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.expandKeys = [];
this.state = {
isAddRoleModalVisible: false,
isAddPermissionModalVisible: false,
roleName: '',
users: [],
nodeList: [],
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
isNodeList: false,
};
}
constructor(props) { openAddModal = () => {
super(props); this.setState({
this.config = this.props.context; isAddRoleModalVisible: true,
this.expandKeys = []; });
this.state = { };
onCancelHandler = e => {
this.setState({
isAddRoleModalVisible: false,
isAddPermissionModalVisible: false,
});
};
getCheckedPermissionsList = data => {
data.forEach(item => {
if (item !== null) {
this.expandKeys.push(item.resourcePath);
this.getCheckedPermissionsList(item.nodeList);
} else {
return null;
}
});
};
onAddRole = e => {
this.props.form.validateFields((err, values) => {
if (!err) {
this.onConfirmAddRole(values);
}
console.log(values);
});
};
onExpand = expandedKeys => {
this.setState({
expandedKeys,
autoExpandParent: false,
});
};
onCheck = checkedKeys => {
this.setState({ checkedKeys });
};
onConfirmAddRole = value => {
const roleData = {
roleName: value.roleName,
users: value.users,
};
this.setState({
roleName: value.roleName,
});
axios
.post(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles',
roleData,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 201) {
this.props.fetchUsers();
this.setState({
isAddRoleModalVisible: false, isAddRoleModalVisible: false,
isAddPermissionModalVisible: false, isAddPermissionModalVisible: true,
roleName: '', });
users: [], notification.success({
nodeList: [], message: 'Done',
expandedKeys: [], duration: 4,
autoExpandParent: true, description: 'Successfully added the role.',
checkedKeys: [], });
isNodeList: false, this.loadPermissionList();
} }
} })
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to add role.',
});
}
});
};
openAddModal = () => { renderTreeNodes = data => {
this.setState({ return data.map(item => {
isAddRoleModalVisible: true if (item !== null) {
}); if (item.hasOwnProperty('nodeList')) {
return (
<TreeNode
title={item.displayName}
key={item.resourcePath}
dataRef={item}
>
{this.renderTreeNodes(item.nodeList)}
</TreeNode>
);
}
return <TreeNode key={item.resourcePath} {...item} />;
}
// eslint-disable-next-line react/jsx-key
return <TreeNode />;
});
};
onAssignPermissions = () => {
const roleData = {
roleName: this.state.roleName,
permissions: this.state.checkedKeys,
}; };
axios
onCancelHandler = e => { .put(
this.setState({ window.location.origin +
isAddRoleModalVisible: false, this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles/' +
this.state.roleName,
roleData,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully Updated the Permissions.',
});
this.setState({
isAddPermissionModalVisible: false, isAddPermissionModalVisible: false,
}); });
}; }
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to add permissions.',
});
}
});
};
getCheckedPermissionsList = (data) =>{ loadPermissionList = () => {
data.forEach(item => { let apiURL =
if (item !== null) { window.location.origin +
this.expandKeys.push(item.resourcePath); this.config.serverConfig.invoker.uri +
this.getCheckedPermissionsList(item.nodeList); this.config.serverConfig.invoker.deviceMgt +
}else{ '/roles/' +
return null; this.state.roleName +
} '/permissions';
});
};
onAddRole = e => { axios
this.props.form.validateFields((err, values) => { .get(apiURL)
if (!err) { .then(res => {
this.onConfirmAddRole(values); if (res.status === 200) {
} this.getCheckedPermissionsList(res.data.data.nodeList);
console.log(values); this.setState({
}); nodeList: res.data.data.nodeList,
}; isNodeList: true,
expandedKeys: this.expandKeys,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load permission.',
});
}
});
};
onExpand = expandedKeys => { loadUsersList = value => {
this.setState({ let apiURL =
expandedKeys, window.location.origin +
autoExpandParent: false, this.config.serverConfig.invoker.uri +
}); this.config.serverConfig.invoker.deviceMgt +
}; '/users/search/usernames?filter=' +
value +
'&domain=Primary';
axios
.get(apiURL)
.then(res => {
if (res.status === 200) {
let user = JSON.parse(res.data.data);
let users = [];
for (let i = 0; i < user.length; i++) {
users.push(
<Option key={user[i].username}>{user[i].username}</Option>,
);
}
this.setState({
users: users,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load users.',
});
}
});
};
onCheck = checkedKeys => { render() {
this.setState({checkedKeys}); const { getFieldDecorator } = this.props.form;
}; return (
<div>
onConfirmAddRole = (value) => { <div>
const roleData = { <Button
roleName: value.roleName, type="primary"
users: value.users, icon="plus"
}; size={'default'}
this.setState({ onClick={this.openAddModal}
roleName: value.roleName, style={{ marginBottom: '10px' }}
}); >
axios.post( Add Role
window.location.origin + this.config.serverConfig.invoker.uri + </Button>
this.config.serverConfig.invoker.deviceMgt + </div>
"/roles", <div>
roleData, <Modal
{headers: {'Content-Type': 'application-json'}} title="ADD NEW ROLE"
).then(res => { width="40%"
if (res.status === 201) { visible={this.state.isAddRoleModalVisible}
this.props.fetchUsers(); onOk={this.onAddRole}
this.setState({ onCancel={this.onCancelHandler}
isAddRoleModalVisible: false, footer={[
isAddPermissionModalVisible: true, <Button key="cancel" onClick={this.onCancelHandler}>
Cancel
}); </Button>,
notification["success"]({ <Button key="submit" type="primary" onClick={this.onAddRole}>
message: "Done", Add Role
duration: 4, </Button>,
description: ]}
"Successfully added the role.", >
}); <div style={{ alignItems: 'center' }}>
this.loadPermissionList(); <p>Create new user on IoT Server.</p>
} <Form labelCol={{ span: 5 }} wrapperCol={{ span: 18 }}>
}).catch((error) => { <Form.Item
if (error.hasOwnProperty("response") && error.response.status === 401) { label="User Store Domain"
//todo display a popop with error style={{ display: 'block' }}
message.error('You are not logged in'); >
window.location.href = window.location.origin + '/entgra/login'; {getFieldDecorator('userStoreDomain', {
} else { initialValue: 'PRIMARY',
notification["error"]({ })(
message: "There was a problem", <Select>
duration: 0, <Option key="PRIMARY">PRIMARY</Option>
description: </Select>,
"Error occurred while trying to add role.", )}
}); </Form.Item>
} <Form.Item label="Role Name" style={{ display: 'block' }}>
}); {getFieldDecorator('roleName', {
}; rules: [
{
renderTreeNodes = (data) => { pattern: new RegExp('^(((?!(\\@|\\/|\\s)).){3,})*$'),
return data.map(item => { message:
if (item !== null) { 'Role name should be in minimum 3 characters long and not ' +
if (item.hasOwnProperty("nodeList")) { 'include any whitespaces or @ or /',
return ( },
<TreeNode title={item.displayName} key={item.resourcePath} dataRef={item}> {
{this.renderTreeNodes(item.nodeList)} required: true,
</TreeNode> message: 'This field is required.',
); },
} ],
return <TreeNode key={item.resourcePath} {...item}/>; })(<Input />)}
} </Form.Item>
else{ <Form.Item label="User List" style={{ display: 'block' }}>
return <TreeNode/>; {getFieldDecorator('users', {})(
} <Select
}); mode="multiple"
}; style={{ width: '100%' }}
onSearch={this.loadUsersList}
onAssignPermissions = () =>{ >
const roleData = { {this.state.users}
roleName : this.state.roleName, </Select>,
permissions : this.state.checkedKeys )}
}; </Form.Item>
axios.put( </Form>
window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
"/roles/"+ this.state.roleName,
roleData,
{headers: {'Content-Type' : 'application-json'}}
).then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully Updated the Permissions.",
});
this.setState({
isAddPermissionModalVisible : false
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to add permissions.",
});
}
});
};
loadPermissionList = () => {
let apiURL = window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt + "/roles/" + this.state.roleName + "/permissions";
axios.get(apiURL).then(res => {
if (res.status === 200) {
this.getCheckedPermissionsList(res.data.data.nodeList);
this.setState({
nodeList: res.data.data.nodeList,
isNodeList: true,
expandedKeys : this.expandKeys
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description: "Error occurred while trying to load permission.",
});
}
})
};
loadUsersList = (value) => {
let apiURL = window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt + "/users/search/usernames?filter=" + value + "&domain=Primary";
axios.get(apiURL).then(res => {
if (res.status === 200) {
let user = JSON.parse(res.data.data);
let users = [];
for (let i = 0; i < user.length; i++) {
users.push(<Option key={user[i].username}>{user[i].username}</Option>);
}
this.setState({
users: users
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description: "Error occurred while trying to load users.",
});
}
})
};
render() {
const {getFieldDecorator} = this.props.form;
return (
<div>
<div>
<Button type="primary" icon="plus" size={"default"} onClick={this.openAddModal}
style={{marginBottom: '10px'}}>
Add Role
</Button>
</div>
<div>
<Modal
title="ADD NEW ROLE"
width="40%"
visible={this.state.isAddRoleModalVisible}
onOk={this.onAddRole}
onCancel={this.onCancelHandler}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.onAddRole}>
Add Role
</Button>,
]}>
<div style={{alignItems: "center"}}>
<p>Create new user on IoT Server.</p>
<Form
labelCol={{span: 5}}
wrapperCol={{span: 18}}>
<Form.Item label="User Store Domain" style={{display: "block"}}>
{getFieldDecorator('userStoreDomain', {
initialValue: 'PRIMARY'
})(
<Select>
<Option key="PRIMARY">PRIMARY</Option>
</Select>
)}
</Form.Item>
<Form.Item label="Role Name" style={{display: "block"}}>
{getFieldDecorator('roleName', {
rules: [
{
pattern: new RegExp("^(((?!(\\@|\\/|\\s)).){3,})*$"),
message: 'Role name should be in minimum 3 characters long and not ' +
'include any whitespaces or @ or /',
},
{
required: true,
message: 'This field is required.',
},
],
})(<Input/>)}
</Form.Item>
<Form.Item label="User List" style={{display: "block"}}>
{getFieldDecorator('users', {})(<Select
mode="multiple"
style={{width: '100%'}}
onSearch={this.loadUsersList}>
{this.state.users}
</Select>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
<div>
<Modal
title="CHANGE ROLE PERMISSION"
width="40%"
visible={this.state.isAddPermissionModalVisible}
onOk={this.onAssignPermissions}
onCancel={this.onCancelHandler}
bodyStyle={{overflowY:"scroll", maxHeight:'500px', marginLeft:'10px'}}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.onAssignPermissions}>
Assign
</Button>,
]}>
<div style={{alignItems: "center"}}>
<div>
{(this.state.isNodeList) && (
<Tree
checkable
onExpand={this.onExpand}
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
onCheck={this.onCheck}
checkedKeys={this.state.checkedKeys}>
{this.renderTreeNodes(this.state.nodeList)}
</Tree>
)}
</div>
</div>
</Modal>
</div>
</div> </div>
); </Modal>
} </div>
<div>
<Modal
title="CHANGE ROLE PERMISSION"
width="40%"
visible={this.state.isAddPermissionModalVisible}
onOk={this.onAssignPermissions}
onCancel={this.onCancelHandler}
bodyStyle={{
overflowY: 'scroll',
maxHeight: '500px',
marginLeft: '10px',
}}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button
key="submit"
type="primary"
onClick={this.onAssignPermissions}
>
Assign
</Button>,
]}
>
<div style={{ alignItems: 'center' }}>
<div>
{this.state.isNodeList && (
<Tree
checkable
onExpand={this.onExpand}
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
onCheck={this.onCheck}
checkedKeys={this.state.checkedKeys}
>
{this.renderTreeNodes(this.state.nodeList)}
</Tree>
)}
</div>
</div>
</Modal>
</div>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'add-role'})(AddRole)) export default withConfigContext(Form.create({ name: 'add-role' })(AddRole));

View File

@ -1,428 +1,489 @@
import React from 'react'; import React from 'react';
import { import {
Button, Button,
Divider, Divider,
Form, Form,
Icon, Icon,
Input, Input,
message, message,
Modal, Modal,
notification, notification,
Popconfirm, Popconfirm,
Select, Select,
Tooltip, Tree, Tooltip,
Typography Tree,
} from "antd"; Typography,
import axios from "axios"; } from 'antd';
import {withConfigContext} from "../../context/ConfigContext"; import axios from 'axios';
import { withConfigContext } from '../../context/ConfigContext';
const { Option } = Select; const { Option } = Select;
const {Text} = Typography; const { Text } = Typography;
const { TreeNode } = Tree; const { TreeNode } = Tree;
class RoleAction extends React.Component { class RoleAction extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.config = this.props.context; this.config = this.props.context;
this.selected = []; this.selected = [];
this.expandKeys = []; this.expandKeys = [];
this.state = { this.state = {
roleData: [], roleData: [],
nodeList : [], nodeList: [],
isNodeList: false, isNodeList: false,
users : [], users: [],
isEditRoleModalVisible: false,
isEditPermissionModalVisible: false,
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
};
}
openEditRoleModal = () => {
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles/' +
this.props.data;
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
roleData: res.data.data,
isEditRoleModalVisible: true,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load role.',
});
}
});
};
openEditPermissionModal = () => {
this.loadPermissionList();
this.setState({
isEditPermissionModalVisible: true,
});
};
loadPermissionList = () => {
let apiURL =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles/' +
this.props.data +
'/permissions';
axios
.get(apiURL)
.then(res => {
if (res.status === 200) {
this.getCheckedPermissions(res.data.data.nodeList);
this.setState({
nodeList: res.data.data.nodeList,
isNodeList: true,
checkedKeys: this.selected,
expandedKeys: this.expandKeys,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load permission.',
});
}
});
};
getCheckedPermissions = data => {
data.forEach(item => {
if (item !== null) {
this.expandKeys.push(item.resourcePath);
if (item.isSelected) {
this.selected.push(item.resourcePath);
}
this.getCheckedPermissions(item.nodeList);
} else {
return null;
}
});
};
onExpand = expandedKeys => {
this.setState({
expandedKeys,
autoExpandParent: false,
});
};
onCheck = checkedKeys => {
this.setState({ checkedKeys });
};
renderTreeNodes = data => {
return data.map(item => {
if (item !== null) {
if (item.hasOwnProperty('nodeList')) {
return (
<TreeNode
title={item.displayName}
key={item.resourcePath}
dataRef={item}
>
{this.renderTreeNodes(item.nodeList)}
</TreeNode>
);
}
return <TreeNode key={item.resourcePath} {...item} />;
}
// eslint-disable-next-line react/jsx-key
return <TreeNode />;
});
};
onUpdateRole = e => {
this.props.form.validateFields((err, values) => {
if (!err) {
this.onConfirmUpdateRole(values);
}
console.log(values);
});
};
onCancelHandler = e => {
this.setState({
isEditRoleModalVisible: false,
isEditPermissionModalVisible: false,
});
};
onConfirmUpdateRole = value => {
const roleData = {
roleName: value.roleName,
users: value.users,
};
axios
.put(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles/' +
this.props.data,
roleData,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchUsers();
this.setState({
isEditRoleModalVisible: false, isEditRoleModalVisible: false,
});
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully Updated the role.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to add role.',
});
}
});
};
onAssignPermission = () => {
const roleData = {
roleName: this.props.data,
permissions: this.state.checkedKeys,
};
axios
.put(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles/' +
this.props.data,
roleData,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully Updated the Permissions.',
});
this.setState({
isEditPermissionModalVisible: false, isEditPermissionModalVisible: false,
expandedKeys: [], });
autoExpandParent: true, }
checkedKeys: [], })
}; .catch(error => {
} if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to add permissions.',
});
}
});
};
openEditRoleModal = () =>{ loadUsersList = value => {
let apiUrl = window.location.origin + this.config.serverConfig.invoker.uri + let apiURL =
this.config.serverConfig.invoker.deviceMgt + window.location.origin +
"/roles/"+ this.props.data; this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/users/search/usernames?filter=' +
value +
'&domain=Primary';
axios
.get(apiURL)
.then(res => {
if (res.status === 200) {
let user = JSON.parse(res.data.data);
let users = [];
for (let i = 0; i < user.length; i++) {
users.push(
<Option key={user[i].username}>{user[i].username}</Option>,
);
}
this.setState({
users: users,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load users.',
});
}
});
};
axios.get(apiUrl).then(res => { onDeleteRole = () => {
if (res.status === 200) { axios
this.setState({ .delete(
roleData : res.data.data, window.location.origin +
isEditRoleModalVisible: true, this.config.serverConfig.invoker.uri +
}); this.config.serverConfig.invoker.deviceMgt +
} '/roles/' +
this.props.data,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully deleted the Role.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to delete role.',
});
}
});
};
}).catch((error) => { render() {
if (error.hasOwnProperty("response") && error.response.status === 401) { const isAdminRole = this.props.data === 'admin';
//todo display a popop with error const { getFieldDecorator } = this.props.form;
message.error('You are not logged in'); return (
window.location.href = window.location.origin + '/entgra/login'; <div>
} else { <div style={{ display: isAdminRole ? 'none' : 'inline' }}>
notification["error"]({ <Tooltip placement="top" title={'Edit Role'}>
message: "There was a problem", <a>
duration: 0, <Icon type="edit" onClick={this.openEditRoleModal} />
description:"Error occurred while trying to load role.", </a>
}); </Tooltip>
} <Divider type="vertical" />
}); <Tooltip placement="top" title={'Edit Permissions'}>
}; <a>
<Icon type="file-add" onClick={this.openEditPermissionModal} />
</a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottom" title={'Remove Role'}>
<Popconfirm
placement="top"
title={'Are you sure?'}
onConfirm={this.onDeleteRole}
okText="Ok"
cancelText="Cancel"
>
<a>
<Text type="danger">
<Icon type="delete" />
</Text>
</a>
</Popconfirm>
</Tooltip>
</div>
openEditPermissionModal =()=>{ <div>
this.loadPermissionList(); <Modal
this.setState({ title="EDIT ROLE"
isEditPermissionModalVisible: true, width="40%"
}); visible={this.state.isEditRoleModalVisible}
}; onOk={this.onUpdateRole}
onCancel={this.onCancelHandler}
loadPermissionList = () => { footer={[
let apiURL = window.location.origin + this.config.serverConfig.invoker.uri + <Button key="cancel" onClick={this.onCancelHandler}>
this.config.serverConfig.invoker.deviceMgt + "/roles/"+this.props.data+"/permissions"; Cancel
</Button>,
axios.get(apiURL).then(res => { <Button key="submit" type="primary" onClick={this.onUpdateRole}>
if (res.status === 200) { Add Role
this.getCheckedPermissions(res.data.data.nodeList); </Button>,
this.setState({ ]}
nodeList : res.data.data.nodeList, >
isNodeList: true, <div style={{ alignItems: 'center' }}>
checkedKeys : this.selected, <p>Create new user on IoT Server.</p>
expandedKeys : this.expandKeys <Form labelCol={{ span: 5 }} wrapperCol={{ span: 18 }}>
}); <Form.Item
} label="User Store Domain"
}).catch((error) => { style={{ display: 'block' }}
if (error.hasOwnProperty("response") && error.response.status === 401) { >
//todo display a popop with error {getFieldDecorator('userStoreDomain', {
message.error('You are not logged in'); initialValue: 'PRIMARY',
window.location.href = window.location.origin + '/entgra/login'; })(
} else { <Select>
notification["error"]({ <Option key="PRIMARY">PRIMARY</Option>
message: "There was a problem", </Select>,
duration: 0, )}
description:"Error occurred while trying to load permission.", </Form.Item>
}); <Form.Item label="Role Name" style={{ display: 'block' }}>
} {getFieldDecorator('roleName', {
}) initialValue: this.state.roleData.roleName,
}; rules: [
{
getCheckedPermissions = (data) =>{ pattern: new RegExp('^(((?!(\\@|\\/|\\s)).){3,})*$'),
data.forEach(item => { message:
if (item !== null) { 'Role name should be in minimum 3 characters long and not ' +
this.expandKeys.push(item.resourcePath); 'include any whitespaces or @ or /',
if (item.isSelected) { },
this.selected.push(item.resourcePath); {
} required: true,
this.getCheckedPermissions(item.nodeList); message: 'This field is required.',
}else{ },
return null; ],
} })(<Input />)}
}); </Form.Item>
}; <Form.Item label="User List" style={{ display: 'block' }}>
{getFieldDecorator('users', {
onExpand = expandedKeys => { initialValue: this.state.roleData.users,
this.setState({ })(
expandedKeys, <Select
autoExpandParent: false, mode="multiple"
}); style={{ width: '100%' }}
}; onSearch={this.loadUsersList}
>
onCheck = checkedKeys => { {this.state.users}
this.setState({checkedKeys}); </Select>,
}; )}
</Form.Item>
renderTreeNodes = (data) => { </Form>
return data.map(item => {
if (item !== null) {
if (item.hasOwnProperty("nodeList")) {
return (
<TreeNode title={item.displayName} key={item.resourcePath} dataRef={item}>
{this.renderTreeNodes(item.nodeList)}
</TreeNode>
);
}
return <TreeNode key={item.resourcePath} {...item}/>;
} else{
return <TreeNode/>;
}
});
};
onUpdateRole = e => {
this.props.form.validateFields((err, values) => {
if (!err) {
this.onConfirmUpdateRole(values);
}
console.log(values);
});
};
onCancelHandler = e =>{
this.setState({
isEditRoleModalVisible: false,
isEditPermissionModalVisible:false,
})
};
onConfirmUpdateRole = (value) =>{
const roleData = {
roleName : value.roleName,
users : value.users,
};
axios.put(
window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
"/roles/"+ this.props.data,
roleData,
{headers: {'Content-Type' : 'application-json'}}
).then(res => {
if (res.status === 200) {
this.props.fetchUsers();
this.setState({
isEditRoleModalVisible: false,
});
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully Updated the role.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to add role.",
});
}
});
};
onAssignPermission = () =>{
const roleData = {
roleName : this.props.data,
permissions : this.state.checkedKeys
};
axios.put(
window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
"/roles/"+ this.props.data,
roleData,
{headers: {'Content-Type' : 'application-json'}}
).then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully Updated the Permissions.",
});
this.setState({
isEditPermissionModalVisible : false
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to add permissions.",
});
}
});
};
loadUsersList = (value) => {
let apiURL = window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt + "/users/search/usernames?filter="+value+"&domain=Primary";
axios.get(apiURL).then(res => {
if (res.status === 200) {
let user = JSON.parse(res.data.data);
let users = [];
for(let i=0; i<user.length; i++){
users.push(<Option key={user[i].username}>{user[i].username}</Option>);
}
this.setState({
users : users
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load users.",
});
}
})
};
onDeleteRole = () => {
axios.delete(
window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
"/roles/" + this.props.data,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully deleted the Role.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to delete role.",
});
}
});
};
render() {
const isAdminRole = this.props.data ==="admin";
const { getFieldDecorator } = this.props.form;
return (
<div>
<div style={{display:isAdminRole ? "none" : "inline"}}>
<Tooltip placement="top" title={"Edit Role"}>
<a><Icon type="edit" onClick={this.openEditRoleModal}/></a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="top" title={"Edit Permissions"}>
<a><Icon type="file-add" onClick={this.openEditPermissionModal}/></a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottom" title={"Remove Role"}>
<Popconfirm
placement="top"
title={"Are you sure?"}
onConfirm={this.onDeleteRole}
okText="Ok"
cancelText="Cancel">
<a><Text type="danger"><Icon type="delete"/></Text></a>
</Popconfirm>
</Tooltip>
</div>
<div>
<Modal
title="EDIT ROLE"
width="40%"
visible={this.state.isEditRoleModalVisible}
onOk={this.onUpdateRole}
onCancel={this.onCancelHandler}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.onUpdateRole}>
Add Role
</Button>,
]}>
<div style={{alignItems:"center"}}>
<p>Create new user on IoT Server.</p>
<Form
labelCol={{ span: 5 }}
wrapperCol={{ span: 18 }}>
<Form.Item label="User Store Domain" style={{display:"block"}}>
{getFieldDecorator('userStoreDomain', {
initialValue : 'PRIMARY'
})(
<Select>
<Option key="PRIMARY">PRIMARY</Option>
</Select>
)}
</Form.Item>
<Form.Item label="Role Name" style={{display:"block"}}>
{getFieldDecorator('roleName', {
initialValue: this.state.roleData.roleName,
rules: [
{
pattern : new RegExp("^(((?!(\\@|\\/|\\s)).){3,})*$"),
message: 'Role name should be in minimum 3 characters long and not ' +
'include any whitespaces or @ or /',
},
{
required: true,
message: 'This field is required.',
},
],
})(<Input/>)}
</Form.Item>
<Form.Item label="User List" style={{display:"block"}}>
{getFieldDecorator('users', {
initialValue: this.state.roleData.users,
})(<Select
mode="multiple"
style={{ width: '100%' }}
onSearch={this.loadUsersList}>
{this.state.users}
</Select>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
<div>
<Modal
title="CHANGE ROLE PERMISSION"
width="40%"
visible={this.state.isEditPermissionModalVisible}
onOk={this.onAssignPermission}
onCancel={this.onCancelHandler}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.onAssignPermission}>
Assign
</Button>,
]}
bodyStyle={{overflowY:"scroll", maxHeight:'500px', marginLeft:'10px'}}>
<div>
{(this.state.isNodeList) &&(
<Tree
checkable
onExpand={this.onExpand}
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
onCheck={this.onCheck}
checkedKeys={this.state.checkedKeys}>
{this.renderTreeNodes(this.state.nodeList)}
</Tree>
)}
</div>
</Modal>
</div>
</div> </div>
); </Modal>
} </div>
<div>
<Modal
title="CHANGE ROLE PERMISSION"
width="40%"
visible={this.state.isEditPermissionModalVisible}
onOk={this.onAssignPermission}
onCancel={this.onCancelHandler}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button
key="submit"
type="primary"
onClick={this.onAssignPermission}
>
Assign
</Button>,
]}
bodyStyle={{
overflowY: 'scroll',
maxHeight: '500px',
marginLeft: '10px',
}}
>
<div>
{this.state.isNodeList && (
<Tree
checkable
onExpand={this.onExpand}
expandedKeys={this.state.expandedKeys}
autoExpandParent={this.state.autoExpandParent}
onCheck={this.onCheck}
checkedKeys={this.state.checkedKeys}
>
{this.renderTreeNodes(this.state.nodeList)}
</Tree>
)}
</div>
</Modal>
</div>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'role-actions'})(RoleAction)); export default withConfigContext(
Form.create({ name: 'role-actions' })(RoleAction),
);

View File

@ -16,243 +16,251 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import { import { Button, message, Modal, notification, Table, List } from 'antd';
Button, import TimeAgo from 'javascript-time-ago';
message,
Modal,
notification,
Table, List
} from "antd";
import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
import AddRole from "./AddRole"; import AddRole from './AddRole';
import RoleAction from "./RoleAction"; import RoleAction from './RoleAction';
import Filter from "../Utils/Filter/Filter"; import Filter from '../Utils/Filter/Filter';
const searchFields = [ const searchFields = [
{ {
name: 'filter', name: 'filter',
placeholder: 'Name' placeholder: 'Name',
} },
]; ];
class RolesTable extends React.Component { class RolesTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.config = this.props.context; this.config = this.props.context;
TimeAgo.addLocale(en); TimeAgo.addLocale(en);
this.state = { this.state = {
data: [], data: [],
pagination: {}, pagination: {},
loading: false, loading: false,
selectedRows: [], selectedRows: [],
userData: [], userData: [],
users : [], users: [],
isEditRoleModalVisible: false, isEditRoleModalVisible: false,
isUserListModalVisible :false, isUserListModalVisible: false,
}; };
} }
rowSelection = { rowSelection = {
onChange: (selectedRowKeys, selectedRows) => { onChange: (selectedRowKeys, selectedRows) => {
this.setState({ this.setState({
selectedRows: selectedRows selectedRows: selectedRows,
}) });
},
};
componentDidMount() {
this.fetchUsers();
}
openUserListModal = event => {
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles/' +
event;
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
userData: res.data.data.users,
isUserListModalVisible: true,
});
} }
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load users.',
});
}
});
};
handleOk = e => {
this.setState({
isUserListModalVisible: false,
});
};
handleCancel = e => {
this.setState({
isUserListModalVisible: false,
});
};
// fetch data from api
fetchUsers = (params = {}, filters = {}) => {
// const config = this.props.context;
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), // calculate the offset
limit: 10,
...filters,
}; };
componentDidMount() { const encodedExtraParams = Object.keys(extraParams)
this.fetchUsers(); .map(key => key + '=' + extraParams[key])
} .join('&');
openUserListModal = (event) => { let apiUrl =
let apiUrl = window.location.origin + this.config.serverConfig.invoker.uri + window.location.origin +
this.config.serverConfig.invoker.deviceMgt + this.config.serverConfig.invoker.uri +
"/roles/"+ event; this.config.serverConfig.invoker.deviceMgt +
'/roles?' +
encodedExtraParams;
//send request to the invokerss // send request to the invokerss
axios.get(apiUrl).then(res => { axios
if (res.status === 200) { .get(apiUrl)
this.setState({ .then(res => {
userData : res.data.data.users, if (res.status === 200) {
isUserListModalVisible: true, const pagination = { ...this.state.pagination };
}); this.setState({
} loading: false,
data: res.data.data.roles,
pagination,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load users.',
});
}
}).catch((error) => { this.setState({ loading: false });
if (error.hasOwnProperty("response") && error.response.status === 401) { });
//todo display a popop with error };
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load users.",
});
}
});
};
handleOk = e => { handleTableChange = (pagination, filters, sorter) => {
this.setState({ const pager = { ...this.state.pagination };
isUserListModalVisible: false, pager.current = pagination.current;
}); this.setState({
}; pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
handleCancel = e => { render() {
this.setState({ const { data, pagination, loading } = this.state;
isUserListModalVisible: false, const columns = [
}); {
}; title: 'Role Name',
dataIndex: '',
//fetch data from api key: 'role',
fetchUsers = (params = {}, filters={}) => { width: '60%',
// const config = this.props.context; },
this.setState({loading: true}); {
title: 'View',
// get current page dataIndex: '',
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; key: 'users',
render: (id, row) => (
const extraParams = { <Button
offset: 10 * (currentPage - 1), //calculate the offset type="primary"
limit: 10, size={'small'}
...filters icon="book"
}; onClick={() => this.openUserListModal(row)}
>
const encodedExtraParams = Object.keys(extraParams) Users
.map(key => key + '=' + extraParams[key]).join('&'); </Button>
),
let apiUrl = window.location.origin + this.config.serverConfig.invoker.uri + },
this.config.serverConfig.invoker.deviceMgt + {
"/roles?" + encodedExtraParams; title: 'Action',
dataIndex: 'id',
//send request to the invokerss key: 'action',
axios.get(apiUrl).then(res => { render: (id, row) => (
if (res.status === 200) { <span>
const pagination = {...this.state.pagination}; <RoleAction data={row} fetchUsers={this.fetchUsers} />
this.setState({ </span>
loading: false, ),
data: res.data.data.roles, },
pagination, ];
}); return (
} <div>
<div style={{ background: '#f0f2f5' }}>
}).catch((error) => { <AddRole fetchUsers={this.fetchUsers} />
if (error.hasOwnProperty("response") && error.response.status === 401) { </div>
//todo display a popop with error <div style={{ textAlign: 'right' }}>
message.error('You are not logged in'); <Filter fields={searchFields} callback={this.fetchUsers} />
window.location.href = window.location.origin + '/entgra/login'; </div>
} else { <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
notification["error"]({ <Table
message: "There was a problem", columns={columns}
duration: 0, rowKey={record => record}
description:"Error occurred while trying to load users.", dataSource={data}
}); pagination={{
} ...pagination,
size: 'small',
this.setState({loading: false}); // position: "top",
}); showTotal: (total, range) =>
}; `showing ${range[0]}-${range[1]} of ${total} groups`,
// showQuickJumper: true
handleTableChange = (pagination, filters, sorter) => { }}
const pager = {...this.state.pagination}; loading={loading}
pager.current = pagination.current; onChange={this.handleTableChange}
this.setState({ rowSelection={this.rowSelection}
pagination: pager, />
}); </div>
this.fetch({ <div>
results: pagination.pageSize, <Modal
page: pagination.current, title="USERS"
sortField: sorter.field, width="900px"
sortOrder: sorter.order, visible={this.state.isUserListModalVisible}
...filters, onOk={this.handleOk}
}); onCancel={this.handleCancel}
}; >
render() {
const {data, pagination, loading, selectedRows} = this.state;
const columns = [
{
title: 'Role Name',
dataIndex: '',
key: "role",
width: "60%",
},
{
title: 'View',
dataIndex: '',
key: 'users',
render: (id, row) =>
<Button
type="primary"
size={"small"}
icon="book"
onClick={()=>this.openUserListModal(row)}>Users</Button>
},
{
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id, row) => (
<span>
<RoleAction data={row} fetchUsers={this.fetchUsers}/>
</span>
),
},
];
return (
<div> <div>
<div style={{background: '#f0f2f5'}}> <List
<AddRole fetchUsers={this.fetchUsers}/> size="small"
</div> bordered
<div style={{textAlign: 'right'}}> dataSource={this.state.userData}
<Filter fields={searchFields} callback={this.fetchUsers}/> renderItem={item => <List.Item>{item}</List.Item>}
</div> />
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}>
<Table
columns={columns}
rowKey={record => (record)}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
<div>
<Modal
title="USERS"
width="900px"
visible={this.state.isUserListModalVisible}
onOk={this.handleOk}
onCancel={this.handleCancel} >
<div>
<List
size="small"
bordered
dataSource={this.state.userData}
renderItem={item => <List.Item>{item}</List.Item>}/>
</div>
</Modal>
</div>
</div> </div>
</Modal>
); </div>
} </div>
);
}
} }
export default withConfigContext(RolesTable); export default withConfigContext(RolesTable);

View File

@ -17,21 +17,28 @@
*/ */
import React from 'react'; import React from 'react';
import {Route} from 'react-router-dom'; import { Route } from 'react-router-dom';
class RouteWithSubRoutes extends React.Component{ class RouteWithSubRoutes extends React.Component {
props; props;
constructor(props){ constructor(props) {
super(props); super(props);
this.props = props; this.props = props;
} }
render() { render() {
return( return (
<Route path={this.props.path} exact={this.props.exact} render={(props) => ( <Route
<this.props.component {...props} {...this.props} routes={this.props.routes}/> path={this.props.path}
)}/> exact={this.props.exact}
); render={props => (
} <this.props.component
{...props}
{...this.props}
routes={this.props.routes}
/>
)}
/>
);
}
} }
export default RouteWithSubRoutes; export default RouteWithSubRoutes;

View File

@ -1,215 +1,245 @@
import React from 'react'; import React from 'react';
import {Button, Form, Select, Input, message, Modal, notification, Typography} from "antd"; import {
import axios from "axios"; Button,
import {withConfigContext} from "../../context/ConfigContext"; Form,
Select,
Input,
message,
Modal,
notification,
} from 'antd';
import axios from 'axios';
import { withConfigContext } from '../../context/ConfigContext';
const { Option } = Select; const { Option } = Select;
const {Text} = Typography;
class AddUser extends React.Component { class AddUser extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.state = {
isModalVisible: false,
roles: [],
};
}
constructor(props) { componentDidMount() {
super(props); this.getRole();
this.config = this.props.context; }
this.state = {
openAddModal = () => {
this.setState({
isModalVisible: true,
});
};
onSubmitHandler = e => {
this.props.form.validateFields((err, values) => {
if (!err) {
this.onConfirmAddUser(values);
}
});
};
onConfirmAddUser = value => {
const userData = {
username: value.userStoreDomain + '/' + value.userName,
firstname: value.firstName,
lastname: value.lastName,
emailAddress: value.email,
roles: value.userRoles,
};
axios
.post(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/users',
userData,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 201) {
this.props.fetchUsers();
this.setState({
isModalVisible: false, isModalVisible: false,
roles : [] });
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully added the user.',
});
} }
} })
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to add user.',
});
}
});
};
componentDidMount() { onCancelHandler = e => {
this.getRole(); this.setState({
} isModalVisible: false,
});
};
openAddModal = () => { getRole = () => {
this.setState({ let apiURL =
isModalVisible:true window.location.origin +
}); this.config.serverConfig.invoker.uri +
}; this.config.serverConfig.invoker.deviceMgt +
'/roles?user-store=PRIMARY&limit=100';
onSubmitHandler = e => { axios
this.props.form.validateFields((err, values) => { .get(apiURL)
if (!err) { .then(res => {
this.onConfirmAddUser(values); if (res.status === 200) {
} const roles = [];
}); for (let i = 0; i < res.data.data.roles.length; i++) {
}; roles.push(
<Option key={res.data.data.roles[i]}>
{res.data.data.roles[i]}
</Option>,
);
}
this.setState({
roles: roles,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
onConfirmAddUser = (value) =>{ message.error('You are not logged in');
const userData = { window.location.href = window.location.origin + '/entgra/login';
username : value.userStoreDomain +"/"+value.userName, } else {
firstname : value.firstName, notification.error({
lastname : value.lastName, message: 'There was a problem',
emailAddress : value.email, duration: 0,
roles : value.userRoles description: 'Error occurred while trying to load roles.',
}; });
axios.post( }
window.location.origin + this.config.serverConfig.invoker.uri + });
this.config.serverConfig.invoker.deviceMgt + };
"/users",
userData,
{headers: {'Content-Type' : 'application-json'}}
).then(res => {
if (res.status === 201) {
this.props.fetchUsers();
this.setState({
isModalVisible: false,
});
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully added the user.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to add user.",
});
}
});
};
onCancelHandler = e => { render() {
this.setState({ const { getFieldDecorator } = this.props.form;
isModalVisible: false, return (
}); <div>
}; <div>
<Button
getRole = () => { type="primary"
let apiURL = window.location.origin + this.config.serverConfig.invoker.uri + icon="plus"
this.config.serverConfig.invoker.deviceMgt + "/roles?user-store=PRIMARY&limit=100"; size={'default'}
onClick={this.openAddModal}
axios.get(apiURL).then(res => { style={{ marginBottom: '10px' }}
if (res.status === 200) { >
const roles = []; Add User
for(let i=0; i<res.data.data.roles.length ; i++){ </Button>
roles.push(<Option key={res.data.data.roles[i]}>{res.data.data.roles[i]}</Option>); </div>
} <div>
this.setState({ <Modal
roles : roles title="ADD NEW USER"
}) width="40%"
} visible={this.state.isModalVisible}
}).catch((error) => { onOk={this.onSubmitHandler}
if (error.hasOwnProperty("response") && error.response.status === 401) { onCancel={this.onCancelHandler}
//todo display a popop with error footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
message.error('You are not logged in'); Cancel
window.location.href = window.location.origin + '/entgra/login'; </Button>,
} else { <Button
notification["error"]({ key="submit"
message: "There was a problem", type="primary"
duration: 0, onClick={this.onSubmitHandler}
description:"Error occurred while trying to load roles.", >
}); Submit
} </Button>,
}) ]}
}; >
<div style={{ alignItems: 'center' }}>
render() { <p>Create new user on IoT Server.</p>
const { getFieldDecorator } = this.props.form; <Form labelCol={{ span: 5 }} wrapperCol={{ span: 18 }}>
return ( <Form.Item
<div> label="User Store Domain"
<div> style={{ display: 'block' }}
<Button type="primary" icon="plus" size={"default"} onClick={this.openAddModal} style={{marginBottom : '10px'}}> >
Add User {getFieldDecorator('userStoreDomain', {
</Button> initialValue: 'PRIMARY',
</div> })(
<div> <Select>
<Modal <Option key="PRIMARY">PRIMARY</Option>
title="ADD NEW USER" </Select>,
width="40%" )}
visible={this.state.isModalVisible} </Form.Item>
onOk={this.onSubmitHandler} <Form.Item label="User Name" style={{ display: 'block' }}>
onCancel={this.onCancelHandler} {getFieldDecorator('userName', {
footer={[ rules: [
<Button key="cancel" onClick={this.onCancelHandler}> {
Cancel required: true,
</Button>, message:
<Button key="submit" type="primary" onClick={this.onSubmitHandler}> 'This field is required. Username should be at least 3 characters long with no white spaces.',
Submit },
</Button>, ],
]}> })(<Input />)}
<div style={{alignItems:"center"}}> </Form.Item>
<p>Create new user on IoT Server.</p> <Form.Item label="First Name" style={{ display: 'block' }}>
<Form {getFieldDecorator('firstName', {
labelCol={{ span: 5 }} rules: [
wrapperCol={{ span: 18 }}> {
<Form.Item label="User Store Domain" style={{display:"block"}}> required: true,
{getFieldDecorator('userStoreDomain', { message: 'This field is required',
initialValue : 'PRIMARY' },
})( ],
<Select> })(<Input />)}
<Option key="PRIMARY">PRIMARY</Option> </Form.Item>
</Select> <Form.Item label="Last Name" style={{ display: 'block' }}>
)} {getFieldDecorator('lastName', {
</Form.Item> rules: [
<Form.Item label="User Name" style={{display:"block"}}> {
{getFieldDecorator('userName', { required: true,
rules: [ message: 'This field is required',
{ },
required: true, ],
message: 'This field is required. Username should be at least 3 characters long with no white spaces.', })(<Input />)}
}, </Form.Item>
], <Form.Item label="Email Address" style={{ display: 'block' }}>
})(<Input/>)} {getFieldDecorator('email', {
</Form.Item> rules: [
<Form.Item label="First Name" style={{display:"block"}}> {
{getFieldDecorator('firstName', { type: 'email',
rules: [ message: 'Invalid Email Address',
{ },
required: true, {
message: 'This field is required', required: true,
}, message: 'This field is required',
], },
})(<Input/>)} ],
</Form.Item> })(<Input />)}
<Form.Item label="Last Name" style={{display:"block"}}> </Form.Item>
{getFieldDecorator('lastName', { <Form.Item label="User Roles" style={{ display: 'block' }}>
rules: [ {getFieldDecorator('userRoles', {})(
{ <Select mode="multiple" style={{ width: '100%' }}>
required: true, {this.state.roles}
message: 'This field is required', </Select>,
}, )}
], </Form.Item>
})(<Input/>)} </Form>
</Form.Item>
<Form.Item label="Email Address" style={{display:"block"}}>
{getFieldDecorator('email', {
rules: [
{
type: 'email',
message: 'Invalid Email Address',
},
{
required: true,
message: 'This field is required',
},
],
})(<Input/>)}
</Form.Item>
<Form.Item label="User Roles" style={{display:"block"}}>
{getFieldDecorator('userRoles', {
})(<Select
mode="multiple"
style={{ width: '100%' }}>
{this.state.roles}
</Select>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
</div> </div>
); </Modal>
} </div>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'add-user'})(AddUser)) export default withConfigContext(Form.create({ name: 'add-user' })(AddUser));

View File

@ -1,423 +1,472 @@
import React from 'react'; import React from 'react';
import { import {
Button, Button,
Divider, Divider,
Form, Form,
Icon, Icon,
Input, Input,
message, message,
Modal, Modal,
notification, notification,
Popconfirm, Popconfirm,
Select, Select,
Tooltip, Tooltip,
Typography } from "antd"; Typography,
import axios from "axios"; } from 'antd';
import {withConfigContext} from "../../context/ConfigContext"; import axios from 'axios';
import { withConfigContext } from '../../context/ConfigContext';
const { Option } = Select; const { Option } = Select;
const {Text} = Typography; const { Text } = Typography;
class UserActions extends React.Component { class UserActions extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.config = this.props.context; this.config = this.props.context;
this.state = { this.state = {
isEditModalVisible: false, isEditModalVisible: false,
isResetPasswordModalVisible: false, isResetPasswordModalVisible: false,
rolesData: [], rolesData: [],
} };
}
openEditModal = () => {
this.setState({
isEditModalVisible: true,
});
this.fetchRoles(this.props.data.username);
};
openPasswordResetModal = () => {
this.setState({
isResetPasswordModalVisible: true,
});
};
onCancelHandler = () => {
this.setState({
isEditModalVisible: false,
isResetPasswordModalVisible: false,
});
};
compareToFirstPassword = (rule, value, callback) => {
if (value && value !== this.props.form.getFieldValue('password')) {
callback("New password doesn't match the confirmation.");
} else {
callback();
} }
openEditModal = () =>{ };
this.setState({
isEditModalVisible: true,
});
this.fetchRoles(this.props.data.username);
}; onSavePassword = () => {
openPasswordResetModal = () =>{ this.props.form.validateFields(
this.setState({ ['password', 'confirmPassword'],
isResetPasswordModalVisible: true, (err, values) => {
}) if (!err) {
}; this.onResetPassword(values);
}
},
);
};
onCancelHandler = () =>{ onResetPassword = value => {
this.setState({ const password = {
isEditModalVisible: false, newPassword: value.password,
};
axios
.post(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/admin/users/' +
this.props.data.username +
'/credentials',
password,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchUsers();
this.setState({
isResetPasswordModalVisible: false, isResetPasswordModalVisible: false,
}) });
}; notification.success({
message: 'Done',
compareToFirstPassword = (rule, value, callback) => { duration: 4,
if (value && value !== this.props.form.getFieldValue('password')) { description: 'Successfully reset the password',
callback('New password doesn\'t match the confirmation.'); });
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else { } else {
callback(); notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to reset password.',
});
} }
});
};
componentDidMount() {
this.getRole();
}
getRole = () => {
let apiURL =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles?user-store=PRIMARY&limit=100';
axios
.get(apiURL)
.then(res => {
if (res.status === 200) {
const roles = [];
for (let i = 0; i < res.data.data.roles.length; i++) {
roles.push(
<Option key={res.data.data.roles[i]}>
{res.data.data.roles[i]}
</Option>,
);
}
this.setState({
roles: roles,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load roles.',
});
}
});
};
onConfirmDeleteUser = () => {
axios
.delete(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/users/' +
this.props.data.username,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully deleted the user.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to delete user.',
});
}
});
};
fetchRoles = username => {
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/users/' +
username +
'/roles';
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
rolesData: res.data.data.roles,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load roles.',
});
}
});
};
handleEditOk = e => {
this.props.form.validateFields(
[
'userStoreDomain',
'userName',
'firstName',
'lastName',
'email',
'userRoles',
],
(err, values) => {
if (!err) {
this.onUpdateUser(values);
}
},
);
};
onUpdateUser = value => {
const userData = {
username: value.userStoreDomain + '/' + value.userName,
firstname: value.firstName,
lastname: value.lastName,
emailAddress: value.email,
roles: value.userRoles,
}; };
axios
.put(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/users/' +
this.props.data.username,
userData,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 200) {
this.props.fetchUsers();
this.setState({
isEditModalVisible: false,
});
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully updated the user.',
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to update user.',
});
}
});
};
onSavePassword = () => { render() {
this.props.form.validateFields((['password', 'confirmPassword']),(err, values) => { const isAdminUser = this.props.data.username === 'admin';
if (!err) { const { getFieldDecorator } = this.props.form;
this.onResetPassword(values); return (
} <div>
}); <div style={{ display: isAdminUser ? 'none' : 'inline' }}>
}; <Tooltip placement="top" title={'Edit User'}>
<a>
<Icon type="edit" onClick={this.openEditModal} />
</a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="top" title={'Reset Password'}>
<a>
<Icon type="key" onClick={this.openPasswordResetModal} />
</a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottom" title={'Remove User'}>
<Popconfirm
placement="top"
title={'Are you sure?'}
onConfirm={this.onConfirmDeleteUser}
okText="Ok"
cancelText="Cancel"
>
<a>
<Text type="danger">
<Icon type="delete" />
</Text>
</a>
</Popconfirm>
</Tooltip>
</div>
onResetPassword = (value) =>{ <div>
const password = { <Modal
newPassword : value.password, title="Reset Password"
}; width="40%"
axios.post( visible={this.state.isResetPasswordModalVisible}
window.location.origin + this.config.serverConfig.invoker.uri + onCancel={this.onCancelHandler}
this.config.serverConfig.invoker.deviceMgt + onOk={this.onSavePassword}
"/admin/users/"+this.props.data.username+"/credentials", footer={[
password, <Button key="cancel" onClick={this.onCancelHandler}>
{headers: {'Content-Type' : 'application-json'}} Cancel
).then(res => { </Button>,
if (res.status === 200) { <Button key="submit" type="primary" onClick={this.onSavePassword}>
this.props.fetchUsers(); Save
this.setState({ </Button>,
isResetPasswordModalVisible: false, ]}
}); >
notification["success"]({ <div style={{ alignItems: 'center' }}>
message: "Done", <Form labelCol={{ span: 6 }} wrapperCol={{ span: 17 }}>
duration: 4, <Form.Item label="New Password" style={{ display: 'block' }}>
description: {getFieldDecorator('password', {
"Successfully reset the password", rules: [
}); {
} required: true,
}).catch((error) => { message: 'This field is required',
if (error.hasOwnProperty("response") && error.response.status === 401) { },
//todo display a popop with error ],
message.error('You are not logged in'); })(<Input.Password />)}
window.location.href = window.location.origin + '/entgra/login'; </Form.Item>
} else { <Form.Item
notification["error"]({ label="Retype New Password"
message: "There was a problem", style={{ display: 'block' }}
duration: 0, >
description: {getFieldDecorator('confirmPassword', {
"Error occurred while trying to reset password.", rules: [
}); {
} required: true,
}); message: 'This field is required',
},
}; {
validator: this.compareToFirstPassword,
componentDidMount() { },
this.getRole(); ],
} })(<Input.Password />)}
</Form.Item>
getRole = () => { </Form>
let apiURL = window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt + "/roles?user-store=PRIMARY&limit=100";
axios.get(apiURL).then(res => {
if (res.status === 200) {
const roles = [];
for(let i=0; i<res.data.data.roles.length ; i++){
roles.push(<Option key={res.data.data.roles[i]}>{res.data.data.roles[i]}</Option>);
}
this.setState({
roles : roles
})
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load roles.",
});
}
})
};
onConfirmDeleteUser = () => {
axios.delete(
window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
"/users/" + this.props.data.username,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 200) {
this.props.fetchUsers();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully deleted the user.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to delete user.",
});
}
});
};
fetchRoles = (username) => {
let apiUrl = window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
"/users/" + username + "/roles";
axios.get(apiUrl).then(res => {
if (res.status === 200) {
this.setState({
rolesData: res.data.data.roles,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load roles.",
});
}
});
};
handleEditOk = e =>{
this.props.form.validateFields((['userStoreDomain', 'userName', 'firstName', 'lastName' , 'email', 'userRoles']),(err, values) => {
if (!err) {
this.onUpdateUser(values);
}
});
};
onUpdateUser = (value) =>{
const userData = {
username : value.userStoreDomain +"/"+value.userName,
firstname : value.firstName,
lastname : value.lastName,
emailAddress : value.email,
roles : value.userRoles
};
axios.put(
window.location.origin + this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
"/users/"+ this.props.data.username,
userData,
{headers: {'Content-Type' : 'application-json'}}
).then(res => {
if (res.status === 200) {
this.props.fetchUsers();
this.setState({
isEditModalVisible: false,
});
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully updated the user.",
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to update user.",
});
}
});
};
render() {
const isAdminUser = this.props.data.username ==="admin";
const { getFieldDecorator } = this.props.form;
return (
<div>
<div style={{display:isAdminUser ? "none" : "inline"}}>
<Tooltip placement="top" title={"Edit User"}>
<a><Icon type="edit" onClick={this.openEditModal}/></a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="top" title={"Reset Password"}>
<a><Icon type="key" onClick={this.openPasswordResetModal}/></a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottom" title={"Remove User"}>
<Popconfirm
placement="top"
title={"Are you sure?"}
onConfirm={this.onConfirmDeleteUser}
okText="Ok"
cancelText="Cancel">
<a><Text type="danger"><Icon type="delete"/></Text></a>
</Popconfirm>
</Tooltip>
</div>
<div>
<Modal
title="Reset Password"
width="40%"
visible={this.state.isResetPasswordModalVisible}
onCancel={this.onCancelHandler}
onOk={this.onSavePassword}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.onSavePassword} >
Save
</Button>,
]}>
<div style={{alignItems:"center"}}>
<Form
labelCol={{ span: 6 }}
wrapperCol={{ span: 17 }}>
<Form.Item label="New Password" style={{display:"block"}}>
{getFieldDecorator(
'password',
{
rules: [
{
required: true,
message: 'This field is required',
},
],
})(<Input.Password/>)}
</Form.Item>
<Form.Item label="Retype New Password" style={{display:"block"}}>
{getFieldDecorator(
'confirmPassword',
{
rules: [
{
required: true,
message: 'This field is required',
},
{
validator: this.compareToFirstPassword,
},
],
})(<Input.Password/>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
<div>
<Modal
title="EDIT USER"
width="40%"
visible={this.state.isEditModalVisible}
onOk={this.handleEditOk}
onCancel={this.onCancelHandler}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleEditOk}>
Update
</Button>,
]}>
<div style={{alignItems:"center"}}>
<p>Create new user on IoT Server.</p>
<Form
labelCol={{ span: 5 }}
wrapperCol={{ span: 18 }}>
<Form.Item label="User Store Domain" style={{display:"block"}}>
{getFieldDecorator('userStoreDomain', {
initialValue : 'PRIMARY'
})(
<Select disabled={true}>
<Option key="PRIMARY">PRIMARY</Option>
</Select>
)}
</Form.Item>
<Form.Item label="User Name" style={{display:"block"}}>
{getFieldDecorator('userName', {
initialValue: this.props.data.username,
rules: [
{
required: true,
message: 'This field is required. Username should be at least 3 characters long with no white spaces.',
},
],
})(<Input disabled={true}/>)}
</Form.Item>
<Form.Item label="First Name" style={{display:"block"}}>
{getFieldDecorator('firstName', {
initialValue: this.props.data.firstname,
rules: [
{
required: true,
message: 'This field is required',
},
],
})(<Input/>)}
</Form.Item>
<Form.Item label="Last Name" style={{display:"block"}}>
{getFieldDecorator('lastName', {
initialValue: this.props.data.lastname,
rules: [
{
required: true,
message: 'This field is required',
},
],
})(<Input/>)}
</Form.Item>
<Form.Item label="Email Address" style={{display:"block"}}>
{getFieldDecorator('email', {
initialValue: this.props.data.emailAddress,
rules: [
{
type: 'email',
message: 'Invalid Email Address',
},
{
required: true,
message: 'This field is required',
},
],
})(<Input/>)}
</Form.Item>
<Form.Item label="User Roles" style={{display:"block"}}>
{getFieldDecorator('userRoles', {
initialValue: this.state.rolesData,
})(<Select
mode="multiple"
style={{ width: '100%' }}>
{this.state.roles}
</Select>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
</div> </div>
); </Modal>
} </div>
<div>
<Modal
title="EDIT USER"
width="40%"
visible={this.state.isEditModalVisible}
onOk={this.handleEditOk}
onCancel={this.onCancelHandler}
footer={[
<Button key="cancel" onClick={this.onCancelHandler}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleEditOk}>
Update
</Button>,
]}
>
<div style={{ alignItems: 'center' }}>
<p>Create new user on IoT Server.</p>
<Form labelCol={{ span: 5 }} wrapperCol={{ span: 18 }}>
<Form.Item
label="User Store Domain"
style={{ display: 'block' }}
>
{getFieldDecorator('userStoreDomain', {
initialValue: 'PRIMARY',
})(
<Select disabled={true}>
<Option key="PRIMARY">PRIMARY</Option>
</Select>,
)}
</Form.Item>
<Form.Item label="User Name" style={{ display: 'block' }}>
{getFieldDecorator('userName', {
initialValue: this.props.data.username,
rules: [
{
required: true,
message:
'This field is required. Username should be at least 3 characters long with no white spaces.',
},
],
})(<Input disabled={true} />)}
</Form.Item>
<Form.Item label="First Name" style={{ display: 'block' }}>
{getFieldDecorator('firstName', {
initialValue: this.props.data.firstname,
rules: [
{
required: true,
message: 'This field is required',
},
],
})(<Input />)}
</Form.Item>
<Form.Item label="Last Name" style={{ display: 'block' }}>
{getFieldDecorator('lastName', {
initialValue: this.props.data.lastname,
rules: [
{
required: true,
message: 'This field is required',
},
],
})(<Input />)}
</Form.Item>
<Form.Item label="Email Address" style={{ display: 'block' }}>
{getFieldDecorator('email', {
initialValue: this.props.data.emailAddress,
rules: [
{
type: 'email',
message: 'Invalid Email Address',
},
{
required: true,
message: 'This field is required',
},
],
})(<Input />)}
</Form.Item>
<Form.Item label="User Roles" style={{ display: 'block' }}>
{getFieldDecorator('userRoles', {
initialValue: this.state.rolesData,
})(
<Select mode="multiple" style={{ width: '100%' }}>
{this.state.roles}
</Select>,
)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
</div>
);
}
} }
export default withConfigContext(Form.create({name: 'user-actions'})(UserActions)); export default withConfigContext(
Form.create({ name: 'user-actions' })(UserActions),
);

View File

@ -16,233 +16,245 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Icon, message, notification, Table, Tag, Tooltip, Typography} from "antd"; import { Icon, message, notification, Table, Tag, Tooltip } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
const {Text} = Typography;
let config = null; let config = null;
let apiUrl;
const columns = [ const columns = [
{ {
title: 'Device', title: 'Device',
dataIndex: 'name', dataIndex: 'name',
width: 100, width: 100,
}, },
{ {
title: 'Type', title: 'Type',
dataIndex: 'type', dataIndex: 'type',
key: 'type', key: 'type',
render: type => { // eslint-disable-next-line react/display-name
const defaultPlatformIcons = config.defaultPlatformIcons; render: type => {
let icon = defaultPlatformIcons.default.icon; const defaultPlatformIcons = config.defaultPlatformIcons;
let color = defaultPlatformIcons.default.color; let icon = defaultPlatformIcons.default.icon;
let theme = defaultPlatformIcons.default.theme; let color = defaultPlatformIcons.default.color;
let theme = defaultPlatformIcons.default.theme;
if (defaultPlatformIcons.hasOwnProperty(type)) { if (defaultPlatformIcons.hasOwnProperty(type)) {
icon = defaultPlatformIcons[type].icon; icon = defaultPlatformIcons[type].icon;
color = defaultPlatformIcons[type].color; color = defaultPlatformIcons[type].color;
theme = defaultPlatformIcons[type].theme; theme = defaultPlatformIcons[type].theme;
} }
return ( return (
<span style={{fontSize: 20, color: color, textAlign: "center"}}> <span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
<Icon type={icon} theme={theme}/> <Icon type={icon} theme={theme} />
</span> </span>
); );
}
// todo add filtering options
}, },
{ // todo add filtering options
title: 'Owner', },
dataIndex: 'enrolmentInfo', {
key: 'owner', title: 'Owner',
render: enrolmentInfo => enrolmentInfo.owner dataIndex: 'enrolmentInfo',
// todo add filtering options key: 'owner',
render: enrolmentInfo => enrolmentInfo.owner,
// todo add filtering options
},
{
title: 'Ownership',
dataIndex: 'enrolmentInfo',
key: 'ownership',
width: 100,
render: enrolmentInfo => enrolmentInfo.ownership,
// todo add filtering options
},
{
title: 'Status',
dataIndex: 'enrolmentInfo',
width: 100,
key: 'status',
// eslint-disable-next-line react/display-name
render: enrolmentInfo => {
const status = enrolmentInfo.status.toLowerCase();
let color = '#f9ca24';
switch (status) {
case 'active':
color = '#badc58';
break;
case 'created':
color = '#6ab04c';
break;
case 'removed':
color = '#ff7979';
break;
case 'inactive':
color = '#f9ca24';
break;
case 'blocked':
color = '#636e72';
break;
}
return <Tag color={color}>{status}</Tag>;
}, },
{ // todo add filtering options
title: 'Ownership', },
dataIndex: 'enrolmentInfo', {
key: 'ownership', title: 'Last Updated',
width: 100, dataIndex: 'enrolmentInfo',
render: enrolmentInfo => enrolmentInfo.ownership key: 'dateOfLastUpdate',
// todo add filtering options // eslint-disable-next-line react/display-name
render: data => {
const { dateOfLastUpdate } = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return (
<Tooltip title={new Date(dateOfLastUpdate).toString()}>
{timeAgoString}
</Tooltip>
);
}, },
{ // todo add filtering options
title: 'Status', },
dataIndex: 'enrolmentInfo',
width: 100,
key: 'status',
render: (enrolmentInfo) => {
const status = enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "removed":
color = "#ff7979";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
}
return <Tag color={color}>{status}</Tag>;
}
// todo add filtering options
},
{
title: 'Last Updated',
dataIndex: 'enrolmentInfo',
key: 'dateOfLastUpdate',
render: (data) => {
const {dateOfLastUpdate} = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return <Tooltip title={new Date(dateOfLastUpdate).toString()}>{timeAgoString}</Tooltip>;
}
// todo add filtering options
}
]; ];
const getTimeAgo = (time) => { const getTimeAgo = time => {
const timeAgo = new TimeAgo('en-US'); const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time); return timeAgo.format(time);
}; };
class UsersDevices extends React.Component { class UsersDevices extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; config = this.props.context;
TimeAgo.addLocale(en); TimeAgo.addLocale(en);
this.state = { this.state = {
data: [], data: [],
pagination: {}, pagination: {},
loading: false,
selectedRows: [],
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows,
});
},
};
componentDidMount() {
this.fetch();
}
// Rerender component when parameters change
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.user !== this.props.user) {
this.fetch();
}
}
// fetch data from api
fetch = (params = {}) => {
const config = this.props.context;
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), // calculate the offset
limit: 10,
user: this.props.user,
requireDeviceInfo: true,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key])
.join('&');
// send request to the invoker
axios
.get(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/devices?' +
encodedExtraParams,
)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [] data: res.data.data.devices,
}; pagination,
} });
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows
})
} }
}; })
.catch(error => {
componentDidMount() { if (error.hasOwnProperty('response') && error.response.status === 401) {
this.fetch(); // todo display a popop with error
} message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
//Rerender component when parameters change } else {
componentDidUpdate(prevProps, prevState, snapshot) { notification.error({
if(prevProps.user !== this.props.user){ message: 'There was a problem',
this.fetch(); duration: 0,
description: 'Error occurred while trying to load devices.',
});
} }
}
//fetch data from api this.setState({ loading: false });
fetch = (params = {}) => { });
const config = this.props.context; };
this.setState({loading: true});
// get current page
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
const extraParams = { handleTableChange = (pagination, filters, sorter) => {
offset: 10 * (currentPage - 1), //calculate the offset const pager = { ...this.state.pagination };
limit: 10, pager.current = pagination.current;
user: this.props.user, this.setState({
requireDeviceInfo: true, pagination: pager,
}; });
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
const encodedExtraParams = Object.keys(extraParams) render() {
.map(key => key + '=' + extraParams[key]).join('&'); const { data, pagination, loading } = this.state;
return (
//send request to the invoker <div>
axios.get( <Table
window.location.origin + config.serverConfig.invoker.uri + columns={columns}
config.serverConfig.invoker.deviceMgt + rowKey={record =>
"/devices?" + encodedExtraParams, record.deviceIdentifier +
).then(res => { record.enrolmentInfo.owner +
if (res.status === 200) { record.enrolmentInfo.ownership
const pagination = {...this.state.pagination}; }
this.setState({ dataSource={data}
loading: false, showHeader={false}
data: res.data.data.devices, size="small"
pagination pagination={{
}); ...pagination,
} size: 'small',
// position: "top",
}).catch((error) => { showTotal: (total, range) =>
if (error.hasOwnProperty("response") && error.response.status === 401) { `showing ${range[0]}-${range[1]} of ${total} devices`,
//todo display a popop with error // showQuickJumper: true
message.error('You are not logged in'); }}
window.location.href = window.location.origin + '/entgra/login'; loading={loading}
} else { onChange={this.handleTableChange}
notification["error"]({ rowSelection={this.rowSelection}
message: "There was a problem", />
duration: 0, </div>
description: );
"Error occurred while trying to load devices.", }
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
return (
<div>
<Table
columns={columns}
rowKey={record => (record.deviceIdentifier + record.enrolmentInfo.owner + record.enrolmentInfo.ownership)}
dataSource={data}
showHeader={false}
size="small"
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
);
}
} }
export default withConfigContext(UsersDevices); export default withConfigContext(UsersDevices);

View File

@ -16,305 +16,322 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import axios from "axios"; import axios from 'axios';
import {Button, Collapse, Icon, List, message, Modal, notification, Table, Tabs, Typography} from "antd"; import { Button, List, message, Modal, notification, Table } from 'antd';
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
import UsersDevices from "./UsersDevices"; import UsersDevices from './UsersDevices';
import AddUser from "./AddUser"; import AddUser from './AddUser';
import UserActions from "./UserActions"; import UserActions from './UserActions';
import Filter from "../Utils/Filter/Filter"; import Filter from '../Utils/Filter/Filter';
const ButtonGroup = Button.Group; const ButtonGroup = Button.Group;
const {Text} = Typography;
let config = null;
let apiUrl; let apiUrl;
const searchFields = [ const searchFields = [
{ {
name: 'username', name: 'username',
placeholder: 'Username' placeholder: 'Username',
}, },
{ {
name: 'firstName', name: 'firstName',
placeholder: 'First Name' placeholder: 'First Name',
}, },
{ {
name: 'lastName', name: 'lastName',
placeholder: 'Last Name' placeholder: 'Last Name',
}, },
{ {
name: 'emailAddress', name: 'emailAddress',
placeholder: 'Email Address' placeholder: 'Email Address',
}, },
]; ];
class UsersTable extends React.Component { class UsersTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
config = this.props.context; TimeAgo.addLocale(en);
TimeAgo.addLocale(en); this.state = {
this.state = { data: [],
data: [], pagination: {},
pagination: {}, loading: false,
selectedRows: [],
rolesModalVisible: false,
devicesModalVisible: false,
rolesData: [],
user: '',
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows,
});
},
};
componentDidMount() {
this.fetchUsers();
}
// fetch data from api
fetchUsers = (params = {}, filters = {}) => {
const config = this.props.context;
this.setState({ loading: true });
// get current page
const currentPage = params.hasOwnProperty('page') ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), // calculate the offset
limit: 10,
...filters,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key])
.join('&');
apiUrl =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/users/search?' +
encodedExtraParams;
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false, loading: false,
selectedRows: [], data: res.data.data.users,
rolesModalVisible: false, pagination,
devicesModalVisible: false, });
rolesData: [],
user:''
};
}
rowSelection = { console.log(res.data.data);
onChange: (selectedRowKeys, selectedRows) => { }
this.setState({ })
selectedRows: selectedRows .catch(error => {
}) if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load users.',
});
} }
};
componentDidMount() { this.setState({ loading: false });
this.fetchUsers(); });
} };
//fetch data from api fetchRoles = username => {
fetchUsers = (params = {}, filters = {}) => { const config = this.props.context;
const config = this.props.context;
this.setState({loading: true});
// get current page this.setState({
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; rolesModalVisible: true,
user: username,
});
const extraParams = { apiUrl =
offset: 10 * (currentPage - 1), //calculate the offset window.location.origin +
limit: 10, config.serverConfig.invoker.uri +
...filters config.serverConfig.invoker.deviceMgt +
}; '/users/' +
username +
'/roles';
const encodedExtraParams = Object.keys(extraParams) axios
.map(key => key + '=' + extraParams[key]).join('&'); .get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
rolesData: res.data.data.roles,
});
}
})
.catch(error => {
if (error.hasOwnProperty('response') && error.response.status === 401) {
// todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to load roles.',
});
}
apiUrl = window.location.origin + config.serverConfig.invoker.uri + this.setState({ loading: false });
config.serverConfig.invoker.deviceMgt + });
"/users/search?" + encodedExtraParams; };
//send request to the invokerss handleTableChange = (pagination, filters, sorter) => {
axios.get(apiUrl).then(res => { const pager = { ...this.state.pagination };
if (res.status === 200) { pager.current = pagination.current;
const pagination = {...this.state.pagination}; this.setState({
this.setState({ pagination: pager,
loading: false, });
data: res.data.data.users, this.fetch({
pagination, results: pagination.pageSize,
}); page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
console.log(res.data.data) handleOk = e => {
} this.setState({
rolesModalVisible: false,
devicesModalVisible: false,
});
};
}).catch((error) => { handleCancel = e => {
if (error.hasOwnProperty("response") && error.response.status === 401) { this.setState({
//todo display a popop with error rolesModalVisible: false,
message.error('You are not logged in'); devicesModalVisible: false,
window.location.href = window.location.origin + '/entgra/login'; });
} else { };
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load users.",
});
}
this.setState({loading: false}); openDeviceListModal = e => {
}); this.setState({
}; devicesModalVisible: true,
});
};
fetchRoles = (username) => { render() {
const config = this.props.context; const { data, pagination, loading } = this.state;
const columns = [
this.setState({ {
rolesModalVisible: true, title: 'User Name',
user: username dataIndex: 'username',
}); key: 'username',
},
apiUrl = window.location.origin + config.serverConfig.invoker.uri + {
config.serverConfig.invoker.deviceMgt + title: 'First Name',
"/users/" + username + "/roles"; dataIndex: 'firstname',
key: 'firstname',
axios.get(apiUrl).then(res => { },
if (res.status === 200) { {
this.setState({ title: 'Last Name',
rolesData: res.data.data.roles, dataIndex: 'lastname',
}); key: 'lastname',
} },
{
}).catch((error) => { title: 'Email',
if (error.hasOwnProperty("response") && error.response.status === 401) { dataIndex: 'emailAddress',
//todo display a popop with error key: 'emailAddress',
message.error('You are not logged in'); },
window.location.href = window.location.origin + '/entgra/login'; {
} else { title: 'View',
notification["error"]({ dataIndex: 'username',
message: "There was a problem", key: 'roles',
duration: 0, render: username => (
description:"Error occurred while trying to load roles.", <ButtonGroup>
}); <Button
} type="primary"
size={'small'}
this.setState({loading: false}); icon="book"
}); onClick={() => this.fetchRoles(username)}
}; >
Roles
handleTableChange = (pagination, filters, sorter) => { </Button>
const pager = {...this.state.pagination}; <Button
pager.current = pagination.current; type="primary"
this.setState({ size={'small'}
pagination: pager, icon="desktop"
}); onClick={this.openDeviceListModal}
this.fetch({ >
results: pagination.pageSize, Devices
page: pagination.current, </Button>
sortField: sorter.field, </ButtonGroup>
sortOrder: sorter.order, ),
...filters, },
}); {
}; title: 'Action',
dataIndex: 'id',
handleOk = e => { key: 'action',
this.setState({ render: (id, row) => (
rolesModalVisible: false, <span>
devicesModalVisible: false <UserActions data={row} fetchUsers={this.fetchUsers} />
}); </span>
}; ),
},
handleCancel = e => { ];
this.setState({ return (
rolesModalVisible: false, <div>
devicesModalVisible: false <div style={{ background: '#f0f2f5' }}>
}); <AddUser fetchUsers={this.fetchUsers} />
}; </div>
<div style={{ textAlign: 'right' }}>
openDeviceListModal = e =>{ <Filter fields={searchFields} callback={this.fetchUsers} />
this.setState({ </div>
devicesModalVisible: true, <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
}) <Table
}; columns={columns}
rowKey={record => record.username}
render() { dataSource={data}
const {data, pagination, loading, selectedRows} = this.state; pagination={{
const { Panel } = Collapse; ...pagination,
const { TabPane } = Tabs; size: 'small',
const columns = [ // position: "top",
{ showTotal: (total, range) =>
title: 'User Name', `showing ${range[0]}-${range[1]} of ${total} groups`,
dataIndex: 'username', // showQuickJumper: true
key: "username", }}
}, loading={loading}
{ onChange={this.handleTableChange}
title: 'First Name', rowSelection={this.rowSelection}
dataIndex: 'firstname', />
key: 'firstname', </div>
}, <div>
{ <Modal
title: 'Last Name', title="ROLES"
dataIndex: 'lastname', width="900px"
key: 'lastname', visible={this.state.rolesModalVisible}
}, onOk={this.handleOk}
{ onCancel={this.handleCancel}
title: 'Email', >
dataIndex: 'emailAddress',
key: 'emailAddress',
},
{
title: 'View',
dataIndex: 'username',
key: 'roles',
render: (username) =>
<ButtonGroup>
<Button
type="primary"
size={"small"}
icon="book"
onClick={() => this.fetchRoles(username)}>Roles</Button>
<Button
type="primary"
size={"small"}
icon="desktop"
onClick={this.openDeviceListModal}>Devices</Button>
</ButtonGroup>
},
{
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id, row) => (
<span>
<UserActions data={row} fetchUsers={this.fetchUsers}/>
</span>
),
},
];
return (
<div> <div>
<div style={{background: '#f0f2f5'}}> <List
<AddUser fetchUsers={this.fetchUsers}/> size="small"
</div> bordered
<div style={{textAlign: 'right'}}> dataSource={this.state.rolesData}
<Filter fields={searchFields} callback={this.fetchUsers}/> renderItem={item => <List.Item>{item}</List.Item>}
</div> />
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}>
<Table
columns={columns}
rowKey={record => (record.username)}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
<div>
<Modal
title="ROLES"
width="900px"
visible={this.state.rolesModalVisible}
onOk={this.handleOk}
onCancel={this.handleCancel} >
<div>
<List
size="small"
bordered
dataSource={this.state.rolesData}
renderItem={item => <List.Item>{item}</List.Item>}/>
</div>
</Modal>
</div>
<div>
<Modal
title="DEVICES"
width="900px"
visible={this.state.devicesModalVisible}
onOk={this.handleOk}
onCancel={this.handleCancel}>
<div>
<UsersDevices user={this.state.user}/>
</div>
</Modal>
</div>
</div> </div>
); </Modal>
} </div>
<div>
<Modal
title="DEVICES"
width="900px"
visible={this.state.devicesModalVisible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<div>
<UsersDevices user={this.state.user} />
</div>
</Modal>
</div>
</div>
);
}
} }
export default withConfigContext(UsersTable); export default withConfigContext(UsersTable);

View File

@ -1,41 +1,38 @@
import React from "react"; import React from 'react';
import {Form, Icon, Input, Button} from 'antd'; import { Form, Input, Button } from 'antd';
const hasErrors = (fieldsError) => {
return Object.keys(fieldsError).some(field => fieldsError[field]);
};
class HorizontalLoginForm extends React.Component { class HorizontalLoginForm extends React.Component {
handleSubmit = e => { handleSubmit = e => {
e.preventDefault(); e.preventDefault();
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
Object.keys(values).forEach(key => values[key] === undefined && delete values[key]); Object.keys(values).forEach(
this.props.callback({},values); key => values[key] === undefined && delete values[key],
}
});
};
render() {
const {getFieldDecorator} = this.props.form;
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
{this.props.fields.map((field) => (
<Form.Item key={field.name}>
{getFieldDecorator(field.name)(
<Input placeholder={field.placeholder}/>,
)}
</Form.Item>
))}
<Form.Item>
<Button htmlType='submit' shape="circle" icon="search" />
</Form.Item>
</Form>
); );
} this.props.callback({}, values);
}
});
};
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
{this.props.fields.map(field => (
<Form.Item key={field.name}>
{getFieldDecorator(field.name)(
<Input placeholder={field.placeholder} />,
)}
</Form.Item>
))}
<Form.Item>
<Button htmlType="submit" shape="circle" icon="search" />
</Form.Item>
</Form>
);
}
} }
export default Form.create({name: 'horizontal_login'})(HorizontalLoginForm); export default Form.create({ name: 'horizontal_login' })(HorizontalLoginForm);

View File

@ -16,19 +16,19 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
const ConfigContext = React.createContext(); const ConfigContext = React.createContext();
export const withConfigContext = Component => { export const withConfigContext = Component => {
return props => ( // eslint-disable-next-line react/display-name
<ConfigContext.Consumer> return props => (
{context => { <ConfigContext.Consumer>
return <Component {...props} context={context}/>; {context => {
}} return <Component {...props} context={context} />;
</ConfigContext.Consumer> }}
); </ConfigContext.Consumer>
);
}; };
export default ConfigContext; export default ConfigContext;

View File

@ -19,119 +19,117 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker'; import * as serviceWorker from './serviceWorker';
import App from "./App"; import App from './App';
import Login from "./pages/Login"; import Login from './pages/Login';
import Dashboard from "./pages/Dashboard/Dashboard"; import Dashboard from './pages/Dashboard/Dashboard';
import './index.css'; import './index.css';
import Devices from "./pages/Dashboard/Devices/Devices"; import Devices from './pages/Dashboard/Devices/Devices';
import Reports from "./pages/Dashboard/Reports/Reports"; import Reports from './pages/Dashboard/Reports/Reports';
import Geo from "./pages/Dashboard/Geo/Geo"; import Geo from './pages/Dashboard/Geo/Geo';
import Groups from "./pages/Dashboard/Groups/Groups"; import Groups from './pages/Dashboard/Groups/Groups';
import Users from "./pages/Dashboard/Users/Users"; import Users from './pages/Dashboard/Users/Users';
import Policies from "./pages/Dashboard/Policies/Policies"; import Policies from './pages/Dashboard/Policies/Policies';
import Roles from "./pages/Dashboard/Roles/Roles"; import Roles from './pages/Dashboard/Roles/Roles';
import DeviceTypes from "./pages/Dashboard/DeviceTypes/DeviceTypes"; import DeviceTypes from './pages/Dashboard/DeviceTypes/DeviceTypes';
import DeviceEnroll from "./pages/Dashboard/Devices/DeviceEnroll"; import DeviceEnroll from './pages/Dashboard/Devices/DeviceEnroll';
import AddNewPolicy from "./pages/Dashboard/Policies/AddNewPolicy"; import AddNewPolicy from './pages/Dashboard/Policies/AddNewPolicy';
import Certificates from "./pages/Dashboard/Configurations/Certificates/Certificates"; import Certificates from './pages/Dashboard/Configurations/Certificates/Certificates';
import ReportDurationItemList from "./pages/Dashboard/Reports/ReportDurationItemList"; import ReportDurationItemList from './pages/Dashboard/Reports/ReportDurationItemList';
import EnrollmentsVsUnenrollmentsReport from "./components/Reports/Templates/EnrollmentsVsUnenrollmentsReport"; import EnrollmentsVsUnenrollmentsReport from './components/Reports/Templates/EnrollmentsVsUnenrollmentsReport';
import EnrollmentTypeReport from "./components/Reports/Templates/EnrollmentTypeReport"; import EnrollmentTypeReport from './components/Reports/Templates/EnrollmentTypeReport';
import DeviceStatusReport from './components/Reports/Templates/DeviceStatusReport';
const routes = [ const routes = [
{ {
path: '/entgra/login', path: '/entgra/login',
exact: true,
component: Login,
},
{
path: '/entgra',
exact: false,
component: Dashboard,
routes: [
{
path: '/entgra/devices',
component: Devices,
exact: true, exact: true,
component: Login },
}, {
{ path: '/entgra/devices/enroll',
path: '/entgra', component: DeviceEnroll,
exact: false, exact: true,
component: Dashboard, },
routes: [ {
{ path: '/entgra/geo',
path: '/entgra/devices', component: Geo,
component: Devices, exact: true,
exact: true },
}, {
{ path: '/entgra/reports',
path: '/entgra/devices/enroll', component: Reports,
component: DeviceEnroll, exact: true,
exact: true },
}, {
{ path: '/entgra/groups',
path: '/entgra/geo', component: Groups,
component: Geo, exact: true,
exact: true },
}, {
{ path: '/entgra/users',
path: '/entgra/reports', component: Users,
component: Reports, exact: true,
exact: true },
}, {
{ path: '/entgra/policies',
path: '/entgra/groups', component: Policies,
component: Groups, exact: true,
exact: true },
}, {
{ path: '/entgra/policy/add',
path: '/entgra/users', component: AddNewPolicy,
component: Users, exact: true,
exact: true },
}, {
{ path: '/entgra/roles',
path: '/entgra/policies', component: Roles,
component: Policies, exact: true,
exact: true },
}, {
{ path: '/entgra/devicetypes',
path: '/entgra/policy/add', component: DeviceTypes,
component: AddNewPolicy, exact: true,
exact: true },
}, {
{ path: '/entgra/certificates',
path: '/entgra/roles', component: Certificates,
component: Roles, exact: true,
exact: true },
}, {
{ path: '/entgra/reportList',
path: '/entgra/devicetypes', component: ReportDurationItemList,
component: DeviceTypes, exact: true,
exact: true },
}, {
{ path: '/entgra/enrollmentsvsunenrollments',
path: '/entgra/certificates', component: EnrollmentsVsUnenrollmentsReport,
component: Certificates, exact: true,
exact: true },
}, {
{ path: '/entgra/enrollmenttype',
path: '/entgra/reportList', component: EnrollmentTypeReport,
component: ReportDurationItemList, exact: true,
exact: true },
}, {
{ path: '/entgra/devicestatus',
path: '/entgra/enrollmentsvsunenrollments', component: DeviceStatusReport,
component: EnrollmentsVsUnenrollmentsReport, exact: true,
exact: true },
}, ],
{ },
path: '/entgra/enrollmenttype',
component: EnrollmentTypeReport,
exact: true
},
{
path: '/entgra/devicestatus',
component: DeviceStatusReport,
exact: true
}
]
}
]; ];
ReactDOM.render(<App routes={routes} />, document.getElementById('root'));
ReactDOM.render(
<App routes={routes}/>,
document.getElementById('root'));
// If you want your app e and load faster, you can change // If you want your app e and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls. // unregister() to register() below. Note this comes with some pitfalls.

View File

@ -16,52 +16,48 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import CertificateTable from '../../../../components/Configurations/Certificates/CertificateTable';
Breadcrumb,
Icon,
} from "antd";
import {Link} from "react-router-dom";
import DeviceTable from "../../../../components/Devices/DevicesTable";
import CertificateTable from "../../../../components/Configurations/Certificates/CertificateTable";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class Certificates extends React.Component { class Certificates extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra/devices"><Icon type="home"/> Home</Link> <Link to="/entgra/devices">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Configurations</Breadcrumb.Item> </Link>
<Breadcrumb.Item>Certificates</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb> <Breadcrumb.Item>Configurations</Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Certificates</Breadcrumb.Item>
<h3>Certificates</h3> </Breadcrumb>
<Paragraph>Certificate configurations</Paragraph> <div className="wrap">
</div> <h3>Certificates</h3>
<div style={{backgroundColor: "#ffffff", borderRadius: 5}}> <Paragraph>Certificate configurations</Paragraph>
<CertificateTable/> </div>
</div> <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
</PageHeader> <CertificateTable />
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> </div>
</PageHeader>
</div> <div
</div> style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
); ></div>
} </div>
);
}
} }
export default Certificates; export default Certificates;

View File

@ -16,193 +16,197 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import {Layout, Menu, Icon} from 'antd'; import { Layout, Menu, Icon } from 'antd';
import {Switch, Link} from "react-router-dom"; import { Switch, Link } from 'react-router-dom';
import RouteWithSubRoutes from "../../components/RouteWithSubRoutes" import RouteWithSubRoutes from '../../components/RouteWithSubRoutes';
import {Redirect} from 'react-router' import { Redirect } from 'react-router';
import "./Dashboard.css"; import './Dashboard.css';
import {withConfigContext} from "../../context/ConfigContext"; import { withConfigContext } from '../../context/ConfigContext';
import Logout from "./Logout/Logout"; import Logout from './Logout/Logout';
const {Header, Content, Footer, Sider} = Layout; const { Header, Content, Footer } = Layout;
const {SubMenu} = Menu; const { SubMenu } = Menu;
class Dashboard extends React.Component { class Dashboard extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const mobileWidth = (window.innerWidth<=768 ? '0' : '80'); const mobileWidth = window.innerWidth <= 768 ? '0' : '80';
this.state = { this.state = {
routes: props.routes, routes: props.routes,
selectedKeys: [], selectedKeys: [],
deviceTypes: [], deviceTypes: [],
isNavBarCollapsed: false, isNavBarCollapsed: false,
mobileWidth mobileWidth,
};
this.logo = this.props.context.theme.logo;
this.config = this.props.context;
}
toggle = () => {
console.log(this.config)
this.setState({
isNavBarCollapsed: !this.state.isNavBarCollapsed,
});
}; };
this.logo = this.props.context.theme.logo;
this.config = this.props.context;
}
render() { toggle = () => {
return ( console.log(this.config);
<div> this.setState({
<Layout className="layout" > isNavBarCollapsed: !this.state.isNavBarCollapsed,
<Layout> });
<Header style={{background: '#fff', padding: 0}}> };
<div className="logo-image">
<Link to="/entgra/devices"><img alt="logo" src={this.logo}/></Link>
</div>
<Menu render() {
theme="light" return (
mode="horizontal" <div>
style={{ <Layout className="layout">
lineHeight: '64px', <Layout>
marginRight: 110 <Header style={{ background: '#fff', padding: 0 }}>
}} <div className="logo-image">
> <Link to="/entgra/devices">
<SubMenu <img alt="logo" src={this.logo} />
key="devices" </Link>
title={ </div>
<span>
<Icon type="appstore"/>
<span>Devices</span>
</span>}
>
<Menu.Item key="devices">
<Link to="/entgra/devices">
<span>View</span>
</Link>
</Menu.Item>
<Menu.Item key="deviceEnroll">
<Link to="/entgra/devices/enroll">
<span>Enroll</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="geo"
title={
<span>
<Icon type="environment"/>
<span>Geo</span>
</span>}
>
<Menu.Item key="singleDevice">
<Link to="/entgra/geo">
<span>Single Device View</span>
</Link>
</Menu.Item>
<Menu.Item key="deviceGroup">
<Link to="#">
<span>Device Group View</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="reports">
<Link to="/entgra/reports">
<Icon type="bar-chart"/>
<span>Reports</span>
</Link>
</Menu.Item>
<Menu.Item key="groups">
<Link to="/entgra/groups">
<Icon type="deployment-unit"/>
<span>Groups</span>
</Link>
</Menu.Item>
<Menu.Item key="users">
<Link to="/entgra/users">
<Icon type="user"/>
<span>Users</span>
</Link>
</Menu.Item>
<SubMenu
key="policies"
title={
<span>
<Icon type="audit"/>
<span>Policies</span>
</span>}
>
<Menu.Item key="policiesList">
<Link to="/entgra/policies">
<span>View</span>
</Link>
</Menu.Item>
<Menu.Item key="addPolicy">
<Link to="/entgra/policy/add">
<span>Add New Policy</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="roles">
<Link to="/entgra/roles">
<Icon type="book"/>
<span>Roles</span>
</Link>
</Menu.Item>
<Menu.Item key="devicetypes">
<Link to="/entgra/devicetypes">
<Icon type="desktop"/>
<span>Device Types</span>
</Link>
</Menu.Item>
<SubMenu
key="configurations"
title={
<span>
<Icon type="setting"/>
<span>Configurations</span>
</span>}
>
<Menu.Item key="certificates">
<Link to="/entgra/certificates">
<span>Certificates</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="trigger">
</Menu.Item>
<SubMenu className="profile"
title={
<span className="submenu-title-wrapper">
<Icon type="user"/>
{this.config.user}
</span> }>
<Logout/>
</SubMenu>
</Menu> <Menu
</Header> theme="light"
mode="horizontal"
style={{
lineHeight: '64px',
marginRight: 110,
}}
>
<SubMenu
key="devices"
title={
<span>
<Icon type="appstore" />
<span>Devices</span>
</span>
}
>
<Menu.Item key="devices">
<Link to="/entgra/devices">
<span>View</span>
</Link>
</Menu.Item>
<Menu.Item key="deviceEnroll">
<Link to="/entgra/devices/enroll">
<span>Enroll</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="geo"
title={
<span>
<Icon type="environment" />
<span>Geo</span>
</span>
}
>
<Menu.Item key="singleDevice">
<Link to="/entgra/geo">
<span>Single Device View</span>
</Link>
</Menu.Item>
<Menu.Item key="deviceGroup">
<Link to="#">
<span>Device Group View</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="reports">
<Link to="/entgra/reports">
<Icon type="bar-chart" />
<span>Reports</span>
</Link>
</Menu.Item>
<Menu.Item key="groups">
<Link to="/entgra/groups">
<Icon type="deployment-unit" />
<span>Groups</span>
</Link>
</Menu.Item>
<Menu.Item key="users">
<Link to="/entgra/users">
<Icon type="user" />
<span>Users</span>
</Link>
</Menu.Item>
<SubMenu
key="policies"
title={
<span>
<Icon type="audit" />
<span>Policies</span>
</span>
}
>
<Menu.Item key="policiesList">
<Link to="/entgra/policies">
<span>View</span>
</Link>
</Menu.Item>
<Menu.Item key="addPolicy">
<Link to="/entgra/policy/add">
<span>Add New Policy</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="roles">
<Link to="/entgra/roles">
<Icon type="book" />
<span>Roles</span>
</Link>
</Menu.Item>
<Menu.Item key="devicetypes">
<Link to="/entgra/devicetypes">
<Icon type="desktop" />
<span>Device Types</span>
</Link>
</Menu.Item>
<SubMenu
key="configurations"
title={
<span>
<Icon type="setting" />
<span>Configurations</span>
</span>
}
>
<Menu.Item key="certificates">
<Link to="/entgra/certificates">
<span>Certificates</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="trigger"></Menu.Item>
<SubMenu
className="profile"
title={
<span className="submenu-title-wrapper">
<Icon type="user" />
{this.config.user}
</span>
}
>
<Logout />
</SubMenu>
</Menu>
</Header>
<Content style={{marginTop: 10}}> <Content style={{ marginTop: 10 }}>
<Switch> <Switch>
<Redirect exact from="/entgra" to="/entgra/devices"/> <Redirect exact from="/entgra" to="/entgra/devices" />
{this.state.routes.map((route) => ( {this.state.routes.map(route => (
<RouteWithSubRoutes key={route.path} {...route} /> <RouteWithSubRoutes key={route.path} {...route} />
))} ))}
</Switch> </Switch>
</Content> </Content>
<Footer style={{textAlign: 'center'}}> <Footer style={{ textAlign: 'center' }}>©2019 entgra.io</Footer>
©2019 entgra.io </Layout>
</Footer> </Layout>
</div>
</Layout> );
</Layout> }
</div>
);
}
} }
export default withConfigContext(Dashboard); export default withConfigContext(Dashboard);

View File

@ -16,50 +16,47 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import DeviceTypesTable from '../../../components/DeviceTypes/DeviceTypesTable';
Breadcrumb,
Icon
} from "antd";
import {Link} from "react-router-dom";
import DeviceTypesTable from "../../../components/DeviceTypes/DeviceTypesTable";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class DeviceTypes extends React.Component { class DeviceTypes extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link> <Link to="/entgra">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Device Types</Breadcrumb.Item> </Link>
</Breadcrumb> </Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Device Types</Breadcrumb.Item>
<h3>Device Types</h3> </Breadcrumb>
<Paragraph>All device types for device management.</Paragraph> <div className="wrap">
</div> <h3>Device Types</h3>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}> <Paragraph>All device types for device management.</Paragraph>
<DeviceTypesTable/> </div>
</div> <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
</PageHeader> <DeviceTypesTable />
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> </div>
</PageHeader>
</div> <div
</div> style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
); ></div>
} </div>
);
}
} }
export default DeviceTypes; export default DeviceTypes;

View File

@ -1,50 +1,47 @@
import React from 'react'; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import AddDevice from '../../../components/Devices/AddDevice';
Breadcrumb,
Icon,
Button, Select
} from "antd";
import {Link} from "react-router-dom";
import AddDevice from "../../../components/Devices/AddDevice";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class DeviceEnroll extends React.Component { class DeviceEnroll extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra/devices"><Icon type="home"/> Home</Link> <Link to="/entgra/devices">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item><Link to="/entgra/devices">Devices</Link></Breadcrumb.Item> </Link>
<Breadcrumb.Item>Enroll Device</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb> <Breadcrumb.Item>
<div className="wrap"> <Link to="/entgra/devices">Devices</Link>
<h3>Devices</h3> </Breadcrumb.Item>
<Paragraph>All enrolled devices</Paragraph> <Breadcrumb.Item>Enroll Device</Breadcrumb.Item>
</div> </Breadcrumb>
<div style={{borderRadius: 5}}> <div className="wrap">
<AddDevice/> <h3>Devices</h3>
</div> <Paragraph>All enrolled devices</Paragraph>
</PageHeader> </div>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> <div style={{ borderRadius: 5 }}>
<AddDevice />
</div> </div>
</PageHeader>
</div> <div
); style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
} ></div>
</div>
);
}
} }
export default DeviceEnroll; export default DeviceEnroll;

View File

@ -16,51 +16,47 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import DeviceTable from '../../../components/Devices/DevicesTable';
Breadcrumb,
Icon,
Button, Select
} from "antd";
import {Link} from "react-router-dom";
import DeviceTable from "../../../components/Devices/DevicesTable";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class Devices extends React.Component { class Devices extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra/devices"><Icon type="home"/> Home</Link> <Link to="/entgra/devices">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Devices</Breadcrumb.Item> </Link>
</Breadcrumb> </Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Devices</Breadcrumb.Item>
<h3>Devices</h3> </Breadcrumb>
<Paragraph>All enrolled devices</Paragraph> <div className="wrap">
</div> <h3>Devices</h3>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}> <Paragraph>All enrolled devices</Paragraph>
<DeviceTable/> </div>
</div> <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
</PageHeader> <DeviceTable />
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> </div>
</PageHeader>
</div> <div
</div> style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
); ></div>
} </div>
);
}
} }
export default Devices; export default Devices;

View File

@ -16,49 +16,51 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import GeoDashboard from '../../../components/Geo/geo-dashboard/GeoDashboard';
Breadcrumb,
Icon,
Card
} from "antd";
import {Link} from "react-router-dom";
import GeoDashboard from "../../../components/Geo/geo-dashboard/GeoDashboard";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class Geo extends React.Component { class Geo extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
}
} render() {
return (
render() { <div>
return ( <PageHeader style={{ paddingTop: 0 }}>
<div> <Breadcrumb style={{ paddingBottom: 16 }}>
<PageHeader style={{paddingTop: 0}}> <Breadcrumb.Item>
<Breadcrumb style={{paddingBottom: 16}}> <Link to="/entgra">
<Breadcrumb.Item> <Icon type="home" /> Home
<Link to="/entgra"><Icon type="home"/> Home</Link> </Link>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item>Geo</Breadcrumb.Item> <Breadcrumb.Item>Geo</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<div className="wrap"> <div className="wrap">
<h3>Geo</h3> <h3>Geo</h3>
<Paragraph>Geo Location Service</Paragraph> <Paragraph>Geo Location Service</Paragraph>
</div> </div>
</PageHeader> </PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720, alignItems: "center"}}> <div
<GeoDashboard/> style={{
</div> background: '#f0f2f5',
</div> padding: 24,
); minHeight: 720,
} alignItems: 'center',
}}
>
<GeoDashboard />
</div>
</div>
);
}
} }
export default Geo; export default Geo;

View File

@ -16,47 +16,44 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import GroupsTable from '../../../components/Groups/GroupsTable';
Breadcrumb,
Icon
} from "antd";
import {Link} from "react-router-dom";
import GroupsTable from "../../../components/Groups/GroupsTable";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class Groups extends React.Component { class Groups extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link> <Link to="/entgra">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Groups</Breadcrumb.Item> </Link>
</Breadcrumb> </Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Groups</Breadcrumb.Item>
<h3>Groups</h3> </Breadcrumb>
<Paragraph>All device groups.</Paragraph> <div className="wrap">
</div> <h3>Groups</h3>
</PageHeader> <Paragraph>All device groups.</Paragraph>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> </div>
<GroupsTable/> </PageHeader>
</div> <div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
</div> <GroupsTable />
); </div>
} </div>
);
}
} }
export default Groups; export default Groups;

View File

@ -16,65 +16,66 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import {notification, Menu, Icon} from 'antd'; import { notification, Menu, Icon } from 'antd';
import axios from 'axios'; import axios from 'axios';
import {withConfigContext} from "../../../context/ConfigContext"; import { withConfigContext } from '../../../context/ConfigContext';
/* /*
This class for call the logout api by sending request This class for call the logout api by sending request
*/ */
class Logout extends React.Component { class Logout extends React.Component {
constructor(props) {
constructor(props) { super(props);
super(props); this.state = {
this.state = { inValid: false,
inValid: false, loading: false,
loading: false };
}; }
} /*
/*
This function call the logout api when the request is success This function call the logout api when the request is success
*/ */
handleSubmit = () => { handleSubmit = () => {
const thisForm = this;
const config = this.props.context;
const thisForm = this; thisForm.setState({
const config = this.props.context; inValid: false,
});
thisForm.setState({ axios
inValid: false .post(window.location.origin + config.serverConfig.logoutUri)
}); .then(res => {
// if the api call status is correct then user will logout and then it goes to login page
if (res.status === 200) {
window.location = window.location.origin + '/entgra/login';
}
})
.catch(function(error) {
if (error.hasOwnProperty('response') && error.response.status === 400) {
thisForm.setState({
inValid: true,
});
} else {
notification.error({
message: 'There was a problem',
duration: 0,
description: 'Error occurred while trying to logout.',
});
}
});
};
axios.post(window.location.origin + config.serverConfig.logoutUri render() {
).then(res => { return (
//if the api call status is correct then user will logout and then it goes to login page <Menu>
if (res.status === 200) { <Menu.Item key="1" onClick={this.handleSubmit}>
window.location = window.location.origin + "/entgra/login"; <Icon type="logout" />
} Logout
}).catch(function (error) { </Menu.Item>
</Menu>
if (error.hasOwnProperty("response") && error.response.status === 400) { );
thisForm.setState({ }
inValid: true
});
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to logout.",
});
}
});
};
render() {
return (
<Menu>
<Menu.Item key="1" onClick={this.handleSubmit}><Icon type="logout"/>Logout</Menu.Item>
</Menu>
);
}
} }
export default withConfigContext(Logout); export default withConfigContext(Logout);

View File

@ -16,50 +16,47 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import AddPolicy from '../../../components/Policies/AddPolicy';
Breadcrumb,
Icon
} from "antd";
import {Link} from "react-router-dom";
import AddPolicy from "../../../components/Policies/AddPolicy";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class AddNewPolicy extends React.Component { class AddNewPolicy extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link> <Link to="/entgra">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Policies</Breadcrumb.Item> </Link>
</Breadcrumb> </Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Policies</Breadcrumb.Item>
<h3>Policies</h3> </Breadcrumb>
<Paragraph>Create new policy on IoT Server.</Paragraph> <div className="wrap">
</div> <h3>Policies</h3>
<div style={{ borderRadius: 5}}> <Paragraph>Create new policy on IoT Server.</Paragraph>
<AddPolicy/> </div>
</div> <div style={{ borderRadius: 5 }}>
</PageHeader> <AddPolicy />
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> </div>
</PageHeader>
</div> <div
</div> style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
); ></div>
} </div>
);
}
} }
export default AddNewPolicy; export default AddNewPolicy;

View File

@ -16,50 +16,47 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import PoliciesTable from '../../../components/Policies/PoliciesTable';
Breadcrumb,
Icon
} from "antd";
import {Link} from "react-router-dom";
import PoliciesTable from "../../../components/Policies/PoliciesTable";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class Policies extends React.Component { class Policies extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link> <Link to="/entgra">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Policies</Breadcrumb.Item> </Link>
</Breadcrumb> </Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Policies</Breadcrumb.Item>
<h3>Policies</h3> </Breadcrumb>
<Paragraph>All policies for device management</Paragraph> <div className="wrap">
</div> <h3>Policies</h3>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}> <Paragraph>All policies for device management</Paragraph>
<PoliciesTable/> </div>
</div> <div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
</PageHeader> <PoliciesTable />
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> </div>
</PageHeader>
</div> <div
</div> style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
); ></div>
} </div>
);
}
} }
export default Policies; export default Policies;

View File

@ -16,137 +16,181 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { Icon, Col, Row, Card } from 'antd';
Icon,
Col,
Row, Select,
Radio, Card,
Button
} from "antd";
import {Link} from "react-router-dom"; import { Link } from 'react-router-dom';
import moment from "moment"; import moment from 'moment';
const { Option } = Select;
class ReportDurationItemList extends React.Component { class ReportDurationItemList extends React.Component {
constructor(props) {
super(props);
this.state = {
reportParams: ['ACTIVE', 'INACTIVE', 'REMOVED'],
enrollmentsVsUnenrollmentsParams: ['Enrollments', 'Unenrollments'],
enrollmentTypeParams: ['BYOD', 'COPE'],
};
}
constructor(props) { durationItemArray = [
super(props); {
this.state = { name: 'Daily Report',
reportParams:["ACTIVE","INACTIVE","REMOVED"], description: 'Enrollments of today',
enrollmentsVsUnenrollmentsParams:["Enrollments", "Unenrollments"], duration: [
enrollmentTypeParams:["BYOD", "COPE"] moment().format('YYYY-MM-DD'),
} moment()
} .add(1, 'days')
.format('YYYY-MM-DD'),
],
},
{
name: 'Weekly Report',
description: 'Enrollments of last 7 days',
duration: [
moment()
.subtract(6, 'days')
.format('YYYY-MM-DD'),
moment()
.add(1, 'days')
.format('YYYY-MM-DD'),
],
},
{
name: 'Monthly Report',
description: 'Enrollments of last month',
duration: [
moment()
.subtract(29, 'days')
.format('YYYY-MM-DD'),
moment()
.add(1, 'days')
.format('YYYY-MM-DD'),
],
},
];
durationItemArray = [ render() {
{name:"Daily Report", description:"Enrollments of today", duration:[moment().format('YYYY-MM-DD'), moment().add(1, 'days').format('YYYY-MM-DD')]}, let itemStatus = this.durationItemArray.map(data => (
{name:"Weekly Report", description:"Enrollments of last 7 days", duration:[moment().subtract(6, 'days').format('YYYY-MM-DD'), moment().add(1, 'days').format('YYYY-MM-DD')]}, <Col key={data.name} span={6}>
{name:"Monthly Report", description:"Enrollments of last month", duration:[moment().subtract(29, 'days').format('YYYY-MM-DD'), moment().add(1, 'days').format('YYYY-MM-DD')]}] <Link
to={{
// Path to respective report page
render(){ pathname: '/entgra/devicestatus',
reportData: {
let itemStatus = this.durationItemArray.map((data) => duration: data.duration,
<Col key={data.name} span={6}> reportType: data.reportType,
<Link params: this.state.reportParams,
to={{ paramsType: data.paramsType,
//Path to respective report page },
pathname: "/entgra/devicestatus", }}
reportData: { >
duration: data.duration, <Card
reportType: data.reportType, key={data.name}
params: this.state.reportParams, bordered={true}
paramsType: data.paramsType hoverable={true}
} style={{ borderRadius: 10, marginBottom: 16 }}
}}> >
<Card key={data.name} bordered={true} hoverable={true} style={{borderRadius: 10, marginBottom: 16}}> <div align="center">
<Icon
<div align='center'> type="desktop"
<Icon type="desktop" style={{ fontSize: '25px', color: '#08c' }}/> style={{ fontSize: '25px', color: '#08c' }}
<h2><b>{data.name}</b></h2> />
<p>{data.description}</p> <h2>
{/*<p>{data.duration}</p>*/} <b>{data.name}</b>
</div> </h2>
</Card> <p>{data.description}</p>
</Link> {/* <p>{data.duration}</p>*/}
</Col>
);
let itemEnrollmentsVsUnenrollments = this.durationItemArray.map((data) =>
<Col key={data.name} span={6}>
<Link
to={{
//Path to respective report page
pathname: "/entgra/enrollmentsvsunenrollments",
reportData: {
duration: data.duration,
reportType: data.reportType,
params: this.state.enrollmentsVsUnenrollmentsParams,
paramsType: data.paramsType
}
}}>
<Card key={data.name} bordered={true} hoverable={true} style={{borderRadius: 10, marginBottom: 16}}>
<div align='center'>
<Icon type="desktop" style={{ fontSize: '25px', color: '#08c' }}/>
<h2><b>{data.name}</b></h2>
<p>{data.description}</p>
</div>
</Card>
</Link>
</Col>
);
let itemEnrollmentType = this.durationItemArray.map((data) =>
<Col key={data.name} span={6}>
<Link
to={{
//Path to respective report page
pathname: "/entgra/enrollmenttype",
reportData: {
duration: data.duration,
reportType: data.reportType,
params: this.state.enrollmentTypeParams,
paramsType: data.paramsType
}
}}>
<Card key={data.name} bordered={true} hoverable={true} style={{borderRadius: 10, marginBottom: 16}}>
<div align='center'>
<Icon type="desktop" style={{ fontSize: '25px', color: '#08c' }}/>
<h2><b>{data.name}</b></h2>
<p>{data.description}</p>
</div>
</Card>
</Link>
</Col>
);
return(
<div>
<div style={{borderRadius: 5}}>
<Row gutter={16} >
{itemStatus}
</Row>
</div>
<div style={{borderRadius: 5}}>
<Row gutter={16} >
{itemEnrollmentsVsUnenrollments}
</Row>
</div>
<div style={{borderRadius: 5}}>
<Row gutter={16} >
{itemEnrollmentType}
</Row>
</div>
</div> </div>
) </Card>
} </Link>
</Col>
));
let itemEnrollmentsVsUnenrollments = this.durationItemArray.map(data => (
<Col key={data.name} span={6}>
<Link
to={{
// Path to respective report page
pathname: '/entgra/enrollmentsvsunenrollments',
reportData: {
duration: data.duration,
reportType: data.reportType,
params: this.state.enrollmentsVsUnenrollmentsParams,
paramsType: data.paramsType,
},
}}
>
<Card
key={data.name}
bordered={true}
hoverable={true}
style={{ borderRadius: 10, marginBottom: 16 }}
>
<div align="center">
<Icon
type="desktop"
style={{ fontSize: '25px', color: '#08c' }}
/>
<h2>
<b>{data.name}</b>
</h2>
<p>{data.description}</p>
</div>
</Card>
</Link>
</Col>
));
let itemEnrollmentType = this.durationItemArray.map(data => (
<Col key={data.name} span={6}>
<Link
to={{
// Path to respective report page
pathname: '/entgra/enrollmenttype',
reportData: {
duration: data.duration,
reportType: data.reportType,
params: this.state.enrollmentTypeParams,
paramsType: data.paramsType,
},
}}
>
<Card
key={data.name}
bordered={true}
hoverable={true}
style={{ borderRadius: 10, marginBottom: 16 }}
>
<div align="center">
<Icon
type="desktop"
style={{ fontSize: '25px', color: '#08c' }}
/>
<h2>
<b>{data.name}</b>
</h2>
<p>{data.description}</p>
</div>
</Card>
</Link>
</Col>
));
return (
<div>
<div style={{ borderRadius: 5 }}>
<Row gutter={16}>{itemStatus}</Row>
</div>
<div style={{ borderRadius: 5 }}>
<Row gutter={16}>{itemEnrollmentsVsUnenrollments}</Row>
</div>
<div style={{ borderRadius: 5 }}>
<Row gutter={16}>{itemEnrollmentType}</Row>
</div>
</div>
);
}
} }
export default ReportDurationItemList; export default ReportDurationItemList;

View File

@ -16,80 +16,69 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import ReportDurationItemList from './ReportDurationItemList';
Breadcrumb,
Icon
} from "antd";
import {Link} from "react-router-dom";
import ReportDurationItemList from "./ReportDurationItemList";
const {Paragraph} = Typography;
class Reports extends React.Component { class Reports extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
this.state = { this.state = {
paramsObject:{}, paramsObject: {},
}
}
//Get modified value from datepicker and set it to paramsObject
updateDurationValue = (modifiedFromDate,modifiedToDate) => {
let tempParamObj = this.state.paramsObject;
tempParamObj.from = modifiedFromDate;
tempParamObj.to = modifiedToDate;
this.setState({paramsObject:tempParamObj});
}; };
}
// Get modified value from datepicker and set it to paramsObject
updateDurationValue = (modifiedFromDate, modifiedToDate) => {
let tempParamObj = this.state.paramsObject;
tempParamObj.from = modifiedFromDate;
tempParamObj.to = modifiedToDate;
this.setState({ paramsObject: tempParamObj });
};
//Get modified value from filters and set it to paramsObject // Get modified value from filters and set it to paramsObject
updateFiltersValue = (modifiedValue,filterType) => { updateFiltersValue = (modifiedValue, filterType) => {
let tempParamObj = this.state.paramsObject; let tempParamObj = this.state.paramsObject;
if(filterType=="Device Status"){ if (filterType == 'Device Status') {
tempParamObj.status = modifiedValue; tempParamObj.status = modifiedValue;
if(modifiedValue=="ALL" && tempParamObj.status){ if (modifiedValue == 'ALL' && tempParamObj.status) {
delete tempParamObj.status; delete tempParamObj.status;
} }
}else{ } else {
tempParamObj.ownership = modifiedValue; tempParamObj.ownership = modifiedValue;
if(modifiedValue=="ALL" && tempParamObj.ownership){ if (modifiedValue == 'ALL' && tempParamObj.ownership) {
delete tempParamObj.ownership; delete tempParamObj.ownership;
} }
}
this.setState({paramsObject:tempParamObj});
};
render() {
//Arrays for filters
const statusObj = ['ALL','ACTIVE','INACTIVE','REMOVED'];
const ownershipObj = ['ALL','BYOD','COPE'];
const params = {...this.state.paramsObject};
return (
<div>
<PageHeader style={{paddingTop: 0}}>
<Breadcrumb style={{paddingBottom: 16}}>
<Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Reports</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap">
<h3>Reports</h3>
<ReportDurationItemList/>
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
</div>
</div>
);
} }
this.setState({ paramsObject: tempParamObj });
};
render() {
return (
<div>
<PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item>
<Link to="/entgra">
<Icon type="home" /> Home
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Reports</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap">
<h3>Reports</h3>
<ReportDurationItemList />
</div>
</PageHeader>
<div
style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
></div>
</div>
);
}
} }
export default Reports; export default Reports;

View File

@ -16,47 +16,44 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import RolesTable from '../../../components/Roles/RolesTable';
Breadcrumb,
Icon
} from "antd";
import {Link} from "react-router-dom";
import RolesTable from "../../../components/Roles/RolesTable";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class Roles extends React.Component { class Roles extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link> <Link to="/entgra">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Roles</Breadcrumb.Item> </Link>
</Breadcrumb> </Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Roles</Breadcrumb.Item>
<h3>Roles</h3> </Breadcrumb>
<Paragraph>All user roles</Paragraph> <div className="wrap">
</div> <h3>Roles</h3>
</PageHeader> <Paragraph>All user roles</Paragraph>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> </div>
<RolesTable/> </PageHeader>
</div> <div style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}>
</div> <RolesTable />
); </div>
} </div>
);
}
} }
export default Roles; export default Roles;

View File

@ -16,48 +16,45 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import { import { PageHeader, Typography, Breadcrumb, Icon } from 'antd';
PageHeader, import { Link } from 'react-router-dom';
Typography, import UsersTable from '../../../components/Users/UsersTable';
Breadcrumb,
Icon
} from "antd";
import {Link} from "react-router-dom";
import UsersTable from "../../../components/Users/UsersTable";
const {Paragraph} = Typography; const { Paragraph } = Typography;
class Users extends React.Component { class Users extends React.Component {
routes; routes;
constructor(props) { constructor(props) {
super(props); super(props);
this.routes = props.routes; this.routes = props.routes;
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader style={{paddingTop: 0}}> <PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{paddingBottom: 16}}> <Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra"><Icon type="home"/> Home</Link> <Link to="/entgra">
</Breadcrumb.Item> <Icon type="home" /> Home
<Breadcrumb.Item>Users</Breadcrumb.Item> </Link>
</Breadcrumb> </Breadcrumb.Item>
<div className="wrap"> <Breadcrumb.Item>Users</Breadcrumb.Item>
<h3>Users</h3> </Breadcrumb>
<Paragraph>All users for device management.</Paragraph> <div className="wrap">
</div> <h3>Users</h3>
<UsersTable/> <Paragraph>All users for device management.</Paragraph>
</PageHeader> </div>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> <UsersTable />
</PageHeader>
</div> <div
</div> style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
); ></div>
} </div>
);
}
} }
export default Users; export default Users;

View File

@ -16,161 +16,180 @@
* under the License. * under the License.
*/ */
import React from "react"; import React from 'react';
import {Typography, Row, Col, Form, Icon, Input, Button, Checkbox} from 'antd'; import {
Typography,
Row,
Col,
Form,
Icon,
Input,
Button,
Checkbox,
} from 'antd';
import './Login.css'; import './Login.css';
import axios from 'axios'; import axios from 'axios';
import {withConfigContext} from "../context/ConfigContext"; import { withConfigContext } from '../context/ConfigContext';
const {Title} = Typography; const { Title } = Typography;
const {Text} = Typography; const { Text } = Typography;
class Login extends React.Component { class Login extends React.Component {
render() { render() {
const config = this.props.context; const config = this.props.context;
return ( return (
<div> <div>
<div className="background"> <div className="background"></div>
</div> <div className="content">
<div className="content"> <Row>
<Row> <Col xs={3} sm={3} md={10}></Col>
<Col xs={3} sm={3} md={10}> <Col xs={18} sm={18} md={4}>
<Row style={{ marginBottom: 20 }}>
</Col> <Col style={{ textAlign: 'center' }}>
<Col xs={18} sm={18} md={4}> <img
<Row style={{marginBottom: 20}}> style={{
<Col style={{textAlign: "center"}}> marginTop: 36,
<img style={ height: 60,
{ }}
marginTop: 36, src={config.theme.logo}
height: 60 />
} </Col>
} </Row>
src={config.theme.logo}/> <Title level={2}>Login</Title>
</Col> <WrappedNormalLoginForm />
</Row> </Col>
<Title level={2}>Login</Title> </Row>
<WrappedNormalLoginForm/> <Row>
<Col span={4} offset={10}></Col>
</Col> </Row>
</Row> </div>
<Row> </div>
<Col span={4} offset={10}> );
}
</Col>
</Row>
</div>
</div>
);
}
} }
class NormalLoginForm extends React.Component { class NormalLoginForm extends React.Component {
constructor(props) {
constructor(props) { super(props);
super(props); this.state = {
this.state = { inValid: false,
inValid: false, loading: false,
loading: false
};
}
handleSubmit = (e) => {
const thisForm = this;
const config = this.props.context;
console.log(config);
e.preventDefault();
this.props.form.validateFields((err, values) => {
thisForm.setState({
inValid: false
});
if (!err) {
thisForm.setState({
loading: true
});
const parameters = {
username: values.username,
password: values.password,
platform: "entgra"
};
const request = Object.keys(parameters).map(key => key + '=' + parameters[key]).join('&');
axios.post(window.location.origin+ config.serverConfig.loginUri, request
).then(res => {
if (res.status === 200) {
let redirectUrl = window.location.origin + "/entgra";
const searchParams = new URLSearchParams(window.location.search);
if (searchParams.has("redirect")) {
redirectUrl = searchParams.get("redirect");
}
window.location = redirectUrl;
}
}).catch(function (error) {
if (error.response.status === 400) {
thisForm.setState({
inValid: true,
loading: false
});
}
});
}
});
}; };
}
render() { handleSubmit = e => {
const {getFieldDecorator} = this.props.form; const thisForm = this;
let errorMsg = ""; const config = this.props.context;
if (this.state.inValid) { console.log(config);
errorMsg = <Text type="danger">Invalid Login Details</Text>;
} e.preventDefault();
let loading = ""; this.props.form.validateFields((err, values) => {
if (this.state.loading) { thisForm.setState({
loading = <Text type="secondary">Loading..</Text>; inValid: false,
} });
return ( if (!err) {
<Form onSubmit={this.handleSubmit} className="login-form"> thisForm.setState({
<Form.Item> loading: true,
{getFieldDecorator('username', { });
rules: [{required: true, message: 'Please input your username!'}], const parameters = {
})( username: values.username,
<Input name="username" style={{height: 32}} password: values.password,
prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>} platform: 'entgra',
placeholder="Username"/> };
)}
</Form.Item> const request = Object.keys(parameters)
<Form.Item> .map(key => key + '=' + parameters[key])
{getFieldDecorator('password', { .join('&');
rules: [{required: true, message: 'Please input your Password!'}],
})( axios
<Input name="password" style={{height: 32}} .post(window.location.origin + config.serverConfig.loginUri, request)
prefix={<Icon type="lock" style={{color: 'rgba(0,0,0,.25)'}}/>} type="password" .then(res => {
placeholder="Password"/> if (res.status === 200) {
)} let redirectUrl = window.location.origin + '/entgra';
</Form.Item> const searchParams = new URLSearchParams(window.location.search);
{loading} if (searchParams.has('redirect')) {
{errorMsg} redirectUrl = searchParams.get('redirect');
<Form.Item> }
{getFieldDecorator('remember', { window.location = redirectUrl;
valuePropName: 'checked', }
initialValue: true, })
})( .catch(function(error) {
<Checkbox>Remember me</Checkbox> if (error.response.status === 400) {
)} thisForm.setState({
<br/> inValid: true,
<a className="login-form-forgot" href="">Forgot password</a> loading: false,
<Button loading={this.state.loading} block type="primary" htmlType="submit" className="login-form-button"> });
Log in }
</Button> });
</Form.Item> }
</Form> });
); };
render() {
const { getFieldDecorator } = this.props.form;
let errorMsg = '';
if (this.state.inValid) {
errorMsg = <Text type="danger">Invalid Login Details</Text>;
} }
let loading = '';
if (this.state.loading) {
loading = <Text type="secondary">Loading..</Text>;
}
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input
name="username"
style={{ height: 32 }}
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>,
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input
name="password"
style={{ height: 32 }}
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Password"
/>,
)}
</Form.Item>
{loading}
{errorMsg}
<Form.Item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(<Checkbox>Remember me</Checkbox>)}
<br />
<a className="login-form-forgot" href="">
Forgot password
</a>
<Button
loading={this.state.loading}
block
type="primary"
htmlType="submit"
className="login-form-button"
>
Log in
</Button>
</Form.Item>
</Form>
);
}
} }
const WrappedNormalLoginForm = withConfigContext(Form.create({name: 'normal_login'})(NormalLoginForm)); const WrappedNormalLoginForm = withConfigContext(
Form.create({ name: 'normal_login' })(NormalLoginForm),
);
export default withConfigContext(Login); export default withConfigContext(Login);

View File

@ -34,8 +34,8 @@ const isLocalhost = Boolean(
window.location.hostname === '[::1]' || window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4. // 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match( window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
) ),
); );
export function register(config) { export function register(config) {
@ -61,7 +61,7 @@ export function register(config) {
navigator.serviceWorker.ready.then(() => { navigator.serviceWorker.ready.then(() => {
console.log( console.log(
'This web app is being served cache-first by a service ' + 'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA' 'worker. To learn more, visit https://bit.ly/CRA-PWA',
); );
}); });
} else { } else {
@ -89,7 +89,7 @@ function registerValidSW(swUrl, config) {
// content until all client tabs are closed. // content until all client tabs are closed.
console.log( console.log(
'New content is available and will be used when all ' + 'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
); );
// Execute callback // Execute callback
@ -139,7 +139,7 @@ function checkValidServiceWorker(swUrl, config) {
}) })
.catch(() => { .catch(() => {
console.log( console.log(
'No internet connection found. App is running in offline mode.' 'No internet connection found. App is running in offline mode.',
); );
}); });
} }

View File

@ -16,119 +16,119 @@
* under the License. * under the License.
*/ */
var path = require('path'); var path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin"); const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const configurations = require("./public/conf/config.json"); const configurations = require('./public/conf/config.json');
const config = { const config = {
devtool: "source-map", devtool: 'source-map',
output: { output: {
publicPath: '/entgra/' publicPath: '/entgra/',
},
watch: false,
resolve: {
alias: {
AppData: path.resolve(__dirname, 'source/src/app/common/'),
AppComponents: path.resolve(__dirname, 'source/src/app/components/'),
}, },
watch: false, extensions: ['.jsx', '.js', '.ttf', '.woff', '.woff2', '.svg'],
resolve: { },
alias: { module: {
AppData: path.resolve(__dirname, 'source/src/app/common/'), rules: [
AppComponents: path.resolve(__dirname, 'source/src/app/components/') {
}, test: /\.(js|jsx)$/,
extensions: ['.jsx', '.js', '.ttf', '.woff', '.woff2', '.svg'] exclude: /node_modules/,
}, use: [
module: { {
rules: [ loader: 'babel-loader',
{ },
test: /\.(js|jsx)$/, ],
exclude: /node_modules/, },
use: [ {
{ test: /\.html$/,
loader: 'babel-loader' use: [
} {
] loader: 'html-loader',
options: { minimize: true },
},
],
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.scss$/,
use: ['style-loader', 'scss-loader'],
},
{
test: /\.less$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'less-loader',
options: {
modifyVars: {
'primary-color': configurations.theme.primaryColor,
'link-color': configurations.theme.primaryColor,
},
javascriptEnabled: true,
}, },
{ },
test: /\.html$/, ],
use: [ },
{ {
loader: "html-loader", test: /\.(woff|woff2|eot|ttf|svg)$/,
options: { minimize: true } loader: 'url-loader?limit=100000',
} },
] {
test: /\.(png|jpe?g)/i,
use: [
{
loader: 'url-loader',
options: {
name: './img/[name].[ext]',
limit: 10000,
}, },
{ },
test: /\.css$/, {
use: [MiniCssExtractPlugin.loader, "css-loader"] loader: 'img-loader',
}, },
{ ],
test: /\.scss$/, },
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader"
]
},
{
test: /\.scss$/,
use: [ 'style-loader', 'scss-loader' ]
},
{
test: /\.less$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
},
{
loader: "less-loader",
options: {
modifyVars: {
'primary-color': configurations.theme.primaryColor,
'link-color': configurations.theme.primaryColor,
},
javascriptEnabled: true,
},
}
]
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000',
},
{
test: /\.(png|jpe?g)/i,
use: [
{
loader: "url-loader",
options: {
name: "./img/[name].[ext]",
limit: 10000
}
},
{
loader: "img-loader"
}
]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
], ],
externals: { },
'Config': JSON.stringify(require('./public/conf/config.json')) plugins: [
} new HtmlWebPackPlugin({
template: './src/index.html',
filename: './index.html',
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
],
externals: {
Config: JSON.stringify(require('./public/conf/config.json')),
},
}; };
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === 'development') {
config.watch = true; config.watch = true;
} }
module.exports = config; module.exports = config;