Merge branch 'application-mgt-new' into 'application-mgt-new'

Create Detailed App view component in APPM publisher

See merge request entgra/carbon-device-mgt!121
feature/appm-store/pbac
Dharmakeerthi Lasantha 6 years ago
commit 0ef803c2c7

@ -26,7 +26,8 @@
"react-router-dom": "latest",
"react-scripts": "2.1.8",
"redux-thunk": "^2.3.0",
"storm-react-diagrams": "^5.2.1"
"storm-react-diagrams": "^5.2.1",
"react-star-ratings": "^2.3.0"
},
"devDependencies": {
"@babel/core": "^7.0.0",

@ -11,8 +11,17 @@
},
"serverUrl": "https://localhost:9443",
"defaultPlatformIcons": {
"default": "http://www.newdesignfile.com/postpic/2015/08/square-app-icon-blue_77131.png",
"android": "data:image/png;base64,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",
"ios" : "data:image/png;base64,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"
"default": {
"icon": "mobile",
"color": "#535c68"
},
"android": {
"icon": "android",
"color": "#7db343"
},
"ios": {
"icon": "apple",
"color": "#535c68"
}
}
}

@ -0,0 +1,72 @@
.d-rating .numeric-data{
box-sizing: border-box;
display: inline-block;
padding: 20px 0 20px 0;
vertical-align: top;
text-align: center;
width: 30%;
}
.d-rating .bar-containers{
box-sizing: border-box;
display: inline-block;
padding: 20px 20px 20px 30px;
vertical-align: top;
width: 70%;
}
.d-rating .bar-containers .bar-container{
color: #737373;
font-weight: 400;
height: 20px;
margin-bottom: 4px;
position: relative;
width: 100%;
}
.d-rating .bar-containers .bar-container .number{
font-size: 11px;
left: -16px;
letter-spacing: 1px;
position: absolute;
}
.d-rating .bar-containers .bar-container .bar{
transition: width .25s ease;
display: inline-block;
height: 100%;
opacity: .8;
border-radius: 5px;
}
.bar-container .rate-5{
background: #57bb8a;
}
.bar-container .rate-4{
background: #9ace6a;
}
.bar-container .rate-3{
background: #ffcf02;
}
.bar-container .rate-2{
background: #ff9f02;
}
.bar-container .rate-1{
background: #ff6f31;
}
.d-rating .numeric-data .rate{
color: #333;
font-size: 64px;
font-weight: 100;
line-height: 64px;
padding-bottom: 6px;
}
.d-rating .numeric-data .people-count{
padding-top: 6px;
}

@ -0,0 +1,122 @@
import React from "react";
import {Row, Typography, Icon} from "antd";
import StarRatings from "react-star-ratings";
import "./DetailedRating.css";
import config from "../../../../public/conf/config.json";
import axios from "axios";
const { Text } = Typography;
class DetailedRating extends React.Component{
constructor(props){
super(props);
this.state={
detailedRating: null
}
}
componentDidMount() {
this.getData(this.props.uuid);
}
componentDidUpdate(nextProps) {
if (nextProps !== this.props) {
this.getData(this.props.uuid);
}
}
getData = (uuid)=>{
const request = "method=get&content-type=application/json&payload={}&api-endpoint=/application-mgt-store/v1.0/reviews/"+uuid+"/rating";
return axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
).then(res => {
if (res.status === 200) {
let detailedRating = res.data.data;
this.setState({
detailedRating
})
}
}).catch(function (error) {
if (error.response.status === 401) {
window.location.href = 'https://localhost:9443/store/login';
}
});
};
render() {
const detailedRating = this.state.detailedRating;
console.log(detailedRating);
if(detailedRating ==null){
return null;
}
const totalCount = detailedRating.noOfUsers;
const ratingVariety = detailedRating.ratingVariety;
const ratingArray = [];
for (let [key, value] of Object.entries(ratingVariety)) {
ratingArray.push(value);
}
const maximumRating = Math.max(...ratingArray);
const ratingBarPercentages = [0,0,0,0,0];
if(maximumRating>0){
for(let i = 0; i<5; i++){
ratingBarPercentages[i] = (ratingVariety[(i+1).toString()])/maximumRating*100;
}
}
console.log(ratingBarPercentages);
return (
<Row className="d-rating">
<div className="numeric-data">
<div className="rate">{detailedRating.ratingValue.toFixed(1)}</div>
<StarRatings
rating={detailedRating.ratingValue}
starRatedColor="#777"
starDimension = "16px"
starSpacing = "2px"
numberOfStars={5}
name='rating'
/>
<br/>
<Text type="secondary" className="people-count"><Icon type="team" /> {totalCount} total</Text>
</div>
<div className="bar-containers">
<div className="bar-container">
<span className="number">5</span>
<span className="bar rate-5" style={{width: ratingBarPercentages[4]+"%"}}> </span>
</div>
<div className="bar-container">
<span className="number">4</span>
<span className="bar rate-4" style={{width: ratingBarPercentages[3]+"%"}}> </span>
</div>
<div className="bar-container">
<span className="number">3</span>
<span className="bar rate-3" style={{width: ratingBarPercentages[2]+"%"}}> </span>
</div>
<div className="bar-container">
<span className="number">2</span>
<span className="bar rate-2" style={{width: ratingBarPercentages[1]+"%"}}> </span>
</div>
<div className="bar-container">
<span className="number">1</span>
<span className="bar rate-1" style={{width: ratingBarPercentages[0]+"%"}}> </span>
</div>
</div>
</Row>
);
}
}
export default DetailedRating;

