diff --git a/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.data.ts b/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.data.ts index 62d7933c5b19a1f5817d5e6e4448fd274f82f703..563bae08a52eedc9aeed029f29f4ae152af50570 100644 --- a/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.data.ts +++ b/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.data.ts @@ -2915,90 +2915,162 @@ export let mockData = { }, ], }, - selectedReportWithConfigurableFilters: { - name: "Filtered task detail report", - encrypt: true, - datasetId: "ml-filtered-task-detail-exhaust", - roles: ["PROGRAM_MANAGER"], - configurableFilters: true, - filters: [ - { - type: "equals", - dimension: "private_program", - value: "false", - }, - { - type: "equals", - dimension: "sub_task_deleted_flag", - value: "false", - }, - { - type: "equals", - dimension: "task_deleted_flag", - value: "false", - }, - { - type: "equals", - dimension: "project_deleted_flag", - value: "false", - }, - { - type: "equals", - dimension: "program_id", - value: "$programId", - }, - { - type: "equals", - dimension: "solution_id", - value: "$solutionId", - }, - { - type: "equals", - dimension: "district_externalId", - value: "$district_externalId", - }, - { - type: "equals", - dimension: "organisation_id", - value: "$organisation_id", - }, - { - type: "greaterthan", - dimension: "task_count", - value: "$task_count", - }, - { - type: "greaterthan", - dimension: "task_evidence_count", - value: "$task_evidence_count", - }, - { - type: "greaterthan", - dimension: "project_evidence_count", - value: "$project_evidence_count", - }, - ], - uiFilters: [ - { - label: "Minimum no. of tasks in the project", - controlType: "number", - reference: "task_count", - defaultValue: 5, - }, - { - label: "Minimum no. of task evidence", - controlType: "number", - reference: "task_evidence_count", - defaultValue: 2, - }, - { - label: "Minimum no. of project evidence", - controlType: "number", - reference: "project_evidence_count", - defaultValue: 1, - }, - ], - }, + selectedReportWithConfigurableFilters: [ + { + name: "Filtered task detail report", + encrypt: true, + datasetId: "ml-filtered-task-detail-exhaust", + roles: ["PROGRAM_MANAGER"], + configurableFilters: true, + filters: [ + { + type: "equals", + dimension: "private_program", + value: "false", + }, + { + type: "equals", + dimension: "sub_task_deleted_flag", + value: "false", + }, + { + type: "equals", + dimension: "task_deleted_flag", + value: "false", + }, + { + type: "equals", + dimension: "project_deleted_flag", + value: "false", + }, + { + type: "equals", + dimension: "program_id", + value: "$programId", + }, + { + type: "equals", + dimension: "solution_id", + value: "$solutionId", + }, + { + type: "equals", + dimension: "district_externalId", + value: "$district_externalId", + }, + { + type: "equals", + dimension: "organisation_id", + value: "$organisation_id", + }, + { + type: "greaterthan", + dimension: "task_count", + value: "$task_count", + }, + { + type: "greaterthan", + dimension: "task_evidence_count", + value: "$task_evidence_count", + }, + { + type: "greaterthan", + dimension: "project_evidence_count", + value: "$project_evidence_count", + }, + ], + uiFilters: [ + { + label: "Minimum no. of tasks in the project", + controlType: "number", + reference: "task_count", + defaultValue: 5, + }, + { + label: "Minimum no. of task evidence", + controlType: "number", + reference: "task_evidence_count", + defaultValue: 2, + }, + { + label: "Minimum no. of project evidence", + controlType: "number", + reference: "project_evidence_count", + defaultValue: 1, + }, + ], + }, + { + "name": "Status Report", + "encrypt": false, + "datasetId": "ml-project-status-exhaust", + "roles": [ + "PROGRAM_MANAGER", + "PROGRAM_DESIGNER" + ], + "configurableFilters": true, + "filters": [ + { + "type": "in", + "dimension": "status_of_project", + "values": "$status_of_project" + }, + { + "type": "equals", + "dimension": "private_program", + "value": "false" + }, + { + "type": "equals", + "dimension": "sub_task_deleted_flag", + "value": "false" + }, + { + "type": "equals", + "dimension": "task_deleted_flag", + "value": "false" + }, + { + "type": "equals", + "dimension": "project_deleted_flag", + "value": "false" + }, + { + "type": "equals", + "dimension": "program_id", + "value": "$programId" + }, + { + "type": "equals", + "dimension": "solution_id", + "value": "$solutionId" + }, + { + "type": "equals", + "dimension": "district_externalId", + "value": "$district_externalId" + }, + { + "type": "equals", + "dimension": "organisation_id", + "value": "$organisation_id" + } + ], + "uiFilters": [ + { + "label": "Status", + "controlType": "multi-select", + "reference": "status_of_project", + "placeholder": "Select status", + "options": [ + "started", + "submitted", + "inProgress" + ] + } + ] + } + ], multipleDataDownloaded: [ { loaded: true, diff --git a/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.ts b/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.ts index d1d9be9269a31aa4f2f29af479a8663632f347ac..d4ace54188815119a2767361132ef92dc6222ce6 100644 --- a/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.ts +++ b/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.spec.ts @@ -526,15 +526,46 @@ describe('DatasetsComponent', () => { it('should update the selected report data for Filtered task detail report ', () => { jest.spyOn(component,'reportChanged'); - component.reportChanged(mockData.selectedReportWithConfigurableFilters) - expect(component.selectedReport).toBe(mockData.selectedReportWithConfigurableFilters); + component.reportChanged(mockData.selectedReportWithConfigurableFilters[0]) + expect(component.selectedReport).toBe(mockData.selectedReportWithConfigurableFilters[0]); expect(component.reportChanged).toHaveBeenCalled(); }); + it('should update the selected report data for status report ', () => { + jest.spyOn(component,'reportChanged'); + component.reportChanged(mockData.selectedReportWithConfigurableFilters[1]) + expect(component.selectedReport).toBe(mockData.selectedReportWithConfigurableFilters[1]); + expect(component.reportChanged).toHaveBeenCalled(); + }); + + it('should call pdFilterChanged method for number type', () => { + jest.spyOn(component,'pdFilterChanged'); + component.pdFilterChanged({data:{task_count:5}, controlType:'number'}); + expect(component.pdFilterChanged).toHaveBeenCalledWith({data:{task_count:5}, controlType:'number'}); + }); + + it('should call pdFilterChanged method for number type with negative value', () => { + jest.spyOn(component,'pdFilterChanged'); + component.pdFilterChanged({data:{task_count:-1}, controlType:'number'}); + expect(component.pdFilterChanged).toHaveBeenCalledWith({data:{task_count:-1}, controlType:'number'}); + }); + + it('should call pdFilterChanged method for number type with zero value', () => { + jest.spyOn(component,'pdFilterChanged'); + component.pdFilterChanged({data:{task_count:0}, controlType:'number'}); + expect(component.pdFilterChanged).toHaveBeenCalledWith({data:{task_count:0}, controlType:'number'}); + }); + + it('should call pdFilterChanged method for number type with null value', () => { + jest.spyOn(component,'pdFilterChanged'); + component.pdFilterChanged({data:{task_count:null}, controlType:'number'}); + expect(component.pdFilterChanged).toHaveBeenCalledWith({data:{task_count:null}, controlType:'number'}); + }); + it('should call pdFilterChanged method', () => { jest.spyOn(component,'pdFilterChanged'); - component.pdFilterChanged({task_count:5}); - expect(component.pdFilterChanged).toHaveBeenCalledWith({task_count:5}); + component.pdFilterChanged({data:{status:['started']}, controlType:'multi-select'}); + expect(component.pdFilterChanged).toHaveBeenCalledWith({data:{status:['started']}, controlType:'multi-select'}); }); it('should request the csv', () => { diff --git a/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.ts b/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.ts index 04ff19579c51e8afe1c99a2e823f35fc63185ad8..39d60af958976a6d8577b8fd58c25a0e368b5bf6 100644 --- a/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.ts +++ b/src/app/client/src/app/modules/program-dashboard/components/program-datasets/program-datasets.component.ts @@ -249,6 +249,9 @@ export class DatasetsComponent implements OnInit, OnDestroy { public programSelection($event) { this.reportForm.reset(); + this.displayFilters = {}; + this.districts = [] + this.organisations = []; const program = this.programs.filter(data => { if (data._id == $event.value) { return data; @@ -271,6 +274,8 @@ export class DatasetsComponent implements OnInit, OnDestroy { this.districts = [] this.organisations = []; this.resetConfigFilters(); + delete this.displayFilters['District']; + delete this.displayFilters['Organisation']; this.globalDistrict = this.globalOrg = undefined; if (this.programSelected && this.reportForm.value && this.reportForm.value['solution']) { const solution = this.solutions.filter(data => { @@ -526,6 +531,7 @@ export class DatasetsComponent implements OnInit, OnDestroy { this.goToPrevLocation = false; this.showPopUpModal = true; this.globalDistrict = this.globalOrg = undefined; + this.displayFilters = {}; this.timeRangeInit(); this.resetConfigFilters(); } @@ -546,6 +552,8 @@ export class DatasetsComponent implements OnInit, OnDestroy { this.reportForm.controls.districtName.setValue($event.value); this.displayFilters['District'] = [$event?.source?.triggerValue] this.tag = _.get(this.reportForm, 'controls.solution.value')+ '_' + this.userId+'_'+ _.toLower(_.trim([$event?.source?.triggerValue]," ")); + this.reportForm.controls.reportType.setValue(''); + this.resetConfigFilters(); this.loadReports(); } @@ -561,7 +569,11 @@ export class DatasetsComponent implements OnInit, OnDestroy { if(this.selectedReport.configurableFilters){ this.pdFilters = this.selectedReport.uiFilters; this.pdFilters.map(filter => { - this.configuredFilters[filter['reference']] = filter['defaultValue'] as number -1 + if(filter['controlType'] === 'number'){ + this.configuredFilters[filter['reference']] = filter['defaultValue'] as number -1 + }else if(filter['controlType'] === 'multi-select'){ + this.configuredFilters[filter['reference']] = undefined + } }) } } @@ -572,15 +584,30 @@ export class DatasetsComponent implements OnInit, OnDestroy { } pdFilterChanged($event){ - const [reference, value]= [Object.keys($event),Object.values($event)] ; - if([0,null].includes(value[0] as number) || value[0] < 0){ - this.configuredFilters[reference[0]] = undefined; - }else{ - this.configuredFilters[reference[0]] = value[0] as number -1; + if($event.data){ + const [reference, value]= [Object.keys($event.data),Object.values($event.data)] ; + if($event.controlType === 'number'){ + if([0,null].includes(value[0] as number) || value[0] < 0){ + this.configuredFilters[reference[0]] = undefined; + }else{ + this.configuredFilters[reference[0]] = value[0] as number -1; + } + }else if($event.controlType === 'multi-select'){ + if((value[0] as string[]).length){ + this.configuredFilters[reference[0]] = value[0] + }else{ + this.configuredFilters[reference[0]] = undefined; + } + } } } addFilters() { + this.pdFilters.map(filter => { + if(filter['controlType'] === 'multi-select' && this.configuredFilters[filter['reference']] === undefined){ + this.configuredFilters[filter['reference']] = filter['options'] + } + }) let filterKeysObj = { program_id: _.get(this.reportForm, 'controls.programName.value'), solution_id: _.get(this.reportForm, 'controls.solution.value'), @@ -595,9 +622,9 @@ export class DatasetsComponent implements OnInit, OnDestroy { this.selectedReport['filters'].map(data => { keys.filter(key => { - return data.dimension == key && (data.value = filterKeysObj[key]); + return data.dimension === key && (_.has(data,'value') ? data.value = filterKeysObj[key] : data.values = filterKeysObj[key]); }) - if (data.value !== undefined) { + if (data.value !== undefined || data.values !== undefined) { this.filter.push(data); } }); diff --git a/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.html b/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.html index 3c7dabd4d169846562d2a11149019633af64ff5f..5239d303f1439eb7970d71b7530b21871ef93d80 100644 --- a/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.html +++ b/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.html @@ -15,5 +15,16 @@ /> </mat-form-field> </ng-container> + <ng-container *ngIf="pdFilter.controlType === 'multi-select'"> + <mat-form-field + appearance="fill" + class="sb-mat__dropdown" + > + <mat-select (selectionChange)="inputChange()" multiple [formControlName]="pdFilter.reference" [placeholder]="pdFilter.placeholder" > + <mat-option class="mat-dropdown__options" role="option" *ngFor="let option of pdFilter.options" [value]="option" + attr.aria-label="{{option}}" class="custom_mat_multi">{{option | lowercase | titlecase}}</mat-option> + </mat-select> + </mat-form-field> + </ng-container> </div> </form> diff --git a/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.scss b/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..a1b554894a4e08245e921d75e16e1d81869fc0ba --- /dev/null +++ b/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.scss @@ -0,0 +1,5 @@ +::ng-deep{ + .custom_mat_multi.mat-option{ + display: flex !important; + } +} diff --git a/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.ts b/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.ts index 8da95651877dc9a59da42f144ed050958712d643..e765cc546765e1c7fb94c751f87eeb115000de53 100644 --- a/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.ts +++ b/src/app/client/src/app/modules/program-dashboard/shared/pd-filters/pd-filters.component.ts @@ -4,7 +4,8 @@ import * as _ from "lodash-es"; @Component({ selector: "app-pd-filters", - templateUrl: "./pd-filters.component.html" + templateUrl: "./pd-filters.component.html", + styleUrls:["./pd-filters.component.scss"] }) export class PdFiltersComponent implements OnInit { @Input() pdFilter: any; @@ -26,6 +27,10 @@ export class PdFiltersComponent implements OnInit { } inputChange() { - this.filterChanged.emit(this.pdFiltersFormGroup.value); + const dataToBeEmitted = { + data:this.pdFiltersFormGroup.value, + controlType:this.pdFilter.controlType + } + this.filterChanged.emit(dataToBeEmitted); } }