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'));
});
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');
});
});

View File

@ -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) {

View File

@ -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');

View File

@ -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';

View File

@ -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<WorkbasketStateModel>,
action: FetchWorkbasketDistributionTargets
): Observable<any> {
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
});
})