@ -0,0 +1,95 @@
import React from 'react';
import {Drawer, Row, Col, Typography, Divider, Tag, Avatar, List} from 'antd';
import "../../../App.css";
import DetailedRating from "../detailed-rating/DetailedRating";
const {Text, Title, Paragraph} = Typography;
class AppDetailsDrawer extends React.Component {
render() {
const {app, visible, onClose} = this.props;
if (app == null) {
return null;
}
console.log(app);
return (
<div>
<Drawer
placement="right"
width={640}
closable={false}
onClose={onClose}
visible={visible}
>
<div style={{textAlign: "center"}}>
<img
style={{
marginBottom: 10,
width: 100,
borderRadius: "28%",
border: "1px solid #ddd"
}}
src={app.applicationReleases[0].iconPath}
/>
<Title level={2}>{app.name}</Title>
</div>
<Divider/>
<Paragraph type="secondary" ellipsis={{rows: 3, expandable: true}}>{app.description}</Paragraph>
<Divider dashed={true}/>
<Text strong={true}>Categories</Text>
<br/>
<br/>
<span>
{app.appCategories.map(category => {
return (
<Tag color="blue" key={category} style={{paddingBottom: 5}}>
{category}
</Tag>
);
})}
</span>
<Divider dashed={true}/>
<Text strong={true}>Tags</Text>
<br/>
<br/>
<span>
{app.tags.map(category => {
return (
<Tag color="gold" key={category} style={{paddingBottom: 5}}>
{category}
</Tag>
);
})}
</span>
<Divider dashed={true}/>
<Text strong={true}>Releases</Text>
<br/>
<List
itemLayout="horizontal"
dataSource={app.applicationReleases}
renderItem={release => (
<List.Item>
<List.Item.Meta
title={<a href={"apps/releases/"+release.uuid}>{release.version}</a>}
description={
<div>
Status : <Tag>{release.currentStatus}</Tag> Release Type <Tag color="green">{release.releaseType}</Tag>
</div>
}
/>
</List.Item>
)}
/>
<Divider dashed={true}/>
<DetailedRating uuid={app.applicationReleases[0].uuid}/>
</Drawer>
</div>
);
}
}
export default AppDetailsDrawer;

