From 622d943e8aa0f8c4c8952cb6eb0090e6c0e16d42 Mon Sep 17 00:00:00 2001 From: freddidierRTE Date: Tue, 18 Feb 2025 09:52:08 +0100 Subject: [PATCH] Solve issue when switching from on custom screen to another Signed-off-by: freddidierRTE --- .../docker/externalJs/CustomScreenExample.js | 16 +- config/docker/ui-config/ui-menu.json | 31 +- .../reference_doc/ui_customization.adoc | 12 +- .../cypress/integration/BusinessMenu.spec.js | 61 ++-- .../cypress/integration/Translation.spec.js | 41 +-- .../customCardList/CustomCardListComponent.ts | 316 +++++++++--------- .../customCardList/CustomScreenComponent.html | 9 + .../customCardList/CustomScreenComponent.ts | 47 +++ .../router/AngularApplicationRouter.module.ts | 2 +- 9 files changed, 289 insertions(+), 246 deletions(-) create mode 100644 ui/main/src/app/components/customCardList/CustomScreenComponent.html create mode 100644 ui/main/src/app/components/customCardList/CustomScreenComponent.ts diff --git a/config/docker/externalJs/CustomScreenExample.js b/config/docker/externalJs/CustomScreenExample.js index 762db14dd1..420a9e70fe 100644 --- a/config/docker/externalJs/CustomScreenExample.js +++ b/config/docker/externalJs/CustomScreenExample.js @@ -135,13 +135,13 @@ { id: 'button1', label: 'Accept proposals', - getUserResponses: (cards, responsesData) => { + getUserResponses: (selectedCards, userInputs) => { const responseCards = []; - cards.forEach((card) => { + selectedCards.forEach((card) => { - const userInputs = responsesData.get(card.id); - const comment = userInputs?.comment ?? ''; + const userInput = userInputs.get(card.id); + const comment = userInput?.comment ?? ''; const responseData = { "choice1": "on", "choice2": "on", "choice3": "on", "comment": comment } responseCards.push({ data: responseData }); }); @@ -151,12 +151,12 @@ { id: 'button2', label: 'Refuse proposals', - getUserResponses: (cards, responsesData) => { + getUserResponses: (selectedCards, userInputs) => { const responseCards = []; let hasAlwaysComment = true; - cards.forEach((card) => { - const userInputs = responsesData.get(card.id); - const comment = userInputs?.comment ?? ''; + selectedCards.forEach((card) => { + const userInput = userInputs.get(card.id); + const comment = userInput?.comment ?? ''; if (comment === '') { hasAlwaysComment = false; } diff --git a/config/docker/ui-config/ui-menu.json b/config/docker/ui-config/ui-menu.json index 23249a9a7e..4cd6d8faba 100644 --- a/config/docker/ui-config/ui-menu.json +++ b/config/docker/ui-config/ui-menu.json @@ -41,18 +41,17 @@ "label": "entry.custom" }, { + "customMenuId": "uid_test_1", - "url": "https://opfab.github.io/", - "label": "entry.entry1", - "linkType": "BOTH", - "showOnlyForGroups": ["Maintainer", "Planner"] + "customScreenId": "testId2", + "label": "entry.custom2" }, { "customMenuId": "uid_test_2", - "url": "https://www.wikipedia.org/", - "label": "entry.entry2", + "url": "https://opfab.github.io/", + "label": "entry.doc", "linkType": "BOTH", - "showOnlyForGroups": ["Dispatcher"] + "showOnlyForGroups": ["Maintainer", "Planner"] }, { "customMenuId": "uid_test_3", @@ -146,11 +145,11 @@ }, "entry": { "single": "Single menu entry", - "entry1": "First menu entry", - "entry2": "Second menu entry", + "doc": "Opfab documentation", "entry3": "External application", "admin": "Admin menu entry", - "custom": "Custom screen" + "custom": "Custom screen", + "custom2": "Custom screen 2" } } }, @@ -164,11 +163,11 @@ }, "entry": { "single": "Unique élément", - "entry1": "Premier élément", - "entry2": "Deuxième élément", + "doc": "Documentation Opfab", "entry3": "Application externe", "admin": "Menu admin élément", - "custom": "Écran personnalisé" + "custom": "Écran personnalisé", + "custom2": "Écran personnalisé 2" } } @@ -183,11 +182,11 @@ }, "entry": { "single": "Enkel menu-item", - "entry1": "Eerste menu-item", - "entry2": "Tweede menu-item", + "doc": "Opfab documentatie", "entry3": "TExterne applicatie", "admin": "Admin menu-item", - "custom": "Aangepast scherm" + "custom": "Aangepast scherm", + "custom2": "Aangepast scherm 2" } } } diff --git a/src/docs/asciidoc/reference_doc/ui_customization.adoc b/src/docs/asciidoc/reference_doc/ui_customization.adoc index e733e9115b..bda11a820f 100644 --- a/src/docs/asciidoc/reference_doc/ui_customization.adoc +++ b/src/docs/asciidoc/reference_doc/ui_customization.adoc @@ -461,13 +461,13 @@ const customScreenExample = { { id: 'button1', label: 'Accept proposals', - getUserResponses: (cards, responsesData) => { + getUserResponses: (cards, userInputs) => { const responseCards = []; cards.forEach((card) => { - const userInputs = responsesData.get(card.id); - const comment = userInputs?.comment ?? ''; + const userInput = responsesData.get(card.id); + const comment = userInput?.comment ?? ''; const responseData = { "choice1": "on", "choice2": "on", "choice3": "on", "comment": comment } responseCards.push({ data: responseData }); }); @@ -477,12 +477,12 @@ const customScreenExample = { { id: 'button2', label: 'Refuse proposals', - getUserResponses: (cards, responsesData) => { + getUserResponses: (cards, userInputs) => { const responseCards = []; let hasAlwaysComment = true; cards.forEach((card) => { - const userInputs = responsesData.get(card.id); - const comment = userInputs?.comment ?? ''; + const userInput = responsesData.get(card.id); + const comment = userInput?.comment ?? ''; if (comment === '') { hasAlwaysComment = false; } diff --git a/src/test/cypress/cypress/integration/BusinessMenu.spec.js b/src/test/cypress/cypress/integration/BusinessMenu.spec.js index 2c1813749c..26f730e488 100644 --- a/src/test/cypress/cypress/integration/BusinessMenu.spec.js +++ b/src/test/cypress/cypress/integration/BusinessMenu.spec.js @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2024, RTE (http://www.rte-france.com) +/* Copyright (c) 2022-2025, RTE (http://www.rte-france.com) * See AUTHORS.txt * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,44 +7,42 @@ * This file is part of the OperatorFabric project. */ -import {OpfabGeneralCommands} from '../support/opfabGeneralCommands'; -import {FeedCommands} from '../support/feedCommands'; -import {ScriptCommands} from '../support/scriptCommands'; +import { OpfabGeneralCommands } from '../support/opfabGeneralCommands'; +import { FeedCommands } from '../support/feedCommands'; +import { ScriptCommands } from '../support/scriptCommands'; describe('Business menu', () => { const opfab = new OpfabGeneralCommands(); const feed = new FeedCommands(); const script = new ScriptCommands(); - before('Set up configuration', function () { + before('Set up configuration', function() { script.loadTestConf(); script.deleteAllCards(); script.sendCard('defaultProcess/chart.json'); }); - it('Test business menu links in night mode ', - { - retries: { - runMode: 2, // to avoid flaky test - openMode: 1 - } - },() => { - opfab.loginWithUser('operator1_fr'); - - feed.openFirstCard(); - clickLinkInsideCard(); - opfab.checkUrlDisplayedIs('https://en.wikipedia.org/w/index.php?search=chart&fulltext=1&opfab_theme=NIGHT'); - - openBusinessDropdownMenu(); - clickOnDropdownMenu('uid_test_1'); - opfab.checkUrlDisplayedIs('https://opfab.github.io/?opfab_theme=NIGHT'); - openBusinessDropdownMenu(); - clickOnDropdownMenu('uid_test_2'); - opfab.checkUrlDisplayedIs('https://www.wikipedia.org/?opfab_theme=NIGHT'); - - openBusinessSingleMenu(); - opfab.checkUrlDisplayedIs('https://en.wikipedia.org/w/index.php?opfab_theme=NIGHT'); - }); + it('Test business menu links in night mode ', + { + retries: { + runMode: 2, // to avoid flaky test + openMode: 1 + } + }, () => { + opfab.loginWithUser('operator1_fr'); + + feed.openFirstCard(); + clickLinkInsideCard(); + opfab.checkUrlDisplayedIs('https://en.wikipedia.org/w/index.php?search=chart&fulltext=1&opfab_theme=NIGHT'); + + openBusinessDropdownMenu(); + clickOnDropdownMenu('uid_test_2'); + opfab.checkUrlDisplayedIs('https://opfab.github.io/?opfab_theme=NIGHT'); + openBusinessDropdownMenu(); + + openBusinessSingleMenu(); + opfab.checkUrlDisplayedIs('https://en.wikipedia.org/w/index.php?opfab_theme=NIGHT'); + }); it( 'Test business menu links in day mode ', @@ -62,12 +60,9 @@ describe('Business menu', () => { clickLinkInsideCard(); opfab.checkUrlDisplayedIs('https://en.wikipedia.org/w/index.php?search=chart&fulltext=1&opfab_theme=DAY'); - openBusinessDropdownMenu(); - clickOnDropdownMenu('uid_test_1'); - opfab.checkUrlDisplayedIs('https://opfab.github.io/?opfab_theme=DAY'); openBusinessDropdownMenu(); clickOnDropdownMenu('uid_test_2'); - opfab.checkUrlDisplayedIs('https://www.wikipedia.org/?opfab_theme=DAY'); + opfab.checkUrlDisplayedIs('https://opfab.github.io/?opfab_theme=DAY'); openBusinessSingleMenu(); opfab.checkUrlDisplayedIs('https://en.wikipedia.org/w/index.php?opfab_theme=DAY'); @@ -83,7 +78,7 @@ describe('Business menu', () => { } function clickOnDropdownMenu(menuId) { - cy.get('#opfab-navbar-menu-dropdown-'+ menuId).click({force:true}); + cy.get('#opfab-navbar-menu-dropdown-' + menuId).click({ force: true }); } diff --git a/src/test/cypress/cypress/integration/Translation.spec.js b/src/test/cypress/cypress/integration/Translation.spec.js index 1b5f9b2422..16eeb22b56 100644 --- a/src/test/cypress/cypress/integration/Translation.spec.js +++ b/src/test/cypress/cypress/integration/Translation.spec.js @@ -7,11 +7,11 @@ * This file is part of the OperatorFabric project. */ -import {OpfabGeneralCommands} from '../support/opfabGeneralCommands'; -import {ScriptCommands} from '../support/scriptCommands'; -import {SettingsCommands} from '../support/settingsCommands'; +import { OpfabGeneralCommands } from '../support/opfabGeneralCommands'; +import { ScriptCommands } from '../support/scriptCommands'; +import { SettingsCommands } from '../support/settingsCommands'; -describe('Test translations', function () { +describe('Test translations', function() { const ENGLISH = 'en'; const FRENCH = 'fr'; const DUTCH = 'nl'; @@ -63,8 +63,7 @@ describe('Test translations', function () { loggingTitle, singleMenuTitle, secondMenuTitle, - firstEntryTitle, - secondEntryTitle + opfabDocTitle, ) { cy.get('#opfab-navbar-menu-feed').should('have.text', feedTitle); cy.get('#opfab-navbar-menu-archives').should('have.text', archivesTitle); @@ -75,8 +74,7 @@ describe('Test translations', function () { // Test dropdown menus titles cy.get('#opfab-navbar-menu-menu2').trigger('mouseenter'); - cy.get('#opfab-navbar-menu-dropdown-uid_test_1').should('have.text', firstEntryTitle); - cy.get('#opfab-navbar-menu-dropdown-uid_test_2').should('have.text', secondEntryTitle); + cy.get('#opfab-navbar-menu-dropdown-uid_test_2').should('have.text', opfabDocTitle); } function checkRightMenuStaticEntries( @@ -237,12 +235,12 @@ describe('Test translations', function () { cy.contains('#opfab-feed-card-not-found', cardNotFoundText); } - before('Set up configuration and cards', function () { + before('Set up configuration and cards', function() { script.deleteAllSettings(); script.loadTestConf(); }); - it('Check translations for menu titles', function () { + it('Check translations for menu titles', function() { opfab.loginWithUser('operator1_fr'); changeLanguage(ENGLISH); @@ -253,8 +251,7 @@ describe('Test translations', function () { 'Logging', 'Single menu entry', 'Second menu', - 'First menu entry', - 'Second menu entry' + 'Opfab documentation' ); changeLanguage(FRENCH); @@ -265,8 +262,7 @@ describe('Test translations', function () { 'Logging', 'Unique élément', 'Deuxième menu', - 'Premier élément', - 'Deuxième élément' + 'Documentation Opfab' ); changeLanguage(DUTCH); @@ -277,12 +273,11 @@ describe('Test translations', function () { 'Logboek', 'Enkel menu-item', 'Tweede menu', - 'Eerste menu-item', - 'Tweede menu-item' + 'Opfab documentatie' ); }); - it('Check translations for user dropdown menu', function () { + it('Check translations for user dropdown menu', function() { opfab.loginWithUser('operator1_fr'); changeLanguage(ENGLISH); @@ -322,7 +317,7 @@ describe('Test translations', function () { checkDayAndNightTitles('Dag modus', 'Nacht modus'); }); - it('Check archives screen translations', function () { + it('Check archives screen translations', function() { opfab.loginWithUser('operator1_fr'); changeLanguage(ENGLISH); @@ -373,7 +368,7 @@ describe('Test translations', function () { ); }); - it('Check Monitoring screen translations', function () { + it('Check Monitoring screen translations', function() { opfab.loginWithUser('operator1_fr'); changeLanguage(ENGLISH); @@ -416,7 +411,7 @@ describe('Test translations', function () { checkMonitoringResultTexts('Kaarten met reactie van mijn entiteit '); }); - it('Check Business period translations', function () { + it('Check Business period translations', function() { const currentDate = new Date(2020, 11, 31, 23, 46); // Date must be in the past to avoid session close with token expiration opfab.loginWithClock(currentDate); @@ -453,7 +448,7 @@ describe('Test translations', function () { cy.get('.opfab-business-period').should('have.text', 'Bedrijfsperiode : 21:30 31/12/2020 -- 09:00 01/01/2021 '); }); - it('Check Feed filter translations', function () { + it('Check Feed filter translations', function() { opfab.loginWithUser('operator1_fr'); changeLanguage(ENGLISH); @@ -465,7 +460,7 @@ describe('Test translations', function () { checkDateFilterTexts('Receipt date', 'START', 'END', 'Apply filters to timeline '); // Deselect 'not acknowledged' checkbox on Acknowledgement filter to show the "Reset" link - cy.get('#opfab-feed-filter-ack-notack').click({force: true}); + cy.get('#opfab-feed-filter-ack-notack').click({ force: true }); checkResetText('Reset filters'); changeLanguage(FRENCH); @@ -485,7 +480,7 @@ describe('Test translations', function () { checkResetText('Filters opnieuw instellen'); }); - it('Check translation for non-existent card', function () { + it('Check translation for non-existent card', function() { opfab.loginWithUser('operator1_fr'); changeLanguage(ENGLISH); diff --git a/ui/main/src/app/components/customCardList/CustomCardListComponent.ts b/ui/main/src/app/components/customCardList/CustomCardListComponent.ts index 14f7c3166c..ca9b6981e8 100644 --- a/ui/main/src/app/components/customCardList/CustomCardListComponent.ts +++ b/ui/main/src/app/components/customCardList/CustomCardListComponent.ts @@ -8,7 +8,7 @@ */ import {AsyncPipe, NgFor, NgIf} from '@angular/common'; -import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core'; +import {Component, ElementRef, Input, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms'; import {ActivatedRoute} from '@angular/router'; import {NgbModal, NgbModalOptions, NgbModalRef, NgbPagination} from '@ng-bootstrap/ng-bootstrap'; @@ -28,7 +28,7 @@ import {DateRangePickerConfig} from 'app/utils/DateRangePickerConfig'; import {ExcelExport} from 'app/utils/excel-export'; import {CustomCardListView} from 'app/views/customCardList/CustomCardListView'; import {NgxDaterangepickerMd} from 'ngx-daterangepicker-material'; -import {Observable, ReplaySubject, Subject, take, takeUntil} from 'rxjs'; +import {Observable, ReplaySubject, Subject, takeUntil} from 'rxjs'; import {ResponsesCellRendererComponent} from './cellRenderers/ResponsesCellRendererComponent'; import {MultiSelectOption} from '../share/multi-select/model/MultiSelect'; import {MultiSelectComponent} from '../share/multi-select/multi-select.component'; @@ -38,7 +38,7 @@ import {HasResponseCellRendererComponent} from './cellRenderers/HasResponseCellR import {InputCellRendererComponent} from './cellRenderers/InputCellRendererComponent'; @Component({ - selector: 'of-custom-screen', + selector: 'of-custom-card-list-screen', templateUrl: './CustomCardListComponent.html', styleUrls: ['./CustomCardListComponent.scss'], standalone: true, @@ -56,10 +56,11 @@ import {InputCellRendererComponent} from './cellRenderers/InputCellRendererCompo MultiSelectComponent ] }) -export class CustomScreenComponent implements OnInit, OnDestroy { +export class CustomCardListComponent implements OnInit, OnDestroy { @ViewChild('cardDetail') cardDetailTemplate: ElementRef; - customScreenId: string; + @Input() customScreenId: string; + customCardListView: CustomCardListView; isCustomScreenDefinitionExist: boolean; gridOptions: GridOptions; @@ -130,174 +131,171 @@ export class CustomScreenComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.route.paramMap.pipe(take(1)).subscribe((params) => { - this.customScreenId = params.get('id'); - this.customCardListView = new CustomCardListView(this.customScreenId); + this.customCardListView = new CustomCardListView(this.customScreenId); - const severityCellClassRules = { - 'opfab-sev-alarm': (field) => field.value === 'ALARM', - 'opfab-sev-action': (field) => field.value === 'ACTION', - 'opfab-sev-compliant': (field) => field.value === 'COMPLIANT', - 'opfab-sev-information': (field) => field.value === 'INFORMATION' - }; + const severityCellClassRules = { + 'opfab-sev-alarm': (field) => field.value === 'ALARM', + 'opfab-sev-action': (field) => field.value === 'ACTION', + 'opfab-sev-compliant': (field) => field.value === 'COMPLIANT', + 'opfab-sev-information': (field) => field.value === 'INFORMATION' + }; - const typeOfStateCellClassRules = { - 'opfab-type-of-state-INPROGRESS': (field) => field.value.value === 'INPROGRESS', - 'opfab-type-of-state-FINISHED': (field) => field.value.value === 'FINISHED', - 'opfab-type-of-state-CANCELED': (field) => field.value.value === 'CANCELED' - }; - this.isCustomScreenDefinitionExist = this.customCardListView.isCustomScreenDefinitionExist(); - this.processFilterVisible = this.customCardListView.isFilterVisibleInHeader(HeaderFilter.PROCESS); - this.typeOfStateFilterVisible = this.customCardListView.isFilterVisibleInHeader(HeaderFilter.TYPE_OF_STATE); - this.responseFromMyEntitiesFilterVisible = this.customCardListView.isFilterVisibleInHeader( - HeaderFilter.RESPONSE_FROM_MY_ENTITIES - ); - this.responseFromAllEntitiesFilterVisible = this.customCardListView.isFilterVisibleInHeader( - HeaderFilter.RESPONSE_FROM_ALL_ENTITIES - ); - this.responseButtons = this.customCardListView.getResponseButtons(); - if (this.responseButtons.length > 0) this.rowSelection = {mode: 'multiRow'}; - this.gridOptions = { - domLayout: 'autoHeight', - components: { - responsesCellRenderer: ResponsesCellRendererComponent, - hasResponseCellRenderer: HasResponseCellRendererComponent, - inputCellRenderer: InputCellRendererComponent - }, + const typeOfStateCellClassRules = { + 'opfab-type-of-state-INPROGRESS': (field) => field.value.value === 'INPROGRESS', + 'opfab-type-of-state-FINISHED': (field) => field.value.value === 'FINISHED', + 'opfab-type-of-state-CANCELED': (field) => field.value.value === 'CANCELED' + }; + this.isCustomScreenDefinitionExist = this.customCardListView.isCustomScreenDefinitionExist(); + this.processFilterVisible = this.customCardListView.isFilterVisibleInHeader(HeaderFilter.PROCESS); + this.typeOfStateFilterVisible = this.customCardListView.isFilterVisibleInHeader(HeaderFilter.TYPE_OF_STATE); + this.responseFromMyEntitiesFilterVisible = this.customCardListView.isFilterVisibleInHeader( + HeaderFilter.RESPONSE_FROM_MY_ENTITIES + ); + this.responseFromAllEntitiesFilterVisible = this.customCardListView.isFilterVisibleInHeader( + HeaderFilter.RESPONSE_FROM_ALL_ENTITIES + ); + this.responseButtons = this.customCardListView.getResponseButtons(); + if (this.responseButtons.length > 0) this.rowSelection = {mode: 'multiRow'}; + this.gridOptions = { + domLayout: 'autoHeight', + components: { + responsesCellRenderer: ResponsesCellRendererComponent, + hasResponseCellRenderer: HasResponseCellRendererComponent, + inputCellRenderer: InputCellRendererComponent + }, + + defaultColDef: { + editable: false + }, + getLocaleText: function (params) { + // To avoid clashing with opfab assets, all keys defined by ag-grid are prefixed with "ag-grid." + // e.g. key "to" defined by ag-grid for use with pagination can be found under "ag-grid.to" in assets + return TranslationService.getTranslation('ag-grid.' + params.key); + }, - defaultColDef: { - editable: false + columnTypes: { + default: { + sortable: true, + filter: true, + resizable: false, + wrapText: false }, - getLocaleText: function (params) { - // To avoid clashing with opfab assets, all keys defined by ag-grid are prefixed with "ag-grid." - // e.g. key "to" defined by ag-grid for use with pagination can be found under "ag-grid.to" in assets - return TranslationService.getTranslation('ag-grid.' + params.key); + severity: { + sortable: false, + resizable: false, + maxWidth: 18, + cellClassRules: severityCellClassRules, + headerClass: 'opfab-ag-header-with-no-padding' }, - - columnTypes: { - default: { - sortable: true, - filter: true, - resizable: false, - wrapText: false - }, - severity: { - sortable: false, - resizable: false, - maxWidth: 18, - cellClassRules: severityCellClassRules, - headerClass: 'opfab-ag-header-with-no-padding' - }, - typeOfState: { - sortable: true, - resizable: false, - wrapText: false, - cellClassRules: typeOfStateCellClassRules, - cellRenderer: (params: any) => params.value.text, - comparator: (valueA: any, valueB: any) => { - if (valueA.text < valueB.text) { - return -1; - } - if (valueA.text > valueB.text) { - return 1; - } - return 0; - }, - filter: true, - filterValueGetter: (params: any) => params.data.typeOfState.text - }, - dateAndTime: { - sortable: true, - resizable: false, - wrapText: false, - cellRenderer: (params: any) => params.value.text, - comparator: (valueA: any, valueB: any) => { - if (valueA.value < valueB.value) { - return -1; - } - if (valueA.value > valueB.value) { - return 1; - } - return 0; - }, - filter: true, - filterValueGetter: (params: any) => { - return params.data[params.column.colId].text; + typeOfState: { + sortable: true, + resizable: false, + wrapText: false, + cellClassRules: typeOfStateCellClassRules, + cellRenderer: (params: any) => params.value.text, + comparator: (valueA: any, valueB: any) => { + if (valueA.text < valueB.text) { + return -1; } - }, - responses: { - sortable: false, - filter: false, - resizable: false, - wrapText: false, - cellRenderer: 'responsesCellRenderer' - }, - coloredCircle: { - sortable: false, - filter: false, - resizable: false, - wrapText: false, - cellStyle: {display: 'flex', 'justify-content': 'center', alignItems: 'center'}, - cellRenderer: (params: any) => { - return ( - '
' - ); + if (valueA.text > valueB.text) { + return 1; } + return 0; }, - responseFromMyEntities: { - sortable: false, - filter: false, - resizable: false, - width: 15, - wrapText: false, - cellRenderer: 'hasResponseCellRenderer' + filter: true, + filterValueGetter: (params: any) => params.data.typeOfState.text + }, + dateAndTime: { + sortable: true, + resizable: false, + wrapText: false, + cellRenderer: (params: any) => params.value.text, + comparator: (valueA: any, valueB: any) => { + if (valueA.value < valueB.value) { + return -1; + } + if (valueA.value > valueB.value) { + return 1; + } + return 0; }, - input: { - cellRenderer: 'inputCellRenderer', - sortable: false, - filter: false, - resizable: false + filter: true, + filterValueGetter: (params: any) => { + return params.data[params.column.colId].text; } }, - ensureDomOrder: true, // rearrange row-index of rows when sorting cards (used for cypress) - pagination: true, - suppressCellFocus: true, - suppressPaginationPanel: true, - suppressHorizontalScroll: true, - columnDefs: this.customCardListView.getColumnsDefinitionForAgGrid(), - headerHeight: 70, - rowHeight: 45, - paginationPageSize: 10, - onRowSelected: (event) => this.onRowSelected(event) - }; - this.rowData$ = this.rowDataSubject.asObservable(); - this.rowDataSubject.next(this.rowData); // needed to have an empty table if no data on component init - this.customCardListView.getResults().subscribe((results) => { - this.rowData = results; - this.rowDataSubject.next(this.rowData); - }); - if (this.responseButtons.length > 0) { - this.rowSelection = { - mode: 'multiRow', - selectAll: 'currentPage', - hideDisabledCheckboxes: true, - isRowSelectable: (node) => { - return node.data.isResponsePossible; + responses: { + sortable: false, + filter: false, + resizable: false, + wrapText: false, + cellRenderer: 'responsesCellRenderer' + }, + coloredCircle: { + sortable: false, + filter: false, + resizable: false, + wrapText: false, + cellStyle: {display: 'flex', 'justify-content': 'center', alignItems: 'center'}, + cellRenderer: (params: any) => { + return ( + '
' + ); } - }; - } + }, + responseFromMyEntities: { + sortable: false, + filter: false, + resizable: false, + width: 15, + wrapText: false, + cellRenderer: 'hasResponseCellRenderer' + }, + input: { + cellRenderer: 'inputCellRenderer', + sortable: false, + filter: false, + resizable: false + } + }, + ensureDomOrder: true, // rearrange row-index of rows when sorting cards (used for cypress) + pagination: true, + suppressCellFocus: true, + suppressPaginationPanel: true, + suppressHorizontalScroll: true, + columnDefs: this.customCardListView.getColumnsDefinitionForAgGrid(), + headerHeight: 70, + rowHeight: 45, + paginationPageSize: 10, + onRowSelected: (event) => this.onRowSelected(event) + }; + this.rowData$ = this.rowDataSubject.asObservable(); + this.rowDataSubject.next(this.rowData); // needed to have an empty table if no data on component init + this.customCardListView.getResults().subscribe((results) => { + this.rowData = results; + this.rowDataSubject.next(this.rowData); + }); + if (this.responseButtons.length > 0) { + this.rowSelection = { + mode: 'multiRow', + selectAll: 'currentPage', + hideDisabledCheckboxes: true, + isRowSelectable: (node) => { + return node.data.isResponsePossible; + } + }; + } - this.headerForm.get('businessDateRanges').setValue({ - startDate: new Date(this.customCardListView.getBusinessPeriod().startDate), - endDate: new Date(this.customCardListView.getBusinessPeriod().endDate) - }); - if (this.processFilterVisible) { - this.headerForm.get('processes').setValue([]); - this.initProcessFilter(); - } + this.headerForm.get('businessDateRanges').setValue({ + startDate: new Date(this.customCardListView.getBusinessPeriod().startDate), + endDate: new Date(this.customCardListView.getBusinessPeriod().endDate) }); + if (this.processFilterVisible) { + this.headerForm.get('processes').setValue([]); + this.initProcessFilter(); + } } private initProcessFilter(): void { this.customCardListView.getProcessList().forEach((process) => { diff --git a/ui/main/src/app/components/customCardList/CustomScreenComponent.html b/ui/main/src/app/components/customCardList/CustomScreenComponent.html new file mode 100644 index 0000000000..4bf3a9d46f --- /dev/null +++ b/ui/main/src/app/components/customCardList/CustomScreenComponent.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ui/main/src/app/components/customCardList/CustomScreenComponent.ts b/ui/main/src/app/components/customCardList/CustomScreenComponent.ts new file mode 100644 index 0000000000..655ebfc754 --- /dev/null +++ b/ui/main/src/app/components/customCardList/CustomScreenComponent.ts @@ -0,0 +1,47 @@ +/* Copyright (c) 2025, RTE (http://www.rte-france.com) + * See AUTHORS.txt + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + * This file is part of the OperatorFabric project. + */ + +import {NgIf} from '@angular/common'; +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import {Subject, takeUntil} from 'rxjs'; +import {CustomCardListComponent} from './CustomCardListComponent'; + +@Component({ + selector: 'of-custom-screen', + templateUrl: './CustomScreenComponent.html', + standalone: true, + imports: [NgIf, CustomCardListComponent] +}) +export class CustomScreenComponent implements OnInit, OnDestroy { + customScreenId: string; + displayComponent = false; + + private readonly ngUnsubscribe$ = new Subject(); + + constructor(private readonly route: ActivatedRoute) {} + + ngOnInit(): void { + this.route.paramMap.pipe(takeUntil(this.ngUnsubscribe$)).subscribe((params) => { + this.customScreenId = params.get('id'); + + // the following hack is needed to have the CustomCardListComponent + // destroyed and recreated when the route changes + this.displayComponent = false; + setTimeout(() => { + this.displayComponent = true; + }); + }); + } + + ngOnDestroy(): void { + this.ngUnsubscribe$.next(); + this.ngUnsubscribe$.complete(); + } +} diff --git a/ui/main/src/app/services/navigation/router/AngularApplicationRouter.module.ts b/ui/main/src/app/services/navigation/router/AngularApplicationRouter.module.ts index f06b5858ef..069940b1b1 100644 --- a/ui/main/src/app/services/navigation/router/AngularApplicationRouter.module.ts +++ b/ui/main/src/app/services/navigation/router/AngularApplicationRouter.module.ts @@ -128,7 +128,7 @@ const routes: Routes = [ { path: 'customscreen/:id', loadComponent: () => - import('../../../components/customCardList/CustomCardListComponent').then((m) => m.CustomScreenComponent) + import('../../../components/customCardList/CustomScreenComponent').then((m) => m.CustomScreenComponent) }, { path: 'devtools/richtext',