[OHIF-309] - Users should be able to delete an SR from the side panel when the behavior is supported by the data source (#1952)
* Remove trailing zeroes from patient age * WIP reject + remove displaySet. * ohif-316: Update DICOM SR thumbnail design to include a delete action (#1948) * add "supportsReject" to netlify config Co-authored-by: Igor Octaviano <igoroctaviano@gmail.com> Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
This commit is contained in:
parent
760e2a62ae
commit
68332701ab
@ -236,9 +236,8 @@ export default function init({
|
|||||||
id: dialogId,
|
id: dialogId,
|
||||||
centralize: true,
|
centralize: true,
|
||||||
isDraggable: false,
|
isDraggable: false,
|
||||||
content: Dialog,
|
|
||||||
useLastPosition: false,
|
|
||||||
showOverlay: true,
|
showOverlay: true,
|
||||||
|
content: Dialog,
|
||||||
contentProps: {
|
contentProps: {
|
||||||
title: 'Enter your annotation',
|
title: 'Enter your annotation',
|
||||||
value: { label },
|
value: { label },
|
||||||
|
|||||||
35
extensions/default/src/DicomWebDataSource/dcm4cheeReject.js
Normal file
35
extensions/default/src/DicomWebDataSource/dcm4cheeReject.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
export default function (wadoRoot) {
|
||||||
|
return {
|
||||||
|
series: (StudyInstanceUID, SeriesInstanceUID) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Reject because of Quality. (Seems the most sensible out of the options)
|
||||||
|
const CodeValueAndCodeSchemeDesignator = `113001%5EDCM`;
|
||||||
|
|
||||||
|
const url = `${wadoRoot}/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/reject/${CodeValueAndCodeSchemeDesignator}`;
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', url, true);
|
||||||
|
|
||||||
|
//Send the proper header information along with the request
|
||||||
|
// TODO -> Auth when we re-add authorization.
|
||||||
|
|
||||||
|
console.log(xhr);
|
||||||
|
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
//Call a function when the state changes.
|
||||||
|
if (xhr.readyState == 4) {
|
||||||
|
switch (xhr.status) {
|
||||||
|
case 204:
|
||||||
|
resolve(xhr.responseText);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
reject('Your dataSource does not support reject functionality');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import {
|
|||||||
processResults,
|
processResults,
|
||||||
processSeriesResults,
|
processSeriesResults,
|
||||||
} from './qido.js';
|
} from './qido.js';
|
||||||
|
import dcm4cheeReject from './dcm4cheeReject';
|
||||||
import { DicomMetadataStore, IWebApiDataSource, utils } from '@ohif/core';
|
import { DicomMetadataStore, IWebApiDataSource, utils } from '@ohif/core';
|
||||||
|
|
||||||
import getImageId from './utils/getImageId';
|
import getImageId from './utils/getImageId';
|
||||||
@ -34,6 +35,7 @@ const EXPLICIT_VR_LITTLE_ENDIAN = '1.2.840.10008.1.2.1';
|
|||||||
* @param {boolean} qidoSupportsIncludeField - Whether QIDO supports the "Include" option to request additional fields in response
|
* @param {boolean} qidoSupportsIncludeField - Whether QIDO supports the "Include" option to request additional fields in response
|
||||||
* @param {string} imageRengering - wadors | ? (unsure of where/how this is used)
|
* @param {string} imageRengering - wadors | ? (unsure of where/how this is used)
|
||||||
* @param {string} thumbnailRendering - wadors | ? (unsure of where/how this is used)
|
* @param {string} thumbnailRendering - wadors | ? (unsure of where/how this is used)
|
||||||
|
* @param {bool} supportsReject - Whether the server supports reject calls (i.e. DCM4CHEE)
|
||||||
* @param {bool} lazyLoadStudy - "enableStudyLazyLoad"; Request series meta async instead of blocking
|
* @param {bool} lazyLoadStudy - "enableStudyLazyLoad"; Request series meta async instead of blocking
|
||||||
*/
|
*/
|
||||||
function createDicomWebApi(dicomWebConfig) {
|
function createDicomWebApi(dicomWebConfig) {
|
||||||
@ -43,6 +45,7 @@ function createDicomWebApi(dicomWebConfig) {
|
|||||||
enableStudyLazyLoad,
|
enableStudyLazyLoad,
|
||||||
supportsFuzzyMatching,
|
supportsFuzzyMatching,
|
||||||
supportsWildcard,
|
supportsWildcard,
|
||||||
|
supportsReject,
|
||||||
} = dicomWebConfig;
|
} = dicomWebConfig;
|
||||||
|
|
||||||
const qidoConfig = {
|
const qidoConfig = {
|
||||||
@ -59,7 +62,7 @@ function createDicomWebApi(dicomWebConfig) {
|
|||||||
const qidoDicomWebClient = new api.DICOMwebClient(qidoConfig);
|
const qidoDicomWebClient = new api.DICOMwebClient(qidoConfig);
|
||||||
const wadoDicomWebClient = new api.DICOMwebClient(wadoConfig);
|
const wadoDicomWebClient = new api.DICOMwebClient(wadoConfig);
|
||||||
|
|
||||||
return IWebApiDataSource.create({
|
const implementation = {
|
||||||
query: {
|
query: {
|
||||||
studies: {
|
studies: {
|
||||||
mapParams: mapParams.bind(),
|
mapParams: mapParams.bind(),
|
||||||
@ -234,7 +237,13 @@ function createDicomWebApi(dicomWebConfig) {
|
|||||||
|
|
||||||
return imageIds;
|
return imageIds;
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (supportsReject) {
|
||||||
|
implementation.reject = dcm4cheeReject(wadoRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IWebApiDataSource.create(implementation);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { createDicomWebApi };
|
export { createDicomWebApi };
|
||||||
|
|||||||
@ -43,6 +43,23 @@ function OHIFCornerstoneSRViewport({
|
|||||||
const [isHydrated, setIsHydrated] = useState(displaySet.isHydrated);
|
const [isHydrated, setIsHydrated] = useState(displaySet.isHydrated);
|
||||||
const { viewports, activeViewportIndex } = viewportGrid;
|
const { viewports, activeViewportIndex } = viewportGrid;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onDisplaySetsRemovedSubscription = DisplaySetService.subscribe(
|
||||||
|
DisplaySetService.EVENTS.DISPLAY_SETS_REMOVED, ({ displaySetInstanceUIDs }) => {
|
||||||
|
const activeViewport = viewports[activeViewportIndex];
|
||||||
|
if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) {
|
||||||
|
viewportGridService.setDisplaysetForViewport({
|
||||||
|
viewportIndex: activeViewportIndex,
|
||||||
|
displaySetInstanceUID: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
onDisplaySetsRemovedSubscription.unsubscribe();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Optional hook into tracking extension, if present.
|
// Optional hook into tracking extension, if present.
|
||||||
let trackedMeasurements;
|
let trackedMeasurements;
|
||||||
let sendTrackedMeasurementsEvent;
|
let sendTrackedMeasurementsEvent;
|
||||||
|
|||||||
@ -191,7 +191,6 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
|||||||
id: 'enter-annotation',
|
id: 'enter-annotation',
|
||||||
centralize: true,
|
centralize: true,
|
||||||
isDraggable: false,
|
isDraggable: false,
|
||||||
useLastPosition: false,
|
|
||||||
showOverlay: true,
|
showOverlay: true,
|
||||||
content: Dialog,
|
content: Dialog,
|
||||||
contentProps: {
|
contentProps: {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
import { StudyBrowser, useImageViewer, useViewportGrid } from '@ohif/ui';
|
import { StudyBrowser, useImageViewer, useViewportGrid, Dialog } from '@ohif/ui';
|
||||||
import { useTrackedMeasurements } from '../../getContextModule';
|
import { useTrackedMeasurements } from '../../getContextModule';
|
||||||
|
|
||||||
const { formatDate } = utils;
|
const { formatDate } = utils;
|
||||||
@ -13,6 +13,8 @@ const { formatDate } = utils;
|
|||||||
function PanelStudyBrowserTracking({
|
function PanelStudyBrowserTracking({
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
|
UIDialogService,
|
||||||
|
UINotificationService,
|
||||||
getImageSrc,
|
getImageSrc,
|
||||||
getStudiesForPatientByStudyInstanceUID,
|
getStudiesForPatientByStudyInstanceUID,
|
||||||
requestDisplaySetCreationForStudy,
|
requestDisplaySetCreationForStudy,
|
||||||
@ -136,7 +138,11 @@ function PanelStudyBrowserTracking({
|
|||||||
thumbnailImageSrcMap,
|
thumbnailImageSrcMap,
|
||||||
trackedSeries,
|
trackedSeries,
|
||||||
viewports,
|
viewports,
|
||||||
isSingleViewport
|
isSingleViewport,
|
||||||
|
dataSource,
|
||||||
|
DisplaySetService,
|
||||||
|
UIDialogService,
|
||||||
|
UINotificationService
|
||||||
);
|
);
|
||||||
|
|
||||||
setDisplaySets(mappedDisplaySets);
|
setDisplaySets(mappedDisplaySets);
|
||||||
@ -146,6 +152,7 @@ function PanelStudyBrowserTracking({
|
|||||||
trackedSeries,
|
trackedSeries,
|
||||||
thumbnailImageSrcMap,
|
thumbnailImageSrcMap,
|
||||||
viewports,
|
viewports,
|
||||||
|
dataSource,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ~~ subscriptions --> displaySets
|
// ~~ subscriptions --> displaySets
|
||||||
@ -193,7 +200,11 @@ function PanelStudyBrowserTracking({
|
|||||||
thumbnailImageSrcMap,
|
thumbnailImageSrcMap,
|
||||||
trackedSeries,
|
trackedSeries,
|
||||||
viewports,
|
viewports,
|
||||||
isSingleViewport
|
isSingleViewport,
|
||||||
|
dataSource,
|
||||||
|
DisplaySetService,
|
||||||
|
UIDialogService,
|
||||||
|
UINotificationService
|
||||||
);
|
);
|
||||||
|
|
||||||
setDisplaySets(mappedDisplaySets);
|
setDisplaySets(mappedDisplaySets);
|
||||||
@ -227,10 +238,10 @@ function PanelStudyBrowserTracking({
|
|||||||
);
|
);
|
||||||
const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy
|
const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy
|
||||||
? [
|
? [
|
||||||
...expandedStudyInstanceUIDs.filter(
|
...expandedStudyInstanceUIDs.filter(
|
||||||
stdyUid => stdyUid !== StudyInstanceUID
|
stdyUid => stdyUid !== StudyInstanceUID
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [...expandedStudyInstanceUIDs, StudyInstanceUID];
|
: [...expandedStudyInstanceUIDs, StudyInstanceUID];
|
||||||
|
|
||||||
setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
|
setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
|
||||||
@ -304,7 +315,7 @@ function PanelStudyBrowserTracking({
|
|||||||
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onClickThumbnail={() => {}}
|
onClickThumbnail={() => { }}
|
||||||
onDoubleClickThumbnail={onDoubleClickThumbnailHandler}
|
onDoubleClickThumbnail={onDoubleClickThumbnailHandler}
|
||||||
activeDisplaySetInstanceUID={activeDisplaySetInstanceUID}
|
activeDisplaySetInstanceUID={activeDisplaySetInstanceUID}
|
||||||
/>
|
/>
|
||||||
@ -359,8 +370,13 @@ function _mapDisplaySets(
|
|||||||
thumbnailImageSrcMap,
|
thumbnailImageSrcMap,
|
||||||
trackedSeriesInstanceUIDs,
|
trackedSeriesInstanceUIDs,
|
||||||
viewports, // TODO: make array of `displaySetInstanceUIDs`?
|
viewports, // TODO: make array of `displaySetInstanceUIDs`?
|
||||||
isSingleViewport
|
isSingleViewport,
|
||||||
|
dataSource,
|
||||||
|
DisplaySetService,
|
||||||
|
UIDialogService,
|
||||||
|
UINotificationService
|
||||||
) {
|
) {
|
||||||
|
console.log(displaySets.length);
|
||||||
const thumbnailDisplaySets = [];
|
const thumbnailDisplaySets = [];
|
||||||
const thumbnailNoImageDisplaySets = [];
|
const thumbnailNoImageDisplaySets = [];
|
||||||
displaySets.forEach(ds => {
|
displaySets.forEach(ds => {
|
||||||
@ -369,19 +385,21 @@ function _mapDisplaySets(
|
|||||||
const viewportIdentificator = isSingleViewport
|
const viewportIdentificator = isSingleViewport
|
||||||
? []
|
? []
|
||||||
: viewports.reduce((acc, viewportData, index) => {
|
: viewports.reduce((acc, viewportData, index) => {
|
||||||
if (viewportData.displaySetInstanceUID === ds.displaySetInstanceUID) {
|
if (viewportData.displaySetInstanceUID === ds.displaySetInstanceUID) {
|
||||||
acc.push(_viewportLabels[index]);
|
acc.push(_viewportLabels[index]);
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const array =
|
const array =
|
||||||
componentType === 'thumbnailTracked'
|
componentType === 'thumbnailTracked'
|
||||||
? thumbnailDisplaySets
|
? thumbnailDisplaySets
|
||||||
: thumbnailNoImageDisplaySets;
|
: thumbnailNoImageDisplaySets;
|
||||||
|
|
||||||
array.push({
|
const { displaySetInstanceUID } = ds;
|
||||||
displaySetInstanceUID: ds.displaySetInstanceUID,
|
|
||||||
|
const thumbnailProps = {
|
||||||
|
displaySetInstanceUID,
|
||||||
description: ds.SeriesDescription,
|
description: ds.SeriesDescription,
|
||||||
seriesNumber: ds.SeriesNumber,
|
seriesNumber: ds.SeriesNumber,
|
||||||
modality: ds.Modality,
|
modality: ds.Modality,
|
||||||
@ -392,12 +410,71 @@ function _mapDisplaySets(
|
|||||||
imageSrc,
|
imageSrc,
|
||||||
dragData: {
|
dragData: {
|
||||||
type: 'displayset',
|
type: 'displayset',
|
||||||
displaySetInstanceUID: ds.displaySetInstanceUID,
|
displaySetInstanceUID,
|
||||||
// .. Any other data to pass
|
// .. Any other data to pass
|
||||||
},
|
},
|
||||||
isTracked: trackedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID),
|
isTracked: trackedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID),
|
||||||
viewportIdentificator,
|
viewportIdentificator,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (componentType === 'thumbnailNoImage') {
|
||||||
|
if (dataSource.reject && dataSource.reject.series) {
|
||||||
|
thumbnailProps.canReject = true;
|
||||||
|
thumbnailProps.onReject = () => {
|
||||||
|
UIDialogService.create({
|
||||||
|
id: 'ds-reject-sr',
|
||||||
|
centralize: true,
|
||||||
|
isDraggable: false,
|
||||||
|
showOverlay: true,
|
||||||
|
content: Dialog,
|
||||||
|
contentProps: {
|
||||||
|
title: 'Reject Report',
|
||||||
|
body: () => (
|
||||||
|
<div className="p-4 bg-primary-dark text-white">
|
||||||
|
<p>This is a destructive action.</p>
|
||||||
|
<p>Are you sure you want to continue?</p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
{ id: 'cancel', text: 'Cancel', type: 'secondary' },
|
||||||
|
{ id: 'save', text: 'Save', type: 'primary' },
|
||||||
|
],
|
||||||
|
onClose: () => UIDialogService.dismiss({ id: 'ds-reject-sr' }),
|
||||||
|
onSubmit: async ({ action }) => {
|
||||||
|
switch (action.id) {
|
||||||
|
case 'save':
|
||||||
|
try {
|
||||||
|
await dataSource.reject.series(ds.StudyInstanceUID, ds.SeriesInstanceUID);
|
||||||
|
DisplaySetService.deleteDisplaySet(displaySetInstanceUID);
|
||||||
|
UIDialogService.dismiss({ id: 'ds-reject-sr' });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Reject Report',
|
||||||
|
message: 'Report rejected successfully',
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
UIDialogService.dismiss({ id: 'ds-reject-sr' });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Reject Report',
|
||||||
|
message: 'Failed to reject report',
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'cancel':
|
||||||
|
UIDialogService.dismiss({ id: 'ds-reject-sr' });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
thumbnailProps.canReject = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array.push(thumbnailProps);
|
||||||
});
|
});
|
||||||
|
|
||||||
return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
|
return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
|
||||||
@ -442,14 +519,35 @@ function _createStudyBrowserTabs(
|
|||||||
const recentStudies = [];
|
const recentStudies = [];
|
||||||
const allStudies = [];
|
const allStudies = [];
|
||||||
|
|
||||||
|
// Iterate over each study...
|
||||||
studyDisplayList.forEach(study => {
|
studyDisplayList.forEach(study => {
|
||||||
const displaySetsForStudy = utils.sortBySeriesDate(displaySets.filter(
|
// Find it's display sets
|
||||||
|
const displaySetsForStudy = displaySets.filter(
|
||||||
ds => ds.StudyInstanceUID === study.studyInstanceUid
|
ds => ds.StudyInstanceUID === study.studyInstanceUid
|
||||||
));
|
);
|
||||||
|
|
||||||
|
// Sort them
|
||||||
|
const sortedDisplaySetsForStudy = utils.sortBySeriesDate(displaySetsForStudy);
|
||||||
|
|
||||||
|
/* Sort by series number, then by series date
|
||||||
|
displaySetsForStudy.sort((a, b) => {
|
||||||
|
if (a.seriesNumber !== b.seriesNumber) {
|
||||||
|
return a.seriesNumber - b.seriesNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
const seriesDateA = Date.parse(a.seriesDate);
|
||||||
|
const seriesDateB = Date.parse(b.seriesDate);
|
||||||
|
|
||||||
|
return seriesDateA - seriesDateB;
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Map the study to it's tab/view representation
|
||||||
const tabStudy = Object.assign({}, study, {
|
const tabStudy = Object.assign({}, study, {
|
||||||
displaySets: displaySetsForStudy,
|
displaySets: displaySetsForStudy,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add the "tab study" to the 'primary', 'recent', and/or 'all' tab group(s)
|
||||||
if (primaryStudyInstanceUIDs.includes(study.studyInstanceUid)) {
|
if (primaryStudyInstanceUIDs.includes(study.studyInstanceUid)) {
|
||||||
primaryStudies.push(tabStudy);
|
primaryStudies.push(tabStudy);
|
||||||
allStudies.push(tabStudy);
|
allStudies.push(tabStudy);
|
||||||
|
|||||||
@ -18,9 +18,7 @@ function WrappedPanelStudyBrowserTracking({
|
|||||||
extensionManager,
|
extensionManager,
|
||||||
servicesManager,
|
servicesManager,
|
||||||
}) {
|
}) {
|
||||||
// TODO: This should be made available a different way; route should have
|
const dataSource = extensionManager.getActiveDataSource()[0];
|
||||||
// already determined our datasource
|
|
||||||
const dataSource = extensionManager.getDataSources('dicomweb')[0];
|
|
||||||
const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind(
|
const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind(
|
||||||
null,
|
null,
|
||||||
dataSource
|
dataSource
|
||||||
@ -37,6 +35,8 @@ function WrappedPanelStudyBrowserTracking({
|
|||||||
<PanelStudyBrowserTracking
|
<PanelStudyBrowserTracking
|
||||||
MeasurementService={servicesManager.services.MeasurementService}
|
MeasurementService={servicesManager.services.MeasurementService}
|
||||||
DisplaySetService={servicesManager.services.DisplaySetService}
|
DisplaySetService={servicesManager.services.DisplaySetService}
|
||||||
|
UIDialogService={servicesManager.services.UIDialogService}
|
||||||
|
UINotificationService={servicesManager.services.UINotificationService}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
getImageSrc={_getImageSrcFromImageId}
|
getImageSrc={_getImageSrcFromImageId}
|
||||||
getStudiesForPatientByStudyInstanceUID={
|
getStudiesForPatientByStudyInstanceUID={
|
||||||
|
|||||||
@ -16,6 +16,7 @@ function create({
|
|||||||
query,
|
query,
|
||||||
retrieve,
|
retrieve,
|
||||||
store,
|
store,
|
||||||
|
reject,
|
||||||
retrieveSeriesMetadata,
|
retrieveSeriesMetadata,
|
||||||
deleteStudyMetadataPromise,
|
deleteStudyMetadataPromise,
|
||||||
getImageIdsForDisplaySet,
|
getImageIdsForDisplaySet,
|
||||||
@ -54,9 +55,12 @@ function create({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultReject = {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: query || defaultQuery,
|
query: query || defaultQuery,
|
||||||
retrieve: retrieve || defaultRetrieve,
|
retrieve: retrieve || defaultRetrieve,
|
||||||
|
reject: reject || defaultReject,
|
||||||
store: store || defaultStore,
|
store: store || defaultStore,
|
||||||
getImageIdsForDisplaySet,
|
getImageIdsForDisplaySet,
|
||||||
retrieveSeriesMetadata,
|
retrieveSeriesMetadata,
|
||||||
|
|||||||
@ -64,6 +64,26 @@ export default class DisplaySetService {
|
|||||||
return displaySet;
|
return displaySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteDisplaySet(displaySetInstanceUID) {
|
||||||
|
const { activeDisplaySets } = this;
|
||||||
|
|
||||||
|
const displaySetCacheIndex = displaySetCache.findIndex(
|
||||||
|
ds => ds.displaySetInstanceUID === displaySetInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
const activeDisplaySetsIndex = activeDisplaySets.findIndex(
|
||||||
|
ds => ds.displaySetInstanceUID === displaySetInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
displaySetCache.splice(displaySetCacheIndex, 1);
|
||||||
|
activeDisplaySets.splice(activeDisplaySetsIndex, 1);
|
||||||
|
|
||||||
|
this._broadcastEvent(EVENTS.DISPLAY_SETS_CHANGED, this.activeDisplaySets);
|
||||||
|
this._broadcastEvent(EVENTS.DISPLAY_SETS_REMOVED, {
|
||||||
|
displaySetInstanceUIDs: [displaySetInstanceUID],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} displaySetInstanceUID
|
* @param {string} displaySetInstanceUID
|
||||||
* @returns {object} displaySet
|
* @returns {object} displaySet
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
const EVENTS = {
|
const EVENTS = {
|
||||||
DISPLAY_SETS_ADDED: 'event::displaySetService:displaySetsAdded',
|
DISPLAY_SETS_ADDED: 'event::displaySetService:displaySetsAdded',
|
||||||
DISPLAY_SETS_CHANGED: 'event::displaySetService:displaySetsChanged',
|
DISPLAY_SETS_CHANGED: 'event::displaySetService:displaySetsChanged',
|
||||||
|
DISPLAY_SETS_REMOVED: 'event::displaySetService:displaySetsRemoved',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EVENTS;
|
export default EVENTS;
|
||||||
|
|||||||
@ -5,14 +5,13 @@ import PropTypes from 'prop-types';
|
|||||||
import { Typography, Icon } from '..';
|
import { Typography, Icon } from '..';
|
||||||
|
|
||||||
const CloseButton = ({ onClick }) => {
|
const CloseButton = ({ onClick }) => {
|
||||||
const theme = 'bg-transparent fill-primary-active';
|
|
||||||
const outline = 'outline-none focus:outline-none';
|
|
||||||
const flex = 'flex h-full';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className={classNames(flex, theme, 'border-0')} onClick={onClick}>
|
<Icon
|
||||||
<Icon name="close" className={classNames(theme, outline, 'h-3 w-3')} />
|
data-cy="close-button"
|
||||||
</button>
|
onClick={onClick}
|
||||||
|
name="close"
|
||||||
|
className="cursor-pointer text-primary-active w-6 h-6"
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,8 @@ const ThumbnailList = ({
|
|||||||
seriesDate,
|
seriesDate,
|
||||||
viewportIdentificator,
|
viewportIdentificator,
|
||||||
isTracked,
|
isTracked,
|
||||||
|
canReject,
|
||||||
|
onReject,
|
||||||
imageSrc,
|
imageSrc,
|
||||||
imageAltText,
|
imageAltText,
|
||||||
}) => {
|
}) => {
|
||||||
@ -82,6 +84,8 @@ const ThumbnailList = ({
|
|||||||
modalityTooltip={_getModalityTooltip(modality)}
|
modalityTooltip={_getModalityTooltip(modality)}
|
||||||
seriesDate={seriesDate}
|
seriesDate={seriesDate}
|
||||||
description={description}
|
description={description}
|
||||||
|
canReject={canReject}
|
||||||
|
onReject={onReject}
|
||||||
onClick={() => onThumbnailClick(displaySetInstanceUID)}
|
onClick={() => onThumbnailClick(displaySetInstanceUID)}
|
||||||
onDoubleClick={() =>
|
onDoubleClick={() =>
|
||||||
onThumbnailDoubleClick(displaySetInstanceUID)
|
onThumbnailDoubleClick(displaySetInstanceUID)
|
||||||
@ -113,7 +117,10 @@ ThumbnailList.propTypes = {
|
|||||||
'thumbnailTracked',
|
'thumbnailTracked',
|
||||||
'thumbnailNoImage',
|
'thumbnailNoImage',
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
viewportIdentificator: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
viewportIdentificator: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.array,
|
||||||
|
]),
|
||||||
isTracked: PropTypes.bool,
|
isTracked: PropTypes.bool,
|
||||||
/**
|
/**
|
||||||
* Data the thumbnail should expose to a receiving drop target. Use a matching
|
* Data the thumbnail should expose to a receiving drop target. Use a matching
|
||||||
|
|||||||
@ -12,12 +12,14 @@ const ThumbnailNoImage = ({
|
|||||||
modalityTooltip,
|
modalityTooltip,
|
||||||
onClick,
|
onClick,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
|
canReject,
|
||||||
|
onReject,
|
||||||
dragData,
|
dragData,
|
||||||
isActive,
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const [collectedProps, drag, dragPreview] = useDrag({
|
const [collectedProps, drag, dragPreview] = useDrag({
|
||||||
item: { ...dragData },
|
item: { ...dragData },
|
||||||
canDrag: function(monitor) {
|
canDrag: function (monitor) {
|
||||||
return Object.keys(dragData).length !== 0;
|
return Object.keys(dragData).length !== 0;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -51,8 +53,11 @@ const ThumbnailNoImage = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<span className="ml-4 text-base text-blue-300">{seriesDate}</span>
|
<span className="ml-4 text-base text-blue-300">{seriesDate}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-12 text-base text-white break-all">
|
<div className="flex flex-row">
|
||||||
{description}
|
{canReject && <Icon name="old-trash" className="ml-4 w-3 text-red-500" onClick={onReject} />}
|
||||||
|
<div className="ml-4 text-base text-white break-all">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -16,11 +16,12 @@ window.config = {
|
|||||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
qidoSupportsIncludeField: true,
|
qidoSupportsIncludeField: true,
|
||||||
|
supportsReject: true,
|
||||||
imageRendering: 'wadors',
|
imageRendering: 'wadors',
|
||||||
thumbnailRendering: 'wadors',
|
thumbnailRendering: 'wadors',
|
||||||
enableStudyLazyLoad: true,
|
enableStudyLazyLoad: true,
|
||||||
supportsFuzzyMatching: true,
|
supportsFuzzyMatching: true,
|
||||||
supportsWildcard: true
|
supportsWildcard: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -16,11 +16,12 @@ window.config = {
|
|||||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
wadoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||||
qidoSupportsIncludeField: true,
|
qidoSupportsIncludeField: true,
|
||||||
|
supportsReject: true,
|
||||||
imageRendering: 'wadors',
|
imageRendering: 'wadors',
|
||||||
thumbnailRendering: 'wadors',
|
thumbnailRendering: 'wadors',
|
||||||
enableStudyLazyLoad: true,
|
enableStudyLazyLoad: true,
|
||||||
supportsFuzzyMatching: true,
|
supportsFuzzyMatching: true,
|
||||||
supportsWildcard: true
|
supportsWildcard: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user