cspace-ui
Version:
CollectionSpace user interface for browsers
172 lines (142 loc) • 4.5 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import { defineMessages, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import get from 'lodash/get';
import PageSizeChooser from './PageSizeChooser';
import { ERR_API, ERR_NOT_ALLOWED } from '../../constants/errorCodes';
import styles from '../../../styles/cspace-ui/SearchResultSummary.css';
const messages = defineMessages({
error: {
id: 'searchResultSummary.error',
defaultMessage: 'Error: {code}',
},
[ERR_NOT_ALLOWED]: {
id: 'searchResultSummary.ERR_NOT_ALLOWED',
defaultMessage: 'You\'re not allowed to perform this search.',
},
editSearch: {
id: 'searchResultSummary.editSearch',
defaultMessage: 'Revise search',
},
});
const propTypes = {
config: PropTypes.shape({
listTypes: PropTypes.object,
}),
listType: PropTypes.string,
searchDescriptor: PropTypes.instanceOf(Immutable.Map),
searchError: PropTypes.instanceOf(Immutable.Map),
searchResult: PropTypes.instanceOf(Immutable.Map),
renderEditLink: PropTypes.func,
onEditSearchLinkClick: PropTypes.func,
onPageSizeChange: PropTypes.func,
};
const defaultProps = {
renderEditLink: (searchDescriptor, onEditSearchLinkClick) => {
const recordType = searchDescriptor.get('recordType');
const vocabulary = searchDescriptor.get('vocabulary');
const subresource = searchDescriptor.get('subresource');
const searchQuery = searchDescriptor.get('searchQuery');
if (subresource || searchQuery.get('rel')) {
// Services layer does not allow combining related record searches or subresource
// (terms/refs) searches with keywords or advanced search conditions, so don't render an edit
// search link.
return null;
}
const vocabularyPath = vocabulary ? `/${vocabulary}` : '';
const path = `/search/${recordType}${vocabularyPath}`;
return (
<Link to={path} onClick={onEditSearchLinkClick}>
<FormattedMessage {...messages.editSearch} />
</Link>
);
},
};
export default function SearchResultSummary(props) {
const {
config,
listType,
searchDescriptor,
searchError,
searchResult,
renderEditLink,
onEditSearchLinkClick,
onPageSizeChange,
} = props;
if (searchError) {
const error = searchError.toJS();
let { code } = error;
if (code === ERR_API) {
const status = get(error, ['error', 'response', 'status']);
if (status === 401) {
// Convert 401 to ERR_NOT_ALLOWED.
code = ERR_NOT_ALLOWED;
}
}
const message = messages[code] || messages.error;
return (
<div className={styles.error}>
<FormattedMessage {...message} values={error} />
<p>{renderEditLink(searchDescriptor, onEditSearchLinkClick)}</p>
</div>
);
}
let isSearching = false;
let message = null;
let pageSize = null;
if (searchResult) {
const listTypeConfig = config.listTypes[listType];
const { listNodeName } = listTypeConfig;
const list = searchResult.get(listNodeName);
const totalItems = parseInt(list.get('totalItems'), 10);
if (Number.isNaN(totalItems)) {
isSearching = true;
message = (
<FormattedMessage {...listTypeConfig.messages.searching} />
);
} else {
const pageNum = parseInt(list.get('pageNum'), 10);
pageSize = parseInt(list.get('pageSize'), 10);
const startNum = (pageNum * pageSize) + 1;
const endNum = Math.min((pageNum * pageSize) + pageSize, totalItems);
message = (
<FormattedMessage
{...listTypeConfig.messages.resultCount}
values={{
totalItems,
startNum,
endNum,
}}
/>
);
}
}
if (pageSize === null) {
pageSize = searchDescriptor.getIn(['searchQuery', 'size']);
}
const editLink = renderEditLink(searchDescriptor, onEditSearchLinkClick);
const content = (
<div>
{message}
{(message && editLink) ? ' | ' : ''}
{editLink}
</div>
);
const pageSizeChooser = (
<PageSizeChooser
pageSize={pageSize}
onPageSizeChange={onPageSizeChange}
/>
);
const className = isSearching ? styles.searching : styles.normal;
return (
<div className={className}>
{content}
{pageSizeChooser}
</div>
);
}
SearchResultSummary.propTypes = propTypes;
SearchResultSummary.defaultProps = defaultProps;