From 900343b722316f2917c844cc3b533b7f9718b5b3 Mon Sep 17 00:00:00 2001
From: Chi Nguyen <6671583+cnguyen-de@users.noreply.github.com>
Date: Tue, 8 Dec 2020 16:11:13 +0100
Subject: [PATCH] TSK-1412: Distribution targets MD (#1365)
* TSK-1412: Refactor workbasket dual list to workbasket distribution targets list
* TSK-1412: Refactor workbasket dual list to workbasket distribution targets list
* TSK-1412: rework css layout to be more dynamic and resilient
* TSK-1412: new list, new dialog, new action bar
* TSK-1412: Refactor workbasket dual list to workbasket distribution targets list
* TSK-1412: rework css layout to be more dynamic and resilient
* TSK-1412: new list, new dialog, new action bar
* TSK-1412: update new workbasket distribution target list to load data internally
* TSK-1412: update distribution targets
* TSK-1421: enable multiple selection
* TSK-1412: Updated angular theme to match with taskana
* TSK-1412: quick bug fixes
* TSK-1412: Rework how workbasket distribution targets list behaves
* TSK-1412: Update workbasket distribution target list to new design
* TSK-1412: fixed filter function in distribution targets
* TSK-1412: remove unused component, rename correct CSS names
* TSK-1412: clean up code in workbasket distribution targets
* TSK-1412: fix bugs, rename variables
* TSK-1412: finalized jest tests
* TSK-1412: fix all other jest tests
* TSK-1412: disable devmode
* TSK-1412: remove unused imports, add tooltips
---
web/angular.json | 3 +-
.../administration/administration.module.ts | 18 +-
.../classification-details.component.html | 26 +-
.../classification-details.component.scss | 20 +-
.../classification-details.component.spec.ts | 2 +
.../classification-list.component.scss | 1 -
.../classification-overview.component.scss | 9 +-
.../workbasket-details.component.scss | 4 -
...t-distribution-targets-list.component.html | 65 ++++
...t-distribution-targets-list.component.scss | 61 ++++
...stribution-targets-list.component.spec.ts} | 38 +-
...ket-distribution-targets-list.component.ts | 75 ++++
...basket-distribution-targets.component.html | 176 ++++++----
...basket-distribution-targets.component.scss | 34 +-
...ket-distribution-targets.component.spec.ts | 156 +++++++++
...rkbasket-distribution-targets.component.ts | 329 +++++++-----------
.../workbasket-dual-list.component.html | 57 ---
.../workbasket-dual-list.component.scss | 112 ------
.../workbasket-dual-list.component.ts | 50 ---
.../workbasket-information.component.scss | 5 +-
.../workbasket-information.component.spec.ts | 4 +-
.../workbasket-list-toolbar.component.html | 2 +-
.../workbasket-list-toolbar.component.spec.ts | 19 +-
.../workbasket-list-toolbar.component.ts | 9 +-
.../workbasket-list.component.scss | 2 -
.../workbasket-list.component.spec.ts | 9 +-
.../workbasket-overview.component.scss | 10 +-
web/src/app/app.component.scss | 4 +-
.../components/filter/filter.component.ts | 3 +
.../shared/pipes/select-workbaskets.pipe.ts | 5 +-
.../app/shared/store/mock-data/mock-store.ts | 210 +++++++++++
.../workbasket-store/workbasket.actions.ts | 4 +
.../workbasket-store/workbasket.selectors.ts | 5 +
.../workbasket-store/workbasket.state.ts | 31 +-
web/src/theme/custom-theme-material.scss | 8 +
35 files changed, 998 insertions(+), 568 deletions(-)
create mode 100644 web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.html
create mode 100644 web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.scss
rename web/src/app/administration/components/{workbasket-dual-list/workbasket-dual-list.component.spec.ts => workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.spec.ts} (69%)
create mode 100644 web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.ts
create mode 100644 web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.spec.ts
delete mode 100644 web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.html
delete mode 100644 web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.scss
delete mode 100644 web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.ts
create mode 100644 web/src/theme/custom-theme-material.scss
diff --git a/web/angular.json b/web/angular.json
index 3fd19046a..93eb971fc 100644
--- a/web/angular.json
+++ b/web/angular.json
@@ -24,7 +24,8 @@
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
- "src/theme/_main.scss"
+ "src/theme/_main.scss",
+ "src/theme/custom-theme-material.scss"
],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
diff --git a/web/src/app/administration/administration.module.ts b/web/src/app/administration/administration.module.ts
index f82ea50c4..4b9af5858 100644
--- a/web/src/app/administration/administration.module.ts
+++ b/web/src/app/administration/administration.module.ts
@@ -19,11 +19,15 @@ import { WorkbasketListToolbarComponent } from './components/workbasket-list-too
import { WorkbasketDetailsComponent } from './components/workbasket-details/workbasket-details.component';
import { WorkbasketInformationComponent } from './components/workbasket-information/workbasket-information.component';
import { WorkbasketDistributionTargetsComponent } from './components/workbasket-distribution-targets/workbasket-distribution-targets.component';
-import { WorkbasketDualListComponent } from './components/workbasket-dual-list/workbasket-dual-list.component';
+import { WorkbasketDistributionTargetsListComponent } from './components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component';
import { WorkbasketAccessItemsComponent } from './components/workbasket-access-items/workbasket-access-items.component';
import { ClassificationListComponent } from './components/classification-list/classification-list.component';
import { ClassificationDetailsComponent } from './components/classification-details/classification-details.component';
import { ImportExportComponent } from './components/import-export/import-export.component';
+import { AdministrationOverviewComponent } from './components/administration-overview/administration-overview.component';
+
+import { ClassificationOverviewComponent } from './components/classification-overview/classification-overview.component';
+import { WorkbasketOverviewComponent } from './components/workbasket-overview/workbasket-overview.component';
/**
* Services
*/
@@ -31,15 +35,16 @@ import { SavingWorkbasketService } from './services/saving-workbaskets.service';
import { ClassificationDefinitionService } from './services/classification-definition.service';
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';
+
+/**
+ * Material Design
+ */
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { MatMenuModule } from '@angular/material/menu';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatTabsModule } from '@angular/material/tabs';
-import { AdministrationOverviewComponent } from './components/administration-overview/administration-overview.component';
import { MatInputModule } from '@angular/material/input';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatDividerModule } from '@angular/material/divider';
@@ -72,7 +77,7 @@ const DECLARATIONS = [
WorkbasketDetailsComponent,
WorkbasketInformationComponent,
WorkbasketDistributionTargetsComponent,
- WorkbasketDualListComponent,
+ WorkbasketDistributionTargetsListComponent,
ClassificationOverviewComponent,
ClassificationListComponent,
ClassificationTypesSelectorComponent,
@@ -111,6 +116,7 @@ const DECLARATIONS = [
SavingWorkbasketService,
ClassificationCategoriesService,
ImportExportService
- ]
+ ],
+ entryComponents: []
})
export class AdministrationModule {}
diff --git a/web/src/app/administration/components/classification-details/classification-details.component.html b/web/src/app/administration/components/classification-details/classification-details.component.html
index 720264688..327f8c6b4 100644
--- a/web/src/app/administration/components/classification-details/classification-details.component.html
+++ b/web/src/app/administration/components/classification-details/classification-details.component.html
@@ -3,7 +3,7 @@
-
-
+
@@ -80,17 +80,19 @@
-
- Service Level
-
-
-
-
{{lengthError}}
+
+
+ Service Level
+
+
+
+
{{lengthError}}
+
-
+
+
0">
+
+
+
+
+
+
+
+
+
+
+ {{workbasket.name}}, {{workbasket.key}}
+
+
{{workbasket.description}}
+
{{workbasket.owner}}
+
+
+
+ error
+
+
+
+
+
+
+
+
+
+
+ There is currently no distributed workbasket
+
+
diff --git a/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.scss b/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.scss
new file mode 100644
index 000000000..c4e2832d3
--- /dev/null
+++ b/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.scss
@@ -0,0 +1,61 @@
+@import 'src/theme/_colors.scss';
+.distribution-targets-list {
+ &__header {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ max-width: 50%;
+ @media screen and (max-width: 1280px) {
+ max-width: 100px;
+ }
+ }
+ &__action-button {
+ margin-left: 0.5rem;
+ }
+
+ &__list {
+ min-width: 100%;
+ background-color: white;
+ border-radius: 10px;
+
+ overflow-y: scroll;
+ height: calc(100vh - 360px) !important;
+ @media screen and (max-width: 991px) {
+ height: calc((100vh - 315px));
+ min-height: 83px;
+ }
+ }
+ &__item-wrapper {
+ display: flex;
+ }
+ &__item-icon {
+ padding: 32px 24px 24px 8px;
+ }
+ &__item-info {
+ display: block;
+ padding: 8px 0;
+ }
+ &__empty-list {
+ height: calc(100vh - 360px);
+ border-radius: 10px;
+ background-color: white;
+ font-size: 24px;
+ font-weight: bold;
+ color: $dark-green;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+}
+
+.mat-list-item {
+ height: 90px !important;
+}
+
+.mat-list-single-selected-option {
+ background-color: $green !important;
+ color: white;
+}
+
+p {
+ margin: 0;
+}
diff --git a/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.spec.ts b/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.spec.ts
similarity index 69%
rename from web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.spec.ts
rename to web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.spec.ts
index b10af4022..62979f4f4 100644
--- a/web/src/app/administration/components/workbasket-dual-list/workbasket-dual-list.component.spec.ts
+++ b/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.spec.ts
@@ -1,6 +1,6 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
-import { WorkbasketDualListComponent } from './workbasket-dual-list.component';
+import { WorkbasketDistributionTargetsListComponent } from './workbasket-distribution-targets-list.component';
import { Filter } from '../../../shared/models/filter';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { ICONTYPES } from '../../../shared/models/icon-types';
@@ -8,6 +8,9 @@ import { SelectWorkBasketPipe } from '../../../shared/pipes/select-workbaskets.p
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { workbasketReadStateMock } from '../../../shared/store/mock-data/mock-store';
import { Side } from '../workbasket-distribution-targets/workbasket-distribution-targets.component';
+import { MatIconModule } from '@angular/material/icon';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatListModule } from '@angular/material/list';
@Component({ selector: 'taskana-shared-filter', template: '' })
class FilterStub {
@@ -25,33 +28,39 @@ class IconTypeStub {
@Input() text: string;
}
-describe('WorkbasketDualListComponent', () => {
- let fixture: ComponentFixture
;
+describe('WorkbasketDistributionTargetsListComponent', () => {
+ let fixture: ComponentFixture;
let debugElement: DebugElement;
- let component: WorkbasketDualListComponent;
+ let component: WorkbasketDistributionTargetsListComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
- imports: [InfiniteScrollModule, BrowserAnimationsModule],
- declarations: [WorkbasketDualListComponent, FilterStub, SpinnerStub, IconTypeStub, SelectWorkBasketPipe],
+ imports: [MatIconModule, MatToolbarModule, MatListModule, InfiniteScrollModule, BrowserAnimationsModule],
+ declarations: [
+ WorkbasketDistributionTargetsListComponent,
+ FilterStub,
+ SpinnerStub,
+ IconTypeStub,
+ SelectWorkBasketPipe
+ ],
providers: []
}).compileComponents();
- fixture = TestBed.createComponent(WorkbasketDualListComponent);
+ fixture = TestBed.createComponent(WorkbasketDistributionTargetsListComponent);
debugElement = fixture.debugElement;
component = fixture.componentInstance;
component.distributionTargets = workbasketReadStateMock.paginatedWorkbasketsSummary.workbaskets;
component.distributionTargetsSelected = [];
- component.side = Side.LEFT;
+ component.side = Side.AVAILABLE;
}));
it('should create component', () => {
expect(component).toBeTruthy();
});
- it('should set sideNumber to 0 when side is Side.LEFT', () => {
+ it('should set sideNumber to 0 when side is Side.AVAILABLE', () => {
fixture.detectChanges();
- expect(component.sideNumber).toBe(0);
+ expect(component.side).toBe(Side.AVAILABLE);
});
it('should select all distribution targets', () => {
@@ -67,13 +76,6 @@ describe('WorkbasketDualListComponent', () => {
expect(scrollingEmitSpy).toHaveBeenCalledWith(component.side);
});
- it('should emit filter model and side when performing filter', () => {
- const performDualListFilterSpy = jest.spyOn(component.performDualListFilter, 'emit');
- const filterModelMock: Filter = { filterParams: 'filter' };
- component.performAvailableFilter(filterModelMock);
- expect(performDualListFilterSpy).toHaveBeenCalledWith({ filterBy: filterModelMock, side: component.side });
- });
-
it('should change toolbar state', () => {
expect(component.toolbarState).toBe(false);
component.changeToolbarState(true);
@@ -83,7 +85,7 @@ describe('WorkbasketDualListComponent', () => {
it('should display all available workbaskets', () => {
fixture.detectChanges();
const distributionTargetList = debugElement.nativeElement.getElementsByClassName(
- 'workbasket-list__distribution-targets'
+ 'workbasket-distribution-targets__workbaskets-item'
);
expect(distributionTargetList).toHaveLength(5);
});
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
new file mode 100644
index 000000000..06a8d3b7b
--- /dev/null
+++ b/web/src/app/administration/components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component.ts
@@ -0,0 +1,75 @@
+import {
+ Component,
+ OnInit,
+ Input,
+ Output,
+ EventEmitter,
+ AfterContentChecked,
+ ChangeDetectorRef,
+ ViewChild
+} from '@angular/core';
+import { WorkbasketSummary } from 'app/shared/models/workbasket-summary';
+import { Filter } from 'app/shared/models/filter';
+import { expandDown } from 'app/shared/animations/expand.animation';
+import { Side } from '../workbasket-distribution-targets/workbasket-distribution-targets.component';
+import { MatSelectionList } from '@angular/material/list';
+
+@Component({
+ selector: 'taskana-administration-workbasket-distribution-targets-list',
+ templateUrl: './workbasket-distribution-targets-list.component.html',
+ styleUrls: ['./workbasket-distribution-targets-list.component.scss'],
+ animations: [expandDown]
+})
+export class WorkbasketDistributionTargetsListComponent implements OnInit, AfterContentChecked {
+ @Input() distributionTargets: WorkbasketSummary[];
+ @Input() distributionTargetsSelected: WorkbasketSummary[];
+ @Output() performDualListFilter = new EventEmitter<{ filterBy: Filter; side: Side }>();
+ @Input() requestInProgress = false;
+ @Input() loadingItems? = false;
+ @Input() side: Side;
+ @Input() header: string;
+ @Output() scrolling = new EventEmitter();
+ @Input() allSelected;
+ @Output() allSelectedChange = new EventEmitter();
+
+ toolbarState = false;
+ component = '';
+ @ViewChild('workbasket') distributionTargetsList: MatSelectionList;
+
+ constructor(private changeDetector: ChangeDetectorRef) {}
+
+ ngOnInit() {
+ this.allSelected = !this.allSelected;
+ }
+
+ ngAfterContentChecked(): void {
+ this.changeDetector.detectChanges();
+ }
+
+ selectAll(selected: boolean) {
+ if (typeof this.distributionTargetsList !== 'undefined') {
+ this.allSelected = !this.allSelected;
+ this.distributionTargetsList.options.map((item) => (item['selected'] = selected));
+ }
+ this.distributionTargets.map((item) => (item['selected'] = selected));
+ this.allSelectedChange.emit(this.allSelected);
+ }
+
+ setComponent(component: string) {
+ this.component = component;
+ }
+
+ onScroll() {
+ this.scrolling.emit(this.side);
+ }
+
+ performAvailableFilter(filterModel: Filter) {
+ if (this.component === 'distribution-target') {
+ this.performDualListFilter.emit({ filterBy: filterModel, side: this.side });
+ }
+ }
+
+ changeToolbarState(state: boolean) {
+ this.toolbarState = state;
+ }
+}
diff --git a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.html b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.html
index e6a42bc81..209da7845 100644
--- a/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.html
+++ b/web/src/app/administration/components/workbasket-distribution-targets/workbasket-distribution-targets.component.html
@@ -1,77 +1,111 @@
-
-
+
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 264975b34..cfde11fe5 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
@@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { WorkbasketListToolbarComponent } from './workbasket-list-toolbar.component';
import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
import { Actions, NgxsModule, ofActionDispatched, Store } from '@ngxs/store';
-import { Observable } from 'rxjs';
+import { Observable, of } from 'rxjs';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { WorkbasketState } from '../../../shared/store/workbasket-store/workbasket.state';
import { WorkbasketService } from '../../../shared/services/workbasket/workbasket.service';
@@ -17,6 +17,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatDialogModule } from '@angular/material/dialog';
import { RouterTestingModule } from '@angular/router/testing';
+import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service';
const getDomainFn = jest.fn().mockReturnValue(true);
const domainServiceMock = jest.fn().mockImplementation(
@@ -43,6 +44,12 @@ class FilterStub {
@Output() performFilter = new EventEmitter
();
}
+const requestInProgressServiceSpy = jest.fn().mockImplementation(
+ (): Partial => ({
+ setRequestInProgress: jest.fn().mockReturnValue(of())
+ })
+);
+
describe('WorkbasketListToolbarComponent', () => {
let fixture: ComponentFixture;
let debugElement: DebugElement;
@@ -62,7 +69,11 @@ describe('WorkbasketListToolbarComponent', () => {
MatDialogModule
],
declarations: [WorkbasketListToolbarComponent, ImportExportStub, SortStub, FilterStub],
- providers: [{ provide: DomainService, useClass: domainServiceMock }, WorkbasketService]
+ providers: [
+ { provide: DomainService, useClass: domainServiceMock },
+ { provide: RequestInProgressService, useClass: requestInProgressServiceSpy },
+ WorkbasketService
+ ]
}).compileComponents();
fixture = TestBed.createComponent(WorkbasketListToolbarComponent);
@@ -107,7 +118,7 @@ describe('WorkbasketListToolbarComponent', () => {
expect(sort).toMatchObject(mockSort);
});
- it('should emit value when filtering is called', (done) => {
+ it('should emit value when filtering is called', async((done) => {
const mockFilter: Filter = { filterParams: 'abc' };
let filterBy: Filter = { filterParams: 'abc' };
component.performFilter.subscribe((filter: Filter) => {
@@ -116,7 +127,7 @@ describe('WorkbasketListToolbarComponent', () => {
});
component.filtering(filterBy);
expect(filterBy).toMatchObject(mockFilter);
- });
+ }));
/* HTML */
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 3a34ada27..1786b2292 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
@@ -43,6 +43,7 @@ export class WorkbasketListToolbarComponent implements OnInit {
filterParams = { name: '', key: '', type: '', description: '', owner: '' };
filterType = TaskanaType.WORKBASKETS;
showFilter = false;
+ component = '';
@Select(WorkbasketSelectors.workbasketActiveAction)
workbasketActiveAction$: Observable;
@@ -63,7 +64,13 @@ export class WorkbasketListToolbarComponent implements OnInit {
}
filtering(filterBy: Filter) {
- this.performFilter.emit(filterBy);
+ if (this.component === 'workbasket-list') {
+ this.performFilter.emit(filterBy);
+ }
+ }
+
+ setComponent(component: string) {
+ this.component = component;
}
addWorkbasket() {
diff --git a/web/src/app/administration/components/workbasket-list/workbasket-list.component.scss b/web/src/app/administration/components/workbasket-list/workbasket-list.component.scss
index bd33b4b95..550e222d1 100644
--- a/web/src/app/administration/components/workbasket-list/workbasket-list.component.scss
+++ b/web/src/app/administration/components/workbasket-list/workbasket-list.component.scss
@@ -2,9 +2,7 @@
.workbasket-list {
height: calc(100vh - 156px);
- overflow-x: hidden;
overflow-y: hidden;
- min-width: 500px;
}
.workbasket-list__workbaskets {
overflow-y: hidden;
diff --git a/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts b/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts
index 3e4911ec9..d6ea91867 100644
--- a/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts
+++ b/web/src/app/administration/components/workbasket-list/workbasket-list.component.spec.ts
@@ -23,17 +23,20 @@ import { MatListModule } from '@angular/material/list';
import { DomainService } from '../../../shared/services/domain/domain.service';
import { RouterTestingModule } from '@angular/router/testing';
import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service';
+import { selectedWorkbasketMock } from '../../../shared/store/mock-data/mock-store';
const workbasketSavedTriggeredFn = jest.fn().mockReturnValue(of(1));
const workbasketSummaryFn = jest.fn().mockReturnValue(of({}));
-const getWorkbasketFn = jest.fn().mockReturnValue(of({ workbasketId: '1' }));
+const getWorkbasketFn = jest.fn().mockReturnValue(of(selectedWorkbasketMock));
const getWorkbasketActionToolbarExpansionFn = jest.fn().mockReturnValue(of(false));
const workbasketServiceMock = jest.fn().mockImplementation(
(): Partial => ({
workbasketSavedTriggered: workbasketSavedTriggeredFn,
getWorkBasketsSummary: workbasketSummaryFn,
getWorkBasket: getWorkbasketFn,
- getWorkbasketActionToolbarExpansion: getWorkbasketActionToolbarExpansionFn
+ getWorkbasketActionToolbarExpansion: getWorkbasketActionToolbarExpansionFn,
+ getWorkBasketAccessItems: jest.fn().mockReturnValue(of({})),
+ getWorkBasketsDistributionTargets: jest.fn().mockReturnValue(of({}))
})
);
@@ -137,7 +140,7 @@ describe('WorkbasketListComponent', () => {
fixture.detectChanges();
let actionDispatched = false;
actions$.pipe(ofActionDispatched(SelectWorkbasket)).subscribe(() => (actionDispatched = true));
- component.selectWorkbasket('1');
+ component.selectWorkbasket('WBI:000000000000000000000000000000000902');
expect(actionDispatched).toBe(true);
}));
diff --git a/web/src/app/administration/components/workbasket-overview/workbasket-overview.component.scss b/web/src/app/administration/components/workbasket-overview/workbasket-overview.component.scss
index 746510663..4bdcdea3a 100644
--- a/web/src/app/administration/components/workbasket-overview/workbasket-overview.component.scss
+++ b/web/src/app/administration/components/workbasket-overview/workbasket-overview.component.scss
@@ -1,13 +1,13 @@
.workbasket-overview {
display: flex;
- flex-direction: row;
width: 100%;
height: 100%;
overflow: hidden;
align-items: stretch;
}
-
-taskana-administration-workbasket-details {
- width: calc(100% - 500px);
- height: calc(100% - 213px);
+taskana-administration-workbasket-list {
+ width: 500px;
+}
+taskana-administration-workbasket-details {
+ flex-grow: 1;
}
diff --git a/web/src/app/app.component.scss b/web/src/app/app.component.scss
index b41e01931..c3ee9061d 100644
--- a/web/src/app/app.component.scss
+++ b/web/src/app/app.component.scss
@@ -17,7 +17,9 @@
width: 350px;
background-color: $dark-green;
}
-
+.mat-drawer-content {
+ overflow: hidden !important;
+}
.sidenav__drawer-list-item {
margin-left: 30px;
}
diff --git a/web/src/app/shared/components/filter/filter.component.ts b/web/src/app/shared/components/filter/filter.component.ts
index a82d583e8..8f9487342 100644
--- a/web/src/app/shared/components/filter/filter.component.ts
+++ b/web/src/app/shared/components/filter/filter.component.ts
@@ -9,6 +9,7 @@ import { TaskanaType } from 'app/shared/models/taskana-type';
styleUrls: ['./filter.component.scss']
})
export class FilterComponent implements OnInit {
+ @Input() component: string;
@Input() allTypes: Map = new Map([
[ICONTYPES.ALL, 'All'],
[ICONTYPES.PERSONAL, 'Personal'],
@@ -29,6 +30,7 @@ export class FilterComponent implements OnInit {
@Input() filterType = TaskanaType.WORKBASKETS;
@Output() performFilter = new EventEmitter();
+ @Output() inputComponent = new EventEmitter();
filter: Filter;
filterParamKeys = [];
@@ -59,6 +61,7 @@ export class FilterComponent implements OnInit {
}
search() {
+ this.inputComponent.emit(this.component);
this.performFilter.emit(this.filter);
}
diff --git a/web/src/app/shared/pipes/select-workbaskets.pipe.ts b/web/src/app/shared/pipes/select-workbaskets.pipe.ts
index b9b0d3ad5..3d011d073 100644
--- a/web/src/app/shared/pipes/select-workbaskets.pipe.ts
+++ b/web/src/app/shared/pipes/select-workbaskets.pipe.ts
@@ -1,14 +1,14 @@
import { Pipe, PipeTransform } from '@angular/core';
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
+import { WorkbasketSummary } from '../models/workbasket-summary';
@Pipe({ name: 'selectWorkbaskets' })
export class SelectWorkBasketPipe implements PipeTransform {
- transform(originArray: any, selectionArray: any, arg1: any): Object[] {
+ transform(originArray: any, selectionArray: any, arg1: any): WorkbasketSummary[] {
let returnArray = [];
if (!originArray || !selectionArray) {
return returnArray;
}
-
for (let index = originArray.length - 1; index >= 0; index--) {
if (
(arg1 &&
@@ -21,6 +21,7 @@ export class SelectWorkBasketPipe implements PipeTransform {
originArray.splice(index, 1);
}
}
+
if (originArray.length > TaskanaQueryParameters.pageSize) {
originArray.slice(0, TaskanaQueryParameters.pageSize);
}
diff --git a/web/src/app/shared/store/mock-data/mock-store.ts b/web/src/app/shared/store/mock-data/mock-store.ts
index e9ac250ad..a234e5a41 100644
--- a/web/src/app/shared/store/mock-data/mock-store.ts
+++ b/web/src/app/shared/store/mock-data/mock-store.ts
@@ -288,5 +288,215 @@ export const workbasketReadStateMock = {
}
},
action: ACTION.READ,
+ workbasketDistributionTargets: {
+ _links: {
+ self: {
+ href:
+ 'http://localhost:8080/taskana/api/v1/workbaskets/WBI:000000000000000000000000000000000900/distribution-targets'
+ }
+ },
+ distributionTargets: [
+ {
+ workbasketId: 'WBI:100000000000000000000000000000000001',
+ key: 'GPK_KSC',
+ name: 'Gruppenpostkorb KSC',
+ domain: 'DOMAIN_A',
+ type: 'GROUP',
+ description: 'Gruppenpostkorb KSC',
+ owner: 'owner0815',
+ custom1: 'ABCQVW',
+ custom2: '',
+ custom3: 'xyz4',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:100000000000000000000000000000000002',
+ key: 'GPK_KSC_1',
+ name: 'Gruppenpostkorb KSC 1',
+ domain: 'DOMAIN_A',
+ type: 'GROUP',
+ description: 'Gruppenpostkorb KSC 1',
+ owner: '',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:100000000000000000000000000000000003',
+ key: 'GPK_KSC_2',
+ name: 'Gruppenpostkorb KSC 2',
+ domain: 'DOMAIN_A',
+ type: 'GROUP',
+ description: 'Gruppenpostkorb KSC 2',
+ owner: '',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ }
+ ]
+ },
+ workbasketAvailableDistributionTargets: [
+ {
+ workbasketId: 'WBI:100000000000000000000000000000000001',
+ key: 'GPK_KSC',
+ name: 'Gruppenpostkorb KSC',
+ domain: 'DOMAIN_A',
+ type: 'GROUP',
+ description: 'Gruppenpostkorb KSC',
+ owner: 'owner0815',
+ custom1: 'ABCQVW',
+ custom2: '',
+ custom3: 'xyz4',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:100000000000000000000000000000000002',
+ key: 'GPK_KSC_1',
+ name: 'Gruppenpostkorb KSC 1',
+ domain: 'DOMAIN_A',
+ type: 'GROUP',
+ description: 'Gruppenpostkorb KSC 1',
+ owner: '',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:100000000000000000000000000000000003',
+ key: 'GPK_KSC_2',
+ name: 'Gruppenpostkorb KSC 2',
+ domain: 'DOMAIN_A',
+ type: 'GROUP',
+ description: 'Gruppenpostkorb KSC 2',
+ owner: '',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:000000000000000000000000000000000900',
+ key: 'sort001',
+ name: 'basxet0',
+ domain: 'DOMAIN_A',
+ type: 'TOPIC',
+ description: 'Lorem ipsum dolor sit amet.',
+ owner: 'Max',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:000000000000000000000000000000000901',
+ key: 'Sort002',
+ name: 'Basxet1',
+ domain: 'DOMAIN_A',
+ type: 'TOPIC',
+ description: 'Lorem ipsum dolor sit amet.',
+ owner: 'Max',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:000000000000000000000000000000000902',
+ key: 'sOrt003',
+ name: 'bAsxet2',
+ domain: 'DOMAIN_A',
+ type: 'TOPIC',
+ description: 'Lorem ipsum dolor sit amet.',
+ owner: 'Max',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:000000000000000000000000000000000903',
+ key: 'soRt004',
+ name: 'baSxet3',
+ domain: 'DOMAIN_A',
+ type: 'TOPIC',
+ description: 'Lorem ipsum dolor sit amet.',
+ owner: 'Max',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ },
+ {
+ workbasketId: 'WBI:000000000000000000000000000000000904',
+ key: 'sorT005',
+ name: 'basXet4',
+ domain: 'DOMAIN_A',
+ type: 'TOPIC',
+ description: 'Lorem ipsum dolor sit amet.',
+ owner: 'Max',
+ custom1: '',
+ custom2: '',
+ custom3: '',
+ custom4: '',
+ orgLevel1: '',
+ orgLevel2: '',
+ orgLevel3: '',
+ orgLevel4: '',
+ markedForDeletion: false
+ }
+ ],
workbasketAccessItems: workbasketAccessItemsMock
};
diff --git a/web/src/app/shared/store/workbasket-store/workbasket.actions.ts b/web/src/app/shared/store/workbasket-store/workbasket.actions.ts
index 1ab1b86b2..90cd71088 100644
--- a/web/src/app/shared/store/workbasket-store/workbasket.actions.ts
+++ b/web/src/app/shared/store/workbasket-store/workbasket.actions.ts
@@ -99,6 +99,10 @@ export class GetWorkbasketDistributionTargets {
constructor(public url: string) {}
}
+export class GetAvailableDistributionTargets {
+ static readonly type = '[Workbasket] Get available distribution targets';
+}
+
export class UpdateWorkbasketDistributionTargets {
static readonly type = '[Workbasket] Update workbasket distribution targets';
constructor(public url: string, public distributionTargetsIds: string[]) {}
diff --git a/web/src/app/shared/store/workbasket-store/workbasket.selectors.ts b/web/src/app/shared/store/workbasket-store/workbasket.selectors.ts
index ec50edf33..81ad29882 100644
--- a/web/src/app/shared/store/workbasket-store/workbasket.selectors.ts
+++ b/web/src/app/shared/store/workbasket-store/workbasket.selectors.ts
@@ -60,6 +60,11 @@ export class WorkbasketSelectors {
static workbasketDistributionTargets(state: WorkbasketStateModel): WorkbasketDistributionTargets {
return state.workbasketDistributionTargets;
}
+
+ @Selector([WorkbasketState])
+ static availableDistributionTargets(state: WorkbasketStateModel): WorkbasketSummary[] {
+ return state.workbasketAvailableDistributionTargets;
+ }
}
export interface WorkbasketAndAction {
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 dd5e54620..83a68ed12 100644
--- a/web/src/app/shared/store/workbasket-store/workbasket.state.ts
+++ b/web/src/app/shared/store/workbasket-store/workbasket.state.ts
@@ -8,6 +8,7 @@ import {
CopyWorkbasket,
CreateWorkbasket,
DeselectWorkbasket,
+ GetAvailableDistributionTargets,
GetWorkbasketAccessItems,
GetWorkbasketDistributionTargets,
GetWorkbasketsSummary,
@@ -32,6 +33,7 @@ import { WorkbasketSummary } from '../../models/workbasket-summary';
import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
import { ButtonAction } from '../../../administration/models/button-action';
import { ActivatedRoute } from '@angular/router';
+import { RequestInProgressService } from '../../services/request-in-progress/request-in-progress.service';
class InitializeStore {
static readonly type = '[Workbasket] Initializing state';
@@ -43,7 +45,8 @@ export class WorkbasketState implements NgxsAfterBootstrap {
private workbasketService: WorkbasketService,
private location: Location,
private notificationService: NotificationService,
- private route: ActivatedRoute
+ private route: ActivatedRoute,
+ private requestInProgressService: RequestInProgressService
) {}
@Action(InitializeStore)
@@ -131,6 +134,10 @@ export class WorkbasketState implements NgxsAfterBootstrap {
selectedWorkbasket,
action: ACTION.READ
});
+ ctx.dispatch(new GetWorkbasketAccessItems(ctx.getState().selectedWorkbasket._links.accessItems.href));
+ ctx.dispatch(
+ new GetWorkbasketDistributionTargets(ctx.getState().selectedWorkbasket._links.distributionTargets.href)
+ );
})
);
}
@@ -186,6 +193,9 @@ export class WorkbasketState implements NgxsAfterBootstrap {
@Action(OnButtonPressed)
doWorkbasketDetailsAction(ctx: StateContext, action: OnButtonPressed): Observable {
ctx.patchState({ button: action.button });
+ setTimeout(() => {
+ ctx.patchState({ button: undefined });
+ }, 500);
return of(null);
}
@@ -347,15 +357,32 @@ export class WorkbasketState implements NgxsAfterBootstrap {
);
}
+ @Action(GetAvailableDistributionTargets)
+ getAvailableDistributionTargets(ctx: StateContext): Observable {
+ return this.workbasketService.getWorkBasketsSummary(true).pipe(
+ take(1),
+ tap((workbasketAvailableDistributionTargets: WorkbasketSummaryRepresentation) => {
+ ctx.patchState({
+ workbasketAvailableDistributionTargets: workbasketAvailableDistributionTargets.workbaskets
+ });
+ })
+ );
+ }
+
@Action(UpdateWorkbasketDistributionTargets)
updateWorkbasketDistributionTargets(
ctx: StateContext,
action: UpdateWorkbasketDistributionTargets
): Observable {
+ this.requestInProgressService.setRequestInProgress(true);
return this.workbasketService.updateWorkBasketsDistributionTargets(action.url, action.distributionTargetsIds).pipe(
take(1),
tap(
(updatedWorkbasketsDistributionTargets) => {
+ ctx.patchState({
+ workbasketDistributionTargets: updatedWorkbasketsDistributionTargets
+ });
+ this.requestInProgressService.setRequestInProgress(false);
this.notificationService.showToast(
NOTIFICATION_TYPES.SUCCESS_ALERT_8,
new Map([['workbasketName', ctx.getState().selectedWorkbasket.name]])
@@ -363,6 +390,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
},
(error) => {
this.notificationService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_3, error);
+ this.requestInProgressService.setRequestInProgress(false);
}
)
);
@@ -392,6 +420,7 @@ export interface WorkbasketStateModel {
action: ACTION;
workbasketAccessItems: WorkbasketAccessItemsRepresentation;
workbasketDistributionTargets: WorkbasketDistributionTargets;
+ workbasketAvailableDistributionTargets: WorkbasketSummary[];
selectedComponent: WorkbasketComponent;
button: ButtonAction | undefined;
}
diff --git a/web/src/theme/custom-theme-material.scss b/web/src/theme/custom-theme-material.scss
new file mode 100644
index 000000000..ea870a4e1
--- /dev/null
+++ b/web/src/theme/custom-theme-material.scss
@@ -0,0 +1,8 @@
+@import '~@angular/material/theming';
+@include mat-core();
+
+$my-app-primary: mat-palette($mat-blue-grey);
+$my-app-accent: mat-palette($mat-teal, 500, 900, A100);
+$my-app-warn: mat-palette($mat-red, 600);
+$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn);
+@include angular-material-theme($my-app-theme);