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 config = this.props.context;
const release = this.props.app.applicationReleases[0]; const release = this.props.app.applicationReleases[0];
const {uuid} = release; const {uuid} = release;
@ -50,7 +50,11 @@ class ReleaseView extends React.Component {
this.setState({ this.setState({
loading: true, 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( axios.post(
url, url,
payload, payload,
@ -61,7 +65,8 @@ class ReleaseView extends React.Component {
if (res.status === 200) { if (res.status === 200) {
this.setState({ this.setState({
loading: false, loading: false,
appInstallModalVisible: false appInstallModalVisible: false,
appUnInstallModalVisible: false,
}); });
notification["success"]({ notification["success"]({
message: 'Done!', message: 'Done!',

@ -18,13 +18,14 @@
import React from "react"; import React from "react";
import axios from "axios"; 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' 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 {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const columns = [ const columns = [
@ -109,7 +110,9 @@ class DeviceInstall extends React.Component {
data: [], data: [],
pagination: {}, pagination: {},
loading: false, loading: false,
selectedRows: [] selectedRows: [],
scheduledTime: null,
isScheduledInstallVisible: false
}; };
} }
@ -163,11 +166,10 @@ class DeviceInstall extends React.Component {
data: res.data.data.devices, data: res.data.data.devices,
pagination, pagination,
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load devices."); handleApiError(error, "Error occurred while trying to load devices.");
this.setState({loading: false}); this.setState({loading: false});
}); });
}; };
@ -187,7 +189,7 @@ class DeviceInstall extends React.Component {
}); });
}; };
install = () => { install = (timestamp=null) => {
const {selectedRows} = this.state; const {selectedRows} = this.state;
const payload = []; const payload = [];
selectedRows.map(device => { selectedRows.map(device => {
@ -196,11 +198,11 @@ class DeviceInstall extends React.Component {
type: device.type type: device.type
}); });
}); });
this.props.onInstall("devices", payload, "install"); this.props.onInstall("devices", payload, "install",timestamp);
}; };
render() { render() {
const {data, pagination, loading, selectedRows} = this.state; const {data, pagination, loading, selectedRows, scheduledTime,isScheduledInstallVisible} = this.state;
return ( return (
<div> <div>
<Text> <Text>
@ -224,12 +226,7 @@ class DeviceInstall extends React.Component {
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}} scroll={{x: 1000}}
/> />
<div style={{paddingTop: 10, textAlign: "right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={selectedRows.length === 0}/>
<Button disabled={selectedRows.length === 0} htmlType="button" type="primary"
onClick={this.install}>
Install
</Button>
</div>
</div> </div>
); );
} }

@ -18,13 +18,14 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Button,Table, Typography} from "antd"; import {Button, Select, Table, Typography} 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 {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const columns = [ const columns = [
@ -151,7 +152,7 @@ class DeviceUninstall extends React.Component {
const uuid = this.props.uuid; const uuid = this.props.uuid;
axios.get( 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, "/devices?" + encodedExtraParams,
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -163,7 +164,7 @@ class DeviceUninstall extends React.Component {
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load devices."); handleApiError(error, "Error occurred while trying to load devices.");
this.setState({loading: false}); this.setState({loading: false});
}); });
}; };
@ -183,7 +184,7 @@ class DeviceUninstall extends React.Component {
}); });
}; };
uninstall = () => { uninstall = (timestamp = null) => {
const {selectedRows} = this.state; const {selectedRows} = this.state;
const payload = []; const payload = [];
selectedRows.map(device => { selectedRows.map(device => {
@ -192,7 +193,7 @@ class DeviceUninstall extends React.Component {
type: device.type type: device.type
}); });
}); });
this.props.onUninstall("devices", payload, "uninstall"); this.props.onUninstall("devices", payload, "uninstall", null);
}; };
render() { render() {
@ -218,12 +219,7 @@ class DeviceUninstall extends React.Component {
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}} scroll={{x: 1000}}
/> />
<div style={{paddingTop: 10, textAlign: "right"}}> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={selectedRows.length === 0}/>
<Button disabled={selectedRows.length === 0} htmlType="button" type="primary"
onClick={this.uninstall}>
Uninstall
</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -112,9 +113,7 @@ class GroupInstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -81,13 +82,13 @@ class GroupUninstall extends React.Component {
}); });
}; };
install = () =>{ uninstall = (timestamp=null) =>{
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val=>{
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("group", data, "uninstall"); this.props.onUninstall("group", data, "uninstall",null);
}; };
render() { render() {
@ -108,15 +109,12 @@ class GroupUninstall extends React.Component {
filterOption={false} filterOption={false}
onSearch={this.fetchUser} onSearch={this.fetchUser}
onChange={this.handleChange} onChange={this.handleChange}
style={{width: '100%'}} style={{width: '100%'}}>
>
{data.map(d => ( {data.map(d => (
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -79,13 +80,13 @@ class RoleInstall extends React.Component {
}); });
}; };
install = () =>{ install = (timestamp=null) =>{
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val=>{
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("role", data, "install"); this.props.onInstall("role", data, "install", timestamp);
}; };
render() { render() {
@ -112,9 +113,7 @@ class RoleInstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -80,17 +81,16 @@ class RoleUninstall extends React.Component {
}); });
}; };
install = () =>{ uninstall = (timestamp=null) =>{
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val=>{
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("role", data, "uninstall"); this.props.onUninstall("role", data, "uninstall",null);
}; };
render() { render() {
const {fetching, data, value} = this.state; const {fetching, data, value} = this.state;
return ( return (
@ -113,9 +113,7 @@ class RoleUninstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop:10, textAlign:"right"}}> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -81,13 +82,13 @@ class UserInstall extends React.Component {
}); });
}; };
install = () => { install = (timestamp=null) => {
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val => { value.map(val => {
data.push(val.key); data.push(val.key);
}); });
this.props.onInstall("user", data, "install"); this.props.onInstall("user", data, "install",timestamp);
}; };
render() { render() {
@ -112,9 +113,7 @@ class UserInstall extends React.Component {
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop: 10, textAlign: "right"}}> <InstallModalFooter type="Install" operation={this.install} disabled={value.length===0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.install}>Install</Button>
</div>
</div> </div>
); );
} }

