Skip to content

Commit

Permalink
Merge branch 'P2-903-Tracking-of-outcomes-achievements-against-target…
Browse files Browse the repository at this point in the history
…s' into dev
  • Loading branch information
xKeCo committed Nov 27, 2024
2 parents 73d7391 + 889c2a8 commit 3e15f88
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ <h1 class="wp_header_title">Indicator list</h1>

<div class="search_input" style="width: 100%; margin-bottom: 2rem">
<i class="material-icons-round">search</i>
<input type="text" placeholder="Find indicator..." [(ngModel)]="this.outcomeIService.searchText" />
<input
type="text"
placeholder="Find indicator..."
[(ngModel)]="this.outcomeIService.searchText"
(keydown.backspace)="this.outcomeIService.searchText.set('')" />
</div>

<p-table
Expand Down Expand Up @@ -60,52 +64,54 @@ <h1 class="wp_header_title">Indicator list</h1>

<ng-template pTemplate="rowexpansion" let-item>
@for (result of item.toc_results; track $index) {
@if (!result.indicators) {
@if (result.indicators.length === 0 && result.isVisible) {
<tr style="height: 50px">
<td>{{ result?.toc_result_description }}</td>
<td colspan="6" style="text-align: center">No indicator data found</td>
</tr>
} @else {
@for (indicator of result.indicators; track $index) {
<tr>
@if ($index === 0) {
<td [attr.rowspan]="result.indicators.length">{{ result?.toc_result_description }}</td>
}
<td
class="indicator_link"
routerLink="/outcome-indicator-module/indicator-details"
[queryParams]="{ indicatorId: indicator?.indicator_uuid, platform: 'wps' }">
{{ indicator?.indicator_description }}
</td>
<td>
<span style="font-weight: bold">
{{ !indicator?.indicator_name ? '' : indicator?.is_indicator_custom ? 'Custom - ' : 'Standard - ' }}
</span>
{{ indicator?.indicator_name }}
</td>
<td style="text-align: center">{{ indicator?.indicator_target_value ?? 'Not defined' }}</td>
<td style="text-align: center">{{ indicator?.indicator_achieved_value ?? 'Not defined' }}</td>
<td style="text-align: center">
{{
this.outcomeIService.achievedStatus(indicator?.indicator_target_value, indicator?.indicator_achieved_value)
? 'Achieved'
: 'Not Achieved'
}}
</td>
<td>
<div
style="display: flex; justify-content: center; align-items: center; width: 100%; height: 100%"
[pTooltip]="indicator?.indicator_submission_status ? 'Submitted' : 'Editing'"
tooltipPosition="top"
positionTop="-5">
@if (indicator.isVisible) {
<tr>
@if ($index === 0) {
<td [attr.rowspan]="result.indicators.length">{{ result?.toc_result_description }}</td>
}
<td
class="indicator_link"
routerLink="/outcome-indicator-module/indicator-details"
[queryParams]="{ indicatorId: indicator?.indicator_uuid, platform: 'wps' }">
{{ indicator?.indicator_description }}
</td>
<td>
<span style="font-weight: bold">
{{ !indicator?.indicator_name ? '' : indicator?.is_indicator_custom ? 'Custom - ' : 'Standard - ' }}
</span>
{{ indicator?.indicator_name }}
</td>
<td style="text-align: center">{{ indicator?.indicator_target_value ?? 'Not defined' }}</td>
<td style="text-align: center">{{ indicator?.indicator_achieved_value ?? 'Not defined' }}</td>
<td style="text-align: center">
{{
this.outcomeIService.achievedStatus(indicator?.indicator_target_value, indicator?.indicator_achieved_value)
? 'Achieved'
: 'Not Achieved'
}}
</td>
<td>
<div
class="circle"
[ngClass]="{
'circle-success': indicator?.indicator_submission_status
}"></div>
</div>
</td>
</tr>
style="display: flex; justify-content: center; align-items: center; width: 100%; height: 100%"
[pTooltip]="indicator?.indicator_submission_status ? 'Submitted' : 'Editing'"
tooltipPosition="top"
positionTop="-5">
<div
class="circle"
[ngClass]="{
'circle-success': indicator?.indicator_submission_status
}"></div>
</div>
</td>
</tr>
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,121 +11,58 @@ describe('FilterIndicatorBySearchPipe', () => {
expect(pipe).toBeTruthy();
});

it('should return an empty array if input is not an array', () => {
const result = pipe.transform(null, 'search');
expect(result).toEqual([]);
});

it('should return the original list if searchFilter is empty', () => {
const list = [{ toc_result_description: 'Test' }];
const list = [{ toc_result_description: 'Test', toc_results: [] }];
const result = pipe.transform(list, '');
expect(result).toBeDefined();
expect(result).toEqual(list);
});

it('should filter the list based on searchFilter', () => {
const list = [
{ toc_result_description: 'Test1', indicators: [{ indicator_description: 'Desc1', indicator_name: 'Name1', is_indicator_custom: false }] },
{ toc_result_description: 'Test2', indicators: [{ indicator_description: 'Desc2', indicator_name: 'Name2', is_indicator_custom: true }] }
{ toc_result_description: 'Test1', toc_results: [{ toc_result_description: 'Desc1', indicators: [] }] },
{ toc_result_description: 'Test2', toc_results: [{ toc_result_description: 'Desc2', indicators: [] }] }
];
const result = pipe.transform(list, 'Desc1');
expect(result.length).toBe(1);
expect(result[0].toc_result_description).toBe('Test1');
});

it('should create joinAll string for each item', () => {
it('should handle isWPsTable flag correctly', () => {
const list = [
{
toc_result_description: 'Test1',
indicators: [{ indicator_description: 'Desc1', indicator_name: 'Name1', is_indicator_custom: false }],
joinAll: ''
toc_results: [
{
toc_result_description: 'Desc1',
indicators: [{ indicator_description: 'Indicator1', isVisible: false }]
}
]
}
];
pipe.transform(list, 'Desc1');
expect(list[0].joinAll).toBe('Test1 Desc1 Standard - Name1');
});

it('should handle items with missing toc_result_description ', () => {
const list = [
{
toc_result_description: null,
indicators: [{ indicator_description: 'Desc1', indicator_name: 'Name1', is_indicator_custom: false }],
joinAll: ''
}
];
pipe.transform(list, 'Desc1');
expect(list[0].joinAll).toBe('Desc1 Standard - Name1');
});

it('should handle items with missing indicator_name', () => {
const list = [
{
toc_result_description: 'Test1',
indicators: [{ indicator_description: 'Desc1', indicator_name: null, is_indicator_custom: false }],
joinAll: ''
}
];
pipe.transform(list, 'Desc1');
expect(list[0].joinAll).toBe('Test1 Desc1');
});

it('should handle items with missing indicators array', () => {
const list = [{ toc_result_description: 'Test1', indicators: [], joinAll: '' }];
pipe.transform(list, 'Test1');
expect(list[0].joinAll).toBe('Test1');
});

it('should filter the list based on searchFilter in a case-insensitive manner', () => {
const list = [
{ toc_result_description: 'Test1', indicators: [{ indicator_description: 'Desc1', indicator_name: 'Name1', is_indicator_custom: false }] },
{ toc_result_description: 'Test2', indicators: [{ indicator_description: 'Desc2', indicator_name: 'Name2', is_indicator_custom: true }] }
];
const result = pipe.transform(list, 'desc1');
const result = pipe.transform(list, 'Indicator1', true);
expect(result.length).toBe(1);
expect(result[0].toc_result_description).toBe('Test1');
});

it('should return the original list if searchFilter is null', () => {
const list = [{ toc_result_description: 'Test' }];
const result = pipe.transform(list, null);
expect(result).toEqual(list);
});

it('should handle items with missing workpackage_name when isWPsTable is true', () => {
const list = [
{
workpackage_name: null,
toc_result_description: 'Test1',
indicators: [{ indicator_description: 'Desc1', indicator_name: 'Name1', is_indicator_custom: false }],
joinAll: ''
}
];
pipe.transform(list, 'Test1', true);
expect(list[0].joinAll).toBe('');
});

it('should create joinAll string using workpackage_name when isWPsTable is true', () => {
const list = [
{
workpackage_name: 'WP1',
toc_result_description: 'Test1',
indicators: [{ indicator_description: 'Desc1', indicator_name: 'Name1', is_indicator_custom: false }],
joinAll: ''
}
];
pipe.transform(list, 'WP1', true);
expect(list[0].joinAll).toBe('WP1');
expect(result[0].toc_results[0].indicators[0].isVisible).toBe(true);
});

it('should filter the list based on workpackage_name when isWPsTable is true', () => {
it('should reset indicators if searchFilter is empty and isWPsTable is true', () => {
const list = [
{
workpackage_name: 'WP1',
toc_result_description: 'Test1',
indicators: [{ indicator_description: 'Desc1', indicator_name: 'Name1', is_indicator_custom: false }]
},
{
workpackage_name: 'WP2',
toc_result_description: 'Test2',
indicators: [{ indicator_description: 'Desc2', indicator_name: 'Name2', is_indicator_custom: true }]
toc_results: [
{
toc_result_description: 'Desc1',
indicators: [{ indicator_description: 'Indicator1', isVisible: true }]
}
]
}
];
const result = pipe.transform(list, 'WP1', true);
expect(result.length).toBe(1);
expect(result[0].workpackage_name).toBe('WP1');
pipe.transform(list, '', true);
expect(list[0].toc_results[0].indicators[0].isVisible).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,97 @@ import { Pipe, PipeTransform } from '@angular/core';
standalone: true
})
export class FilterIndicatorBySearchPipe implements PipeTransform {
transform(list, searchFilter: string, isWPsTable: boolean = false): any[] {
transform(list: any[], searchFilter: string, isWPsTable: boolean = false): any[] {
if (!Array.isArray(list)) return [];

const searchUpper = searchFilter?.toUpperCase() || '';

list.forEach(item => {
item.joinAll = isWPsTable ? this.createJoinAllStringForWPsTable(item) : this.createJoinAllStringDefault(item);
if (isWPsTable) {
this.processIndicators(item, searchUpper);
}
});

if (!searchFilter) {
if (isWPsTable) {
this.resetIndicators(list);
}
return list;
}

return isWPsTable ? this.filterWPsTable(list, searchUpper) : this.filterList(list, searchUpper);
}

private processIndicators(item: any, searchUpper: string): void {
item.toc_results.forEach(result => {
result.originalIndicators = result.originalIndicators || [...result.indicators];
result.isVisible = this.isResultVisible(result, searchUpper);
result.indicators.forEach(indicator => {
indicator.isVisible = result.isVisible || this.isIndicatorVisible(indicator, searchUpper, result);
});
});
}

private isIndicatorVisible(indicator: any, searchUpper: string, result: any): boolean {
// return indicator.indicator_description.toUpperCase().includes(searchUpper);
return (
!searchUpper ||
indicator.indicator_description.toUpperCase().includes(searchUpper) ||
result.toc_result_description.toUpperCase().includes(searchUpper)
);
}

private isResultVisible(result: any, searchUpper: string): boolean {
return result.toc_result_description.toUpperCase().includes(searchUpper);
}

private resetIndicators(list: any[]): void {
list.forEach(item => {
item.joinAll = this.createJoinAllString(item, isWPsTable).trim();
item.toc_results.forEach(result => {
result.indicators = [...result.originalIndicators];
});
});
}

private filterWPsTable(list: any[], searchUpper: string): any[] {
return list.filter(item => {
const hasVisibleIndicators = item.toc_results.some(result => result.indicators.some(indicator => indicator.isVisible));
if (hasVisibleIndicators) {
item.toc_results.forEach(result => {
result.indicators = result.indicators.filter(indicator => indicator.isVisible);
});
return true;
}
return item.joinAll.toUpperCase().includes(searchUpper);
});
}

return list.filter(item => item.joinAll.toUpperCase().includes(searchFilter.toUpperCase()));
private filterList(list: any[], searchUpper: string): any[] {
return list.filter(item => item.joinAll.toUpperCase().includes(searchUpper));
}

private createJoinAllString(item, isWPsTable): string {
return this.createDefaultString(item, isWPsTable);
private createJoinAllStringForWPsTable(item: any): string {
return this.createWPsTableString(item);
}

private createDefaultString(item, isWPsTable): string {
if (isWPsTable) {
return `${item?.workpackage_name ?? ''}`;
private createJoinAllStringDefault(item: any): string {
return this.createDefaultString(item);
}

private createWPsTableString(item: any): string {
return item.toc_results.map(result => result.toc_result_description).join(', ');
}

private createDefaultString(item: any): string {
const indicator = item?.indicators?.[0] || {};
const indicatorName = indicator.indicator_name || '';
let indicatorType = '';

if (indicatorName) {
indicatorType = indicator.is_indicator_custom ? 'Custom -' : 'Standard -';
}

return `${item?.toc_result_description ?? ''} ${item?.indicators?.[0]?.indicator_description ?? ''} ${!item?.indicators?.[0]?.indicator_name ? '' : item?.indicators?.[0]?.is_indicator_custom ? 'Custom -' : 'Standard -'} ${item?.indicators?.[0]?.indicator_name ?? ''}`;
return `${item?.toc_result_description || ''} ${indicator.indicator_description || ''} ${indicatorType} ${indicatorName}`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ describe('OutcomeIndicatorService', () => {
expect(service.loadingWPs()).toBe(false);
});

it('should set wpsData with indicators when indicators are null', () => {
const response = { data: [{ toc_results: [{ indicators: null }] }] };
const subscribeMock = jest.fn(({ next }) => next(response));
apiServiceMock.resultsSE.GET_contributionsToIndicatorsWPS.mockReturnValue({ subscribe: subscribeMock });

service.getWorkPackagesData();
});

it('should return true when achievedTarget is greater than or equal to expectedTarget', () => {
const expectedTarget = 10;
const achievedTarget = 15;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@ export class OutcomeIndicatorService {
this.loadingWPs.set(true);
this.api.resultsSE.GET_contributionsToIndicatorsWPS(this.initiativeIdFilter).subscribe({
next: res => {
this.wpsData = res.data;
this.wpsData = res.data.map(item => {
item.toc_results.forEach(result => {
if (result.indicators === null) {
result.indicators = [];
}
});
return item;
});
this.loadingWPs.set(false);
this.expandAll();
},
Expand Down

0 comments on commit 3e15f88

Please sign in to comment.