@ -0,0 +1,180 @@
import React from "react";
import {Avatar, Card, Col, Row, Table, Typography, Tag, Icon, message} from "antd";
import {connect} from "react-redux";
import {getApps} from "../../../js/actions";
import axios from "axios";
import config from "../../../../public/conf/config.json";
const {Title} = Typography;
// connecting state.apps with the component
const mapStateToProps = state => {
return {apps: state.apps}
};
const columns = [
{
title: '',
dataIndex: 'name',
render: (name, row) => {
return (
<div>
<Avatar shape="square" size="large"
style={{
marginRight: 20,
borderRadius: "28%",
border: "1px solid #ddd"
}}
src={row.applicationReleases[0].iconPath}
/>
{name}
</div>);
}
},
{
title: 'Categories',
dataIndex: 'appCategories',
render: appCategories => (
<span>
{appCategories.map(category => {
return (
<Tag color="blue" key={category}>
{category}
</Tag>
);
})}
</span>
)
},
{
title: 'Platform',
dataIndex: 'deviceType',
render: platform => {
const defaultPlatformIcons = config.defaultPlatformIcons;
let icon = defaultPlatformIcons.default.icon;
let color = defaultPlatformIcons.default.color;
if (defaultPlatformIcons.hasOwnProperty(platform)) {
icon = defaultPlatformIcons[platform].icon;
color = defaultPlatformIcons[platform].color;
}
return (<span style={{fontSize: 20, color: color, textAlign: "center"}}><Icon type={icon}
theme="filled"/></span>)
}
},
{
title: 'Type',
dataIndex: 'type'
},
{
title: 'Subscription',
dataIndex: 'subType'
},
];
class ConnectedAppsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
pagination: {
total: 100
},
apps: []
};
}
componentDidMount() {
this.fetch();
}
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,
});
};
fetch = (params = {}) => {
this.setState({loading: true});
const extraParams = {
offset: 10 * (params.page - 1),
limit: 10
};
// note: encode with '%26' not '&'
const encodedExtraParams = Object.keys(extraParams).map(key => key + '=' + extraParams[key]).join('%26');
const parameters = {
method: "post",
'content-type': "application/json",
payload: JSON.stringify({}),
'api-endpoint': "/application-mgt-publisher/v1.0/applications?" + encodedExtraParams
};
const request = Object.keys(parameters).map(key => key + '=' + parameters[key]).join('&');
console.log(request);
axios.post('https://' + config.serverConfig.hostname + ':' + config.serverConfig.httpsPort + config.serverConfig.invokerUri, request
).then(res => {
if (res.status === 200) {
let apps = [];
if (res.data.data.hasOwnProperty("applications")) {
apps = res.data.data.applications;
}
const pagination = {...this.state.pagination};
// Read total count from server
// pagination.total = data.totalCount;
pagination.total = 200;
this.setState({
loading: false,
apps: apps,
pagination,
});
}
}).catch((error) => {
if (error.response.status === 401) {
message.error('You are not logged in');
window.location.href = 'https://localhost:9443/publisher/login';
} else {
message.error('Something went wrong... :(');
}
this.setState({loading: false});
});
};
render() {
console.log("rendered");
return (
<Table
rowKey={record => record.id}
dataSource={this.state.apps}
columns={columns}
pagination={this.state.pagination}
onChange={this.handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: event => {
this.props.showDrawer(record);
},
};
}}
/>
);
}
}
const AppsTable = connect(mapStateToProps, {getApps})(ConnectedAppsTable);
export default AppsTable;

@ -0,0 +1,74 @@
import React from "react";
import {Avatar, Card, Col, Row, Table, Typography, Input, Divider, Checkbox, Select, Button} from "antd";
const {Option} = Select;
const {Title, Text} = Typography;
class Filters extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Card>
<Row>
<Col span={6}>
<Title level={4}>Filter</Title>
</Col>
</Row>
<Divider/>
<Text strong={true}>Category</Text>
<br/><br/>
<Select
mode="multiple"
style={{width: '100%'}}
placeholder="All Categories"
>
<Option key={1}>IoT</Option>
<Option key={2}>EMM</Option>
</Select>
<Divider/>
<Text strong={true}>Platform</Text>
<br/><br/>
<Checkbox>Android</Checkbox><br/>
<Checkbox>iOS</Checkbox><br/>
<Checkbox>Windows</Checkbox><br/>
<Checkbox>Default</Checkbox><br/>
<Divider/>
<Text strong={true}>Tags</Text>
<br/><br/>
<Select
mode="multiple"
style={{width: '100%'}}
placeholder="All Tags"
>
<Option key={1}>test tag</Option>
</Select>
<Divider/>
<Text strong={true}>Type</Text>
<br/><br/>
<Checkbox>Enterprise</Checkbox><br/>
<Checkbox>Public</Checkbox><br/>
<Checkbox>Web APP</Checkbox><br/>
<Checkbox>Web Clip</Checkbox><br/>
<Divider/>
<Text strong={true}>Subscription</Text>
<br/><br/>
<Checkbox>Free</Checkbox><br/>
<Checkbox>Paid</Checkbox><br/>
<Divider/>
</Card>
);
}
}
export default Filters;

