cspace-ui
Version:
CollectionSpace user interface for browsers
401 lines (337 loc) • 10.2 kB
JSX
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { defineMessages, FormattedMessage } from 'react-intl';
import Immutable from 'immutable';
import get from 'lodash/get';
import { components as inputComponents } from 'cspace-input';
import { Modal } from 'cspace-layout';
import InvokeButton from './InvokeButton';
import InvocationEditorContainer from '../../containers/invocable/InvocationEditorContainer';
import { normalizeInvocationDescriptor } from '../../helpers/invocationHelpers';
import CancelButton from '../navigation/CancelButton';
import styles from '../../../styles/cspace-ui/InvocationModal.css';
import formatPickerStyles from '../../../styles/cspace-ui/InvocationFormatPicker.css';
import { OptionPickerInput } from '../../helpers/configContextInputs';
const { Label } = inputComponents;
const messages = defineMessages({
cancel: {
id: 'invocationModal.cancel',
description: 'Label of the cancel button in the invocation modal.',
defaultMessage: 'Cancel',
},
invoke: {
id: 'invocationModal.run',
description: 'Label of the invoke button in the invocation modal.',
defaultMessage: 'Run',
},
running: {
id: 'invocationModal.running',
description: 'Message displayed in the invocation modal when the report/batch job is running.',
defaultMessage: 'Running…',
},
format: {
id: 'invocationModal.format',
description: 'Label of the output format picker in the invocation modal.',
defaultMessage: 'Format:',
},
});
const propTypes = {
allowedModes: PropTypes.func,
config: PropTypes.shape({
recordTypes: PropTypes.object,
}).isRequired,
csid: PropTypes.string,
data: PropTypes.instanceOf(Immutable.Map),
initialInvocationDescriptor: PropTypes.instanceOf(Immutable.Map),
modeReadOnly: PropTypes.bool,
invocationTargetReadOnly: PropTypes.bool,
isOpen: PropTypes.bool,
isRecordModified: PropTypes.bool,
isRunning: PropTypes.bool,
recordType: PropTypes.oneOf(['report', 'batch']),
readRecord: PropTypes.func,
searchCsid: PropTypes.func,
onCancelButtonClick: PropTypes.func,
onCloseButtonClick: PropTypes.func,
onInvokeButtonClick: PropTypes.func,
};
export default class InvocationModal extends Component {
constructor(props) {
super(props);
this.handleFormatPickerCommit = this.handleFormatPickerCommit.bind(this);
this.handleInvocationDescriptorCommit = this.handleInvocationDescriptorCommit.bind(this);
this.handleInvokeButtonClick = this.handleInvokeButtonClick.bind(this);
this.renderButtonBar = this.renderButtonBar.bind(this);
const {
data,
initialInvocationDescriptor,
} = this.props;
this.state = {
invocationDescriptor: normalizeInvocationDescriptor(initialInvocationDescriptor, data),
};
}
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(nextProps) {
const {
data,
isOpen,
} = this.props;
const {
data: nextData,
isOpen: nextIsOpen,
} = nextProps;
if (!isOpen && nextIsOpen) {
const {
initialInvocationDescriptor,
} = nextProps;
this.setState({
invocationDescriptor: normalizeInvocationDescriptor(initialInvocationDescriptor, nextData),
});
} else if (!data && nextData) {
const { invocationDescriptor } = this.state;
this.setState({
invocationDescriptor: normalizeInvocationDescriptor(invocationDescriptor, nextData),
});
}
}
componentDidUpdate(prevProps) {
const {
csid,
isOpen,
readRecord,
} = this.props;
const {
isOpen: prevIsOpen,
} = prevProps;
if (csid && !prevIsOpen && isOpen) {
if (readRecord) {
readRecord();
}
this.readInvocationItem();
}
}
handleFormatPickerCommit(path, value) {
const {
invocationDescriptor,
} = this.state;
this.setState({
invocationDescriptor: invocationDescriptor.set('outputMIME', value),
});
}
handleInvocationDescriptorCommit(invocationDescriptor) {
this.setState({
invocationDescriptor,
});
}
handleInvokeButtonClick() {
const {
data,
onInvokeButtonClick,
} = this.props;
const {
invocationDescriptor,
} = this.state;
const mode = invocationDescriptor.get('mode');
// Translate the items map to csids.
const items = invocationDescriptor.get('items') || Immutable.Map();
let csid = items.keySeq().toJS();
if (mode === 'single' || mode === 'group') {
[csid] = csid;
}
if (onInvokeButtonClick) {
onInvokeButtonClick(data, invocationDescriptor.set('csid', csid));
}
}
readInvocationItem() {
const {
config,
searchCsid,
} = this.props;
const {
invocationDescriptor,
} = this.state;
if (invocationDescriptor) {
const invocationCsid = invocationDescriptor.get('csid');
if (
invocationCsid
&& typeof invocationCsid === 'string'
&& !invocationDescriptor.get('items')
) {
searchCsid(config, invocationDescriptor.get('recordType'), invocationCsid)
.then((response) => {
let item = get(response, ['data', 'ns2:abstract-common-list', 'list-item']);
if (item) {
item = Immutable.fromJS(item);
} else {
item = Immutable.Map({
csid: invocationCsid,
});
}
this.setState((prevState) => ({
invocationDescriptor: prevState.invocationDescriptor.set(
'items', Immutable.Map({ [invocationCsid]: item }),
),
}));
})
.catch(() => {
const item = Immutable.Map({
csid: invocationCsid,
});
this.setState((prevState) => ({
invocationDescriptor: prevState.invocationDescriptor.set(
'items', Immutable.Map({ [invocationCsid]: item }),
),
}));
});
}
}
}
renderFormatPicker() {
const {
recordType,
data,
} = this.props;
const {
invocationDescriptor,
} = this.state;
if (recordType === 'report') {
let mimeList = [];
if (data) {
mimeList = data.getIn(['document', 'ns2:reports_common', 'supportsOutputMIMEList', 'outputMIME']);
}
const prefilter = (option) => mimeList.includes(option.value);
return (
<div className={formatPickerStyles.common}>
<OptionPickerInput
blankable={false}
label={<Label><FormattedMessage {...messages.format} /></Label>}
source="reportMimeTypes"
prefilter={mimeList ? prefilter : null}
value={invocationDescriptor.get('outputMIME')}
onCommit={this.handleFormatPickerCommit}
// blankable
/>
</div>
);
}
return null;
}
renderButtonBar() {
const {
isRunning,
onCancelButtonClick,
recordType,
} = this.props;
const {
invocationDescriptor,
} = this.state;
const mode = invocationDescriptor.get('mode');
const items = invocationDescriptor.get('items');
return (
<div>
{this.renderFormatPicker()}
<CancelButton
disabled={isRunning}
label={<FormattedMessage {...messages.cancel} />}
onClick={onCancelButtonClick}
/>
<InvokeButton
disabled={
isRunning
|| (mode !== 'nocontext' && (!items || items.isEmpty()))
}
label={<FormattedMessage {...messages.invoke} />}
recordType={recordType}
onClick={this.handleInvokeButtonClick}
/>
</div>
);
}
renderTitle() {
const {
config,
data,
recordType,
} = this.props;
if (!data) {
return ' ';
}
const recordTypeConfig = get(config, ['recordTypes', recordType]);
const titleGetter = get(recordTypeConfig, 'title');
let title = (titleGetter && titleGetter(data));
if (!title) {
const recordTypeMessages = get(recordTypeConfig, ['messages', 'record']);
title = <FormattedMessage {...recordTypeMessages.name} />;
}
return (
<h1>{title}</h1>
);
}
renderInvocationEditor() {
const {
allowedModes,
config,
data,
modeReadOnly,
invocationTargetReadOnly,
isRecordModified,
recordType,
} = this.props;
const {
invocationDescriptor,
} = this.state;
const recordTypeConfig = get(config, ['recordTypes', recordType]);
const invocableNameGetter = get(recordTypeConfig, 'invocableName');
const invocableName = invocableNameGetter && invocableNameGetter(data);
return (
<InvocationEditorContainer
allowedModes={allowedModes}
config={config}
metadata={data}
invocationDescriptor={invocationDescriptor}
modeReadOnly={modeReadOnly}
invocationTargetReadOnly={invocationTargetReadOnly}
invocableName={invocableName}
isInvocationTargetModified={isRecordModified}
recordType={recordType}
onInvocationDescriptorCommit={this.handleInvocationDescriptorCommit}
/>
);
}
render() {
const {
csid,
isOpen,
isRunning,
onCloseButtonClick,
} = this.props;
if (!isOpen || !csid) {
return null;
}
let content;
if (isRunning) {
content = (
<p>
<FormattedMessage {...messages.running} />
</p>
);
} else {
content = this.renderInvocationEditor();
}
return (
<Modal
className={isRunning ? styles.running : styles.common}
isOpen={isOpen}
title={this.renderTitle()}
closeButtonClassName="material-icons"
closeButtonDisabled={isRunning}
closeButtonLabel="close"
renderButtonBar={this.renderButtonBar}
onCloseButtonClick={onCloseButtonClick}
>
{content}
</Modal>
);
}
}
InvocationModal.propTypes = propTypes;