From b3eb7a42cecc4158cf356f22593c622621a76b6e Mon Sep 17 00:00:00 2001 From: Tristan <19949441+Tristan2357@users.noreply.github.com> Date: Mon, 28 Feb 2022 18:39:03 +0100 Subject: [PATCH] TSK-1806: Bug fixes for Workbasket Distribution Targets Bug fixes: Changes no longer get discarded when switching tabs Saving of Workbasket Distribution Targets is now possible from all tabs Saving of Workbasket Distribution Targets now works correctly and does not throw an error 400 Removing of Distribution Targets now works correctly Removed loading of duplicate distribution targets Added E2E tests for most previously mentioned bugs --- .../workbaskets/workbaskets.spec.js | 54 +++++++++++++++++-- .../workbasket-details.component.ts | 17 ++++++ ...ket-distribution-targets-list.component.ts | 35 ++++++------ ...rkbasket-distribution-targets.component.ts | 4 +- .../workbasket-store/workbasket.state.ts | 16 ++++-- 5 files changed, 97 insertions(+), 29 deletions(-) diff --git a/web/cypress/integration/workbaskets/workbaskets.spec.js b/web/cypress/integration/workbaskets/workbaskets.spec.js index 0dbd33c92..efe52b8ac 100644 --- a/web/cypress/integration/workbaskets/workbaskets.spec.js +++ b/web/cypress/integration/workbaskets/workbaskets.spec.js @@ -197,11 +197,6 @@ context('TASKANA Workbaskets', () => { cy.get('#wb-custom-4').should('have.value', Cypress.env('testValueWorkbaskets')); }); - it('should be possible', () => { - cy.visitTestWorkbasket(); - cy.visitWorkbasketsDistributionTargetsPage(); - }); - it('should be possible to remove all distribution targets', () => { cy.visitTestWorkbasket(); cy.visitWorkbasketsDistributionTargetsPage(); @@ -217,4 +212,53 @@ context('TASKANA Workbaskets', () => { cy.undoWorkbaskets(); }); + + it('should filter selected distribution targets including newly transferred targets', () => { + cy.visitTestWorkbasket(); + cy.visitWorkbasketsDistributionTargetsPage(); + + cy.get('#dual-list-Right > .distribution-targets-list > .mat-toolbar > :nth-child(2)').click(); + cy.get( + '#dual-list-Right > .distribution-targets-list > taskana-shared-workbasket-filter > .filter > .filter__expanded-filter > .filter__text-input > :nth-child(1) > :nth-child(2) > .mat-form-field-wrapper > .mat-form-field-flex > .mat-form-field-infix > .mat-input-element' + ) + .clear() + .type('002'); + cy.get('.filter__action-buttons > .filter__search-button').click(); + + cy.get( + '#dual-list-Right > .distribution-targets-list > .mat-selection-list > .cdk-virtual-scroll-viewport > .cdk-virtual-scroll-content-wrapper > :nth-child(1)' + ).should('contain.text', 'Basxet1'); + + cy.get('.filter__action-buttons > [mattooltip="Clear Workbasket filter"]').click(); + + cy.get( + '#dual-list-Right > .distribution-targets-list > .mat-selection-list > .cdk-virtual-scroll-viewport > .cdk-virtual-scroll-content-wrapper' + ) + .children() + .should('have.length', 2); + }); + + it('should filter available distribution targets', () => { + cy.visitTestWorkbasket(); + cy.visitWorkbasketsDistributionTargetsPage(); + + cy.get('#dual-list-Left > .distribution-targets-list > .mat-toolbar > :nth-child(2)').click(); + cy.get( + '#dual-list-Left > .distribution-targets-list > taskana-shared-workbasket-filter > .filter > .filter__expanded-filter > .filter__text-input > :nth-child(1) > :nth-child(2) > .mat-form-field-wrapper > .mat-form-field-flex > .mat-form-field-infix > .mat-input-element' + ) + .clear() + .type('008'); + + cy.get('.filter__action-buttons > .filter__search-button').click(); + + cy.get( + '#dual-list-Left > .distribution-targets-list > .mat-selection-list > .cdk-virtual-scroll-viewport > .cdk-virtual-scroll-content-wrapper' + ) + .children() + .should('have.length', 1); + + cy.get( + '#dual-list-Left > .distribution-targets-list > .mat-selection-list > .cdk-virtual-scroll-viewport > .cdk-virtual-scroll-content-wrapper > :nth-child(1)' + ).should('contain.text', 'BAsxet7'); + }); }); diff --git a/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts b/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts index e71672abe..b151423fe 100644 --- a/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts +++ b/web/src/app/administration/components/workbasket-details/workbasket-details.component.ts @@ -15,6 +15,7 @@ import { CopyWorkbasket, DeselectWorkbasket, OnButtonPressed, + SaveNewWorkbasket, SelectComponent, UpdateWorkbasket, UpdateWorkbasketDistributionTargets @@ -97,6 +98,22 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy { .subscribe((wb) => (this.workbasket = wb)); }); }); + + this.ngxsActions$.pipe(ofActionSuccessful(SaveNewWorkbasket), takeUntil(this.destroy$)).subscribe(() => { + this.store + .dispatch(new UpdateWorkbasketDistributionTargets()) + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.selectedWorkbasket$ + .pipe( + take(5), + timeout(250), + catchError(() => of(null)), + filter((val) => val !== null) + ) + .subscribe((wb) => (this.workbasket = wb)); + }); + }); } selectComponent(index) { diff --git a/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.ts b/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.ts index 05dea6002..1e8610a19 100644 --- a/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.ts +++ b/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.ts @@ -58,6 +58,7 @@ export class WorkbasketDistributionTargetsListComponent toolbarState = false; distributionTargets: WorkbasketDistributionTarget[]; + distributionTargetsClone: WorkbasketDistributionTarget[]; @ViewChild('workbasket') distributionTargetsList: MatSelectionList; @ViewChild('scroller') workbasketList: CdkVirtualScrollViewport; @@ -69,30 +70,25 @@ export class WorkbasketDistributionTargetsListComponent ngOnInit(): void { if (this.side === Side.AVAILABLE) { - this.availableDistributionTargets$.pipe(takeUntil(this.destroy$)).subscribe((wbs) => { - this.distributionTargets = wbs.map((wb) => { - return { ...wb, selected: this.allSelected }; - }); - }); + this.availableDistributionTargets$.pipe(takeUntil(this.destroy$)).subscribe((wbs) => this.assignWbs(wbs)); this.availableDistributionTargetsFilter$.pipe(takeUntil(this.destroy$)).subscribe((filter) => { - if (typeof this.filter === 'undefined' || isEqual(this.filter, filter)) return; + if (typeof this.filter === 'undefined' || isEqual(this.filter, filter)) { + this.filter = filter; + return; + } this.filter = filter; this.store.dispatch(new FetchAvailableDistributionTargets(true, this.filter)); this.selectAll(false); }); } else { - this.workbasketDistributionTargets$.pipe().subscribe((wbs) => { - this.distributionTargets = wbs.map((wb) => { - return { ...wb }; - }); - }); + this.workbasketDistributionTargets$.pipe().subscribe((wbs) => this.assignWbs(wbs)); this.selectedDistributionTargetsFilter$.pipe(takeUntil(this.destroy$)).subscribe((filter) => { - if (typeof this.filter === 'undefined' || isEqual(this.filter, filter)) return; + if (typeof this.filter === 'undefined' || isEqual(this.filter, filter)) { + this.filter = filter; + return; + } this.filter = filter; - this.store - .dispatch(new FetchWorkbasketDistributionTargets(true)) - .pipe(take(1)) - .subscribe(() => this.applyFilter()); + this.applyFilter(); this.selectAll(false); }); } @@ -167,6 +163,11 @@ export class WorkbasketDistributionTargetsListComponent this.destroy$.complete(); } + private assignWbs(wbs: WorkbasketSummary[]) { + this.distributionTargets = wbs.map((wb) => ({ ...wb, selected: this.allSelected })); + this.distributionTargetsClone = this.distributionTargets; + } + private applyFilter() { function filterExact(target: WorkbasketDistributionTarget, filterStrings: string[], attribute: string) { if (!!filterStrings && filterStrings?.length !== 0) { @@ -186,7 +187,7 @@ export class WorkbasketDistributionTargetsListComponent return true; } - this.distributionTargets = this.distributionTargets?.filter((target) => { + this.distributionTargets = this.distributionTargetsClone?.filter((target) => { let matches = true; matches = matches && filterExact(target, this.filter.name, 'name'); matches = matches && filterExact(target, this.filter.key, 'key'); diff --git a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts index 83247b360..a6ad06169 100644 --- a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts +++ b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.ts @@ -3,14 +3,12 @@ import { forkJoin, Observable, Subject } from 'rxjs'; import { Workbasket } from 'app/shared/models/workbasket'; import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; -import { Actions, ofActionCompleted, Select, Store } from '@ngxs/store'; +import { Actions, Select, Store } from '@ngxs/store'; import { filter, take, takeUntil } from 'rxjs/operators'; import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { FetchAvailableDistributionTargets, FetchWorkbasketDistributionTargets, - SaveNewWorkbasket, - UpdateWorkbasket, UpdateWorkbasketDistributionTargets } from '../../../shared/store/workbasket-store/workbasket.actions'; import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors'; diff --git a/web/src/app/shared/store/workbasket-store/workbasket.state.ts b/web/src/app/shared/store/workbasket-store/workbasket.state.ts index 6c5e51a8d..5d056aeb9 100644 --- a/web/src/app/shared/store/workbasket-store/workbasket.state.ts +++ b/web/src/app/shared/store/workbasket-store/workbasket.state.ts @@ -237,7 +237,7 @@ export class WorkbasketState implements NgxsAfterBootstrap { tap((domain) => { this.location.go(this.location.path().replace(/(workbaskets).*/g, 'workbaskets/(detail:new-workbasket)')); - if (!ctx.getState().availableDistributionTargets) { + if (ctx.getState().availableDistributionTargets.workbaskets.length === 0) { ctx.dispatch(new FetchAvailableDistributionTargets(true)); } @@ -389,7 +389,8 @@ export class WorkbasketState implements NgxsAfterBootstrap { this.notificationService.showSuccess('WORKBASKET_DISTRIBUTION_TARGET_SAVE', { workbasketName: ctx.getState().selectedWorkbasket.name }); - + ctx.dispatch(new FetchAvailableDistributionTargets(true)); + ctx.dispatch(new FetchWorkbasketDistributionTargets(true)); return of(null); }, error: () => { @@ -404,12 +405,13 @@ export class WorkbasketState implements NgxsAfterBootstrap { ctx: StateContext, action: FetchWorkbasketDistributionTargets ): Observable { - const { selectedWorkbasket, distributionTargetsPage, workbasketDistributionTargets } = ctx.getState(); + const { selectedWorkbasket, distributionTargetsPage, workbasketDistributionTargets, availableDistributionTargets } = + ctx.getState(); const { filterParameter, sortParameter, refetchAll } = action; const nextDistributionTargetsPage = refetchAll ? 1 : distributionTargetsPage + 1; return this.workbasketService .getWorkBasketsDistributionTargets( - selectedWorkbasket._links.distributionTargets.href, + selectedWorkbasket._links?.distributionTargets.href, filterParameter, sortParameter, new WorkbasketQueryPagingParameter(nextDistributionTargetsPage) @@ -422,8 +424,14 @@ export class WorkbasketState implements NgxsAfterBootstrap { const idArrayNoDupe = [...new Set(completeArray.map((wb) => wb.workbasketId))]; wbt.distributionTargets = idArrayNoDupe.map((id) => completeArray.find((wb) => wb.workbasketId === id)); } + const distributionTargetSet = new Set(wbt.distributionTargets.map((wb) => wb.workbasketId)); + let availableTargets = cloneDeep(availableDistributionTargets); + availableTargets.workbaskets = availableTargets.workbaskets.filter((wb) => { + return !distributionTargetSet.has(wb.workbasketId); + }); ctx.patchState({ workbasketDistributionTargets: wbt, + availableDistributionTargets: availableTargets, distributionTargetsPage: nextDistributionTargetsPage }); })