@ -0,0 +1,76 @@
import React from "react";
import {Avatar, Card, Col, Row, Table, Typography, Input, Divider, Checkbox, Select, Button} from "antd";
import {connect} from "react-redux";
import {getApps} from "../../../js/actions";
import AppsTable from "./AppsTable";
import Filters from "./Filters";
import AppDetailsDrawer from "./AppDetailsDrawer";
const {Option} = Select;
const {Title, Text} = Typography;
const Search = Input.Search;
// connecting state.apps with the component
const mapStateToProps = state => {
return {apps: state.apps}
};
class ConnectedListApps extends React.Component {
constructor(props) {
super(props);
this.state = {
isDrawerVisible: false,
selectedApp: null
}
}
//handler to show app drawer
showDrawer = (app) => {
console.log(app);
this.setState({
isDrawerVisible: true,
selectedApp: app
});
};
// handler to close the app drawer
closeDrawer = () => {
this.setState({
isDrawerVisible: false
})
};
render() {
const {isDrawerVisible} = this.state;
return (
<Row gutter={28}>
<Col md={6}>
<Filters/>
</Col>
<Col md={18}>
<Card>
<Row>
<Col span={6}>
<Title level={4}>Apps</Title>
</Col>
<Col span={18} style={{textAlign: "right"}}>
<Search
placeholder="input search text"
onSearch={value => console.log(value)}
style={{width: 200}}
/>
</Col>
</Row>
<Divider dashed={true}/>
<AppsTable showDrawer={this.showDrawer} />
<AppDetailsDrawer visible={isDrawerVisible} onClose={this.closeDrawer} app={this.state.selectedApp}/>
</Card>
</Col>
</Row>
);
}
}
const ListApps = connect(mapStateToProps, {getApps})(ConnectedListApps);
export default ListApps;

@ -28,7 +28,7 @@ class Dashboard extends React.Component {
<Menu
theme="light"
mode="horizontal"
defaultSelectedKeys={['2']}
defaultSelectedKeys={['1']}
style={{lineHeight: '64px'}}
>
<Menu.Item key="1"><Link to="/publisher/apps"><Icon type="appstore"/>Apps</Link></Menu.Item>

@ -0,0 +1,60 @@
import React from "react";
import "antd/dist/antd.css";
import {PageHeader, Typography,Input, Button, Row, Col} from "antd";
import AppList from "../../../components/apps/AppList";
import ReleaseModal from "../../../components/apps/ReleaseModal";
const Search = Input.Search;
const routes = [
{
path: 'index',
breadcrumbName: 'Publisher',
},
{
path: 'first',
breadcrumbName: 'Dashboard',
},
{
path: 'second',
breadcrumbName: 'Apps',
},
];
class Apps extends React.Component {
routes;
constructor(props) {
super(props);
this.routes = props.routes;
}
render() {
return (
<div>
<PageHeader
breadcrumb={{routes}}
/>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 780}}>
<Row style={{padding:10}}>
<Col span={6} offset={18}>
<Search
placeholder="search"
onSearch={value => console.log(value)}
style={{ width: 200}}
/>
<Button style={{margin:5}}>Advanced Search</Button>
</Col>
</Row>
<ReleaseModal/>
<AppList/>
</div>
</div>
);
}
}
export default Apps;

@ -1,7 +1,7 @@
import React from "react";
import "antd/dist/antd.css";
import {PageHeader, Typography,Input, Button, Row, Col} from "antd";
import AppList from "../../../components/apps/AppList";
import ListApps from "../../../components/apps/list-apps/ListApps";
import ReleaseModal from "../../../components/apps/ReleaseModal";
const Search = Input.Search;
@ -37,18 +37,8 @@ class Apps extends React.Component {
breadcrumb={{routes}}
/>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 780}}>
<Row style={{padding:10}}>
<Col span={6} offset={18}>
<Search
placeholder="search"
onSearch={value => console.log(value)}
style={{ width: 200}}
/>
<Button style={{margin:5}}>Advanced Search</Button>
</Col>
</Row>
<ReleaseModal/>
<AppList/>
<ListApps/>
</div>
</div>

@ -68,7 +68,7 @@ class AddReview extends React.Component {
});
setTimeout(()=>{
window.location.reload();
window.location.href= uuid;
},2000)
}else{
this.setState({

Loading…
Cancel
Save