Create the uninstall ui of the APPM store

feature/appm-store/pbac
nipunnadeen 5 years ago committed by nipun
parent 5cbe25c0d7
commit 153b7f1385

@ -25,6 +25,7 @@ import DetailedRating from "./DetailedRating";
import Reviews from "./review/Reviews"; import Reviews from "./review/Reviews";
import axios from "axios"; import axios from "axios";
import AppInstallModal from "./install/AppInstallModal"; import AppInstallModal from "./install/AppInstallModal";
import AppUninstallModal from "./install/AppUninstallModal";
import CurrentUsersReview from "./review/CurrentUsersReview"; import CurrentUsersReview from "./review/CurrentUsersReview";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils"; import {handleApiError} from "../../../js/Utils";
@ -36,7 +37,8 @@ class ReleaseView extends React.Component {
super(props); super(props);
this.state = { this.state = {
loading: false, loading: false,
appInstallModalVisible: false appInstallModalVisible: false,
appUninstallModalVisible: false
} }
} }
@ -77,21 +79,27 @@ class ReleaseView extends React.Component {
"Error occurred while installing app", "Error occurred while installing app",
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while installing the app."); handleApiError(error,"Error occurred while installing the app.");
}); });
}; };
showAppInstallModal = () => { showAppInstallModal = () => {
this.setState({ this.setState({
appInstallModalVisible: true appInstallModalVisible: true
}); });
}; };
closeAppInstallModal = () => { closeAppInstallModal = () => {
this.setState({ this.setState({
appInstallModalVisible: false appInstallModalVisible: false,
appUninstallModalVisible: false
});
};
showAppUninstallModal = () => {
this.setState({
appUninstallModalVisible: true
}); });
}; };
@ -106,6 +114,12 @@ class ReleaseView extends React.Component {
deviceType = {deviceType} deviceType = {deviceType}
onClose={this.closeAppInstallModal} onClose={this.closeAppInstallModal}
onInstall={this.installApp}/> onInstall={this.installApp}/>
<AppUninstallModal
uuid={release.uuid}
visible={this.state.appUninstallModalVisible}
deviceType = {deviceType}
onClose={this.closeAppInstallModal}
onInstall={this.installApp}/>
<div className="release"> <div className="release">
<Row> <Row>
<Col xl={4} sm={6} xs={8} className="release-icon"> <Col xl={4} sm={6} xs={8} className="release-icon">
@ -130,6 +144,12 @@ class ReleaseView extends React.Component {
htmlType="button" type="primary" icon="download">Install</Button> htmlType="button" type="primary" icon="download">Install</Button>
</Button.Group> </Button.Group>
</div> </div>
<div>
<Button.Group style={{float: "right",marginRight:'3%'}}>
<Button onClick={this.showAppUninstallModal} loading={this.state.loading}
htmlType="button" type="primary" icon="delete">UnInstall</Button>
</Button.Group>
</div>
</Col> </Col>
</Row> </Row>
<Divider/> <Divider/>
@ -156,4 +176,4 @@ class ReleaseView extends React.Component {
} }
} }
export default withConfigContext(ReleaseView); export default withConfigContext(ReleaseView);

@ -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 (
<div>
<Modal
title="Uninstall App"
visible={this.props.visible}
onCancel={this.props.onClose}
footer={null}
>
<Tabs defaultActiveKey="device">
<TabPane tab="Device" key="device">
<DeviceUninstall deviceType={deviceType} onUninstall={this.props.onUninstall} uuid={this.props.uuid}/>
</TabPane>
<TabPane tab="User" key="user">
<UserUninstall onUninstall={this.props.onUninstall} uuid={this.props.uuid}/>
</TabPane>
<TabPane tab="Role" key="role">
<RoleUninstall onUninstall={this.props.onUninstall} uuid={this.props.uuid}/>
</TabPane>
<TabPane tab="Group" key="group">
<GroupUninstall onUninstall={this.props.onUninstall} uuid={this.props.uuid}/>
</TabPane>
</Tabs>
</Modal>
</div>
);
}
}
export default AppUninstallModal;

@ -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 (
<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>
);
}
}
export default withConfigContext(DeviceUninstall);

@ -47,7 +47,7 @@ class GroupInstall extends React.Component {
const config = this.props.context; const config = this.props.context;
this.setState({data: [], fetching: true}); this.setState({data: [], fetching: true});
axios.post( axios.get(
window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt+"/groups?name=" + value, window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt+"/groups?name=" + value,
).then(res => { ).then(res => {

@ -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 (
<div>
<Text>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.</Text>
<br/>
<br/>
<Select
mode="multiple"
labelInValue
value={value}
placeholder="Search groups"
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.install}>Install</Button>
</div>
</div>
);
}
}
export default withConfigContext(GroupUninstall);

@ -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 (
<div>
<Text>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.</Text>
<br/>
<br/>
<Select
mode="multiple"
labelInValue
value={value}
placeholder="Search roles"
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.install}>Install</Button>
</div>
</div>
);
}
}
export default withConfigContext(RoleUninstall);

@ -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 (
<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>
);
}
}
export default withConfigContext(UserUninstall);
Loading…
Cancel
Save