diff --git a/src/googleCloud/ConnectedDicomFilesUploader.js b/src/googleCloud/ConnectedDicomFilesUploader.js index 15a818fd8..1edf639f0 100644 --- a/src/googleCloud/ConnectedDicomFilesUploader.js +++ b/src/googleCloud/ConnectedDicomFilesUploader.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import DicomFileUploader from './DicomFileUploader.js'; +import DicomFileUploaderModal from './DicomFileUploaderModal.js'; const isActive = a => a.active === true; @@ -17,6 +17,6 @@ const mapStateToProps = state => { const ConnectedDicomFileUploader = connect( mapStateToProps, null -)(DicomFileUploader); +)(DicomFileUploaderModal); export default ConnectedDicomFileUploader; diff --git a/src/googleCloud/ConnectedDicomStorePicker.js b/src/googleCloud/ConnectedDicomStorePicker.js index e6dcd6e88..4572af90f 100644 --- a/src/googleCloud/ConnectedDicomStorePicker.js +++ b/src/googleCloud/ConnectedDicomStorePicker.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import DicomStorePickerWindow from './DicomStorePickerWindow.js'; +import DicomStorePickerModal from './DicomStorePickerModal.js'; const isActive = a => a.active === true; @@ -29,6 +29,6 @@ const mapDispatchToProps = dispatch => { const ConnectedDicomStorePicker = connect( mapStateToProps, mapDispatchToProps -)(DicomStorePickerWindow); +)(DicomStorePickerModal); export default ConnectedDicomStorePicker; diff --git a/src/googleCloud/DatasetPicker.js b/src/googleCloud/DatasetPicker.js index a7a62a30d..581dd2be0 100644 --- a/src/googleCloud/DatasetPicker.js +++ b/src/googleCloud/DatasetPicker.js @@ -5,14 +5,11 @@ import DatasetsList from './DatasetsList'; import './googleCloud.css'; export default class DatasetPicker extends Component { - constructor(props) { - super(props); - this.state = { - error: null, - loading: false, - datasets: [], - }; - } + state = { + error: null, + loading: true, + datasets: [], + }; static propTypes = { project: PropTypes.object, @@ -20,7 +17,6 @@ export default class DatasetPicker extends Component { onSelect: PropTypes.func, oidcKey: PropTypes.string, }; - static defaultProps = {}; async componentDidMount() { api.setOidcStorageKey(this.props.oidcKey); @@ -29,12 +25,19 @@ export default class DatasetPicker extends Component { this.props.project.projectId, this.props.location.locationId ); - this.loading = false; + if (response.isError) { - this.error = response.message; + this.setState({ + error: response.message, + }); + return; } - this.setState({ datasets: response.data.datasets || [] }); + + this.setState({ + datasets: response.data.datasets || [], + loading: false, + }); } render() { @@ -46,7 +49,7 @@ export default class DatasetPicker extends Component { loading={loading} error={error} onSelect={onSelect} - > + /> ); } } diff --git a/src/googleCloud/DatasetSelector.js b/src/googleCloud/DatasetSelector.js index 85119fb59..f347226f1 100644 --- a/src/googleCloud/DatasetSelector.js +++ b/src/googleCloud/DatasetSelector.js @@ -1,5 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import { withTranslation } from 'react-i18next'; import DicomStorePicker from './DicomStorePicker'; import DatasetPicker from './DatasetPicker'; import ProjectPicker from './ProjectPicker'; @@ -7,16 +8,13 @@ import LocationPicker from './LocationPicker'; import GoogleCloudApi from './api/GoogleCloudApi'; import './googleCloud.css'; -export default class DatasetSelector extends Component { - constructor(props) { - super(props); - this.state = { - project: null, - location: null, - dataset: null, - unloading: false, - }; - } +class DatasetSelector extends Component { + state = { + project: null, + location: null, + dataset: null, + unloading: false, + }; static propTypes = { id: PropTypes.string, @@ -25,23 +23,22 @@ export default class DatasetSelector extends Component { canClose: PropTypes.string, setServers: PropTypes.func.isRequired, }; - static defaultProps = {}; onProjectSelect = project => { this.setState({ - project: project, + project, }); }; onLocationSelect = location => { this.setState({ - location: location, + location, }); }; onDatasetSelect = dataset => { this.setState({ - dataset: dataset, + dataset, }); }; @@ -92,65 +89,68 @@ export default class DatasetSelector extends Component { onDatasetSelect, onDicomStoreSelect, } = this; + + let projectBreadcrumbs = ( +
+ {this.props.t('Select a Project')} +
+ ); + + if (project) { + projectBreadcrumbs = ( +
+ {project.name} + {project && location && ( + + {' '} + -> {location.name.split('/')[3]} + + )} + {project && location && dataset && ( + + {' '} + -> {dataset.name.split('/')[5]} + + )} +
+ ); + } + return ( <> - Google Cloud Healthcare API - {project && ( -
- {project.name} - {project && location && ( - <> - - {' '} - -> {location.name.split('/')[3]} - - {project && location && dataset && ( - - {' '} - -> {dataset.name.split('/')[5]} - - )} - - )} -
- )} - + {projectBreadcrumbs} {!project && ( + /> )} {project && !location && ( - <> - - + )} {project && location && !dataset && ( - <> - - + )} {project && location && dataset && ( - <> - - + )} ); } } + +export default withTranslation('Common')(DatasetSelector); diff --git a/src/googleCloud/DatasetsList.js b/src/googleCloud/DatasetsList.js index af1d038b0..cdcc103d1 100644 --- a/src/googleCloud/DatasetsList.js +++ b/src/googleCloud/DatasetsList.js @@ -1,14 +1,13 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import './googleCloud.css'; +import { withTranslation } from 'react-i18next'; +import { Icon } from 'react-viewerbase'; -export default class DatasetsList extends Component { - constructor(props) { - super(props); - this.state = { - search: '', - }; - } +class DatasetsList extends Component { + state = { + search: '', + }; static propTypes = { datasets: PropTypes.array, @@ -16,16 +15,19 @@ export default class DatasetsList extends Component { error: PropTypes.string, onSelect: PropTypes.func, }; - static defaultProps = {}; - renderTableRow(dataset) { + static defaultProps = { + loading: true, + }; + + renderTableRow = dataset => { return ( { this.onHighlightItem(dataset.name); @@ -37,21 +39,42 @@ export default class DatasetsList extends Component { {dataset.name.split('/')[5]} ); - } + }; onHighlightItem(dataset) { this.setState({ highlightedItem: dataset }); } render() { + if (this.props.error) { + return

{this.props.error}

; + } + + const loadingIcon = ( + + ); + + if (this.props.loading) { + return loadingIcon; + } + + const body = ( + + {this.props.datasets.map(this.renderTableRow)} + + ); + return ( - - - {this.props.datasets.map(dataset => { - return this.renderTableRow(dataset); - })} - +
+ + + + + + {this.props.datasets && body}
{this.props.t('Dataset')}
); } } + +export default withTranslation('Common')(DatasetsList); diff --git a/src/googleCloud/DicomFileUploader.js b/src/googleCloud/DicomFileUploader.js deleted file mode 100644 index 9e7fe0b76..000000000 --- a/src/googleCloud/DicomFileUploader.js +++ /dev/null @@ -1,38 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import DicomUploader from './DicomUploader'; - -class DicomFileUploader extends Component { - static propTypes = { - url: PropTypes.string, - oidcStorageKey: PropTypes.string.isRequired, - onClose: PropTypes.func, - }; - state = { - uploaded: false, - }; - - constructor(props) { - super(props); - this.element = React.createRef(); - } - - render() { - if (this.props.url != null) { - return ( -
- - -
- ); - } - return <>; - } -} - -export default DicomFileUploader; diff --git a/src/googleCloud/DicomFileUploaderModal.js b/src/googleCloud/DicomFileUploaderModal.js new file mode 100644 index 000000000..f0e38fe4a --- /dev/null +++ b/src/googleCloud/DicomFileUploaderModal.js @@ -0,0 +1,49 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Modal from 'react-bootstrap-modal'; +import DicomUploader from './DicomUploader'; +import { withTranslation } from 'react-i18next'; + +class DicomFileUploaderModal extends Component { + static propTypes = { + url: PropTypes.string, + oidcStorageKey: PropTypes.string.isRequired, + onClose: PropTypes.func, + }; + + state = { + uploaded: false, + }; + + render() { + if (!this.props.url) { + return null; + } + + return ( + + + + {this.props.t('Google Cloud Healthcare API - DICOM Upload')} + + + + + + + ); + } +} + +export default withTranslation('Common')(DicomFileUploaderModal); diff --git a/src/googleCloud/DicomStoreList.js b/src/googleCloud/DicomStoreList.js index ba5a16ca6..aac4b3f85 100644 --- a/src/googleCloud/DicomStoreList.js +++ b/src/googleCloud/DicomStoreList.js @@ -1,31 +1,33 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import './googleCloud.css'; +import { withTranslation } from 'react-i18next'; +import { Icon } from 'react-viewerbase'; -export default class DicomStoreList extends Component { - constructor(props) { - super(props); - this.state = { - search: '', - }; - } +class DicomStoreList extends Component { + state = { + search: '', + }; static propTypes = { stores: PropTypes.array, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, error: PropTypes.string, onSelect: PropTypes.func, }; - static defaultProps = {}; - renderTableRow(store) { + static defaultProps = { + loading: true, + }; + + renderTableRow = store => { return ( { this.onHighlightItem(store.name); @@ -37,21 +39,40 @@ export default class DicomStoreList extends Component { {store.name.split('/')[7]} ); - } + }; onHighlightItem(store) { this.setState({ highlightedItem: store }); } render() { + if (this.props.error) { + return

{this.props.error}

; + } + + const loadingIcon = ( + + ); + + if (this.props.loading) { + return loadingIcon; + } + + const body = ( + {this.props.stores.map(this.renderTableRow)} + ); + return ( - - - {this.props.stores.map(store => { - return this.renderTableRow(store); - })} - +
+ + + + + + {this.props.stores && body}
{this.props.t('DICOM Store')}
); } } + +export default withTranslation('Common')(DicomStoreList); diff --git a/src/googleCloud/DicomStorePicker.js b/src/googleCloud/DicomStorePicker.js index f4a44182b..7437bb9a2 100644 --- a/src/googleCloud/DicomStorePicker.js +++ b/src/googleCloud/DicomStorePicker.js @@ -5,33 +5,36 @@ import DicomStoreList from './DicomStoreList'; import './googleCloud.css'; export default class DicomStorePicker extends Component { - constructor(props) { - super(props); - this.state = { - error: null, - loading: false, - stores: [], - locations: [], - }; - } + state = { + error: null, + loading: true, + stores: [], + locations: [], + }; static propTypes = { dataset: PropTypes.object, onSelect: PropTypes.func, }; - static defaultProps = {}; async componentDidMount() { const { authority, client_id } = window.config.oidc[0]; const oidcStorageKey = `oidc.user:${authority}:${client_id}`; api.setOidcStorageKey(oidcStorageKey); const response = await api.loadDicomStores(this.props.dataset.name); - this.loading = false; + if (response.isError) { - this.error = response.message; + this.setState({ + error: response.message, + }); + return; } - this.setState({ stores: response.data.dicomStores || [] }); + + this.setState({ + stores: response.data.dicomStores || [], + loading: false, + }); } render() { @@ -44,7 +47,7 @@ export default class DicomStorePicker extends Component { loading={loading} error={error} onSelect={onSelect} - > + /> ); } } diff --git a/src/googleCloud/DicomStorePickerModal.js b/src/googleCloud/DicomStorePickerModal.js new file mode 100644 index 000000000..5de69f531 --- /dev/null +++ b/src/googleCloud/DicomStorePickerModal.js @@ -0,0 +1,67 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Modal from 'react-bootstrap-modal'; +import DatasetSelector from './DatasetSelector'; +import './googleCloud.css'; +import { withTranslation } from 'react-i18next'; + +class DicomStorePickerModal extends Component { + static propTypes = { + url: PropTypes.string, + oidcStorageKey: PropTypes.string.isRequired, + setServers: PropTypes.func.isRequired, + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func, + }; + + static defaultProps = { + isOpen: false, + }; + + handleEvent = data => { + const servers = [ + { + name: data.dicomStore, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + qidoSupportsIncludeField: false, + type: 'dicomWeb', + qidoRoot: data.qidoRoot, + wadoRoot: data.wadoRoot, + wadoUriRoot: data.wadoUriRoot, + active: true, + }, + ]; + + this.props.setServers(servers); + }; + + render() { + return ( + + + + {this.props.t('Google Cloud Healthcare API')} + + + + + + + ); + } +} + +export default withTranslation('Common')(DicomStorePickerModal); diff --git a/src/googleCloud/DicomStorePickerWindow.js b/src/googleCloud/DicomStorePickerWindow.js deleted file mode 100644 index 8b080f91e..000000000 --- a/src/googleCloud/DicomStorePickerWindow.js +++ /dev/null @@ -1,57 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import DatasetSelector from './DatasetSelector'; -import './googleCloud.css'; - -class DicomStorePickerWindow extends Component { - static propTypes = { - url: PropTypes.string, - oidcStorageKey: PropTypes.string.isRequired, - setServers: PropTypes.func.isRequired, - onClose: PropTypes.func, - }; - - constructor(props) { - super(props); - this.element = React.createRef(); - this.handleEvent = this.handleEvent.bind(this); - } - - componentDidMount() {} - - handleEvent(data) { - const servers = [ - { - name: data.dicomStore, - imageRendering: 'wadors', - thumbnailRendering: 'wadors', - qidoSupportsIncludeField: false, - requestOptions: { requestFromBrowser: true }, - type: 'dicomWeb', - qidoRoot: data.qidoRoot, - wadoRoot: data.wadoRoot, - wadoUriRoot: data.wadoUriRoot, - active: true, - }, - ]; - - this.props.setServers(servers); - } - - render() { - return ( -
- - -
- ); - } -} - -export default DicomStorePickerWindow; diff --git a/src/googleCloud/DicomUploader.js b/src/googleCloud/DicomUploader.js index 423c51fb5..1f03dd06a 100644 --- a/src/googleCloud/DicomUploader.js +++ b/src/googleCloud/DicomUploader.js @@ -6,25 +6,21 @@ import dicomUploader from './api/DicomUploadService'; import './googleCloud.css'; export default class DicomUploader extends Component { - constructor(props) { - super(props); - this.state = { - status: 'Upload', - isCancelled: false, - errorsCount: 0, - files: null, - uploadedVolume: null, - wholeVolumeStr: null, - isFilesListHidden: true, - timeLeft: null, - uploadedList: null, - totalCount: 0, - successfullyUploadedCount: 0, - lastFile: '', - uploadContext: null, // this is probably not needed, but we use this variable to destinguish between different downloads - }; - this.uploadFiles = this.uploadFiles.bind(this); - } + state = { + status: 'Upload', + isCancelled: false, + errorsCount: 0, + files: null, + uploadedVolume: null, + wholeVolumeStr: null, + isFilesListHidden: true, + timeLeft: null, + uploadedList: null, + totalCount: 0, + successfullyUploadedCount: 0, + lastFile: '', + uploadContext: null, // this is probably not needed, but we use this variable to distinguish between different downloads + }; static propTypes = { id: PropTypes.string, @@ -32,7 +28,6 @@ export default class DicomUploader extends Component { url: PropTypes.string, oidcKey: PropTypes.string, }; - static defaultProps = {}; filesLeft() { return ( @@ -66,7 +61,7 @@ export default class DicomUploader extends Component { ); } - uploadFiles(files) { + uploadFiles = files => { const filesArray = Array.from(files.target.files); const filesDict = {}; filesArray.forEach((file, i) => { @@ -107,14 +102,14 @@ export default class DicomUploader extends Component { uploadCallback, cancellationToken ); - } + }; uploadCallback(fileId, error) { const file = this.state.files[fileId]; file.processed = true; if (!error) { let uploadedVolume = this.state.uploadedVolume + file.size; - this.setState({ uploadedVolume: uploadedVolume }); + this.setState({ uploadedVolume }); } else { file.error = error; this.setState({ errorsCount: this.state.errorsCount + 1 }); @@ -122,17 +117,13 @@ export default class DicomUploader extends Component { this.setState({ lastFile: file.name }); let uploadedList = this.state.uploadedList; uploadedList.push(file); - this.setState({ uploadedList: uploadedList }); + this.setState({ uploadedList }); } - renderTableRow(file) { - let error = <>; - if (file.error != null) { - error = ( - <> - {file.error} - - ); + renderTableRow = file => { + let error = null; + if (file.error !== null) { + error =

{file.error}

; } return ( @@ -141,10 +132,10 @@ export default class DicomUploader extends Component { ); - } + }; render() { - if (this.state.files == null) { + if (this.state.files === null) { return (
@@ -177,28 +168,21 @@ export default class DicomUploader extends Component {
); - } else { - return ( - <> - - - - - - - - {this.state.uploadedList.map(file => { - return this.renderTableRow(file); - })} - -
- {this.percents()}% {this.filesLeft()} -
- - ); } + + return ( + + + + + + + + {this.state.uploadedList.map(this.renderTableRow)} + +
+ {this.percents()}% {this.filesLeft()} +
+ ); } } diff --git a/src/googleCloud/LocationPicker.js b/src/googleCloud/LocationPicker.js index f15782dc2..423d194c5 100644 --- a/src/googleCloud/LocationPicker.js +++ b/src/googleCloud/LocationPicker.js @@ -5,14 +5,11 @@ import LocationsList from './LocationsList'; import './googleCloud.css'; export default class LocationPicker extends Component { - constructor(props) { - super(props); - this.state = { - error: null, - loading: false, - locations: [], - }; - } + state = { + error: null, + loading: true, + locations: [], + }; static propTypes = { project: PropTypes.object, @@ -25,15 +22,19 @@ export default class LocationPicker extends Component { const response = await api.loadLocations(this.props.project.projectId); - this.loading = false; if (response.isError) { - this.error = response.message; + this.setState({ + error: response.message, + }); + return; } - this.setState({ locations: response.data.locations || [] }); - } - static defaultProps = {}; + this.setState({ + locations: response.data.locations || [], + loading: false, + }); + } render() { const { locations, loading, error } = this.state; @@ -44,7 +45,7 @@ export default class LocationPicker extends Component { loading={loading} error={error} onSelect={onSelect} - > + /> ); } } diff --git a/src/googleCloud/LocationsList.js b/src/googleCloud/LocationsList.js index a1ad230a7..cab67e08e 100644 --- a/src/googleCloud/LocationsList.js +++ b/src/googleCloud/LocationsList.js @@ -1,30 +1,33 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import './googleCloud.css'; -export default class LocationsList extends Component { - constructor(props) { - super(props); - this.state = { - search: '', - }; - } +import { withTranslation } from 'react-i18next'; +import { Icon } from 'react-viewerbase'; + +class LocationsList extends Component { + state = { + search: '', + }; static propTypes = { locations: PropTypes.array, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, error: PropTypes.string, onSelect: PropTypes.func, }; - static defaultProps = {}; - renderTableRow(location) { + static defaultProps = { + loading: true, + }; + + renderTableRow = location => { return ( { this.onHighlightItem(location.locationId); @@ -36,21 +39,42 @@ export default class LocationsList extends Component { {location.name.split('/')[3]} ); - } + }; onHighlightItem(locationId) { this.setState({ highlightedItem: locationId }); } render() { + if (this.props.error) { + return

{this.props.error}

; + } + + const loadingIcon = ( + + ); + + if (this.props.loading) { + return loadingIcon; + } + + const body = ( + + {this.props.locations.map(this.renderTableRow)} + + ); + return ( - - - {this.props.locations.map(project => { - return this.renderTableRow(project); - })} - +
+ + + + + + {this.props.locations && body}
{this.props.t('Location')}
); } } + +export default withTranslation('Common')(LocationsList); diff --git a/src/googleCloud/ProjectPicker.js b/src/googleCloud/ProjectPicker.js index d9ba1f8c7..4455f211b 100644 --- a/src/googleCloud/ProjectPicker.js +++ b/src/googleCloud/ProjectPicker.js @@ -5,30 +5,33 @@ import ProjectsList from './ProjectsList'; import './googleCloud.css'; export default class ProjectPicker extends Component { - constructor(props) { - super(props); - this.state = { - error: null, - loading: false, - projects: [], - }; - } + state = { + error: null, + loading: true, + projects: [], + }; static propTypes = { onSelect: PropTypes.func, oidcKey: PropTypes.string, }; - static defaultProps = {}; async componentDidMount() { api.setOidcStorageKey(this.props.oidcKey); const response = await api.loadProjects(); - this.loading = false; + if (response.isError) { - this.error = response.message; + this.setState({ + error: response.message, + }); + return; } - this.setState({ projects: response.data.projects || [] }); + + this.setState({ + projects: response.data.projects || [], + loading: false, + }); } render() { @@ -40,7 +43,7 @@ export default class ProjectPicker extends Component { loading={loading} error={error} onSelect={onSelect} - > + /> ); } } diff --git a/src/googleCloud/ProjectsList.js b/src/googleCloud/ProjectsList.js index 98974d8a5..22b731039 100644 --- a/src/googleCloud/ProjectsList.js +++ b/src/googleCloud/ProjectsList.js @@ -1,32 +1,35 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import './googleCloud.css'; +import { withTranslation } from 'react-i18next'; +import { Icon } from 'react-viewerbase'; -export default class ProjectsList extends Component { - constructor(props) { - super(props); - this.state = { - search: '', - highlightedItem: null, - }; - } +class ProjectsList extends Component { + state = { + search: '', + highlightedItem: null, + }; static propTypes = { projects: PropTypes.array, - loading: PropTypes.bool, + loading: PropTypes.bool.isRequired, error: PropTypes.string, onSelect: PropTypes.func.isRequired, + t: PropTypes.func, }; - static defaultProps = {}; - renderTableRow(project) { + static defaultProps = { + loading: true, + }; + + renderTableRow = project => { return ( { this.onHighlightItem(project.projectId); @@ -37,23 +40,46 @@ export default class ProjectsList extends Component { }} > {project.name} + {project.projectId} ); - } + }; onHighlightItem(project) { this.setState({ highlightedItem: project }); } render() { + if (this.props.error) { + return

{this.props.error}

; + } + + const loadingIcon = ( + + ); + + if (this.props.loading) { + return loadingIcon; + } + + const body = ( + + {this.props.projects.map(this.renderTableRow)} + + ); + return ( - - - {this.props.projects.map(project => { - return this.renderTableRow(project); - })} - +
+ + + + + + + {this.props.projects && body}
{this.props.t('Project')}{this.props.t('ID')}
); } } + +export default withTranslation('Common')(ProjectsList); diff --git a/src/googleCloud/api/DicomUploadService.js b/src/googleCloud/api/DicomUploadService.js index 4ad30ce40..07250ab72 100644 --- a/src/googleCloud/api/DicomUploadService.js +++ b/src/googleCloud/api/DicomUploadService.js @@ -62,8 +62,8 @@ class DicomUploadService { } readFile(file) { - const promise = new Promise((resolve, reject) => { - var reader = new FileReader(); + return new Promise((resolve, reject) => { + const reader = new FileReader(); reader.onload = () => { resolve({ name: file.name, @@ -75,7 +75,6 @@ class DicomUploadService { reader.onerror = error => reject(error); reader.readAsArrayBuffer(file); }); - return promise; } getClient(url) { diff --git a/src/googleCloud/api/GoogleCloudApi.js b/src/googleCloud/api/GoogleCloudApi.js index 616ae8259..d3a4cf5d2 100644 --- a/src/googleCloud/api/GoogleCloudApi.js +++ b/src/googleCloud/api/GoogleCloudApi.js @@ -27,7 +27,7 @@ class GoogleCloudApi { } async doRequest(urlStr, config = {}, params = {}) { - var url = new URL(urlStr); + const url = new URL(urlStr); let data = null; url.search = new URLSearchParams(params); @@ -40,7 +40,7 @@ class GoogleCloudApi { if (data.nextPageToken != null) { params.pageToken = data.nextPageToken; let subPage = await this.doRequest(urlStr, config, params); - for (var key in data) { + for (let key in data) { if (data.hasOwnProperty(key)) { data[key] = data[key].concat(subPage.data[key]); } @@ -49,7 +49,7 @@ class GoogleCloudApi { return { isError: false, status: response.status, - data: data, + data, }; } else { return { @@ -81,17 +81,17 @@ class GoogleCloudApi { } async loadLocations(projectId) { - return this.doRequest(this.urlBaseProject + `/${projectId}/locations`); + return this.doRequest(`${this.urlBaseProject}/${projectId}/locations`); } async loadDatasets(projectId, locationId) { return this.doRequest( - this.urlBaseProject + `/${projectId}/locations/${locationId}/datasets` + `${this.urlBaseProject}/${projectId}/locations/${locationId}/datasets` ); } async loadDicomStores(dataset) { - return this.doRequest(this.urlBase + `/${dataset}/dicomStores`); + return this.doRequest(`${this.urlBase}/${dataset}/dicomStores`); } } diff --git a/src/googleCloud/googleCloud.css b/src/googleCloud/googleCloud.css index 466483d93..b83d718cd 100644 --- a/src/googleCloud/googleCloud.css +++ b/src/googleCloud/googleCloud.css @@ -1,3 +1,76 @@ +.loading-icon-spin { + animation: spin 2s linear infinite; +} + +.loading-icon { + font-size: 30px; + margin: 20px 0; +} + +@keyframes spin { + 100% { + transform: rotate(360deg); + } +} + +.gcp-table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; + border-spacing: 0; + border-collapse: collapse; +} +.gcp-table > tr { + height: 20px; +} +.gcp-table > thead { + white-space: nowrap; +} +.gcp-table > thead tr th { + padding: 0; + border-bottom: 1px solid var(--ui-border-color-active); + width: 100%; + text-align: left; + border-top: 0; +} +.gcp-table > tbody tr { + padding: 5px; + background-color: var(--ui-gray-darker); +} +.gcp-table > tbody tr:nth-child(even) { + background-color: var(--ui-gray-dark); +} + +.gcp-table > tbody tr td { + padding: 8px; + height: $body-cell-height; + line-height: $body-cell-height; + color: var(--table-text-primary-color); + font-weight: 300; + border-top: 1px solid var(--ui-gray-lighter); + border-bottom: 1px solid var(--ui-gray-lighter); + white-space: nowrap; +} +.gcp-table > tbody tr td.emptyCell { + color: #516873; +} +.gcp-table > tbody tr td.emptyValue { + color: var(--ui-gray-light); +} +.gcp-table > tbody tr:hover, +.gcp-table > tbody tr:active, +.gcp-table > tbody tr.active { + background-color: var(--table-hover-color); + color: var(--text-primary-color); +} +.gcp-table > tbody tr:hover td, +.gcp-table > tbody tr:active td, +.gcp-table > tbody tr.active td { + border-top: 1px solid var(--ui-gray-lighter); + border-bottom: 1px solid var(--ui-gray-lighter); + background-color: var(--table-hover-color); +} + .gcp-files-selector { display: flex; justify-content: space-between; @@ -32,10 +105,37 @@ display: none; } +.gcp-picker { + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + width: 650px; + height: 600px; + background-color: #161a1f; + padding: 20px 30px; + position: relative; +} + +.gcp-dicom-picker__exit { + cursor: pointer; + position: absolute; + top: 30px; + right: 30px; + width: 14px !important; + height: 14px !important; + filter: opacity(0.5); +} + +.gcp-dicom-picker__exit:hover { + filter: opacity(1); +} + +.gcp-picker--btn { + margin: auto; +} + .gcp-picker--title { color: #ffffff; font-size: 24px; - font-weight: 28px; text-align: center; margin: 20px auto; } @@ -44,6 +144,11 @@ color: #ffffff; font-size: 16px; font-weight: 28px; - text-align: leaft; + text-align: left; margin: 20px auto; } + +.gcp-dicom-uploader .button { + float: left; + margin: 5px; +} diff --git a/src/studylist/StudyListWithData.js b/src/studylist/StudyListWithData.js index c6bf3d4c4..3f97015cf 100644 --- a/src/studylist/StudyListWithData.js +++ b/src/studylist/StudyListWithData.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import OHIF from 'ohif-core'; import { withRouter } from 'react-router-dom'; +import { withTranslation } from 'react-i18next'; import { StudyList } from 'react-viewerbase'; import ConnectedHeader from '../connectedComponents/ConnectedHeader.js'; import moment from 'moment'; @@ -14,7 +15,6 @@ class StudyListWithData extends Component { studies: [], error: null, modalComponentId: null, - showStudyList: true, }; static propTypes = { @@ -47,7 +47,6 @@ class StudyListWithData extends Component { if (!this.props.server && window.config.enableGoogleCloudAdapter) { this.setState({ modalComponentId: 'DicomStorePicker', - showStudyList: false, }); } else { this.searchForStudies({ @@ -64,7 +63,6 @@ class StudyListWithData extends Component { if (this.props.server !== prevProps.server) { this.setState({ modalComponentId: null, - showStudyList: true, searchData: null, studies: null, }); @@ -151,7 +149,6 @@ class StudyListWithData extends Component { openModal = modalComponentId => { this.setState({ modalComponentId, - showStudyList: false, }); }; @@ -167,10 +164,9 @@ class StudyListWithData extends Component { this.searchForStudies(searchData); }; - update = () => { + closeModals = () => { this.setState({ modalComponentId: null, - showStudyList: true, }); }; @@ -181,49 +177,47 @@ class StudyListWithData extends Component { return
Loading...
; } - let healthCareApiButtons = ''; - let healthCareApiWindows = ''; + let healthCareApiButtons = null; + let healthCareApiWindows = null; + + // TODO: This should probably be a prop if (window.config.enableGoogleCloudAdapter) { - if (this.state.modalComponentId) { - if (this.state.modalComponentId === 'DicomStorePicker') { - healthCareApiWindows = ( - - ); - } else if (this.state.modalComponentId === 'DicomFilesUploader') { - healthCareApiWindows = ( - - ); - } - healthCareApiWindows = ( - <> -
- {healthCareApiWindows} -
- - ); - } - healthCareApiButtons = ( + healthCareApiWindows = ( <> -
- - -
+ + ); + + healthCareApiButtons = ( +
+ + +
+ ); } - let studyList = <>; - studyList = ( -
+ + const studyList = ( +