import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Dropzone from 'react-dropzone'; import OHIF from '@ohif/core'; import { withRouter } from 'react-router-dom'; import { withTranslation } from 'react-i18next'; import { StudyList } from '@ohif/ui'; import ConnectedHeader from '../connectedComponents/ConnectedHeader.js'; import * as RoutesUtil from '../routes/routesUtil'; import moment from 'moment'; import isEqual from 'lodash.isequal'; import ConnectedDicomFilesUploader from '../googleCloud/ConnectedDicomFilesUploader'; import ConnectedDicomStorePicker from '../googleCloud/ConnectedDicomStorePicker'; import filesToStudies from '../lib/filesToStudies.js'; const { urlUtil: UrlUtil } = OHIF.utils; // Contexts import UserManagerContext from '../context/UserManagerContext'; import WhiteLabellingContext from '../context/WhiteLabellingContext'; import AppContext from '../context/AppContext'; class StudyListWithData extends Component { static contextType = AppContext; state = { searchOutdated: true, studies: [], searchingStudies: false, error: null, modalComponentId: null, }; static propTypes = { filters: PropTypes.object, patientId: PropTypes.string, server: PropTypes.object, user: PropTypes.object, history: PropTypes.object, studyListFunctionsEnabled: PropTypes.bool, }; static defaultProps = { studyListFunctionsEnabled: true, }; static rowsPerPage = 25; static defaultSort = { field: 'patientName', order: 'desc' }; static studyListDateFilterNumDays = 25000; // TODO: put this in the settings static defaultStudyDateFrom = moment() .subtract(StudyListWithData.studyListDateFilterNumDays, 'days') .toDate(); static defaultStudyDateTo = new Date(); static defaultSearchData = { currentPage: 0, rowsPerPage: StudyListWithData.rowsPerPage, studyDateFrom: StudyListWithData.defaultStudyDateFrom, studyDateTo: StudyListWithData.defaultStudyDateTo, sortData: StudyListWithData.defaultSort, }; componentDidMount() { const { appConfig = {} } = this.context; // TODO: Avoid using timepoints here //const params = { studyInstanceUids, seriesInstanceUids, timepointId, timepointsFilter={} }; if (!this.props.server && appConfig.enableGoogleCloudAdapter) { this.setState({ modalComponentId: 'DicomStorePicker', searchOutdated: false, }); } else { this.searchForStudies({ ...StudyListWithData.defaultSearchData, ...(this.props.filters || {}), }); } } componentDidUpdate(prevProps, prevState) { const hasNewServer = !isEqual(this.props.server, prevProps.server); const { searchOutdated, searchingStudies } = this.state; if (!searchingStudies) { if (hasNewServer) { const { appConfig = {} } = this.context; const newState = { searchOutdated: true, studies: null, }; if (appConfig.enableGoogleCloudAdapter) { newState.modalComponentId = null; } this.setState(newState); } if (searchOutdated) { this.searchForStudies(); } } } searchForStudies = (searchData = StudyListWithData.defaultSearchData) => { const { server = {} } = this.props; const filter = { patientId: searchData.patientId, patientName: searchData.patientName, accessionNumber: searchData.accessionNumber, studyDescription: searchData.studyDescription, modalitiesInStudy: searchData.modalities, studyDateFrom: searchData.studyDateFrom, studyDateTo: searchData.studyDateTo, limit: searchData.rowsPerPage, offset: searchData.currentPage * searchData.rowsPerPage, }; if (server.supportsFuzzyMatching) { filter.fuzzymatching = true; } // TODO: add sorting const promise = OHIF.studies.searchStudies(server, filter); this.setState({ searchingStudies: true, }); promise .then(studies => { if (!studies) { studies = []; } const { field, order } = searchData.sortData; let sortedStudies = studies.map(study => { if (!moment(study.studyDate, 'MMM DD, YYYY', true).isValid()) { study.studyDate = moment(study.studyDate, 'YYYYMMDD').format( 'MMM DD, YYYY' ); } return study; }); sortedStudies.sort(function(a, b) { let fieldA = a[field]; let fieldB = b[field]; if (field === 'studyDate') { fieldA = moment(fieldA).toISOString(); fieldB = moment(fieldB).toISOString(); } if (order === 'desc') { if (fieldA < fieldB) { return -1; } if (fieldA > fieldB) { return 1; } return 0; } else { if (fieldA > fieldB) { return -1; } if (fieldA < fieldB) { return 1; } return 0; } }); this.setState({ studies: sortedStudies, searchingStudies: false, searchOutdated: false, }); }) .catch(error => { this.setState({ error: true, searchingStudies: false, searchOutdated: false, }); throw new Error(error); }); }; onImport = () => { this.openModal('DicomFilesUploader'); }; openModal = modalComponentId => { this.setState({ modalComponentId, }); }; closeModal = () => { this.setState({ modalComponentId: null }); }; onSelectItem = studyInstanceUID => { const { appConfig = {} } = this.context; const { server } = this.props; const viewerPath = RoutesUtil.parseViewerPath(appConfig, server, { studyInstanceUids: studyInstanceUID, }); if (UrlUtil.paramString.isValidPath(viewerPath)) { this.props.history.push(viewerPath); } }; updateURL(modalOpened) { if (!modalOpened) { const { appConfig = {} } = this.context; const { server } = this.props; const listPath = RoutesUtil.parseStudyListPath(appConfig, server); if (UrlUtil.paramString.isValidPath(listPath)) { const { location = {} } = this.props.history; if (location.pathname !== listPath) { this.props.history.replace(listPath); } } } } onSearch = searchData => { this.searchForStudies(searchData); }; closeModals = () => { this.setState({ modalComponentId: null, }); }; render() { const { appConfig = {} } = this.context; const onDrop = async acceptedFiles => { try { const studies = await filesToStudies(acceptedFiles); this.setState({ studies }); } catch (error) { this.setState({ error }); } }; if (this.state.error) { return
Error: {JSON.stringify(this.state.error)}
; } let healthCareApiButtons = null; let healthCareApiWindows = null; if (appConfig.enableGoogleCloudAdapter) { const modalOpened = this.state.modalComponentId === 'DicomStorePicker'; this.updateURL(modalOpened); healthCareApiWindows = ( ); healthCareApiButtons = (
); } const studyList = (
{this.state.studies || this.state.searchingStudies ? ( {this.props.studyListFunctionsEnabled ? ( ) : null} {healthCareApiButtons} {healthCareApiWindows} ) : ( {({ getRootProps, getInputProps }) => (

{this.props.t( 'Drag and Drop DICOM files here to load them in the Viewer' )}

{this.props.t("Or click to load the browser's file selector")}

)}
)}
); return ( <> {whiteLabelling => ( {userManager => ( {whiteLabelling.logoComponent} )} )} {studyList} ); } } export default withRouter(withTranslation('Common')(StudyListWithData));