From ff875c3330f477ac088a594fe4fe77aaafc75acd Mon Sep 17 00:00:00 2001 From: Jayasanka Date: Sun, 6 Oct 2019 20:29:17 +0530 Subject: [PATCH] Add scheduled install/uninstall feature to APPM store ui --- .../components/apps/release/ReleaseView.js | 11 ++- .../apps/release/install/DeviceInstall.js | 25 +++-- .../apps/release/install/DeviceUninstall.js | 86 ++++++++--------- .../apps/release/install/GroupInstall.js | 5 +- .../apps/release/install/GroupUninstall.js | 20 ++-- .../apps/release/install/RoleInstall.js | 9 +- .../apps/release/install/RoleUninstall.js | 10 +- .../apps/release/install/UserInstall.js | 9 +- .../apps/release/install/UserUninstall.js | 63 ++++++------- .../installModalFooter/InstallModalFooter.js | 94 +++++++++++++++++++ .../react-app/src/js/Utils.js | 2 +- 11 files changed, 209 insertions(+), 125 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/installModalFooter/InstallModalFooter.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 33d6afaca8..690a71f01d 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 @@ -42,7 +42,7 @@ class ReleaseView extends React.Component { } } - appOperation = (type, payload, operation) => { + appOperation = (type, payload, operation, timestamp=null) => { const config = this.props.context; const release = this.props.app.applicationReleases[0]; const {uuid} = release; @@ -50,7 +50,11 @@ class ReleaseView extends React.Component { this.setState({ loading: true, }); - const url = window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" + type + "/" + operation; + let url = window.location.origin+ config.serverConfig.invoker.uri + + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" + type + "/" + operation; + if(timestamp!= null){ + url += `?timestamp=${timestamp}`; + } axios.post( url, payload, @@ -61,7 +65,8 @@ class ReleaseView extends React.Component { if (res.status === 200) { this.setState({ loading: false, - appInstallModalVisible: false + appInstallModalVisible: false, + appUnInstallModalVisible: false, }); notification["success"]({ message: 'Done!', diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js index 14fbb0ce67..3e67c54c03 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js @@ -18,13 +18,14 @@ import React from "react"; import axios from "axios"; -import {Button, message, notification, Table, Typography} from "antd"; +import {Button, message, DatePicker, 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"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const columns = [ @@ -109,7 +110,9 @@ class DeviceInstall extends React.Component { data: [], pagination: {}, loading: false, - selectedRows: [] + selectedRows: [], + scheduledTime: null, + isScheduledInstallVisible: false }; } @@ -147,7 +150,7 @@ class DeviceInstall extends React.Component { if (deviceType !== 'ANY') { extraParams.type = deviceType; } - + // note: encode with '%26' not '&' const encodedExtraParams = Object.keys(extraParams).map(key => key + '=' + extraParams[key]).join('&'); @@ -163,11 +166,10 @@ class DeviceInstall extends React.Component { data: res.data.data.devices, pagination, }); - } }).catch((error) => { - handleApiError(error,"Error occurred while trying to load devices."); + handleApiError(error, "Error occurred while trying to load devices."); this.setState({loading: false}); }); }; @@ -187,7 +189,7 @@ class DeviceInstall extends React.Component { }); }; - install = () => { + install = (timestamp=null) => { const {selectedRows} = this.state; const payload = []; selectedRows.map(device => { @@ -196,11 +198,11 @@ class DeviceInstall extends React.Component { type: device.type }); }); - this.props.onInstall("devices", payload, "install"); + this.props.onInstall("devices", payload, "install",timestamp); }; render() { - const {data, pagination, loading, selectedRows} = this.state; + const {data, pagination, loading, selectedRows, scheduledTime,isScheduledInstallVisible} = this.state; return (
@@ -224,12 +226,7 @@ class DeviceInstall extends React.Component { rowSelection={this.rowSelection} scroll={{x: 1000}} /> -
- -
+
); } 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 index f9d30db39f..c590608fb8 100644 --- 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 @@ -18,13 +18,14 @@ import React from "react"; import axios from "axios"; -import {Button,Table, Typography} from "antd"; +import {Button, Select, 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"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const columns = [ @@ -115,8 +116,8 @@ class DeviceUninstall extends React.Component { rowSelection = { onChange: (selectedRowKeys, selectedRows) => { this.setState({ - selectedRows: selectedRows - }) + selectedRows: selectedRows + }) }, getCheckboxProps: record => ({ disabled: record.name === 'Disabled User', // Column configuration not to be checked @@ -151,7 +152,7 @@ class DeviceUninstall extends React.Component { const uuid = this.props.uuid; axios.get( - window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/"+ + window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" + "/devices?" + encodedExtraParams, ).then(res => { if (res.status === 200) { @@ -163,7 +164,7 @@ class DeviceUninstall extends React.Component { }); } }).catch((error) => { - handleApiError(error,"Error occurred while trying to load devices."); + handleApiError(error, "Error occurred while trying to load devices."); this.setState({loading: false}); }); }; @@ -172,59 +173,54 @@ class DeviceUninstall extends React.Component { const pager = {...this.state.pagination}; pager.current = pagination.current; this.setState({ - pagination: pager, - }); + pagination: pager, + }); this.fetch({ - results: pagination.pageSize, - page: pagination.current, - sortField: sorter.field, - sortOrder: sorter.order, - ...filters, - }); + results: pagination.pageSize, + page: pagination.current, + sortField: sorter.field, + sortOrder: sorter.order, + ...filters, + }); }; - uninstall = () => { + uninstall = (timestamp = null) => { const {selectedRows} = this.state; const payload = []; selectedRows.map(device => { payload.push({ - id: device.deviceIdentifier, - type: device.type - }); + id: device.deviceIdentifier, + type: device.type + }); }); - this.props.onUninstall("devices", payload, "uninstall"); + this.props.onUninstall("devices", payload, "uninstall", null); }; 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}} - /> -
- -
- +
+ + 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}} + /> + + ); } } 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 47d3b628c4..be8c598a23 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 @@ -22,6 +22,7 @@ import debounce from 'lodash.debounce'; import axios from "axios"; import {withConfigContext} from "../../../../context/ConfigContext"; import {handleApiError} from "../../../../js/Utils"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const {Option} = Select; @@ -112,9 +113,7 @@ class GroupInstall extends React.Component { ))} -
- -
+ ); } 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 index f3aeebc974..014d37105d 100644 --- 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 @@ -22,6 +22,7 @@ import debounce from 'lodash.debounce'; import axios from "axios"; import {withConfigContext} from "../../../../context/ConfigContext"; import {handleApiError} from "../../../../js/Utils"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const {Option} = Select; @@ -75,19 +76,19 @@ class GroupUninstall extends React.Component { handleChange = value => { this.setState({ - value, - data: [], - fetching: false, - }); + value, + data: [], + fetching: false, + }); }; - install = () =>{ + uninstall = (timestamp=null) =>{ const {value} = this.state; const data = []; value.map(val=>{ data.push(val.key); }); - this.props.onInstall("group", data, "uninstall"); + this.props.onUninstall("group", data, "uninstall",null); }; render() { @@ -108,15 +109,12 @@ class GroupUninstall extends React.Component { filterOption={false} onSearch={this.fetchUser} onChange={this.handleChange} - style={{width: '100%'}} - > + style={{width: '100%'}}> {data.map(d => ( ))} -
- -
+ ); } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js index b9edba286e..276ba34ab3 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js @@ -22,6 +22,7 @@ import debounce from 'lodash.debounce'; import axios from "axios"; import {withConfigContext} from "../../../../context/ConfigContext"; import {handleApiError} from "../../../../js/Utils"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const {Option} = Select; @@ -79,13 +80,13 @@ class RoleInstall extends React.Component { }); }; - install = () =>{ + install = (timestamp=null) =>{ const {value} = this.state; const data = []; value.map(val=>{ data.push(val.key); }); - this.props.onInstall("role", data, "install"); + this.props.onInstall("role", data, "install", timestamp); }; render() { @@ -112,9 +113,7 @@ class RoleInstall extends React.Component { ))} -
- -
+ ); } 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 index 53c3a6c557..83e27f920c 100644 --- 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 @@ -22,6 +22,7 @@ import debounce from 'lodash.debounce'; import axios from "axios"; import {withConfigContext} from "../../../../context/ConfigContext"; import {handleApiError} from "../../../../js/Utils"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const {Option} = Select; @@ -80,17 +81,16 @@ class RoleUninstall extends React.Component { }); }; - install = () =>{ + uninstall = (timestamp=null) =>{ const {value} = this.state; const data = []; value.map(val=>{ data.push(val.key); }); - this.props.onInstall("role", data, "uninstall"); + this.props.onUninstall("role", data, "uninstall",null); }; render() { - const {fetching, data, value} = this.state; return ( @@ -113,9 +113,7 @@ class RoleUninstall extends React.Component { ))} -
- -
+ ); } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js index 7be794f9ae..82f91bddff 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js @@ -22,6 +22,7 @@ import debounce from 'lodash.debounce'; import axios from "axios"; import {withConfigContext} from "../../../../context/ConfigContext"; import {handleApiError} from "../../../../js/Utils"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const {Option} = Select; @@ -81,13 +82,13 @@ class UserInstall extends React.Component { }); }; - install = () => { + install = (timestamp=null) => { const {value} = this.state; const data = []; value.map(val => { data.push(val.key); }); - this.props.onInstall("user", data, "install"); + this.props.onInstall("user", data, "install",timestamp); }; render() { @@ -112,9 +113,7 @@ class UserInstall extends React.Component { ))} -
- -
+ ); } 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 index 5c68455abd..73786afca2 100644 --- 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 @@ -22,6 +22,7 @@ import debounce from 'lodash.debounce'; import axios from "axios"; import {withConfigContext} from "../../../../context/ConfigContext"; import {handleApiError} from "../../../../js/Utils"; +import InstallModalFooter from "./installModalFooter/InstallModalFooter"; const {Text} = Typography; const {Option} = Select; @@ -49,9 +50,8 @@ class UserUninstall extends React.Component { const uuid = this.props.uuid; axios.get( - window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+ - "/USER?", - + window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" + + "/USER?", ).then(res => { if (res.status === 200) { if (fetchId !== this.lastFetchId) { @@ -67,54 +67,53 @@ class UserUninstall extends React.Component { } }).catch((error) => { - handleApiError(error,"Error occurred while trying to load users."); + handleApiError(error, "Error occurred while trying to load users."); this.setState({fetching: false}); }); }; handleChange = value => { this.setState({ - value, - data: [], - fetching: false, - }); + value, + data: [], + fetching: false, + }); }; - uninstall = () => { + uninstall = (timestamp=null) => { const {value} = this.state; const data = []; value.map(val => { data.push(val.key); }); - this.props.onUninstall("user", data, "uninstall"); + this.props.onUninstall("user", data, "uninstall",null); }; 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

- -
- -
-
+
+ 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

+ + +
); } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/installModalFooter/InstallModalFooter.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/installModalFooter/InstallModalFooter.js new file mode 100644 index 0000000000..72552dfc7a --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/installModalFooter/InstallModalFooter.js @@ -0,0 +1,94 @@ +/* + * 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 {Button, DatePicker} from "antd"; + +class InstallModalFooter extends React.Component{ + constructor(props) { + super(props); + this.state={ + scheduledTime: null, + isScheduledInstallVisible: false + } + } + + onDateTimeChange = (value, dateString) => { + this.setState({ + scheduledTime: dateString + }); + }; + + showScheduledInstall = ()=>{ + this.setState({ + isScheduledInstallVisible: true + }) + }; + + hideScheduledInstall = ()=>{ + this.setState({ + isScheduledInstallVisible: false + }) + }; + + render() { + const {scheduledTime,isScheduledInstallVisible} =this.state; + const {disabled, type} = this.props; + return ( +
+
+ + +
+
+ + + +
+
+ ); + } +} + +export default InstallModalFooter; \ No newline at end of file diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js index 1850d22132..9bb8c4a521 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js @@ -25,7 +25,7 @@ export const handleApiError = (error, message) => { } else { notification["error"]({ message: "There was a problem", - duration: 10, + duration: 2, description: message, }); }