Add scheduled install/uninstall feature to APPM store ui

feature/appm-store/pbac
Jayasanka 5 years ago
parent 2e690892d5
commit ff875c3330

@ -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!',

@ -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 (
<div>
<Text>
@ -224,12 +226,7 @@ class DeviceInstall extends React.Component {
rowSelection={this.rowSelection}
scroll={{x: 1000}}
/>
<div style={{paddingTop: 10, textAlign: "right"}}>
<Button disabled={selectedRows.length === 0} htmlType="button" type="primary"
onClick={this.install}>
Install
</Button>
</div>
<InstallModalFooter type="Install" operation={this.install} disabled={selectedRows.length === 0}/>
</div>
);
}

@ -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 (
<div>
<Text>
Start uninstalling the application for devices by selecting the corresponding devices.
Select uninstall to automatically start uninstalling the application for the respective devices.
</Text>
<Table
style={{paddingTop: 20}}
columns={columns}
rowKey={record => 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}}
/>
<div style={{paddingTop: 10, textAlign: "right"}}>
<Button disabled={selectedRows.length === 0} htmlType="button" type="primary"
onClick={this.uninstall}>
Uninstall
</Button>
</div>
</div>
<div>
<Text>
Start uninstalling the application for devices by selecting the corresponding devices.
Select uninstall to automatically start uninstalling the application for the respective devices.
</Text>
<Table
style={{paddingTop: 20}}
columns={columns}
rowKey={record => 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}}
/>
<InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={selectedRows.length === 0}/>
</div>
);
}
}

@ -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 {
<Option key={d.value}>{d.text}</Option>
))}
</Select>
<div style={{paddingTop:10, textAlign:"right"}}>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
<InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
</div>
);
}

@ -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 => (
<Option key={d.value}>{d.text}</Option>
))}
</Select>
<div style={{paddingTop:10, textAlign:"right"}}>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
<InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
</div>
);
}

@ -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 {
<Option key={d.value}>{d.text}</Option>
))}
</Select>
<div style={{paddingTop:10, textAlign:"right"}}>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
<InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
</div>
);
}

@ -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 {
<Option key={d.value}>{d.text}</Option>
))}
</Select>
<div style={{paddingTop:10, textAlign:"right"}}>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
<InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
</div>
);
}

@ -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 {
<Option key={d.value}>{d.text}</Option>
))}
</Select>
<div style={{paddingTop: 10, textAlign: "right"}}>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
<InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
</div>
);
}

@ -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 (
<div>
<Text>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. </Text>
<p>Select users</p>
<Select
mode="multiple"
labelInValue
value={value}
placeholder="Enter the username"
notFoundContent={fetching ? <Spin size="small"/> : null}
filterOption={false}
onSearch={this.fetchUser}
onChange={this.handleChange}
style={{width: '100%'}}
>
{data.map(d => (
<Option key={d.value}>{d.text}</Option>
))}
</Select>
<div style={{paddingTop: 10, textAlign: "right"}}>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.uninstall}>Uninstall</Button>
</div>
</div>
<div>
<Text>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. </Text>
<p>Select users</p>
<Select
mode="multiple"
labelInValue
value={value}
placeholder="Enter the username"
notFoundContent={fetching ? <Spin size="small"/> : null}
filterOption={false}
onSearch={this.fetchUser}
onChange={this.handleChange}
style={{width: '100%'}}>
{data.map(d => (
<Option key={d.value}>{d.text}</Option>
))}
</Select>
<InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length === 0}/>
</div>
);
}
}

@ -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 (
<div>
<div style={{
textAlign: "right",
display: (!isScheduledInstallVisible)?'block':'none'
}}>
<Button style={{margin: 5}} disabled={disabled} htmlType="button" type="primary"
onClick={this.props.operation}>
{type}
</Button>
<Button style={{margin: 5}} disabled={disabled} htmlType="button"
onClick={this.showScheduledInstall}>
Scheduled {type}
</Button>
</div>
<div style={{
textAlign: "right",
display: (isScheduledInstallVisible)?'block':'none'
}}>
<DatePicker showTime
placeholder="Select Time"
format="YYYY-MM-DDTHH:mm"
onChange={this.onDateTimeChange}/>
<Button disabled={scheduledTime == null}
style={{margin: 5}}
htmlType="button"
type="primary"
onClick={()=>{
this.props.operation(scheduledTime);
}}>
Schedule
</Button>
<Button style={{margin: 5}} htmlType="button"
onClick={this.hideScheduledInstall}>
Cancel
</Button>
</div>
</div>
);
}
}
export default InstallModalFooter;

@ -25,7 +25,7 @@ export const handleApiError = (error, message) => {
} else {
notification["error"]({
message: "There was a problem",
duration: 10,
duration: 2,
description: message,
});
}

Loading…
Cancel
Save