diff --git a/QualityControl/public/common/timestampSelectForm.js b/QualityControl/public/common/timestampSelectForm.js index 4046b6887..79b314329 100644 --- a/QualityControl/public/common/timestampSelectForm.js +++ b/QualityControl/public/common/timestampSelectForm.js @@ -20,38 +20,29 @@ import { prettyFormatDate } from './utils.js'; * @param {Model} model - root model of the application * @returns {vnode} - virtual node element */ -export default ({ object: objectModel }) => { - const { objects, selected } = objectModel; - const isObjectLoaded = selected && objects?.[selected.name]?.isSuccess(); - return h( +export const timestampSelectForm = ({ versions = [], selectedId = null, onSelect }) => + h( '.w-100.flex-row', - isObjectLoaded && - h('select.form-control.gray-darker.text-center', { - onchange: (e) => { - const { value } = e.target; - if (selected && value !== 'Invalid Timestamp') { - const valueJson = JSON.parse(value); - objectModel.loadObjectByName(selected.name, valueJson.validFrom, valueJson.id); - } - }, - }, [ - objectModel.getObjectVersions(selected.name) - .map((version) => { - const versionString = JSON.stringify(version); - const object = objects[selected.name].payload; - return h('option.text-center', { - id: versionString, - key: versionString, - value: versionString, - selected: version.createdAt === object.createdAt ? true : false, - }, [ - 'Created: ', - prettyFormatDate(version.createdAt), - ' (id: ', - version.id, - ')', - ]); - }), - ]), + h('select.form-control.gray-darker.text-center', { + onchange: (e) => { + const { value } = e.target; + if (value && value !== 'Invalid Timestamp') { + onSelect?.(JSON.parse(value)); + } + }, + }, versions.map((version) => { + const versionString = JSON.stringify(version); + return h('option.text-center', { + id: versionString, + key: versionString, + value: versionString, + selected: selectedId ? version.id === selectedId : false, + }, [ + 'Created: ', + prettyFormatDate(version.createdAt), + ' (id: ', + version.id, + ')', + ]); + })), ); -}; diff --git a/QualityControl/public/object/QCObject.js b/QualityControl/public/object/QCObject.js index 72780449e..c8758fbfc 100644 --- a/QualityControl/public/object/QCObject.js +++ b/QualityControl/public/object/QCObject.js @@ -345,11 +345,11 @@ export default class QCObject extends BaseViewModel { /** * Indicate that the object loaded is wrong. Used after trying to print it with jsroot * @param {string} name - name of the object - * @param {string} reason - the reason for invalidating the object + * @param {object} details - object containing detail information for invalidation * @returns {undefined} */ - invalidObject(name, reason) { - this.objects[name] = RemoteData.failure(reason || 'JSROOT was unable to draw this object'); + invalidObject(name, details) { + this.objects[name] = RemoteData.failure(details || 'JSROOT was unable to draw this object'); this.notify(); } diff --git a/QualityControl/public/object/objectTreePage.js b/QualityControl/public/object/objectTreePage.js index 7a9b19716..6b017ad07 100644 --- a/QualityControl/public/object/objectTreePage.js +++ b/QualityControl/public/object/objectTreePage.js @@ -1,5 +1,5 @@ /** - * @license +* @license * Copyright 2019-2020 CERN and copyright holders of ALICE O2. * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. * All rights not expressly granted are reserved. @@ -23,7 +23,7 @@ import { } from '/js/src/index.js'; import { spinner } from '../common/spinner.js'; import { draw } from '../common/object/draw.js'; -import timestampSelectForm from './../common/timestampSelectForm.js'; +import { timestampSelectForm } from './../common/timestampSelectForm.js'; import virtualTable from './virtualTable.js'; import { defaultRowAttributes, qcObjectInfoPanel } from '../common/object/objectInfoCard.js'; import { downloadButton } from '../common/downloadButton.js'; @@ -93,14 +93,13 @@ export default (model) => { */ function objectPanel(model) { const selectedObjectName = model.object.selected.name; - if (model.object.objects && model.object.objects[selectedObjectName]) { + if (model.object.objects?.[selectedObjectName]) { return model.object.objects[selectedObjectName].match({ NotAsked: () => null, Loading: () => h('.h-100.w-100.flex-column.items-center.justify-center.f5', [spinner(3), h('', 'Loading Object')]), Success: (data) => drawPlot(model, data), - Failure: (error) => - h('.h-100.w-100.flex-column.items-center.justify-center.f5', [h('.f1', iconCircleX()), error]), + Failure: (invalidObjectDetails) => drawFailure(model, invalidObjectDetails), }); } return null; @@ -113,46 +112,76 @@ function objectPanel(model) { * @returns {vnode} - virtual node element */ const drawPlot = (model, object) => { - const { name, qcObject, validFrom, id } = object; - const { root } = qcObject; - const href = validFrom ? - `?page=objectView&objectName=${name}&ts=${validFrom}&id=${id}` - : `?page=objectView&objectName=${name}`; - return h('', { style: 'height:100%; display: flex; flex-direction: column' }, [ - h('.item-action-row.flex-row.g1.p1', [ - downloadRootImageButton(`${name}.png`, root, ['stat']), - downloadButton({ - href: model.objectViewModel.getDownloadQcdbObjectUrl(id), - title: 'Download root object', - }), - h( - 'a.btn#fullscreen-button', - { - title: 'Open object plot in full screen', - href, - onclick: (e) => model.router.handleLinkEvent(e), - }, - iconResizeBoth(), - ), - h( - 'a.btn#close-button', - { - title: 'Close the object plot', - onclick: () => model.object.select(), - }, - iconCircleX(), - ), - ]), + const { name, qcObject, validFrom, id, versions } = object; + return h('.h-100.flex-column', [ + actionButtonsRow(model, name, qcObject, validFrom, id), h('', { style: 'height:77%;' }, draw(model.object.objects[name], { }, ['stat'], (error) => { - model.object.invalidObject(name, error.message); + const invalidObjectDetails = { name, message: error.message, validFrom, id, versions }; + model.object.invalidObject(name, invalidObjectDetails); })), h('.scroll-y', {}, [ - h('.w-100.flex-row', { style: 'justify-content: center' }, h('.w-80', timestampSelectForm(model))), + h('.w-100.flex-row.justify-center', h('.w-80', timestampSelectForm({ + versions: versions ?? [], + selectedId: id ?? null, + onSelect: (version) => model.object.loadObjectByName(name, version.validFrom, version.id), + }))), qcObjectInfoPanel(object, { 'font-size': '.875rem;' }, defaultRowAttributes(model.notification)), ]), ]); }; +/** + * Draw the failure message when object cannot be drawn + * @param {Model} model - root model of the application + * @param {object} invalidObjectDetails - details about the invalid object + * @returns {vnode} - virtual node element + */ +const drawFailure = (model, invalidObjectDetails) => { + const { name, message, validFrom, id, versions } = invalidObjectDetails ?? {}; + return h('.h-100.flex-column', [ + actionButtonsRow(model, name, null, validFrom, id), + h( + '.h-100.flex-column.items-center.justify-center.text-center.f5', + [h('.f1', iconCircleX()), message], + ), + h('.w-100.flex-row.justify-center.pv2', h('.w-80', timestampSelectForm({ + versions: versions ?? [], + selectedId: id ?? null, + onSelect: (version) => model.object.loadObjectByName(name, version.validFrom, version.id), + }))), + ]); +}; + +const actionButtonsRow = (model, objectName, root, validFrom, id) => { + const href = validFrom + ? `?page=objectView&objectName=${objectName}&ts=${validFrom}&id=${id}` + : `?page=objectView&objectName=${objectName}`; + return h('.item-action-row.flex-row.g1.p1', [ + root && downloadRootImageButton(`${objectName}.png`, root, ['stat']), + downloadButton({ + href: model.objectViewModel.getDownloadQcdbObjectUrl(id), + title: 'Download root object', + }), + h( + 'a.btn#fullscreen-button', + { + title: 'Open object plot in full screen', + href, + onclick: (e) => model.router.handleLinkEvent(e), + }, + iconResizeBoth(), + ), + h( + 'a.btn#close-button', + { + title: 'Close the object plot', + onclick: () => model.object.select(), + }, + iconCircleX(), + ), + ]); +}; + /** * Shows status of current tree with its options (online, loaded, how many) * @param {Model} model - root model of the application