Skip to content

Commit 3524c59

Browse files
freddidierRTEvlo-rte
authored andcommitted
Custom card list screen : add "Cards totally answered" filter (#7924)
Signed-off-by: freddidierRTE <[email protected]>
1 parent 2e41dc3 commit 3524c59

File tree

13 files changed

+202
-43
lines changed

13 files changed

+202
-43
lines changed

config/docker/externalJs/CustomScreenExample.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
const customScreenExample = {
1313
id: 'testId',
1414
name: 'testName',
15-
headerFilters: ['PROCESS', 'TYPE_OF_STATE', 'RESPONSE_FROM_MY_ENTITIES'],
15+
headerFilters: ['PROCESS', 'TYPE_OF_STATE', 'RESPONSE_FROM_MY_ENTITIES', 'RESPONSE_FROM_ALL_ENTITIES'],
1616
results: {
1717
columns: [
1818
{

src/docs/asciidoc/reference_doc/ui_customization.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ opfab.businessconfig.registerCustomScreen(customScreenExample);
405405

406406
In this example, the custom screen is accessible via the URL `#/customscreen/testId`.
407407

408-
You can define the filters to be displayed in the header of the screen by adding them to the `headerFilters` array. The following filters are available: `PROCESS`, `TYPE_OF_STATE` and `RESPONSE_FROM_MY_ENTITIES`.
408+
You can define the filters to be displayed in the header of the screen by adding them to the `headerFilters` array. The following filters are available: `PROCESS`, `TYPE_OF_STATE`, `RESPONSE_FROM_MY_ENTITIES` and `RESPONSE_FROM_ALL_ENTITIES`.
409409

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

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

+26-11
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,32 @@ <h1>Custom Screen {{id}} does not exist</h1>
7171
</div>
7272
</div>
7373
</div>
74-
<div *ngIf="!responseFromMyEntitiesFilterVisible" style="height: 20px"></div>
75-
<div *ngIf="responseFromMyEntitiesFilterVisible" class="opfab-custom-card-list-response-checkbox">
76-
<label
77-
id="opfab-show-cards-with-response"
78-
style="font-weight: bold; margin-left: 5px; margin-bottom: 0px"
79-
class="opfab-checkbox"
80-
translate
81-
>feed.filters.showCardsWithResponse.label
82-
<input type="checkbox" formControlName="responseFromMyEntities" />
83-
<span class="opfab-checkbox-checkmark"></span>
84-
</label>
74+
<div
75+
*ngIf="!responseFromMyEntitiesFilterVisible && !responseFromAllEntitiesFilterVisible"
76+
style="height: 20px"></div>
77+
<div class="opfab-custom-card-list-responses-checkbox">
78+
<div *ngIf="responseFromMyEntitiesFilterVisible">
79+
<label
80+
id="opfab-show-cards-with-response"
81+
style="font-weight: bold; margin-left: 5px; margin-right: 30px; margin-bottom: 0px"
82+
class="opfab-checkbox"
83+
translate
84+
>feed.filters.showCardsWithResponse.label
85+
<input type="checkbox" formControlName="responseFromMyEntities" />
86+
<span class="opfab-checkbox-checkmark"></span>
87+
</label>
88+
</div>
89+
<div *ngIf="responseFromAllEntitiesFilterVisible">
90+
<label
91+
id="opfab-show-cards-with-all-response"
92+
style="font-weight: bold; margin-left: 5px; margin-bottom: 0px"
93+
class="opfab-checkbox"
94+
translate
95+
>shared.filters.showCardsWithResponseFromAllEntities
96+
<input type="checkbox" formControlName="responseFromAllEntities" />
97+
<span class="opfab-checkbox-checkmark"></span>
98+
</label>
99+
</div>
85100
</div>
86101

87102
<div

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727
margin-right: 10px;
2828
}
2929

30-
.opfab-custom-card-list-response-checkbox {
30+
.opfab-custom-card-list-responses-checkbox {
31+
display: flex;
3132
margin-left: 5%;
3233
margin-top: 20px;
3334
margin-bottom: 10px;
3435
font-size: 30px;
35-
border: solid 1px var(--opfab-bgcolor);
3636
text-align: center;
3737
background-color: var(--opfab-bgcolor);
3838
color: var(--opfab-text-color);

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

+10-17
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ export class CustomScreenComponent implements OnInit, OnDestroy {
7373
businessDateRanges: new FormControl({}),
7474
processes: new FormControl([]),
7575
typeOfState: new FormControl([]),
76-
responseFromMyEntities: new FormControl(true)
76+
responseFromMyEntities: new FormControl(true),
77+
responseFromAllEntities: new FormControl(true)
7778
});
7879
modalRef: NgbModalRef;
7980

@@ -111,6 +112,7 @@ export class CustomScreenComponent implements OnInit, OnDestroy {
111112
};
112113

113114
responseFromMyEntitiesFilterVisible = false;
115+
responseFromAllEntitiesFilterVisible = false;
114116
responseButtons = [];
115117
public rowSelection: RowSelectionOptions;
116118

@@ -149,6 +151,9 @@ export class CustomScreenComponent implements OnInit, OnDestroy {
149151
this.responseFromMyEntitiesFilterVisible = this.customCardListView.isFilterVisibleInHeader(
150152
HeaderFilter.RESPONSE_FROM_MY_ENTITIES
151153
);
154+
this.responseFromAllEntitiesFilterVisible = this.customCardListView.isFilterVisibleInHeader(
155+
HeaderFilter.RESPONSE_FROM_ALL_ENTITIES
156+
);
152157
this.responseButtons = this.customCardListView.getResponseButtons();
153158
if (this.responseButtons.length > 0) this.rowSelection = {mode: 'multiRow'};
154159
this.gridOptions = {
@@ -315,22 +320,10 @@ export class CustomScreenComponent implements OnInit, OnDestroy {
315320
const startDate = Date.parse(businessDates.startDate?.toISOString());
316321
const endDate = Date.parse(businessDates.endDate?.toISOString());
317322
this.customCardListView.setBusinessPeriod(startDate, endDate);
318-
const processIds = [];
319-
this.headerForm.get('processes').value.forEach((processId: string) => {
320-
processIds.push(processId);
321-
});
322-
this.customCardListView.setProcessList(processIds);
323-
const typeOfState = [];
324-
this.headerForm.get('typeOfState').value.forEach((type: string) => {
325-
typeOfState.push(type);
326-
});
327-
this.customCardListView.setTypesOfStateFilter(typeOfState);
328-
const responseFromMyEntities = this.headerForm.get('responseFromMyEntities').value;
329-
if (responseFromMyEntities) {
330-
this.customCardListView.includeCardsWithResponseFromMyEntities();
331-
} else {
332-
this.customCardListView.excludeCardsWithResponseFromMyEntities();
333-
}
323+
this.customCardListView.setProcessList([...this.headerForm.get('processes').value]);
324+
this.customCardListView.setTypesOfStateFilter([...this.headerForm.get('typeOfState').value]);
325+
this.customCardListView.setResponseFromMyEntitiesChoice(this.headerForm.get('responseFromMyEntities').value);
326+
this.customCardListView.setResponseFromAllEntitiesChoice(this.headerForm.get('responseFromAllEntities').value);
334327
this.customCardListView.search();
335328
}
336329

ui/main/src/app/services/cardResponse/CardResponse.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ describe('Card response service', () => {
121121
expect(cardServerMock.cardsPosted[0].entityRecipients).toContain('ENTITY1');
122122
});
123123

124-
it("Should set publihser to first entity user allowed to respond if publisher isn't provided", async () => {
124+
it("Should set publisher to first entity user allowed to respond if publisher isn't provided", async () => {
125125
await CardResponseService.sendResponse(card, {});
126126
expect(cardServerMock.cardsPosted[0].publisher).toBe('ENTITY1');
127127
});

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,6 @@ export enum FieldType {
5656
export enum HeaderFilter {
5757
PROCESS = 'PROCESS',
5858
TYPE_OF_STATE = 'TYPE_OF_STATE',
59-
RESPONSE_FROM_MY_ENTITIES = 'RESPONSE_FROM_MY_ENTITIES'
59+
RESPONSE_FROM_MY_ENTITIES = 'RESPONSE_FROM_MY_ENTITIES',
60+
RESPONSE_FROM_ALL_ENTITIES = 'RESPONSE_FROM_ALL_ENTITIES'
6061
}

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

+14-6
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,20 @@ export class CustomCardListView {
103103
this.resultTable.setTypesOfStateFilter(typesOfState);
104104
}
105105

106-
public excludeCardsWithResponseFromMyEntities() {
107-
this.resultTable.excludeCardsWithResponseFromMyEntities();
108-
}
109-
110-
public includeCardsWithResponseFromMyEntities() {
111-
this.resultTable.includeCardsWithResponseFromMyEntities();
106+
//We disable sonar rules typescript:S2301 here because we
107+
//want the method to reflect the checkbox status
108+
public setResponseFromMyEntitiesChoice(checked: boolean) {
109+
// NOSONAR
110+
if (checked) {
111+
this.resultTable.includeCardsWithResponseFromMyEntities();
112+
} else this.resultTable.excludeCardsWithResponseFromMyEntities();
113+
}
114+
115+
public setResponseFromAllEntitiesChoice(checked: boolean) {
116+
// NOSONAR
117+
if (checked) {
118+
this.resultTable.includeCardsWithResponseFromAllEntities();
119+
} else this.resultTable.excludeCardsWithResponseFromAllEntities();
112120
}
113121

114122
public getProcessList(): {id: string; label: string}[] {

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

+118
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,124 @@ describe('CustomScreenView - ResultTable', () => {
588588
const dataArray = resultTable.getDataArrayFromCards(cards, emptyChildCardsList);
589589
expect(dataArray).toEqual([{cardId: 'id1', testField: 'processId0'}]);
590590
});
591+
it('that have all entities entitiesRequiredToRespond responded if excludeCardsWithAllEntitiesHaveResponded is called', () => {
592+
const resultTable = getResultTable({
593+
columns: [
594+
{
595+
field: 'testField',
596+
headerName: 'Process',
597+
cardField: 'process',
598+
fieldType: FieldType.STRING
599+
}
600+
]
601+
});
602+
const cards = [
603+
getOneLightCard({
604+
process: 'processId1',
605+
state: 'state1.0',
606+
entitiesRequiredToRespond: ['entity1', 'entity2'],
607+
id: 'card1'
608+
}),
609+
getOneLightCard({
610+
process: 'processId2',
611+
state: 'state1.1',
612+
entitiesRequiredToRespond: ['entity1', 'entity2'],
613+
id: 'card2'
614+
}),
615+
getOneLightCard({
616+
process: 'processId3',
617+
state: 'state1.2',
618+
entitiesRequiredToRespond: [],
619+
id: 'card3'
620+
})
621+
];
622+
const childCards = new Map<string, Array<Card>>();
623+
childCards.set('card1', [
624+
getOneLightCard({
625+
publisher: 'entity1',
626+
publisherType: 'ENTITY',
627+
severity: Severity.ALARM
628+
}),
629+
getOneLightCard({
630+
publisher: 'entity2',
631+
publisherType: 'ENTITY',
632+
severity: Severity.ACTION
633+
})
634+
]);
635+
childCards.set('card2', [
636+
getOneLightCard({
637+
publisher: 'entity1',
638+
publisherType: 'ENTITY',
639+
severity: Severity.COMPLIANT
640+
})
641+
]);
642+
resultTable.excludeCardsWithResponseFromAllEntities();
643+
const dataArray = resultTable.getDataArrayFromCards(cards, childCards);
644+
expect(dataArray).toEqual([
645+
{
646+
cardId: 'card2',
647+
testField: 'processId2'
648+
},
649+
{
650+
cardId: 'card3',
651+
testField: 'processId3'
652+
}
653+
]);
654+
});
655+
it('that have all entities in entitiesAllowedToRespond responded if entitiesRequiredToRespond is empty and excludeCardsWithAllEntitiesHaveResponded is called', () => {
656+
const resultTable = getResultTable({
657+
columns: [
658+
{
659+
field: 'testField',
660+
headerName: 'Process',
661+
cardField: 'process',
662+
fieldType: FieldType.STRING
663+
}
664+
]
665+
});
666+
const cards = [
667+
getOneLightCard({
668+
process: 'processId1',
669+
state: 'state1.0',
670+
entitiesAllowedToRespond: ['entity1', 'entity2'],
671+
id: 'card1'
672+
}),
673+
getOneLightCard({
674+
process: 'processId2',
675+
state: 'state1.1',
676+
entitiesAllowedToRespond: ['entity1', 'entity2'],
677+
id: 'card2'
678+
})
679+
];
680+
const childCards = new Map<string, Array<Card>>();
681+
childCards.set('card1', [
682+
getOneLightCard({
683+
publisher: 'entity1',
684+
publisherType: 'ENTITY',
685+
severity: Severity.ALARM
686+
}),
687+
getOneLightCard({
688+
publisher: 'entity2',
689+
publisherType: 'ENTITY',
690+
severity: Severity.ACTION
691+
})
692+
]);
693+
childCards.set('card2', [
694+
getOneLightCard({
695+
publisher: 'entity1',
696+
publisherType: 'ENTITY',
697+
severity: Severity.COMPLIANT
698+
})
699+
]);
700+
resultTable.excludeCardsWithResponseFromAllEntities();
701+
const dataArray = resultTable.getDataArrayFromCards(cards, childCards);
702+
expect(dataArray).toEqual([
703+
{
704+
cardId: 'card2',
705+
testField: 'processId2'
706+
}
707+
]);
708+
});
591709
});
592710

593711
describe('Should get responses in data array', () => {

ui/main/src/app/views/customCardList/ResultTable.ts

+21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class ResultTable {
2626
private processIds = [];
2727
private typesOfState = [];
2828
private areCardsWithResponseFromMyEntitiesExcluded = false;
29+
private areCardsWithResponseFromAllEntitiesExcluded = false;
2930

3031
constructor(customScreenDefinition: CustomScreenDefinition) {
3132
this.customScreenDefinition = customScreenDefinition;
@@ -131,13 +132,23 @@ export class ResultTable {
131132
this.areCardsWithResponseFromMyEntitiesExcluded = false;
132133
}
133134

135+
public excludeCardsWithResponseFromAllEntities() {
136+
this.areCardsWithResponseFromAllEntitiesExcluded = true;
137+
}
138+
139+
public includeCardsWithResponseFromAllEntities() {
140+
this.areCardsWithResponseFromAllEntitiesExcluded = false;
141+
}
142+
134143
public getDataArrayFromCards(cards: Card[], childCards: Map<string, Array<Card>>): any[] {
135144
const dataArray = [];
136145
cards.forEach((card) => {
137146
if (!this.isCardInDateRange(card)) return;
138147
if (!this.isCardInProcessIds(card)) return;
139148
if (!this.isCardInTypesOfState(card)) return;
140149
if (this.areCardsWithResponseFromMyEntitiesExcluded && card.hasChildCardFromCurrentUserEntity) return;
150+
if (this.areCardsWithResponseFromAllEntitiesExcluded && this.doesAllEntitiesHaveResponded(card, childCards))
151+
return;
141152
const data = {};
142153
this.customScreenDefinition.results.columns.forEach((column) => {
143154
switch (column.fieldType) {
@@ -198,6 +209,16 @@ export class ResultTable {
198209
value: dateAndTime.valueOf()
199210
};
200211
}
212+
private doesAllEntitiesHaveResponded(card: Card, childCards: Map<string, Array<Card>>): boolean {
213+
const entitiesToRespond = new Set(
214+
card.entitiesRequiredToRespond?.length > 0
215+
? card.entitiesRequiredToRespond
216+
: (card.entitiesAllowedToRespond ?? [])
217+
);
218+
if (entitiesToRespond.size === 0) return false;
219+
const respondedEntities = new Set(childCards.get(card.id)?.map((card) => card.publisher) ?? []);
220+
return Array.from(entitiesToRespond).every((entity) => respondedEntities.has(entity));
221+
}
201222

202223
private getPublisherLabel(card: Card): string {
203224
let publisherLabel = card.publisher;

ui/main/src/assets/i18n/en.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"toDateBeforeFromDate": "'To' date must be after 'From' date.",
2626
"businessDateRange": "BUSINESS DATE RANGE",
2727
"publishDateRange":"PUBLISH DATE RANGE",
28-
"activeDateRange": "ACTIVE DATE RANGE"
28+
"activeDateRange": "ACTIVE DATE RANGE",
29+
"showCardsWithResponseFromAllEntities": "Cards totally answered"
2930
},
3031
"result": {
3132
"severity": "SEVERITY",

ui/main/src/assets/i18n/fr.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"toDateBeforeFromDate": "La date 'Jusqu'à' doit être postérieure à la date 'À partir de'.",
2626
"businessDateRange": "PÉRIODE MÉTIER",
2727
"publishDateRange": "PÉRIODE DE PUBLICATION",
28-
"activeDateRange": "PÉRIODE D'ACTIVITÉ"
28+
"activeDateRange": "PÉRIODE D'ACTIVITÉ",
29+
"showCardsWithResponseFromAllEntities": "Cartes avec réponse de toutes les entités"
2930
},
3031
"result": {
3132
"severity": "SÉVÉRITÉ",

ui/main/src/assets/i18n/nl.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"toDateBeforeFromDate": "'Tot' moet na 'Van' liggen.",
2626
"businessDateRange": "BEDRIJFSDATUMBEREIK",
2727
"publishDateRange": "PUBLICATIEDATUMBEREIK",
28-
"activeDateRange": "ACTIEVE DATUMBEREIK"
28+
"activeDateRange": "ACTIEVE DATUMBEREIK",
29+
"showCardsWithResponseFromAllEntities": "Toon kaarten met reactie van alle entiteiten"
2930
},
3031
"result": {
3132
"severity": "ERNST",

0 commit comments

Comments
 (0)