feat(errorHandling): Create fallback page for Server, Study and Series (#3592)

This commit is contained in:
rodrigobasilio2022 2023-08-15 23:59:29 -03:00 committed by GitHub
parent 9c51f17c60
commit 19408c987f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 100 additions and 1 deletions

View File

@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { StudyBrowser, useImageViewer, useViewportGrid } from '@ohif/ui'; import { StudyBrowser, useImageViewer, useViewportGrid } from '@ohif/ui';
import { utils } from '@ohif/core'; import { utils } from '@ohif/core';
import { useNavigate } from 'react-router-dom';
const { sortStudyInstances, formatDate } = utils; const { sortStudyInstances, formatDate } = utils;
@ -21,6 +22,8 @@ function PanelStudyBrowser({
displaySetService, displaySetService,
uiNotificationService, uiNotificationService,
} = servicesManager.services; } = servicesManager.services;
const navigate = useNavigate();
// Normally you nest the components so the tree isn't so deep, and the data // Normally you nest the components so the tree isn't so deep, and the data
// doesn't have to have such an intense shape. This works well enough for now. // doesn't have to have such an intense shape. This works well enough for now.
// Tabs --> Studies --> DisplaySets --> Thumbnails // Tabs --> Studies --> DisplaySets --> Thumbnails
@ -69,6 +72,11 @@ function PanelStudyBrowser({
studyInstanceUid: StudyInstanceUID, studyInstanceUid: StudyInstanceUID,
}); });
if (!qidoForStudyUID?.length) {
navigate('/notfoundstudy', '_self');
throw new Error('Invalid study URL');
}
let qidoStudiesForPatient = qidoForStudyUID; let qidoStudiesForPatient = qidoForStudyUID;
// try to fetch the prior studies based on the patientID if the // try to fetch the prior studies based on the patientID if the

View File

@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { utils } from '@ohif/core'; import { utils } from '@ohif/core';
@ -30,6 +31,7 @@ function PanelStudyBrowserTracking({
hangingProtocolService, hangingProtocolService,
uiNotificationService, uiNotificationService,
} = servicesManager.services; } = servicesManager.services;
const navigate = useNavigate();
const { t } = useTranslation('Common'); const { t } = useTranslation('Common');
@ -90,6 +92,11 @@ function PanelStudyBrowserTracking({
studyInstanceUid: StudyInstanceUID, studyInstanceUid: StudyInstanceUID,
}); });
if (!qidoForStudyUID?.length) {
navigate('/notfoundstudy', '_self');
throw new Error('Invalid study URL');
}
let qidoStudiesForPatient = qidoForStudyUID; let qidoStudiesForPatient = qidoForStudyUID;
// try to fetch the prior studies based on the patientID if the // try to fetch the prior studies based on the patientID if the

View File

