Add custom forbidden alerts messages in APPM publisher UI

feature/appm-store/pbac
Jayasanka 5 years ago
parent 6245e564a4
commit 84ad27f299

@ -61,7 +61,7 @@ class DetailedRating extends React.Component{
} }
}).catch(function (error) { }).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);
}); });
}; };

@ -136,7 +136,7 @@ class AppDetailsDrawer extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load app details."); handleApiError(error, "Error occurred while trying to load categories.", true);
this.setState({ this.setState({
loading: false loading: false
}); });

@ -30,7 +30,7 @@ import {
Form, Form,
message, message,
Radio, Radio,
notification notification, Alert
} from "antd"; } from "antd";
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
@ -46,7 +46,12 @@ class FiltersForm extends React.Component {
this.state = { this.state = {
categories: [], categories: [],
tags: [], 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"]; delete values["deviceType"];
} }
if(values.hasOwnProperty("appType") && values.appType==="ALL"){ if (values.hasOwnProperty("appType") && values.appType === "ALL") {
delete values["appType"]; delete values["appType"];
} }
@ -73,16 +78,17 @@ class FiltersForm extends React.Component {
componentDidMount() { componentDidMount() {
this.getCategories(); this.getCategories();
this.getTags();
this.getDeviceTypes();
} }
getCategories = () => { getCategories = () => {
const config = this.props.context; const config = this.props.context;
axios.get( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
let categories = JSON.parse(res.data.data); let categories = JSON.parse(res.data.data);
this.getTags();
this.setState({ this.setState({
categories: categories, categories: categories,
loading: false loading: false
@ -90,21 +96,29 @@ class FiltersForm extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load categories."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
getTags = () => { getTags = () => {
const config = this.props.context; const config = this.props.context;
axios.get( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
let tags = JSON.parse(res.data.data); let tags = JSON.parse(res.data.data);
this.getDeviceTypes();
this.setState({ this.setState({
tags: tags, tags: tags,
loading: false, loading: false,
@ -112,10 +126,19 @@ class FiltersForm extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load tags."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
@ -123,7 +146,7 @@ class FiltersForm extends React.Component {
getDeviceTypes = () => { getDeviceTypes = () => {
const config = this.props.context; const config = this.props.context;
axios.get( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
const deviceTypes = JSON.parse(res.data.data); const deviceTypes = JSON.parse(res.data.data);
@ -134,15 +157,24 @@ class FiltersForm extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load device types."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
render() { render() {
const {categories, tags, deviceTypes} = this.state; const {categories, tags, deviceTypes, forbiddenErrors} = this.state;
const {getFieldDecorator} = this.props.form; const {getFieldDecorator} = this.props.form;
return ( return (
@ -170,7 +202,13 @@ class FiltersForm extends React.Component {
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
{(forbiddenErrors.categories) && (
<Alert
message="You don't have permission to view categories."
type="warning"
banner
closable/>
)}
<Form.Item label="Categories"> <Form.Item label="Categories">
{getFieldDecorator('categories', { {getFieldDecorator('categories', {
rules: [{ rules: [{
@ -182,8 +220,7 @@ class FiltersForm extends React.Component {
mode="multiple" mode="multiple"
style={{width: '100%'}} style={{width: '100%'}}
placeholder="Select a Category" placeholder="Select a Category"
onChange={this.handleCategoryChange} onChange={this.handleCategoryChange}>
>
{ {
categories.map(category => { categories.map(category => {
return ( return (
@ -198,7 +235,13 @@ class FiltersForm extends React.Component {
)} )}
</Form.Item> </Form.Item>
{(forbiddenErrors.deviceTypes) && (
<Alert
message="You don't have permission to view device types."
type="warning"
banner
closable/>
)}
<Form.Item label="Device Type"> <Form.Item label="Device Type">
{getFieldDecorator('deviceType', { {getFieldDecorator('deviceType', {
rules: [{ rules: [{
@ -208,8 +251,7 @@ class FiltersForm extends React.Component {
})( })(
<Select <Select
style={{width: '100%'}} style={{width: '100%'}}
placeholder="Select device types" placeholder="Select device types">
>
{ {
deviceTypes.map(deviceType => { deviceTypes.map(deviceType => {
return ( return (
@ -226,7 +268,13 @@ class FiltersForm extends React.Component {
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
{(forbiddenErrors.tags) && (
<Alert
message="You don't have permission to view tags."
type="warning"
banner
closable/>
)}
<Form.Item label="Tags"> <Form.Item label="Tags">
{getFieldDecorator('tags', { {getFieldDecorator('tags', {
rules: [{ rules: [{

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 axios from "axios";
import pSBC from 'shade-blend-color'; import pSBC from 'shade-blend-color';
import "./AppsTable.css"; import "./AppsTable.css";
@ -58,7 +58,7 @@ const columns = [
avatar = (hasPublishedRelease) ? ( avatar = (hasPublishedRelease) ? (
<Badge <Badge
title="Published" title="Published"
style={{ backgroundColor: '#52c41a', borderRadius:"50%", color:"white"}} style={{backgroundColor: '#52c41a', borderRadius: "50%", color: "white"}}
count={ count={
<Icon <Icon
type="check-circle"/> type="check-circle"/>
@ -147,7 +147,8 @@ class AppsTable extends React.Component {
isDrawerVisible: false, isDrawerVisible: false,
selectedApp: null, selectedApp: null,
selectedAppIndex: -1, selectedAppIndex: -1,
loading: false loading: false,
isForbiddenErrorVisible: false
}; };
config = this.props.context; config = this.props.context;
} }
@ -239,7 +240,12 @@ class AppsTable extends React.Component {
}); });
} }
}).catch((error) => { }).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}); this.setState({loading: false});
}); });
}; };
@ -255,6 +261,14 @@ class AppsTable extends React.Component {
render() { render() {
const {isDrawerVisible, loading} = this.state; const {isDrawerVisible, loading} = this.state;
return ( return (
<div>
{(this.state.isForbiddenErrorVisible) && (
<Alert
message="You don't have permission to view apps."
type="warning"
banner
closable/>
)}
<div className="apps-table"> <div className="apps-table">
<Table <Table
rowKey={record => record.id} rowKey={record => record.id}
@ -277,7 +291,7 @@ class AppsTable extends React.Component {
app={this.state.selectedApp} app={this.state.selectedApp}
onUpdateApp={this.onUpdateApp}/> onUpdateApp={this.onUpdateApp}/>
</div> </div>
</div>
); );
} }
} }

@ -24,6 +24,7 @@ import "../../../App.css";
import DetailedRating from "../detailed-rating/DetailedRating"; import DetailedRating from "../detailed-rating/DetailedRating";
import EditRelease from "./edit-release/EditRelease"; import EditRelease from "./edit-release/EditRelease";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import NewAppUploadForm from "../../new-app/subForms/NewAppUploadForm";
const {Title, Text, Paragraph} = Typography; const {Title, Text, Paragraph} = Typography;
@ -97,6 +98,7 @@ class ReleaseView extends React.Component {
<Text>Version : {release.version}</Text><br/> <Text>Version : {release.version}</Text><br/>
<EditRelease <EditRelease
forbiddenErrors={this.props.forbiddenErrors}
isAppUpdatable={isAppUpdatable} isAppUpdatable={isAppUpdatable}
type={app.type} type={app.type}
deviceType={app.deviceType} deviceType={app.deviceType}

@ -31,7 +31,7 @@ import {
Divider, Divider,
Row, Row,
Col, Col,
Select Select, Alert
} from 'antd'; } from 'antd';
import axios from "axios"; import axios from "axios";
import "@babel/polyfill"; import "@babel/polyfill";
@ -522,6 +522,14 @@ class EditReleaseModal extends React.Component {
)} )}
</Form.Item> </Form.Item>
{(config.deviceTypes.mobileTypes.includes(deviceType)) && ( {(config.deviceTypes.mobileTypes.includes(deviceType)) && (
<div>
{(this.props.forbiddenErrors.supportedOsVersions) && (
<Alert
message="You don't have permission to view supported OS versions."
type="warning"
banner
closable/>
)}
<Form.Item {...formItemLayout} label="Supported OS Versions"> <Form.Item {...formItemLayout} label="Supported OS Versions">
{getFieldDecorator('supportedOS')( {getFieldDecorator('supportedOS')(
<div> <div>
@ -579,6 +587,7 @@ class EditReleaseModal extends React.Component {
</div> </div>
)} )}
</Form.Item> </Form.Item>
</div>
)} )}
<Form.Item {...formItemLayout} label="Meta Data"> <Form.Item {...formItemLayout} label="Meta Data">
{getFieldDecorator('meta', { {getFieldDecorator('meta', {

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 "./Reviews.css";
import InfiniteScroll from 'react-infinite-scroller'; import InfiniteScroll from 'react-infinite-scroller';
@ -33,7 +33,10 @@ class Reviews extends React.Component {
data: [], data: [],
loading: false, loading: false,
hasMore: 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 config = this.props.context;
const {uuid, type} = this.props; const {uuid, type} = this.props;
this.setState({
loading: true
});
axios.get( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
let reviews = res.data.data.data; let reviews = res.data.data.data;
callback(reviews); callback(reviews);
} }
}).catch(function (error) { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load reviews."); 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,22 +121,28 @@ class Reviews extends React.Component {
render() { render() {
return ( return (
<div>
{(this.state.forbiddenErrors.reviews) && (
<Alert
message="You don't have permission to view reviews."
type="warning"
banner
closable/>
)}
<div className="demo-infinite-container"> <div className="demo-infinite-container">
<InfiniteScroll <InfiniteScroll
initialLoad={false} initialLoad={false}
pageStart={0} pageStart={0}
loadMore={this.handleInfiniteOnLoad} loadMore={this.handleInfiniteOnLoad}
hasMore={!this.state.loading && this.state.hasMore} hasMore={!this.state.loading && this.state.hasMore}
useWindow={true} useWindow={true}>
>
<List <List
dataSource={this.state.data} dataSource={this.state.data}
renderItem={item => ( renderItem={item => (
<List.Item key={item.id}> <List.Item key={item.id}>
<SingleReview review={item}/> <SingleReview review={item}/>
</List.Item> </List.Item>
)} )}>
>
{this.state.loading && this.state.hasMore && ( {this.state.loading && this.state.hasMore && (
<div className="demo-loading-container"> <div className="demo-loading-container">
<Spin/> <Spin/>
@ -128,6 +154,7 @@ class Reviews extends React.Component {
<Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button> <Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button>
</div>)} </div>)}
</div> </div>
</div>
); );
} }
} }

@ -32,7 +32,7 @@ import {
Modal, Modal,
Row, Row,
Col, Col,
Typography Typography, Alert
} from "antd"; } from "antd";
import axios from "axios"; import axios from "axios";
import {TweenOneGroup} from 'rc-tween-one'; import {TweenOneGroup} from 'rc-tween-one';
@ -53,13 +53,16 @@ class ManageCategories extends React.Component {
isAddNewVisible: false, isAddNewVisible: false,
isEditModalVisible: false, isEditModalVisible: false,
currentlyEditingId: null, currentlyEditingId: null,
editingValue: null editingValue: null,
forbiddenErrors: {
categories: false
}
}; };
componentDidMount() { componentDidMount() {
const config = this.props.context; const config = this.props.context;
axios.get( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
let categories = JSON.parse(res.data.data); let categories = JSON.parse(res.data.data);
@ -70,10 +73,19 @@ class ManageCategories extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occured while trying to load categories"); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
} }
@ -90,7 +102,7 @@ class ManageCategories extends React.Component {
loading: true loading: true
}); });
axios.delete( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
notification["success"]({ notification["success"]({
@ -125,8 +137,7 @@ class ManageCategories extends React.Component {
const tagElem = ( const tagElem = (
<Tag <Tag
color={pSBC(0.30, config.theme.primaryColor)} color={pSBC(0.30, config.theme.primaryColor)}
style={{marginTop:8}} style={{marginTop: 8}}>
>
{categoryName} {categoryName}
<Divider type="vertical"/> <Divider type="vertical"/>
<Tooltip title="edit"> <Tooltip title="edit">
@ -150,8 +161,7 @@ class ManageCategories extends React.Component {
} }
}} }}
okText="Yes" okText="Yes"
cancelText="No" cancelText="No">
>
<Icon type="delete"/> <Icon type="delete"/>
</Popconfirm> </Popconfirm>
</Tooltip> </Tooltip>
@ -168,7 +178,7 @@ class ManageCategories extends React.Component {
const config = this.props.context; const config = this.props.context;
const tagElem = ( const tagElem = (
<Tag <Tag
style={{marginTop:8}} style={{marginTop: 8}}
closable closable
onClose={e => { onClose={e => {
e.preventDefault(); e.preventDefault();
@ -229,7 +239,7 @@ class ManageCategories extends React.Component {
const data = tempElements.map(category => category.categoryName); const data = tempElements.map(category => category.categoryName);
axios.post( 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, data,
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -287,7 +297,7 @@ class ManageCategories extends React.Component {
}); });
axios.put( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -324,11 +334,18 @@ class ManageCategories extends React.Component {
}; };
render() { 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 categoriesElements = categories.map(this.renderElement);
const temporaryElements = tempElements.map(this.renderTempElement); const temporaryElements = tempElements.map(this.renderTempElement);
return ( return (
<div style={{marginBottom: 16}}> <div style={{marginBottom: 16}}>
{(forbiddenErrors.categories) && (
<Alert
message="You don't have permission to view categories."
type="warning"
banner
closable/>
)}
<Card> <Card>
<Spin tip="Working on it..." spinning={this.state.loading}> <Spin tip="Working on it..." spinning={this.state.loading}>
<Row> <Row>

@ -31,7 +31,7 @@ import {
Popconfirm, Popconfirm,
Modal, Modal,
Row, Col, Row, Col,
Typography Typography, Alert
} from "antd"; } from "antd";
import axios from "axios"; import axios from "axios";
import {TweenOneGroup} from 'rc-tween-one'; import {TweenOneGroup} from 'rc-tween-one';
@ -51,13 +51,16 @@ class ManageTags extends React.Component {
isAddNewVisible: false, isAddNewVisible: false,
isEditModalVisible: false, isEditModalVisible: false,
currentlyEditingId: null, currentlyEditingId: null,
editingValue: null editingValue: null,
forbiddenErrors: {
tags: false
}
}; };
componentDidMount() { componentDidMount() {
const config = this.props.context; const config = this.props.context;
axios.get( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
let tags = JSON.parse(res.data.data); let tags = JSON.parse(res.data.data);
@ -68,10 +71,19 @@ class ManageTags extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load tags."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
} }
@ -90,7 +102,7 @@ class ManageTags extends React.Component {
}); });
axios.delete( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
notification["success"]({ notification["success"]({
@ -124,7 +136,7 @@ class ManageTags extends React.Component {
const tagElem = ( const tagElem = (
<Tag <Tag
color="#34495e" color="#34495e"
style={{marginTop:8}} style={{marginTop: 8}}
> >
{tagName} {tagName}
<Divider type="vertical"/> <Divider type="vertical"/>
@ -167,7 +179,7 @@ class ManageTags extends React.Component {
const {tempElements} = this.state; const {tempElements} = this.state;
const tagElem = ( const tagElem = (
<Tag <Tag
style={{marginTop:8}} style={{marginTop: 8}}
closable closable
onClose={e => { onClose={e => {
e.preventDefault(); e.preventDefault();
@ -226,7 +238,7 @@ class ManageTags extends React.Component {
const data = tempElements.map(tag => tag.tagName); 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, data,
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -284,7 +296,7 @@ class ManageTags extends React.Component {
}); });
axios.put( 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 => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -321,11 +333,18 @@ class ManageTags extends React.Component {
}; };
render() { 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 tagsElements = tags.map(this.renderElement);
const temporaryElements = tempElements.map(this.renderTempElement); const temporaryElements = tempElements.map(this.renderTempElement);
return ( return (
<div style={{marginBottom: 16}}> <div style={{marginBottom: 16}}>
{(forbiddenErrors.tags) && (
<Alert
message="You don't have permission to view tags."
type="warning"
banner
closable/>
)}
<Card> <Card>
<Spin tip="Working on it..." spinning={this.state.loading}> <Spin tip="Working on it..." spinning={this.state.loading}>
<Row> <Row>
@ -365,8 +384,7 @@ class ManageTags extends React.Component {
}, },
}} }}
leave={{opacity: 0, width: 0, scale: 0, duration: 200}} leave={{opacity: 0, width: 0, scale: 0, duration: 200}}
appear={false} appear={false}>
>
{temporaryElements} {temporaryElements}
{inputVisible && ( {inputVisible && (

@ -55,7 +55,10 @@ class AddNewAppFormComponent extends React.Component {
isError: false, isError: false,
deviceType: null, deviceType: null,
supportedOsVersions: [], supportedOsVersions: [],
errorText: "" errorText: "",
forbiddenErrors: {
supportedOsVersions: false
}
}; };
} }
@ -143,15 +146,24 @@ class AddNewAppFormComponent extends React.Component {
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load supported OS versions."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
render() { render() {
const {loading, current, isError, supportedOsVersions, errorText} = this.state; const {loading, current, isError, supportedOsVersions, errorText, forbiddenErrors} = this.state;
const {formConfig} = this.props; const {formConfig} = this.props;
return ( return (
<div> <div>
@ -171,6 +183,7 @@ class AddNewAppFormComponent extends React.Component {
</div> </div>
<div style={{display: (current === 1 ? 'unset' : 'none')}}> <div style={{display: (current === 1 ? 'unset' : 'none')}}>
<NewAppUploadForm <NewAppUploadForm
forbiddenErrors={forbiddenErrors}
formConfig={formConfig} formConfig={formConfig}
supportedOsVersions={supportedOsVersions} supportedOsVersions={supportedOsVersions}
onSuccessReleaseData={this.onSuccessReleaseData} onSuccessReleaseData={this.onSuccessReleaseData}

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 axios from "axios";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils"; import {handleApiError} from "../../../js/Utils";
@ -46,10 +46,16 @@ class NewAppDetailsForm extends React.Component {
deviceTypes: [], deviceTypes: [],
fetching: false, fetching: false,
roleSearchValue: [], roleSearchValue: [],
unrestrictedRoles: [] unrestrictedRoles: [],
forbiddenErrors: {
categories: false,
tags: false,
deviceTypes: false,
roles: false
}
}; };
this.lastFetchId = 0; this.lastFetchId = 0;
this.fetchUser = debounce(this.fetchRoles, 800); this.fetchRoles = debounce(this.fetchRoles, 800);
} }
handleSubmit = e => { handleSubmit = e => {
@ -64,7 +70,7 @@ class NewAppDetailsForm extends React.Component {
}); });
const {name, description, categories, tags, unrestrictedRoles} = values; const {name, description, categories, tags, unrestrictedRoles} = values;
const unrestrictedRolesData = []; const unrestrictedRolesData = [];
unrestrictedRoles.map(val=>{ unrestrictedRoles.map(val => {
unrestrictedRolesData.push(val.key); unrestrictedRolesData.push(val.key);
}); });
const application = { const application = {
@ -89,6 +95,8 @@ class NewAppDetailsForm extends React.Component {
componentDidMount() { componentDidMount() {
this.getCategories(); this.getCategories();
this.getTags();
this.getDeviceTypes();
} }
getCategories = () => { getCategories = () => {
@ -103,13 +111,20 @@ class NewAppDetailsForm extends React.Component {
loading: false loading: false
}); });
} }
this.getTags();
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load categories."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
@ -125,13 +140,20 @@ class NewAppDetailsForm extends React.Component {
loading: false, loading: false,
}); });
} }
this.getDeviceTypes();
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load tags."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
@ -169,10 +191,19 @@ class NewAppDetailsForm extends React.Component {
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load device types."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
@ -203,8 +234,19 @@ class NewAppDetailsForm extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load roles."); handleApiError(error, "Error occurred while trying to load roles.", true);
this.setState({fetching: false}); 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() { render() {
const {formConfig} = this.props; 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; const {getFieldDecorator} = this.props.form;
return ( return (
@ -233,6 +275,14 @@ class NewAppDetailsForm extends React.Component {
layout="horizontal" layout="horizontal"
onSubmit={this.handleSubmit}> onSubmit={this.handleSubmit}>
{formConfig.installationType !== "WEB_CLIP" && ( {formConfig.installationType !== "WEB_CLIP" && (
<div>
{(forbiddenErrors.deviceTypes) && (
<Alert
message="You don't have permission to view device types."
type="warning"
banner
closable/>
)}
<Form.Item {...formItemLayout} label="Device Type"> <Form.Item {...formItemLayout} label="Device Type">
{getFieldDecorator('deviceType', { {getFieldDecorator('deviceType', {
rules: [ rules: [
@ -245,8 +295,7 @@ class NewAppDetailsForm extends React.Component {
)( )(
<Select <Select
style={{width: '100%'}} style={{width: '100%'}}
placeholder="select device type" placeholder="select device type">
onChange={this.handleCategoryChange}>
{ {
deviceTypes.map(deviceType => { deviceTypes.map(deviceType => {
return ( return (
@ -260,6 +309,7 @@ class NewAppDetailsForm extends React.Component {
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
</div>
)} )}
{/*app name*/} {/*app name*/}
@ -287,6 +337,13 @@ class NewAppDetailsForm extends React.Component {
</Form.Item> </Form.Item>
{/*Unrestricted Roles*/} {/*Unrestricted Roles*/}
{(forbiddenErrors.roles) && (
<Alert
message="You don't have permission to view roles."
type="warning"
banner
closable/>
)}
<Form.Item {...formItemLayout} label="Unrestricted Roles"> <Form.Item {...formItemLayout} label="Unrestricted Roles">
{getFieldDecorator('unrestrictedRoles', { {getFieldDecorator('unrestrictedRoles', {
rules: [], rules: [],
@ -295,7 +352,7 @@ class NewAppDetailsForm extends React.Component {
<Select <Select
mode="multiple" mode="multiple"
labelInValue labelInValue
value={roleSearchValue} // value={roleSearchValue}
placeholder="Search roles" placeholder="Search roles"
notFoundContent={fetching ? <Spin size="small"/> : null} notFoundContent={fetching ? <Spin size="small"/> : null}
filterOption={false} filterOption={false}
@ -308,6 +365,13 @@ class NewAppDetailsForm extends React.Component {
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
{(forbiddenErrors.categories) && (
<Alert
message="You don't have permission to view categories."
type="warning"
banner
closable/>
)}
<Form.Item {...formItemLayout} label="Categories"> <Form.Item {...formItemLayout} label="Categories">
{getFieldDecorator('categories', { {getFieldDecorator('categories', {
rules: [{ rules: [{
@ -333,6 +397,13 @@ class NewAppDetailsForm extends React.Component {
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
{(forbiddenErrors.tags) && (
<Alert
message="You don't have permission to view tags."
type="warning"
banner
closable/>
)}
<Form.Item {...formItemLayout} label="Tags"> <Form.Item {...formItemLayout} label="Tags">
{getFieldDecorator('tags', { {getFieldDecorator('tags', {
rules: [{ rules: [{

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 "@babel/polyfill";
import axios from "axios"; import axios from "axios";
import {handleApiError} from "../../../js/Utils"; import {handleApiError} from "../../../js/Utils";
@ -400,6 +400,14 @@ class NewAppUploadForm extends React.Component {
</Form.Item> </Form.Item>
{(formConfig.installationType !== "WEB_CLIP" && formConfig.installationType !== "CUSTOM") && ( {(formConfig.installationType !== "WEB_CLIP" && formConfig.installationType !== "CUSTOM") && (
<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} {...formItemLayout}
label="Supported OS Versions" label="Supported OS Versions"
@ -435,9 +443,9 @@ class NewAppUploadForm extends React.Component {
placeholder="Upper version" placeholder="Upper version"
defaultActiveFirstOption={true} defaultActiveFirstOption={true}
onChange={this.handleUpperOsVersionChange}> onChange={this.handleUpperOsVersionChange}>
{(supportedOsVersions.length > 0) &&( {(supportedOsVersions.length > 0) && (
<Option key="all" <Option key="all"
value={supportedOsVersions[supportedOsVersions.length-1]["versionName"]}> value={supportedOsVersions[supportedOsVersions.length - 1]["versionName"]}>
All All
</Option> </Option>
)} )}
@ -454,6 +462,7 @@ class NewAppUploadForm extends React.Component {
</div> </div>
)} )}
</Form.Item> </Form.Item>
</div>
)} )}
<Form.Item {...formItemLayout} label="Meta Data"> <Form.Item {...formItemLayout} label="Meta Data">
{getFieldDecorator('meta', {})( {getFieldDecorator('meta', {})(

@ -41,7 +41,10 @@ class AddNewReleaseFormComponent extends React.Component {
supportedOsVersions: [], supportedOsVersions: [],
application: null, application: null,
release: null, release: null,
deviceType: null deviceType: null,
forbiddenErrors: {
supportedOsVersions: false
}
}; };
} }
@ -63,10 +66,19 @@ class AddNewReleaseFormComponent extends React.Component {
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load supported OS versions."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
@ -122,14 +134,15 @@ class AddNewReleaseFormComponent extends React.Component {
}; };
render() { render() {
const {loading, supportedOsVersions} = this.state; const {loading, supportedOsVersions, forbiddenErrors} = this.state;
return ( return (
<div> <div>
<Spin tip="Uploading..." spinning={loading}> <Spin tip="Uploading..." spinning={loading}>
<Row> <Row>
<Col span={17} offset={4} > <Col span={17} offset={4}>
<Card> <Card>
<NewAppUploadForm <NewAppUploadForm
forbiddenErrors={forbiddenErrors}
formConfig={formConfig} formConfig={formConfig}
supportedOsVersions={supportedOsVersions} supportedOsVersions={supportedOsVersions}
onSuccessReleaseData={this.onSuccessReleaseData} onSuccessReleaseData={this.onSuccessReleaseData}

@ -18,11 +18,12 @@
import {notification} from "antd"; import {notification} from "antd";
export const handleApiError = (error, message) => { export const handleApiError = (error, message, isForbiddenMessageSilent) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { if (error.hasOwnProperty("response") && error.response.status === 401) {
const redirectUrl = encodeURI(window.location.href); const redirectUrl = encodeURI(window.location.href);
window.location.href = window.location.origin + `/publisher/login?redirect=${redirectUrl}`; 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"]({ notification["error"]({
message: "There was a problem", message: "There was a problem",
duration: 10, duration: 10,

@ -24,6 +24,7 @@ import ReleaseView from "../../../../components/apps/release/ReleaseView";
import LifeCycle from "../../../../components/apps/release/lifeCycle/LifeCycle"; import LifeCycle from "../../../../components/apps/release/lifeCycle/LifeCycle";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
import {handleApiError} from "../../../../js/Utils"; import {handleApiError} from "../../../../js/Utils";
import NewAppUploadForm from "../../../../components/new-app/subForms/NewAppUploadForm";
const {Title} = Typography; const {Title} = Typography;
@ -40,13 +41,19 @@ class Release extends React.Component {
release: null, release: null,
currentLifecycleStatus: null, currentLifecycleStatus: null,
lifecycle: null, lifecycle: null,
supportedOsVersions:[] supportedOsVersions: [],
forbiddenErrors: {
supportedOsVersions: false,
lifeCycle: false
}
}; };
} }
componentDidMount() { componentDidMount() {
const {uuid} = this.props.match.params; const {uuid} = this.props.match.params;
this.fetchData(uuid); this.fetchData(uuid);
this.getLifecycle();
} }
componentDidUpdate(prevProps, prevState, snapshot) { componentDidUpdate(prevProps, prevState, snapshot) {
@ -86,10 +93,8 @@ class Release extends React.Component {
loading: false, loading: false,
uuid: uuid uuid: uuid
}); });
if(config.deviceTypes.mobileTypes.includes(app.deviceType)){ if (config.deviceTypes.mobileTypes.includes(app.deviceType)) {
this.getSupportedOsVersions(app.deviceType); this.getSupportedOsVersions(app.deviceType);
}else{
this.getLifecycle();
} }
} }
@ -111,8 +116,15 @@ class Release extends React.Component {
}) })
} }
}).catch(function (error) { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load lifecycle configuration."); 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) { if (res.status === 200) {
let supportedOsVersions = JSON.parse(res.data.data); let supportedOsVersions = JSON.parse(res.data.data);
this.setState({ this.setState({
supportedOsVersions, supportedOsVersions
loading: false,
}); });
this.getLifecycle();
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load supported OS versions."); 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({ this.setState({
loading: false loading: false
}); });
}
}); });
}; };
render() { render() {
const {app, release, currentLifecycleStatus, lifecycle, loading} = this.state; const {app, release, currentLifecycleStatus, lifecycle, loading, forbiddenErrors} = this.state;
if (release == null && loading === false) { if (release == null && loading === false) {
return ( return (
@ -159,12 +178,13 @@ class Release extends React.Component {
<Skeleton loading={loading} avatar={{size: 'large'}} active paragraph={{rows: 18}}> <Skeleton loading={loading} avatar={{size: 'large'}} active paragraph={{rows: 18}}>
{(release !== null) && ( {(release !== null) && (
<ReleaseView <ReleaseView
forbiddenErrors={forbiddenErrors}
app={app} app={app}
release={release} release={release}
currentLifecycleStatus={currentLifecycleStatus} currentLifecycleStatus={currentLifecycleStatus}
lifecycle={lifecycle} lifecycle={lifecycle}
updateRelease={this.updateRelease} updateRelease={this.updateRelease}
supportedOsVersions = {[...this.state.supportedOsVersions]} supportedOsVersions={[...this.state.supportedOsVersions]}
/>) />)
} }
</Skeleton> </Skeleton>

Loading…
Cancel
Save