diff --git a/OHIFViewer-react/package.json b/OHIFViewer-react/package.json index b046c78e7..c2fad2fe5 100644 --- a/OHIFViewer-react/package.json +++ b/OHIFViewer-react/package.json @@ -13,7 +13,7 @@ "hammerjs": "^2.0.8", "lodash.debounce": "^4.0.8", "moment": "^2.22.2", - "ohif-core": "^0.1.1", + "ohif-core": "^0.1.2", "prop-types": "^15.6.2", "query-string": "^6.2.0", "react": "^16.4.1", @@ -23,7 +23,7 @@ "react-router": "^4.3.1", "react-router-dom": "^4.3.1", "react-scripts": "^2.1.1", - "react-viewerbase": "^0.1.5", + "react-viewerbase": "^0.1.6", "redux": "^4.0.1" }, "scripts": { diff --git a/OHIFViewer-react/src/App.js b/OHIFViewer-react/src/App.js index 2fef83862..489a2e3b9 100644 --- a/OHIFViewer-react/src/App.js +++ b/OHIFViewer-react/src/App.js @@ -2,13 +2,13 @@ import React, { Component } from 'react'; import { withRouter } from 'react-router'; import { Route, Switch } from 'react-router-dom'; import ViewerRouting from "./ViewerRouting.js"; +import StudyListRouting from './StudyListRouting.js'; import StandaloneRouting from './StandaloneRouting.js'; import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js'; import './App.css'; import './variables.css'; // TODO: figure out how to change themes dynamically import './theme-tide.css'; -import { StudyList } from 'react-viewerbase'; const reload = () => window.location.reload(); @@ -43,14 +43,14 @@ class App extends Component { diff --git a/OHIFViewer-react/src/ConnectedStudyList.js b/OHIFViewer-react/src/ConnectedStudyList.js new file mode 100644 index 000000000..eb5f3c991 --- /dev/null +++ b/OHIFViewer-react/src/ConnectedStudyList.js @@ -0,0 +1,21 @@ +import { connect } from 'react-redux'; + +import StudyListWithData from './StudyListWithData.js'; + +const isActive = (a) => a.active === true; + +const mapStateToProps = (state, ownProps) => { + const activeServer = state.servers.servers.find(isActive); + + return { + server: activeServer, + ...ownProps + }; +}; + +const ConnectedStudyList = connect( + mapStateToProps, + null +)(StudyListWithData); + +export default ConnectedStudyList; diff --git a/OHIFViewer-react/src/FlexboxLayout/ConnectedFlexboxLayout.js b/OHIFViewer-react/src/FlexboxLayout/ConnectedFlexboxLayout.js new file mode 100644 index 000000000..09966d3ad --- /dev/null +++ b/OHIFViewer-react/src/FlexboxLayout/ConnectedFlexboxLayout.js @@ -0,0 +1,16 @@ +import { connect } from 'react-redux'; +import FlexboxLayout from './FlexboxLayout'; + +const mapStateToProps = state => { + return { + leftSidebarOpen: state.ui.leftSidebarOpen, + rightSidebarOpen: state.ui.rightSidebarOpen + }; +}; + +const ConnectedFlexboxLayout = connect( + mapStateToProps, + null +)(FlexboxLayout); + +export default ConnectedFlexboxLayout; diff --git a/OHIFViewer-react/src/FlexboxLayout/ConnectedToolbarRow.js b/OHIFViewer-react/src/FlexboxLayout/ConnectedToolbarRow.js new file mode 100644 index 000000000..2b73baf66 --- /dev/null +++ b/OHIFViewer-react/src/FlexboxLayout/ConnectedToolbarRow.js @@ -0,0 +1,29 @@ +import { connect } from 'react-redux'; +import ToolbarRow from './ToolbarRow'; +import { setLeftSidebarOpen, setRightSidebarOpen } from '../redux/actions.js'; + +const mapStateToProps = state => { + return { + leftSidebarOpen: state.ui.leftSidebarOpen, + rightSidebarOpen: state.ui.rightSidebarOpen + }; +}; + + +const mapDispatchToProps = dispatch => { + return { + setLeftSidebarOpen: state => { + dispatch(setLeftSidebarOpen(state)) + }, + setRightSidebarOpen: state => { + dispatch(setRightSidebarOpen(state)) + } + }; +}; + +const ConnectedToolbarRow = connect( + mapStateToProps, + mapDispatchToProps +)(ToolbarRow); + +export default ConnectedToolbarRow; diff --git a/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js b/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js index 015b736c4..256aaf720 100644 --- a/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js +++ b/OHIFViewer-react/src/FlexboxLayout/FlexboxLayout.js @@ -1,6 +1,5 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import OHIF from 'ohif-core'; import cornerstone from 'cornerstone-core'; import { StudyBrowser } from 'react-viewerbase'; import ViewerMain from './ViewerMain.js'; @@ -81,12 +80,12 @@ class FlexboxLayout extends Component { } getThumbnailsFromImageIds() { - const studyThumbnails = this.state.studiesForBrowser.forEach(function (study) { + this.state.studiesForBrowser.forEach(function (study) { const { studyInstanceUid } = study; study.thumbnails.forEach(function (displaySet) { const imageId = displaySet.imageId; - const {displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames} = displaySet; + const { displaySetInstanceUid } = displaySet; cornerstone.loadAndCacheImage(imageId).then((image) => { loadSuccess.call(this, image, studyInstanceUid, displaySetInstanceUid); diff --git a/OHIFViewer-react/src/IHEInvokeImageDisplay.js b/OHIFViewer-react/src/IHEInvokeImageDisplay.js index ed8cbc116..209b92059 100644 --- a/OHIFViewer-react/src/IHEInvokeImageDisplay.js +++ b/OHIFViewer-react/src/IHEInvokeImageDisplay.js @@ -5,7 +5,7 @@ import ViewerFromStudyData from "./ViewerFromStudyData.js"; function IHEInvokeImageDisplay({ match }) { const requestType = match.params.query.requestType; let studyInstanceUids; - let patientUids; + //let patientUids; let displayStudyList = false; if (requestType === "STUDY") { @@ -15,7 +15,7 @@ function IHEInvokeImageDisplay({ match }) { const decodedData = window.atob(uids); studyInstanceUids = decodedData.split(';'); } else if (requestType === "PATIENT") { - patientUids = this.params.query.patientID.split(';'); + //patientUidspatientUids = this.params.query.patientID.split(';'); displayStudyList = true } else { displayStudyList = true diff --git a/OHIFViewer-react/src/StudyListRouting.js b/OHIFViewer-react/src/StudyListRouting.js new file mode 100644 index 000000000..afdaeb8bf --- /dev/null +++ b/OHIFViewer-react/src/StudyListRouting.js @@ -0,0 +1,24 @@ +import React from "react"; +import PropTypes from "prop-types"; +import ConnectedStudyList from "./ConnectedStudyList"; + +// TODO: Move to react-viewerbase + +function StudyListRouting({ match }) { + // TODO: Figure out which filters we want to pass in via a URL + //const { patientId } = match.params; + + return ( + + ); +} + +StudyListRouting.propTypes = { + match: PropTypes.shape({ + params: PropTypes.shape({ + patientIds: PropTypes.string, + }) + }) +}; + +export default StudyListRouting; diff --git a/OHIFViewer-react/src/StudyListWithData.js b/OHIFViewer-react/src/StudyListWithData.js new file mode 100644 index 000000000..455c85067 --- /dev/null +++ b/OHIFViewer-react/src/StudyListWithData.js @@ -0,0 +1,120 @@ +import React, {Component} from "react"; +import PropTypes from "prop-types"; +import OHIF from 'ohif-core'; +import { withRouter } from 'react-router-dom'; +import { StudyList } from "react-viewerbase"; +import Header from "./Header"; + +class StudyListWithData extends Component { + constructor(props) { + super(props); + + this.state = { + searchData: {}, + studies: null, + error: null + }; + + this.pageSize = 5; + this.defaultSort = { field: 'patientName', order: 'desc' }; + } + + componentDidMount() { + // TODO: Avoid using timepoints here + //const params = { studyInstanceUids, seriesInstanceUids, timepointId, timepointsFilter={} }; + + this.searchForStudies(); + } + + searchForStudies = (searchData = { + currentPage: 0, + pageSize: 5 + }) => { + const { server } = this.props; + console.log(searchData); + const filter = { + patientId: searchData.patientId, + patientName: searchData.patientName, + accessionNumber: searchData.accessionNumber, + studyDescription: searchData.studyDescription, + modalitiesInStudy: searchData.modalitiesInStudy, + limit: searchData.currentPage * searchData.pageSize + searchData.pageSize, + offset: searchData.currentPage * searchData.pageSize + }; + + // TODO: add sorting + const promise = OHIF.studies.searchStudies(server, filter) + + // Render the viewer when the data is ready + promise.then(studies => { + if (!studies) { + studies = []; + } + + // TODO: Fix casing of this property! + const fixedStudies = studies.map(study => { + const fixedStudy = study; + fixedStudy.studyInstanceUID = study.studyInstanceUid; + + return fixedStudy; + }) + + this.setState({ + studies: fixedStudies, + }); + }).catch(error => { + this.setState({ + error: true, + }); + + console.error(error); + }); + } + + onImport = () => { + console.log('onImport'); + } + + onSelectItem = (studyInstanceUID) => { + console.log('onSelectItem'); + + console.log('studyInstanceUID'); + + this.props.history.push(`/viewer/${studyInstanceUID}`); + } + + onSearch = (searchData) => { + // TODO: Update search filters + this.searchForStudies(searchData); + } + + render() { + if (this.state.error) { + return (
Error: {JSON.stringify(this.state.error)}
); + } else if (this.state.studies === null) { + return (
Loading...
); + } + + const studyCount = this.state.studies ? this.state.studies.length : 0; + + return (<> +
+ + + ); + } +} + +StudyListWithData.propTypes = { + patientId: PropTypes.string, + server: PropTypes.object +}; + +export default withRouter(StudyListWithData); diff --git a/OHIFViewer-react/src/Viewer/Viewer.js b/OHIFViewer-react/src/Viewer/Viewer.js index 7a43189ba..102028286 100644 --- a/OHIFViewer-react/src/Viewer/Viewer.js +++ b/OHIFViewer-react/src/Viewer/Viewer.js @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import cornerstone from 'cornerstone-core'; import cornerstoneTools from 'cornerstone-tools'; import OHIF from 'ohif-core'; -import { CineDialog } from 'react-viewerbase'; +//import { CineDialog } from 'react-viewerbase'; import Header from '../Header' import ConnectedFlexboxLayout from '../FlexboxLayout/ConnectedFlexboxLayout.js'; diff --git a/OHIFViewer-react/src/components/Dropdown/Dropdown.js b/OHIFViewer-react/src/components/Dropdown/Dropdown.js index ec2d0d632..d046b0de5 100644 --- a/OHIFViewer-react/src/components/Dropdown/Dropdown.js +++ b/OHIFViewer-react/src/components/Dropdown/Dropdown.js @@ -8,7 +8,7 @@ class Dropdown extends Component { } renderList = () => { - const { list, link, align } = this.props + const { list, align } = this.props if (!this.state.open) { return null @@ -80,8 +80,6 @@ class Dropdown extends Component { } render() { - const { open } = this.state - return (
this.node = node}>
diff --git a/OHIFViewer-react/src/index.js b/OHIFViewer-react/src/index.js index 4f9b8f5ec..619ade962 100644 --- a/OHIFViewer-react/src/index.js +++ b/OHIFViewer-react/src/index.js @@ -18,9 +18,12 @@ const servers = { dicomWeb: [ { "name": "DCM4CHEE", - "wadoUriRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado", - "qidoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs", - "wadoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs", + //"wadoUriRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado", + //"qidoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs", + //"wadoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs", + "wadoUriRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/wado", + "qidoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs", + "wadoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs", "qidoSupportsIncludeField": true, "imageRendering": "wadors", "thumbnailRendering": "wadors", @@ -29,7 +32,8 @@ const servers = { "logRequests": true, "logResponses": false, "logTiming": true, - "auth": "admin:admin" + //"auth": "admin:admin" + "auth": "cloud:healthcare" } } ] diff --git a/OHIFViewer-react/src/redux/actions.js b/OHIFViewer-react/src/redux/actions.js new file mode 100644 index 000000000..9ae04a9fd --- /dev/null +++ b/OHIFViewer-react/src/redux/actions.js @@ -0,0 +1,16 @@ +export const setLeftSidebarOpen = state => ({ + type: 'SET_LEFT_SIDEBAR_OPEN', + state +}); + +export const setRightSidebarOpen = state => ({ + type: 'SET_RIGHT_SIDEBAR_OPEN', + state +}); + +const actions = { + setLeftSidebarOpen, + setRightSidebarOpen +}; + +export default actions; diff --git a/OHIFViewer-react/src/sha.js b/OHIFViewer-react/src/sha.js index 456722165..9d6a5da56 100644 --- a/OHIFViewer-react/src/sha.js +++ b/OHIFViewer-react/src/sha.js @@ -1 +1 @@ -export default 'c08d12cb36ad7b1ff310b4970a84bc7e6a14e6ed'; +export default '4fbc208b41fcaa0f957a49409cb55e91c6397d03'; diff --git a/OHIFViewer-react/yarn.lock b/OHIFViewer-react/yarn.lock index 96fda6abf..d236d16ba 100644 --- a/OHIFViewer-react/yarn.lock +++ b/OHIFViewer-react/yarn.lock @@ -6832,10 +6832,10 @@ obuf@^1.0.0, obuf@^1.1.1: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -ohif-core@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ohif-core/-/ohif-core-0.1.1.tgz#594b5a7969c11e5d5eaa3890a384f39409543d3b" - integrity sha512-Ib/lI9fioMiqt5DXmZ8iXtiSne8Z2ubQoh/ePAwgZPljXfMvSY1cUpQK+O90pZqBh444KDolYX00OrJIIbSRgg== +ohif-core@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ohif-core/-/ohif-core-0.1.2.tgz#897c63b73de7a139567d0a6cfe98426aa25e1cb9" + integrity sha512-KkiN14ctO3Dp4fJkjndSyUH5suBVcMVxetZdOVGrSlxWbnpCg6Hd2FPZpP6PcYPE4yhWNvUyV5dfP4Qhg14oxw== dependencies: cornerstone-math "^0.1.7" isomorphic-base64 "^1.0.2" @@ -8288,10 +8288,10 @@ react-scripts@^2.1.1: optionalDependencies: fsevents "1.2.4" -react-viewerbase@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.1.5.tgz#a5ef7fd852ead91c74fb0068a7cb97c922a9c17a" - integrity sha512-196Req5szlhpvQWivxZmQzF3dMj2vCJtjnyusullRd8isTdaK+cLaCuKBRiJ5Eo6zwM69uTXj7ar8BHQARRzeQ== +react-viewerbase@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.1.6.tgz#09c775aed103d4382e7f24e5e0850c45327fa481" + integrity sha512-msFND25X0uooifaPf8y/VA/2yHrz+M/6yiJwh/rJD8HiM2Bel4JExDOJw9hJmmOS4l7dHpooxAI9ackibvwFAQ== dependencies: classnames "^2.2.6"