@ -5,6 +5,7 @@ import { ExtensionManager, MODULE_TYPES } from '@ohif/core';
// //
import { extensionManager } from '../App.tsx'; import { extensionManager } from '../App.tsx';
import { useParams, useLocation } from 'react-router'; import { useParams, useLocation } from 'react-router';
import { useNavigate } from 'react-router-dom';
import useSearchParams from '../hooks/useSearchParams.ts'; import useSearchParams from '../hooks/useSearchParams.ts';
/** /**
@ -27,6 +28,7 @@ const areLocationsTheSame = (location0, location1) => {
* @param {function} props.children - Layout Template React Component * @param {function} props.children - Layout Template React Component
*/ */
function DataSourceWrapper(props) { function DataSourceWrapper(props) {
const navigate = useNavigate();
const { children: LayoutTemplate, ...rest } = props; const { children: LayoutTemplate, ...rest } = props;
const params = useParams(); const params = useParams();
const location = useLocation(); const location = useLocation();
@ -189,7 +191,7 @@ function DataSourceWrapper(props) {
(!isLoading && (newOffset !== previousOffset || isLocationUpdated)); (!isLoading && (newOffset !== previousOffset || isLocationUpdated));
if (isDataInvalid) { if (isDataInvalid) {
getData(); getData().catch(() => navigate('/notfoundserver', '_self'));
} }
} catch (ex) { } catch (ex) {
console.warn(ex); console.warn(ex);

View File

@ -11,6 +11,7 @@ import Compose from './Compose';
import getStudies from './studiesList'; import getStudies from './studiesList';
import { history } from '../../utils/history'; import { history } from '../../utils/history';
import loadModules from '../../pluginImports'; import loadModules from '../../pluginImports';
import isSeriesFilterUsed from '../../utils/isSeriesFilterUsed';
const { getSplitParam } = utils; const { getSplitParam } = utils;
@ -30,9 +31,11 @@ function defaultRouteInit(
const { const {
displaySetService, displaySetService,
hangingProtocolService, hangingProtocolService,
uiNotificationService,
} = servicesManager.services; } = servicesManager.services;
const unsubscriptions = []; const unsubscriptions = [];
const issuedWarningSeries = [];
const { const {
unsubscribe: instanceAddedUnsubscribe, unsubscribe: instanceAddedUnsubscribe,
} = DicomMetadataStore.subscribe( } = DicomMetadataStore.subscribe(
@ -43,6 +46,22 @@ function defaultRouteInit(
SeriesInstanceUID SeriesInstanceUID
); );
// checks if the series filter was used, if it exists
const seriesInstanceUIDs = filters?.seriesInstanceUID;
if (
seriesInstanceUIDs?.length &&
!isSeriesFilterUsed(seriesMetadata.instances, filters) &&
!issuedWarningSeries.includes(seriesInstanceUIDs[0])
) {
// stores the series instance filter so it shows only once the warning
issuedWarningSeries.push(seriesInstanceUIDs[0]);
uiNotificationService.show({
title: 'Series filter',
message: `Each of the series in filter: ${seriesInstanceUIDs} are not part of the current study. The entire study is being displayed`,
type: 'error',
duration: 7000,
});
}
displaySetService.makeDisplaySets(seriesMetadata.instances, madeInClient); displaySetService.makeDisplaySets(seriesMetadata.instances, madeInClient);
} }
); );

View File

@ -10,9 +10,56 @@ import Debug from './Debug';
import NotFound from './NotFound'; import NotFound from './NotFound';
import buildModeRoutes from './buildModeRoutes'; import buildModeRoutes from './buildModeRoutes';
import PrivateRoute from './PrivateRoute'; import PrivateRoute from './PrivateRoute';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
const NotFoundServer = ({
message = 'Unable to query for studies at this time. Check your data source configuration or network connection',
}) => {
return (
<div className="absolute w-full h-full flex justify-center items-center text-white">
<div>
<h4>{message}</h4>
</div>
</div>
);
};
NotFoundServer.propTypes = {
message: PropTypes.string,
};
const NotFoundStudy = () => {
return (
<div className="absolute w-full h-full flex justify-center items-center text-white">
<div>
<h4>
One or more of the requested studies are not available at this time.
Return to the{' '}
<Link className="text-primary-light" to={'/'}>
study list
</Link>{' '}
to select a different study to view.
</h4>
</div>
</div>
);
};
NotFoundStudy.propTypes = {
message: PropTypes.string,
};
// TODO: Include "routes" debug route if dev build // TODO: Include "routes" debug route if dev build
const bakedInRoutes = [ const bakedInRoutes = [
{
path: '/notfoundserver',
children: NotFoundServer,
},
{
path: '/notfoundstudy',
children: NotFoundStudy,
},
{ {
path: '/debug', path: '/debug',
children: Debug, children: Debug,

View File

@ -0,0 +1,16 @@
/**
* This function is used to check if the filter is used. Its intend is to
* warn the user in case of link with a SeriesInstanceUID was called
* @param instances
* @returns
*/
export default function isSeriesFilterUsed(instances, filters) {
const seriesInstanceUIDs = filters?.seriesInstanceUID;
if (!seriesInstanceUIDs) {
return true;
}
if (!instances.length) {
return false;
}
return seriesInstanceUIDs.includes(instances[0].SeriesInstanceUID);
}