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"]; delete values["deviceType"];
} }
if(values.hasOwnProperty("subscriptionType") && values.subscriptionType==="ALL"){
delete values["subscriptionType"];
}
if(values.hasOwnProperty("appType") && values.appType==="ALL"){ if(values.hasOwnProperty("appType") && values.appType==="ALL"){
delete values["appType"]; delete values["appType"];
} }
@ -271,17 +268,6 @@ class FiltersForm extends React.Component {
)} )}
</Form.Item> </Form.Item>
<Divider/> <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> </Form>
</Card> </Card>
); );

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

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

@ -54,7 +54,8 @@ class AddNewAppFormComponent extends React.Component {
release: null, release: null,
isError: false, isError: false,
deviceType: null, deviceType: null,
supportedOsVersions: [] supportedOsVersions: [],
errorText: ""
}; };
} }
@ -112,11 +113,12 @@ class AddNewAppFormComponent extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Sorry, we were unable to complete your request.") handleApiError(error, error.response.data.data);
this.setState({ this.setState({
loading: false, loading: false,
isError: true, isError: true,
current: 2 current: 2,
errorText: error.response.data.data
}); });
}); });
@ -149,7 +151,7 @@ class AddNewAppFormComponent extends React.Component {
}; };
render() { render() {
const {loading, current, isError, supportedOsVersions} = this.state; const {loading, current, isError, supportedOsVersions, errorText} = this.state;
const {formConfig} = this.props; const {formConfig} = this.props;
return ( return (
<div> <div>
@ -190,7 +192,7 @@ class AddNewAppFormComponent extends React.Component {
{isError && (<Result {isError && (<Result
status="500" status="500"
title="Error occurred while creating the application." title={errorText}
subTitle="Go back to edit the details and submit again." subTitle="Go back to edit the details and submit again."
extra={<Button onClick={this.onClickBackButton}>Back</Button>} 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 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 "../../../App.css";
import ImgViewer from "../../apps/release/images/ImgViewer"; import ImgViewer from "../../apps/release/images/ImgViewer";
import StarRatings from "react-star-ratings"; import StarRatings from "react-star-ratings";
@ -30,8 +30,10 @@ 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";
import ReviewContainer from "./review/ReviewContainer"; import ReviewContainer from "./review/ReviewContainer";
import InstalledDevicesTable from "./InstalledDevicesTable";
const {Title, Text, Paragraph} = Typography; const {Title, Text, Paragraph} = Typography;
const {TabPane} = Tabs;
class ReleaseView extends React.Component { class ReleaseView extends React.Component {
constructor(props) { constructor(props) {
@ -118,6 +120,12 @@ class ReleaseView extends React.Component {
metaData = JSON.parse(release.metaData); metaData = JSON.parse(release.metaData);
} catch (e) { } catch (e) {
}
if (app.hasOwnProperty("packageName")) {
metaData.push({
key: "Package Name",
value: app.packageName
});
} }
const menu = ( const menu = (
<Menu onClick={this.handleSubscribeClick}> <Menu onClick={this.handleSubscribeClick}>
@ -171,31 +179,38 @@ class ReleaseView extends React.Component {
</div> </div>
</Col> </Col>
</Row> </Row>
<Divider/> <Divider dashed={true}/>
<Row> <Tabs>
<ImgViewer images={release.screenshots}/> <TabPane tab="App" key="1">
</Row> <Row>
<Divider/> <ImgViewer images={release.screenshots}/>
<Paragraph type="secondary" ellipsis={{rows: 3, expandable: true}}> </Row>
{release.description} <Divider/>
</Paragraph> <Paragraph type="secondary" ellipsis={{rows: 3, expandable: true}}>
<Divider/> {release.description}
<Text>META DATA</Text> </Paragraph>
<Row> <Divider/>
{ <Text>META DATA</Text>
metaData.map((data, index) => { <Row>
return ( {
<Col key={index} lg={8} md={6} xs={24} style={{marginTop: 15}}> metaData.map((data, index) => {
<Text>{data.key}</Text><br/> return (
<Text type="secondary">{data.value}</Text> <Col key={index} lg={8} md={6} xs={24} style={{marginTop: 15}}>
</Col> <Text>{data.key}</Text><br/>
) <Text type="secondary">{data.value}</Text>
}) </Col>
} )
{(metaData.length === 0) && (<Text type="secondary">No meta data available.</Text>)} })
</Row> }
<Divider/> {(metaData.length === 0) && (<Text type="secondary">No meta data available.</Text>)}
<ReviewContainer uuid={release.uuid}/> </Row>
<Divider/>
<ReviewContainer uuid={release.uuid}/>
</TabPane>
<TabPane tab="Installed devices" key="2">
<InstalledDevicesTable uuid={release.uuid}/>
</TabPane>
</Tabs>
</div> </div>
</div> </div>
); );

Loading…
Cancel
Save