From 9886b9f2fec076c707b2ad57c91f3fcaa4dd1eb3 Mon Sep 17 00:00:00 2001 From: nipunnadeen Date: Wed, 25 Sep 2019 10:23:13 +0530 Subject: [PATCH] Create the uninstall ui of the APPM store --- .../components/apps/release/ReleaseView.js | 30 ++- .../apps/release/install/AppUninstallModal.js | 61 +++++ .../apps/release/install/DeviceUninstall.js | 232 ++++++++++++++++++ .../apps/release/install/GroupInstall.js | 2 +- .../apps/release/install/GroupUninstall.js | 125 ++++++++++ .../apps/release/install/RoleUninstall.js | 124 ++++++++++ .../apps/release/install/UserUninstall.js | 122 +++++++++ 7 files changed, 690 insertions(+), 6 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/AppUninstallModal.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js index d2b45a191c..0ef4dd8adc 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js @@ -25,6 +25,7 @@ import DetailedRating from "./DetailedRating"; import Reviews from "./review/Reviews"; import axios from "axios"; import AppInstallModal from "./install/AppInstallModal"; +import AppUninstallModal from "./install/AppUninstallModal"; import CurrentUsersReview from "./review/CurrentUsersReview"; import {withConfigContext} from "../../../context/ConfigContext"; import {handleApiError} from "../../../js/Utils"; @@ -36,7 +37,8 @@ class ReleaseView extends React.Component { super(props); this.state = { loading: false, - appInstallModalVisible: false + appInstallModalVisible: false, + appUninstallModalVisible: false } } @@ -77,21 +79,27 @@ class ReleaseView extends React.Component { "Error occurred while installing app", }); } - }).catch((error) => { handleApiError(error,"Error occurred while installing the app."); }); }; + showAppInstallModal = () => { this.setState({ appInstallModalVisible: true }); }; - closeAppInstallModal = () => { this.setState({ - appInstallModalVisible: false + appInstallModalVisible: false, + appUninstallModalVisible: false + }); + }; + + showAppUninstallModal = () => { + this.setState({ + appUninstallModalVisible: true }); }; @@ -106,6 +114,12 @@ class ReleaseView extends React.Component { deviceType = {deviceType} onClose={this.closeAppInstallModal} onInstall={this.installApp}/> +
@@ -130,6 +144,12 @@ class ReleaseView extends React.Component { htmlType="button" type="primary" icon="download">Install
+
+ + + +
@@ -156,4 +176,4 @@ class ReleaseView extends React.Component { } } -export default withConfigContext(ReleaseView); \ No newline at end of file +export default withConfigContext(ReleaseView); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/AppUninstallModal.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/AppUninstallModal.js new file mode 100644 index 0000000000..14a729466a --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/AppUninstallModal.js @@ -0,0 +1,61 @@ +/* + * 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 {Modal, Tabs} from "antd"; +import DeviceUninstall from "./DeviceUninstall"; +import UserUninstall from "./UserUninstall"; +import RoleUninstall from "./RoleUninstall"; +import GroupUninstall from "./GroupUninstall"; + +const { TabPane } = Tabs; + +class AppUninstallModal extends React.Component{ + state={ + data:[] + }; + render() { + const {deviceType} = this.props; + return ( +
+ + + + + + + + + + + + + + + + +
+ ); + } +} + +export default AppUninstallModal; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js new file mode 100644 index 0000000000..60cc898080 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js @@ -0,0 +1,232 @@ +/* + * 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 {Button,Table, Typography} 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"; +import {handleApiError} from "../../../../js/Utils"; + +const {Text} = Typography; +const columns = [ + { + title: 'Device', + dataIndex: 'name', + fixed: 'left', + width: 100, + }, + { + title: 'Modal', + dataIndex: 'deviceInfo', + key: 'modal', + render: deviceInfo => `${deviceInfo.vendor} ${deviceInfo.deviceModel}` + // todo add filtering options + }, + { + title: 'Owner', + dataIndex: 'enrolmentInfo', + key: 'owner', + render: enrolmentInfo => enrolmentInfo.owner + // todo add filtering options + }, + { + title: 'Last Updated', + dataIndex: 'enrolmentInfo', + key: 'dateOfLastUpdate', + render: (data) => { + return (getTimeAgo(data.dateOfLastUpdate)); + } + // todo add filtering options + }, + { + title: 'Status', + dataIndex: 'enrolmentInfo', + key: 'status', + render: enrolmentInfo => enrolmentInfo.status + // todo add filtering options + }, + { + title: 'Ownership', + dataIndex: 'enrolmentInfo', + key: 'ownership', + render: enrolmentInfo => enrolmentInfo.ownership + // todo add filtering options + }, + { + title: 'OS Version', + dataIndex: 'deviceInfo', + key: 'osVersion', + render: deviceInfo => deviceInfo.osVersion + // todo add filtering options + }, + { + title: 'IMEI', + dataIndex: 'properties', + key: 'imei', + render: properties => { + let imei = "not-found"; + for (let i = 0; i < properties.length; i++) { + if (properties[i].name === "IMEI") { + imei = properties[i].value; + } + } + return imei; + } + // todo add filtering options + }, +]; + +const getTimeAgo = (time) => { + const timeAgo = new TimeAgo('en-US'); + return timeAgo.format(time); +}; + +class DeviceUninstall extends React.Component { + constructor(props) { + super(props); + TimeAgo.addLocale(en); + this.state = { + data: [], + pagination: {}, + loading: false, + selectedRows: [] + }; + } + + rowSelection = { + onChange: (selectedRowKeys, selectedRows) => { + this.setState({ + selectedRows: selectedRows + }) + }, + getCheckboxProps: record => ({ + disabled: record.name === 'Disabled User', // Column configuration not to be checked + name: record.name, + }), + }; + + componentDidMount() { + this.fetch(); + } + + //fetch data from api + fetch = (params = {}) => { + const config = this.props.context; + this.setState({loading: true}); + const {deviceType} = this.props; + // get current page + const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; + + const extraParams = { + offset: 10 * (currentPage - 1), //calculate the offset + limit: 10, + status: "ACTIVE", + }; + + if (deviceType !== 'ANY') { + extraParams.type = deviceType; + } + + // note: encode with '%26' not '&' + const encodedExtraParams = Object.keys(extraParams).map(key => key + '=' + extraParams[key]).join('&'); + + const uuid = this.props.uuid; + axios.get( + window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/"+ + "/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) => { + handleApiError(error,"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, + }); + }; + + uninstall = () => { + const {selectedRows} = this.state; + const payload = []; + selectedRows.map(device => { + payload.push({ + id: device.deviceIdentifier, + type: device.type + }); + }); + this.props.onUninstall("devices", payload); + }; + + render() { + const {data, pagination, loading, selectedRows} = this.state; + return ( +
+ + Start uninstalling the application for devices by selecting the corresponding devices. + Select uninstall to automatically start uninstalling the application for the respective devices. + + record.deviceIdentifier} + dataSource={data} + pagination={{ + ...pagination, + size: "small", + showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices` + }} + loading={loading} + onChange={this.handleTableChange} + rowSelection={this.rowSelection} + scroll={{x: 1000}} + /> +
+ +
+ + ); + } +} + +export default withConfigContext(DeviceUninstall); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js index 44024687a2..1722d27952 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js @@ -47,7 +47,7 @@ class GroupInstall extends React.Component { const config = this.props.context; this.setState({data: [], fetching: true}); - axios.post( + axios.get( window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt+"/groups?name=" + value, ).then(res => { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js new file mode 100644 index 0000000000..e976cbb303 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js @@ -0,0 +1,125 @@ +/* + * 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 {Typography, Select, Spin, message, notification, Button} from "antd"; +import debounce from 'lodash.debounce'; +import axios from "axios"; +import {withConfigContext} from "../../../../context/ConfigContext"; +import {handleApiError} from "../../../../js/Utils"; + +const {Text} = Typography; +const {Option} = Select; + +class GroupUninstall extends React.Component { + + constructor(props) { + super(props); + this.lastFetchId = 0; + this.fetchUser = debounce(this.fetchUser, 800); + } + + state = { + data: [], + value: [], + fetching: false, + }; + + fetchUser = value => { + this.lastFetchId += 1; + const fetchId = this.lastFetchId; + const config = this.props.context; + this.setState({data: [], fetching: true}); + + const uuid = this.props.uuid; + + axios.get( + window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+ + "/GROUP?", + + ).then(res => { + if (res.status === 200) { + if (fetchId !== this.lastFetchId) { + // for fetch callback order + return; + } + + const data = res.data.data.deviceGroups.map(group => ({ + text: group, + value: group, + })); + + this.setState({data, fetching: false}); + } + + }).catch((error) => { + handleApiError(error,"Error occurred while trying to load groups."); + this.setState({fetching: false}); + }); + }; + + handleChange = value => { + this.setState({ + value, + data: [], + fetching: false, + }); + }; + + install = () =>{ + const {value} = this.state; + const data = []; + value.map(val=>{ + data.push(val.key); + }); + this.props.onInstall("group",data); + }; + + render() { + + const {fetching, data, value} = this.state; + + return ( +
+ Start uninstalling the application for one or more groups by entering the corresponding group name. Select uninstall to automatically start uninstalling the application for the respective device group/ groups. +
+
+ +
+ +
+
+ ); + } +} + +export default withConfigContext(GroupUninstall); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js new file mode 100644 index 0000000000..0bd14bfb4d --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js @@ -0,0 +1,124 @@ +/* + * 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 {Typography, Select, Spin, message, notification, Button} from "antd"; +import debounce from 'lodash.debounce'; +import axios from "axios"; +import {withConfigContext} from "../../../../context/ConfigContext"; +import {handleApiError} from "../../../../js/Utils"; + +const {Text} = Typography; +const {Option} = Select; + +class RoleUninstall extends React.Component { + + constructor(props) { + super(props); + this.lastFetchId = 0; + this.fetchUser = debounce(this.fetchUser, 800); + } + + state = { + data: [], + value: [], + fetching: false, + }; + + fetchUser = value => { + const config = this.props.context; + this.lastFetchId += 1; + const fetchId = this.lastFetchId; + this.setState({data: [], fetching: true}); + + const uuid = this.props.uuid; + + axios.get( + window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+ + "/ROLE?", + ).then(res => { + if (res.status === 200) { + if (fetchId !== this.lastFetchId) { + // for fetch callback order + return; + } + + const data = res.data.data.roles.map(role => ({ + text: role, + value: role, + })); + + this.setState({data, fetching: false}); + } + + }).catch((error) => { + handleApiError(error,"Error occurred while trying to load roles."); + this.setState({fetching: false}); + }); + }; + + handleChange = value => { + this.setState({ + value, + data: [], + fetching: false, + }); + }; + + install = () =>{ + const {value} = this.state; + const data = []; + value.map(val=>{ + data.push(val.key); + }); + this.props.onInstall("role",data); + }; + + render() { + + const {fetching, data, value} = this.state; + + return ( +
+ Start uninstalling the application for one or more roles by entering the corresponding role name. Select uninstall to automatically start uninstalling the application for the respective user role/roles. +
+
+ +
+ +
+
+ ); + } +} + +export default withConfigContext(RoleUninstall); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js new file mode 100644 index 0000000000..97219d8e0a --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js @@ -0,0 +1,122 @@ +/* + * 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 {Typography, Select, Spin, message, notification, Button} from "antd"; +import debounce from 'lodash.debounce'; +import axios from "axios"; +import {withConfigContext} from "../../../../context/ConfigContext"; +import {handleApiError} from "../../../../js/Utils"; + +const {Text} = Typography; +const {Option} = Select; + +class UserUninstall extends React.Component { + + constructor(props) { + super(props); + this.lastFetchId = 0; + this.fetchUser = debounce(this.fetchUser, 800); + } + + state = { + data: [], + value: [], + fetching: false, + }; + + fetchUser = (value) => { + const config = this.props.context; + this.lastFetchId += 1; + const fetchId = this.lastFetchId; + this.setState({data: [], fetching: true}); + + const uuid = this.props.uuid; + + axios.get( + window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+ + "/USER?", + + ).then(res => { + if (res.status === 200) { + if (fetchId !== this.lastFetchId) { + // for fetch callback order + return; + } + const data = res.data.data.users.map(user => ({ + text: user, + value: user, + })); + + this.setState({data, fetching: false}); + } + + }).catch((error) => { + handleApiError(error,"Error occurred while trying to load users."); + this.setState({fetching: false}); + }); + }; + + handleChange = value => { + this.setState({ + value, + data: [], + fetching: false, + }); + }; + + uninstall = () => { + const {value} = this.state; + const data = []; + value.map(val => { + data.push(val.key); + }); + this.props.onUninstall("user", data); + }; + + render() { + const {fetching, data, value} = this.state; + + return ( +
+ Start uninstalling the application for one or more users by entering the corresponding user name. Select uninstall to automatically start uninstalling the application for the respective user/users. +

Select users

+ +
+ +
+
+ ); + } +} + +export default withConfigContext(UserUninstall);