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
This commit is contained in:
Tristan 2022-02-28 18:39:03 +01:00 committed by Tristan2357
parent 38dba9a098
commit b3eb7a42ce
5 changed files with 97 additions and 29 deletions

View File

@ -197,11 +197,6 @@ context('TASKANA Workbaskets', () => {
cy.get('#wb-custom-4').should('have.value', Cypress.env('testValueWorkbaskets')); 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', () => { it('should be possible to remove all distribution targets', () => {
cy.visitTestWorkbasket(); cy.visitTestWorkbasket();
cy.visitWorkbasketsDistributionTargetsPage(); cy.visitWorkbasketsDistributionTargetsPage();
@ -217,4 +212,53 @@ context('TASKANA Workbaskets', () => {
cy.undoWorkbaskets(); 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');
});
}); });

View File

@ -15,6 +15,7 @@ import {
CopyWorkbasket, CopyWorkbasket,
DeselectWorkbasket, DeselectWorkbasket,
OnButtonPressed, OnButtonPressed,
SaveNewWorkbasket,
SelectComponent, SelectComponent,
UpdateWorkbasket, UpdateWorkbasket,
UpdateWorkbasketDistributionTargets UpdateWorkbasketDistributionTargets
@ -97,6 +98,22 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
.subscribe((wb) => (this.workbasket = wb)); .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) { selectComponent(index) {

View File

@ -58,6 +58,7 @@ export class WorkbasketDistributionTargetsListComponent
toolbarState = false; toolbarState = false;
distributionTargets: WorkbasketDistributionTarget[]; distributionTargets: WorkbasketDistributionTarget[];
distributionTargetsClone: WorkbasketDistributionTarget[];
@ViewChild('workbasket') distributionTargetsList: MatSelectionList; @ViewChild('workbasket') distributionTargetsList: MatSelectionList;
@ViewChild('scroller') workbasketList: CdkVirtualScrollViewport; @ViewChild('scroller') workbasketList: CdkVirtualScrollViewport;
@ -69,30 +70,25 @@ export class WorkbasketDistributionTargetsListComponent
ngOnInit(): void { ngOnInit(): void {
if (this.side === Side.AVAILABLE) { if (this.side === Side.AVAILABLE) {
this.availableDistributionTargets$.pipe(takeUntil(this.destroy$)).subscribe((wbs) => { this.availableDistributionTargets$.pipe(takeUntil(this.destroy$)).subscribe((wbs) => this.assignWbs(wbs));
this.distributionTargets = wbs.map((wb) => {
return { ...wb, selected: this.allSelected };
});
});
this.availableDistributionTargetsFilter$.pipe(takeUntil(this.destroy$)).subscribe((filter) => { 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.filter = filter;
this.store.dispatch(new FetchAvailableDistributionTargets(true, this.filter)); this.store.dispatch(new FetchAvailableDistributionTargets(true, this.filter));
this.selectAll(false); this.selectAll(false);
}); });
} else { } else {
this.workbasketDistributionTargets$.pipe().subscribe((wbs) => { this.workbasketDistributionTargets$.pipe().subscribe((wbs) => this.assignWbs(wbs));
this.distributionTargets = wbs.map((wb) => {
return { ...wb };
});
});
this.selectedDistributionTargetsFilter$.pipe(takeUntil(this.destroy$)).subscribe((filter) => { 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.filter = filter;
this.store this.applyFilter();
.dispatch(new FetchWorkbasketDistributionTargets(true))
.pipe(take(1))
.subscribe(() => this.applyFilter());
this.selectAll(false); this.selectAll(false);
}); });
} }
@ -167,6 +163,11 @@ export class WorkbasketDistributionTargetsListComponent
this.destroy$.complete(); this.destroy$.complete();
} }
private assignWbs(wbs: WorkbasketSummary[]) {
this.distributionTargets = wbs.map((wb) => ({ ...wb, selected: this.allSelected }));
this.distributionTargetsClone = this.distributionTargets;
}
private applyFilter() { private applyFilter() {
function filterExact(target: WorkbasketDistributionTarget, filterStrings: string[], attribute: string) { function filterExact(target: WorkbasketDistributionTarget, filterStrings: string[], attribute: string) {
if (!!filterStrings && filterStrings?.length !== 0) { if (!!filterStrings && filterStrings?.length !== 0) {
@ -186,7 +187,7 @@ export class WorkbasketDistributionTargetsListComponent
return true; return true;
} }
this.distributionTargets = this.distributionTargets?.filter((target) => { this.distributionTargets = this.distributionTargetsClone?.filter((target) => {
let matches = true; let matches = true;
matches = matches && filterExact(target, this.filter.name, 'name'); matches = matches && filterExact(target, this.filter.name, 'name');
matches = matches && filterExact(target, this.filter.key, 'key'); matches = matches && filterExact(target, this.filter.key, 'key');

View File

@ -3,14 +3,12 @@ import { forkJoin, Observable, Subject } from 'rxjs';
import { Workbasket } from 'app/shared/models/workbasket'; import { Workbasket } from 'app/shared/models/workbasket';
import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; 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 { filter, take, takeUntil } from 'rxjs/operators';
import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { import {
FetchAvailableDistributionTargets, FetchAvailableDistributionTargets,
FetchWorkbasketDistributionTargets, FetchWorkbasketDistributionTargets,
SaveNewWorkbasket,
UpdateWorkbasket,
UpdateWorkbasketDistributionTargets UpdateWorkbasketDistributionTargets
} from '../../../shared/store/workbasket-store/workbasket.actions'; } from '../../../shared/store/workbasket-store/workbasket.actions';
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors'; import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';

View File

@ -237,7 +237,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
tap((domain) => { tap((domain) => {
this.location.go(this.location.path().replace(/(workbaskets).*/g, 'workbaskets/(detail:new-workbasket)')); 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)); ctx.dispatch(new FetchAvailableDistributionTargets(true));
} }
@ -389,7 +389,8 @@ export class WorkbasketState implements NgxsAfterBootstrap {
this.notificationService.showSuccess('WORKBASKET_DISTRIBUTION_TARGET_SAVE', { this.notificationService.showSuccess('WORKBASKET_DISTRIBUTION_TARGET_SAVE', {
workbasketName: ctx.getState().selectedWorkbasket.name workbasketName: ctx.getState().selectedWorkbasket.name
}); });
ctx.dispatch(new FetchAvailableDistributionTargets(true));
ctx.dispatch(new FetchWorkbasketDistributionTargets(true));
return of(null); return of(null);
}, },
error: () => { error: () => {
@ -404,12 +405,13 @@ export class WorkbasketState implements NgxsAfterBootstrap {
ctx: StateContext<WorkbasketStateModel>, ctx: StateContext<WorkbasketStateModel>,
action: FetchWorkbasketDistributionTargets action: FetchWorkbasketDistributionTargets
): Observable<any> { ): Observable<any> {
const { selectedWorkbasket, distributionTargetsPage, workbasketDistributionTargets } = ctx.getState(); const { selectedWorkbasket, distributionTargetsPage, workbasketDistributionTargets, availableDistributionTargets } =
ctx.getState();
const { filterParameter, sortParameter, refetchAll } = action; const { filterParameter, sortParameter, refetchAll } = action;
const nextDistributionTargetsPage = refetchAll ? 1 : distributionTargetsPage + 1; const nextDistributionTargetsPage = refetchAll ? 1 : distributionTargetsPage + 1;
return this.workbasketService return this.workbasketService
.getWorkBasketsDistributionTargets( .getWorkBasketsDistributionTargets(
selectedWorkbasket._links.distributionTargets.href, selectedWorkbasket._links?.distributionTargets.href,
filterParameter, filterParameter,
sortParameter, sortParameter,
new WorkbasketQueryPagingParameter(nextDistributionTargetsPage) new WorkbasketQueryPagingParameter(nextDistributionTargetsPage)
@ -422,8 +424,14 @@ export class WorkbasketState implements NgxsAfterBootstrap {
const idArrayNoDupe = [...new Set(completeArray.map((wb) => wb.workbasketId))]; const idArrayNoDupe = [...new Set(completeArray.map((wb) => wb.workbasketId))];
wbt.distributionTargets = idArrayNoDupe.map((id) => completeArray.find((wb) => wb.workbasketId === id)); 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({ ctx.patchState({
workbasketDistributionTargets: wbt, workbasketDistributionTargets: wbt,
availableDistributionTargets: availableTargets,
distributionTargetsPage: nextDistributionTargetsPage distributionTargetsPage: nextDistributionTargetsPage
}); });
}) })