From 6c784914824e859e4cdf5c06c1ae89e79363b251 Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Mon, 21 Oct 2019 17:00:46 +0530
Subject: [PATCH 01/10] Add loading animation to filter & lifecycle components
 in APPM

---
 .../components/apps/list-apps/appsTable/AppsTable.js |  8 +++++---
 .../components/apps/release/lifeCycle/LifeCycle.js   | 12 ++++--------
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
index aae593fb2a..24e43a89c6 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
@@ -123,7 +123,8 @@ class AppsTable extends React.Component {
             filters: {},
             isDrawerVisible: false,
             selectedApp: null,
-            selectedAppIndex: -1
+            selectedAppIndex: -1,
+            loading: false
         };
         config = this.props.context;
     }
@@ -222,14 +223,14 @@ class AppsTable extends React.Component {
 
     onUpdateApp = (key, value) => {
         const apps = [...this.state.apps];
-        apps[this.state.selectedAppIndex][key]= value;
+        apps[this.state.selectedAppIndex][key] = value;
         this.setState({
             apps
         });
     };
 
     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 => {
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/lifeCycle/LifeCycle.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/lifeCycle/LifeCycle.js
index 3230939c72..34384f7f24 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/lifeCycle/LifeCycle.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/lifeCycle/LifeCycle.js
@@ -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

From 6a5e21dd9be5aab1a342fd09acb725d8f1de9590 Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Mon, 21 Oct 2019 21:50:12 +0530
Subject: [PATCH 02/10] Create UI to display installed devices

---
 .../apps/release/InstalledDevicesTable.js     | 199 ++++++++++++++++++
 .../components/apps/release/ReleaseView.js    |  67 +++---
 2 files changed, 240 insertions(+), 26 deletions(-)
 create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
new file mode 100644
index 0000000000..6284808ef8
--- /dev/null
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
@@ -0,0 +1,199 @@
+/*
+ * 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: 'name',
+        width: 100,
+    },
+    {
+        title: 'Owner',
+        dataIndex: 'enrolmentInfo',
+        key: 'owner',
+        render: enrolmentInfo => enrolmentInfo.owner
+        // todo add filtering options
+    },
+    {
+        title: 'Ownership',
+        dataIndex: 'enrolmentInfo',
+        key: 'ownership',
+        render: enrolmentInfo => enrolmentInfo.ownership
+        // todo add filtering options
+    },
+    {
+        title: 'Status',
+        dataIndex: 'enrolmentInfo',
+        key: 'status',
+        render: (enrolmentInfo) => {
+            const status = 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>;
+        }
+        // todo add filtering options
+    },
+    {
+        title: 'Last Updated',
+        dataIndex: 'enrolmentInfo',
+        key: 'dateOfLastUpdate',
+        render: (data) => {
+            const {dateOfLastUpdate} = data;
+            const timeAgoString = getTimeAgo(dateOfLastUpdate);
+            return <Tooltip title={new Date(dateOfLastUpdate).toString()}>{timeAgoString}</Tooltip>;
+        }
+        // todo add filtering options
+    }
+];
+
+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.deviceMgt +
+            "/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) => {
+            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.deviceIdentifier + record.enrolmentInfo.owner + record.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);
\ No newline at end of file
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
index 6994f16e47..a35ccb5e51 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
@@ -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,31 +179,38 @@ class ReleaseView extends React.Component {
                             </div>
                         </Col>
                     </Row>
-                    <Divider/>
-                    <Row>
-                        <ImgViewer images={release.screenshots}/>
-                    </Row>
-                    <Divider/>
-                    <Paragraph type="secondary" ellipsis={{rows: 3, expandable: true}}>
-                        {release.description}
-                    </Paragraph>
-                    <Divider/>
-                    <Text>META DATA</Text>
-                    <Row>
-                        {
-                            metaData.map((data, index) => {
-                                return (
-                                    <Col key={index} lg={8} md={6} xs={24} style={{marginTop: 15}}>
-                                        <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/>
-                    <ReviewContainer uuid={release.uuid}/>
+                    <Divider dashed={true}/>
+                    <Tabs>
+                        <TabPane tab="App" key="1">
+                            <Row>
+                                <ImgViewer images={release.screenshots}/>
+                            </Row>
+                            <Divider/>
+                            <Paragraph type="secondary" ellipsis={{rows: 3, expandable: true}}>
+                                {release.description}
+                            </Paragraph>
+                            <Divider/>
+                            <Text>META DATA</Text>
+                            <Row>
+                                {
+                                    metaData.map((data, index) => {
+                                        return (
+                                            <Col key={index} lg={8} md={6} xs={24} style={{marginTop: 15}}>
+                                                <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/>
+                            <ReviewContainer uuid={release.uuid}/>
+                        </TabPane>
+                        <TabPane tab="Installed devices" key="2">
+                            <InstalledDevicesTable/>
+                        </TabPane>
+                    </Tabs>
                 </div>
             </div>
         );

From 951325ee75ff7d93ede759640b22345bb06d9e76 Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Tue, 22 Oct 2019 16:08:09 +0530
Subject: [PATCH 03/10] Add ui improvements to APPM Publisher UI

The following changes are with this commit
- View subscription status of devices in release view
- Display server error response in add new app form
---
 .../src/components/new-app/AddNewAppForm.js   | 12 ++-
 .../apps/release/InstalledDevicesTable.js     | 94 +++++++++++++------
 .../components/apps/release/ReleaseView.js    |  2 +-
 3 files changed, 71 insertions(+), 37 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js
index 6e731679e1..340d28aa93 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js
@@ -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>}
                                     />)}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
index 6284808ef8..1b6642f3d4 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
@@ -32,29 +32,72 @@ let config = null;
 const columns = [
     {
         title: 'Device',
-        dataIndex: 'name',
+        dataIndex: 'device',
         width: 100,
+        render: device => device.name
     },
     {
         title: 'Owner',
-        dataIndex: 'enrolmentInfo',
+        dataIndex: 'device',
         key: 'owner',
-        render: enrolmentInfo => enrolmentInfo.owner
-        // todo add filtering options
+        render: device => device.enrolmentInfo.owner
     },
     {
-        title: 'Ownership',
-        dataIndex: 'enrolmentInfo',
-        key: 'ownership',
-        render: enrolmentInfo => enrolmentInfo.ownership
-        // todo add filtering options
+        title: 'Action Type',
+        dataIndex: 'actionType',
+        key: 'actionType',
+        render: actionType => actionType.toLowerCase()
     },
     {
-        title: 'Status',
-        dataIndex: 'enrolmentInfo',
-        key: 'status',
-        render: (enrolmentInfo) => {
-            const status = enrolmentInfo.status.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":
@@ -75,18 +118,6 @@ const columns = [
             }
             return <Tag color={color}>{status}</Tag>;
         }
-        // todo add filtering options
-    },
-    {
-        title: 'Last Updated',
-        dataIndex: 'enrolmentInfo',
-        key: 'dateOfLastUpdate',
-        render: (data) => {
-            const {dateOfLastUpdate} = data;
-            const timeAgoString = getTimeAgo(dateOfLastUpdate);
-            return <Tooltip title={new Date(dateOfLastUpdate).toString()}>{timeAgoString}</Tooltip>;
-        }
-        // todo add filtering options
     }
 ];
 
@@ -135,14 +166,15 @@ class InstalledDevicesTable extends React.Component {
         //send request to the invoker
         axios.get(
             window.location.origin + config.serverConfig.invoker.uri +
-            config.serverConfig.invoker.deviceMgt +
-            "/devices?" + encodedExtraParams,
+            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.devices,
+                    data: res.data.data.data,
                     pagination,
                 });
             }
@@ -169,7 +201,7 @@ class InstalledDevicesTable extends React.Component {
         const {data, pagination, loading, selectedRows} = this.state;
         return (
             <div>
-                <div style={{paddingBottom:24}}>
+                <div style={{paddingBottom: 24}}>
                     <Text>
                         Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
                         laudantium,
@@ -179,7 +211,7 @@ class InstalledDevicesTable extends React.Component {
                 </div>
                 <Table
                     columns={columns}
-                    rowKey={record => (record.deviceIdentifier + record.enrolmentInfo.owner + record.enrolmentInfo.ownership)}
+                    rowKey={record => (record.device.deviceIdentifier + record.device.enrolmentInfo.owner + record.device.enrolmentInfo.ownership)}
                     dataSource={data}
                     pagination={{
                         ...pagination,
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
index a35ccb5e51..51ab619df2 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
@@ -208,7 +208,7 @@ class ReleaseView extends React.Component {
                             <ReviewContainer uuid={release.uuid}/>
                         </TabPane>
                         <TabPane tab="Installed devices" key="2">
-                            <InstalledDevicesTable/>
+                            <InstalledDevicesTable uuid={release.uuid}/>
                         </TabPane>
                     </Tabs>
                 </div>

From e762d35a74a07a72b09ba8831bd93e5439a290cc Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Tue, 22 Oct 2019 22:28:44 +0530
Subject: [PATCH 04/10] Remove subscription type from APPM publisher filter UI

---
 .../src/components/apps/list-apps/Filters.js       | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js
index de8bbf8357..e042def20d 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js
@@ -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>
         );

From cd062f0788d37954d5d4bf869d71b0dfb1a219a3 Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Thu, 24 Oct 2019 16:56:24 +0530
Subject: [PATCH 05/10] Add UI improvements to APPM UI

The following changes are with this commit
- Fix wrong 401 redirection in Subscription details table
- Add pagination to releases list in App Details Drawer
- Show published badge on app icon in apps table and releases list
---
 .../AppDetailsDrawer/AppDetailsDrawer.js      | 55 +++++++++++++++----
 .../apps/list-apps/appsTable/AppsTable.js     | 32 +++++++++--
 .../apps/release/InstalledDevicesTable.js     | 20 +------
 .../components/apps/release/ReleaseView.js    |  5 +-
 4 files changed, 76 insertions(+), 36 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
index 335e1c46dd..fc54ab399b 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
@@ -30,7 +30,7 @@ import {
     Spin,
     message,
     Icon,
-    Card
+    Card, Badge
 } from 'antd';
 import DetailedRating from "../../detailed-rating/DetailedRating";
 import {Link} from "react-router-dom";
@@ -487,18 +487,14 @@ class AppDetailsDrawer extends React.Component {
                         )}
 
                         <Text strong={true}>Releases </Text>
-                        {/*display add new release only if app type is enterprise*/}
-
                         <div className="releases-details">
-
-                            {(app.type === "ENTERPRISE") && (
-                                <Link to={`/publisher/apps/${app.deviceType}/${app.id}/add-release`}><Button
-                                    htmlType="button"
-                                    size="small">Add
-                                    new release</Button></Link>)}
                             <List
                                 style={{paddingTop: 16}}
                                 grid={{gutter: 16, column: 2}}
+                                pagination={{
+                                    pageSize: 4, // number of releases per page
+                                    size: "small",
+                                }}
                                 dataSource={app.applicationReleases}
                                 renderItem={release => (
                                     <div className="app-release-cards">
@@ -507,7 +503,27 @@ class AppDetailsDrawer extends React.Component {
                                                 <Card className="release-card">
                                                     <Meta
                                                         avatar={
-                                                            <Avatar size="large" shape="square" src={release.iconPath}/>
+                                                            <div>
+                                                                {(release.currentStatus === "PUBLISHED") ? (
+                                                                    <Badge
+                                                                        title="Published"
+                                                                        style={{
+                                                                            backgroundColor: '#52c41a',
+                                                                            borderRadius:"50%",
+                                                                            color:"white"
+                                                                        }}
+                                                                        count={
+                                                                            <Icon
+                                                                                type="check-circle"/>
+                                                                        }>
+                                                                        <Avatar size="large" shape="square"
+                                                                                src={release.iconPath}/>
+                                                                    </Badge>
+                                                                ) : (
+                                                                    <Avatar size="large" shape="square"
+                                                                            src={release.iconPath}/>
+                                                                )}
+                                                            </div>
                                                         }
                                                         title={release.version}
                                                         description={
@@ -529,10 +545,27 @@ class AppDetailsDrawer extends React.Component {
                                     </div>
                                 )}
                             />
-
                         </div>
 
                         <Divider dashed={true}/>
+                        {/*display add new release only if app type is enterprise*/}
+                        {(app.type === "ENTERPRISE") && (
+                            <div>
+                                <div style={{paddingBottom: 16}}>
+                                    <Text>
+                                        Add new release for the application
+                                    </Text>
+                                </div>
+                                <Link to={`/publisher/apps/${app.deviceType}/${app.id}/add-release`}>
+                                    <Button
+                                        htmlType="button"
+                                        type="primary"
+                                        size="small">
+                                        Add
+                                    </Button>
+                                </Link>
+                            </div>)}
+                        <Divider dashed={true}/>
 
                         <Text strong={true}>Description </Text>
                         {!isDescriptionEditEnabled && (
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
index 24e43a89c6..d008cf5609 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Avatar, Table, Tag, Icon, message, notification, Col} from "antd";
+import {Avatar, Table, Tag, Icon, message, notification, Col, Badge} from "antd";
 import axios from "axios";
 import pSBC from 'shade-blend-color';
 import "./AppsTable.css";
@@ -47,7 +47,31 @@ const columns = [
                     </Avatar>
                 );
             } else {
-                avatar = (
+                const {applicationReleases} = row;
+                let hasPublishedRelease = false;
+                for (let i = 0; i < applicationReleases.length; i++) {
+                    if (applicationReleases[i].currentStatus === "PUBLISHED") {
+                        hasPublishedRelease = true;
+                        break;
+                    }
+                }
+                avatar = (hasPublishedRelease) ? (
+                    <Badge
+                        title="Published"
+                        style={{ backgroundColor: '#52c41a', borderRadius:"50%", color:"white"}}
+                        count={
+                            <Icon
+                                type="check-circle"/>
+                        }>
+                        <Avatar shape="square" size="large"
+                                style={{
+                                    borderRadius: "28%",
+                                    border: "1px solid #ddd"
+                                }}
+                                src={row.applicationReleases[0].iconPath}
+                        />
+                    </Badge>
+                ) : (
                     <Avatar shape="square" size="large"
                             style={{
                                 marginRight: 20,
@@ -56,13 +80,13 @@ const columns = [
                             }}
                             src={row.applicationReleases[0].iconPath}
                     />
-                )
+                );
             }
 
             return (
                 <div>
                     {avatar}
-                    {name}
+                    <span style={{marginLeft: 20}}>{name}</span>
                 </div>);
         }
     },
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
index 1b6642f3d4..39fd3ef27c 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
@@ -24,6 +24,7 @@ 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;
 
@@ -180,19 +181,7 @@ class InstalledDevicesTable extends React.Component {
             }
 
         }).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.",
-                });
-            }
-
+            handleApiError(error, "Something went wrong when trying to load subscription data.");
             this.setState({loading: false});
         });
     };
