cspace-ui
Version:
CollectionSpace user interface for browsers
411 lines (352 loc) • 10.1 kB
JSX
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import { defineMessages, intlShape, FormattedMessage } from 'react-intl';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import RelationButtonBar from './RelationButtonBar';
import WorkflowStateIcon from './WorkflowStateIcon';
import RecordEditorContainer from '../../containers/record/RecordEditorContainer';
import ConfirmRecordUnrelateModal from './ConfirmRecordUnrelateModal';
import { MODAL_CONFIRM_RECORD_UNRELATE } from '../../constants/modalNames';
import { canUnrelate } from '../../helpers/permissionHelpers';
import { getWorkflowState } from '../../helpers/recordDataHelpers';
import styles from '../../../styles/cspace-ui/RelationEditor.css';
export const confirmUnrelateModalName = `RelationEditor-${MODAL_CONFIRM_RECORD_UNRELATE}`;
const messages = defineMessages({
editTitle: {
id: 'relationEditor.editTitle',
defaultMessage: 'Related {recordTypeName}',
},
newTitle: {
id: 'relationEditor.newTitle',
defaultMessage: 'New Related {recordTypeName}',
},
notFound: {
id: 'relationEditor.notFound',
defaultMessage: 'Not Found',
},
noRelation: {
id: 'relationEditor.noRelation',
defaultMessage: 'There is no related record with CSID "{csid}" and type "{recordType}".',
},
});
const propTypes = {
cloneCsid: PropTypes.string,
config: PropTypes.shape({
recordTypes: PropTypes.object,
}),
perms: PropTypes.instanceOf(Immutable.Map),
// TODO: These uses aren't properly detected. Try updating eslint-plugin-react.
/* eslint-disable react/no-unused-prop-types */
subject: PropTypes.shape({
csid: PropTypes.string,
recordType: PropTypes.string,
}),
subjectWorkflowState: PropTypes.string,
object: PropTypes.shape({
csid: PropTypes.string,
recordType: PropTypes.string,
}),
/* eslint-enable react/no-unused-prop-types */
objectData: PropTypes.instanceOf(Immutable.Map),
objectError: PropTypes.instanceOf(Immutable.Map),
openModalName: PropTypes.string,
predicate: PropTypes.string,
findResult: PropTypes.instanceOf(Immutable.Map),
cloneRecord: PropTypes.func,
createRelation: PropTypes.func,
findRelation: PropTypes.func,
closeModal: PropTypes.func,
openModal: PropTypes.func,
unrelate: PropTypes.func,
onClose: PropTypes.func,
onRecordCreated: PropTypes.func,
onRecordTransitioned: PropTypes.func,
onUnmount: PropTypes.func,
onUnrelated: PropTypes.func,
};
const contextTypes = {
intl: intlShape,
};
export default class RelationEditor extends Component {
constructor() {
super();
this.handleCancelButtonClick = this.handleCancelButtonClick.bind(this);
this.handleCloseButtonClick = this.handleCloseButtonClick.bind(this);
this.handleConfirmUnrelateButtonClick = this.handleConfirmUnrelateButtonClick.bind(this);
this.handleUnrelateButtonClick = this.handleUnrelateButtonClick.bind(this);
this.handleModalCancelButtonClick = this.handleModalCancelButtonClick.bind(this);
this.handleRecordCreated = this.handleRecordCreated.bind(this);
this.handleRecordTransitioned = this.handleRecordTransitioned.bind(this);
this.handleSaveCancelled = this.handleSaveCancelled.bind(this);
}
componentDidMount() {
this.initRelation();
}
componentDidUpdate(prevProps) {
const {
subject,
object,
predicate,
findResult,
} = this.props;
const {
subject: prevSubject,
object: prevObject,
predicate: prevPredicate,
findResult: prevFindResult,
} = prevProps;
if (
!isEqual(subject, prevSubject)
|| !isEqual(object, prevObject)
|| predicate !== prevPredicate
|| (!findResult && prevFindResult)
) {
this.initRelation();
}
}
componentWillUnmount() {
const {
onUnmount,
} = this.props;
if (this.unrelateWhenUnmounted) {
this.unrelate();
}
if (onUnmount) {
onUnmount();
}
}
handleCancelButtonClick() {
this.close();
}
handleCloseButtonClick() {
this.close();
}
handleConfirmUnrelateButtonClick() {
const {
closeModal,
} = this.props;
if (closeModal) {
closeModal(false);
}
this.unrelateWhenUnmounted = true;
this.close();
}
handleModalCancelButtonClick() {
const {
closeModal,
} = this.props;
if (closeModal) {
closeModal(false);
}
}
handleUnrelateButtonClick() {
const {
openModal,
} = this.props;
if (openModal) {
openModal(confirmUnrelateModalName);
}
}
handleRecordCreated(newRecordCsid, isNavigating) {
const {
subject,
predicate,
createRelation,
onRecordCreated,
} = this.props;
if (createRelation) {
const object = {
csid: newRecordCsid,
};
return createRelation(subject, object, predicate)
.then(() => (onRecordCreated ? onRecordCreated(newRecordCsid, isNavigating) : null));
}
return null;
}
handleRecordTransitioned(transitionName) {
const {
onRecordTransitioned,
} = this.props;
if (transitionName === 'delete') {
this.close();
}
if (onRecordTransitioned) {
onRecordTransitioned(transitionName);
}
}
handleSaveCancelled() {
// This handles unrelating an unsaved record. The confirm navigation dialog will be shown, and
// if it's cancelled, we shouldn't unrelate.
this.unrelateWhenUnmounted = false;
}
close() {
const {
onClose,
} = this.props;
if (onClose) {
onClose();
}
}
initRelation() {
const {
config,
subject,
object,
predicate,
findRelation,
} = this.props;
if (findRelation && object.csid) {
findRelation(config, subject, object, predicate);
}
}
unrelate() {
const {
config,
subject,
object,
predicate,
unrelate,
onUnrelated,
} = this.props;
if (unrelate) {
unrelate(config, subject, object, predicate)
.then(() => {
if (onUnrelated) {
onUnrelated(subject, object, predicate);
}
});
}
}
renderHeader() {
const {
config,
perms,
subject,
subjectWorkflowState,
object,
objectData,
} = this.props;
const {
intl,
} = this.context;
const recordTypeConfig = config.recordTypes[object.recordType];
const recordTitle = recordTypeConfig.title(objectData, { config, intl });
const recordTypeName = (
<FormattedMessage
{...get(recordTypeConfig, ['messages', 'record', 'name'])}
/>
);
const values = {
recordTypeName,
};
const title = object.csid
? <FormattedMessage {...messages.editTitle} values={values} />
: <FormattedMessage {...messages.newTitle} values={values} />;
const objectWorkflowState = getWorkflowState(objectData);
const objectWorkflowStateIcon = <WorkflowStateIcon value={objectWorkflowState} />;
const isUnrelatable = (
subjectWorkflowState !== 'locked'
&& objectWorkflowState !== 'locked'
&& canUnrelate(subject.recordType, perms, config)
&& canUnrelate(object.recordType, perms, config)
);
return (
<header>
<h3>{title}</h3>
<div>
{objectWorkflowStateIcon}
<h1>{recordTitle}</h1>
<RelationButtonBar
isUnrelatable={isUnrelatable}
object={object}
onCancelButtonClick={this.handleCancelButtonClick}
onCloseButtonClick={this.handleCloseButtonClick}
onUnrelateButtonClick={this.handleUnrelateButtonClick}
/>
</div>
</header>
);
}
renderConfirmRecordUnrelateModal() {
const {
config,
object,
objectData,
openModalName,
} = this.props;
return (
<ConfirmRecordUnrelateModal
config={config}
recordType={object.recordType}
data={objectData}
isOpen={openModalName === confirmUnrelateModalName}
onCancelButtonClick={this.handleModalCancelButtonClick}
onCloseButtonClick={this.handleModalCancelButtonClick}
onUnrelateButtonClick={this.handleConfirmUnrelateButtonClick}
/>
);
}
render() {
const {
cloneRecord,
cloneCsid,
config,
subject,
subjectWorkflowState,
object,
objectError,
findResult,
} = this.props;
if (object.csid) {
if (!findResult) {
return null;
}
let isObjectFound = true;
if (objectError) {
isObjectFound = false;
} else {
const list = findResult.get('rel:relations-common-list');
const count = parseInt(list.get('totalItems'), 10);
if (Number.isNaN(count) || count < 1) {
isObjectFound = false;
}
}
if (!isObjectFound) {
// There is no related object record.
return (
<div>
<h1><FormattedMessage {...messages.notFound} /></h1>
<p>
<FormattedMessage
{...messages.noRelation}
values={{ csid: object.csid, recordType: object.recordType }}
/>
</p>
</div>
);
}
}
return (
<div className={styles.common}>
{this.renderHeader()}
<RecordEditorContainer
cloneCsid={cloneCsid}
config={config}
csid={object.csid}
recordType={object.recordType}
relatedSubjectCsid={subject.csid}
relatedSubjectWorkflowState={subjectWorkflowState}
clone={cloneRecord}
onRecordCreated={this.handleRecordCreated}
onRecordTransitioned={this.handleRecordTransitioned}
onSaveCancelled={this.handleSaveCancelled}
/>
{this.renderConfirmRecordUnrelateModal()}
</div>
);
}
}
RelationEditor.propTypes = propTypes;
RelationEditor.contextTypes = contextTypes;