Add custom forbidden alerts messages in APPM store UI

feature/appm-store/pbac
Jayasanka 5 years ago
parent 84ad27f299
commit 9303d37451

@ -18,12 +18,12 @@
import {notification} from "antd"; 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) { 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}`;
// silence 403 forbidden message // 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"]({ notification["error"]({
message: "There was a problem", message: "There was a problem",
duration: 10, duration: 10,

@ -18,7 +18,7 @@
import React from "react"; import React from "react";
import AppCard from "./AppCard"; 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 axios from "axios";
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
import {handleApiError} from "../../js/Utils"; import {handleApiError} from "../../js/Utils";
@ -28,7 +28,10 @@ class AppList extends React.Component {
super(props); super(props);
this.state = { this.state = {
apps: [], apps: [],
loading: true loading: true,
forbiddenErrors: {
apps: false
}
} }
} }
@ -60,7 +63,7 @@ class AppList extends React.Component {
}); });
//send request to the invoker //send request to the invoker
axios.post( 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, payload,
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -73,18 +76,37 @@ class AppList 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);
this.setState({loading: false}); 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() { render() {
const {apps,loading} = this.state; const {apps, loading, forbiddenErrors} = this.state;
return ( return (
<Skeleton loading={loading} active> <Skeleton loading={loading} active>
<Row gutter={16}> <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 <Result
status="404" status="404"
title="No apps, yet." title="No apps, yet."

@ -18,7 +18,20 @@
import React from "react"; import React from "react";
import axios from "axios"; 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' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
@ -149,7 +162,8 @@ class SubscriptionDetails extends React.Component {
selectedRows: [], selectedRows: [],
deviceGroups: [], deviceGroups: [],
groupModalVisible: false, groupModalVisible: false,
selectedGroupId: [] selectedGroupId: [],
isForbidden: false
}; };
} }
@ -188,8 +202,17 @@ class SubscriptionDetails extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Something went wrong when trying to load subscription data."); handleApiError(error, "Something went wrong when trying to load subscription data.", true);
this.setState({loading: false}); 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; const {data, pagination, loading, selectedRows} = this.state;
return ( return (
<div> <div>
{(this.state.isForbidden) && (
<Alert
message="You don't have permission to view subscription details."
type="warning"
banner
closable/>
)}
<div style={{paddingBottom: 24}}> <div style={{paddingBottom: 24}}>
<Text> <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.

@ -18,7 +18,7 @@
import React from "react"; import React from "react";
import axios from "axios"; 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' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
@ -112,7 +112,8 @@ class DeviceInstall extends React.Component {
loading: false, loading: false,
selectedRows: [], selectedRows: [],
scheduledTime: null, scheduledTime: null,
isScheduledInstallVisible: false isScheduledInstallVisible: false,
isForbidden: false
}; };
} }
@ -169,8 +170,17 @@ class DeviceInstall extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load devices."); handleApiError(error, "Error occurred while trying to load devices.", true);
this.setState({loading: false}); 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. 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. Select install to automatically start downloading the application for the respective user/users.
</Text> </Text>
{(this.state.isForbidden) && (
<Alert
message="You don't have permission to view devices."
type="warning"
banner
closable/>
)}
<Table <Table
style={{paddingTop: 20}} style={{paddingTop: 20}}
columns={columns} columns={columns}

@ -18,7 +18,7 @@
import React from "react"; import React from "react";
import axios from "axios"; 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' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
@ -109,7 +109,8 @@ class DeviceUninstall extends React.Component {
data: [], data: [],
pagination: {}, pagination: {},
loading: false, loading: false,
selectedRows: [] selectedRows: [],
isForbidden: false
}; };
} }
@ -164,8 +165,17 @@ class DeviceUninstall extends React.Component {
}); });
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load devices."); handleApiError(error, "Error occurred while trying to load devices.", true);
this.setState({loading: false}); 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. Start uninstalling the application for devices by selecting the corresponding devices.
Select uninstall to automatically start uninstalling the application for the respective devices. Select uninstall to automatically start uninstalling the application for the respective devices.
</Text> </Text>
{(this.state.isForbidden) && (
<Alert
message="You don't have permission to view installed devices."
type="warning"
banner
closable/>
)}
<Table <Table
style={{paddingTop: 20}} style={{paddingTop: 20}}
columns={columns} columns={columns}

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
@ -40,6 +40,7 @@ class GroupInstall extends React.Component {
data: [], data: [],
value: [], value: [],
fetching: false, fetching: false,
isForbidden: false
}; };
fetchUser = value => { fetchUser = value => {
@ -67,8 +68,17 @@ class GroupInstall extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load groups."); handleApiError(error,"Error occurred while trying to load groups.", true);
this.setState({fetching: false}); 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 ( return (
<div> <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> <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/>
<br/> <br/>
<Select <Select

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
@ -39,6 +39,7 @@ class GroupUninstall extends React.Component {
data: [], data: [],
value: [], value: [],
fetching: false, fetching: false,
isForbidden: false
}; };
fetchUser = value => { fetchUser = value => {
@ -50,9 +51,8 @@ class GroupUninstall extends React.Component {
const uuid = this.props.uuid; const uuid = this.props.uuid;
axios.get( axios.get(
window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+ window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" +
"/GROUP?", "/GROUP?",
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
if (fetchId !== this.lastFetchId) { if (fetchId !== this.lastFetchId) {
@ -69,8 +69,17 @@ class GroupUninstall extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load groups."); handleApiError(error, "Error occurred while trying to load groups.", true);
this.setState({fetching: false}); 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 {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val => {
data.push(val.key); data.push(val.key);
}); });
this.props.onUninstall("group", data, "uninstall",timestamp); this.props.onUninstall("group", data, "uninstall", timestamp);
}; };
render() { render() {
@ -96,26 +105,35 @@ class GroupUninstall extends React.Component {
const {fetching, data, value} = this.state; const {fetching, data, value} = this.state;
return ( return (
<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> <Text>Start uninstalling the application for one or more groups by entering the corresponding group
<br/> name. Select uninstall to automatically start uninstalling the application for the respective device
<br/> group/ groups.</Text>
<Select {(this.state.isForbidden) && (
mode="multiple" <Alert
labelInValue message="You don't have permission to view installed groups."
value={value} type="warning"
placeholder="Search groups" banner
notFoundContent={fetching ? <Spin size="small"/> : null} closable/>
filterOption={false} )}
onSearch={this.fetchUser} <br/>
onChange={this.handleChange} <br/>
style={{width: '100%'}}> <Select
{data.map(d => ( mode="multiple"
<Option key={d.value}>{d.text}</Option> labelInValue
))} value={value}
</Select> placeholder="Search groups"
<InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/> notFoundContent={fetching ? <Spin size="small"/> : null}
</div> 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>
); );
} }
} }

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
@ -40,6 +40,7 @@ class RoleInstall extends React.Component {
data: [], data: [],
value: [], value: [],
fetching: false, fetching: false,
isForbidden: false
}; };
fetchUser = value => { fetchUser = value => {
@ -67,8 +68,17 @@ class RoleInstall 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) {
this.setState({
isForbidden: true,
loading: false
})
} else {
this.setState({
loading: false
});
}
}); });
}; };
@ -96,6 +106,13 @@ class RoleInstall extends React.Component {
return ( return (
<div> <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> <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/>
<br/> <br/>
<Select <Select

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
@ -39,6 +39,7 @@ class RoleUninstall extends React.Component {
data: [], data: [],
value: [], value: [],
fetching: false, fetching: false,
isForbidden: false
}; };
fetchUser = value => { fetchUser = value => {
@ -50,8 +51,8 @@ class RoleUninstall extends React.Component {
const uuid = this.props.uuid; const uuid = this.props.uuid;
axios.get( axios.get(
window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.store+ "/subscription/" + uuid + "/"+ window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.store + "/subscription/" + uuid + "/" +
"/ROLE?", "/ROLE?",
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
if (fetchId !== this.lastFetchId) { if (fetchId !== this.lastFetchId) {
@ -68,53 +69,71 @@ class RoleUninstall 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) {
this.setState({
isForbidden: true,
loading: false
})
} else {
this.setState({
loading: false
});
}
}); });
}; };
handleChange = value => { handleChange = value => {
this.setState({ this.setState({
value, value,
data: [], data: [],
fetching: false, fetching: false,
}); });
}; };
uninstall = (timestamp=null) =>{ uninstall = (timestamp = null) => {
const {value} = this.state; const {value} = this.state;
const data = []; const data = [];
value.map(val=>{ value.map(val => {
data.push(val.key); data.push(val.key);
}); });
this.props.onUninstall("role", data, "uninstall",timestamp); this.props.onUninstall("role", data, "uninstall", timestamp);
}; };
render() { render() {
const {fetching, data, value} = this.state; const {fetching, data, value} = this.state;
return ( return (
<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> <Text>Start uninstalling the application for one or more roles by entering the corresponding role name.
<br/> Select uninstall to automatically start uninstalling the application for the respective user
<br/> role/roles.</Text>
<Select {(this.state.isForbidden) && (
mode="multiple" <Alert
labelInValue message="You don't have permission to view uninstalled roles."
value={value} type="warning"
placeholder="Search roles" banner
notFoundContent={fetching ? <Spin size="small"/> : null} closable/>
filterOption={false} )}
onSearch={this.fetchUser} <br/>
onChange={this.handleChange} <br/>
style={{width: '100%'}} <Select
> mode="multiple"
{data.map(d => ( labelInValue
<Option key={d.value}>{d.text}</Option> value={value}
))} placeholder="Search roles"
</Select> notFoundContent={fetching ? <Spin size="small"/> : null}
<InstallModalFooter type="Uninstall" operation={this.uninstall} disabled={value.length===0}/> filterOption={false}
</div> 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>
); );
} }
} }

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
@ -69,8 +69,17 @@ class UserInstall extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load users."); handleApiError(error,"Error occurred while trying to load users.", true);
this.setState({fetching: false}); 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, value,
data: [], data: [],
fetching: false, fetching: false,
isForbidden: false
}); });
}; };
@ -97,6 +107,13 @@ class UserInstall extends React.Component {
return ( return (
<div> <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> <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> <p>Select users</p>
<Select <Select
mode="multiple" mode="multiple"

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 debounce from 'lodash.debounce';
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../../context/ConfigContext"; import {withConfigContext} from "../../../../context/ConfigContext";
@ -39,6 +39,7 @@ class UserUninstall extends React.Component {
data: [], data: [],
value: [], value: [],
fetching: false, fetching: false,
isForbidden: false
}; };
fetchUser = (value) => { fetchUser = (value) => {
@ -67,8 +68,17 @@ class UserUninstall extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error, "Error occurred while trying to load users."); handleApiError(error, "Error occurred while trying to load users.", true);
this.setState({fetching: false}); 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. <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 Select uninstall to automatically start uninstalling the application for the respective
user/users. </Text> 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> <p>Select users</p>
<Select <Select
mode="multiple" mode="multiple"

@ -17,7 +17,7 @@
*/ */
import React from "react"; 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 SingleReview from "./singleReview/SingleReview";
import axios from "axios"; import axios from "axios";
import AddReview from "./AddReview"; import AddReview from "./AddReview";
@ -34,52 +34,59 @@ class CurrentUsersReview extends React.Component {
return ( return (
<div> <div>
<Text>MY REVIEW</Text> <Text>MY REVIEW</Text>
<div style={{ {(this.props.forbidden) && (
overflow: "auto", <Alert
paddingTop: 8, message="You don't have permission to add reviews."
paddingLeft: 24 type="warning"
}}> banner
{currentUserReviews.length > 0 && ( closable/>
<div> )}
<List {(!this.props.forbidden) && (
dataSource={currentUserReviews} <div style={{
renderItem={item => ( overflow: "auto",
<List.Item key={item.id}> paddingTop: 8,
<SingleReview paddingLeft: 24
uuid={uuid} }}>
review={item} {currentUserReviews.length > 0 && (
isDeletable={true} <div>
isEditable={true} <List
deleteCallback={this.props.deleteCallback} dataSource={currentUserReviews}
onUpdateReview={this.props.onUpdateReview} renderItem={item => (
isPersonalReview={true}/> <List.Item key={item.id}>
</List.Item> <SingleReview
)} uuid={uuid}
> review={item}
</List> isDeletable={true}
</div> isEditable={true}
)} deleteCallback={this.props.deleteCallback}
onUpdateReview={this.props.onUpdateReview}
isPersonalReview={true}/>
</List.Item>
)}
>
</List>
</div>
)}
{currentUserReviews.length === 0 && ( {currentUserReviews.length === 0 && (
<div> <div>
<Empty <Empty
image={Empty.PRESENTED_IMAGE_DEFAULT} image={Empty.PRESENTED_IMAGE_DEFAULT}
imagestyle={{ imagestyle={{
height: 60, height: 60,
}} }}
description={ description={
<span>Share your experience with your community by adding a review.</span> <span>Share your experience with your community by adding a review.</span>
} }>
> {/*<Button type="primary">Add review</Button>*/}
{/*<Button type="primary">Add review</Button>*/} <AddReview
<AddReview uuid={uuid}
uuid={uuid} onUpdateReview={this.props.onUpdateReview}/>
onUpdateReview={this.props.onUpdateReview}/> </Empty>
</Empty> </div>
</div> )}
)} </div>
)}
</div>
</div> </div>
); );
} }

@ -32,7 +32,12 @@ class ReviewContainer extends React.Component {
super(props); super(props);
this.state = { this.state = {
currentUserReviews: [], currentUserReviews: [],
detailedRating: null detailedRating: null,
forbiddenErrors: {
currentReview: false,
reviews: false,
rating: false
}
} }
} }
@ -54,7 +59,19 @@ class ReviewContainer extends React.Component {
} }
}).catch((error) => { }).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) { }).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() { render() {
const {uuid} = this.props; const {uuid} = this.props;
const {currentUserReviews,detailedRating} = this.state; const {currentUserReviews,detailedRating, forbiddenErrors} = this.state;
return ( return (
<div> <div>
<CurrentUsersReview <CurrentUsersReview
forbidden={forbiddenErrors.currentReview}
uuid={uuid} uuid={uuid}
currentUserReviews={currentUserReviews} currentUserReviews={currentUserReviews}
onUpdateReview={this.onUpdateReview} onUpdateReview={this.onUpdateReview}

@ -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
}
}; };
@ -51,7 +54,7 @@ class Reviews extends React.Component {
const config = this.props.context; const config = this.props.context;
axios.get( 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} headers: {'X-Platform': config.serverConfig.platform}
}).then(res => { }).then(res => {
@ -60,8 +63,20 @@ class Reviews extends React.Component {
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,7 +116,7 @@ class Reviews extends React.Component {
}); });
}; };
deleteCallback = () =>{ deleteCallback = () => {
this.fetchData(0, limit, res => { this.fetchData(0, limit, res => {
this.setState({ this.setState({
data: res, data: res,
@ -114,30 +129,40 @@ class Reviews extends React.Component {
const {loading, hasMore, data, loadMore} = this.state; const {loading, hasMore, data, loadMore} = this.state;
const {uuid} = this.props; const {uuid} = this.props;
return ( return (
<div className="infinite-container"> <div>
<InfiniteScroll {(this.state.forbiddenErrors.reviews) && (
initialLoad={false} <Alert
pageStart={0} message="You don't have permission to view reviews."
loadMore={this.handleInfiniteOnLoad} type="warning"
hasMore={!loading && hasMore} banner
useWindow={true}> closable/>
<List )}
dataSource={data} <div className="infinite-container">
renderItem={item => ( <InfiniteScroll
<List.Item key={item.id}> initialLoad={false}
<SingleReview uuid={uuid} review={item} isDeletable={true} isEditable={false} deleteCallback={this.deleteCallback}/> pageStart={0}
</List.Item> loadMore={this.handleInfiniteOnLoad}
)}> hasMore={!loading && hasMore}
{loading && hasMore && ( useWindow={true}>
<div className="loading-container"> <List
<Spin/> dataSource={data}
</div> renderItem={item => (
)} <List.Item key={item.id}>
</List> <SingleReview uuid={uuid} review={item} isDeletable={true} isEditable={false}
</InfiniteScroll> deleteCallback={this.deleteCallback}/>
{!loadMore && (data.length >= limit) && (<div style={{textAlign: "center"}}> </List.Item>
<Button type="dashed" htmlType="button" onClick={this.enableLoading}>Read All Reviews</Button> )}>
</div>)} {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> </div>
); );
} }

@ -18,14 +18,15 @@
import {notification} from "antd"; import {notification} from "antd";
export const handleApiError = (error, message) => { export const handleApiError = (error, message, isForbiddenMessageSilent = false) => {
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 + `/store/login?redirect=${redirectUrl}`; 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"]({ notification["error"]({
message: "There was a problem", message: "There was a problem",
duration: 2, duration: 10,
description: message, description: message,
}); });
} }

@ -17,7 +17,8 @@
*/ */
import React from "react"; 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; const {Header, Content, Footer} = Layout;
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import RouteWithSubRoutes from "../../components/RouteWithSubRoutes"; import RouteWithSubRoutes from "../../components/RouteWithSubRoutes";
@ -38,7 +39,10 @@ class Dashboard extends React.Component {
selectedKeys: [], selectedKeys: [],
deviceTypes: [], deviceTypes: [],
visible: false, visible: false,
collapsed: false collapsed: false,
forbiddenErrors: {
deviceTypes: false
}
}; };
this.logo = this.props.context.theme.logo; this.logo = this.props.context.theme.logo;
this.config = this.props.context; this.config = this.props.context;
@ -62,10 +66,19 @@ class Dashboard 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);
this.setState({ if (error.hasOwnProperty("response") && error.response.status === 403) {
loading: false const {forbiddenErrors} = this.state;
}); forbiddenErrors.deviceTypes = true;
this.setState({
forbiddenErrors,
loading: false
})
} else {
this.setState({
loading: false
});
}
}); });
}; };
@ -90,7 +103,7 @@ class Dashboard extends React.Component {
render() { render() {
const config = this.props.context; const config = this.props.context;
const {selectedKeys, deviceTypes} = this.state; const {selectedKeys, deviceTypes, forbiddenErrors} = this.state;
const DeviceTypesData = deviceTypes.map((deviceType) => { const DeviceTypesData = deviceTypes.map((deviceType) => {
const platform = deviceType.name; const platform = deviceType.name;
@ -116,7 +129,7 @@ class Dashboard extends React.Component {
<Layout> <Layout>
<Header style={{paddingLeft: 0, paddingRight: 0, backgroundColor: "white"}}> <Header style={{paddingLeft: 0, paddingRight: 0, backgroundColor: "white"}}>
<div className="logo-image"> <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>
<div className="web-layout"> <div className="web-layout">
@ -131,7 +144,7 @@ class Dashboard extends React.Component {
<Menu.Item key="web-clip"> <Menu.Item key="web-clip">
<Link to="/store/web-clip"> <Link to="/store/web-clip">
<Icon type="upload"/> <Icon type="upload"/>
Web Clips Web Clips
</Link> </Link>
</Menu.Item> </Menu.Item>
@ -140,7 +153,7 @@ class Dashboard extends React.Component {
<span className="submenu-title-wrapper"> <span className="submenu-title-wrapper">
<Icon type="user"/> <Icon type="user"/>
{this.config.user} {this.config.user}
</span> }> </span>}>
<Logout/> <Logout/>
</SubMenu> </SubMenu>
</Menu> </Menu>
@ -156,32 +169,32 @@ class Dashboard extends React.Component {
</Button> </Button>
</div> </div>
</Layout> </Layout>
<Drawer <Drawer
title={<Link to="/store/android" onClick={this.onCloseMobileNavigationBar}> title={<Link to="/store" onClick={this.onCloseMobileNavigationBar}>
<img alt="logo" src={this.logo} style={{marginLeft: 30}} width={"60%"}/> <img alt="logo" src={this.logo} style={{marginLeft: 30}} width={"60%"}/>
</Link>} </Link>}
placement="left" placement="left"
closable={false} closable={false}
onClose={this.onCloseMobileNavigationBar} onClose={this.onCloseMobileNavigationBar}
visible={this.state.visible} visible={this.state.visible}
getContainer={false} getContainer={false}
style={{position: 'absolute'}}> style={{position: 'absolute'}}>
<Menu <Menu
theme="light" theme="light"
mode="inline" mode="inline"
defaultSelectedKeys={selectedKeys} defaultSelectedKeys={selectedKeys}
style={{lineHeight: '64px', width: 231}} style={{lineHeight: '64px', width: 231}}
onClick={this.onCloseMobileNavigationBar}> onClick={this.onCloseMobileNavigationBar}>
{DeviceTypesData} {DeviceTypesData}
<Menu.Item key="web-clip"> <Menu.Item key="web-clip">
<Link to="/store/web-clip"> <Link to="/store/web-clip">
<Icon type="upload"/>Web Clips <Icon type="upload"/>Web Clips
</Link> </Link>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</Drawer> </Drawer>
<Layout className="mobile-layout"> <Layout className="mobile-layout">
<Menu <Menu
mode="horizontal" mode="horizontal"
@ -198,6 +211,13 @@ class Dashboard extends React.Component {
</Layout> </Layout>
<Layout className="dashboard-body"> <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'}}> <Content style={{padding: '0 0'}}>
<Switch> <Switch>
{this.state.routes.map((route) => ( {this.state.routes.map((route) => (

@ -36,7 +36,10 @@ class Release extends React.Component {
this.state = { this.state = {
loading: true, loading: true,
app: null, app: null,
uuid: null uuid: null,
forbiddenErrors: {
app: false
}
}; };
} }
@ -72,8 +75,19 @@ class Release extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
handleApiError(error,"Error occurred while trying to load releases."); handleApiError(error,"Error occurred while trying to load releases.", false);
this.setState({loading: 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
});
}
}); });
}; };

Loading…
Cancel
Save