From fdf660014fa5573384cf2dd6c8393feb218035a9 Mon Sep 17 00:00:00 2001 From: Shamalka Navod Date: Thu, 3 Oct 2019 13:30:13 +0000 Subject: [PATCH] UI to generate reports Get devices which are enrolled between two date according to device status and ownership --- .../react-app/package.json | 6 +- .../src/components/Devices/DevicesTable.js | 2 +- .../components/Devices/ReportDevicesTable.js | 260 ++++++++++++++++++ .../src/components/Reports/DateRangePicker.js | 60 ++++ .../src/components/Reports/Filter.js | 67 +++++ .../src/pages/Dashboard/Reports/Reports.js | 77 +++++- 6 files changed, 465 insertions(+), 7 deletions(-) create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/ReportDevicesTable.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/DateRangePicker.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Filter.js diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json b/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json index 33bc553aa9..31a539e7f6 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json @@ -6,15 +6,19 @@ "license": "Apache License 2.0", "dependencies": { "acorn": "^6.2.0", - "antd": "^3.22.0", + "antd": "^3.23.5", "axios": "^0.18.1", + "bootstrap": "^4.3.1", "javascript-time-ago": "^2.0.1", "keymirror": "^0.1.1", "lodash.debounce": "^4.0.8", + "moment": "^2.24.0", "rc-viewer": "0.0.9", + "react-bootstrap": "^1.0.0-beta.12", "react-highlight-words": "^0.16.0", "react-image-viewer-zoom": "^1.0.36", "react-infinite-scroller": "^1.2.4", + "react-moment": "^0.9.2", "react-router": "^5.0.1", "react-router-config": "^5.0.1", "react-router-dom": "^5.0.1", diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/DevicesTable.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/DevicesTable.js index dc2a247f14..41301c1d89 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/DevicesTable.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/DevicesTable.js @@ -225,7 +225,7 @@ class DeviceTable extends React.Component {
record.deviceIdentifier} + rowKey={record => (record.deviceIdentifier + record.enrolmentInfo.owner + record.enrolmentInfo.ownership)} dataSource={data} pagination={{ ...pagination, diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/ReportDevicesTable.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/ReportDevicesTable.js new file mode 100644 index 0000000000..d71f141e96 --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Devices/ReportDevicesTable.js @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from "react"; +import axios from "axios"; +import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd"; +import TimeAgo from 'javascript-time-ago' + +// Load locale-specific relative date/time formatting rules. +import en from 'javascript-time-ago/locale/en' +import {withConfigContext} from "../../context/ConfigContext"; + +const {Text} = Typography; + +let config = null; +let apiUrl; + +const columns = [ + { + title: 'Device', + dataIndex: 'name', + width: 100, + }, + { + title: 'Type', + dataIndex: 'type', + key: 'type', + render: type => { + const defaultPlatformIcons = config.defaultPlatformIcons; + let icon = defaultPlatformIcons.default.icon; + let color = defaultPlatformIcons.default.color; + let theme = defaultPlatformIcons.default.theme; + + if (defaultPlatformIcons.hasOwnProperty(type)) { + icon = defaultPlatformIcons[type].icon; + color = defaultPlatformIcons[type].color; + theme = defaultPlatformIcons[type].theme; + } + + return ( + + + + ); + } + // todo add filtering options + }, + { + title: 'Owner', + dataIndex: 'enrolmentInfo', + 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', + 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 {status}; + } + // todo add filtering options + }, + { + title: 'Last Updated', + dataIndex: 'enrolmentInfo', + key: 'dateOfLastUpdate', + render: (data) => { + const {dateOfLastUpdate} = data; + const timeAgoString = getTimeAgo(dateOfLastUpdate); + return {timeAgoString}; + } + // todo add filtering options + }, + { + title: 'Action', + key: 'action', + render: () => ( + + + + + + ), + }, +]; + +const getTimeAgo = (time) => { + const timeAgo = new TimeAgo('en-US'); + return timeAgo.format(time); +}; + +class ReportDeviceTable extends React.Component { + constructor(props) { + super(props); + config = this.props.context; + TimeAgo.addLocale(en); + this.state = { + data: [], + 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, + 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}); + }); + }; + + 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 ( +
+
(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} + scroll={{x: 1000}} + /> + + ); + } +} + +export default withConfigContext(ReportDeviceTable); \ No newline at end of file diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/DateRangePicker.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/DateRangePicker.js new file mode 100644 index 0000000000..c41c0c86e5 --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/DateRangePicker.js @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from "react"; +import { DatePicker } from 'antd'; +import moment from 'moment'; + +class DateRangePicker extends React.Component { + + constructor(props){ + super(props); + } + + //Send updated date range to Reports.js when duration change + onChange = (dates, dateStrings) => { + this.props.updateDurationValue(dateStrings[0],dateStrings[1]); + } + + render(){ + const { RangePicker } = DatePicker; + return( + + ) + } +} + +export default DateRangePicker; \ No newline at end of file diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Filter.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Filter.js new file mode 100644 index 0000000000..606f5d6a3d --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Filter.js @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from "react"; +import { Select } from 'antd'; + +class Filter extends React.Component { + + constructor(props){ + super(props); + const { Option } = Select; + this.state = { + selectedItem:null + } + } + + //Send updated filter value to Reports.js + 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) => + + {data} + ); + return( + + ) + } +} + +export default Filter; \ No newline at end of file diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js index 5449bf726b..0f271fa05a 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js @@ -21,11 +21,12 @@ import { PageHeader, Typography, Breadcrumb, - Icon, - Card + Icon } from "antd"; import {Link} from "react-router-dom"; -import DeviceTable from "../../../components/Devices/DevicesTable"; +import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable"; +import Filter from "../../../components/Reports/Filter"; +import DateRangePicker from "../../../components/Reports/DateRangePicker"; const {Paragraph} = Typography; @@ -35,10 +36,43 @@ class Reports extends React.Component { constructor(props) { super(props); this.routes = props.routes; + this.state = { + 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 filters and set it to paramsObject + updateFiltersValue = (modifiedValue,filterType) => { + let tempParamObj = this.state.paramsObject; + if(filterType=="Device Status"){ + tempParamObj.status = modifiedValue; + if(modifiedValue=="ALL" && tempParamObj.status){ + delete tempParamObj.status; + } + }else{ + tempParamObj.ownership = modifiedValue; + if(modifiedValue=="ALL" && 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 (
@@ -50,8 +84,41 @@ class Reports extends React.Component {

Reports

- Lorem ipsum dolor sit amet, est similique constituto at, quot inermis id mel, an - illud incorrupte nam. + + To generate a report, select a duration and apply filters + +
+
+ + + + + + + + + + + + +
Select DurationDevice StatusDevice Ownership
+ + + + + +
+
+
+ +