Adding loading state and error message when saving measurements
This commit is contained in:
parent
5d8c608e70
commit
6b17deabcb
@ -1,5 +1,6 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Template.dialogForm.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -14,7 +15,7 @@ Template.dialogForm.onCreated(() => {
|
||||
}
|
||||
|
||||
// Hide the modal, removing the backdrop
|
||||
instance.$('.modal').on('hidden.bs.modal', event => {
|
||||
instance.$('.modal').one('hidden.bs.modal', event => {
|
||||
// Get the form value and call the confirm callback or resolve the promise
|
||||
const formData = form.value();
|
||||
if (_.isFunction(instance.data.confirmCallback)) {
|
||||
@ -27,7 +28,7 @@ Template.dialogForm.onCreated(() => {
|
||||
|
||||
cancel() {
|
||||
// Hide the modal, removing the backdrop
|
||||
instance.$('.modal').on('hidden.bs.modal', event => {
|
||||
instance.$('.modal').one('hidden.bs.modal', event => {
|
||||
// Call the cancel callback or resolve the promise
|
||||
if (_.isFunction(instance.data.cancelCallback)) {
|
||||
instance.data.cancelCallback(instance.data.promiseReject);
|
||||
@ -61,21 +62,21 @@ Template.dialogForm.onRendered(() => {
|
||||
});
|
||||
|
||||
Template.dialogForm.events({
|
||||
'keydown'(event) {
|
||||
keydown(event) {
|
||||
const instance = Template.instance(),
|
||||
keyCode = event.keyCode || event.which;
|
||||
|
||||
let handled = false;
|
||||
|
||||
if(keyCode === 27) {
|
||||
if (keyCode === 27) {
|
||||
instance.$('.btn.btn-cancel').click();
|
||||
handled = true;
|
||||
} else if(keyCode === 13) {
|
||||
} else if (keyCode === 13) {
|
||||
instance.$('.btn.btn-confirm').click();
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if(handled) {
|
||||
if (handled) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
<template name="dialogInfo">
|
||||
{{#dialogSimple (extend this
|
||||
title=(choose this.title 'Error')
|
||||
)}}
|
||||
<div class="messages">
|
||||
{{#each message in this.messages}}
|
||||
<div class="message">{{{message}}}</div>
|
||||
{{else}}
|
||||
<div class="message">An error has ocurred.</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/dialogSimple}}
|
||||
</template>
|
||||
@ -0,0 +1,9 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
Template.dialogInfo.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
const $modal = instance.$('.modal');
|
||||
|
||||
$modal.one('hidden.bs.modal', () => instance.data.promiseResolve());
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
<template name="dialogLoading">
|
||||
<div id="{{this.id}}" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="loading-text noselect">
|
||||
{{choose this.text 'Loading...'}} <i class="fa fa-spin fa-circle-o-notch fa-fw"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -0,0 +1,14 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
Template.dialogLoading.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
const $modal = instance.$('.modal');
|
||||
|
||||
// Create the bootstrap modal
|
||||
$modal.modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false,
|
||||
modal: true
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,9 @@
|
||||
@import "{ohif:design}/app"
|
||||
|
||||
.modal .loading-text
|
||||
theme('color', '$textSecondaryColor')
|
||||
font-size: 30px
|
||||
height: 100vh
|
||||
line-height: 100vh
|
||||
text-align: center
|
||||
text-shadow: 1px 1px 0 #000, -1px -1px 0 #000, -1px 1px 0 #000, 1px -1px 0 #000, -1px 0 0 #000, 0 -1px 0 #000, 1px 0 0 #000, 0 1px 0 #000
|
||||
@ -1,10 +1,14 @@
|
||||
import './dialog/confirm.html';
|
||||
import './dialog/progress.html';
|
||||
import './dialog/progress.js';
|
||||
import './dialog/form.html';
|
||||
import './dialog/form.js';
|
||||
import './dialog/info.html';
|
||||
import './dialog/info.js';
|
||||
import './dialog/loading.html';
|
||||
import './dialog/loading.js';
|
||||
import './dialog/login.html';
|
||||
import './dialog/login.js';
|
||||
import './dialog/progress.html';
|
||||
import './dialog/progress.js';
|
||||
import './dialog/simple.html';
|
||||
import './dialog/simple.js';
|
||||
import './dialog/unsavedChangesDialog.html';
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Blaze } from 'meteor/blaze';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
OHIF.ui.showDialog = (templateName, dialogData) => {
|
||||
@ -13,25 +14,51 @@ OHIF.ui.showDialog = (templateName, dialogData) => {
|
||||
};
|
||||
}
|
||||
|
||||
// Create a new promise to control the modal and store its resolve and reject callbacks
|
||||
let promiseResolve;
|
||||
let promiseReject;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
promiseResolve = resolve;
|
||||
promiseReject = reject;
|
||||
});
|
||||
let promise;
|
||||
let templateData;
|
||||
if (dialogData && dialogData.promise instanceof Promise) {
|
||||
// Use the given promise to control the modal
|
||||
promise = dialogData.promise;
|
||||
templateData = dialogData;
|
||||
} else {
|
||||
// Create a new promise to control the modal and store its resolve and reject callbacks
|
||||
let promiseResolve;
|
||||
let promiseReject;
|
||||
promise = new Promise((resolve, reject) => {
|
||||
promiseResolve = resolve;
|
||||
promiseReject = reject;
|
||||
});
|
||||
|
||||
// Render the dialog with the given template passing the promise object and callbacks
|
||||
templateData = _.extend({}, dialogData, {
|
||||
promise,
|
||||
promiseResolve,
|
||||
promiseReject
|
||||
});
|
||||
}
|
||||
|
||||
// Render the dialog with the given template passing the promise object and callbacks
|
||||
const templateData = _.extend({}, dialogData, {
|
||||
promise,
|
||||
promiseResolve,
|
||||
promiseReject
|
||||
});
|
||||
const view = Blaze.renderWithData(template, templateData, document.body);
|
||||
|
||||
const node = view.firstNode();
|
||||
const $node = node && $(node);
|
||||
|
||||
let $modal;
|
||||
if ($node && $node.hasClass('modal')) {
|
||||
$modal = $node;
|
||||
} else if ($node && $node.has('.modal')) {
|
||||
$modal = $node.find('.modal:first');
|
||||
}
|
||||
|
||||
// Destroy the created dialog view when the promise is either resolved or rejected
|
||||
const dismissModal = () => Blaze.remove(view);
|
||||
promise.then(dismissModal, dismissModal);
|
||||
const dismissModal = () => {
|
||||
if (dialogData.promise && $modal) {
|
||||
$modal.one('hidden.bs.modal', () => Blaze.remove(view)).modal('hide');
|
||||
} else {
|
||||
Blaze.remove(view);
|
||||
}
|
||||
};
|
||||
|
||||
promise.then(dismissModal).catch(dismissModal);
|
||||
|
||||
// Return the promise to allow callbacks stacking from outside
|
||||
return promise;
|
||||
|
||||
@ -26,6 +26,7 @@ Package.onUse(function(api) {
|
||||
api.addFiles([
|
||||
'client/ui/dimensional/dimensional.styl',
|
||||
'client/ui/resizable/resizable.styl',
|
||||
'client/components/bootstrap/dialog/loading.styl',
|
||||
'client/components/bootstrap/dialog/progress.styl',
|
||||
'client/components/bootstrap/dialog/unsavedChangesDialog.styl',
|
||||
'client/components/bootstrap/dropdown/dropdown.styl'
|
||||
|
||||
@ -250,7 +250,7 @@ class MeasurementApi {
|
||||
};
|
||||
|
||||
OHIF.log.info('Saving Measurements for timepoints:', timepoints);
|
||||
storeFn(measurementData, filter).then(() => {
|
||||
return storeFn(measurementData, filter).then(() => {
|
||||
OHIF.log.info('Measurement storage completed');
|
||||
});
|
||||
}
|
||||
|
||||
@ -9,12 +9,27 @@ Template.caseProgress.onCreated(() => {
|
||||
instance.progressPercent = new ReactiveVar();
|
||||
instance.progressText = new ReactiveVar();
|
||||
instance.isLocked = new ReactiveVar();
|
||||
instance.path = 'viewer.studyViewer.measurements';
|
||||
|
||||
instance.saveData = () => {
|
||||
instance.data.measurementApi.storeMeasurements();
|
||||
const api = instance.data.measurementApi;
|
||||
|
||||
// Clear signaled unsaved changes...
|
||||
OHIF.ui.unsavedChanges.clear('viewer.studyViewer.measurements.*');
|
||||
const successHandler = () => {
|
||||
OHIF.ui.unsavedChanges.clear(`${instance.path}.*`);
|
||||
};
|
||||
|
||||
// Display the error messages
|
||||
const errorHandler = data => OHIF.ui.showDialog('dialogInfo', data);
|
||||
|
||||
const promise = api.storeMeasurements();
|
||||
promise.then(successHandler).catch(errorHandler);
|
||||
OHIF.ui.showDialog('dialogLoading', {
|
||||
promise,
|
||||
text: 'Saving measurements data'
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
instance.unsavedChangesHandler = () => {
|
||||
@ -25,14 +40,13 @@ Template.caseProgress.onCreated(() => {
|
||||
};
|
||||
|
||||
// Attach handler for unsaved changes dialog...
|
||||
OHIF.ui.unsavedChanges.attachHandler('viewer.studyViewer.measurements', 'save', instance.unsavedChangesHandler);
|
||||
|
||||
OHIF.ui.unsavedChanges.attachHandler(instance.path, 'save', instance.unsavedChangesHandler);
|
||||
});
|
||||
|
||||
Template.caseProgress.onDestroyed(() => {
|
||||
const instance = Template.instance();
|
||||
// Remove unsaved changes handler after this view has been destroyed...
|
||||
OHIF.ui.unsavedChanges.removeHandler('viewer.studyViewer.measurements', 'save', instance.unsavedChangesHandler);
|
||||
OHIF.ui.unsavedChanges.removeHandler(instance.path, 'save', instance.unsavedChangesHandler);
|
||||
});
|
||||
|
||||
Template.caseProgress.onRendered(() => {
|
||||
@ -169,8 +183,6 @@ Template.caseProgress.events({
|
||||
return;
|
||||
}
|
||||
|
||||
instance.saveData();
|
||||
switchToTab('studylistTab');
|
||||
|
||||
instance.saveData().then(() => switchToTab('studylistTab'));
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user