@ -22,6 +22,7 @@ import debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import InstallModalFooter from "./installModalFooter/InstallModalFooter";
const {Text} = Typography; const {Text} = Typography;
const {Option} = Select; const {Option} = Select;
@ -49,9 +50,8 @@ class UserUninstall extends React.Component {
const uuid = this.props.uuid; const uuid = this.props.uuid;
axios.get( 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 + "/" +
"/USER?", "/USER?",
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
if (fetchId !== this.lastFetchId) { if (fetchId !== this.lastFetchId) {
@ -67,7 +67,7 @@ class UserUninstall extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load users."); handleApiError(error, "Error occurred while trying to load users.");
this.setState({fetching: false}); this.setState({fetching: false});
}); });
}; };
@ -80,13 +80,13 @@ class UserUninstall extends React.Component {
}); });
}; };
uninstall = () => { uninstall = (timestamp=null) => {
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val => { value.map(val => {
data.push(val.key); data.push(val.key);
}); });
this.props.onUninstall("user", data, "uninstall"); this.props.onUninstall("user", data, "uninstall",null);
}; };
render() { render() {
@ -94,7 +94,9 @@ class UserUninstall extends React.Component {
return ( return (
<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> <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> <p>Select users</p>
<Select <Select
mode="multiple" mode="multiple"
@ -105,15 +107,12 @@ class UserUninstall extends React.Component {
filterOption={false} filterOption={false}
onSearch={this.fetchUser} onSearch={this.fetchUser}
onChange={this.handleChange} onChange={this.handleChange}
style={{width: '100%'}} style={{width: '100%'}}>
>
{data.map(d => ( {data.map(d => (
<Option key={d.value}>{d.text}</Option> <Option key={d.value}>{d.text}</Option>
))} ))}
</Select> </Select>
<div style={{paddingTop: 10, textAlign: "right"}}> <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length === 0}/>
<Button disabled={value.length===0} htmlType="button" type="primary" onClick={this.uninstall}>Uninstall</Button>
</div>
</div> </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 { } else {
notification["error"]({ notification["error"]({
message: "There was a problem", message: "There was a problem",
duration: 10, duration: 2,
description: message, description: message,
}); });
} }

Loading…
Cancel
Save