Commit 0dd9797d authored by Loic Huder's avatar Loic Huder
Browse files

Merge branch 'investigation-date-filter' into 'master'

Adds a date filter to the InvestigationTable (single day pick)

See merge request !424
parents db08287a 4f94d870
Pipeline #32081 passed with stages
in 7 minutes and 32 seconds
import React from 'react';
import { Glyphicon, Button } from 'react-bootstrap';
import DayPickerInput from 'react-day-picker/DayPickerInput';
import { formatDate, parseDate } from 'react-day-picker/moment';
import { INVESTIGATION_DATE_FORMAT } from '../../constants';
import styles from './InvestigationDateFilter.module.css';
function InvestigationDateFilter(props) {
const { rootClassName, value, onDayChange, onClear } = props;
return (
<div className={rootClassName}>
<DayPickerInput
inputProps={{ className: styles.dateFilterInput }}
value={value}
placeholder="Filter by start date"
format={INVESTIGATION_DATE_FORMAT}
formatDate={formatDate}
parseDate={parseDate}
onDayChange={onDayChange}
/>
<Button
disabled={!value}
onClick={onClear}
bsStyle="link"
className={styles.clearButton}
>
<Glyphicon glyph="remove" />
</Button>
</div>
);
}
export default InvestigationDateFilter;
.clearButton {
color: inherit;
padding-left: 6px;
}
.clearButton:hover {
color: darkslategray;
}
.dateFilterInput {
padding: 7px 0;
}
import React from 'react';
import React, { useState } from 'react';
import { Col, Glyphicon, Grid, Row } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import moment from 'moment';
import {
DATASET_COUNT,
FILE_COUNT,
SAMPLE_COUNT,
VOLUME,
} from '../../constants/parameterTypes';
import DOIBadge from '../doi/DOIBadge';
......@@ -12,290 +11,279 @@ import ResponsiveTable from '../Table/ResponsiveTable';
import InvestigationWidget from './InvestigationWidget';
import { beamlineFormatter, dateFormatter, nameFormatter } from './utils';
import { stringifyBytesSize } from '../../helpers';
import InvestigationDateFilter from './InvestigationDateFilter';
import styles from './InvestigationTable.module.css';
class InvestigationTable extends React.Component {
logbookFormatter(cell, investigation) {
return (
<Link to={`/investigation/${investigation.id}/events`}>
<Glyphicon glyph="list-alt" />
</Link>
);
function volumeFormatter(cell, investigation) {
const volume = investigation.parameters.find((o) => o.name === VOLUME);
if (volume && volume.value && volume.value !== 0) {
return stringifyBytesSize(volume.value);
}
}
volumeFormatter(cell, investigation) {
const volume = investigation.parameters.find((o) => o.name === VOLUME);
if (volume && volume.value && volume.value !== 0) {
return stringifyBytesSize(volume.value);
}
}
function experimentFormatter(cell, investigation, rowIndex, extraData) {
return (
<Grid style={{ textAlign: 'center' }}>
<Row className="show-grid">
<Col xs={12} md={12}>
{nameFormatter(
investigation,
extraData.props.withProposalLinks,
extraData.props.user.isAdministrator
)}
</Col>
</Row>
<Row className="show-grid">
<Col xs={12} md={12}>
<span style={{ fontWeight: 'bold' }}>
{beamlineFormatter(investigation, investigation.visitId)}
</span>
</Col>
</Row>
<Row className="show-grid">
<Col xs={12}>
<div style={{ color: 'gray', fontStyle: 'italic' }}>
{investigation.summary}
</div>
</Col>
</Row>
<Row className="show-grid" style={{ fontSize: 10 }}>
<Col xs={12}>
<DOIBadge doi={investigation.doi} />
</Col>
</Row>
</Grid>
);
}
experimentFormatter(cell, investigation, rowIndex, extraData) {
return (
<Grid style={{ textAlign: 'center' }}>
<Row className="show-grid">
<Col xs={12} md={12}>
{nameFormatter(
investigation,
extraData.props.withProposalLinks,
extraData.props.user.isAdministrator
)}
</Col>
</Row>
<Row className="show-grid">
<Col xs={12} md={12}>
<span style={{ fontWeight: 'bold' }}>
{beamlineFormatter(investigation, investigation.visitId)}
</span>
</Col>
</Row>
<Row className="show-grid">
<Col xs={12}>
<div style={{ color: 'gray', fontStyle: 'italic' }}>
{investigation.summary}
</div>
</Col>
</Row>
<Row className="show-grid" style={{ fontSize: 10 }}>
<Col xs={12}>
<DOIBadge doi={investigation.doi} />
</Col>
</Row>
</Grid>
);
/**
* This looks into the parameters of the investigation
*/
function getParameter(investigation, parameterName) {
const parameter = investigation.parameters.find(
(o) => o.name === parameterName
);
if (parameter && parameter.value) {
return parameter.value;
}
}
/**
* This looks into the parameters of the investigation
*/
getParameter(investigation, parameterName) {
const parameter = investigation.parameters.find(
(o) => o.name === parameterName
);
if (parameter && parameter.value) {
return parameter.value;
}
}
function fileCountFormatter(cell, investigation) {
const fileCount = getParameter(investigation, FILE_COUNT);
sampleCountFormatter(cell, investigation, extraData) {
const sampleCount = extraData.getParameter(investigation, SAMPLE_COUNT);
if (sampleCount) {
if (sampleCount !== 0) {
return sampleCount;
}
}
if (!fileCount) {
return undefined;
}
fileCountFormatter(cell, investigation, rowIndex, extraData) {
const fileCount = extraData.getParameter(investigation, FILE_COUNT);
if (fileCount) {
if (fileCount !== 0) {
return (
<span style={{ width: 50, textAlign: 'right', float: 'left' }}>
{fileCount}
</span>
);
}
}
}
return (
<span style={{ width: 50, textAlign: 'right', float: 'left' }}>
{fileCount}
</span>
);
}
datasetCountFormatter(cell, investigation, rowIndex, extraData) {
const datasetCount = investigation.parameters.find(
(o) => o.name === DATASET_COUNT
function datasetCountFormatter(cell, investigation) {
const datasetCount = investigation.parameters.find(
(o) => o.name === DATASET_COUNT
);
if (datasetCount && datasetCount.value) {
return (
<>
<span style={{ width: 40, textAlign: 'right', float: 'left' }}>
{datasetCount.value}
</span>
<span
style={{
width: 70,
marginLeft: 5,
float: 'left',
fontStyle: 'italic',
color: '#999',
fontSize: 12,
}}
>
({volumeFormatter(cell, investigation)})
</span>
</>
);
if (datasetCount && datasetCount.value && datasetCount.value !== 0) {
return (
<>
<span style={{ width: 40, textAlign: 'right', float: 'left' }}>
{datasetCount.value}
</span>
<span
style={{
width: 70,
marginLeft: 5,
float: 'left',
fontStyle: 'italic',
color: '#999',
fontSize: 12,
}}
>
({extraData.volumeFormatter(cell, investigation, extraData)})
</span>
</>
);
}
}
}
/**
* It return true if releaseDate < now
*/
isReleased(investigation) {
return !!investigation && !!investigation.releaseDate;
}
function getLgHeaderStyle(width, hidden) {
return {
xs: { hidden: true },
sm: { hidden: true },
md: { hidden: true },
lg: { hidden, width, textAlign: 'center' },
};
}
getColumns() {
function getLgHeaderStyle(width, hidden) {
return {
xs: { hidden: true },
sm: { hidden: true },
function getColumns(props) {
return [
{
text: 'id',
dataField: 'id',
hidden: true,
},
{
text: 'Experiment',
dataField: 'name',
formatter: experimentFormatter,
formatExtraData: props,
sort: true,
hidden: false,
headerStyle: () => ({ width: '50%', textAlign: 'center' }),
responsiveHeaderStyle: {
xs: { width: '100%' },
sm: { width: '100%' },
md: { hidden: true },
lg: { hidden, width, textAlign: 'center' },
};
}
return [
{
text: 'id',
dataField: 'id',
hidden: true,
},
{
text: 'Experiment',
dataField: 'name',
formatter: this.experimentFormatter,
formatExtraData: this,
sort: true,
hidden: false,
headerStyle: () => ({ width: '50%', textAlign: 'center' }),
responsiveHeaderStyle: {
xs: { width: '100%' },
sm: { width: '100%' },
md: { hidden: true },
lg: { hidden: true },
},
},
{
text: 'Proposal',
dataField: 'name',
formatter: (_, investigation, __, props) =>
nameFormatter(
investigation,
props.withProposalLinks,
props.user.isAdministrator
),
formatExtraData: this.props,
sort: true,
hidden: false,
headerStyle: () => ({ width: '50%', textAlign: 'center' }),
responsiveHeaderStyle: {
xs: { hidden: true },
sm: { hidden: true },
md: { width: 140, textAlign: 'center' },
lg: { width: 140, textAlign: 'center' },
},
lg: { hidden: true },
},
{
text: 'Beamline',
dataField: 'visitId',
formatter: (visitId, investigation) => (
<span style={{ fontWeight: 'bold' }}>
{beamlineFormatter(investigation, visitId)}
</span>
),
sort: true,
responsiveHeaderStyle: {
xs: { hidden: true },
sm: { hidden: true },
md: { width: 110, textAlign: 'center' },
lg: { width: 110, textAlign: 'center' },
},
},
{
text: 'Start Date',
dataField: 'startDate',
formatter: dateFormatter,
sort: true,
responsiveHeaderStyle: getLgHeaderStyle(110, false),
},
{
text: 'Title',
dataField: 'summary',
sort: true,
responsiveHeaderStyle: {
xs: { hidden: true },
sm: { hidden: true },
},
},
{
text: 'Datasets',
dataField: 'datasets',
formatter: this.datasetCountFormatter,
formatExtraData: this,
responsiveHeaderStyle: getLgHeaderStyle(
130,
!this.props.showInvestigationStats
),
},
{
text: 'Files',
hidden: !this.props.user.isAdministrator,
dataField: 'dummy-1',
isDummyField: true,
formatter: this.fileCountFormatter,
formatExtraData: this,
responsiveHeaderStyle: getLgHeaderStyle(
80,
!this.props.showInvestigationStats
},
{
text: 'Proposal',
dataField: 'name',
formatter: (_, investigation, __, props) =>
nameFormatter(
investigation,
props.withProposalLinks,
props.user.isAdministrator
),
formatExtraData: props,
sort: true,
hidden: false,
headerStyle: () => ({ width: '50%', textAlign: 'center' }),
responsiveHeaderStyle: {
xs: { hidden: true },
sm: { hidden: true },
md: { width: 140, textAlign: 'center' },
lg: { width: 140, textAlign: 'center' },
},
{
text: 'Release',
dataField: 'releaseDate',
formatter: dateFormatter,
sort: true,
responsiveHeaderStyle: getLgHeaderStyle(110, false),
},
{
text: 'Beamline',
dataField: 'visitId',
formatter: (visitId, investigation) => (
<span style={{ fontWeight: 'bold' }}>
{beamlineFormatter(investigation, visitId)}
</span>
),
sort: true,
responsiveHeaderStyle: {
xs: { hidden: true },
sm: { hidden: true },
md: { width: 110, textAlign: 'center' },
lg: { width: 110, textAlign: 'center' },
},
{
text: 'DOI',
dataField: 'doi',
formatter: (doi) => <DOIBadge doi={doi} />,
sort: true,
responsiveHeaderStyle: getLgHeaderStyle(260, false),
},
{
text: 'Start Date',
dataField: 'startDate',
formatter: dateFormatter,
sort: true,
responsiveHeaderStyle: getLgHeaderStyle(110, false),
},
{
text: 'Title',
dataField: 'summary',
sort: true,
responsiveHeaderStyle: {
xs: { hidden: true },
sm: { hidden: true },
},
];
}
},
{
text: 'Datasets',
dataField: 'datasets',
formatter: datasetCountFormatter,
responsiveHeaderStyle: getLgHeaderStyle(
130,
!props.showInvestigationStats
),
},
{
text: 'Files',
hidden: !props.user.isAdministrator,
dataField: 'dummy-1',
isDummyField: true,
formatter: fileCountFormatter,
responsiveHeaderStyle: getLgHeaderStyle(
80,
!props.showInvestigationStats
),
},
{
text: 'Release',
dataField: 'releaseDate',
formatter: dateFormatter,
sort: true,
responsiveHeaderStyle: getLgHeaderStyle(110, false),
},
{
text: 'DOI',
dataField: 'doi',
formatter: (doi) => <DOIBadge doi={doi} />,
sort: true,
responsiveHeaderStyle: getLgHeaderStyle(260, false),
},
];
}
render() {
const expandRow = {
showExpandColumn: true,
//expandByColumnOnly: true,
expanded: this.props.expanded,
expandColumnPosition: 'right',
expandHeaderColumnRenderer: ({ isAnyExpands }) => {
if (isAnyExpands) {
return (
<span style={{ fontSize: 18 }}>
<Glyphicon glyph="zoom-out" />
</span>
);
}
function InvestigationTable(props) {
const { expanded, user, investigations } = props;
const [dateFilter, setDateFilter] = useState();
const expandRow = {
showExpandColumn: true,
expanded,
expandColumnPosition: 'right',
expandHeaderColumnRenderer: ({ isAnyExpands }) => {
if (isAnyExpands) {
return (
<span style={{ fontSize: 18 }}>
<Glyphicon glyph="zoom-in" />
<Glyphicon glyph="zoom-out" />
</span>
);
},
expandColumnRenderer: ({ expanded }) => {
if (expanded) {
return <Glyphicon glyph="zoom-out" />;
}
return <Glyphicon glyph="zoom-in" />;
},
renderer: (investigation) => (
<InvestigationWidget
investigation={investigation}
sessionId={this.props.user.sessionId}
/>
),
};
}
return (
<span style={{ fontSize: 18 }}>
<Glyphicon glyph="zoom-in" />
</span>
);
},
expandColumnRenderer: ({ expanded }) => {
if (expanded) {
return <Glyphicon glyph="zoom-out" />;
}
return <Glyphicon glyph="zoom-in" />;
},
renderer: (investigation) => (
<InvestigationWidget
investigation={investigation}
sessionId={user.sessionId}
/>
),
};
return (
return (
<>
<div className={styles.wrapper}>
<InvestigationDateFilter
rootClassName={styles.filter}
value={dateFilter && dateFilter.toDate()}
onDayChange={(date) => setDateFilter(moment(date))}
onClear={() => setDateFilter(undefined)}
/>
</div>
<ResponsiveTable
data={this.props.investigations}
columns={this.getColumns()}
data={investigations.filter(
(inv) => !dateFilter || dateFilter.isSame(inv.startDate, 'day')