diff --git a/lib/public/views/Runs/Details/RunPatch.js b/lib/public/views/Runs/Details/RunPatch.js index 3b000b8223..54f7e347c6 100644 --- a/lib/public/views/Runs/Details/RunPatch.js +++ b/lib/public/views/Runs/Details/RunPatch.js @@ -9,6 +9,7 @@ import { RunQualities } from '../../../domain/enums/RunQualities.js'; * @property {string} category * @property {string} title * @property {string} description + * @property {string|null} [lastEditedName] */ /** @@ -75,7 +76,8 @@ export class RunPatch extends Observable { } if (this._eorReasons.length !== this._run.eorReasons.length || this._eorReasons.some(({ id }) => id === undefined)) { - ret.eorReasons = this._eorReasons; + // Strip lastEditedName — the server's EorReasonDto only accepts id, reasonTypeId, and description + ret.eorReasons = this._eorReasons.map(({ id, reasonTypeId, description }) => ({ id, reasonTypeId, description })); } if (this._hasRunQualityChange()) { @@ -126,7 +128,12 @@ export class RunPatch extends Observable { } = this._run || {}; this._runQuality = runQuality; - this._eorReasons = eorReasons.map(({ id, description, reasonTypeId }) => ({ id, description, reasonTypeId })); + this._eorReasons = eorReasons.map(({ id, description, reasonTypeId, lastEditedName }) => ({ + id, + description, + reasonTypeId, + lastEditedName, + })); this._tags = tags.map(({ text }) => text); this.formData = { diff --git a/lib/public/views/Runs/Details/runDetailsComponent.js b/lib/public/views/Runs/Details/runDetailsComponent.js index ebb7ae4fe7..fe370ced70 100644 --- a/lib/public/views/Runs/Details/runDetailsComponent.js +++ b/lib/public/views/Runs/Details/runDetailsComponent.js @@ -40,7 +40,7 @@ import { RunDefinition } from '../../../domain/enums/RunDefinition.js'; import { formatFloat } from '../../../utilities/formatting/formatFloat.js'; import { formatEditableNumber } from '../format/formatEditableNumber.js'; import { editRunEorReasons } from '../format/editRunEorReasons.js'; -import { formatEorReason } from '../format/formatEorReason.mjs'; +import { formatRunEorReason } from '../format/formatRunEorReason.js'; import { selectionDropdown } from '../../../components/common/selection/dropdown/selectionDropdown.js'; import { formatRunCalibrationStatus } from '../format/formatRunCalibrationStatus.js'; import { BeamModes } from '../../../domain/enums/BeamModes.js'; @@ -533,7 +533,10 @@ export const runDetailsComponent = (runDetailsModel, router) => runDetailsModel. h('#eor-reasons.flex-row', [ runDetailsModel.isEditModeEnabled ? editRunEorReasons(runDetailsModel) - : h('.flex-column.g2', run.eorReasons.map((eorReason) => h('.eor-reason', formatEorReason(eorReason)))), + : h( + '.flex-column.g2.w-100', + run.eorReasons.map((eorReason) => h('.eor-reason', formatRunEorReason(eorReason))), + ), ]), ]), ]), diff --git a/lib/public/views/Runs/format/editRunEorReasons.js b/lib/public/views/Runs/format/editRunEorReasons.js index 56c69e6f04..6ba0d59e24 100644 --- a/lib/public/views/Runs/format/editRunEorReasons.js +++ b/lib/public/views/Runs/format/editRunEorReasons.js @@ -94,20 +94,23 @@ export const editRunEorReasons = (runDetailsModel) => { */ runDetailsModel.runPatch.eorReasons.length > 0 ? runDetailsModel.runPatch.eorReasons.map((eorReason) => { - const { reasonTypeId, description } = eorReason; + const { reasonTypeId, description, lastEditedName } = eorReason; const { category = '-', title } = eorReasonTypes.find((eorReasonType) => eorReasonType.id === reasonTypeId) || {}; const titleString = title ? ` - ${title}` : ''; const descriptionString = description ? ` - ${description}` : ''; return h( - '.flex-row.items-center', + '.flex-row.justify-between', { key: `${category} ${titleString} ${descriptionString}`, }, [ - h('label.remove-eor-reason.danger.ph1.actionable-icon', { - onclick: () => runDetailsModel.runPatch.removeEorReason(eorReason), - }, iconTrash()), - h('.w-wrapped', `${category} ${titleString} ${descriptionString}`), + h('.flex-row.items-center', [ + h('label.remove-eor-reason.danger.ph1.actionable-icon', { + onclick: () => runDetailsModel.runPatch.removeEorReason(eorReason), + }, iconTrash()), + h('.w-wrapped', `${category} ${titleString} ${descriptionString}`), + ]), + h('.w-wrapped', lastEditedName || null), ], ); }) diff --git a/lib/public/views/Runs/format/formatRunEorReason.js b/lib/public/views/Runs/format/formatRunEorReason.js new file mode 100644 index 0000000000..b97ab4a223 --- /dev/null +++ b/lib/public/views/Runs/format/formatRunEorReason.js @@ -0,0 +1,36 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { h } from '/js/src/index.js'; +import { tooltip } from '../../../../components/common/popover/tooltip.js'; +import { formatEorReason } from './formatEorReason.mjs'; + +/** + * Display the given EoR reason as a vnode component with lastEditedName tooltip + * + * @param {Partial<{ + * category: string, + * title: string, + * description: string, + * lastEditedName: string, + * }>} eorReason the EoR reason to display + * @return {VNode} the vnode component + */ +export const formatRunEorReason = (eorReason) => { + const { lastEditedName } = eorReason; + const reasonText = formatEorReason(eorReason); + return h('.w-100.flex-row.justify-between', [ + h('', reasonText), + lastEditedName ? tooltip(h('.w-wrapped', lastEditedName), 'Last edited by') : null, + ]); +}; diff --git a/test/public/runs/detail.test.js b/test/public/runs/detail.test.js index fa94143746..b7ffefe5a5 100644 --- a/test/public/runs/detail.test.js +++ b/test/public/runs/detail.test.js @@ -208,10 +208,10 @@ module.exports = () => { expect(eorReasons).to.lengthOf(2); expect(await eorReasons[0].evaluate((element) => element.innerText)) - .to.equal('DETECTORS - TPC - Some Reason other than selected plus one'); + .to.equal('DETECTORS - TPC - Some Reason other than selected plus one\nAnonymous'); expect(await eorReasons[1].evaluate((element) => element.innerText)) - .to.equal('DETECTORS - CPV - A new EOR reason'); + .to.equal('DETECTORS - CPV - A new EOR reason\nAnonymous'); }); it('should successfully revert the update end of run reasons', async () => { @@ -234,10 +234,19 @@ module.exports = () => { expect(eorReasons).to.lengthOf(2); expect(await eorReasons[0].evaluate((element) => element.innerText)) - .to.equal('DETECTORS - TPC - Some Reason other than selected plus one'); + .to.equal('DETECTORS - TPC - Some Reason other than selected plus one\nAnonymous'); expect(await eorReasons[1].evaluate((element) => element.innerText)) - .to.equal('DETECTORS - CPV - A new EOR reason'); + .to.equal('DETECTORS - CPV - A new EOR reason\nAnonymous'); + }); + + it('should display lastEditedName tooltip with "Last edited by" on formatRunEorReason', async () => { + const eorReasonElement = await page.$('#eor-reasons .eor-reason'); + const popoverTrigger = await eorReasonElement.$('.popover-trigger'); + expect(popoverTrigger).to.not.be.null; + + const popoverContent = await getPopoverContent(popoverTrigger); + expect(popoverContent).to.equal('Last edited by'); }); it('should successfully update inelasticInteractionRate values of PbPb run', async () => {