Skip to content

Commit 094d1cd

Browse files
freddidierRTEvlo-rte
authored andcommitted
Custom card list screen : add response from my entities column (#FE-7922)
Signed-off-by: freddidierRTE <[email protected]>
1 parent 4487db4 commit 094d1cd

File tree

9 files changed

+599
-476
lines changed

9 files changed

+599
-476
lines changed

config/docker/externalJs/CustomScreenExample.js

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
cardField: 'publishDate',
2626
fieldType: 'DATE_AND_TIME'
2727
},
28+
{
29+
fieldType: 'RESPONSE_FROM_MY_ENTITIES'
30+
},
2831
{
2932
field: 'testField',
3033
headerName: 'TITLE',

src/docs/asciidoc/reference_doc/ui_customization.adoc

+8-4
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ const customScreenExample = {
320320
cardField: 'publishDate',
321321
fieldType: 'DATE_AND_TIME'
322322
},
323+
{
324+
fieldType: 'RESPONSE_FROM_MY_ENTITIES'
325+
},
323326
{
324327
field: 'testField',
325328
headerName: 'TITLE',
@@ -362,16 +365,17 @@ You can define the filters to be displayed in the header of the screen by adding
362365

363366
For each field you want to display in the result table, define a column object. The following field types are available:
364367

365-
- `SEVERITY`: Displays the severity as a color rectangle.
366368
- `DATE_AND_TIME`: Displays the date and time formatted according to the user's locale.
367-
- `STRING`: Displays a string value.
368-
- `TYPE_OF_STATE`: Displays the type of state value: `INPROGRESS`, `FINISHED`, `CANCELLED`.
369369
- `PUBLISHER`: Displays the publisher entity if the card is published by an entity, otherwise displays the user who published the card.
370370
- `RESPONSES`: Displays the list of entities that must respond, with the color indicating the severity of the response if the entity has responded. It uses the `entitiesRequiredToRespond` field of the card. If this field is empty, it will use the `entitiesAllowedToRespond` field of the card.
371+
- `RESPONSE_FROM_MY_ENTITIES`: Displays a blue arrow if one of the user's entity has responded to the card.
372+
- `SEVERITY`: Displays the severity as a color rectangle.
373+
- `STRING`: Displays a string value.
374+
- `TYPE_OF_STATE`: Displays the type of state value: `INPROGRESS`, `FINISHED`, `CANCELLED`.
371375

372376
You can define the column header text using the `headerName` field.
373377

374-
The `cardField` is the field of the card that will be displayed in the result. It is not used for the following field types: `SEVERITY`, `TYPE_OF_STATE`, and `RESPONSES`.
378+
The `cardField` is the field of the card that will be displayed in the result. It is not used for the following field types: `SEVERITY`, `TYPE_OF_STATE`,`RESPONSES` and `RESPONSE_FROM_MY_ENTITIES`.
375379

376380
The `flex` field sets the CSS flex property for the column.
377381

ui/main/src/app/components/customCardList/CustomCardListComponent.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {MultiSelectOption} from '../share/multi-select/model/MultiSelect';
2828
import {MultiSelectComponent} from '../share/multi-select/multi-select.component';
2929
import {HeaderFilter} from '@ofServices/customScreen/model/CustomScreenDefinition';
3030
import {TypeOfStateEnum} from '@ofServices/processes/model/Processes';
31+
import {HasResponseCellRendererComponent} from './cellRenderers/HasResponseCellRendererComponent';
3132

3233
@Component({
3334
selector: 'of-custom-screen',
@@ -134,7 +135,8 @@ export class CustomScreenComponent implements OnInit, OnDestroy {
134135
this.gridOptions = {
135136
domLayout: 'autoHeight',
136137
components: {
137-
responsesCellRenderer: ResponsesCellRendererComponent
138+
responsesCellRenderer: ResponsesCellRendererComponent,
139+
hasResponseCellRenderer: HasResponseCellRendererComponent
138140
},
139141

140142
defaultColDef: {
@@ -198,6 +200,14 @@ export class CustomScreenComponent implements OnInit, OnDestroy {
198200
'"></div>'
199201
);
200202
}
203+
},
204+
responseFromMyEntities: {
205+
sortable: false,
206+
filter: false,
207+
resizable: false,
208+
width: 15,
209+
wrapText: false,
210+
cellRenderer: 'hasResponseCellRenderer'
201211
}
202212
},
203213
ensureDomOrder: true, // rearrange row-index of rows when sorting cards (used for cypress)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!-- Copyright (c) 2025, RTE (http://www.rte-france.com) -->
2+
<!-- See AUTHORS.txt -->
3+
<!-- This Source Code Form is subject to the terms of the Mozilla Public -->
4+
<!-- License, v. 2.0. If a copy of the MPL was not distributed with this -->
5+
<!-- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
6+
<!-- SPDX-License-Identifier: MPL-2.0 -->
7+
<!-- This file is part of the OperatorFabric project. -->
8+
9+
<ng-template #tipHasAResponseFromYourEntity>
10+
<span translate>monitoring.tips.hasAResponseFromYourEntity</span>
11+
</ng-template>
12+
<em
13+
*ngIf="params.value"
14+
[ngbPopover]="tipHasAResponseFromYourEntity"
15+
popoverClass="opfab-popover-unclickable"
16+
container="body"
17+
triggers="mouseenter:mouseleave"
18+
style="color: var(--opfab-color-blue)"
19+
class="fa fa-reply"></em>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* Copyright (c) 2025, RTE (http://www.rte-france.com)
2+
* See AUTHORS.txt
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
* SPDX-License-Identifier: MPL-2.0
7+
* This file is part of the OperatorFabric project.
8+
*/
9+
10+
import {Component} from '@angular/core';
11+
import {ICellRendererAngularComp} from 'ag-grid-angular';
12+
import {ICellRendererParams} from 'ag-grid-community';
13+
import {TranslateModule} from '@ngx-translate/core';
14+
import {NgIf} from '@angular/common';
15+
import {NgbPopover} from '@ng-bootstrap/ng-bootstrap';
16+
17+
@Component({
18+
selector: 'of-has-response-cell-renderer',
19+
templateUrl: './HasResponseCellRendererComponent.html',
20+
standalone: true,
21+
imports: [TranslateModule, NgIf, NgbPopover]
22+
})
23+
export class HasResponseCellRendererComponent implements ICellRendererAngularComp {
24+
public params: any;
25+
26+
agInit(params: any): void {
27+
this.params = params;
28+
}
29+
30+
/** This method returns true to signal to the grid that this renderer doesn't need to be recreated if the underlying data changes
31+
* See https://www.ag-grid.com/documentation/angular/component-cell-renderer/#handling-refresh
32+
* */
33+
refresh(params: ICellRendererParams): boolean {
34+
return true;
35+
}
36+
}

ui/main/src/app/services/customScreen/model/CustomScreenDefinition.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ export enum FieldType {
3636
PUBLISHER = 'PUBLISHER',
3737
TYPE_OF_STATE = 'TYPE_OF_STATE',
3838
RESPONSES = 'RESPONSES',
39-
COLORED_CIRCLE = 'COLORED_CIRCLE'
39+
COLORED_CIRCLE = 'COLORED_CIRCLE',
40+
RESPONSE_FROM_MY_ENTITIES = 'RESPONSE_FROM_MY_ENTITIES'
4041
}
4142

4243
export enum HeaderFilter {

ui/main/src/app/views/customCardList/CustomCardListView.spec.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('CustomScreenView', () => {
5555
RealTimeDomainService.setStartAndEndPeriod(0, new Date().valueOf() + 1000);
5656
filteredLightCardStore = OpfabStore.getFilteredLightCardStore();
5757
});
58-
describe('custom screen configuration', () => {
58+
describe('Custom screen configuration', () => {
5959
it('should return false if custom screen definition does not exist', () => {
6060
const customScreenView = new CustomCardListView('unexistingId');
6161
expect(customScreenView.isCustomScreenDefinitionExist()).toEqual(false);
@@ -65,14 +65,14 @@ describe('CustomScreenView', () => {
6565
const customScreenView = new CustomCardListView('testId');
6666
expect(customScreenView.isCustomScreenDefinitionExist()).toEqual(true);
6767
});
68-
it('should return true if custom screen header filter PROCESS is define visible in customScreenDefinition', () => {
68+
it('filter visibility should be true if custom screen header filter PROCESS is define visible in customScreenDefinition', () => {
6969
const customScreenDefinition = getCustomScreenDefintion();
7070
customScreenDefinition.headerFilters = [HeaderFilter.PROCESS];
7171
CustomScreenService.addCustomScreenDefinition(customScreenDefinition);
7272
const customScreenView = new CustomCardListView('testId');
7373
expect(customScreenView.isFilterVisibleInHeader(HeaderFilter.PROCESS)).toEqual(true);
7474
});
75-
it('should return false if custom screen header filter PROCESS is not define visible in customScreenDefinition', () => {
75+
it('filter visibility should be false if custom screen header filter PROCESS is not define visible in customScreenDefinition', () => {
7676
const customScreenDefinition = getCustomScreenDefintion();
7777
customScreenDefinition.headerFilters = [HeaderFilter.TYPE_OF_STATE];
7878
CustomScreenService.addCustomScreenDefinition(customScreenDefinition);
@@ -108,7 +108,7 @@ describe('CustomScreenView', () => {
108108
]);
109109
});
110110
});
111-
describe('processes', () => {
111+
describe('Shoud get processe list', () => {
112112
beforeEach(() => {
113113
const process = [
114114
new Process('myProcess', '1', 'my process label', null, new Map<string, State>()),
@@ -117,7 +117,7 @@ describe('CustomScreenView', () => {
117117
setProcessConfiguration(process);
118118
});
119119

120-
it('should return one process from user perimeter if user has one process state visible', async () => {
120+
it('with one process from user perimeter if user has one process state visible', async () => {
121121
const customScreenDefinition = getCustomScreenDefintion();
122122
CustomScreenService.addCustomScreenDefinition(customScreenDefinition);
123123
const customScreenView = new CustomCardListView('testId');
@@ -134,7 +134,7 @@ describe('CustomScreenView', () => {
134134
const result = customScreenView.getProcessList();
135135
expect(result).toEqual([{id: 'myProcess', label: 'my process label'}]);
136136
});
137-
it('should return one process form user perimeter if user has one process with 2 states visible', async () => {
137+
it('with one process form user perimeter if user has one process with 2 states visible', async () => {
138138
const customScreenDefinition = getCustomScreenDefintion();
139139
CustomScreenService.addCustomScreenDefinition(customScreenDefinition);
140140
const customScreenView = new CustomCardListView('testId');
@@ -155,7 +155,7 @@ describe('CustomScreenView', () => {
155155
expect(result).toEqual([{id: 'myProcess', label: 'my process label'}]);
156156
});
157157
});
158-
describe(' custom screen data', () => {
158+
describe('Should return custom screen data', () => {
159159
beforeEach(() => {
160160
setEntities([
161161
{
@@ -166,7 +166,7 @@ describe('CustomScreenView', () => {
166166
]);
167167
});
168168

169-
it('should return data array from light cards store', async () => {
169+
it('from light cards store', async () => {
170170
// this is necessary to set the user perimeter for child card
171171
// to be processed correctly by the ligthcard store
172172
await setUserPerimeter({
@@ -225,7 +225,7 @@ describe('CustomScreenView', () => {
225225
]);
226226
});
227227

228-
it('should return data array filtered from light cards store', async () => {
228+
it('filtered from light cards store', async () => {
229229
// this is necessary to set the user perimeter for child card
230230
// to be processed correctly by the ligthcard store
231231
await setUserPerimeter({
@@ -276,7 +276,7 @@ describe('CustomScreenView', () => {
276276
]);
277277
});
278278

279-
it('should convert data for export', async () => {
279+
it('converted for export', async () => {
280280
const customScreenDefinition = getCustomScreenDefintion();
281281
customScreenDefinition.results = {
282282
columns: [

0 commit comments

Comments
 (0)