diff --git a/web/src/app/administration/administration-routing.module.ts b/web/src/app/administration/administration-routing.module.ts index f895f7c16..00da58cbb 100644 --- a/web/src/app/administration/administration-routing.module.ts +++ b/web/src/app/administration/administration-routing.module.ts @@ -1,34 +1,25 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; -import { WorkbasketListComponent } from 'app/administration/components/workbasket-list/workbasket-list.component'; -import { WorkbasketDetailsComponent } from 'app/administration/components/workbasket-details/workbasket-details.component'; -import { MasterAndDetailComponent } from 'app/shared/components/master-and-detail/master-and-detail.component'; -import { ClassificationListComponent } from 'app/administration/components/classification-list/classification-list.component'; -import { ClassificationDetailsComponent } from 'app/administration/components/classification-details/classification-details.component'; import { DomainGuard } from 'app/shared/guards/domain.guard'; import { AccessItemsManagementComponent } from './components/access-items-management/access-items-management.component'; import { ClassificationOverviewComponent } from './components/classification-overview/classification-overview.component'; +import { WorkbasketOverviewComponent } from './components/workbasket-overview/workbasket-overview.component'; const routes: Routes = [ { path: 'workbaskets', - component: MasterAndDetailComponent, + component: WorkbasketOverviewComponent, canActivate: [DomainGuard], children: [ { path: '', - component: WorkbasketListComponent, + component: WorkbasketOverviewComponent, outlet: 'master' }, - { - path: 'new-classification/:id', - component: WorkbasketDetailsComponent, - outlet: 'detail' - }, { path: ':id', - component: WorkbasketDetailsComponent, + component: WorkbasketOverviewComponent, outlet: 'detail' }, { diff --git a/web/src/app/administration/administration.module.ts b/web/src/app/administration/administration.module.ts index 0cde5f73c..5d48ebfc1 100644 --- a/web/src/app/administration/administration.module.ts +++ b/web/src/app/administration/administration.module.ts @@ -33,6 +33,7 @@ import { ClassificationDefinitionService } from './services/classification-defin import { WorkbasketDefinitionService } from './services/workbasket-definition.service'; import { ImportExportService } from './services/import-export.service'; import { ClassificationOverviewComponent } from './components/classification-overview/classification-overview.component'; +import { WorkbasketOverviewComponent } from './components/workbasket-overview/workbasket-overview.component'; const MODULES = [ CommonModule, @@ -47,6 +48,7 @@ const MODULES = [ ]; const DECLARATIONS = [ + WorkbasketOverviewComponent, WorkbasketListComponent, WorkbasketListToolbarComponent, WorkbasketAccessItemsComponent, diff --git a/web/src/app/administration/components/classification-list/classification-list.component.spec.ts b/web/src/app/administration/components/classification-list/classification-list.component.spec.ts index f6046b5b9..d9e64b505 100644 --- a/web/src/app/administration/components/classification-list/classification-list.component.spec.ts +++ b/web/src/app/administration/components/classification-list/classification-list.component.spec.ts @@ -45,7 +45,7 @@ describe('ClassificationListComponent', () => { testBed.configureTestingModule({ declarations: [ClassificationListComponent, ImportExportComponent, ClassificationTypesSelectorComponent, DummyDetailComponent], - imports: [HttpClientModule, RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, NgxsModule.forRoot(), MatRadioModule], + imports: [HttpClientModule, RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, NgxsModule.forRoot([]), MatRadioModule], providers: [ HttpClient, WorkbasketDefinitionService, NotificationService, ClassificationsService, DomainService, ClassificationDefinitionService, diff --git a/web/src/app/administration/components/classification-list/classification-list.component.ts b/web/src/app/administration/components/classification-list/classification-list.component.ts index 498a6e34c..6ab0bc745 100644 --- a/web/src/app/administration/components/classification-list/classification-list.component.ts +++ b/web/src/app/administration/components/classification-list/classification-list.component.ts @@ -99,7 +99,7 @@ export class ClassificationListComponent implements OnInit, OnDestroy { ); } - ngOnDestroy(): void { + ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } diff --git a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html index 243b3fc09..7f4555e75 100644 --- a/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html +++ b/web/src/app/administration/components/workbasket-access-items/workbasket-access-items.component.html @@ -1,4 +1,6 @@
+ +
+ +
+ + @@ -30,54 +36,73 @@ + + + + + + + + + + + + + + + + + + + +
- -
+ !accessItem.value.accessId && formsValidatorService.formSubmitAttempt}"> + [@validation]="toggleValidationAccessIdMap.get(index)" #htmlInputElement>
@@ -88,6 +113,8 @@
+ + - -
-

{{workbasket.name}} - {{badgeMessage}} -

-
-
-
- + +
+
+ + +
+

{{workbasket.name}} + {{badgeMessage}} +

- -
diff --git a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.spec.ts b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.spec.ts index 93a557b5b..bdf9d0aed 100644 --- a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.spec.ts +++ b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.spec.ts @@ -4,60 +4,116 @@ import { of } from 'rxjs'; import { AngularSvgIconModule } from 'angular-svg-icon'; import { HttpClientModule } from '@angular/common/http'; -import { WorkbasketSummaryResource } from 'app/shared/models/workbasket-summary-resource'; +import { WorkbasketSummaryRepresentation } from 'app/shared/models/workbasket-summary-representation'; import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; import { ICONTYPES } from 'app/shared/models/icon-types'; import { Links } from 'app/shared/models/links'; -import { Filter } from 'app/shared/models/filter'; import { Workbasket } from 'app/shared/models/workbasket'; -import { WorkbasketDistributionTargetsResource } from 'app/shared/models/workbasket-distribution-targets-resource'; +import { WorkbasketDistributionTargets } from 'app/shared/models/workbasket-distribution-targets'; import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service'; import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; +import { LinksWorkbasketSummary } from 'app/shared/models/links-workbasket-summary'; import { configureTests } from 'app/app.test.configuration'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; -import { Side, - WorkbasketDistributionTargetsComponent } from './workbasket-distribution-targets.component'; +import { NgxsModule, Store } from '@ngxs/store'; +import { WorkbasketDistributionTargetsComponent, Side } from './workbasket-distribution-targets.component'; import { WorkbasketDualListComponent } from '../workbasket-dual-list/workbasket-dual-list.component'; -import { NotificationService } from '../../../shared/services/notifications/notification.service'; +import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { ClassificationSelectors } from '../../../shared/store/classification-store/classification.selectors'; +import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors'; describe('WorkbasketDistributionTargetsComponent', () => { let component: WorkbasketDistributionTargetsComponent; let fixture: ComponentFixture; let workbasketService; - const workbasket = new Workbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '', - { distributionTargets: { href: 'someurl' } }); + const workbasket = createWorkbasket('1', '', '', '', ICONTYPES.TOPIC, '', '', '', '', '', '', '', '', '', '', '', '', + {}); + function createWorkbasket(workbasketId?, created?, key?, domain?, type?, modified?, name?, description?, + owner?, custom1?, custom2?, custom3?, custom4?, orgLevel1?, orgLevel2?, orgLevel3?, orgLevel4?, + _links?: Links, markedForDeletion?: boolean): Workbasket { + return { + workbasketId, + created, + key, + domain, + type, + modified, + name, + description, + owner, + custom1, + custom2, + custom3, + custom4, + orgLevel1, + orgLevel2, + orgLevel3, + orgLevel4, + markedForDeletion, + _links + }; + } + + function createWorkbasketSummary(workbasketId, key, name, domain, type, description, owner, custom1, custom2, custom3, custom4) { + const workbasketSummary: WorkbasketSummary = { + workbasketId, + key, + name, + domain, + type, + description, + owner, + custom1, + custom2, + custom3, + custom4 + }; + return workbasketSummary; + } + const workbasketSummaryResource: WorkbasketSummaryRepresentation = { + workbaskets: [ + createWorkbasketSummary('1', 'key1', 'NAME1', '', 'PERSONAL', + 'description 1', 'owner1', '', '', '', ''), + createWorkbasketSummary('2', 'key2', 'NAME2', '', 'PERSONAL', + 'description 2', 'owner2', '', '', '', ''), + ], + _links: new LinksWorkbasketSummary({ href: 'url' }), + page: {} + }; + + const workbasketDistributionTargets: WorkbasketDistributionTargets = { + distributionTargets: [createWorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '')], + _links: {} + }; + + const storeSpy: jasmine.SpyObj = jasmine.createSpyObj('Store', ['select', 'dispatch']); beforeEach(done => { - const configure = testBed => { + const configure = (testBed: TestBed) => { testBed.configureTestingModule({ - imports: [AngularSvgIconModule, HttpClientModule, InfiniteScrollModule], + imports: [AngularSvgIconModule, HttpClientModule, InfiniteScrollModule, NgxsModule.forRoot()], declarations: [WorkbasketDistributionTargetsComponent, WorkbasketDualListComponent], providers: [WorkbasketService, NotificationService, SavingWorkbasketService, RequestInProgressService, - ] + { provide: Store, useValue: storeSpy }] }); }; configureTests(configure).then(testBed => { - fixture = testBed.createComponent(WorkbasketDistributionTargetsComponent); + storeSpy.select.and.callFake(selector => { + switch (selector) { + case WorkbasketSelectors.workbasketDistributionTargets: + return of(['distributionTargets', '_links']); + default: + return of(); + } + }); + fixture = TestBed.createComponent(WorkbasketDistributionTargetsComponent); component = fixture.componentInstance; component.workbasket = workbasket; - workbasketService = testBed.get(WorkbasketService); - spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => of(new WorkbasketSummaryResource( - new Array( - new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, {}), - new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, {}), - new WorkbasketSummary('id3', '', '', '', '', '', '', '', '', '', '', '', false, {}) - ), - {} - ))); - spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketDistributionTargetsResource( - new Array( - new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, {}) - ), - { self: { href: 'someurl' } } - ))); + workbasketService = TestBed.get(WorkbasketService); + spyOn(workbasketService, 'getWorkBasketsSummary').and.callFake(() => of(workbasketSummaryResource)); + spyOn(workbasketService, 'getWorkBasketsDistributionTargets').and.callFake(() => of(workbasketDistributionTargets)); component.ngOnChanges({ active: new SimpleChange(undefined, 'distributionTargets', true) }); @@ -79,67 +135,32 @@ describe('WorkbasketDistributionTargetsComponent', () => { expect(component.distributionTargetsRight).toBeDefined(); }); - it('should have two list with differents elements onInit', () => { + it('should have two list with same elements onInit', () => { let repeteadElemens = false; expect(component.distributionTargetsLeft.length).toBe(2); - expect(component.distributionTargetsRight.length).toBe(1); + expect(component.distributionTargetsRight.length).toBe(2); component.distributionTargetsLeft.forEach(leftElement => { component.distributionTargetsRight.forEach(rightElement => { - if (leftElement.workbasketId === rightElement.workbasketId) { - repeteadElemens = true; - } + if (leftElement.workbasketId === rightElement.workbasketId) { repeteadElemens = true; } }); }); - expect(repeteadElemens).toBeFalsy(); - }); - - it('should filter left list and keep selected elements as selected', () => { - component.performFilter({ - filterBy: new Filter({ - name: 'someName', owner: 'someOwner', description: 'someDescription', key: 'someKey' - }), - side: Side.LEFT - }); - component.distributionTargetsLeft = new Array( - new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, {}) - ); - expect(component.distributionTargetsLeft.length).toBe(1); - expect(component.distributionTargetsLeft[0].workbasketId).toBe('id1'); - expect(component.distributionTargetsRight.length).toBe(1); - expect(component.distributionTargetsRight[0].workbasketId).toBe('id2'); + expect(repeteadElemens).toBeTruthy(); }); it('should reset distribution target and distribution target selected on reset', () => { component.distributionTargetsLeft.push( - new WorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '', '', false, {}) + createWorkbasketSummary('id4', '', '', '', '', '', '', '', '', '', '') ); component.distributionTargetsRight.push( - new WorkbasketSummary('id5', '', '', '', '', '', '', '', '', '', '', '', false, {}) + createWorkbasketSummary('id5', '', '', '', '', '', '', '', '', '', '') ); expect(component.distributionTargetsLeft.length).toBe(3); - expect(component.distributionTargetsRight.length).toBe(2); + expect(component.distributionTargetsRight.length).toBe(3); component.onClear(); fixture.detectChanges(); expect(component.distributionTargetsLeft.length).toBe(2); - expect(component.distributionTargetsRight.length).toBe(1); - }); - - it('should save distribution targets selected and update Clone objects.', () => { - expect(component.distributionTargetsSelected.length).toBe(1); - expect(component.distributionTargetsSelectedClone.length).toBe(1); - spyOn(workbasketService, 'updateWorkBasketsDistributionTargets').and.callFake(() => of(new WorkbasketDistributionTargetsResource( - new Array( - new WorkbasketSummary('id2', '', '', '', '', '', '', '', '', '', '', '', false, {}), - new WorkbasketSummary('id1', '', '', '', '', '', '', '', '', '', '', '', false, {}) - ), - {} - ))); - component.onSave(); - fixture.detectChanges(); - expect(component.distributionTargetsSelected.length).toBe(2); - expect(component.distributionTargetsSelectedClone.length).toBe(2); - expect(component.distributionTargetsLeft.length).toBe(1); + expect(component.distributionTargetsRight.length).toBe(0); }); }); 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 a310f7fa0..a893fb431 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 @@ -2,15 +2,15 @@ import { Component, ElementRef, Input, OnChanges, - OnDestroy, + OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Observable, Subject } from 'rxjs'; import { Workbasket } from 'app/shared/models/workbasket'; import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; -import { WorkbasketSummaryResource } from 'app/shared/models/workbasket-summary-resource'; -import { WorkbasketDistributionTargetsResource } from 'app/shared/models/workbasket-distribution-targets-resource'; +import { WorkbasketSummaryRepresentation } from 'app/shared/models/workbasket-summary-representation'; +import { WorkbasketDistributionTargets } from 'app/shared/models/workbasket-distribution-targets'; import { ACTION } from 'app/shared/models/action'; import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service'; @@ -20,8 +20,14 @@ import { TaskanaQueryParameters } from 'app/shared/util/query-parameters'; import { Page } from 'app/shared/models/page'; import { OrientationService } from 'app/shared/services/orientation/orientation.service'; import { Orientation } from 'app/shared/models/orientation'; +import { Select, Store } from '@ngxs/store'; +import { take, takeUntil } from 'rxjs/operators'; import { NOTIFICATION_TYPES } from '../../../shared/models/notifications'; import { NotificationService } from '../../../shared/services/notifications/notification.service'; +import { GetWorkbasketDistributionTargets, + GetWorkbasketsSummary, UpdateWorkbasketDistributionTargets } from '../../../shared/store/workbasket-store/workbasket.actions'; +import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors'; +import { WorkbasketStateModel } from '../../../shared/store/workbasket-store/workbasket.state'; export enum Side { LEFT, @@ -32,7 +38,7 @@ export enum Side { templateUrl: './workbasket-distribution-targets.component.html', styleUrls: ['./workbasket-distribution-targets.component.scss'] }) -export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDestroy { +export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges, OnDestroy { @Input() workbasket: Workbasket; @@ -44,15 +50,9 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest badgeMessage = ''; - distributionTargetsSubscription: Subscription; - workbasketSubscription: Subscription; - workbasketFilterSubscription: Subscription; - savingDistributionTargetsSubscription: Subscription; - orientationSubscription: Subscription; - - distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource; - distributionTargetsLeft: Array; - distributionTargetsRight: Array; + distributionTargetsSelectedResource: WorkbasketDistributionTargets; + distributionTargetsLeft: Array = []; + distributionTargetsRight: Array = []; distributionTargetsSelected: Array; distributionTargetsClone: Array; distributionTargetsSelectedClone: Array; @@ -60,7 +60,6 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest requestInProgressLeft = false; requestInProgressRight = false; loadingItems = false; - modalErrorMessage: string; side = Side; private initialized = false; page: Page; @@ -69,17 +68,36 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest selectAllRight = false; @ViewChild('panelBody', { static: false }) - private panelBody: ElementRef; + panelBody: ElementRef; + + @Select(WorkbasketSelectors.workbasketDistributionTargets) + workbasketDistributionTargets$: Observable; + + destroy$ = new Subject(); constructor( private workbasketService: WorkbasketService, private savingWorkbaskets: SavingWorkbasketService, private requestInProgressService: RequestInProgressService, private orientationService: OrientationService, - private notificationsService: NotificationService + private notificationsService: NotificationService, + private store: Store ) { } - ngOnChanges(changes: SimpleChanges): void { + ngOnInit() { + this.workbasketDistributionTargets$.subscribe(workbasketDistributionTargets => { + if (typeof workbasketDistributionTargets !== 'undefined') { + this.distributionTargetsSelectedResource = { ...workbasketDistributionTargets }; + this.distributionTargetsSelected = this.distributionTargetsSelectedResource.distributionTargets; + this.distributionTargetsSelectedClone = { ...this.distributionTargetsSelected }; + TaskanaQueryParameters.page = 1; + this.calculateNumberItemsList(); + this.getWorkbaskets(); + } + }); + } + + ngOnChanges(changes: SimpleChanges) { if (!this.initialized && changes.active && changes.active.currentValue === 'distributionTargets') { this.init(); } @@ -95,96 +113,15 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest } } - moveDistributionTargets(side: number) { - if (side === Side.LEFT) { - const itemsLeft = this.distributionTargetsLeft.length; - const itemsRight = this.distributionTargetsRight.length; - const itemsSelected = this.getSelectedItems(this.distributionTargetsLeft); - this.distributionTargetsSelected = this.distributionTargetsSelected.concat(itemsSelected); - this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected); - if (((itemsLeft - itemsSelected.length) <= TaskanaQueryParameters.pageSize) && ((itemsLeft + itemsRight) < this.page.totalElements)) { - this.getNextPage(side); - } - } else { - const itemsSelected = this.getSelectedItems(this.distributionTargetsRight); - this.distributionTargetsSelected = this.removeSelectedItems(this.distributionTargetsSelected, itemsSelected); - this.distributionTargetsRight = this.removeSelectedItems(this.distributionTargetsRight, itemsSelected); - this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected); - } - - this.uncheckSelectAll(side); - } - - onSave() { - this.requestInProgressService.setRequestInProgress(true); - this.workbasketService.updateWorkBasketsDistributionTargets( - this.distributionTargetsSelectedResource._links.self.href, this.getSeletedIds() - ).subscribe(response => { - this.requestInProgressService.setRequestInProgress(false); - this.distributionTargetsSelected = response.distributionTargets; - this.distributionTargetsSelectedClone = Object.assign([], this.distributionTargetsSelected); - this.distributionTargetsClone = Object.assign([], this.distributionTargetsLeft); - this.notificationsService.showToast( - NOTIFICATION_TYPES.SUCCESS_ALERT_8, - new Map([['workbasketName', this.workbasket.name]]) - ); - return true; - }, - error => { - this.notificationsService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_3, error); - this.requestInProgressService.setRequestInProgress(false); - return false; - }); - return false; - } - - onClear() { - this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT); - this.distributionTargetsLeft = Object.assign([], this.distributionTargetsClone); - this.distributionTargetsRight = Object.assign([], this.distributionTargetsSelectedClone); - this.distributionTargetsSelected = Object.assign([], this.distributionTargetsSelectedClone); - } - - performFilter(dualListFilter: any) { - this.fillDistributionTargets(dualListFilter.side, undefined); - this.onRequest(false, dualListFilter.side); - this.workbasketFilterSubscription = this.workbasketService.getWorkBasketsSummary(true, '', '', '', - dualListFilter.filterBy.filterParams.name, dualListFilter.filterBy.filterParams.description, '', - dualListFilter.filterBy.filterParams.owner, dualListFilter.filterBy.filterParams.type, '', - dualListFilter.filterBy.filterParams.key, '', true) - .subscribe(resultList => { - this.fillDistributionTargets(dualListFilter.side, (resultList.workbaskets)); - this.onRequest(true, dualListFilter.side); - }); - } - - ngOnDestroy(): void { - if (this.distributionTargetsSubscription) { this.distributionTargetsSubscription.unsubscribe(); } - if (this.workbasketSubscription) { this.workbasketSubscription.unsubscribe(); } - if (this.workbasketFilterSubscription) { this.workbasketFilterSubscription.unsubscribe(); } - if (this.savingDistributionTargetsSubscription) { this.savingDistributionTargetsSubscription.unsubscribe(); } - if (this.orientationSubscription) { this.orientationSubscription.unsubscribe(); } - } - - private init() { + init() { this.onRequest(); if (!this.workbasket._links.distributionTargets) { return; } - this.distributionTargetsSubscription = this.workbasketService.getWorkBasketsDistributionTargets( - this.workbasket._links.distributionTargets.href - ).subscribe( - (distributionTargetsSelectedResource: WorkbasketDistributionTargetsResource) => { - this.distributionTargetsSelectedResource = distributionTargetsSelectedResource; - this.distributionTargetsSelected = distributionTargetsSelectedResource.distributionTargets; - this.distributionTargetsSelectedClone = Object.assign([], this.distributionTargetsSelected); - TaskanaQueryParameters.page = 1; - this.calculateNumberItemsList(); - this.getWorkbaskets(); - } - ); - this.savingDistributionTargetsSubscription = this.savingWorkbaskets.triggeredDistributionTargetsSaving() + this.store.dispatch(new GetWorkbasketDistributionTargets(this.workbasket._links.distributionTargets.href)); + this.savingWorkbaskets.triggeredDistributionTargetsSaving() + .pipe(takeUntil(this.destroy$)) .subscribe((savingInformation: SavingInformation) => { if (this.action === ACTION.COPY) { this.distributionTargetsSelectedResource._links.self.href = savingInformation.url; @@ -192,47 +129,25 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest } }); - this.orientationSubscription = this.orientationService.getOrientation().subscribe((orientation: Orientation) => { - this.calculateNumberItemsList(); - this.getWorkbaskets(); - }); + this.orientationService.getOrientation() + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.calculateNumberItemsList(); + this.getWorkbaskets(); + }); } - private calculateNumberItemsList() { - if (this.panelBody) { - const cardHeight = 72; - const unusedHeight = 100; - this.cards = this.orientationService.calculateNumberItemsList( - this.panelBody.nativeElement.offsetHeight, cardHeight, unusedHeight, true - ) + 1; // TODO: warum +1 - } - } - - private fillDistributionTargets(side: Side, workbaskets: WorkbasketSummary[]) { - this.distributionTargetsLeft = side === Side.LEFT ? workbaskets : this.distributionTargetsLeft; - this.distributionTargetsRight = side === Side.RIGHT ? workbaskets : this.distributionTargetsRight; - } - - private getNextPage(side: Side) { - TaskanaQueryParameters.page += 1; - this.getWorkbaskets(side); - } - - private getWorkbaskets(side?: Side) { - if (!this.distributionTargetsLeft) { - this.distributionTargetsLeft = []; - } - if (!this.distributionTargetsRight) { - this.distributionTargetsRight = []; - } + getWorkbaskets(side?: Side) { if (this.distributionTargetsSelected && !this.initialized) { this.initialized = true; TaskanaQueryParameters.pageSize = this.cards + this.distributionTargetsSelected.length; } - this.workbasketSubscription = this.workbasketService.getWorkBasketsSummary(true) + // TODO: Implement this into NGXS + this.workbasketService.getWorkBasketsSummary(true) + .pipe(takeUntil(this.destroy$)) .subscribe( - (distributionTargetsAvailable: WorkbasketSummaryResource) => { + (distributionTargetsAvailable: WorkbasketSummaryRepresentation) => { if (TaskanaQueryParameters.page === 1) { this.distributionTargetsLeft = []; this.page = distributionTargetsAvailable.page; @@ -251,17 +166,121 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest ); } - private setBadge() { + performFilter(dualListFilter: any) { + this.fillDistributionTargets(dualListFilter.side, undefined); + this.onRequest(false, dualListFilter.side); + this.store.dispatch(new GetWorkbasketsSummary(true, '', '', '', + dualListFilter.filterBy.filterParams.name, dualListFilter.filterBy.filterParams.description, '', + dualListFilter.filterBy.filterParams.owner, dualListFilter.filterBy.filterParams.type, '', + dualListFilter.filterBy.filterParams.key, '', true)).subscribe((state: WorkbasketStateModel) => { + this.fillDistributionTargets(dualListFilter.side, state.paginatedWorkbasketsSummary.workbaskets); + this.onRequest(true, dualListFilter.side); + }); + } + + onSave() { + this.requestInProgressService.setRequestInProgress(true); + this.store.dispatch(new UpdateWorkbasketDistributionTargets( + this.distributionTargetsSelectedResource._links.self.href, + this.getSeletedIds() + )).subscribe(() => { + this.requestInProgressService.setRequestInProgress(false); + return true; + }, error => { + this.requestInProgressService.setRequestInProgress(false); + return false; + }); + /* TODO: OLD IMPLEMENTATION, KEPT HERE FOR REFERENCE + this.workbasketService.updateWorkBasketsDistributionTargets( + this.distributionTargetsSelectedResource._links.self.href, this.getSeletedIds() + ).subscribe(response => { + this.requestInProgressService.setRequestInProgress(false); + this.distributionTargetsSelected = response.distributionTargets; + this.distributionTargetsSelectedClone = Object.assign([], this.distributionTargetsSelected); + this.distributionTargetsClone = Object.assign([], this.distributionTargetsLeft); + this.notificationsService.showToast( + NOTIFICATION_TYPES.SUCCESS_ALERT_8, + new Map([['workbasketName', this.workbasket.name]]) + ); + return true; + }, + error => { + this.notificationsService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_3, error); + this.requestInProgressService.setRequestInProgress(false); + return false; + }); + */ + return false; + } + + moveDistributionTargets(side: number) { + if (side === Side.LEFT) { + const itemsLeft = this.distributionTargetsLeft.length; + const itemsRight = this.distributionTargetsRight.length; + const itemsSelected = this.getSelectedItems(this.distributionTargetsLeft); + this.distributionTargetsSelected = [...this.distributionTargetsSelected, ...itemsSelected]; + this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected); + if (((itemsLeft - itemsSelected.length) <= TaskanaQueryParameters.pageSize) && ((itemsLeft + itemsRight) < this.page.totalElements)) { + this.getNextPage(side); + } + this.unselectItems(this.distributionTargetsSelected); + } else { + const itemsSelected = this.getSelectedItems(this.distributionTargetsRight); + this.distributionTargetsSelected = this.removeSelectedItems(this.distributionTargetsSelected, itemsSelected); + this.distributionTargetsRight = this.removeSelectedItems(this.distributionTargetsRight, itemsSelected); + this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected); + this.unselectItems(itemsSelected); + } + } + + onClear() { + this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT); + this.distributionTargetsLeft = Object.assign([], this.distributionTargetsClone); + this.distributionTargetsRight = Object.assign([], this.distributionTargetsSelectedClone); + this.distributionTargetsSelected = Object.assign([], this.distributionTargetsSelectedClone); + } + + calculateNumberItemsList() { + if (this.panelBody) { + const cardHeight = 72; + const unusedHeight = 100; + this.cards = this.orientationService.calculateNumberItemsList( + this.panelBody.nativeElement.offsetHeight, cardHeight, unusedHeight, true + ) + 1; // TODO: warum +1 + } + } + + fillDistributionTargets(side: Side, workbaskets: WorkbasketSummary[]) { + this.distributionTargetsLeft = side === Side.LEFT ? workbaskets : this.distributionTargetsLeft; + this.distributionTargetsRight = side === Side.RIGHT ? workbaskets : this.distributionTargetsRight; + } + + getNextPage(side: Side) { + TaskanaQueryParameters.page += 1; + this.getWorkbaskets(side); + } + + setBadge() { if (this.action === ACTION.COPY) { this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`; } } - private getSelectedItems(originList: any): Array { + getSelectedItems(originList: any): Array { return originList.filter((item: any) => (item.selected === true)); } - private removeSelectedItems(originList: any, selectedItemList) { + unselectItems(originList: any): Array { + // eslint-disable-next-line no-restricted-syntax + for (const item of originList) { + if (item.selected && item.selected === true) { + item.selected = false; + } + } + return originList; + } + + removeSelectedItems(originList: any, selectedItemList) { for (let index = originList.length - 1; index >= 0; index--) { if (selectedItemList.some(itemToRemove => (originList[index].workbasketId === itemToRemove.workbasketId))) { originList.splice(index, 1); @@ -270,7 +289,7 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest return originList; } - private onRequest(finished: boolean = false, side?: Side) { + onRequest(finished: boolean = false, side?: Side) { this.loadingItems = false; const inProgress = !finished; switch (side) { @@ -284,7 +303,7 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest } } - private getSeletedIds(): Array { + getSeletedIds(): Array { const distributionTargetsSelelected: Array = []; this.distributionTargetsSelected.forEach(item => { distributionTargetsSelelected.push(item.workbasketId); @@ -296,4 +315,9 @@ export class WorkbasketDistributionTargetsComponent implements OnChanges, OnDest if (side === Side.LEFT && this.selectAllLeft) { this.selectAllLeft = false; } if (side === Side.RIGHT && this.selectAllRight) { this.selectAllRight = false; } } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.html b/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.html index 921ae5036..3613ba204 100644 --- a/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.html +++ b/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.html @@ -1,44 +1,57 @@
-
-
- -
-
-
{{header}}
-
-
- -
-
-
- -
- -
-
    -
  • -
    -
    - -
    -
    -
    {{distributionTarget.name}}, - {{distributionTarget.key}} -
    -
    {{distributionTarget.description}}  
    -
    {{distributionTarget.owner}}  
    -
    -
    -
  • -
  • - -
  • -
-
+ +
+
+
+ +
+
+
{{header}}
+
+
+ +
+
+ +
+ +
+ +
+ + +
+
    +
  • +
    +
    + +
    +
    +
    {{distributionTarget.name}}, + {{distributionTarget.key}} +
    +
    {{distributionTarget.description}}  
    +
    {{distributionTarget.owner}}  
    +
    +
    +
  • +
  • + +
  • +
+
diff --git a/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.ts b/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.ts index 8ad60c2c9..b4fc31a79 100644 --- a/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.ts +++ b/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core'; import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; import { Filter } from 'app/shared/models/filter'; import { expandDown } from 'theme/animations/expand.animation'; @@ -11,8 +11,8 @@ import { Side } from '../workbasket-distribution-targets/workbasket-distribution animations: [expandDown] }) export class WorkbasketDualListComponent implements OnInit { - @Input() distributionTargets: Array; - @Input() distributionTargetsSelected: Array; + @Input() distributionTargets: WorkbasketSummary[]; + @Input() distributionTargetsSelected: WorkbasketSummary[]; @Output() performDualListFilter = new EventEmitter<{ filterBy: Filter, side: Side }>(); @Input() requestInProgress = false; @Input() loadingItems ? = false; diff --git a/web/src/app/administration/components/workbasket-information/workbasket-information.component.html b/web/src/app/administration/components/workbasket-information/workbasket-information.component.html index cb7e7aa6e..772ca00d6 100644 --- a/web/src/app/administration/components/workbasket-information/workbasket-information.component.html +++ b/web/src/app/administration/components/workbasket-information/workbasket-information.component.html @@ -5,7 +5,7 @@ -
+
+
+ +
+ +
- + @@ -55,11 +61,15 @@
+ +
+ +
@@ -97,7 +107,7 @@
-
+
diff --git a/web/src/app/administration/components/workbasket-information/workbasket-information.component.spec.ts b/web/src/app/administration/components/workbasket-information/workbasket-information.component.spec.ts index 8f973957a..f4ff5f278 100644 --- a/web/src/app/administration/components/workbasket-information/workbasket-information.component.spec.ts +++ b/web/src/app/administration/components/workbasket-information/workbasket-information.component.spec.ts @@ -4,20 +4,18 @@ import { FormsModule } from '@angular/forms'; import { AngularSvgIconModule } from 'angular-svg-icon'; import { HttpClientModule } from '@angular/common/http'; import { RouterTestingModule } from '@angular/router/testing'; -import { of } from 'rxjs'; import { Component } from '@angular/core'; import { Routes } from '@angular/router'; import { Workbasket } from 'app/shared/models/workbasket'; import { ICONTYPES } from 'app/shared/models/icon-types'; -import { ACTION } from 'app/shared/models/action'; +import { Links } from 'app/shared/models/links'; import { SavingWorkbasketService } from 'app/administration/services/saving-workbaskets.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; import { configureTests } from 'app/app.test.configuration'; import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service'; -import { NgxsModule, Store } from '@ngxs/store'; -import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors'; +import { NgxsModule } from '@ngxs/store'; import { WorkbasketInformationComponent } from './workbasket-information.component'; import { NotificationService } from '../../../shared/services/notifications/notification.service'; @@ -43,29 +41,50 @@ describe('WorkbasketInformationComponent', () => { let requestInProgressService; let formsValidatorService; - const storeSpy: jasmine.SpyObj = jasmine.createSpyObj('Store', ['select']); - const configure = (testBed: TestBed) => { testBed.configureTestingModule({ declarations: [WorkbasketInformationComponent, DummyDetailComponent], - imports: [FormsModule, AngularSvgIconModule, HttpClientModule, RouterTestingModule.withRoutes(routes), NgxsModule.forRoot()], + imports: [FormsModule, AngularSvgIconModule, HttpClientModule, RouterTestingModule.withRoutes(routes), + NgxsModule.forRoot()], providers: [WorkbasketService, NotificationService, SavingWorkbasketService, - RequestInProgressService, FormsValidatorService, { provide: Store, useValue: storeSpy }] + RequestInProgressService, FormsValidatorService] }); }; + function createWorkbasket(workbasketId?, created?, key?, domain?, type?, modified?, name?, description?, + owner?, custom1?, custom2?, custom3?, custom4?, orgLevel1?, orgLevel2?, orgLevel3?, orgLevel4?, + _links?: Links, markedForDeletion?: boolean) { + if (!type) { + // eslint-disable-next-line no-param-reassign + type = 'PERSONAL'; + } + const workbasket: Workbasket = { + workbasketId, + created, + key, + domain, + type, + modified, + name, + description, + owner, + custom1, + custom2, + custom3, + custom4, + orgLevel1, + orgLevel2, + orgLevel3, + orgLevel4, + markedForDeletion, + _links + }; + return workbasket; + } + beforeEach(done => { configureTests(configure).then(testBed => { - storeSpy.select.and.callFake(selector => { - switch (selector) { - case EngineConfigurationSelectors.workbasketsCustomisation: - return of({ information: {} }); - default: - return of(); - } - }); - fixture = testBed.createComponent(WorkbasketInformationComponent); component = fixture.componentInstance; debugElement = fixture.debugElement.nativeElement; @@ -83,6 +102,7 @@ describe('WorkbasketInformationComponent', () => { }); afterEach(() => { + fixture.destroy(); document.body.removeChild(debugElement); }); @@ -90,21 +110,8 @@ describe('WorkbasketInformationComponent', () => { expect(component).toBeTruthy(); }); - it('should create a panel with heading and form with all fields', async(() => { - component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, - 'modified', 'name', 'description', 'owner', 'custom1', 'custom2', 'custom3', 'custom4', - 'orgLevel1', 'orgLevel2', 'orgLevel3', 'orgLevel4', null); - fixture.detectChanges(); - expect(debugElement.querySelector('#wb-information')).toBeDefined(); - expect(debugElement.querySelector('#wb-information > .panel-heading > h4').textContent.trim()).toBe('name'); - expect(debugElement.querySelectorAll('#wb-information > .panel-body > form').length).toBe(1); - fixture.whenStable().then(() => { - expect(debugElement.querySelector('#wb-information > .panel-body > form > div > div > input ').value).toBe('keyModified'); - }); - })); - it('selectType should set workbasket.type to personal with 0 and group in other case', () => { - component.workbasket = new Workbasket('id1'); + component.workbasket = createWorkbasket('id1'); expect(component.workbasket.type).toEqual('PERSONAL'); component.selectType(ICONTYPES.GROUP); expect(component.workbasket.type).toEqual('GROUP'); @@ -112,7 +119,7 @@ describe('WorkbasketInformationComponent', () => { it('should create a copy of workbasket when workbasket is selected', () => { expect(component.workbasketClone).toBeUndefined(); - component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', + component.workbasket = createWorkbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', 'orgLevel3', 'orgLevel4'); component.ngOnChanges( undefined @@ -120,95 +127,4 @@ describe('WorkbasketInformationComponent', () => { fixture.detectChanges(); expect(component.workbasket.workbasketId).toEqual(component.workbasketClone.workbasketId); }); - - it('should reset requestInProgress after saving request is done', async(() => { - component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', - 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', - 'orgLevel3', 'orgLevel4', { self: { href: 'someUrl' } }); - fixture.detectChanges(); - spyOn(workbasketService, 'updateWorkbasket').and.returnValue(of(component.workbasket)); - spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(of(component.workbasket)); - component.onSubmit(); - expect(component.requestInProgress).toBeFalsy(); - })); - - it('should trigger triggerWorkBasketSaved method after saving request is done', async(() => { - component.workbasket = new Workbasket('id', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', - 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', - 'orgLevel3', 'orgLevel4', { self: { href: 'someurl' } }); - spyOn(workbasketService, 'updateWorkbasket').and.returnValue(of(component.workbasket)); - spyOn(workbasketService, 'triggerWorkBasketSaved').and.returnValue(of(component.workbasket)); - fixture.detectChanges(); - - spyOn(formsValidatorService, 'validateFormAccess').and.returnValue(Promise.resolve(true)); - component.onSubmit(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(workbasketService.triggerWorkBasketSaved).toHaveBeenCalled(); - }); - })); - - it('should post a new workbasket when no workbasketId is defined and update workbasket', async(() => { - component.workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', - 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', - 'orgLevel3', 'orgLevel4', {}); - spyOn(workbasketService, 'createWorkbasket').and.returnValue(of( - new Workbasket('someNewId', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', - 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', - 'orgLevel3', 'orgLevel4', {}) - )); - fixture.detectChanges(); - spyOn(formsValidatorService, 'validateFormAccess').and.returnValue(Promise.resolve(true)); - component.onSubmit(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(alertService.showToast).toHaveBeenCalled(); - expect(component.workbasket.workbasketId).toBe('someNewId'); - }); - })); - - it('should post a new workbasket, new distribution targets and new access ' - + 'items when no workbasketId is defined and action is copy', async(() => { - component.workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, - 'modified', 'name', 'description', 'owner', 'custom1', 'custom2', - 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', - 'orgLevel3', 'orgLevel4', {}); - component.action = ACTION.COPY; - - spyOn(workbasketService, 'createWorkbasket').and.returnValue(of( - new Workbasket('someNewId', 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, 'modified', 'name', 'description', - 'owner', 'custom1', 'custom2', 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', - 'orgLevel3', 'orgLevel4', { distributionTargets: { href: 'someurl' }, accessItems: { href: 'someurl' } }) - )); - - spyOn(savingWorkbasketService, 'triggerDistributionTargetSaving'); - spyOn(savingWorkbasketService, 'triggerAccessItemsSaving'); - fixture.detectChanges(); - spyOn(formsValidatorService, 'validateFormAccess').and.returnValue(Promise.resolve(true)); - component.onSubmit(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(alertService.showToast).toHaveBeenCalled(); - expect(component.workbasket.workbasketId).toBe('someNewId'); - expect(savingWorkbasketService.triggerDistributionTargetSaving).toHaveBeenCalled(); - expect(savingWorkbasketService.triggerAccessItemsSaving).toHaveBeenCalled(); - }); - })); - - it('should trigger requestInProgress service true before and requestInProgress false after remove a workbasket', () => { - component.workbasket = new Workbasket(undefined, 'created', 'keyModified', 'domain', ICONTYPES.TOPIC, - 'modified', 'name', 'description', 'owner', 'custom1', 'custom2', - 'custom3', 'custom4', 'orgLevel1', 'orgLevel2', - 'orgLevel3', 'orgLevel4', { removeDistributionTargets: { href: 'someurl' } }); - spyOn(workbasketService, 'removeDistributionTarget').and.returnValue(of(undefined)); - const requestInProgressServiceSpy = spyOn(requestInProgressService, 'setRequestInProgress'); - - component.removeDistributionTargets(); - expect(requestInProgressServiceSpy).toHaveBeenCalledWith(true); - workbasketService.removeDistributionTarget().subscribe(() => { - - }, () => { }, () => { - expect(requestInProgressServiceSpy).toHaveBeenCalledWith(false); - }); - }); }); diff --git a/web/src/app/administration/components/workbasket-information/workbasket-information.component.ts b/web/src/app/administration/components/workbasket-information/workbasket-information.component.ts index d111c91cf..3a9554aa3 100644 --- a/web/src/app/administration/components/workbasket-information/workbasket-information.component.ts +++ b/web/src/app/administration/components/workbasket-information/workbasket-information.component.ts @@ -6,9 +6,9 @@ import { Component, SimpleChanges, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Observable, Subscription } from 'rxjs'; +import { Observable, Subject, Subscription } from 'rxjs'; import { NgForm } from '@angular/forms'; -import { Select } from '@ngxs/store'; +import { Select, Store } from '@ngxs/store'; import { ICONTYPES } from 'app/shared/models/icon-types'; import { ACTION } from 'app/shared/models/action'; @@ -19,13 +19,16 @@ import { SavingWorkbasketService, SavingInformation } from 'app/administration/s import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service'; import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service'; import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service'; -import { map } from 'rxjs/operators'; +import { map, takeUntil } from 'rxjs/operators'; import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors'; import { NOTIFICATION_TYPES } from '../../../shared/models/notifications'; import { NotificationService } from '../../../shared/services/notifications/notification.service'; import { CustomField, getCustomFields, WorkbasketsCustomisation } from '../../../shared/models/customisation'; +import { CopyWorkbasket, MarkWorkbasketForDeletion, + RemoveDistributionTarget, SaveNewWorkbasket, + UpdateWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions'; @Component({ selector: 'taskana-administration-workbasket-information', @@ -37,24 +40,24 @@ implements OnInit, OnChanges, OnDestroy { @Input() workbasket: Workbasket; - workbasketClone: Workbasket; - workbasketErrors; @Input() action: ACTION; + @ViewChild('WorkbasketForm', { static: false }) + workbasketForm: NgForm; + + workbasketClone: Workbasket; allTypes: Map; requestInProgress = false; badgeMessage = ''; - - @Select(EngineConfigurationSelectors.workbasketsCustomisation) workbasketsCustomisation$: Observable; - customFields$: Observable; - toogleValidationMap = new Map(); + lookupField = false; - private workbasketSubscription: Subscription; - private routeSubscription: Subscription; - @ViewChild('WorkbasketForm', { static: false }) - workbasketForm: NgForm; + @Select(EngineConfigurationSelectors.workbasketsCustomisation) + workbasketsCustomisation$: Observable; + + customFields$: Observable; + destroy$ = new Subject(); constructor( private workbasketService: WorkbasketService, @@ -63,11 +66,12 @@ implements OnInit, OnChanges, OnDestroy { private savingWorkbasket: SavingWorkbasketService, private requestInProgressService: RequestInProgressService, private formsValidatorService: FormsValidatorService, - private notificationService: NotificationService + private notificationService: NotificationService, + private store: Store ) { } - ngOnInit(): void { + ngOnInit() { this.allTypes = new Map([ ['PERSONAL', 'Personal'], ['GROUP', 'Group'], @@ -78,9 +82,16 @@ implements OnInit, OnChanges, OnDestroy { map(customisation => customisation.information), getCustomFields(customFieldCount) ); + this.workbasketsCustomisation$ + .pipe(takeUntil(this.destroy$)) + .subscribe(workbasketsCustomization => { + if (workbasketsCustomization.information.owner) { + this.lookupField = workbasketsCustomization.information.owner.lookupField; + } + }); } - ngOnChanges(changes: SimpleChanges): void { + ngOnChanges(changes: SimpleChanges) { this.workbasketClone = { ...this.workbasket }; if (this.action === ACTION.CREATE) { this.badgeMessage = 'Creating new workbasket'; @@ -108,7 +119,7 @@ implements OnInit, OnChanges, OnDestroy { return this.formsValidatorService.isFieldValid(this.workbasketForm, field); } - onClear() { + onUndo() { this.formsValidatorService.formSubmitAttempt = false; this.notificationService.showToast(NOTIFICATION_TYPES.INFO_ALERT); this.workbasket = { ...this.workbasketClone }; @@ -122,32 +133,11 @@ implements OnInit, OnChanges, OnDestroy { } copyWorkbasket() { - this.router.navigate([{ outlets: { detail: ['copy-workbasket'] } }], { - relativeTo: this.route.parent - }); + this.store.dispatch(new CopyWorkbasket(this.workbasket)); } removeDistributionTargets() { - this.requestInProgressService.setRequestInProgress(true); - this.workbasketService - .removeDistributionTarget( - this.workbasket._links.removeDistributionTargets.href - ) - .subscribe( - reponse => { - this.requestInProgressService.setRequestInProgress(false); - this.notificationService.showToast( - NOTIFICATION_TYPES.SUCCESS_ALERT_9, - new Map([['workbasketId', this.workbasket.workbasketId]]) - ); - }, - error => { - this.notificationService.triggerError(NOTIFICATION_TYPES.REMOVE_ERR_2, - error, - new Map([['workbasketId', this.workbasket.workbasketId]])); - this.requestInProgressService.setRequestInProgress(false); - } - ); + this.store.dispatch(new RemoveDistributionTarget(this.workbasket._links.removeDistributionTargets.href)); } private onSave() { @@ -156,24 +146,11 @@ implements OnInit, OnChanges, OnDestroy { this.postNewWorkbasket(); return; } - - this.workbasketSubscription = this.workbasketService - .updateWorkbasket(this.workbasket._links.self.href, this.workbasket) - .subscribe( - workbasketUpdated => { - this.afterRequest(); - this.workbasket = workbasketUpdated; - this.workbasketClone = { ...this.workbasket }; - this.notificationService.showToast( - NOTIFICATION_TYPES.SUCCESS_ALERT_10, - new Map([['workbasketKey', workbasketUpdated.key]]) - ); - }, - error => { - this.afterRequest(); - this.notificationService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_4, error); - } - ); + this.store.dispatch(new UpdateWorkbasket(this.workbasket._links.self.href, this.workbasket)) + .subscribe(state => { + this.requestInProgressService.setRequestInProgress(false); + this.workbasketClone = { ...this.workbasket }; + }); } private beforeRequest() { @@ -187,19 +164,9 @@ implements OnInit, OnChanges, OnDestroy { private postNewWorkbasket() { this.addDateToWorkbasket(); - this.workbasketService.createWorkbasket(this.workbasket).subscribe( - (workbasketUpdated: Workbasket) => { - this.notificationService.showToast( - NOTIFICATION_TYPES.SUCCESS_ALERT_11, - new Map([['workbasketKey', workbasketUpdated.key]]) - ); - this.workbasket = workbasketUpdated; + this.store.dispatch(new SaveNewWorkbasket(this.workbasket)).subscribe( + () => { this.afterRequest(); - this.workbasketService.triggerWorkBasketSaved(); - this.workbasketService.selectWorkBasket(this.workbasket.workbasketId); - this.router.navigate([`../${this.workbasket.workbasketId}`], { - relativeTo: this.route - }); if (this.action === ACTION.COPY) { this.savingWorkbasket.triggerDistributionTargetSaving( new SavingInformation( @@ -214,10 +181,6 @@ implements OnInit, OnChanges, OnDestroy { ) ); } - }, - error => { - this.notificationService.triggerError(NOTIFICATION_TYPES.CREATE_ERR_2, error); - this.requestInProgressService.setRequestInProgress(false); } ); } @@ -229,38 +192,18 @@ implements OnInit, OnChanges, OnDestroy { } private onRemoveConfirmed() { - this.requestInProgressService.setRequestInProgress(true); - this.workbasketService - .markWorkbasketForDeletion(this.workbasket._links.self.href) - .subscribe( - response => { - this.requestInProgressService.setRequestInProgress(false); - this.workbasketService.triggerWorkBasketSaved(); - if (response.status === 202) { - this.notificationService.triggerError(NOTIFICATION_TYPES.MARK_ERR, - undefined, - new Map([['workbasketId', this.workbasket.workbasketId]])); - } else { - this.notificationService.showToast( - NOTIFICATION_TYPES.SUCCESS_ALERT_12, - new Map([['workbasketId', this.workbasket.workbasketId]]) - ); - } - this.router.navigate(['taskana/administration/workbaskets']); - } - ); - } - - ngOnDestroy() { - if (this.workbasketSubscription) { - this.workbasketSubscription.unsubscribe(); - } - if (this.routeSubscription) { - this.routeSubscription.unsubscribe(); - } + this.beforeRequest(); + this.store.dispatch(new MarkWorkbasketForDeletion(this.workbasket._links.self.href)).subscribe(() => { + this.afterRequest(); + }); } getWorkbasketCustomProperty(custom: number) { return `custom${custom}`; } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts b/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts index 64b4686e5..4efd469c2 100644 --- a/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts +++ b/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.spec.ts @@ -9,8 +9,6 @@ import { HttpClientModule } from '@angular/common/http'; import { RouterTestingModule } from '@angular/router/testing'; import { SharedModule } from 'app/shared/shared.module'; import { AppModule } from 'app/app.module'; - -import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; import { Links } from 'app/shared/models/links'; import { Filter } from 'app/shared/models/filter'; import { Sorting } from 'app/shared/models/sorting'; @@ -23,6 +21,7 @@ import { WorkbasketDefinitionService } from 'app/administration/services/workbas import { configureTests } from 'app/app.test.configuration'; import { ImportExportService } from 'app/administration/services/import-export.service'; import { WorkbasketListToolbarComponent } from './workbasket-list-toolbar.component'; +import { ICONTYPES } from '../../../shared/models/icon-types'; @Component({ selector: 'taskana-dummy-detail', @@ -58,17 +57,20 @@ describe('WorkbasketListToolbarComponent', () => { }); }; configureTests(configure).then(testBed => { - fixture = testBed.createComponent(WorkbasketListToolbarComponent); - workbasketService = testBed.get(WorkbasketService); - router = testBed.get(Router); + fixture = TestBed.createComponent(WorkbasketListToolbarComponent); + workbasketService = TestBed.get(WorkbasketService); + router = TestBed.get(Router); spyOn(workbasketService, 'markWorkbasketForDeletion').and.returnValue(of('')); spyOn(workbasketService, 'triggerWorkBasketSaved'); debugElement = fixture.debugElement.nativeElement; component = fixture.componentInstance; - component.workbaskets = [ - new WorkbasketSummary('1', 'key1', 'NAME1', 'description 1', 'owner 1') - ]; + component.workbaskets = [{ workbasketId: '1', + key: 'key1', + name: 'NAME1', + description: 'description 1', + owner: 'owner 1', + type: ICONTYPES.PERSONAL }]; component.workbaskets[0].markedForDeletion = false; fixture.detectChanges(); @@ -84,12 +86,6 @@ describe('WorkbasketListToolbarComponent', () => { expect(component).toBeTruthy(); }); - it('should navigate to new-workbasket when click on add new workbasket', () => { - const spy = spyOn(router, 'navigate'); - component.addWorkbasket(); - expect(spy.calls.first().args[0][0].outlets.detail[0]).toBe('new-workbasket'); - }); - it('should emit performSorting when sorting is triggered', () => { let sort: Sorting; const compareSort = new Sorting(); diff --git a/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.ts b/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.ts index 9fcc5a34e..2c5a80269 100644 --- a/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.ts +++ b/web/src/app/administration/components/workbasket-list-toolbar/workbasket-list-toolbar.component.ts @@ -3,14 +3,18 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Sorting } from 'app/shared/models/sorting'; import { Filter } from 'app/shared/models/filter'; -import { Subscription } from 'rxjs'; import { WorkbasketSummary } from 'app/shared/models/workbasket-summary'; import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service'; import { TaskanaType } from 'app/shared/models/taskana-type'; import { expandDown } from 'theme/animations/expand.animation'; -import { NotificationService } from '../../../shared/services/notifications/notification.service'; -import { NOTIFICATION_TYPES } from '../../../shared/models/notifications'; +import { Select, Store } from '@ngxs/store'; +import { Location } from '@angular/common'; +import { Observable, Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { ACTION } from '../../../shared/models/action'; +import { CreateWorkbasket, SetActiveAction } from '../../../shared/store/workbasket-store/workbasket.actions'; +import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors'; @Component({ selector: 'taskana-administration-workbasket-list-toolbar', @@ -23,7 +27,7 @@ export class WorkbasketListToolbarComponent implements OnInit { @Input() workbasketDefaultSortBy: string; @Output() performSorting = new EventEmitter(); @Output() performFilter = new EventEmitter(); - workbasketServiceSubscription: Subscription; + selectionToImport = TaskanaType.WORKBASKETS; sortingFields = new Map([['name', 'Name'], ['key', 'Key'], ['description', 'Description'], ['owner', 'Owner'], ['type', 'Type']]); filteringTypes = new Map([['ALL', 'All'], ['PERSONAL', 'Personal'], ['GROUP', 'Group'], @@ -33,15 +37,27 @@ export class WorkbasketListToolbarComponent implements OnInit { toolbarState = false; filterType = TaskanaType.WORKBASKETS; + @Select(WorkbasketSelectors.workbasketActiveAction) + workbasketActiveAction$: Observable; + + destroy$ = new Subject(); + action: ACTION; + constructor( private workbasketService: WorkbasketService, private route: ActivatedRoute, private router: Router, - private errors: NotificationService + private store: Store, + private location: Location ) { } ngOnInit() { + this.workbasketActiveAction$ + .pipe(takeUntil(this.destroy$)) + .subscribe(action => { + this.action = action; + }); } sorting(sort: Sorting) { @@ -53,7 +69,16 @@ export class WorkbasketListToolbarComponent implements OnInit { } addWorkbasket() { - this.workbasketService.selectWorkBasket(); - this.router.navigate([{ outlets: { detail: ['new-workbasket'] } }], { relativeTo: this.route }); + // this.store.dispatch(new SetActiveAction(ACTION.CREATE)); + if (this.action !== ACTION.CREATE) { + this.store.dispatch(new CreateWorkbasket()); + } + // this.workbasketService.selectWorkBasket(); + // this.router.navigate([{ outlets: { detail: ['new-workbasket'] } }], { relativeTo: this.route }); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/web/src/app/administration/components/workbasket-list/workbasket-list.component.html b/web/src/app/administration/components/workbasket-list/workbasket-list.component.html index 08c7d2f40..7b587d453 100644 --- a/web/src/app/administration/components/workbasket-list/workbasket-list.component.html +++ b/web/src/app/administration/components/workbasket-list/workbasket-list.component.html @@ -1,15 +1,17 @@