Add messages to sr pacs
This commit is contained in:
parent
d5d219c271
commit
1bfb62d26d
@ -24,7 +24,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
||||
measurementChangeTimestamp,
|
||||
200
|
||||
);
|
||||
const { MeasurementService } = servicesManager.services;
|
||||
const { MeasurementService, UINotificationService, UIDialogService } = servicesManager.services;
|
||||
const [
|
||||
trackedMeasurements,
|
||||
sendTrackedMeasurementsEvent,
|
||||
@ -117,21 +117,40 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
||||
DICOMSR.downloadReport(trackedMeasurements, dataSource);
|
||||
};
|
||||
|
||||
const onCreateReportClick = () => {
|
||||
const measurements = MeasurementService.getMeasurements();
|
||||
const trackedMeasurements = measurements.filter(
|
||||
m =>
|
||||
trackedStudy === m.referenceStudyUID &&
|
||||
trackedSeries.includes(m.referenceSeriesUID)
|
||||
);
|
||||
const onCreateReportClick = async () => {
|
||||
// TODO: Create a loading service that uses the dialog service with these options?
|
||||
const loadingDialogId = UIDialogService.create({
|
||||
showOverlay: true,
|
||||
isDraggable: false,
|
||||
centralize: true,
|
||||
// TODO: Create a loading indicator component + zeplin design?
|
||||
content: () => <div className="text-primary-active">Loading...</div>
|
||||
});
|
||||
|
||||
try {
|
||||
const measurements = MeasurementService.getMeasurements();
|
||||
const trackedMeasurements = measurements.filter(
|
||||
m =>
|
||||
trackedStudy === m.referenceStudyUID &&
|
||||
trackedSeries.includes(m.referenceSeriesUID)
|
||||
);
|
||||
|
||||
const dataSources = extensionManager.getDataSources();
|
||||
// TODO -> Eventually deal with multiple dataSources.
|
||||
// Would need some way of saying which one is the "push" dataSource
|
||||
const dataSource = dataSources[0];
|
||||
const dataSources = extensionManager.getDataSources();
|
||||
// TODO -> Eventually deal with multiple dataSources.
|
||||
// Would need some way of saying which one is the "push" dataSource
|
||||
const dataSource = dataSources[0];
|
||||
|
||||
DICOMSR.storeMeasurements(trackedMeasurements, dataSource);
|
||||
const { message } = await DICOMSR.storeMeasurements(trackedMeasurements, dataSource);
|
||||
UINotificationService.show({ title: 'STOW SR', message, type: 'success' });
|
||||
} catch (error) {
|
||||
UINotificationService.show({
|
||||
title: 'STOW SR',
|
||||
message: error.message || 'Failed to store measurements',
|
||||
type: 'error',
|
||||
});
|
||||
} finally {
|
||||
UIDialogService.dismiss({ id: loadingDialogId });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -148,7 +167,7 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
|
||||
title="Measurements"
|
||||
amount={displayMeasurements.length}
|
||||
data={displayMeasurements}
|
||||
onClick={() => {}}
|
||||
onClick={() => { }}
|
||||
onEdit={id => alert(`Edit: ${id}`)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 19 19">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 19">
|
||||
<g fill="currentColor" fill-rule="evenodd">
|
||||
<g stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
|
||||
<path d="M.188.187L8.813 8.812M8.813.187L.188 8.812" transform="translate(5 5)"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 355 B After Width: | Height: | Size: 332 B |
@ -113,7 +113,7 @@ const IconButton = ({
|
||||
};
|
||||
|
||||
IconButton.defaultProps = {
|
||||
onClick: () => {},
|
||||
onClick: () => { },
|
||||
color: 'default',
|
||||
disabled: false,
|
||||
fullWidth: false,
|
||||
|
||||
172
platform/ui/src/components/Snackbar/Snackbar.css
Normal file
172
platform/ui/src/components/Snackbar/Snackbar.css
Normal file
@ -0,0 +1,172 @@
|
||||
/* TODO: Create tailwind styles for this component */
|
||||
.sb-topLeft {
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.sb-topCenter {
|
||||
left: 50%;
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.sb-topRight {
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.sb-bottomLeft {
|
||||
top: auto;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.sb-bottomCenter {
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
top: auto;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.sb-bottomRight {
|
||||
top: auto;
|
||||
bottom: 0px;
|
||||
left: auto;
|
||||
right: 0px;
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.sb-topLeft .sb-item,
|
||||
.sb-topCenter .sb-item,
|
||||
.sb-topRight .sb-item {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.sb-bottomLeft .sb-item,
|
||||
.sb-bottomCenter .sb-item,
|
||||
.sb-bottomRight .sb-item {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.sb-closeBtn {
|
||||
height: 20px;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
text-shadow: none;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.sb-closeBtn:hover {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.sb-closeIcon {
|
||||
display: block;
|
||||
font-size: 0;
|
||||
height: 100%;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sb-closeIcon:after,
|
||||
.sb-closeIcon:before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 2px;
|
||||
transition: all 0.3s ease;
|
||||
width: 12px;
|
||||
background-color: #222;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.sb-closeIcon:before {
|
||||
left: 4px;
|
||||
top: 3px;
|
||||
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 0px 50%;
|
||||
}
|
||||
|
||||
.sb-closeIcon:after {
|
||||
right: 3px;
|
||||
top: 5px;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: calc(100% - 3px) 50%;
|
||||
}
|
||||
|
||||
.sb-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sb-message {
|
||||
font-size: 14px;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.sb-item {
|
||||
position: relative;
|
||||
transition: height 300ms ease;
|
||||
animation: fadein 1s;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.2), 0 1px 18px 0 rgba(0, 0, 0, 0.12),
|
||||
0 3px 5px -1px rgba(0, 0, 0, 0.14);
|
||||
}
|
||||
|
||||
.sb-item a {
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.sb-hidden {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
top: 30px;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Internet Explorer */
|
||||
@-ms-keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
top: 30px;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
@ -2,16 +2,18 @@ import React from 'react';
|
||||
import SnackbarItem from './SnackbarItem';
|
||||
import { useSnackbar } from '../../contextProviders';
|
||||
|
||||
import './Snackbar.css';
|
||||
|
||||
const SnackbarContainer = () => {
|
||||
const { snackbarItems, hide } = useSnackbar();
|
||||
|
||||
const renderItem = item => {
|
||||
return <SnackbarItem key={item.itemId} options={item} onClose={hide} />;
|
||||
};
|
||||
|
||||
if (!snackbarItems) {
|
||||
return null;
|
||||
}
|
||||
const renderItem = item => (
|
||||
<SnackbarItem
|
||||
key={item.itemId}
|
||||
options={item}
|
||||
onClose={hide}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderItems = () => {
|
||||
const items = {
|
||||
@ -23,11 +25,9 @@ const SnackbarContainer = () => {
|
||||
bottomRight: [],
|
||||
};
|
||||
|
||||
snackbarItems.map(item => {
|
||||
items[item.position].push(item);
|
||||
});
|
||||
snackbarItems.forEach(item => items[item.position].push(item));
|
||||
|
||||
return (
|
||||
return snackbarItems && (
|
||||
<div>
|
||||
{Object.keys(items).map(pos => {
|
||||
if (!items[pos].length) {
|
||||
@ -35,7 +35,7 @@ const SnackbarContainer = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={pos} className={`sb-container sb-${pos}`}>
|
||||
<div key={pos} className={`fixed z-50 p-6 box-border h-auto sb-${pos}`}>
|
||||
{items[pos].map((item, index) => (
|
||||
<div key={item.id + index}>{renderItem(item)}</div>
|
||||
))}
|
||||
|
||||
@ -1,25 +1,36 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import SnackbarTypes from './SnackbarTypes';
|
||||
|
||||
const SnackbarItem = ({ options, onClose }) => {
|
||||
const handleClose = () => {
|
||||
onClose(options.id);
|
||||
};
|
||||
const handleClose = () => onClose(options.id);
|
||||
|
||||
useEffect(() => {
|
||||
if (options.autoClose) {
|
||||
setTimeout(() => {
|
||||
handleClose();
|
||||
}, options.duration);
|
||||
setTimeout(() => handleClose(), options.duration);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const typeClasses = {
|
||||
[SnackbarTypes.INFO]: 'bg-primary-active',
|
||||
[SnackbarTypes.WARNING]: 'bg-yellow-600',
|
||||
[SnackbarTypes.SUCCESS]: 'bg-green-600',
|
||||
[SnackbarTypes.ERROR]: 'bg-red-600'
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span onClick={handleClose}>
|
||||
<span>x</span>
|
||||
<div
|
||||
className={classNames(
|
||||
`${options.visible ? '' : 'sb-hidden'} sb-item`,
|
||||
typeClasses[options.type]
|
||||
)}
|
||||
>
|
||||
<span className="sb-closeBtn" onClick={handleClose}>
|
||||
<span className="sb-closeIcon">x</span>
|
||||
</span>
|
||||
{options.title && <div>{options.title}</div>}
|
||||
{options.message && <div>{options.message}</div>}
|
||||
{options.title && <div className="sb-title">{options.title}</div>}
|
||||
{options.message && <div className="sb-message">{options.message}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
8
platform/ui/src/contextProviders/DialogProvider.css
Normal file
8
platform/ui/src/contextProviders/DialogProvider.css
Normal file
@ -0,0 +1,8 @@
|
||||
/* TODO: Find a better way to set the cursor for all contents of dialog. */
|
||||
.DraggableItem.draggable div {
|
||||
cursor: grab !important;
|
||||
}
|
||||
|
||||
.DraggableItem.draggable.dragging div {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
@ -5,11 +5,14 @@ import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import Draggable from 'react-draggable';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
import './DialogProvider.css';
|
||||
|
||||
const DialogContext = createContext(null);
|
||||
|
||||
@ -150,6 +153,7 @@ const DialogProvider = ({ children, service }) => {
|
||||
onStart,
|
||||
onStop,
|
||||
onDrag,
|
||||
showOverlay,
|
||||
} = dialog;
|
||||
|
||||
let position =
|
||||
@ -158,13 +162,13 @@ const DialogProvider = ({ children, service }) => {
|
||||
position = centerPositions.find(position => position.id === id);
|
||||
}
|
||||
|
||||
return (
|
||||
const dragableItem = () => (
|
||||
<Draggable
|
||||
key={id}
|
||||
disabled={!isDraggable}
|
||||
position={position}
|
||||
defaultPosition={position}
|
||||
bounds="parent"
|
||||
bounds='parent'
|
||||
onStart={event => {
|
||||
const e = event || window.event;
|
||||
const target = e.target || e.srcElement;
|
||||
@ -215,6 +219,21 @@ const DialogProvider = ({ children, service }) => {
|
||||
</div>
|
||||
</Draggable>
|
||||
);
|
||||
|
||||
const withOverlay = component => {
|
||||
const background = 'bg-black bg-opacity-50';
|
||||
const overlay = 'fixed z-50 left-0 top-0 w-full h-full overflow-auto';
|
||||
return (
|
||||
<div
|
||||
className={classNames(overlay, background)}
|
||||
key={id}
|
||||
>
|
||||
{component}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return showOverlay ? withOverlay(dragableItem()) : dragableItem();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -236,13 +255,11 @@ const DialogProvider = ({ children, service }) => {
|
||||
|
||||
return (
|
||||
<DialogContext.Provider value={{ create, dismiss, dismissAll, isEmpty }}>
|
||||
<div className="DraggableArea">
|
||||
{dialogs.some(dialog => dialog.showOverlay) ? (
|
||||
<div className="Overlay active">{renderDialogs()}</div>
|
||||
) : (
|
||||
renderDialogs()
|
||||
)}
|
||||
</div>
|
||||
{!isEmpty() &&
|
||||
<div className='w-full h-full absolute'>
|
||||
{renderDialogs()}
|
||||
</div>
|
||||
}
|
||||
{children}
|
||||
</DialogContext.Provider>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user