diff --git a/lib/public/Model.js b/lib/public/Model.js index 0d0ae222f3..1efd2f8835 100644 --- a/lib/public/Model.js +++ b/lib/public/Model.js @@ -101,21 +101,24 @@ export default class Model extends Observable { this.router.bubbleTo(this); registerFrontLinkListener((e) => this.router.handleLinkEvent(e)); + // Setup warnings + this._warnings = new Map(); + // Models this.home = new HomePageModel(this); this.home.bubbleTo(this); - this.lhcPeriods = new LhcPeriodsModel(this.router); + this.lhcPeriods = new LhcPeriodsModel(this.router, this.warnings); this.lhcPeriods.bubbleTo(this); - this.dataPasses = new DataPassesModel(this.router); + this.dataPasses = new DataPassesModel(this.router, this.warnings); this.dataPasses.bubbleTo(this); this.qcFlags = new QcFlagsModel(this); this.qcFlags.bubbleTo(this); - this.simulationPasses = new SimulationPassesModel(this.router); + this.simulationPasses = new SimulationPassesModel(this.router, this.warnings); this.simulationPasses.bubbleTo(this); this.qcFlagTypes = new QcFlagTypesModel(this); @@ -192,6 +195,14 @@ export default class Model extends Observable { this.dropdownMenu = false; } + /** + * Returns warnings map + * @returns {Map} warnings map + */ + get warnings() { + return this._warnings; + } + /** * Delegates sub-model actions depending on new location of the page * @returns {vnode} The page to be loaded diff --git a/lib/public/app.css b/lib/public/app.css index ec66c3717c..bb67bc66bd 100644 --- a/lib/public/app.css +++ b/lib/public/app.css @@ -266,6 +266,12 @@ th.text-center, td.text-center { border-color: #f5c6cb; } +.alert-warning { + color: var(--color-warning); + background-color: #ffe8c8; + border-color: #fdd69f; +} + .alert-danger hr { border-top-color: #f1b0b7; } diff --git a/lib/public/components/Filters/common/FilteringModel.js b/lib/public/components/Filters/common/FilteringModel.js index 4bba7bb35c..8df07417c9 100644 --- a/lib/public/components/Filters/common/FilteringModel.js +++ b/lib/public/components/Filters/common/FilteringModel.js @@ -25,11 +25,13 @@ export class FilteringModel extends Observable { * * @param {QueryRouter} router router that controls the application's page navigation * @param {Object} filters the filters with their label and model + * @param {object} warnings object reference used to define warnings. */ - constructor(router, filters) { + constructor(router, filters, warnings) { super(); this._visualChange$ = new Observable(); this._pageIdentifier = null; + this._warnings = warnings; this._router = router; this._filters = {}; @@ -142,22 +144,44 @@ export class FilteringModel extends Observable { * @returns {undefined} */ setFilterFromURL(notify = false) { - const { params: { page = '', filter } } = this._router; + const { params: { page = '', filter = {} } } = this._router; if (this._pageIdentifier === page) { - if (filter) { - for (const [key, value] of Object.entries(filter)) { - if (key in this._filters) { + const unknownFilters = []; + const setFilterErrors = []; + + for (const [key, value] of Object.entries(filter)) { + if (key in this._filters) { + try { this._filters[key].normalized = value; + } catch { + setFilterErrors.push(`${buildUrl('', { [key]: value }).slice(1)}`); } + } else { + unknownFilters.push(`'${key}'`); } + } + + if (setFilterErrors.length > 0) { + this._warnings.set( + 'Unparsable Filters', + `The following filter-value pairs could not be parsed: [${setFilterErrors.join(', ')}]`, + ); } else { - this.reset(); + this._warnings.delete('Unparsable Filters'); } - } - if (notify) { - this.notify(); + if (unknownFilters.length > 0) { + this._warnings.set( + 'Unknown Filters', + `The filters: [${unknownFilters.join(', ')}]; are not reccognised. Check if they are spelled correctly.`, + ); + } else { + this._warnings.delete('Unknown Filters'); + } + if (notify) { + this.notify(); + } } } diff --git a/lib/public/components/common/messages/warningComponent.js b/lib/public/components/common/messages/warningComponent.js new file mode 100644 index 0000000000..1c37ddf5d7 --- /dev/null +++ b/lib/public/components/common/messages/warningComponent.js @@ -0,0 +1,35 @@ +import { h } from '/js/src/index.js'; +import { iconX } from '/js/src/icons.js'; + +/** + * Component to display whenever a page has warnings. + * + * @param {OverviewPageModel} overviewModel model that controlls an overview page + * @returns {Component} the warning componen + */ +export const warningComponent = (overviewModel) => { + const { warnings } = overviewModel; + + if (!warnings.size) { + return null; + } + + return h('details.alert.alert-warning', { open: true }, [ + h('summary', 'Warnings'), + h('ul', warnings.entries().toArray().map(([key, message]) => + h('li.flex-row.items-center', [ + h( + '.btn.btn-pill.alert-warning.mh1', + { + onclick: () => { + warnings.delete(key); + overviewModel.notify(); + }, + }, + iconX(), + ), + h('strong.mh1', `${key}:`), + h('span', message), + ]))), + ]); +}; diff --git a/lib/public/models/OverviewModel.js b/lib/public/models/OverviewModel.js index 69ae0c3df3..b27f7b16a0 100644 --- a/lib/public/models/OverviewModel.js +++ b/lib/public/models/OverviewModel.js @@ -35,10 +35,11 @@ import { SortModel } from '../components/common/table/SortModel.js'; export class OverviewPageModel extends Observable { /** * Constructor + * @param {Map} warnings a map storing warnings to show to the user */ - constructor() { + constructor(warnings) { super(); - + this._warnings = warnings; this._sortModel = new SortModel(); this._sortModel.observe(() => { this._pagination.silentlySetCurrentPage(1); @@ -97,6 +98,7 @@ export class OverviewPageModel extends Observable { reset() { this._item$.setCurrent(RemoteData.notAsked()); this._pagination.reset(); + this._warnings.clear(); } /** @@ -249,4 +251,13 @@ export class OverviewPageModel extends Observable { hasAnyData() { return this._item$.getCurrent().match({ Success: ({ length = 0 } = {}) => length > 0, Other: () => false }); } + + /** + * Returns the warnings object + * + * @return {object} the warning model + */ + get warnings() { + return this._warnings; + } } diff --git a/lib/public/views/DataPasses/DataPassesModel.js b/lib/public/views/DataPasses/DataPassesModel.js index 42fed10c3a..fcbcce846a 100644 --- a/lib/public/views/DataPasses/DataPassesModel.js +++ b/lib/public/views/DataPasses/DataPassesModel.js @@ -22,14 +22,16 @@ export class DataPassesModel extends Observable { /** * The constructor of the model * @param {QueryRouter} router router that controls the application's page navigation + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router) { + constructor(router, warnings) { super(); - this._perLhcPeriodOverviewModel = new DataPassesPerLhcPeriodOverviewModel(router, 'data-passes-per-lhc-period-overview'); + this._perLhcPeriodOverviewModel = new DataPassesPerLhcPeriodOverviewModel(router, 'data-passes-per-lhc-period-overview', warnings); this._perLhcPeriodOverviewModel.bubbleTo(this); - this._perSimulationPassOverviewModel = new DataPassesPerSimulationPassOverviewModel(router, 'data-passes-per-simulation-pass-overview'); + this._perSimulationPassOverviewModel = + new DataPassesPerSimulationPassOverviewModel(router, 'data-passes-per-simulation-pass-overview', warnings); this._perSimulationPassOverviewModel.bubbleTo(this); } diff --git a/lib/public/views/DataPasses/DataPassesOverviewModel.js b/lib/public/views/DataPasses/DataPassesOverviewModel.js index 728f995f85..d15f3eb909 100644 --- a/lib/public/views/DataPasses/DataPassesOverviewModel.js +++ b/lib/public/views/DataPasses/DataPassesOverviewModel.js @@ -25,8 +25,8 @@ export class DataPassesOverviewModel extends OverviewPageModel { * @param {QueryRouter} router router that controls the application's page navigation * @param {string} pageIdentifier string that indicates what page this model represents */ - constructor(router, pageIdentifier) { - super(); + constructor(router, pageIdentifier, warnings) { + super(warnings); this._filteringModel = new FilteringModel( router, { @@ -35,6 +35,7 @@ export class DataPassesOverviewModel extends OverviewPageModel { availableOptions: NON_PHYSICS_PRODUCTIONS_NAMES_WORDS.map((word) => ({ label: word.toUpperCase(), value: word })), }), }, + this._warnings, ); this._filteringModel.pageIdentifier = pageIdentifier; diff --git a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js index 6da2205751..1be58d10f2 100644 --- a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewModel.js @@ -21,9 +21,10 @@ export class DataPassesPerLhcPeriodOverviewModel extends DataPassesOverviewModel * Constructor * @param {QueryRouter} router router that controls the application's page navigation * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router, pageIdentifier) { - super(router, pageIdentifier); + constructor(router, pageIdentifier, warnings) { + super(router, pageIdentifier, warnings); this._lhcPeriodId = null; } diff --git a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewPage.js b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewPage.js index e97dca2170..7cb3e5fb65 100644 --- a/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewPage.js +++ b/lib/public/views/DataPasses/PerLhcPeriodOverview/DataPassesPerLhcPeriodOverviewPage.js @@ -19,6 +19,7 @@ import { filtersPanelPopover } from '../../../components/Filters/common/filtersP import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplayableRowsCount.js'; import { dataPassesActiveColumns } from '../ActiveColumns/dataPassesActiveColumns.js'; import { DataPassVersionStatus } from '../../../domain/enums/DataPassVersionStatus.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 42; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -42,24 +43,18 @@ const getRowClasses = ({ versions }) => { * @returns {Component} The overview screen */ export const DataPassesPerLhcPeriodOverviewPage = ({ dataPasses: { perLhcPeriodOverviewModel: dataPassesPerLhcPeriodOverviewModel } }) => { - dataPassesPerLhcPeriodOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + const { filteringModel, sortModel, pagination, items } = dataPassesPerLhcPeriodOverviewModel; + + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); return h('', { onremove: () => dataPassesPerLhcPeriodOverviewModel.reset(), }, [ - h('.flex-row.header-container.pv2', filtersPanelPopover(dataPassesPerLhcPeriodOverviewModel.filteringModel, dataPassesActiveColumns)), + h('.flex-row.header-container.pv2', filtersPanelPopover(filteringModel, dataPassesActiveColumns)), + warningComponent(dataPassesPerLhcPeriodOverviewModel), h('.w-100.flex-column', [ - table( - dataPassesPerLhcPeriodOverviewModel.items, - dataPassesActiveColumns, - { classes: getRowClasses }, - null, - { sort: dataPassesPerLhcPeriodOverviewModel.sortModel }, - ), - paginationComponent(dataPassesPerLhcPeriodOverviewModel.pagination), + table(items, dataPassesActiveColumns, { classes: getRowClasses }, null, { sort: sortModel }), + paginationComponent(pagination), ]), ]); }; diff --git a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js index d9b1008552..87e6d72cb7 100644 --- a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js +++ b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewModel.js @@ -23,9 +23,10 @@ export class DataPassesPerSimulationPassOverviewModel extends DataPassesOverview * Constructor * @param {QueryRouter} router router that controls the application's page navigation * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router, pageIdentifier) { - super(router, pageIdentifier); + constructor(router, pageIdentifier, warnings) { + super(router, pageIdentifier, warnings); this._simulationPass = new ObservableData(RemoteData.notAsked()); this._simulationPass.bubbleTo(this); } diff --git a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewPage.js b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewPage.js index 2473f3383d..6e11d594a8 100644 --- a/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewPage.js +++ b/lib/public/views/DataPasses/PerSimulationPassOverview/DataPassesPerSimulationPassOverviewPage.js @@ -22,6 +22,7 @@ import { breadcrumbs } from '../../../components/common/navigation/breadcrumbs.j import spinner from '../../../components/common/spinner.js'; import { tooltip } from '../../../components/common/popover/tooltip.js'; import { DataPassVersionStatus } from '../../../domain/enums/DataPassVersionStatus.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 42; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -46,12 +47,9 @@ const getRowClasses = ({ versions }) => { */ export const DataPassesPerSimulationPassOverviewPage = ({ dataPasses: { perSimulationPassOverviewModel: dataPassesPerSimulationPassOverviewModel } }) => { - dataPassesPerSimulationPassOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + const { items, simulationPass, pagination, filteringModel, sortModel } = dataPassesPerSimulationPassOverviewModel; - const { items, simulationPass, pagination } = dataPassesPerSimulationPassOverviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); const commonTitle = h('h2#breadcrumb-header', 'Data Passes per MC'); @@ -59,7 +57,7 @@ export const DataPassesPerSimulationPassOverviewPage = ({ dataPasses: { onremove: () => dataPassesPerSimulationPassOverviewModel.reset(), }, [ h('.flex-row.items-center.g2', [ - filtersPanelPopover(dataPassesPerSimulationPassOverviewModel.filteringModel, dataPassesActiveColumns), + filtersPanelPopover(filteringModel, dataPassesActiveColumns), h( '.flex-row.g1.items-center', simulationPass.match({ @@ -70,14 +68,9 @@ export const DataPassesPerSimulationPassOverviewPage = ({ dataPasses: { }), ), ]), + warningComponent(dataPassesPerSimulationPassOverviewModel), h('.w-100.flex-column', [ - table( - items, - dataPassesActiveColumns, - { classes: getRowClasses }, - null, - { sort: dataPassesPerSimulationPassOverviewModel.sortModel }, - ), + table(items, dataPassesActiveColumns, { classes: getRowClasses }, null, { sort: sortModel }), paginationComponent(pagination), ]), ]); diff --git a/lib/public/views/Environments/EnvironmentModel.js b/lib/public/views/Environments/EnvironmentModel.js index 1cc7fa484d..f53b5cf9c5 100644 --- a/lib/public/views/Environments/EnvironmentModel.js +++ b/lib/public/views/Environments/EnvironmentModel.js @@ -29,7 +29,7 @@ export class EnvironmentModel extends Observable { super(); // Sub-models - this._overviewModel = new EnvironmentOverviewModel(model, 'env-overview'); + this._overviewModel = new EnvironmentOverviewModel(model, 'env-overview', model.warnings); this._overviewModel.bubbleTo(this); this._detailsModel = new EnvironmentDetailsModel(); diff --git a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js index 59ca3961f5..90d42ccecf 100644 --- a/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js +++ b/lib/public/views/Environments/Overview/EnvironmentOverviewModel.js @@ -29,9 +29,10 @@ export class EnvironmentOverviewModel extends OverviewPageModel { * Constructor * @param {Model} model global model * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(model, pageIdentifier) { - super(); + constructor(model, pageIdentifier, warnings) { + super(warnings); this._filteringModel = new FilteringModel( model.router, @@ -48,6 +49,7 @@ export class EnvironmentOverviewModel extends OverviewPageModel { }), ids: new RawTextFilterModel(), }, + this._warnings, ); this._filteringModel.pageIdentifier = pageIdentifier; diff --git a/lib/public/views/Environments/Overview/environmentOverviewComponent.js b/lib/public/views/Environments/Overview/environmentOverviewComponent.js index 7cc60ecd22..df8f5a332d 100644 --- a/lib/public/views/Environments/Overview/environmentOverviewComponent.js +++ b/lib/public/views/Environments/Overview/environmentOverviewComponent.js @@ -17,6 +17,7 @@ import { environmentsActiveColumns } from '../ActiveColumns/environmentsActiveCo import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplayableRowsCount.js'; import { paginationComponent } from '../../../components/Pagination/paginationComponent.js'; import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 58; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -30,16 +31,14 @@ const PAGE_USED_HEIGHT = 181; export const environmentOverviewComponent = (envsOverviewModel) => { const { pagination, environments } = envsOverviewModel; - pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); return h('', [ h( '.flex-row.header-container.g2.pv2', filtersPanelPopover(envsOverviewModel, environmentsActiveColumns), ), + warningComponent(envsOverviewModel), h('.w-100.flex-column', [ h('.header-container.pv2'), table(environments, environmentsActiveColumns, { classes: 'table-sm' }), diff --git a/lib/public/views/LhcFills/LhcFills.js b/lib/public/views/LhcFills/LhcFills.js index a4343be26a..7733cea457 100644 --- a/lib/public/views/LhcFills/LhcFills.js +++ b/lib/public/views/LhcFills/LhcFills.js @@ -29,7 +29,7 @@ export default class LhcFills extends Observable { this.model = model; // Sub-models - this._overviewModel = new LhcFillsOverviewModel(model.router, true, 'lhc-fill-overview'); + this._overviewModel = new LhcFillsOverviewModel(model.router, true, 'lhc-fill-overview', model.warnings); this._overviewModel.bubbleTo(this); this._detailsModel = new LhcFillDetailsModel(); diff --git a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js index 8855e3fac5..fa5177b64b 100644 --- a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js +++ b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js @@ -33,9 +33,10 @@ export class LhcFillsOverviewModel extends OverviewPageModel { * @param {QueryRouter} router router that controls the application's page navigation * @param {boolean} [stableBeamsOnly=false] if true, overview will load stable beam only * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router, stableBeamsOnly = false, pageIdentifier) { - super(); + constructor(router, stableBeamsOnly = false, pageIdentifier, warnings) { + super(warnings); this._filteringModel = new FilteringModel( router, @@ -49,6 +50,7 @@ export class LhcFillsOverviewModel extends OverviewPageModel { beamTypes: new BeamTypeFilterModel(), schemeName: new RawTextFilterModel(), }, + this._warnings, ); this._filteringModel.pageIdentifier = pageIdentifier; diff --git a/lib/public/views/LhcFills/Overview/index.js b/lib/public/views/LhcFills/Overview/index.js index a29abf5145..f790bb9957 100644 --- a/lib/public/views/LhcFills/Overview/index.js +++ b/lib/public/views/LhcFills/Overview/index.js @@ -19,6 +19,7 @@ import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplay import { paginationComponent } from '../../../components/Pagination/paginationComponent.js'; import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 53.3; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -41,20 +42,18 @@ export const Index = (model) => h('', { * @returns {Object} Html page */ const showLhcFillsTable = (lhcFillsOverviewModel) => { - lhcFillsOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - 1, - )); + const { items, pagination, filteringModel } = lhcFillsOverviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT, 1)); return [ h('.flex-row.header-container.g2.pv2', [ filtersPanelPopover(lhcFillsOverviewModel, lhcFillsActiveColumns), - toggleFilter(lhcFillsOverviewModel.filteringModel.get('hasStableBeams'), 'STABLE BEAM ONLY'), + toggleFilter(filteringModel.get('hasStableBeams'), 'STABLE BEAM ONLY'), ]), + warningComponent(lhcFillsOverviewModel), h('.w-100.flex-column', [ - table(lhcFillsOverviewModel.items, lhcFillsActiveColumns, null, { tableClasses: '.table-sm' }), - paginationComponent(lhcFillsOverviewModel.pagination), + table(items, lhcFillsActiveColumns, null, { tableClasses: '.table-sm' }), + paginationComponent(pagination), ]), ]; }; diff --git a/lib/public/views/Logs/Overview/LogsOverviewModel.js b/lib/public/views/Logs/Overview/LogsOverviewModel.js index d0ba79ef33..f55fea52fd 100644 --- a/lib/public/views/Logs/Overview/LogsOverviewModel.js +++ b/lib/public/views/Logs/Overview/LogsOverviewModel.js @@ -38,6 +38,7 @@ export class LogsOverviewModel extends Observable { */ constructor(model, excludeAnonymous = false, pageIdentifier) { super(); + this._warnings = model.warnings; this._filteringModel = new FilteringModel( model.router, @@ -51,6 +52,7 @@ export class LogsOverviewModel extends Observable { fillNumbers: new RawTextFilterModel(), created: new TimeRangeInputModel(), }, + this._warnings, ); this._overviewSortModel = new SortModel(); @@ -183,6 +185,15 @@ export class LogsOverviewModel extends Observable { return this._pagination; } + /** + * Returns the warnings object + * + * @return {object} the warning model + */ + get warnings() { + return this._warnings; + } + /** * Apply the current filtering and update the remote data list * diff --git a/lib/public/views/Logs/Overview/index.js b/lib/public/views/Logs/Overview/index.js index 93f58c8c40..babeb820e5 100644 --- a/lib/public/views/Logs/Overview/index.js +++ b/lib/public/views/Logs/Overview/index.js @@ -19,6 +19,7 @@ import { paginationComponent } from '../../../components/Pagination/paginationCo import { frontLink } from '../../../components/common/navigation/frontLink.js'; import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; import { excludeAnonymousLogAuthorToggle } from '../../../components/Filters/LogsFilter/author/authorFilter.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 69; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -30,22 +31,22 @@ const PAGE_USED_HEIGHT = 215; * @return {Component} Returns a vnode with the table containing the logs */ const logOverviewScreen = ({ logs: { overviewModel: logsOverviewModel } }) => { - logsOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + const { pagination, filteringModel, logs, overviewSortModel } = logsOverviewModel; + + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); return h('', [ h('#main-action-bar.flex-row.justify-between.header-container.pv2', [ h('.flex-row.g3', [ filtersPanelPopover(logsOverviewModel, logsActiveColumns), - excludeAnonymousLogAuthorToggle(logsOverviewModel.filteringModel.get('author')), + excludeAnonymousLogAuthorToggle(filteringModel.get('author')), ]), actionButtons(), ]), + warningComponent(logsOverviewModel), h('.w-100.flex-column', [ - table(logsOverviewModel.logs, logsActiveColumns, null, null, { sort: logsOverviewModel.overviewSortModel }), - paginationComponent(logsOverviewModel.pagination), + table(logs, logsActiveColumns, null, null, { sort: overviewSortModel }), + paginationComponent(pagination), ]), ]); }; diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js index 531889a6c5..b084cc7ce8 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js @@ -25,9 +25,10 @@ export class QcFlagTypesOverviewModel extends OverviewPageModel { * Constructor * @param {QueryRouter} router router that controls the application's page navigation * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router, pageIdentifier) { - super(); + constructor(router, pageIdentifier, warnings) { + super(warnings); this._filteringModel = new FilteringModel( router, @@ -36,6 +37,7 @@ export class QcFlagTypesOverviewModel extends OverviewPageModel { methods: new TextTokensFilterModel(), bad: new RadioButtonFilterModel([{ label: 'Any' }, { label: 'Bad', value: true }, { label: 'Not Bad', value: false }]), }, + this._warnings, ); this._filteringModel.pageIdentifier = pageIdentifier; diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewPage.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewPage.js index 6b2a818527..0c3fb2a71e 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewPage.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewPage.js @@ -19,6 +19,7 @@ import { qcFlagTypesActiveColumns } from '../ActiveColumns/qcFlagTypesActiveColu import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; import { frontLink } from '../../../components/common/navigation/frontLink.js'; import { BkpRoles } from '../../../domain/enums/BkpRoles.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 30; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -30,12 +31,9 @@ const PAGE_USED_HEIGHT = 215; * @return {Component} The overview page */ export const QcFlagTypesOverviewPage = ({ qcFlagTypes: { overviewModel } }) => { - overviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + const { items: qcFlagTypes, pagination, sortModel } = overviewModel; - const { items: qcFlagTypes } = overviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); return h('', [ h('.flex-row.justify-between.items-center.g2', [ @@ -50,15 +48,10 @@ export const QcFlagTypesOverviewPage = ({ qcFlagTypes: { overviewModel } }) => { }), ], ]), + warningComponent(overviewModel), h('.flex-column.w-100', [ - table( - qcFlagTypes, - qcFlagTypesActiveColumns, - { classes: '.table-sm' }, - null, - { sort: overviewModel.sortModel }, - ), - paginationComponent(overviewModel.pagination), + table(qcFlagTypes, qcFlagTypesActiveColumns, { classes: '.table-sm' }, null, { sort: sortModel }), + paginationComponent(pagination), ]), ]); }; diff --git a/lib/public/views/QcFlagTypes/QcFlagTypesModel.js b/lib/public/views/QcFlagTypes/QcFlagTypesModel.js index 9fe8118a76..edb73afb2c 100644 --- a/lib/public/views/QcFlagTypes/QcFlagTypesModel.js +++ b/lib/public/views/QcFlagTypes/QcFlagTypesModel.js @@ -29,7 +29,7 @@ export class QcFlagTypesModel extends Observable { this.model = model; // Overview - this._overviewModel = new QcFlagTypesOverviewModel(model.router, 'qc-flag-types-overview'); + this._overviewModel = new QcFlagTypesOverviewModel(model.router, 'qc-flag-types-overview', model.warnings); this._overviewModel.bubbleTo(this); } diff --git a/lib/public/views/QcFlags/ForDataPass/QcFlagsForDataPassOverviewModel.js b/lib/public/views/QcFlags/ForDataPass/QcFlagsForDataPassOverviewModel.js index 20a919657f..2dce5f925a 100644 --- a/lib/public/views/QcFlags/ForDataPass/QcFlagsForDataPassOverviewModel.js +++ b/lib/public/views/QcFlags/ForDataPass/QcFlagsForDataPassOverviewModel.js @@ -25,9 +25,10 @@ import { DetectorType } from '../../../domain/enums/DetectorTypes.js'; export class QcFlagsForDataPassOverviewModel extends QcFlagsOverviewModel { /** * The constructor of the Overview model object + * @param {Map} warnings a map storing warnings to show to the user */ - constructor() { - super(); + constructor(warnings) { + super(warnings); this._dataPass$ = new ObservableData(RemoteData.notAsked()); this._dataPass$.bubbleTo(this); } diff --git a/lib/public/views/QcFlags/ForSimulationPass/QcFlagsForSimulationPassOverviewModel.js b/lib/public/views/QcFlags/ForSimulationPass/QcFlagsForSimulationPassOverviewModel.js index f0d20714fe..6bfe2ac94d 100644 --- a/lib/public/views/QcFlags/ForSimulationPass/QcFlagsForSimulationPassOverviewModel.js +++ b/lib/public/views/QcFlags/ForSimulationPass/QcFlagsForSimulationPassOverviewModel.js @@ -24,9 +24,10 @@ import { ObservableData } from '../../../utilities/ObservableData.js'; export class QcFlagsForSimulationPassOverviewModel extends QcFlagsOverviewModel { /** * The constructor of the Overview model object + * @param {Map} warnings a map storing warnings to show to the user */ - constructor() { - super(); + constructor(warnings) { + super(warnings); this._simulationPass$ = new ObservableData(RemoteData.notAsked()); this._simulationPass$.bubbleTo(this); } diff --git a/lib/public/views/QcFlags/GaqFlags/GaqFlagsOverviewModel.js b/lib/public/views/QcFlags/GaqFlags/GaqFlagsOverviewModel.js index bf38d822b0..a1d99cf23e 100644 --- a/lib/public/views/QcFlags/GaqFlags/GaqFlagsOverviewModel.js +++ b/lib/public/views/QcFlags/GaqFlags/GaqFlagsOverviewModel.js @@ -24,9 +24,10 @@ import { PhysicalDetectorPickerModel } from '../../../components/detector/Physic export class GaqFlagsOverviewModel extends OverviewPageModel { /** * The constructor of the Overview model object + * @param {Map} warnings a map storing warnings to show to the user */ - constructor() { - super(); + constructor(warnings) { + super(warnings); this._run$ = new ObservableData(RemoteData.notAsked()); this._run$.bubbleTo(this); diff --git a/lib/public/views/QcFlags/Overview/QcFlagsOverviewModel.js b/lib/public/views/QcFlags/Overview/QcFlagsOverviewModel.js index 5eaa5d58f0..8264eb3a80 100644 --- a/lib/public/views/QcFlags/Overview/QcFlagsOverviewModel.js +++ b/lib/public/views/QcFlags/Overview/QcFlagsOverviewModel.js @@ -26,9 +26,10 @@ import { detectorsProvider } from '../../../services/detectors/detectorsProvider export class QcFlagsOverviewModel extends OverviewPageModel { /** * The constructor of the Overview model object + * @param {Map} warnings a map storing warnings to show to the user */ - constructor() { - super(); + constructor(warnings) { + super(warnings); detectorsProvider.qc$.observe(() => this._getDetector()); this._dplDetector$ = new ObservableData(RemoteData.notAsked()); this._dplDetector$.bubbleTo(this); diff --git a/lib/public/views/QcFlags/QcFlagsModel.js b/lib/public/views/QcFlags/QcFlagsModel.js index 9cb026aa8a..81f5edf242 100644 --- a/lib/public/views/QcFlags/QcFlagsModel.js +++ b/lib/public/views/QcFlags/QcFlagsModel.js @@ -33,16 +33,16 @@ export class QcFlagsModel extends Observable { super(); this.model = model; - this._forDataPassOverviewModel = new QcFlagsForDataPassOverviewModel(); + this._forDataPassOverviewModel = new QcFlagsForDataPassOverviewModel(model.warnings); this._forDataPassOverviewModel.bubbleTo(this); - this._forSimulationPassOverviewModel = new QcFlagsForSimulationPassOverviewModel(); + this._forSimulationPassOverviewModel = new QcFlagsForSimulationPassOverviewModel(model.warnings); this._forSimulationPassOverviewModel.bubbleTo(this); - this._synchronousOverviewModel = new SynchronousQcFlagsOverviewModel(); + this._synchronousOverviewModel = new SynchronousQcFlagsOverviewModel(model.warnings); this._synchronousOverviewModel.bubbleTo(this); - this._gaqFlagsOverviewModel = new GaqFlagsOverviewModel(); + this._gaqFlagsOverviewModel = new GaqFlagsOverviewModel(model.warnings); this._gaqFlagsOverviewModel.bubbleTo(this); } diff --git a/lib/public/views/QcFlags/Synchronous/SynchronousQcFlagsOverviewModel.js b/lib/public/views/QcFlags/Synchronous/SynchronousQcFlagsOverviewModel.js index 0428a4020d..552c05703e 100644 --- a/lib/public/views/QcFlags/Synchronous/SynchronousQcFlagsOverviewModel.js +++ b/lib/public/views/QcFlags/Synchronous/SynchronousQcFlagsOverviewModel.js @@ -22,9 +22,10 @@ import { buildUrl } from '/js/src/index.js'; export class SynchronousQcFlagsOverviewModel extends QcFlagsOverviewModel { /** * The constructor of the Overview model object + * @param {Map} warnings a map storing warnings to show to the user */ - constructor() { - super(); + constructor(warnings) { + super(warnings); } /** diff --git a/lib/public/views/Runs/Overview/FixedPdpBeamTypeRunsOverviewModel.js b/lib/public/views/Runs/Overview/FixedPdpBeamTypeRunsOverviewModel.js index 5edce4c14e..1ebc0b530e 100644 --- a/lib/public/views/Runs/Overview/FixedPdpBeamTypeRunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/FixedPdpBeamTypeRunsOverviewModel.js @@ -24,9 +24,10 @@ export class FixedPdpBeamTypeRunsOverviewModel extends RunsWithQcModel { * Constructor * @param {Model} model global model * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(model, pageIdentifier) { - super(model, pageIdentifier); + constructor(model, pageIdentifier, warnings) { + super(model, pageIdentifier, warnings); this._pdpBeamTypes = []; } diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 53bcffd59f..f731d87b50 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -49,9 +49,10 @@ export class RunsOverviewModel extends OverviewPageModel { * The constructor of the Overview model object * @param {Model} model global model * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(model, pageIdentifier) { - super(); + constructor(model, pageIdentifier, warnings) { + super(warnings); this._filteringModel = new FilteringModel( model.router, @@ -100,6 +101,7 @@ export class RunsOverviewModel extends OverviewPageModel { epn: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), triggerValues: new SelectionModel({ availableOptions: TRIGGER_VALUES.map((value) => ({ label: value, value })) }), }, + this._warnings, ); this._filteringModel.pageIdentifier = pageIdentifier; diff --git a/lib/public/views/Runs/Overview/RunsOverviewPage.js b/lib/public/views/Runs/Overview/RunsOverviewPage.js index 6355872b9e..4f76d417d9 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewPage.js +++ b/lib/public/views/Runs/Overview/RunsOverviewPage.js @@ -20,6 +20,7 @@ import { table } from '../../../components/common/table/table.js'; import { switchInput } from '../../../components/common/form/switchInput.js'; import { exportTriggerAndModal } from '../../../components/common/dataExport/exportTriggerAndModal.js'; import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 59; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -46,21 +47,20 @@ export const togglePhysicsOnlyFilter = (runDefinitionFilterModel) => { * @return {Component} Returns a vnode with the table containing the runs */ export const RunsOverviewPage = ({ runs: { overviewModel: runsOverviewModel }, modalModel }) => { - runsOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + const { pagination, items, exportModel, filteringModel } = runsOverviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); return h('', [ h('.flex-row.header-container.g2.pv2', [ filtersPanelPopover(runsOverviewModel, runsActiveColumns), h('.pl2#runOverviewFilter', textInputFilter(runsOverviewModel.filteringModel, 'runNumbers', 'e.g. 534454, 534455...')), - togglePhysicsOnlyFilter(runsOverviewModel.filteringModel.get('definitions')), - exportTriggerAndModal(runsOverviewModel.exportModel, modalModel), + togglePhysicsOnlyFilter(filteringModel.get('definitions')), + exportTriggerAndModal(exportModel, modalModel), ]), + warningComponent(runsOverviewModel), h('.flex-column.w-100', [ - table(runsOverviewModel.items, runsActiveColumns), - paginationComponent(runsOverviewModel.pagination), + table(items, runsActiveColumns), + paginationComponent(pagination), ]), ]); }; diff --git a/lib/public/views/Runs/Overview/RunsWithQcModel.js b/lib/public/views/Runs/Overview/RunsWithQcModel.js index cc1687cf30..fa1bfedf33 100644 --- a/lib/public/views/Runs/Overview/RunsWithQcModel.js +++ b/lib/public/views/Runs/Overview/RunsWithQcModel.js @@ -68,10 +68,11 @@ export class RunsWithQcModel extends RunsOverviewModel { /** * Constructor * @param {Model} model global model + * @param {Map} warnings a map storing warnings to show to the user * @param {string} pageIdentifier string that indicates what page this model represents */ - constructor(model, pageIdentifier) { - super(model, pageIdentifier); + constructor(model, pageIdentifier, warnings) { + super(model, pageIdentifier, warnings); this._detectorsNotBadFractionRegistered = false; this._detectorsForQcFlagRegistered = false; diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js index 5bceeeeeb4..0f167fa875 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js @@ -36,9 +36,10 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo * Constructor * @param {Model} model global model * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(model, pageIdentifier) { - super(model, pageIdentifier); + constructor(model, pageIdentifier, warnings) { + super(model, pageIdentifier, warnings); this._dataPass$ = new ObservableData(RemoteData.notAsked()); this._dataPass$.bubbleTo(this); diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js index 59f396515c..fd847389f5 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js @@ -39,6 +39,7 @@ import { getInelasticInteractionRateColumns } from '../ActiveColumns/getInelasti import { exportTriggerAndModal } from '../../../components/common/dataExport/exportTriggerAndModal.js'; import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js'; import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 59; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -297,6 +298,7 @@ export const RunsPerDataPassOverviewPage = ({ { alignment: 'right' }, )), ]), + warningComponent(perDataPassOverviewModel), h( '.intermediate-flex-column', { onremove: () => perDataPassOverviewModel._abortGaqFetches() }, diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js index 2ae78a395c..05dafe6e3b 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewModel.js @@ -32,9 +32,10 @@ export class RunsPerLhcPeriodOverviewModel extends FixedPdpBeamTypeRunsOverviewM * * @param {Model} model global model * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(model, pageIdentifier) { - super(model, pageIdentifier); + constructor(model, pageIdentifier, warnings) { + super(model, pageIdentifier, warnings); this._lhcPeriodId = null; this._lhcPeriodStatistics$ = new ObservableData(RemoteData.notAsked()); diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js index 12ccefb1b7..4a08a95565 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js @@ -29,6 +29,7 @@ import { filtersPanelPopover } from '../../../components/Filters/common/filtersP import { exportTriggerAndModal } from '../../../components/common/dataExport/exportTriggerAndModal.js'; import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js'; import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 62; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -50,11 +51,6 @@ const getRowClasses = (run) => isRunNotSubjectToQc(run) ? '.danger' : null; * @return {Component} The overview page */ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel }, modalModel }) => { - perLhcPeriodOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); - const { items: remoteRuns, lhcPeriodStatistics: remoteLhcPeriodStatistics, @@ -66,8 +62,11 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel mcReproducibleAsNotBad, qcSummary: remoteQcSummary, pdpBeamTypes, + pagination, } = perLhcPeriodOverviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); + /** * Render runs table with given detectors' active columns configuration * @@ -110,6 +109,7 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel filtersPanelPopover(perLhcPeriodOverviewModel, activeColumns, { profile: 'runsPerLhcPeriod' }), h('.pl2#runOverviewFilter', textInputFilter(perLhcPeriodOverviewModel.filteringModel, 'runNumbers', 'e.g. 534454, 534455...')), h('h2', ['Good, physics runs of ', lhcPeriodName]), + warningComponent(perLhcPeriodOverviewModel), toggleFilter(mcReproducibleAsNotBad, h('em', 'MC.R as not-bad'), 'mcReproducibleAsNotBadToggle'), exportTriggerAndModal(perLhcPeriodOverviewModel.exportModel, modalModel), ]), @@ -153,7 +153,7 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel }, { panelClass: ['scroll-auto'] }, ), - paginationComponent(perLhcPeriodOverviewModel.pagination), + paginationComponent(pagination), ] }), ), ]; diff --git a/lib/public/views/Runs/RunsModel.js b/lib/public/views/Runs/RunsModel.js index 007a456368..d1b4890eb9 100644 --- a/lib/public/views/Runs/RunsModel.js +++ b/lib/public/views/Runs/RunsModel.js @@ -32,13 +32,13 @@ export class RunsModel extends Observable { super(); this._detailsModel = new RunDetailsModel(); this._detailsModel.bubbleTo(this); - this._overviewModel = new RunsOverviewModel(model, 'run-overview'); + this._overviewModel = new RunsOverviewModel(model, 'run-overview', model.warnings); this._overviewModel.bubbleTo(this); - this._perLhcPeriodOverviewModel = new RunsPerLhcPeriodOverviewModel(model, 'runs-per-lhc-period'); + this._perLhcPeriodOverviewModel = new RunsPerLhcPeriodOverviewModel(model, 'runs-per-lhc-period', model.warnings); this._perLhcPeriodOverviewModel.bubbleTo(this); - this._perDataPassOverviewModel = new RunsPerDataPassOverviewModel(model, 'runs-per-data-pass'); + this._perDataPassOverviewModel = new RunsPerDataPassOverviewModel(model, 'runs-per-data-pass', model.warnings); this._perDataPassOverviewModel.bubbleTo(this); - this._perSimulationPassOverviewModel = new RunsPerSimulationPassOverviewModel(model, 'runs-per-simulation-pass'); + this._perSimulationPassOverviewModel = new RunsPerSimulationPassOverviewModel(model, 'runs-per-simulation-pass', model.warnings); this._perSimulationPassOverviewModel.bubbleTo(this); } diff --git a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js index 084b57d130..a629e86619 100644 --- a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js +++ b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewModel.js @@ -24,9 +24,10 @@ export class RunsPerSimulationPassOverviewModel extends FixedPdpBeamTypeRunsOver * Constructor * @param {Model} model global model * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(model, pageIdentifier) { - super(model, pageIdentifier); + constructor(model, pageIdentifier, warnings) { + super(model, pageIdentifier, warnings); this._simulationPass$ = new ObservableData(RemoteData.notAsked()); diff --git a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js index 3b4ad6157f..c64fcbe6c8 100644 --- a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js +++ b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js @@ -29,6 +29,7 @@ import { exportTriggerAndModal } from '../../../components/common/dataExport/exp import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 59; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -52,11 +53,6 @@ export const RunsPerSimulationPassOverviewPage = ({ dplDetectorsUserHasAccessTo: remoteDplDetectorsUserHasAccessTo, modalModel, }) => { - perSimulationPassOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); - const { items: remoteRuns, detectors: remoteDetectors, @@ -67,8 +63,11 @@ export const RunsPerSimulationPassOverviewPage = ({ sortModel, pdpBeamTypes, mcReproducibleAsNotBad, + pagination, } = perSimulationPassOverviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); + const commonTitle = h('h2', 'Runs per MC'); const fullPageData = mergeRemoteData([remoteRuns, remoteSimulationPass, remoteDetectors, remoteQcSummary]); @@ -120,6 +119,7 @@ export const RunsPerSimulationPassOverviewPage = ({ }, ), ]), + warningComponent(perSimulationPassOverviewModel), h( '.intermediate-flex-column', fullPageData.match({ @@ -136,7 +136,7 @@ export const RunsPerSimulationPassOverviewPage = ({ }, { sort: sortModel }, ), - paginationComponent(perSimulationPassOverviewModel.pagination), + paginationComponent(pagination), ], Loading: () => spinner(), }), diff --git a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js index 6fb3af5ea8..90c94bbb86 100644 --- a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js +++ b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewModel.js @@ -25,11 +25,12 @@ export class AnchoredSimulationPassesOverviewModel extends OverviewPageModel { * Constructor * @param {QueryRouter} router router that controls the application's page navigation * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router, pageIdentifier) { - super(); + constructor(router, pageIdentifier, warnings) { + super(warnings); - this._filteringModel = new FilteringModel(router, { names: new TextTokensFilterModel() }); + this._filteringModel = new FilteringModel(router, { names: new TextTokensFilterModel() }, this._warnings); this._filteringModel.pageIdentifier = pageIdentifier; this._filteringModel.observe(() => { diff --git a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewPage.js b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewPage.js index 5894ba1a05..f9f752836c 100644 --- a/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewPage.js +++ b/lib/public/views/SimulationPasses/AnchoredOverview/AnchoredSimulationPassesOverviewPage.js @@ -21,6 +21,7 @@ import { simulationPassesActiveColumns } from '../ActiveColumns/simulationPasses import { breadcrumbs } from '../../../components/common/navigation/breadcrumbs.js'; import spinner from '../../../components/common/spinner.js'; import { tooltip } from '../../../components/common/popover/tooltip.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 42; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -34,13 +35,9 @@ const PAGE_USED_HEIGHT = 215; export const AnchoredSimulationPassesOverviewPage = ({ simulationPasses: { anchoredOverviewModel: anchoredSimulationPassesOverviewModel }, }) => { - anchoredSimulationPassesOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); - - const { items, dataPass, pagination } = anchoredSimulationPassesOverviewModel; + const { items, dataPass, pagination, sortModel } = anchoredSimulationPassesOverviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); const commonTitle = h('h2#breadcrumb-header', { style: 'white-space: nowrap;' }, 'Anchored MC'); return h( @@ -61,13 +58,14 @@ export const AnchoredSimulationPassesOverviewPage = ({ }), ), ]), + warningComponent(anchoredSimulationPassesOverviewModel), h('.w-100.flex-column', [ table( items, simulationPassesActiveColumns, { classes: '.table-sm' }, null, - { sort: anchoredSimulationPassesOverviewModel.sortModel }, + { sort: sortModel }, ), paginationComponent(pagination), ]), diff --git a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js index e3651ce6e4..1a42fb4e6c 100644 --- a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js +++ b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewModel.js @@ -25,11 +25,12 @@ export class SimulationPassesPerLhcPeriodOverviewModel extends OverviewPageModel * Constructor * @param {QueryRouter} router router that controls the application's page navigation * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router, pageIdentifier) { - super(); + constructor(router, pageIdentifier, warnings) { + super(warnings); - this._filteringModel = new FilteringModel(router, { names: new TextTokensFilterModel() }); + this._filteringModel = new FilteringModel(router, { names: new TextTokensFilterModel() }, this._warnings); this._filteringModel.pageIdentifier = pageIdentifier; this._filteringModel.visualChange$.bubbleTo(this); diff --git a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewPage.js b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewPage.js index 3cc12756d0..0d2961b5f3 100644 --- a/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewPage.js +++ b/lib/public/views/SimulationPasses/PerLhcPeriodOverview/SimulationPassesPerLhcPeriodOverviewPage.js @@ -21,6 +21,7 @@ import { simulationPassesActiveColumns } from '../ActiveColumns/simulationPasses import spinner from '../../../components/common/spinner.js'; import { tooltip } from '../../../components/common/popover/tooltip.js'; import { breadcrumbs } from '../../../components/common/navigation/breadcrumbs.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 42; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -33,12 +34,9 @@ const PAGE_USED_HEIGHT = 215; */ export const SimulationPassesPerLhcPeriodOverviewPage = ({ simulationPasses: { perLhcPeriodOverviewModel: simulationPassesPerLhcPeriodOverviewModel } }) => { - simulationPassesPerLhcPeriodOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + const { items: simulationPasses, lhcPeriod, pagination, sortModel } = simulationPassesPerLhcPeriodOverviewModel; - const { items: simulationPasses, lhcPeriod } = simulationPassesPerLhcPeriodOverviewModel; + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); const commonTitle = h('h2#breadcrumb-header', { style: 'white-space: nowrap;' }, 'Monte Carlo'); @@ -57,15 +55,10 @@ export const SimulationPassesPerLhcPeriodOverviewPage = ({ simulationPasses: { }), ), ]), + warningComponent(simulationPassesPerLhcPeriodOverviewModel), h('.w-100.flex-column', [ - table( - simulationPasses, - simulationPassesActiveColumns, - { classes: '.table-sm' }, - null, - { sort: simulationPassesPerLhcPeriodOverviewModel.sortModel }, - ), - paginationComponent(simulationPassesPerLhcPeriodOverviewModel.pagination), + table(simulationPasses, simulationPassesActiveColumns, { classes: '.table-sm' }, null, { sort: sortModel }), + paginationComponent(pagination), ]), ]); }; diff --git a/lib/public/views/SimulationPasses/SimulationPassesModel.js b/lib/public/views/SimulationPasses/SimulationPassesModel.js index 8ba624efd8..e89ccd6deb 100644 --- a/lib/public/views/SimulationPasses/SimulationPassesModel.js +++ b/lib/public/views/SimulationPasses/SimulationPassesModel.js @@ -22,14 +22,16 @@ export class SimulationPassesModel extends Observable { /** * The constructor of the model * @param {QueryRouter} router router that controls the application's page navigation + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router) { + constructor(router, warnings) { super(); - this._perLhcPeriodOverviewModel = new SimulationPassesPerLhcPeriodOverviewModel(router, 'simulation-passes-per-lhc-period-overview'); + this._perLhcPeriodOverviewModel = + new SimulationPassesPerLhcPeriodOverviewModel(router, 'simulation-passes-per-lhc-period-overview', warnings); this._perLhcPeriodOverviewModel.bubbleTo(this); - this._anchoredOverviewModel = new AnchoredSimulationPassesOverviewModel(router, 'anchored-simulation-passes-overview'); + this._anchoredOverviewModel = new AnchoredSimulationPassesOverviewModel(router, 'anchored-simulation-passes-overview', warnings); this._anchoredOverviewModel.bubbleTo(this); } diff --git a/lib/public/views/lhcPeriods/LhcPeriodsModel.js b/lib/public/views/lhcPeriods/LhcPeriodsModel.js index 74df7b9dc7..4f1122d921 100644 --- a/lib/public/views/lhcPeriods/LhcPeriodsModel.js +++ b/lib/public/views/lhcPeriods/LhcPeriodsModel.js @@ -21,11 +21,12 @@ export class LhcPeriodsModel extends Observable { /** * The constructor of the model * @param {QueryRouter} router router that controls the application's page navigation + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router) { + constructor(router, warnings) { super(); - this._overviewModel = new LhcPeriodsOverviewModel(router, 'lhc-period-overview'); + this._overviewModel = new LhcPeriodsOverviewModel(router, 'lhc-period-overview', warnings); this._overviewModel.bubbleTo(this); } diff --git a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js index 944032bef8..669e06c287 100644 --- a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js +++ b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewModel.js @@ -26,9 +26,10 @@ export class LhcPeriodsOverviewModel extends OverviewPageModel { * The constructor of the Overview model object * @param {QueryRouter} router router that controls the application's page navigation * @param {string} pageIdentifier string that indicates what page this model represents + * @param {Map} warnings a map storing warnings to show to the user */ - constructor(router, pageIdentifier) { - super(); + constructor(router, pageIdentifier, warnings) { + super(warnings); this._filteringModel = new FilteringModel( router, @@ -37,6 +38,7 @@ export class LhcPeriodsOverviewModel extends OverviewPageModel { years: new TextTokensFilterModel(), pdpBeamTypes: new TextTokensFilterModel(), }, + this._warnings, ); this._filteringModel.pageIdentifier = pageIdentifier; diff --git a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewPage.js b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewPage.js index b431c62d42..89c0def48c 100644 --- a/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewPage.js +++ b/lib/public/views/lhcPeriods/Overview/LhcPeriodsOverviewPage.js @@ -18,6 +18,7 @@ import { lhcPeriodsActiveColumns } from '../ActiveColumns/lhcPeriodsActiveColumn import { paginationComponent } from '../../../components/Pagination/paginationComponent.js'; import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplayableRowsCount.js'; +import { warningComponent } from '../../../components/common/messages/warningComponent.js'; const TABLEROW_HEIGHT = 35; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -29,22 +30,19 @@ const PAGE_USED_HEIGHT = 215; * @returns {Component} The overview screen */ export const LhcPeriodsOverviewPage = ({ lhcPeriods: { overviewModel: lhcPeriodsOverviewModel } }) => { - lhcPeriodsOverviewModel.pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount( - TABLEROW_HEIGHT, - PAGE_USED_HEIGHT, - )); + const { sortModel, pagination, items } = lhcPeriodsOverviewModel; + + pagination.provideDefaultItemsPerPage(estimateDisplayableRowsCount(TABLEROW_HEIGHT, PAGE_USED_HEIGHT)); return h('', [ - h('.flex-row.header-container.pv2', filtersPanelPopover(lhcPeriodsOverviewModel, lhcPeriodsActiveColumns)), + h( + '.flex-row.header-container.pv2', + filtersPanelPopover(lhcPeriodsOverviewModel, lhcPeriodsActiveColumns), + ), + warningComponent(lhcPeriodsOverviewModel), h('.w-100.flex-column', [ - table( - lhcPeriodsOverviewModel.items, - lhcPeriodsActiveColumns, - { classes: '.table-sm' }, - null, - { sort: lhcPeriodsOverviewModel.sortModel }, - ), - paginationComponent(lhcPeriodsOverviewModel.pagination), + table(items, lhcPeriodsActiveColumns, { classes: '.table-sm' }, null, { sort: sortModel }), + paginationComponent(pagination), ]), ]); }; diff --git a/test/public/components/index.js b/test/public/components/index.js index 5e06743c62..700564755c 100644 --- a/test/public/components/index.js +++ b/test/public/components/index.js @@ -12,7 +12,9 @@ */ const NavBarSuite = require('./navBar.test') +const WarningSuite = require('./warnings.test') module.exports = () => { describe('Navbar component', NavBarSuite); + describe('Warning component', WarningSuite) }; diff --git a/test/public/components/warnings.test.js b/test/public/components/warnings.test.js new file mode 100644 index 0000000000..a8a824feee --- /dev/null +++ b/test/public/components/warnings.test.js @@ -0,0 +1,58 @@ +/** + * @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. + */ + +const { expect } = require('chai'); +const { + defaultBefore, + defaultAfter, + getInnerText, + pressElement, +} = require('../defaults.js'); + +module.exports = () => { + let page; + let browser; + + before(async () => { + [page, browser] = await defaultBefore(); + }); + + it('Should show warning when a filter in the url is not recognised', async () => { + await page.goto('http://localhost:4000/?page=log-overview&filter[fake]=fake', { waitUntil: 'load' }); + const warningText = await getInnerText(await page.waitForSelector('.alert-warning > ul')); + + expect(warningText).to.equal('Unknown Filters:\nThe filters: [\'fake\']; are not reccognised. Check if they are spelled correctly.'); + }); + + it('Should remove warnings entry after clicking the x icon', async () => { + await pressElement(page, '.alert-warning .btn', true); + const warning = await page.$('.alert-warning'); + + expect(warning).to.be.null; + }); + + it('Should show warning when a url filter cannot be parsed/normalized', async () => { + await page.goto('http://localhost:4000/?page=run-overview&filter[detectors][operator]=or&filter[detecttors][values]=CTP&filter[tagss][values]=CPV&filter[tags][operation]=or', { waitUntil: 'load' }); + const unparsableWarningText = await getInnerText(await page.waitForSelector('.alert-warning > ul > li:nth-of-type(1)')); + const unknownFilterWarningText = await getInnerText(await page.waitForSelector('.alert-warning > ul > li:nth-of-type(2)')); + + // The tags and detectors filters will fail if it has no value. + // However, if the url also contains its operator, it will still attempt to set the filters, which would fail, hence the warning + expect(unparsableWarningText).to.equal('Unparsable Filters:\nThe following filter-value pairs could not be parsed: [detectors[operator]=or, tags[operation]=or]'); + expect(unknownFilterWarningText).to.equal('Unknown Filters:\nThe filters: [\'detecttors\', \'tagss\']; are not reccognised. Check if they are spelled correctly.'); + }); + + after(async () => { + await defaultAfter(page, browser); + }); +};