feat(worklist): new investigational use text (#3999)

This commit is contained in:
Ibrahim 2024-03-25 11:33:24 -04:00 committed by GitHub
parent d70aae3233
commit 45b68e841d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 153 additions and 2 deletions

View File

@ -1,7 +1,12 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { SidePanel, ErrorBoundary, LoadingIndicatorProgress } from '@ohif/ui';
import {
SidePanel,
ErrorBoundary,
LoadingIndicatorProgress,
InvestigationalUseDialog,
} from '@ohif/ui';
import { ServicesManager, HangingProtocolService, CommandsManager } from '@ohif/core';
import { useAppConfig } from '@state';
import ViewerHeader from './ViewerHeader';
@ -152,6 +157,8 @@ function ViewerLayout({
) : null}
</React.Fragment>
</div>
<InvestigationalUseDialog dialogConfiguration={appConfig?.investigationalUseDialog} />
</div>
);
}

View File

@ -27,6 +27,7 @@ import {
UserPreferences,
LoadingIndicatorProgress,
useSessionStorage,
InvestigationalUseDialog,
Button,
ButtonEnums,
} from '@ohif/ui';
@ -527,6 +528,7 @@ function WorkList({
isReturnEnabled={false}
WhiteLabeling={appConfig.whiteLabeling}
/>
<InvestigationalUseDialog dialogConfiguration={appConfig?.investigationalUseDialog} />
<div className="ohif-scrollbar flex grow flex-col overflow-y-auto">
<StudyListFilter
numOfStudies={pageNumber * resultsPerPage > 100 ? 101 : numOfStudies}

View File

@ -122,6 +122,8 @@ Here are a list of some options available:
decoding. Defaults to minimum of `navigator.hardwareConcurrency` and
what is specified by `maxNumberOfWebWorkers`. Some windows machines require smaller values.
- `acceptHeader` : accept header to request specific dicom transfer syntax ex : [ 'multipart/related; type=image/jls; q=1', 'multipart/related; type=application/octet-stream; q=0.1' ]
- `investigationalUseDialog`: This should contain an object with `option` value, it can be either `always` which always shows the dialog once per session, `never` which never shows the dialog, or `configure` which shows the dialog once and won't show it again until a set number of days defined by the user, if it's set to configure, you are required to add an additional property `days` which is the number of days to wait before showing the dialog again.
- `groupEnabled`: boolean, if set to true, all valid modes for the study get grouped together first, then the rest of the modes. If false, all modes are shown in the order they are defined in the configuration.
- `requestTransferSyntaxUID` : Request a specific Transfer syntax from dicom web server ex: 1.2.840.10008.1.2.4.80 (applied only if acceptHeader is not set)
- `omitQuotationForMultipartRequest`: Some servers (e.g., .NET) require the `multipart/related` request to be sent without quotation marks. Defaults to `false`. If your server doesn't require this, then setting this flag to `true` might improve performance (by removing the need for preflight requests). Also note that
if auth headers are used, a preflight request is required.

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="68px" height="68px" viewBox="0 0 68 68" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>illustration-investigational-use</title>
<g id="Investigational-Use" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Investigational-use-messaging-component" transform="translate(-55, -284)">
<g id="Messaging-component" transform="translate(38.75, 203)">
<g id="Group-19" transform="translate(1.25, 72)">
<g id="illustration-investigational-use" transform="translate(15, 9)">
<rect id="Rectangle" fill="#090C29" opacity="0.900000036" x="0" y="0" width="68" height="68" rx="34"></rect>
<g id="single-neutral-search" transform="translate(15, 16)" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<circle id="Oval" stroke="#3A3F99" cx="16.0737705" cy="16.0737705" r="16.0737705"></circle>
<line x1="37" y1="37" x2="27.295082" y2="27.295082" id="Path" stroke="#3A3F99"></line>
<path d="M24.8688525,24.8688525 C24.8688525,24.8688525 21.6386587,23.455813 19.3807213,22.6273157 C17.8999493,22.0823767 18.1110313,19.6063894 18.968152,18.6765081 C20.4710303,17.2173856 21.2133915,15.1548596 20.9814262,13.0829629 C21.1101279,11.7283944 20.6465988,10.3841411 19.7082359,9.39065905 C18.7698729,8.39717696 17.4466768,7.84975339 16.0737705,7.88703309 C14.7011699,7.84980444 13.3782601,8.39701666 12.4399544,9.39013526 C11.5016487,10.3832539 11.0379007,11.7270704 11.1661148,13.0813788 C10.9341495,15.1532755 11.6765106,17.2158015 13.179389,18.6749239 C14.0365097,19.6048053 14.2475917,22.0807926 12.7668197,22.6257315 C10.5088823,23.4542289 7.27868852,24.8688525 7.27868852,24.8688525" id="Path" stroke="#348CFD"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -58,7 +58,6 @@ function Header({
</div>
<div className="flex items-center">{children}</div>
<div className="flex items-center">
<span className="text-common-light mr-3 text-lg">{t('INVESTIGATIONAL USE ONLY')}</span>
<Dropdown
id="options"
showDropdownIcon={false}

View File

@ -145,6 +145,10 @@ import oldTrash from './../../assets/icons/old-trash.svg';
import oldPlay from './../../assets/icons/old-play.svg';
import oldStop from './../../assets/icons/old-stop.svg';
/** New investigational use */
import investigationalUse from './../../assets/icons/illustration-investigational-use.svg';
const ICONS = {
'arrow-down': arrowDown,
'arrow-left': arrowLeft,
@ -290,6 +294,9 @@ const ICONS = {
'old-trash': oldTrash,
'old-play': oldPlay,
'old-stop': oldStop,
/** New investigational use */
'illustration-investigational-use': investigationalUse,
};
function addIcon(iconName, iconSVG) {

View File

@ -0,0 +1,108 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import Icon from '../Icon';
import Button, { ButtonEnums } from '../Button';
export enum showDialogOption {
NeverShowDialog = 'never',
AlwaysShowDialog = 'always',
ShowOnceAndConfigure = 'configure',
}
const InvestigationalUseDialog = ({ dialogConfiguration }) => {
const { option, days } = dialogConfiguration;
const [isHidden, setIsHidden] = useState(true);
useEffect(() => {
const dialogLocalState = localStorage.getItem('investigationalUseDialog');
const dialogSessionState = sessionStorage.getItem('investigationalUseDialog');
switch (option) {
case showDialogOption.NeverShowDialog:
setIsHidden(true);
break;
case showDialogOption.AlwaysShowDialog:
setIsHidden(!!dialogSessionState);
break;
case showDialogOption.ShowOnceAndConfigure:
if (dialogLocalState) {
const { expiryDate } = JSON.parse(dialogLocalState);
const isExpired = new Date() > new Date(expiryDate);
setIsHidden(!isExpired);
} else {
setIsHidden(false);
}
break;
default:
setIsHidden(true);
}
}, [option, days]);
const handleConfirmAndHide = () => {
const expiryDate = new Date();
switch (option) {
case showDialogOption.ShowOnceAndConfigure:
expiryDate.setDate(expiryDate.getDate() + days);
localStorage.setItem('investigationalUseDialog', JSON.stringify({ expiryDate }));
break;
case showDialogOption.AlwaysShowDialog:
sessionStorage.setItem('investigationalUseDialog', 'hidden');
break;
}
setIsHidden(true);
};
if (isHidden) {
return null;
}
return (
<div className="fixed bottom-2 z-50 flex h-[86px] w-full justify-center">
<div className="bg-secondary-dark border-primary-dark flex w-[90%] items-center justify-between rounded-lg border-2 pl-[22px] pr-[22px] pt-[10px] pb-[10px] shadow-lg">
<div className="flex items-center gap-4">
<Icon
name="illustration-investigational-use"
className="h-18 w-18"
/>
<div className="flex flex-col">
<div className="text-[19px] text-white">
OHIF Viewer is{' '}
<span className="text-primary-light">for investigational use only</span>
</div>
<div className="text-[13px] text-white">
<span
className="text-primary-active cursor-pointer"
onClick={() => window.open('https://ohif.org/', '_blank')}
>
Learn more about OHIF Viewer
</span>
</div>
</div>
</div>
<Button
type={ButtonEnums.type.primary}
onClick={handleConfirmAndHide}
className="bg-primary-main"
>
Confirm and Hide
</Button>
</div>
</div>
);
};
InvestigationalUseDialog.propTypes = {
dialogConfiguration: PropTypes.shape({
option: PropTypes.oneOf(Object.values(showDialogOption)).isRequired,
days: PropTypes.number,
}).isRequired,
};
InvestigationalUseDialog.defaultProps = {
dialogConfiguration: {
option: showDialogOption.AlwaysShowDialog,
},
};
export default InvestigationalUseDialog;

View File

@ -0,0 +1,3 @@
import InvestigationalUseDialog from './InvestigationalUseDialog';
export default InvestigationalUseDialog;

View File

@ -79,6 +79,7 @@ import PanelSection from './PanelSection';
import AdvancedToolbox from './AdvancedToolbox';
import InputDoubleRange from './InputDoubleRange';
import LegacyButtonGroup from './LegacyButtonGroup';
import InvestigationalUseDialog from './InvestigationalUseDialog';
import MeasurementItem from './MeasurementTable/MeasurementItem';
export {
@ -164,5 +165,6 @@ export {
ViewportPane,
ViewportOverlay,
WindowLevelMenuItem,
InvestigationalUseDialog,
MeasurementItem,
};

View File

@ -117,6 +117,7 @@ export {
WindowLevelMenuItem,
ImageScrollbar,
ViewportOverlay,
InvestigationalUseDialog,
MeasurementItem,
} from './components';