Add ui improvements to APPM Publisher UI

feature/appm-store/pbac
Dharmakeerthi Lasantha 5 years ago
commit 3789a2d196

@ -63,9 +63,6 @@ class FiltersForm extends React.Component {
delete values["deviceType"];
}
if(values.hasOwnProperty("subscriptionType") && values.subscriptionType==="ALL"){
delete values["subscriptionType"];
}
if(values.hasOwnProperty("appType") && values.appType==="ALL"){
delete values["appType"];
}
@ -271,17 +268,6 @@ class FiltersForm extends React.Component {
)}
</Form.Item>
<Divider/>
<Form.Item label="Subscription Type">
{getFieldDecorator('subscriptionType', {})(
<Radio.Group style={{width: '100%'}}>
<Radio value="FREE">Free</Radio>
<Radio value="PAID">Paid</Radio>
<Radio value="ALL">All</Radio>
</Radio.Group>,
)}
</Form.Item>
<Divider/>
</Form>
</Card>
);

@ -123,7 +123,8 @@ class AppsTable extends React.Component {
filters: {},
isDrawerVisible: false,
selectedApp: null,
selectedAppIndex: -1
selectedAppIndex: -1,
loading: false
};
config = this.props.context;
}
@ -229,7 +230,7 @@ class AppsTable extends React.Component {
};
render() {
const {isDrawerVisible} = this.state;
const {isDrawerVisible, loading} = this.state;
return (
<div className="apps-table">
<Table
@ -239,6 +240,7 @@ class AppsTable extends React.Component {
pagination={this.state.pagination}
onChange={this.handleTableChange}
rowClassName="app-row"
loading={loading}
onRow={(record, rowIndex) => {
return {
onClick: event => {

@ -132,7 +132,7 @@ class LifeCycle extends React.Component {
render() {
const {currentStatus, selectedStatus} = this.state;
const {currentStatus, selectedStatus, isConfirmButtonLoading} = this.state;
const {lifecycle} = this.props;
const selectedValue = selectedStatus == null ? [] : selectedStatus;
let proceedingStates = [];
@ -180,21 +180,17 @@ class LifeCycle extends React.Component {
type="primary"
htmlType="button"
onClick={this.showReasonModal}
disabled={selectedStatus == null}
>
loading={isConfirmButtonLoading}
disabled={selectedStatus == null}>
Change
</Button>
<Divider/>
<Modal
title="Confirm changing lifecycle state"
visible={this.state.isReasonModalVisible}
onOk={this.addLifeCycle}
onCancel={this.closeReasonModal}
okText="Confirm"
>
okText="Confirm">
<Text>
You are going to change the lifecycle state from,<br/>
<Tag color="blue">{currentStatus}</Tag>to <Tag

@ -54,7 +54,8 @@ class AddNewAppFormComponent extends React.Component {
release: null,
isError: false,
deviceType: null,
supportedOsVersions: []
supportedOsVersions: [],
errorText: ""
};
}
@ -112,11 +113,12 @@ class AddNewAppFormComponent extends React.Component {
}
}).catch((error) => {
handleApiError(error, "Sorry, we were unable to complete your request.")
handleApiError(error, error.response.data.data);
this.setState({
loading: false,
isError: true,
current: 2
current: 2,
errorText: error.response.data.data
});
});
@ -149,7 +151,7 @@ class AddNewAppFormComponent extends React.Component {
};
render() {
const {loading, current, isError, supportedOsVersions} = this.state;
const {loading, current, isError, supportedOsVersions, errorText} = this.state;
const {formConfig} = this.props;
return (
<div>
@ -190,7 +192,7 @@ class AddNewAppFormComponent extends React.Component {
{isError && (<Result
status="500"
title="Error occurred while creating the application."
title={errorText}
subTitle="Go back to edit the details and submit again."
extra={<Button onClick={this.onClickBackButton}>Back</Button>}
/>)}

@ -0,0 +1,231 @@
/*
* 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 {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider, Button, Modal, Select} 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";
const {Text} = Typography;
let config = null;
const columns = [
{
title: 'Device',
dataIndex: 'device',
width: 100,
render: device => device.name
},
{
title: 'Owner',
dataIndex: 'device',
key: 'owner',
render: device => device.enrolmentInfo.owner
},
{
title: 'Action Type',
dataIndex: 'actionType',
key: 'actionType',
render: actionType => actionType.toLowerCase()
},
{
title: 'Action',
dataIndex: 'action',
key: 'action',
render: action => action.toLowerCase()
},
{
title: 'Triggered By',
dataIndex: 'actionTriggeredBy',
key: 'actionTriggeredBy'
},
{
title: 'Action Triggered At',
dataIndex: 'actionTriggeredTimestamp',
key: 'actionTriggeredTimestamp'
},
{
title: 'Action Status',
dataIndex: 'status',
key: 'actionStatus',
render: (status) => {
let color = "#f9ca24";
switch (status) {
case "COMPLETED":
color = "#badc58";
break;
case "REPEATED":
color = "#6ab04c";
break;
case "ERROR":
case "INVALID":
case "UNAUTHORIZED":
color = "#ff7979";
break;
case "IN_PROGRESS":
color = "#f9ca24";
break;
case "PENDING":
color = "#636e72";
break;
}
return <Tag color={color}>{status.toLowerCase()}</Tag>;
}
},
{
title: 'Device Status',
dataIndex: 'device',
key: 'deviceStatus',
render: (device) => {
const status = device.enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "removed":
color = "#ff7979";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
}
return <Tag color={color}>{status}</Tag>;
}
}
];
const getTimeAgo = (time) => {
const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time);
};
class InstalledDevicesTable extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
TimeAgo.addLocale(en);
this.state = {
data: [],
pagination: {},
loading: false,
selectedRows: [],
deviceGroups: [],
groupModalVisible: false,
selectedGroupId: []
};
}
componentDidMount() {
this.fetch();
}
//fetch data from api
fetch = (params = {}) => {
const config = this.props.context;
this.setState({loading: true});
// get current page
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), //calculate the offset
limit: 10,
requireDeviceInfo: true,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key]).join('&');
//send request to the invoker
axios.get(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.store +
`/admin/subscription/${this.props.uuid}?` + encodedExtraParams,
).then(res => {
if (res.status === 200) {
const pagination = {...this.state.pagination};
console.log(res.data.data.data);
this.setState({
loading: false,
data: res.data.data.data,
pagination,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load devices.",
});
}
this.setState({loading: false});
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
return (
<div>
<div style={{paddingBottom: 24}}>
<Text>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
laudantium,
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
dicta sunt explicabo.
</Text>
</div>
<Table
columns={columns}
rowKey={record => (record.device.deviceIdentifier + record.device.enrolmentInfo.owner + record.device.enrolmentInfo.ownership)}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices`
// showQuickJumper: true
}}
loading={loading}
scroll={{x: 1000}}
/>
</div>
);
}
}
export default withConfigContext(InstalledDevicesTable);

@ -17,7 +17,7 @@
*/
import React from "react";
import {Divider, Row, Col, Typography, Button, Dropdown, notification, Menu, Icon, Spin} from "antd";
import {Divider, Row, Col, Typography, Button, Dropdown, notification, Menu, Icon, Spin, Tabs} from "antd";
import "../../../App.css";
import ImgViewer from "../../apps/release/images/ImgViewer";
import StarRatings from "react-star-ratings";
@ -30,8 +30,10 @@ import CurrentUsersReview from "./review/CurrentUsersReview";
import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils";
import ReviewContainer from "./review/ReviewContainer";
import InstalledDevicesTable from "./InstalledDevicesTable";
const {Title, Text, Paragraph} = Typography;
const {TabPane} = Tabs;
class ReleaseView extends React.Component {
constructor(props) {
@ -118,6 +120,12 @@ class ReleaseView extends React.Component {
metaData = JSON.parse(release.metaData);
} catch (e) {
}
if (app.hasOwnProperty("packageName")) {
metaData.push({
key: "Package Name",
value: app.packageName
});
}
const menu = (
<Menu onClick={this.handleSubscribeClick}>
@ -171,7 +179,9 @@ class ReleaseView extends React.Component {
</div>
</Col>
</Row>
<Divider/>
<Divider dashed={true}/>
<Tabs>
<TabPane tab="App" key="1">
<Row>
<ImgViewer images={release.screenshots}/>
</Row>
@ -196,6 +206,11 @@ class ReleaseView extends React.Component {
</Row>
<Divider/>
<ReviewContainer uuid={release.uuid}/>
</TabPane>
<TabPane tab="Installed devices" key="2">
<InstalledDevicesTable uuid={release.uuid}/>
</TabPane>
</Tabs>
</div>
</div>
);

Loading…
Cancel
Save