@@ -203,10 +192,7 @@ class InstalledDevicesTable extends React.Component {
             <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.
+                       The following are the subscription details of the application in each respective device.
                     </Text>
                 </div>
                 <Table
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
index 51ab619df2..83fce13416 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
@@ -21,12 +21,9 @@ import {Divider, Row, Col, Typography, Button, Dropdown, notification, Menu, Ico
 import "../../../App.css";
 import ImgViewer from "../../apps/release/images/ImgViewer";
 import StarRatings from "react-star-ratings";
-import DetailedRating from "./DetailedRating";
-import Reviews from "./review/Reviews";
 import axios from "axios";
 import AppInstallModal from "./install/AppInstallModal";
 import AppUninstallModal from "./install/AppUninstallModal";
-import CurrentUsersReview from "./review/CurrentUsersReview";
 import {withConfigContext} from "../../../context/ConfigContext";
 import {handleApiError} from "../../../js/Utils";
 import ReviewContainer from "./review/ReviewContainer";
@@ -207,7 +204,7 @@ class ReleaseView extends React.Component {
                             <Divider/>
                             <ReviewContainer uuid={release.uuid}/>
                         </TabPane>
-                        <TabPane tab="Installed devices" key="2">
+                        <TabPane tab="Subscription Details" key="2">
                             <InstalledDevicesTable uuid={release.uuid}/>
                         </TabPane>
                     </Tabs>

From 9dd7444cd7b78c0735266a174a631280b0089601 Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Sat, 26 Oct 2019 11:33:03 +0530
Subject: [PATCH 06/10] Add unrestricted roles field to Add new app form

---
 .../react-app/package.json                    |   3 +-
 .../AppDetailsDrawer/AppDetailsDrawer.js      |   6 +-
 .../apps/list-apps/appsTable/AppsTable.js     |   1 -
 .../new-app/subForms/NewAppDetailsForm.js     | 106 ++++++++++++++----
 .../apps/release/install/RoleInstall.js       |   3 +-
 5 files changed, 93 insertions(+), 26 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json
index dece5f9363..dbba25e052 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/package.json
@@ -39,7 +39,8 @@
     "redux-thunk": "^2.3.0",
     "shade-blend-color": "^1.0.0",
     "storm-react-diagrams": "^5.2.1",
-    "typescript": "^3.6.4"
+    "typescript": "^3.6.4",
+    "lodash.debounce": "latest"
   },
   "devDependencies": {
     "@babel/core": "^7.5.0",
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
index fc54ab399b..6e5e6b4816 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
@@ -509,8 +509,8 @@ class AppDetailsDrawer extends React.Component {
                                                                         title="Published"
                                                                         style={{
                                                                             backgroundColor: '#52c41a',
-                                                                            borderRadius:"50%",
-                                                                            color:"white"
+                                                                            borderRadius: "50%",
+                                                                            color: "white"
                                                                         }}
                                                                         count={
                                                                             <Icon
@@ -547,10 +547,10 @@ class AppDetailsDrawer extends React.Component {
                             />
                         </div>
 
-                        <Divider dashed={true}/>
                         {/*display add new release only if app type is enterprise*/}
                         {(app.type === "ENTERPRISE") && (
                             <div>
+                                <Divider dashed={true}/>
                                 <div style={{paddingBottom: 16}}>
                                     <Text>
                                         Add new release for the application
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
index d008cf5609..faaaf7b99d 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
@@ -74,7 +74,6 @@ const columns = [
                 ) : (
                     <Avatar shape="square" size="large"
                             style={{
-                                marginRight: 20,
                                 borderRadius: "28%",
                                 border: "1px solid #ddd"
                             }}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js
index 1667f2bb4c..881dd9f14a 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js
@@ -17,10 +17,11 @@
  */
 
 import React from "react";
-import {Button, Col, Divider, Form, Icon, Input, notification, Row, Select, Switch, Upload} from "antd";
+import {Button, Col, Divider, Form, Icon, Input, notification, Row, Select, Spin, Switch, Upload} from "antd";
 import axios from "axios";
 import {withConfigContext} from "../../../context/ConfigContext";
 import {handleApiError} from "../../../js/Utils";
+import debounce from 'lodash.debounce';
 
 const formItemLayout = {
     labelCol: {
@@ -42,12 +43,15 @@ class NewAppDetailsForm extends React.Component {
         this.state = {
             categories: [],
             tags: [],
-            deviceTypes:[]
+            deviceTypes: [],
+            fetching: false,
+            roleSearchValue: [],
+            unrestrictedRoles: []
         };
-
+        this.lastFetchId = 0;
+        this.fetchUser = debounce(this.fetchRoles, 800);
     }
 
-
     handleSubmit = e => {
         e.preventDefault();
         const {formConfig} = this.props;
@@ -58,13 +62,17 @@ class NewAppDetailsForm extends React.Component {
                 this.setState({
                     loading: true
                 });
-                const {name, description, categories, tags, price, isSharedWithAllTenants, binaryFile, icon, screenshots, releaseDescription, releaseType} = values;
+                const {name, description, categories, tags, unrestrictedRoles} = values;
+                const unrestrictedRolesData = [];
+                unrestrictedRoles.map(val=>{
+                    unrestrictedRolesData.push(val.key);
+                });
                 const application = {
                     name,
                     description,
                     categories,
                     tags,
-                    unrestrictedRoles: [],
+                    unrestrictedRoles: unrestrictedRolesData,
                 };
 
                 if (formConfig.installationType !== "WEB_CLIP") {
@@ -141,15 +149,15 @@ class NewAppDetailsForm extends React.Component {
                 const allowedDeviceTypes = [];
 
                 // exclude mobile device types if installation type is custom
-                if(installationType==="CUSTOM"){
-                    allDeviceTypes.forEach(deviceType=>{
-                        if(!mobileDeviceTypes.includes(deviceType.name)){
+                if (installationType === "CUSTOM") {
+                    allDeviceTypes.forEach(deviceType => {
+                        if (!mobileDeviceTypes.includes(deviceType.name)) {
                             allowedDeviceTypes.push(deviceType);
                         }
                     });
-                }else{
-                    allDeviceTypes.forEach(deviceType=>{
-                        if(mobileDeviceTypes.includes(deviceType.name)){
+                } else {
+                    allDeviceTypes.forEach(deviceType => {
+                        if (mobileDeviceTypes.includes(deviceType.name)) {
                             allowedDeviceTypes.push(deviceType);
                         }
                     });
@@ -168,9 +176,49 @@ class NewAppDetailsForm extends React.Component {
         });
     };
 
+    fetchRoles = value => {
+        const config = this.props.context;
+        this.lastFetchId += 1;
+        const fetchId = this.lastFetchId;
+        this.setState({data: [], fetching: true});
+
+        axios.get(
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt + "/roles?filter=" + value,
+        ).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({
+                    unrestrictedRoles: data,
+                    fetching: false
+                });
+            }
+
+        }).catch((error) => {
+            handleApiError(error, "Error occurred while trying to load roles.");
+            this.setState({fetching: false});
+        });
+    };
+
+    handleRoleSearch = roleSearchValue => {
+        this.setState({
+            roleSearchValue,
+            unrestrictedRoles: [],
+            fetching: false,
+        });
+    };
+
     render() {
         const {formConfig} = this.props;
-        const {categories, tags, deviceTypes} = this.state;
+        const {categories, tags, deviceTypes, fetching, roleSearchValue, unrestrictedRoles} = this.state;
         const {getFieldDecorator} = this.props.form;
 
         return (
@@ -198,8 +246,7 @@ class NewAppDetailsForm extends React.Component {
                                         <Select
                                             style={{width: '100%'}}
                                             placeholder="select device type"
-                                            onChange={this.handleCategoryChange}
-                                        >
+                                            onChange={this.handleCategoryChange}>
                                             {
                                                 deviceTypes.map(deviceType => {
                                                     return (
@@ -238,6 +285,29 @@ class NewAppDetailsForm extends React.Component {
                                     <TextArea placeholder="Enter the description..." rows={7}/>
                                 )}
                             </Form.Item>
+
+                            {/*Unrestricted Roles*/}
+                            <Form.Item {...formItemLayout} label="Unrestricted Roles">
+                                {getFieldDecorator('unrestrictedRoles', {
+                                    rules: [],
+                                    initialValue: []
+                                })(
+                                    <Select
+                                        mode="multiple"
+                                        labelInValue
+                                        value={roleSearchValue}
+                                        placeholder="Search roles"
+                                        notFoundContent={fetching ? <Spin size="small"/> : null}
+                                        filterOption={false}
+                                        onSearch={this.fetchRoles}
+                                        onChange={this.handleRoleSearch}
+                                        style={{width: '100%'}}>
+                                        {unrestrictedRoles.map(d => (
+                                            <Option key={d.value}>{d.text}</Option>
+                                        ))}
+                                    </Select>
+                                )}
+                            </Form.Item>
                             <Form.Item {...formItemLayout} label="Categories">
                                 {getFieldDecorator('categories', {
                                     rules: [{
@@ -249,8 +319,7 @@ class NewAppDetailsForm extends React.Component {
                                         mode="multiple"
                                         style={{width: '100%'}}
                                         placeholder="Select a Category"
-                                        onChange={this.handleCategoryChange}
-                                    >
+                                        onChange={this.handleCategoryChange}>
                                         {
                                             categories.map(category => {
                                                 return (
@@ -274,8 +343,7 @@ class NewAppDetailsForm extends React.Component {
                                     <Select
                                         mode="tags"
                                         style={{width: '100%'}}
-                                        placeholder="Tags"
-                                    >
+                                        placeholder="Tags">
                                         {
                                             tags.map(tag => {
                                                 return (
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js
index 276ba34ab3..1b29d7dcb6 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js
@@ -107,8 +107,7 @@ class RoleInstall extends React.Component {
                     filterOption={false}
                     onSearch={this.fetchUser}
                     onChange={this.handleChange}
-                    style={{width: '100%'}}
-                >
+                    style={{width: '100%'}}>
                     {data.map(d => (
                         <Option key={d.value}>{d.text}</Option>
                     ))}

From 6245e564a4be01c7fb1135cbcc98276f6b145d3a Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Sat, 26 Oct 2019 13:04:02 +0530
Subject: [PATCH 07/10] Add refresh button to subscription details table

---
 .../react-app/src/components/apps/AppCard.js  |  2 +-
 .../components/apps/release/ReleaseView.js    |  4 +--
 ...DevicesTable.js => SubscriptionDetails.js} | 26 ++++++++++++++-----
 3 files changed, 22 insertions(+), 10 deletions(-)
 rename components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/{InstalledDevicesTable.js => SubscriptionDetails.js} (88%)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js
index e60e8652a1..64d155299f 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppCard.js
@@ -50,7 +50,7 @@ class AppCard extends React.Component {
                         </Col>
                         <Col span={24} style={{paddingTop:10}}>
                             <Text className="app-name" strong level={4}>{app.name}</Text><br/>
-                            <Text type="secondary" level={4}>{app.subMethod.toLowerCase()}</Text><br/>
+                            <Text type="secondary" level={4}>{app.type.toLowerCase()}</Text><br/>
                             <StarRatings
                                 rating={app.rating}
                                 starRatedColor="#777"
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
index 83fce13416..7fd5c6eee1 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/ReleaseView.js
@@ -27,7 +27,7 @@ import AppUninstallModal from "./install/AppUninstallModal";
 import {withConfigContext} from "../../../context/ConfigContext";
 import {handleApiError} from "../../../js/Utils";
 import ReviewContainer from "./review/ReviewContainer";
-import InstalledDevicesTable from "./InstalledDevicesTable";
+import SubscriptionDetails from "./SubscriptionDetails";
 
 const {Title, Text, Paragraph} = Typography;
 const {TabPane} = Tabs;
@@ -205,7 +205,7 @@ class ReleaseView extends React.Component {
                             <ReviewContainer uuid={release.uuid}/>
                         </TabPane>
                         <TabPane tab="Subscription Details" key="2">
-                            <InstalledDevicesTable uuid={release.uuid}/>
+                            <SubscriptionDetails uuid={release.uuid}/>
                         </TabPane>
                     </Tabs>
                 </div>
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/SubscriptionDetails.js
similarity index 88%
rename from components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
rename to components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/SubscriptionDetails.js
index 39fd3ef27c..f6bace7dd0 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/InstalledDevicesTable.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/SubscriptionDetails.js
@@ -53,7 +53,16 @@ const columns = [
         title: 'Action',
         dataIndex: 'action',
         key: 'action',
-        render: action => action.toLowerCase()
+        render: action => {
+            action = action.toLowerCase();
+            let color = "fff";
+            if(action==="subscribed"){
+                color = "#6ab04c"
+            }else if(action === "unsubscribed"){
+                color = "#f0932b"
+            }
+            return <span style={{color:color}}>{action}</span>
+        }
     },
     {
         title: 'Triggered By',
@@ -128,7 +137,7 @@ const getTimeAgo = (time) => {
 };
 
 
-class InstalledDevicesTable extends React.Component {
+class SubscriptionDetails extends React.Component {
     constructor(props) {
         super(props);
         config = this.props.context;
@@ -171,12 +180,10 @@ class InstalledDevicesTable extends React.Component {
             `/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,
+                    data: res.data.data.data
                 });
             }
 
@@ -192,9 +199,14 @@ class InstalledDevicesTable extends React.Component {
             <div>
                 <div style={{paddingBottom: 24}}>
                     <Text>
-                       The following are the subscription details of the application in each respective device.
+                        The following are the subscription details of the application in each respective device.
                     </Text>
                 </div>
+                <div style={{textAlign: "right", paddingBottom: 6}}>
+                    <Button icon="sync" onClick={this.fetch}>
+                        Refresh
+                    </Button>
+                </div>
                 <Table
                     columns={columns}
                     rowKey={record => (record.device.deviceIdentifier + record.device.enrolmentInfo.owner + record.device.enrolmentInfo.ownership)}
@@ -214,4 +226,4 @@ class InstalledDevicesTable extends React.Component {
     }
 }
 
-export default withConfigContext(InstalledDevicesTable);
\ No newline at end of file
+export default withConfigContext(SubscriptionDetails);
\ No newline at end of file

From 84ad27f29937154437673bef63d5e5abd165ac24 Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Sat, 26 Oct 2019 16:15:55 +0530
Subject: [PATCH 08/10] Add custom forbidden alerts messages in APPM publisher
 UI

---
 .../apps/detailed-rating/DetailedRating.js    |   2 +-
 .../AppDetailsDrawer/AppDetailsDrawer.js      |   2 +-
 .../src/components/apps/list-apps/Filters.js  | 106 ++++++++---
 .../apps/list-apps/appsTable/AppsTable.js     |  66 ++++---
 .../components/apps/release/ReleaseView.js    |   2 +
 .../apps/release/edit-release/EditRelease.js  | 123 +++++++------
 .../components/apps/release/review/Reviews.js |  91 ++++++----
 .../manage/categories/ManageCategories.js     |  49 +++--
 .../manage/categories/ManageTags.js           |  48 +++--
 .../src/components/new-app/AddNewAppForm.js   |  25 ++-
 .../new-app/subForms/NewAppDetailsForm.js     | 169 +++++++++++++-----
 .../new-app/subForms/NewAppUploadForm.js      | 117 ++++++------
 .../components/new-release/AddReleaseForm.js  |  29 ++-
 .../react-app/src/js/Utils.js                 |   5 +-
 .../pages/dashboard/apps/release/Release.js   |  50 ++++--
 15 files changed, 573 insertions(+), 311 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/detailed-rating/DetailedRating.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/detailed-rating/DetailedRating.js
index 2ef88827be..dba024f2ed 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/detailed-rating/DetailedRating.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/detailed-rating/DetailedRating.js
@@ -61,7 +61,7 @@ class DetailedRating extends React.Component{
             }
 
         }).catch(function (error) {
-            handleApiError(error, "Error occurred while trying to load rating for the release.");
+            handleApiError(error, "Error occurred while trying to load rating for the release.", true);
         });
     };
 
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
index 6e5e6b4816..7eaea07d32 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js
@@ -136,7 +136,7 @@ class AppDetailsDrawer extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load app details.");
+            handleApiError(error, "Error occurred while trying to load categories.", true);
             this.setState({
                 loading: false
             });
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js
index e042def20d..14c76e24a7 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js
@@ -30,7 +30,7 @@ import {
     Form,
     message,
     Radio,
-    notification
+    notification, Alert
 } from "antd";
 import axios from "axios";
 import {withConfigContext} from "../../../context/ConfigContext";
@@ -46,7 +46,12 @@ class FiltersForm extends React.Component {
         this.state = {
             categories: [],
             tags: [],
-            deviceTypes: []
+            deviceTypes: [],
+            forbiddenErrors: {
+                categories: false,
+                tags: false,
+                deviceTypes: false
+            }
         };
     }
 
@@ -59,11 +64,11 @@ class FiltersForm extends React.Component {
                 }
             }
 
-            if(values.hasOwnProperty("deviceType") && values.deviceType==="ALL"){
+            if (values.hasOwnProperty("deviceType") && values.deviceType === "ALL") {
                 delete values["deviceType"];
             }
 
-            if(values.hasOwnProperty("appType") && values.appType==="ALL"){
+            if (values.hasOwnProperty("appType") && values.appType === "ALL") {
                 delete values["appType"];
             }
 
@@ -73,16 +78,17 @@ class FiltersForm extends React.Component {
 
     componentDidMount() {
         this.getCategories();
+        this.getTags();
+        this.getDeviceTypes();
     }
 
     getCategories = () => {
         const config = this.props.context;
         axios.get(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories"
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories"
         ).then(res => {
             if (res.status === 200) {
                 let categories = JSON.parse(res.data.data);
-                this.getTags();
                 this.setState({
                     categories: categories,
                     loading: false
@@ -90,21 +96,29 @@ class FiltersForm extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load categories.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load categories.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.categories = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
     getTags = () => {
         const config = this.props.context;
         axios.get(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags"
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags"
         ).then(res => {
             if (res.status === 200) {
                 let tags = JSON.parse(res.data.data);
-                this.getDeviceTypes();
                 this.setState({
                     tags: tags,
                     loading: false,
@@ -112,10 +126,19 @@ class FiltersForm extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load tags.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load tags.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.tags = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -123,7 +146,7 @@ class FiltersForm extends React.Component {
     getDeviceTypes = () => {
         const config = this.props.context;
         axios.get(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt + "/device-types"
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt + "/device-types"
         ).then(res => {
             if (res.status === 200) {
                 const deviceTypes = JSON.parse(res.data.data);
@@ -134,15 +157,24 @@ class FiltersForm extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load device types.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load device types.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.deviceTypes = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
     render() {
-        const {categories, tags, deviceTypes} = this.state;
+        const {categories, tags, deviceTypes, forbiddenErrors} = this.state;
         const {getFieldDecorator} = this.props.form;
 
         return (
@@ -170,7 +202,13 @@ class FiltersForm extends React.Component {
                             </Form.Item>
                         </Col>
                     </Row>
-
+                    {(forbiddenErrors.categories) && (
+                        <Alert
+                            message="You don't have permission to view categories."
+                            type="warning"
+                            banner
+                            closable/>
+                    )}
                     <Form.Item label="Categories">
                         {getFieldDecorator('categories', {
                             rules: [{
@@ -182,8 +220,7 @@ class FiltersForm extends React.Component {
                                 mode="multiple"
                                 style={{width: '100%'}}
                                 placeholder="Select a Category"
-                                onChange={this.handleCategoryChange}
-                            >
+                                onChange={this.handleCategoryChange}>
                                 {
                                     categories.map(category => {
                                         return (
@@ -198,7 +235,13 @@ class FiltersForm extends React.Component {
                         )}
                     </Form.Item>
 
-
+                    {(forbiddenErrors.deviceTypes) && (
+                        <Alert
+                            message="You don't have permission to view device types."
+                            type="warning"
+                            banner
+                            closable/>
+                    )}
                     <Form.Item label="Device Type">
                         {getFieldDecorator('deviceType', {
                             rules: [{
@@ -208,8 +251,7 @@ class FiltersForm extends React.Component {
                         })(
                             <Select
                                 style={{width: '100%'}}
-                                placeholder="Select device types"
-                            >
+                                placeholder="Select device types">
                                 {
                                     deviceTypes.map(deviceType => {
                                         return (
@@ -226,7 +268,13 @@ class FiltersForm extends React.Component {
                             </Select>
                         )}
                     </Form.Item>
-
+                    {(forbiddenErrors.tags) && (
+                        <Alert
+                            message="You don't have permission to view tags."
+                            type="warning"
+                            banner
+                            closable/>
+                    )}
                     <Form.Item label="Tags">
                         {getFieldDecorator('tags', {
                             rules: [{
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
index faaaf7b99d..01d6c8c159 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/appsTable/AppsTable.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Avatar, Table, Tag, Icon, message, notification, Col, Badge} from "antd";
+import {Avatar, Table, Tag, Icon, message, notification, Col, Badge, Alert} from "antd";
 import axios from "axios";
 import pSBC from 'shade-blend-color';
 import "./AppsTable.css";
@@ -58,7 +58,7 @@ const columns = [
                 avatar = (hasPublishedRelease) ? (
                     <Badge
                         title="Published"
-                        style={{ backgroundColor: '#52c41a', borderRadius:"50%", color:"white"}}
+                        style={{backgroundColor: '#52c41a', borderRadius: "50%", color: "white"}}
                         count={
                             <Icon
                                 type="check-circle"/>
@@ -147,7 +147,8 @@ class AppsTable extends React.Component {
             isDrawerVisible: false,
             selectedApp: null,
             selectedAppIndex: -1,
-            loading: false
+            loading: false,
+            isForbiddenErrorVisible: false
         };
         config = this.props.context;
     }
@@ -239,7 +240,12 @@ class AppsTable extends React.Component {
                 });
             }
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load apps.");
+            handleApiError(error, "Error occurred while trying to load apps.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbiddenErrorVisible: true
+                })
+            }
             this.setState({loading: false});
         });
     };
@@ -255,29 +261,37 @@ class AppsTable extends React.Component {
     render() {
         const {isDrawerVisible, loading} = this.state;
         return (
-            <div className="apps-table">
-                <Table
-                    rowKey={record => record.id}
-                    dataSource={this.state.apps}
-                    columns={columns}
-                    pagination={this.state.pagination}
-                    onChange={this.handleTableChange}
-                    rowClassName="app-row"
-                    loading={loading}
-                    onRow={(record, rowIndex) => {
-                        return {
-                            onClick: event => {
-                                this.showDrawer(record, rowIndex);
-                            },
-                        };
-                    }}/>
-                <AppDetailsDrawer
-                    visible={isDrawerVisible}
-                    onClose={this.closeDrawer}
-                    app={this.state.selectedApp}
-                    onUpdateApp={this.onUpdateApp}/>
+            <div>
+                {(this.state.isForbiddenErrorVisible) && (
+                    <Alert
+                        message="You don't have permission to view apps."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
+                <div className="apps-table">
+                    <Table
+                        rowKey={record => record.id}
+                        dataSource={this.state.apps}
+                        columns={columns}
+                        pagination={this.state.pagination}
+                        onChange={this.handleTableChange}
+                        rowClassName="app-row"
+                        loading={loading}
+                        onRow={(record, rowIndex) => {
+                            return {
+                                onClick: event => {
+                                    this.showDrawer(record, rowIndex);
+                                },
+                            };
+                        }}/>
+                    <AppDetailsDrawer
+                        visible={isDrawerVisible}
+                        onClose={this.closeDrawer}
+                        app={this.state.selectedApp}
+                        onUpdateApp={this.onUpdateApp}/>
+                </div>
             </div>
-
         );
     }
 }
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js
index 0c208c776b..6ea67eee20 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/ReleaseView.js
@@ -24,6 +24,7 @@ import "../../../App.css";
 import DetailedRating from "../detailed-rating/DetailedRating";
 import EditRelease from "./edit-release/EditRelease";
 import {withConfigContext} from "../../../context/ConfigContext";
+import NewAppUploadForm from "../../new-app/subForms/NewAppUploadForm";
 
 const {Title, Text, Paragraph} = Typography;
 
@@ -97,6 +98,7 @@ class ReleaseView extends React.Component {
                             <Text>Version : {release.version}</Text><br/>
 
                             <EditRelease
+                                forbiddenErrors={this.props.forbiddenErrors}
                                 isAppUpdatable={isAppUpdatable}
                                 type={app.type}
                                 deviceType={app.deviceType}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js
index 062fa56542..f4c6e84670 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js
@@ -31,7 +31,7 @@ import {
     Divider,
     Row,
     Col,
-    Select
+    Select, Alert
 } from 'antd';
 import axios from "axios";
 import "@babel/polyfill";
@@ -522,63 +522,72 @@ class EditReleaseModal extends React.Component {
                                     )}
                                 </Form.Item>
                                 {(config.deviceTypes.mobileTypes.includes(deviceType)) && (
-                                    <Form.Item {...formItemLayout} label="Supported OS Versions">
-                                        {getFieldDecorator('supportedOS')(
-                                            <div>
-                                                <InputGroup>
-                                                    <Row gutter={8}>
-                                                        <Col span={11}>
-                                                            <Form.Item>
-                                                                {getFieldDecorator('lowerOsVersion', {
-                                                                    rules: [{
-                                                                        required: true,
-                                                                        message: 'Please select Value'
-                                                                    }],
-                                                                })(
-                                                                    <Select
-                                                                        placeholder="Lower version"
-                                                                        style={{width: "100%"}}
-                                                                        onChange={this.handleLowerOsVersionChange}>
-                                                                        {supportedOsVersions.map(version => (
-                                                                            <Option key={version.versionName}
-                                                                                    value={version.versionName}>
-                                                                                {version.versionName}
-                                                                            </Option>
-                                                                        ))}
-                                                                    </Select>
-                                                                )}
-                                                            </Form.Item>
-                                                        </Col>
-                                                        <Col span={2}>
-                                                            <p> - </p>
-                                                        </Col>
-                                                        <Col span={11}>
-                                                            <Form.Item>
-                                                                {getFieldDecorator('upperOsVersion', {
-                                                                    rules: [{
-                                                                        required: true,
-                                                                        message: 'Please select Value'
-                                                                    }],
-                                                                })(
-                                                                    <Select style={{width: "100%"}}
-                                                                            placeholder="Upper version"
-                                                                            onChange={this.handleUpperOsVersionChange}>
-                                                                        {supportedOsVersions.map(version => (
-                                                                            <Option key={version.versionName}
-                                                                                    value={version.versionName}>
-                                                                                {version.versionName}
-                                                                            </Option>
-                                                                        ))}
-                                                                    </Select>
-                                                                )}
-                                                            </Form.Item>
-
-                                                        </Col>
-                                                    </Row>
-                                                </InputGroup>
-                                            </div>
+                                    <div>
+                                        {(this.props.forbiddenErrors.supportedOsVersions) && (
+                                            <Alert
+                                                message="You don't have permission to view supported OS versions."
+                                                type="warning"
+                                                banner
+                                                closable/>
                                         )}
-                                    </Form.Item>
+                                        <Form.Item {...formItemLayout} label="Supported OS Versions">
+                                            {getFieldDecorator('supportedOS')(
+                                                <div>
+                                                    <InputGroup>
+                                                        <Row gutter={8}>
+                                                            <Col span={11}>
+                                                                <Form.Item>
+                                                                    {getFieldDecorator('lowerOsVersion', {
+                                                                        rules: [{
+                                                                            required: true,
+                                                                            message: 'Please select Value'
+                                                                        }],
+                                                                    })(
+                                                                        <Select
+                                                                            placeholder="Lower version"
+                                                                            style={{width: "100%"}}
+                                                                            onChange={this.handleLowerOsVersionChange}>
+                                                                            {supportedOsVersions.map(version => (
+                                                                                <Option key={version.versionName}
+                                                                                        value={version.versionName}>
+                                                                                    {version.versionName}
+                                                                                </Option>
+                                                                            ))}
+                                                                        </Select>
+                                                                    )}
+                                                                </Form.Item>
+                                                            </Col>
+                                                            <Col span={2}>
+                                                                <p> - </p>
+                                                            </Col>
+                                                            <Col span={11}>
+                                                                <Form.Item>
+                                                                    {getFieldDecorator('upperOsVersion', {
+                                                                        rules: [{
+                                                                            required: true,
+                                                                            message: 'Please select Value'
+                                                                        }],
+                                                                    })(
+                                                                        <Select style={{width: "100%"}}
+                                                                                placeholder="Upper version"
+                                                                                onChange={this.handleUpperOsVersionChange}>
+                                                                            {supportedOsVersions.map(version => (
+                                                                                <Option key={version.versionName}
+                                                                                        value={version.versionName}>
+                                                                                    {version.versionName}
+                                                                                </Option>
+                                                                            ))}
+                                                                        </Select>
+                                                                    )}
+                                                                </Form.Item>
+
+                                                            </Col>
+                                                        </Row>
+                                                    </InputGroup>
+                                                </div>
+                                            )}
+                                        </Form.Item>
+                                    </div>
                                 )}
                                 <Form.Item {...formItemLayout} label="Meta Data">
                                     {getFieldDecorator('meta', {
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/review/Reviews.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/review/Reviews.js
index 0e75a3d663..53aa43d298 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/review/Reviews.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/review/Reviews.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {List, message, Avatar, Spin, Button, notification} from 'antd';
+import {List, message, Avatar, Spin, Button, notification, Alert} from 'antd';
 import "./Reviews.css";
 
 import InfiniteScroll from 'react-infinite-scroller';
@@ -33,7 +33,10 @@ class Reviews extends React.Component {
         data: [],
         loading: false,
         hasMore: false,
-        loadMore: false
+        loadMore: false,
+        forbiddenErrors: {
+            reviews: false
+        }
     };
 
 
@@ -49,17 +52,34 @@ class Reviews extends React.Component {
         const config = this.props.context;
 
         const {uuid, type} = this.props;
-
+        this.setState({
+            loading: true
+        });
         axios.get(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/reviews/" + type + "/" + uuid
+            window.location.origin +
+            config.serverConfig.invoker.uri +
+            config.serverConfig.invoker.publisher +
+            "/admin/reviews/" + type + "/" + uuid
         ).then(res => {
             if (res.status === 200) {
                 let reviews = res.data.data.data;
                 callback(reviews);
             }
 
-        }).catch(function (error) {
-            handleApiError(error, "Error occurred while trying to load reviews.");
+        }).catch((error) => {
+            handleApiError(error, "Error occurred while trying to load reviews.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.reviews = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -101,32 +121,39 @@ class Reviews extends React.Component {
 
     render() {
         return (
-            <div className="demo-infinite-container">
-                <InfiniteScroll
-                    initialLoad={false}
-                    pageStart={0}
-                    loadMore={this.handleInfiniteOnLoad}
-                    hasMore={!this.state.loading && this.state.hasMore}
-                    useWindow={true}
-                >
-                    <List
-                        dataSource={this.state.data}
-                        renderItem={item => (
-                            <List.Item key={item.id}>
-                                <SingleReview review={item}/>
-                            </List.Item>
-                        )}
-                    >
-                        {this.state.loading && this.state.hasMore && (
-                            <div className="demo-loading-container">
-                                <Spin/>
-                            </div>
-                        )}
-                    </List>
-                </InfiniteScroll>
-                {!this.state.loadMore && (this.state.data.length >= limit) && (<div style={{textAlign: "center"}}>
-                    <Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button>
-                </div>)}
+            <div>
+                {(this.state.forbiddenErrors.reviews) && (
+                    <Alert
+                        message="You don't have permission to view reviews."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
+                <div className="demo-infinite-container">
+                    <InfiniteScroll
+                        initialLoad={false}
+                        pageStart={0}
+                        loadMore={this.handleInfiniteOnLoad}
+                        hasMore={!this.state.loading && this.state.hasMore}
+                        useWindow={true}>
+                        <List
+                            dataSource={this.state.data}
+                            renderItem={item => (
+                                <List.Item key={item.id}>
+                                    <SingleReview review={item}/>
+                                </List.Item>
+                            )}>
+                            {this.state.loading && this.state.hasMore && (
+                                <div className="demo-loading-container">
+                                    <Spin/>
+                                </div>
+                            )}
+                        </List>
+                    </InfiniteScroll>
+                    {!this.state.loadMore && (this.state.data.length >= limit) && (<div style={{textAlign: "center"}}>
+                        <Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button>
+                    </div>)}
+                </div>
             </div>
         );
     }
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageCategories.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageCategories.js
index 984cd5d67d..a4de159263 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageCategories.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageCategories.js
@@ -32,7 +32,7 @@ import {
     Modal,
     Row,
     Col,
-    Typography
+    Typography, Alert
 } from "antd";
 import axios from "axios";
 import {TweenOneGroup} from 'rc-tween-one';
@@ -53,13 +53,16 @@ class ManageCategories extends React.Component {
         isAddNewVisible: false,
         isEditModalVisible: false,
         currentlyEditingId: null,
-        editingValue: null
+        editingValue: null,
+        forbiddenErrors: {
+            categories: false
+        }
     };
 
     componentDidMount() {
         const config = this.props.context;
         axios.get(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories",
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/categories",
         ).then(res => {
             if (res.status === 200) {
                 let categories = JSON.parse(res.data.data);
@@ -70,10 +73,19 @@ class ManageCategories extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occured while trying to load categories");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occured while trying to load categories", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.categories = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     }
 
@@ -90,7 +102,7 @@ class ManageCategories extends React.Component {
             loading: true
         });
         axios.delete(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories/" + id,
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories/" + id,
         ).then(res => {
             if (res.status === 200) {
                 notification["success"]({
@@ -125,8 +137,7 @@ class ManageCategories extends React.Component {
         const tagElem = (
             <Tag
                 color={pSBC(0.30, config.theme.primaryColor)}
-                style={{marginTop:8}}
-            >
+                style={{marginTop: 8}}>
                 {categoryName}
                 <Divider type="vertical"/>
                 <Tooltip title="edit">
@@ -150,8 +161,7 @@ class ManageCategories extends React.Component {
                             }
                         }}
                         okText="Yes"
-                        cancelText="No"
-                    >
+                        cancelText="No">
                         <Icon type="delete"/>
                     </Popconfirm>
                 </Tooltip>
@@ -168,7 +178,7 @@ class ManageCategories extends React.Component {
         const config = this.props.context;
         const tagElem = (
             <Tag
-                style={{marginTop:8}}
+                style={{marginTop: 8}}
                 closable
                 onClose={e => {
                     e.preventDefault();
@@ -229,7 +239,7 @@ class ManageCategories extends React.Component {
         const data = tempElements.map(category => category.categoryName);
 
         axios.post(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories",
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories",
             data,
         ).then(res => {
             if (res.status === 200) {
@@ -287,7 +297,7 @@ class ManageCategories extends React.Component {
         });
 
         axios.put(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories/rename?from=" + currentlyEditingId + "&to=" + editingValue,
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/categories/rename?from=" + currentlyEditingId + "&to=" + editingValue,
             {},
         ).then(res => {
             if (res.status === 200) {
@@ -324,11 +334,18 @@ class ManageCategories extends React.Component {
     };
 
     render() {
-        const {categories, inputVisible, inputValue, tempElements, isAddNewVisible} = this.state;
+        const {categories, inputVisible, inputValue, tempElements, isAddNewVisible, forbiddenErrors} = this.state;
         const categoriesElements = categories.map(this.renderElement);
         const temporaryElements = tempElements.map(this.renderTempElement);
         return (
             <div style={{marginBottom: 16}}>
+                {(forbiddenErrors.categories) && (
+                    <Alert
+                        message="You don't have permission to view categories."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <Card>
                     <Spin tip="Working on it..." spinning={this.state.loading}>
                         <Row>
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageTags.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageTags.js
index 69c269bc17..8b9ed800b1 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageTags.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/manage/categories/ManageTags.js
@@ -31,7 +31,7 @@ import {
     Popconfirm,
     Modal,
     Row, Col,
-    Typography
+    Typography, Alert
 } from "antd";
 import axios from "axios";
 import {TweenOneGroup} from 'rc-tween-one';
@@ -51,13 +51,16 @@ class ManageTags extends React.Component {
         isAddNewVisible: false,
         isEditModalVisible: false,
         currentlyEditingId: null,
-        editingValue: null
+        editingValue: null,
+        forbiddenErrors: {
+            tags: false
+        }
     };
 
     componentDidMount() {
         const config = this.props.context;
         axios.get(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
         ).then(res => {
             if (res.status === 200) {
                 let tags = JSON.parse(res.data.data);
@@ -68,10 +71,19 @@ class ManageTags extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load tags.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load tags.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.tags = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     }
 
@@ -90,7 +102,7 @@ class ManageTags extends React.Component {
         });
 
         axios.delete(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/tags/" + id
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/admin/applications/tags/" + id
         ).then(res => {
             if (res.status === 200) {
                 notification["success"]({
@@ -124,7 +136,7 @@ class ManageTags extends React.Component {
         const tagElem = (
             <Tag
                 color="#34495e"
-                style={{marginTop:8}}
+                style={{marginTop: 8}}
             >
                 {tagName}
                 <Divider type="vertical"/>
@@ -167,7 +179,7 @@ class ManageTags extends React.Component {
         const {tempElements} = this.state;
         const tagElem = (
             <Tag
-                style={{marginTop:8}}
+                style={{marginTop: 8}}
                 closable
                 onClose={e => {
                     e.preventDefault();
@@ -226,7 +238,7 @@ class ManageTags extends React.Component {
 
         const data = tempElements.map(tag => tag.tagName);
 
-        axios.post(window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
+        axios.post(window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags",
             data,
         ).then(res => {
             if (res.status === 200) {
@@ -284,7 +296,7 @@ class ManageTags extends React.Component {
         });
 
         axios.put(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags/rename?from=" + currentlyEditingId + "&to=" + editingValue,
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/tags/rename?from=" + currentlyEditingId + "&to=" + editingValue,
             {},
         ).then(res => {
             if (res.status === 200) {
@@ -321,11 +333,18 @@ class ManageTags extends React.Component {
     };
 
     render() {
-        const {tags, inputVisible, inputValue, tempElements, isAddNewVisible} = this.state;
+        const {tags, inputVisible, inputValue, tempElements, isAddNewVisible, forbiddenErrors} = this.state;
         const tagsElements = tags.map(this.renderElement);
         const temporaryElements = tempElements.map(this.renderTempElement);
         return (
             <div style={{marginBottom: 16}}>
+                {(forbiddenErrors.tags) && (
+                    <Alert
+                        message="You don't have permission to view tags."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <Card>
                     <Spin tip="Working on it..." spinning={this.state.loading}>
                         <Row>
@@ -365,8 +384,7 @@ class ManageTags extends React.Component {
                                         },
                                     }}
                                     leave={{opacity: 0, width: 0, scale: 0, duration: 200}}
-                                    appear={false}
-                                >
+                                    appear={false}>
                                     {temporaryElements}
 
                                     {inputVisible && (
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js
index 340d28aa93..39dc99ddb1 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/AddNewAppForm.js
@@ -55,7 +55,10 @@ class AddNewAppFormComponent extends React.Component {
             isError: false,
             deviceType: null,
             supportedOsVersions: [],
-            errorText: ""
+            errorText: "",
+            forbiddenErrors: {
+                supportedOsVersions: false
+            }
         };
     }
 
@@ -143,15 +146,24 @@ class AddNewAppFormComponent extends React.Component {
                 });
             }
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load supported OS versions.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load supported OS versions.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.supportedOsVersions = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
     render() {
-        const {loading, current, isError, supportedOsVersions, errorText} = this.state;
+        const {loading, current, isError, supportedOsVersions, errorText, forbiddenErrors} = this.state;
         const {formConfig} = this.props;
         return (
             <div>
@@ -171,6 +183,7 @@ class AddNewAppFormComponent extends React.Component {
                                 </div>
                                 <div style={{display: (current === 1 ? 'unset' : 'none')}}>
                                     <NewAppUploadForm
+                                        forbiddenErrors={forbiddenErrors}
                                         formConfig={formConfig}
                                         supportedOsVersions={supportedOsVersions}
                                         onSuccessReleaseData={this.onSuccessReleaseData}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js
index 881dd9f14a..1a0ac43127 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppDetailsForm.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Button, Col, Divider, Form, Icon, Input, notification, Row, Select, Spin, Switch, Upload} from "antd";
+import {Alert, Button, Col, Divider, Form, Icon, Input, notification, Row, Select, Spin, Switch, Upload} from "antd";
 import axios from "axios";
 import {withConfigContext} from "../../../context/ConfigContext";
 import {handleApiError} from "../../../js/Utils";
@@ -46,10 +46,16 @@ class NewAppDetailsForm extends React.Component {
             deviceTypes: [],
             fetching: false,
             roleSearchValue: [],
-            unrestrictedRoles: []
+            unrestrictedRoles: [],
+            forbiddenErrors: {
+                categories: false,
+                tags: false,
+                deviceTypes: false,
+                roles: false
+            }
         };
         this.lastFetchId = 0;
-        this.fetchUser = debounce(this.fetchRoles, 800);
+        this.fetchRoles = debounce(this.fetchRoles, 800);
     }
 
     handleSubmit = e => {
@@ -64,7 +70,7 @@ class NewAppDetailsForm extends React.Component {
                 });
                 const {name, description, categories, tags, unrestrictedRoles} = values;
                 const unrestrictedRolesData = [];
-                unrestrictedRoles.map(val=>{
+                unrestrictedRoles.map(val => {
                     unrestrictedRolesData.push(val.key);
                 });
                 const application = {
@@ -89,6 +95,8 @@ class NewAppDetailsForm extends React.Component {
 
     componentDidMount() {
         this.getCategories();
+        this.getTags();
+        this.getDeviceTypes();
     }
 
     getCategories = () => {
@@ -103,13 +111,20 @@ class NewAppDetailsForm extends React.Component {
                     loading: false
                 });
             }
-            this.getTags();
-
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load categories.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load categories.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.categories = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -125,13 +140,20 @@ class NewAppDetailsForm extends React.Component {
                     loading: false,
                 });
             }
-            this.getDeviceTypes();
-
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load tags.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load tags.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.tags = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -169,10 +191,19 @@ class NewAppDetailsForm extends React.Component {
                 });
             }
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load device types.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load device types.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.deviceTypes = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -203,8 +234,19 @@ class NewAppDetailsForm extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load roles.");
-            this.setState({fetching: false});
+            handleApiError(error, "Error occurred while trying to load roles.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.roles = true;
+                this.setState({
+                    forbiddenErrors,
+                    fetching: false
+                })
+            } else {
+                this.setState({
+                    fetching: false
+                });
+            }
         });
     };
 
@@ -218,7 +260,7 @@ class NewAppDetailsForm extends React.Component {
 
     render() {
         const {formConfig} = this.props;
-        const {categories, tags, deviceTypes, fetching, roleSearchValue, unrestrictedRoles} = this.state;
+        const {categories, tags, deviceTypes, fetching, roleSearchValue, unrestrictedRoles, forbiddenErrors} = this.state;
         const {getFieldDecorator} = this.props.form;
 
         return (
@@ -233,33 +275,41 @@ class NewAppDetailsForm extends React.Component {
                             layout="horizontal"
                             onSubmit={this.handleSubmit}>
                             {formConfig.installationType !== "WEB_CLIP" && (
-                                <Form.Item {...formItemLayout} label="Device Type">
-                                    {getFieldDecorator('deviceType', {
-                                            rules: [
+                                <div>
+                                    {(forbiddenErrors.deviceTypes) && (
+                                        <Alert
+                                            message="You don't have permission to view device types."
+                                            type="warning"
+                                            banner
+                                            closable/>
+                                    )}
+                                    <Form.Item {...formItemLayout} label="Device Type">
+                                        {getFieldDecorator('deviceType', {
+                                                rules: [
+                                                    {
+                                                        required: true,
+                                                        message: 'Please select device type'
+                                                    }
+                                                ],
+                                            }
+                                        )(
+                                            <Select
+                                                style={{width: '100%'}}
+                                                placeholder="select device type">
                                                 {
-                                                    required: true,
-                                                    message: 'Please select device type'
+                                                    deviceTypes.map(deviceType => {
+                                                        return (
+                                                            <Option
+                                                                key={deviceType.name}>
+                                                                {deviceType.name}
+                                                            </Option>
+                                                        )
+                                                    })
                                                 }
-                                            ],
-                                        }
-                                    )(
-                                        <Select
-                                            style={{width: '100%'}}
-                                            placeholder="select device type"
-                                            onChange={this.handleCategoryChange}>
-                                            {
-                                                deviceTypes.map(deviceType => {
-                                                    return (
-                                                        <Option
-                                                            key={deviceType.name}>
-                                                            {deviceType.name}
-                                                        </Option>
-                                                    )
-                                                })
-                                            }
-                                        </Select>
-                                    )}
-                                </Form.Item>
+                                            </Select>
+                                        )}
+                                    </Form.Item>
+                                </div>
                             )}
 
                             {/*app name*/}
@@ -287,6 +337,13 @@ class NewAppDetailsForm extends React.Component {
                             </Form.Item>
 
                             {/*Unrestricted Roles*/}
+                            {(forbiddenErrors.roles) && (
+                                <Alert
+                                    message="You don't have permission to view roles."
+                                    type="warning"
+                                    banner
+                                    closable/>
+                            )}
                             <Form.Item {...formItemLayout} label="Unrestricted Roles">
                                 {getFieldDecorator('unrestrictedRoles', {
                                     rules: [],
@@ -295,7 +352,7 @@ class NewAppDetailsForm extends React.Component {
                                     <Select
                                         mode="multiple"
                                         labelInValue
-                                        value={roleSearchValue}
+                                        // value={roleSearchValue}
                                         placeholder="Search roles"
                                         notFoundContent={fetching ? <Spin size="small"/> : null}
                                         filterOption={false}
@@ -308,6 +365,13 @@ class NewAppDetailsForm extends React.Component {
                                     </Select>
                                 )}
                             </Form.Item>
+                            {(forbiddenErrors.categories) && (
+                                <Alert
+                                    message="You don't have permission to view categories."
+                                    type="warning"
+                                    banner
+                                    closable/>
+                            )}
                             <Form.Item {...formItemLayout} label="Categories">
                                 {getFieldDecorator('categories', {
                                     rules: [{
@@ -333,6 +397,13 @@ class NewAppDetailsForm extends React.Component {
                                     </Select>
                                 )}
                             </Form.Item>
+                            {(forbiddenErrors.tags) && (
+                                <Alert
+                                    message="You don't have permission to view tags."
+                                    type="warning"
+                                    banner
+                                    closable/>
+                            )}
                             <Form.Item {...formItemLayout} label="Tags">
                                 {getFieldDecorator('tags', {
                                     rules: [{
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js
index c7916c2c13..a0aa1e6efa 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Button, Col, Form, Icon, Input, Row, Select, Switch, Upload, InputNumber, Modal} from "antd";
+import {Button, Col, Form, Icon, Input, Row, Select, Switch, Upload, InputNumber, Modal, Alert} from "antd";
 import "@babel/polyfill";
 import axios from "axios";
 import {handleApiError} from "../../../js/Utils";
@@ -400,60 +400,69 @@ class NewAppUploadForm extends React.Component {
                             </Form.Item>
 
                             {(formConfig.installationType !== "WEB_CLIP" && formConfig.installationType !== "CUSTOM") && (
-                                <Form.Item
-                                    {...formItemLayout}
-                                    label="Supported OS Versions"
-                                    validateStatus={osVersionsValidateStatus}
-                                    help={osVersionsHelperText}>
-                                    {getFieldDecorator('supportedOS', {
-                                        rules: [{
-                                            required: true
-                                        }],
-                                        initialValue: false
-                                    })(
-                                        <div>
-                                            <InputGroup>
-                                                <Row gutter={8}>
-                                                    <Col span={11}>
-                                                        <Select
-                                                            placeholder="Lower version"
-                                                            style={{width: "100%"}}
-                                                            onChange={this.handleLowerOsVersionChange}>
-                                                            {supportedOsVersions.map(version => (
-                                                                <Option key={version.versionName}
-                                                                        value={version.versionName}>
-                                                                    {version.versionName}
-                                                                </Option>
-                                                            ))}
-                                                        </Select>
-                                                    </Col>
-                                                    <Col span={2}>
-                                                        <p> - </p>
-                                                    </Col>
-                                                    <Col span={11}>
-                                                        <Select style={{width: "100%"}}
-                                                                placeholder="Upper version"
-                                                                defaultActiveFirstOption={true}
-                                                                onChange={this.handleUpperOsVersionChange}>
-                                                            {(supportedOsVersions.length > 0) &&(
-                                                                <Option key="all"
-                                                                        value={supportedOsVersions[supportedOsVersions.length-1]["versionName"]}>
-                                                                    All
-                                                                </Option>
-                                                            )}
-                                                            {supportedOsVersions.map(version => (
-                                                                <Option key={version.versionName}
-                                                                        value={version.versionName}>
-                                                                    {version.versionName}
-                                                                </Option>
-                                                            ))}
-                                                        </Select>
-                                                    </Col>
-                                                </Row>
-                                            </InputGroup>
-                                        </div>
+                                <div>
+                                    {(this.props.forbiddenErrors.supportedOsVersions) && (
+                                        <Alert
+                                            message="You don't have permission to view supported OS versions."
+                                            type="warning"
+                                            banner
+                                            closable/>
                                     )}
-                                </Form.Item>
+                                    <Form.Item
+                                        {...formItemLayout}
+                                        label="Supported OS Versions"
+                                        validateStatus={osVersionsValidateStatus}
+                                        help={osVersionsHelperText}>
+                                        {getFieldDecorator('supportedOS', {
+                                            rules: [{
+                                                required: true
+                                            }],
+                                            initialValue: false
+                                        })(
+                                            <div>
+                                                <InputGroup>
+                                                    <Row gutter={8}>
+                                                        <Col span={11}>
+                                                            <Select
+                                                                placeholder="Lower version"
+                                                                style={{width: "100%"}}
+                                                                onChange={this.handleLowerOsVersionChange}>
+                                                                {supportedOsVersions.map(version => (
+                                                                    <Option key={version.versionName}
+                                                                            value={version.versionName}>
+                                                                        {version.versionName}
+                                                                    </Option>
+                                                                ))}
+                                                            </Select>
+                                                        </Col>
+                                                        <Col span={2}>
+                                                            <p> - </p>
+                                                        </Col>
+                                                        <Col span={11}>
+                                                            <Select style={{width: "100%"}}
+                                                                    placeholder="Upper version"
+                                                                    defaultActiveFirstOption={true}
+                                                                    onChange={this.handleUpperOsVersionChange}>
+                                                                {(supportedOsVersions.length > 0) && (
+                                                                    <Option key="all"
+                                                                            value={supportedOsVersions[supportedOsVersions.length - 1]["versionName"]}>
+                                                                        All
+                                                                    </Option>
+                                                                )}
+                                                                {supportedOsVersions.map(version => (
+                                                                    <Option key={version.versionName}
+                                                                            value={version.versionName}>
+                                                                        {version.versionName}
+                                                                    </Option>
+                                                                ))}
+                                                            </Select>
+                                                        </Col>
+                                                    </Row>
+                                                </InputGroup>
+                                            </div>
+                                        )}
+                                    </Form.Item>
+                                </div>
                             )}
                             <Form.Item {...formItemLayout} label="Meta Data">
                                 {getFieldDecorator('meta', {})(
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js
index 1fa47170e9..abaea5dbe9 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js
@@ -41,7 +41,10 @@ class AddNewReleaseFormComponent extends React.Component {
             supportedOsVersions: [],
             application: null,
             release: null,
-            deviceType: null
+            deviceType: null,
+            forbiddenErrors: {
+                supportedOsVersions: false
+            }
         };
     }
 
@@ -63,10 +66,19 @@ class AddNewReleaseFormComponent extends React.Component {
                 });
             }
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load supported OS versions.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load supported OS versions.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.supportedOsVersions = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -85,7 +97,7 @@ class AddNewReleaseFormComponent extends React.Component {
         data.append("applicationRelease", blob);
 
         const url = window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher +
-                    "/applications/" + deviceType + "/ent-app/" + appId;
+            "/applications/" + deviceType + "/ent-app/" + appId;
         axios.post(
             url,
             data
@@ -122,14 +134,15 @@ class AddNewReleaseFormComponent extends React.Component {
     };
 
     render() {
-        const {loading, supportedOsVersions} = this.state;
+        const {loading, supportedOsVersions, forbiddenErrors} = this.state;
         return (
             <div>
                 <Spin tip="Uploading..." spinning={loading}>
                     <Row>
-                        <Col span={17} offset={4} >
+                        <Col span={17} offset={4}>
                             <Card>
                                 <NewAppUploadForm
+                                    forbiddenErrors={forbiddenErrors}
                                     formConfig={formConfig}
                                     supportedOsVersions={supportedOsVersions}
                                     onSuccessReleaseData={this.onSuccessReleaseData}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js
index 2ef4f8bf0f..9be2810b0f 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js
@@ -18,11 +18,12 @@
 
 import {notification} from "antd";
 
-export const handleApiError = (error, message) => {
+export const handleApiError = (error, message, isForbiddenMessageSilent) => {
     if (error.hasOwnProperty("response") && error.response.status === 401) {
         const redirectUrl = encodeURI(window.location.href);
         window.location.href = window.location.origin + `/publisher/login?redirect=${redirectUrl}`;
-    } else {
+        // silence 403 forbidden message
+    } else if(!(isForbiddenMessageSilent && error.hasOwnProperty("response") && error.response.status === 403)){
         notification["error"]({
             message: "There was a problem",
             duration: 10,
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js
index 40fd63a0ba..fc653d7bf4 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/apps/release/Release.js
@@ -24,6 +24,7 @@ import ReleaseView from "../../../../components/apps/release/ReleaseView";
 import LifeCycle from "../../../../components/apps/release/lifeCycle/LifeCycle";
 import {withConfigContext} from "../../../../context/ConfigContext";
 import {handleApiError} from "../../../../js/Utils";
+import NewAppUploadForm from "../../../../components/new-app/subForms/NewAppUploadForm";
 
 const {Title} = Typography;
 
@@ -40,13 +41,19 @@ class Release extends React.Component {
             release: null,
             currentLifecycleStatus: null,
             lifecycle: null,
-            supportedOsVersions:[]
+            supportedOsVersions: [],
+            forbiddenErrors: {
+                supportedOsVersions: false,
+                lifeCycle: false
+            }
         };
     }
 
     componentDidMount() {
         const {uuid} = this.props.match.params;
         this.fetchData(uuid);
+        this.getLifecycle();
+
     }
 
     componentDidUpdate(prevProps, prevState, snapshot) {
@@ -86,10 +93,8 @@ class Release extends React.Component {
                     loading: false,
                     uuid: uuid
                 });
-                if(config.deviceTypes.mobileTypes.includes(app.deviceType)){
+                if (config.deviceTypes.mobileTypes.includes(app.deviceType)) {
                     this.getSupportedOsVersions(app.deviceType);
-                }else{
-                    this.getLifecycle();
                 }
             }
 
@@ -111,8 +116,15 @@ class Release extends React.Component {
                 })
             }
 
-        }).catch(function (error) {
-            handleApiError(error, "Error occurred while trying to load lifecycle configuration.");
+        }).catch((error) => {
+            handleApiError(error, "Error occurred while trying to load lifecycle configuration.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.lifeCycle = true;
+                this.setState({
+                    forbiddenErrors
+                })
+            }
         });
     };
 
@@ -125,21 +137,28 @@ class Release extends React.Component {
             if (res.status === 200) {
                 let supportedOsVersions = JSON.parse(res.data.data);
                 this.setState({
-                    supportedOsVersions,
-                    loading: false,
+                    supportedOsVersions
                 });
-                this.getLifecycle();
             }
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load supported OS versions.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load supported OS versions.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.supportedOsVersions = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
     render() {
-        const {app, release, currentLifecycleStatus, lifecycle, loading} = this.state;
+        const {app, release, currentLifecycleStatus, lifecycle, loading, forbiddenErrors} = this.state;
 
         if (release == null && loading === false) {
             return (
@@ -159,12 +178,13 @@ class Release extends React.Component {
                                 <Skeleton loading={loading} avatar={{size: 'large'}} active paragraph={{rows: 18}}>
                                     {(release !== null) && (
                                         <ReleaseView
+                                            forbiddenErrors={forbiddenErrors}
                                             app={app}
                                             release={release}
                                             currentLifecycleStatus={currentLifecycleStatus}
                                             lifecycle={lifecycle}
                                             updateRelease={this.updateRelease}
-                                            supportedOsVersions = {[...this.state.supportedOsVersions]}
+                                            supportedOsVersions={[...this.state.supportedOsVersions]}
                                         />)
                                     }
                                 </Skeleton>

From 9303d37451c41bfe3e90c58c240158b59ff0aa5d Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Sat, 26 Oct 2019 17:06:35 +0530
Subject: [PATCH 09/10] Add custom forbidden alerts messages in APPM store UI

---
 .../react-app/src/js/Utils.js                 |  4 +-
 .../react-app/src/components/apps/AppList.js  | 36 +++++--
 .../apps/release/SubscriptionDetails.js       | 38 ++++++-
 .../apps/release/install/DeviceInstall.js     | 25 ++++-
 .../apps/release/install/DeviceUninstall.js   | 25 ++++-
 .../apps/release/install/GroupInstall.js      | 23 ++++-
 .../apps/release/install/GroupUninstall.js    | 76 ++++++++------
 .../apps/release/install/RoleInstall.js       | 23 ++++-
 .../apps/release/install/RoleUninstall.js     | 85 +++++++++-------
 .../apps/release/install/UserInstall.js       | 23 ++++-
 .../apps/release/install/UserUninstall.js     | 23 ++++-
 .../apps/release/review/CurrentUsersReview.js | 99 ++++++++++---------
 .../apps/release/review/ReviewContainer.js    | 26 ++++-
 .../components/apps/release/review/Reviews.js | 85 ++++++++++------
 .../react-app/src/js/Utils.js                 |  7 +-
 .../src/pages/dashboard/Dashboard.js          | 92 ++++++++++-------
 .../pages/dashboard/apps/release/Release.js   | 20 +++-
 17 files changed, 493 insertions(+), 217 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js
index 9be2810b0f..d5b976499e 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/js/Utils.js
@@ -18,12 +18,12 @@
 
 import {notification} from "antd";
 
-export const handleApiError = (error, message, isForbiddenMessageSilent) => {
+export const handleApiError = (error, message, isForbiddenMessageSilent = false) => {
     if (error.hasOwnProperty("response") && error.response.status === 401) {
         const redirectUrl = encodeURI(window.location.href);
         window.location.href = window.location.origin + `/publisher/login?redirect=${redirectUrl}`;
         // silence 403 forbidden message
-    } else if(!(isForbiddenMessageSilent && error.hasOwnProperty("response") && error.response.status === 403)){
+    } else if (!(isForbiddenMessageSilent && error.hasOwnProperty("response") && error.response.status === 403)) {
         notification["error"]({
             message: "There was a problem",
             duration: 10,
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js
index 241ea12acd..710821f324 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/AppList.js
@@ -18,7 +18,7 @@
 
 import React from "react";
 import AppCard from "./AppCard";
-import {Col, message, notification, Row, Result, Skeleton} from "antd";
+import {Col, message, notification, Row, Result, Skeleton, Alert} from "antd";
 import axios from "axios";
 import {withConfigContext} from "../../context/ConfigContext";
 import {handleApiError} from "../../js/Utils";
@@ -28,7 +28,10 @@ class AppList extends React.Component {
         super(props);
         this.state = {
             apps: [],
-            loading: true
+            loading: true,
+            forbiddenErrors: {
+                apps: false
+            }
         }
     }
 
@@ -60,7 +63,7 @@ class AppList extends React.Component {
         });
         //send request to the invoker
         axios.post(
-            window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/applications/",
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/applications/",
             payload,
         ).then(res => {
             if (res.status === 200) {
@@ -73,18 +76,37 @@ class AppList extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load apps.");
-            this.setState({loading: false});
+            handleApiError(error, "Error occurred while trying to load apps.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.apps = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
     render() {
-        const {apps,loading} = this.state;
+        const {apps, loading, forbiddenErrors} = this.state;
 
         return (
             <Skeleton loading={loading} active>
                 <Row gutter={16}>
-                    {apps.length === 0 && (
+                    {(forbiddenErrors.apps) && (
+                        <Result
+                            status="403"
+                            title="403"
+                            subTitle="You don't have permission to view apps."
+                            // extra={<Button type="primary">Back Home</Button>}
+                        />
+                    )}
+                    {!((forbiddenErrors.apps)) && apps.length === 0 && (
                         <Result
                             status="404"
                             title="No apps, yet."
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/SubscriptionDetails.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/SubscriptionDetails.js
index f6bace7dd0..be71eb4e8f 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/SubscriptionDetails.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/SubscriptionDetails.js
@@ -18,7 +18,20 @@
 
 import React from "react";
 import axios from "axios";
-import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider, Button, Modal, Select} from "antd";
+import {
+    Tag,
+    message,
+    notification,
+    Table,
+    Typography,
+    Tooltip,
+    Icon,
+    Divider,
+    Button,
+    Modal,
+    Select,
+    Alert
+} from "antd";
 import TimeAgo from 'javascript-time-ago'
 
 // Load locale-specific relative date/time formatting rules.
@@ -149,7 +162,8 @@ class SubscriptionDetails extends React.Component {
             selectedRows: [],
             deviceGroups: [],
             groupModalVisible: false,
-            selectedGroupId: []
+            selectedGroupId: [],
+            isForbidden: false
         };
     }
 
@@ -188,8 +202,17 @@ class SubscriptionDetails extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Something went wrong when trying to load subscription data.");
-            this.setState({loading: false});
+            handleApiError(error, "Something went wrong when trying to load subscription data.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -197,6 +220,13 @@ class SubscriptionDetails extends React.Component {
         const {data, pagination, loading, selectedRows} = this.state;
         return (
             <div>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view subscription details."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <div style={{paddingBottom: 24}}>
                     <Text>
                         The following are the subscription details of the application in each respective device.
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js
index 3e67c54c03..ed69db96ef 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceInstall.js
@@ -18,7 +18,7 @@
 
 import React from "react";
 import axios from "axios";
-import {Button, message, DatePicker, Table, Typography} from "antd";
+import {Button, message, DatePicker, Table, Typography, Alert} from "antd";
 import TimeAgo from 'javascript-time-ago'
 
 // Load locale-specific relative date/time formatting rules.
@@ -112,7 +112,8 @@ class DeviceInstall extends React.Component {
             loading: false,
             selectedRows: [],
             scheduledTime: null,
-            isScheduledInstallVisible: false
+            isScheduledInstallVisible: false,
+            isForbidden: false
         };
     }
 
@@ -169,8 +170,17 @@ class DeviceInstall extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load devices.");
-            this.setState({loading: false});
+            handleApiError(error, "Error occurred while trying to load devices.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -209,6 +219,13 @@ class DeviceInstall extends React.Component {
                     Start installing the application for one or more users by entering the corresponding user name.
                     Select install to automatically start downloading the application for the respective user/users.
                 </Text>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view devices."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <Table
                     style={{paddingTop: 20}}
                     columns={columns}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js
index 4daf769792..fa449ccd37 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/DeviceUninstall.js
@@ -18,7 +18,7 @@
 
 import React from "react";
 import axios from "axios";
-import {Button, Select, Table, Typography} from "antd";
+import {Alert, Button, Select, Table, Typography} from "antd";
 import TimeAgo from 'javascript-time-ago'
 
 // Load locale-specific relative date/time formatting rules.
@@ -109,7 +109,8 @@ class DeviceUninstall extends React.Component {
             data: [],
             pagination: {},
             loading: false,
-            selectedRows: []
+            selectedRows: [],
+            isForbidden: false
         };
     }
 
@@ -164,8 +165,17 @@ class DeviceUninstall extends React.Component {
                 });
             }
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load devices.");
-            this.setState({loading: false});
+            handleApiError(error, "Error occurred while trying to load devices.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -204,6 +214,13 @@ class DeviceUninstall extends React.Component {
                     Start uninstalling the application for devices by selecting the corresponding devices.
                     Select uninstall to automatically start uninstalling the application for the respective devices.
                 </Text>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view installed devices."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <Table
                     style={{paddingTop: 20}}
                     columns={columns}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js
index be8c598a23..7d1d41a048 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupInstall.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Typography, Select, Spin, message, notification, Button} from "antd";
+import {Typography, Select, Spin, message, notification, Button, Alert} from "antd";
 import debounce from 'lodash.debounce';
 import axios from "axios";
 import {withConfigContext} from "../../../../context/ConfigContext";
@@ -40,6 +40,7 @@ class GroupInstall extends React.Component {
         data: [],
         value: [],
         fetching: false,
+        isForbidden: false
     };
 
     fetchUser = value => {
@@ -67,8 +68,17 @@ class GroupInstall extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load groups.");
-            this.setState({fetching: false});
+            handleApiError(error,"Error occurred while trying to load groups.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -96,6 +106,13 @@ class GroupInstall extends React.Component {
         return (
             <div>
                 <Text>Start installing the application for one or more groups by entering the corresponding group name. Select install to automatically start downloading the application for the respective device group/ groups.</Text>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view groups."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <br/>
                 <br/>
                 <Select
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js
index 82061b434c..b7f998a9d7 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/GroupUninstall.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Typography, Select, Spin, message, notification, Button} from "antd";
+import {Typography, Select, Spin, message, notification, Button, Alert} from "antd";
 import debounce from 'lodash.debounce';
 import axios from "axios";
 import {withConfigContext} from "../../../../context/ConfigContext";
@@ -39,6 +39,7 @@ class GroupUninstall extends React.Component {
         data: [],
         value: [],
         fetching: false,
+        isForbidden: false
     };
 
     fetchUser = value => {
@@ -50,9 +51,8 @@ class GroupUninstall extends React.Component {
         const uuid = this.props.uuid;
 
         axios.get(
-                window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+
-                "/GROUP?",
-
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" +
+            "/GROUP?",
         ).then(res => {
             if (res.status === 200) {
                 if (fetchId !== this.lastFetchId) {
@@ -69,8 +69,17 @@ class GroupUninstall extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load groups.");
-            this.setState({fetching: false});
+            handleApiError(error, "Error occurred while trying to load groups.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -82,13 +91,13 @@ class GroupUninstall extends React.Component {
         });
     };
 
-    uninstall = (timestamp=null) =>{
+    uninstall = (timestamp = null) => {
         const {value} = this.state;
         const data = [];
-        value.map(val=>{
+        value.map(val => {
             data.push(val.key);
         });
-        this.props.onUninstall("group", data, "uninstall",timestamp);
+        this.props.onUninstall("group", data, "uninstall", timestamp);
     };
 
     render() {
@@ -96,26 +105,35 @@ class GroupUninstall extends React.Component {
         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>
-                    <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
-                </div>
+            <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>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view installed groups."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
+                <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>
+                <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length === 0}/>
+            </div>
         );
     }
 }
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js
index 1b29d7dcb6..063311953a 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleInstall.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Typography, Select, Spin, message, notification, Button} from "antd";
+import {Typography, Select, Spin, message, notification, Button, Alert} from "antd";
 import debounce from 'lodash.debounce';
 import axios from "axios";
 import {withConfigContext} from "../../../../context/ConfigContext";
@@ -40,6 +40,7 @@ class RoleInstall extends React.Component {
         data: [],
         value: [],
         fetching: false,
+        isForbidden: false
     };
 
     fetchUser = value => {
@@ -67,8 +68,17 @@ class RoleInstall extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load roles.");
-            this.setState({fetching: false});
+            handleApiError(error,"Error occurred while trying to load roles.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -96,6 +106,13 @@ class RoleInstall extends React.Component {
         return (
             <div>
                 <Text>Start installing the application for one or more roles by entering the corresponding role name. Select install to automatically start downloading the application for the respective user role/roles.</Text>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view roles."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <br/>
                 <br/>
                 <Select
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js
index 5e7462de3c..0076e80b70 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/RoleUninstall.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Typography, Select, Spin, message, notification, Button} from "antd";
+import {Typography, Select, Spin, message, notification, Button, Alert} from "antd";
 import debounce from 'lodash.debounce';
 import axios from "axios";
 import {withConfigContext} from "../../../../context/ConfigContext";
@@ -39,6 +39,7 @@ class RoleUninstall extends React.Component {
         data: [],
         value: [],
         fetching: false,
+        isForbidden: false
     };
 
     fetchUser = value => {
@@ -50,8 +51,8 @@ class RoleUninstall extends React.Component {
         const uuid = this.props.uuid;
 
         axios.get(
-                window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+
-                "/ROLE?",
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" +
+            "/ROLE?",
         ).then(res => {
             if (res.status === 200) {
                 if (fetchId !== this.lastFetchId) {
@@ -68,53 +69,71 @@ class RoleUninstall extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load roles.");
-            this.setState({fetching: false});
+            handleApiError(error, "Error occurred while trying to load roles.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
     handleChange = value => {
         this.setState({
-                          value,
-                          data: [],
-                          fetching: false,
-                      });
+            value,
+            data: [],
+            fetching: false,
+        });
     };
 
-    uninstall = (timestamp=null) =>{
+    uninstall = (timestamp = null) => {
         const {value} = this.state;
         const data = [];
-        value.map(val=>{
+        value.map(val => {
             data.push(val.key);
         });
-        this.props.onUninstall("role", data, "uninstall",timestamp);
+        this.props.onUninstall("role", data, "uninstall", timestamp);
     };
 
     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>
-                    <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/>
-                </div>
+            <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>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view uninstalled roles."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
+                <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>
+                <InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length === 0}/>
+            </div>
         );
     }
 }
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js
index 82f91bddff..ddac89bcab 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserInstall.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Typography, Select, Spin, message, notification, Button} from "antd";
+import {Typography, Select, Spin, message, notification, Button, Alert} from "antd";
 import debounce from 'lodash.debounce';
 import axios from "axios";
 import {withConfigContext} from "../../../../context/ConfigContext";
@@ -69,8 +69,17 @@ class UserInstall extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load users.");
-            this.setState({fetching: false});
+            handleApiError(error,"Error occurred while trying to load users.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -79,6 +88,7 @@ class UserInstall extends React.Component {
             value,
             data: [],
             fetching: false,
+            isForbidden: false
         });
     };
 
@@ -97,6 +107,13 @@ class UserInstall extends React.Component {
         return (
             <div>
                 <Text>Start installing the application for one or more users by entering the corresponding user name. Select install to automatically start downloading the application for the respective user/users. </Text>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view users."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <p>Select users</p>
                 <Select
                     mode="multiple"
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js
index cc53ca8504..935d2f47a0 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/install/UserUninstall.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {Typography, Select, Spin, message, notification, Button} from "antd";
+import {Typography, Select, Spin, message, notification, Button, Alert} from "antd";
 import debounce from 'lodash.debounce';
 import axios from "axios";
 import {withConfigContext} from "../../../../context/ConfigContext";
@@ -39,6 +39,7 @@ class UserUninstall extends React.Component {
         data: [],
         value: [],
         fetching: false,
+        isForbidden: false
     };
 
     fetchUser = (value) => {
@@ -67,8 +68,17 @@ class UserUninstall extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to load users.");
-            this.setState({fetching: false});
+            handleApiError(error, "Error occurred while trying to load users.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                this.setState({
+                    isForbidden: true,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -97,6 +107,13 @@ class UserUninstall extends React.Component {
                 <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>
+                {(this.state.isForbidden) && (
+                    <Alert
+                        message="You don't have permission to view uninstalled users."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
                 <p>Select users</p>
                 <Select
                     mode="multiple"
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/CurrentUsersReview.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/CurrentUsersReview.js
index 8489a26800..44d464b128 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/CurrentUsersReview.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/CurrentUsersReview.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {List, message, Typography, Empty, Button, Row, Col, notification} from "antd";
+import {List, message, Typography, Empty, Button, Row, Col, notification, Alert} from "antd";
 import SingleReview from "./singleReview/SingleReview";
 import axios from "axios";
 import AddReview from "./AddReview";
@@ -34,52 +34,59 @@ class CurrentUsersReview extends React.Component {
         return (
             <div>
                 <Text>MY REVIEW</Text>
-                <div style={{
-                    overflow: "auto",
-                    paddingTop: 8,
-                    paddingLeft: 24
-                }}>
-                    {currentUserReviews.length > 0 && (
-                        <div>
-                            <List
-                                dataSource={currentUserReviews}
-                                renderItem={item => (
-                                    <List.Item key={item.id}>
-                                        <SingleReview
-                                            uuid={uuid}
-                                            review={item}
-                                            isDeletable={true}
-                                            isEditable={true}
-                                            deleteCallback={this.props.deleteCallback}
-                                            onUpdateReview={this.props.onUpdateReview}
-                                            isPersonalReview={true}/>
-                                    </List.Item>
-                                )}
-                            >
-                            </List>
-                        </div>
-                    )}
+                {(this.props.forbidden) && (
+                    <Alert
+                        message="You don't have permission to add reviews."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
+                {(!this.props.forbidden) && (
+                    <div style={{
+                        overflow: "auto",
+                        paddingTop: 8,
+                        paddingLeft: 24
+                    }}>
+                        {currentUserReviews.length > 0 && (
+                            <div>
+                                <List
+                                    dataSource={currentUserReviews}
+                                    renderItem={item => (
+                                        <List.Item key={item.id}>
+                                            <SingleReview
+                                                uuid={uuid}
+                                                review={item}
+                                                isDeletable={true}
+                                                isEditable={true}
+                                                deleteCallback={this.props.deleteCallback}
+                                                onUpdateReview={this.props.onUpdateReview}
+                                                isPersonalReview={true}/>
+                                        </List.Item>
+                                    )}
+                                >
+                                </List>
+                            </div>
+                        )}
 
-                    {currentUserReviews.length === 0 && (
-                        <div>
-                            <Empty
-                                image={Empty.PRESENTED_IMAGE_DEFAULT}
-                                imagestyle={{
-                                    height: 60,
-                                }}
-                                description={
-                                    <span>Share your experience with your community by adding a review.</span>
-                                }
-                            >
-                                {/*<Button type="primary">Add review</Button>*/}
-                                <AddReview
-                                    uuid={uuid}
-                                    onUpdateReview={this.props.onUpdateReview}/>
-                            </Empty>
-                        </div>
-                    )}
-
-                </div>
+                        {currentUserReviews.length === 0 && (
+                            <div>
+                                <Empty
+                                    image={Empty.PRESENTED_IMAGE_DEFAULT}
+                                    imagestyle={{
+                                        height: 60,
+                                    }}
+                                    description={
+                                        <span>Share your experience with your community by adding a review.</span>
+                                    }>
+                                    {/*<Button type="primary">Add review</Button>*/}
+                                    <AddReview
+                                        uuid={uuid}
+                                        onUpdateReview={this.props.onUpdateReview}/>
+                                </Empty>
+                            </div>
+                        )}
+                    </div>
+                )}
             </div>
         );
     }
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/ReviewContainer.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/ReviewContainer.js
index 2b8f124cb2..291d032939 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/ReviewContainer.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/ReviewContainer.js
@@ -32,7 +32,12 @@ class ReviewContainer extends React.Component {
         super(props);
         this.state = {
             currentUserReviews: [],
-            detailedRating: null
+            detailedRating: null,
+            forbiddenErrors: {
+                currentReview: false,
+                reviews: false,
+                rating: false
+            }
         }
     }
 
@@ -54,7 +59,19 @@ class ReviewContainer extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error, "Error occurred while trying to get your review.");
+            handleApiError(error, "Error occurred while trying to get your review.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.currentReview = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -79,7 +96,7 @@ class ReviewContainer extends React.Component {
             }
 
         }).catch(function (error) {
-            handleApiError(error, "Error occurred while trying to load ratings.");
+            handleApiError(error, "Error occurred while trying to load ratings.", true);
         });
     };
 
@@ -90,10 +107,11 @@ class ReviewContainer extends React.Component {
 
     render() {
         const {uuid} = this.props;
-        const {currentUserReviews,detailedRating} = this.state;
+        const {currentUserReviews,detailedRating, forbiddenErrors} = this.state;
         return (
             <div>
                 <CurrentUsersReview
+                    forbidden={forbiddenErrors.currentReview}
                     uuid={uuid}
                     currentUserReviews={currentUserReviews}
                     onUpdateReview={this.onUpdateReview}
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/Reviews.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/Reviews.js
index 7c74574b6b..fff6364c78 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/Reviews.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/components/apps/release/review/Reviews.js
@@ -17,7 +17,7 @@
  */
 
 import React from "react";
-import {List, message, Avatar, Spin, Button, notification} from 'antd';
+import {List, message, Avatar, Spin, Button, notification, Alert} from 'antd';
 import "./Reviews.css";
 
 import InfiniteScroll from 'react-infinite-scroller';
@@ -33,7 +33,10 @@ class Reviews extends React.Component {
         data: [],
         loading: false,
         hasMore: false,
-        loadMore: false
+        loadMore: false,
+        forbiddenErrors: {
+            reviews: false
+        }
     };
 
 
@@ -51,7 +54,7 @@ class Reviews extends React.Component {
         const config = this.props.context;
 
         axios.get(
-            window.location.origin+ config.serverConfig.invoker.uri +config.serverConfig.invoker.store+"/reviews/"+type+"/"+uuid,
+            window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/reviews/" + type + "/" + uuid,
             {
                 headers: {'X-Platform': config.serverConfig.platform}
             }).then(res => {
@@ -60,8 +63,20 @@ class Reviews extends React.Component {
                 callback(reviews);
             }
 
-        }).catch(function (error) {
-            handleApiError(error,"Error occurred while trying to load reviews.");
+        }).catch((error) => {
+            handleApiError(error, "Error occurred while trying to load reviews.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.reviews = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -101,7 +116,7 @@ class Reviews extends React.Component {
         });
     };
 
-    deleteCallback = () =>{
+    deleteCallback = () => {
         this.fetchData(0, limit, res => {
             this.setState({
                 data: res,
@@ -114,30 +129,40 @@ class Reviews extends React.Component {
         const {loading, hasMore, data, loadMore} = this.state;
         const {uuid} = this.props;
         return (
-            <div className="infinite-container">
-                <InfiniteScroll
-                    initialLoad={false}
-                    pageStart={0}
-                    loadMore={this.handleInfiniteOnLoad}
-                    hasMore={!loading && hasMore}
-                    useWindow={true}>
-                    <List
-                        dataSource={data}
-                        renderItem={item => (
-                            <List.Item key={item.id}>
-                                <SingleReview uuid={uuid} review={item} isDeletable={true} isEditable={false} deleteCallback={this.deleteCallback}/>
-                            </List.Item>
-                        )}>
-                        {loading && hasMore && (
-                            <div className="loading-container">
-                                <Spin/>
-                            </div>
-                        )}
-                    </List>
-                </InfiniteScroll>
-                {!loadMore && (data.length >= limit) && (<div style={{textAlign: "center"}}>
-                    <Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button>
-                </div>)}
+            <div>
+                {(this.state.forbiddenErrors.reviews) && (
+                    <Alert
+                        message="You don't have permission to view reviews."
+                        type="warning"
+                        banner
+                        closable/>
+                )}
+                <div className="infinite-container">
+                    <InfiniteScroll
+                        initialLoad={false}
+                        pageStart={0}
+                        loadMore={this.handleInfiniteOnLoad}
+                        hasMore={!loading && hasMore}
+                        useWindow={true}>
+                        <List
+                            dataSource={data}
+                            renderItem={item => (
+                                <List.Item key={item.id}>
+                                    <SingleReview uuid={uuid} review={item} isDeletable={true} isEditable={false}
+                                                  deleteCallback={this.deleteCallback}/>
+                                </List.Item>
+                            )}>
+                            {loading && hasMore && (
+                                <div className="loading-container">
+                                    <Spin/>
+                                </div>
+                            )}
+                        </List>
+                    </InfiniteScroll>
+                    {!loadMore && (data.length >= limit) && (<div style={{textAlign: "center"}}>
+                        <Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button>
+                    </div>)}
+                </div>
             </div>
         );
     }
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js
index 7bdcef3eea..aea3333985 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/js/Utils.js
@@ -18,14 +18,15 @@
 
 import {notification} from "antd";
 
-export const handleApiError = (error, message) => {
+export const handleApiError = (error, message, isForbiddenMessageSilent = false) => {
     if (error.hasOwnProperty("response") && error.response.status === 401) {
         const redirectUrl = encodeURI(window.location.href);
         window.location.href = window.location.origin + `/store/login?redirect=${redirectUrl}`;
-    } else {
+        // silence 403 forbidden message
+    } else if (!(isForbiddenMessageSilent && error.hasOwnProperty("response") && error.response.status === 403)) {
         notification["error"]({
             message: "There was a problem",
-            duration: 2,
+            duration: 10,
             description: message,
         });
     }
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js
index 30c7da2df1..b0a5108ddf 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/Dashboard.js
@@ -17,7 +17,8 @@
  */
 
 import React from "react";
-import {Layout, Menu, Icon, Drawer, Button} from 'antd';
+import {Layout, Menu, Icon, Drawer, Button, Alert} from 'antd';
+
 const {Header, Content, Footer} = Layout;
 import {Link} from "react-router-dom";
 import RouteWithSubRoutes from "../../components/RouteWithSubRoutes";
@@ -38,7 +39,10 @@ class Dashboard extends React.Component {
             selectedKeys: [],
             deviceTypes: [],
             visible: false,
-            collapsed: false
+            collapsed: false,
+            forbiddenErrors: {
+                deviceTypes: false
+            }
         };
         this.logo = this.props.context.theme.logo;
         this.config = this.props.context;
@@ -62,10 +66,19 @@ class Dashboard extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load device types.");
-            this.setState({
-                loading: false
-            });
+            handleApiError(error, "Error occurred while trying to load device types.", true);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.deviceTypes = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 
@@ -81,7 +94,7 @@ class Dashboard extends React.Component {
             collapsed: !this.state.collapsed,
         });
     };
-    
+
     onCloseMobileNavigationBar = () => {
         this.setState({
             visible: false,
@@ -90,7 +103,7 @@ class Dashboard extends React.Component {
 
     render() {
         const config = this.props.context;
-        const {selectedKeys, deviceTypes} = this.state;
+        const {selectedKeys, deviceTypes, forbiddenErrors} = this.state;
 
         const DeviceTypesData = deviceTypes.map((deviceType) => {
             const platform = deviceType.name;
@@ -116,7 +129,7 @@ class Dashboard extends React.Component {
                 <Layout>
                     <Header style={{paddingLeft: 0, paddingRight: 0, backgroundColor: "white"}}>
                         <div className="logo-image">
-                            <Link to="/store/android"><img alt="logo" src={this.logo}/></Link>
+                            <Link to="/store"><img alt="logo" src={this.logo}/></Link>
                         </div>
 
                         <div className="web-layout">
@@ -131,7 +144,7 @@ class Dashboard extends React.Component {
                                 <Menu.Item key="web-clip">
                                     <Link to="/store/web-clip">
                                         <Icon type="upload"/>
-                                            Web Clips
+                                        Web Clips
                                     </Link>
                                 </Menu.Item>
 
@@ -140,7 +153,7 @@ class Dashboard extends React.Component {
                                              <span className="submenu-title-wrapper">
                                      <Icon type="user"/>
                                                  {this.config.user}
-                                     </span> }>
+                                     </span>}>
                                     <Logout/>
                                 </SubMenu>
                             </Menu>
@@ -156,32 +169,32 @@ class Dashboard extends React.Component {
                         </Button>
                     </div>
                 </Layout>
-                    <Drawer
-                        title={<Link to="/store/android" onClick={this.onCloseMobileNavigationBar}>
-                            <img alt="logo" src={this.logo} style={{marginLeft: 30}} width={"60%"}/>
-                        </Link>}
-                        placement="left"
-                        closable={false}
-                        onClose={this.onCloseMobileNavigationBar}
-                        visible={this.state.visible}
-                        getContainer={false}
-                        style={{position: 'absolute'}}>
-                        <Menu
-                            theme="light"
-                            mode="inline"
-                            defaultSelectedKeys={selectedKeys}
-                            style={{lineHeight: '64px', width: 231}}
-                            onClick={this.onCloseMobileNavigationBar}>
-
-                            {DeviceTypesData}
-
-                            <Menu.Item key="web-clip">
-                                <Link to="/store/web-clip">
+                <Drawer
+                    title={<Link to="/store" onClick={this.onCloseMobileNavigationBar}>
+                        <img alt="logo" src={this.logo} style={{marginLeft: 30}} width={"60%"}/>
+                    </Link>}
+                    placement="left"
+                    closable={false}
+                    onClose={this.onCloseMobileNavigationBar}
+                    visible={this.state.visible}
+                    getContainer={false}
+                    style={{position: 'absolute'}}>
+                    <Menu
+                        theme="light"
+                        mode="inline"
+                        defaultSelectedKeys={selectedKeys}
+                        style={{lineHeight: '64px', width: 231}}
+                        onClick={this.onCloseMobileNavigationBar}>
+
+                        {DeviceTypesData}
+
+                        <Menu.Item key="web-clip">
+                            <Link to="/store/web-clip">
                                 <Icon type="upload"/>Web Clips
-                                </Link>
-                            </Menu.Item>
-                        </Menu>
-                    </Drawer>
+                            </Link>
+                        </Menu.Item>
+                    </Menu>
+                </Drawer>
                 <Layout className="mobile-layout">
                     <Menu
                         mode="horizontal"
@@ -198,6 +211,13 @@ class Dashboard extends React.Component {
                 </Layout>
 
                 <Layout className="dashboard-body">
+                    {(forbiddenErrors.deviceTypes) && (
+                        <Alert
+                            message="You don't have permission to view device types."
+                            type="warning"
+                            banner
+                            closable/>
+                    )}
                     <Content style={{padding: '0 0'}}>
                         <Switch>
                             {this.state.routes.map((route) => (
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js
index adac67e737..80cfe2b9cb 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.ui/react-app/src/pages/dashboard/apps/release/Release.js
@@ -36,7 +36,10 @@ class Release extends React.Component {
         this.state = {
             loading: true,
             app: null,
-            uuid: null
+            uuid: null,
+            forbiddenErrors: {
+                app: false
+            }
         };
     }
 
@@ -72,8 +75,19 @@ class Release extends React.Component {
             }
 
         }).catch((error) => {
-            handleApiError(error,"Error occurred while trying to load releases.");
-            this.setState({loading: false});
+            handleApiError(error,"Error occurred while trying to load releases.", false);
+            if (error.hasOwnProperty("response") && error.response.status === 403) {
+                const {forbiddenErrors} = this.state;
+                forbiddenErrors.app = true;
+                this.setState({
+                    forbiddenErrors,
+                    loading: false
+                })
+            } else {
+                this.setState({
+                    loading: false
+                });
+            }
         });
     };
 

From c5bc96d8cc0cd68219f6b3fa296b255786b5cc09 Mon Sep 17 00:00:00 2001
From: Jayasanka <jayasanka.sack@gmail.com>
Date: Sat, 26 Oct 2019 19:40:20 +0530
Subject: [PATCH 10/10] Fix invalid support OS version issue in APPM publisher
 UI

---
 .../components/new-app/subForms/NewAppUploadForm.js  | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js
index a0aa1e6efa..ebb761c322 100644
--- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js
+++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js
@@ -117,7 +117,7 @@ class NewAppUploadForm extends React.Component {
                             osVersionsHelperText: 'Please select supported OS versions',
                             osVersionsValidateStatus: 'error',
                         });
-                    } else if (this.lowerOsVersion >= this.upperOsVersion) {
+                    } else if (parseFloat(this.lowerOsVersion) >= parseFloat(this.upperOsVersion)) {
                         isFormValid = false;
                         this.setState({
                             osVersionsHelperText: 'Please select valid range',
@@ -221,7 +221,7 @@ class NewAppUploadForm extends React.Component {
     };
 
     handleLowerOsVersionChange = (lowerOsVersion) => {
-        this.lowerOsVersion = parseFloat(lowerOsVersion);
+        this.lowerOsVersion = lowerOsVersion;
         this.setState({
             osVersionsValidateStatus: 'validating',
             osVersionsHelperText: ''
@@ -229,7 +229,11 @@ class NewAppUploadForm extends React.Component {
     };
 
     handleUpperOsVersionChange = (upperOsVersion) => {
-        this.upperOsVersion = parseFloat(upperOsVersion);
+        if (upperOsVersion === "all") {
+            this.upperOsVersion = this.props.supportedOsVersions[this.props.supportedOsVersions.length - 1]["versionName"];
+        }else{
+            this.upperOsVersion = upperOsVersion;
+        }
         this.setState({
             osVersionsValidateStatus: 'validating',
             osVersionsHelperText: ''
@@ -445,7 +449,7 @@ class NewAppUploadForm extends React.Component {
                                                                     onChange={this.handleUpperOsVersionChange}>
                                                                 {(supportedOsVersions.length > 0) && (
                                                                     <Option key="all"
-                                                                            value={supportedOsVersions[supportedOsVersions.length - 1]["versionName"]}>
+                                                                            value="all">
                                                                         All
                                                                     </Option>
                                                                 )}