Files
awx/src/screens/Organization/OrganizationAccess/OrganizationAccess.jsx
Michael Abashian 52851c57d8 Display details about network errors in alert modal and content error components (#288)
Display details about network errors in alert modal and content error components
2019-06-26 11:40:15 -04:00

224 lines
6.4 KiB
JavaScript

import React, { Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { OrganizationsAPI, TeamsAPI, UsersAPI } from '@api';
import AddResourceRole from '@components/AddRole/AddResourceRole';
import AlertModal from '@components/AlertModal';
import DataListToolbar from '@components/DataListToolbar';
import ErrorDetail from '@components/ErrorDetail';
import PaginatedDataList, { ToolbarAddButton } from '@components/PaginatedDataList';
import {
getQSConfig,
encodeQueryString,
parseNamespacedQueryString
} from '@util/qs';
import { Organization } from '@types';
import DeleteRoleConfirmationModal from './DeleteRoleConfirmationModal';
import OrganizationAccessItem from './OrganizationAccessItem';
const QS_CONFIG = getQSConfig('access', {
page: 1,
page_size: 5,
order_by: 'first_name',
});
class OrganizationAccess extends React.Component {
static propTypes = {
organization: Organization.isRequired,
};
constructor (props) {
super(props);
this.state = {
accessRecords: [],
contentError: null,
hasContentLoading: true,
deletionError: null,
deletionRecord: null,
deletionRole: null,
isAddModalOpen: false,
itemCount: 0,
};
this.loadAccessList = this.loadAccessList.bind(this);
this.handleAddClose = this.handleAddClose.bind(this);
this.handleAddOpen = this.handleAddOpen.bind(this);
this.handleAddSuccess = this.handleAddSuccess.bind(this);
this.handleDeleteCancel = this.handleDeleteCancel.bind(this);
this.handleDeleteConfirm = this.handleDeleteConfirm.bind(this);
this.handleDeleteErrorClose = this.handleDeleteErrorClose.bind(this);
this.handleDeleteOpen = this.handleDeleteOpen.bind(this);
}
componentDidMount () {
this.loadAccessList();
}
componentDidUpdate (prevProps) {
const { location } = this.props;
const prevParams = parseNamespacedQueryString(QS_CONFIG, prevProps.location.search);
const currentParams = parseNamespacedQueryString(QS_CONFIG, location.search);
if (encodeQueryString(currentParams) !== encodeQueryString(prevParams)) {
this.loadAccessList();
}
}
async loadAccessList () {
const { organization, location } = this.props;
const params = parseNamespacedQueryString(QS_CONFIG, location.search);
this.setState({ contentError: null, hasContentLoading: true });
try {
const {
data: {
results: accessRecords = [],
count: itemCount = 0
}
} = await OrganizationsAPI.readAccessList(organization.id, params);
this.setState({ itemCount, accessRecords });
} catch (err) {
this.setState({ contentError: err });
} finally {
this.setState({ hasContentLoading: false });
}
}
handleDeleteOpen (deletionRole, deletionRecord) {
this.setState({ deletionRole, deletionRecord });
}
handleDeleteCancel () {
this.setState({ deletionRole: null, deletionRecord: null });
}
handleDeleteErrorClose () {
this.setState({
deletionError: null,
deletionRecord: null,
deletionRole: null
});
}
async handleDeleteConfirm () {
const { deletionRole, deletionRecord } = this.state;
if (!deletionRole || !deletionRecord) {
return;
}
let promise;
if (typeof deletionRole.team_id !== 'undefined') {
promise = TeamsAPI.disassociateRole(deletionRole.team_id, deletionRole.id);
} else {
promise = UsersAPI.disassociateRole(deletionRecord.id, deletionRole.id);
}
this.setState({ hasContentLoading: true });
try {
await promise.then(this.loadAccessList);
this.setState({
deletionRole: null,
deletionRecord: null
});
} catch (err) {
this.setState({
hasContentLoading: false,
deletionError: err
});
}
}
handleAddClose () {
this.setState({ isAddModalOpen: false });
}
handleAddOpen () {
this.setState({ isAddModalOpen: true });
}
handleAddSuccess () {
this.setState({ isAddModalOpen: false });
this.loadAccessList();
}
render () {
const { organization, i18n } = this.props;
const {
accessRecords,
contentError,
hasContentLoading,
deletionRole,
deletionRecord,
deletionError,
itemCount,
isAddModalOpen
} = this.state;
const canEdit = organization.summary_fields.user_capabilities.edit;
const isDeleteModalOpen = !hasContentLoading && !deletionError && deletionRole;
return (
<Fragment>
<PaginatedDataList
error={contentError}
hasContentLoading={hasContentLoading}
items={accessRecords}
itemCount={itemCount}
itemName="role"
qsConfig={QS_CONFIG}
toolbarColumns={[
{ name: i18n._(t`Name`), key: 'first_name', isSortable: true },
{ name: i18n._(t`Username`), key: 'username', isSortable: true },
{ name: i18n._(t`Last Name`), key: 'last_name', isSortable: true },
]}
renderToolbar={(props) => (
<DataListToolbar
{...props}
additionalControls={canEdit ? [
<ToolbarAddButton key="add" onClick={this.handleAddOpen} />
] : null}
/>
)}
renderItem={accessRecord => (
<OrganizationAccessItem
key={accessRecord.id}
accessRecord={accessRecord}
onRoleDelete={this.handleDeleteOpen}
/>
)}
/>
{isAddModalOpen && (
<AddResourceRole
onClose={this.handleAddClose}
onSave={this.handleAddSuccess}
roles={organization.summary_fields.object_roles}
/>
)}
{isDeleteModalOpen && (
<DeleteRoleConfirmationModal
role={deletionRole}
username={deletionRecord.username}
onCancel={this.handleDeleteCancel}
onConfirm={this.handleDeleteConfirm}
/>
)}
<AlertModal
isOpen={deletionError}
variant="danger"
title={i18n._(t`Error!`)}
onClose={this.handleDeleteErrorClose}
>
{i18n._(t`Failed to delete role`)}
<ErrorDetail error={deletionError} />
</AlertModal>
</Fragment>
);
}
}
export { OrganizationAccess as _OrganizationAccess };
export default withI18n()(withRouter(OrganizationAccess));