From 1bfb62d26d2d9ab9486fe25fff7c7f7da0ba7e47 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Tue, 30 Jun 2020 12:33:38 -0300 Subject: [PATCH] Add messages to sr pacs --- .../PanelMeasurementTableTracking/index.js | 47 +++-- platform/ui/src/assets/icons/close.svg | 2 +- .../src/components/IconButton/IconButton.jsx | 2 +- .../ui/src/components/Snackbar/Snackbar.css | 172 ++++++++++++++++++ .../components/Snackbar/SnackbarContainer.jsx | 24 +-- .../src/components/Snackbar/SnackbarItem.jsx | 33 ++-- .../src/contextProviders/DialogProvider.css | 8 + .../src/contextProviders/DialogProvider.jsx | 35 +++- 8 files changed, 275 insertions(+), 48 deletions(-) create mode 100644 platform/ui/src/components/Snackbar/Snackbar.css create mode 100644 platform/ui/src/contextProviders/DialogProvider.css diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js index 10184832a..20987a38e 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js @@ -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: () =>
Loading...
+ }); + 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}`)} /> diff --git a/platform/ui/src/assets/icons/close.svg b/platform/ui/src/assets/icons/close.svg index 83b259fca..03181c71f 100644 --- a/platform/ui/src/assets/icons/close.svg +++ b/platform/ui/src/assets/icons/close.svg @@ -1,4 +1,4 @@ - + diff --git a/platform/ui/src/components/IconButton/IconButton.jsx b/platform/ui/src/components/IconButton/IconButton.jsx index 290bfa273..38b94a87f 100644 --- a/platform/ui/src/components/IconButton/IconButton.jsx +++ b/platform/ui/src/components/IconButton/IconButton.jsx @@ -113,7 +113,7 @@ const IconButton = ({ }; IconButton.defaultProps = { - onClick: () => {}, + onClick: () => { }, color: 'default', disabled: false, fullWidth: false, diff --git a/platform/ui/src/components/Snackbar/Snackbar.css b/platform/ui/src/components/Snackbar/Snackbar.css new file mode 100644 index 000000000..0c3a0546a --- /dev/null +++ b/platform/ui/src/components/Snackbar/Snackbar.css @@ -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; + } +} diff --git a/platform/ui/src/components/Snackbar/SnackbarContainer.jsx b/platform/ui/src/components/Snackbar/SnackbarContainer.jsx index eb0e83fb2..40c76fc00 100644 --- a/platform/ui/src/components/Snackbar/SnackbarContainer.jsx +++ b/platform/ui/src/components/Snackbar/SnackbarContainer.jsx @@ -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 ; - }; - - if (!snackbarItems) { - return null; - } + const renderItem = item => ( + + ); 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 && (
{Object.keys(items).map(pos => { if (!items[pos].length) { @@ -35,7 +35,7 @@ const SnackbarContainer = () => { } return ( -
+
{items[pos].map((item, index) => (
{renderItem(item)}
))} diff --git a/platform/ui/src/components/Snackbar/SnackbarItem.jsx b/platform/ui/src/components/Snackbar/SnackbarItem.jsx index abeef2ccf..dc0f2bcef 100644 --- a/platform/ui/src/components/Snackbar/SnackbarItem.jsx +++ b/platform/ui/src/components/Snackbar/SnackbarItem.jsx @@ -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 ( -
- - x +
+ + x - {options.title &&
{options.title}
} - {options.message &&
{options.message}
} + {options.title &&
{options.title}
} + {options.message &&
{options.message}
}
); }; diff --git a/platform/ui/src/contextProviders/DialogProvider.css b/platform/ui/src/contextProviders/DialogProvider.css new file mode 100644 index 000000000..09438e95b --- /dev/null +++ b/platform/ui/src/contextProviders/DialogProvider.css @@ -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; +} diff --git a/platform/ui/src/contextProviders/DialogProvider.jsx b/platform/ui/src/contextProviders/DialogProvider.jsx index eb3c207cb..58ed46432 100644 --- a/platform/ui/src/contextProviders/DialogProvider.jsx +++ b/platform/ui/src/contextProviders/DialogProvider.jsx @@ -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 = () => ( { const e = event || window.event; const target = e.target || e.srcElement; @@ -215,6 +219,21 @@ const DialogProvider = ({ children, service }) => {
); + + 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 ( +
+ {component} +
+ ); + }; + + return showOverlay ? withOverlay(dragableItem()) : dragableItem(); }); /** @@ -236,13 +255,11 @@ const DialogProvider = ({ children, service }) => { return ( -
- {dialogs.some(dialog => dialog.showOverlay) ? ( -
{renderDialogs()}
- ) : ( - renderDialogs() - )} -
+ {!isEmpty() && +
+ {renderDialogs()} +
+ } {children}
);