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
This commit is contained in:
parent
4dd426e060
commit
900343b722
|
|
@ -24,7 +24,8 @@
|
||||||
"styles": [
|
"styles": [
|
||||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||||
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
|
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||||
"src/theme/_main.scss"
|
"src/theme/_main.scss",
|
||||||
|
"src/theme/custom-theme-material.scss"
|
||||||
],
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"node_modules/jquery/dist/jquery.min.js",
|
"node_modules/jquery/dist/jquery.min.js",
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,15 @@ import { WorkbasketListToolbarComponent } from './components/workbasket-list-too
|
||||||
import { WorkbasketDetailsComponent } from './components/workbasket-details/workbasket-details.component';
|
import { WorkbasketDetailsComponent } from './components/workbasket-details/workbasket-details.component';
|
||||||
import { WorkbasketInformationComponent } from './components/workbasket-information/workbasket-information.component';
|
import { WorkbasketInformationComponent } from './components/workbasket-information/workbasket-information.component';
|
||||||
import { WorkbasketDistributionTargetsComponent } from './components/workbasket-distribution-targets/workbasket-distribution-targets.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 { WorkbasketAccessItemsComponent } from './components/workbasket-access-items/workbasket-access-items.component';
|
||||||
import { ClassificationListComponent } from './components/classification-list/classification-list.component';
|
import { ClassificationListComponent } from './components/classification-list/classification-list.component';
|
||||||
import { ClassificationDetailsComponent } from './components/classification-details/classification-details.component';
|
import { ClassificationDetailsComponent } from './components/classification-details/classification-details.component';
|
||||||
import { ImportExportComponent } from './components/import-export/import-export.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
|
* Services
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,15 +35,16 @@ import { SavingWorkbasketService } from './services/saving-workbaskets.service';
|
||||||
import { ClassificationDefinitionService } from './services/classification-definition.service';
|
import { ClassificationDefinitionService } from './services/classification-definition.service';
|
||||||
import { WorkbasketDefinitionService } from './services/workbasket-definition.service';
|
import { WorkbasketDefinitionService } from './services/workbasket-definition.service';
|
||||||
import { ImportExportService } from './services/import-export.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 { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { AdministrationOverviewComponent } from './components/administration-overview/administration-overview.component';
|
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { MatDividerModule } from '@angular/material/divider';
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
|
@ -72,7 +77,7 @@ const DECLARATIONS = [
|
||||||
WorkbasketDetailsComponent,
|
WorkbasketDetailsComponent,
|
||||||
WorkbasketInformationComponent,
|
WorkbasketInformationComponent,
|
||||||
WorkbasketDistributionTargetsComponent,
|
WorkbasketDistributionTargetsComponent,
|
||||||
WorkbasketDualListComponent,
|
WorkbasketDistributionTargetsListComponent,
|
||||||
ClassificationOverviewComponent,
|
ClassificationOverviewComponent,
|
||||||
ClassificationListComponent,
|
ClassificationListComponent,
|
||||||
ClassificationTypesSelectorComponent,
|
ClassificationTypesSelectorComponent,
|
||||||
|
|
@ -111,6 +116,7 @@ const DECLARATIONS = [
|
||||||
SavingWorkbasketService,
|
SavingWorkbasketService,
|
||||||
ClassificationCategoriesService,
|
ClassificationCategoriesService,
|
||||||
ImportExportService
|
ImportExportService
|
||||||
]
|
],
|
||||||
|
entryComponents: []
|
||||||
})
|
})
|
||||||
export class AdministrationModule {}
|
export class AdministrationModule {}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="classification-details__wrapper" id="classification-details" *ngIf="classification && !requestInProgress">
|
<div class="classification-details__wrapper" id="classification-details" *ngIf="classification && !requestInProgress">
|
||||||
|
|
||||||
<!-- TITLE + ACTION BUTTONS -->
|
<!-- TITLE + ACTION BUTTONS -->
|
||||||
<section class="classification-details__action-toolbar">
|
<mat-toolbar class="classification-details__action-toolbar">
|
||||||
<h4 class="classification-details__headline">{{classification.name}} [{{classification.type}}]
|
<h4 class="classification-details__headline">{{classification.name}} [{{classification.type}}]
|
||||||
<span *ngIf="isCreatingNewClassification" class="badge warning"> {{badgeMessage$ | async}}</span>
|
<span *ngIf="isCreatingNewClassification" class="badge warning"> {{badgeMessage$ | async}}</span>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</mat-toolbar>
|
||||||
|
|
||||||
<!-- DETAILED FIELDS -->
|
<!-- DETAILED FIELDS -->
|
||||||
<div class="panel-body" style="padding: 0">
|
<div class="panel-body" style="padding: 0">
|
||||||
|
|
@ -80,6 +80,7 @@
|
||||||
|
|
||||||
<!-- SERVICE LEVEL AND PRIORITY-->
|
<!-- SERVICE LEVEL AND PRIORITY-->
|
||||||
<div class="classification-details__service-and-priority">
|
<div class="classification-details__service-and-priority">
|
||||||
|
<div class="classification-details__service">
|
||||||
<mat-form-field appearance="outline" class="classification-details__mat-form-field">
|
<mat-form-field appearance="outline" class="classification-details__mat-form-field">
|
||||||
<mat-label> Service Level </mat-label>
|
<mat-label> Service Level </mat-label>
|
||||||
<label for="classification-service-level"></label>
|
<label for="classification-service-level"></label>
|
||||||
|
|
@ -89,8 +90,9 @@
|
||||||
#serviceLevel="ngModel" (input)="validateInputOverflow(serviceLevel, 255)">
|
#serviceLevel="ngModel" (input)="validateInputOverflow(serviceLevel, 255)">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<div *ngIf="inputOverflowMap.get(serviceLevel.name)" class="error">{{lengthError}}</div>
|
<div *ngIf="inputOverflowMap.get(serviceLevel.name)" class="error">{{lengthError}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="classification-details__priority">
|
||||||
<!-- I replaced this component by the mat-form-field. Is this the same?
|
<!-- I replaced this component by the mat-form-field. Is this the same?
|
||||||
I don't understand all methods in the number-picker component.
|
I don't understand all methods in the number-picker component.
|
||||||
<taskana-shared-number-picker [(ngModel)]="classification.priority"
|
<taskana-shared-number-picker [(ngModel)]="classification.priority"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
@import 'src/theme/_colors.scss';
|
@import 'src/theme/_colors.scss';
|
||||||
|
|
||||||
.classification-details {
|
.classification-details {
|
||||||
width: 100%;
|
|
||||||
height: calc(100vh - 100px);
|
height: calc(100vh - 100px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
@ -12,12 +11,12 @@
|
||||||
|
|
||||||
/* ACTION TOOLBAR */
|
/* ACTION TOOLBAR */
|
||||||
.classification-details__headline {
|
.classification-details__headline {
|
||||||
padding-top: 0.5rem;
|
font-size: 1.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
.classification-details__action-toolbar {
|
.classification-details__action-toolbar {
|
||||||
width: calc(100% - 500px);
|
padding: 8px 30px 12px 24px;
|
||||||
position: fixed;
|
height: 68px;
|
||||||
padding: 12px 32px 12px 24px;
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
display: flex;
|
display: flex;
|
||||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
|
@ -98,11 +97,18 @@
|
||||||
|
|
||||||
.classification-details__service-and-priority {
|
.classification-details__service-and-priority {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
width: 100%;
|
||||||
|
}
|
||||||
|
.classification-details__service {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.classification-details__priority {
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.classification-details__mat-form-field {
|
.classification-details__mat-form-field {
|
||||||
width: 70%;
|
width: 100%;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import { MatInputModule } from '@angular/material/input';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
|
|
||||||
@Component({ selector: 'taskana-shared-field-error-display', template: '' })
|
@Component({ selector: 'taskana-shared-field-error-display', template: '' })
|
||||||
class FieldErrorDisplayStub {
|
class FieldErrorDisplayStub {
|
||||||
|
|
@ -120,6 +121,7 @@ describe('ClassificationDetailsComponent', () => {
|
||||||
NgxsModule.forRoot([ClassificationState, EngineConfigurationState]),
|
NgxsModule.forRoot([ClassificationState, EngineConfigurationState]),
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
|
MatToolbarModule,
|
||||||
MatDividerModule,
|
MatDividerModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
MatInputModule,
|
MatInputModule,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
.classification-list {
|
.classification-list {
|
||||||
height: calc(100vh - 55px);
|
height: calc(100vh - 55px);
|
||||||
width: 500px;
|
|
||||||
}
|
}
|
||||||
.classification-list__action-toolbar {
|
.classification-list__action-toolbar {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
.classification-overview {
|
.classification-overview {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
taskana-administration-classification-details {
|
taskana-administration-classification-list {
|
||||||
width: 100%;
|
width: 500px;
|
||||||
|
}
|
||||||
|
taskana-administration-classification-details {
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
@import 'src/theme/_colors.scss';
|
@import 'src/theme/_colors.scss';
|
||||||
|
|
||||||
.workbasket-details {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100vh - 100px);
|
|
||||||
}
|
|
||||||
.workbasket-details__toolbar {
|
.workbasket-details__toolbar {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 16px 36px 12px 24px;
|
padding: 16px 36px 12px 24px;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<div id="dual-list-Left" class="distribution-targets-list">
|
||||||
|
<mat-toolbar>
|
||||||
|
<span class="distribution-targets-list__header">{{header}}</span>
|
||||||
|
|
||||||
|
<button mat-flat-button class="distribution-targets-list__action-button" (click)="changeToolbarState(!toolbarState)"
|
||||||
|
>
|
||||||
|
<span *ngIf="!toolbarState">
|
||||||
|
Display filter
|
||||||
|
<mat-icon class="button-icon">filter_list</mat-icon>
|
||||||
|
</span>
|
||||||
|
<span *ngIf="toolbarState">
|
||||||
|
Hide filter
|
||||||
|
<mat-icon class="button-icon">keyboard_arrow_up</mat-icon>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span style="flex: 1 1 auto"> </span>
|
||||||
|
|
||||||
|
<button mat-flat-button class="distribution-targets-list__action-button" (click)="selectAll(allSelected);">
|
||||||
|
<mat-icon class="button-icon" *ngIf="!allSelected" matTooltip="Deselect all items">check_box</mat-icon>
|
||||||
|
<mat-icon class="button-icon" *ngIf="allSelected" matTooltip="Select all items">check_box_outline_blank</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-toolbar>
|
||||||
|
<taskana-shared-filter *ngIf="toolbarState" (performFilter)="performAvailableFilter($event)"
|
||||||
|
component="distribution-target" (inputComponent)="setComponent($event)"></taskana-shared-filter>
|
||||||
|
|
||||||
|
<!-- WORKBASKET LIST -->
|
||||||
|
<div class="distribution-targets-list__list" infiniteScroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="50" (scrolled)="onScroll()"
|
||||||
|
[scrollWindow]="false" *ngIf="distributionTargets?.length > 0">
|
||||||
|
<mat-selection-list #workbasket [multiple]="true">
|
||||||
|
<mat-list-option class="workbasket-distribution-targets__workbaskets-item"
|
||||||
|
*ngFor="let workbasket of distributionTargets | selectWorkbaskets: distributionTargetsSelected: side"
|
||||||
|
[selected]="workbasket.selected" type="text"
|
||||||
|
(click)="workbasket.selected = !workbasket.selected"
|
||||||
|
[value]="workbasket.workbasketId">
|
||||||
|
<div class="distribution-targets-list__item-wrapper">
|
||||||
|
|
||||||
|
<div class="distribution-targets-list__item-icon">
|
||||||
|
<taskana-administration-icon-type [type]="workbasket.type" size="large" tooltip="true"></taskana-administration-icon-type>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="distribution-targets-list__item-info">
|
||||||
|
<p>
|
||||||
|
<b>{{workbasket.name}}</b>, <i>{{workbasket.key}} </i>
|
||||||
|
</p>
|
||||||
|
<p>{{workbasket.description}} </p>
|
||||||
|
<p>{{workbasket.owner}} </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="workbaskets-item__marked" *ngIf="workbasket.markedForDeletion">
|
||||||
|
<span title="Marked for deletion" matTooltip="Marked for deletion"
|
||||||
|
class="material-icons md-20 {{workbasket.workbasketId === selectedId ? 'white': 'red' }} ">error</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
</mat-list-option>
|
||||||
|
</mat-selection-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="distribution-targets-list__empty-list" *ngIf="distributionTargets?.length == 0">
|
||||||
|
There is currently no distributed workbasket
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
|
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 { Filter } from '../../../shared/models/filter';
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
import { ICONTYPES } from '../../../shared/models/icon-types';
|
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 { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { workbasketReadStateMock } from '../../../shared/store/mock-data/mock-store';
|
import { workbasketReadStateMock } from '../../../shared/store/mock-data/mock-store';
|
||||||
import { Side } from '../workbasket-distribution-targets/workbasket-distribution-targets.component';
|
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: '' })
|
@Component({ selector: 'taskana-shared-filter', template: '' })
|
||||||
class FilterStub {
|
class FilterStub {
|
||||||
|
|
@ -25,33 +28,39 @@ class IconTypeStub {
|
||||||
@Input() text: string;
|
@Input() text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('WorkbasketDualListComponent', () => {
|
describe('WorkbasketDistributionTargetsListComponent', () => {
|
||||||
let fixture: ComponentFixture<WorkbasketDualListComponent>;
|
let fixture: ComponentFixture<WorkbasketDistributionTargetsListComponent>;
|
||||||
let debugElement: DebugElement;
|
let debugElement: DebugElement;
|
||||||
let component: WorkbasketDualListComponent;
|
let component: WorkbasketDistributionTargetsListComponent;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [InfiniteScrollModule, BrowserAnimationsModule],
|
imports: [MatIconModule, MatToolbarModule, MatListModule, InfiniteScrollModule, BrowserAnimationsModule],
|
||||||
declarations: [WorkbasketDualListComponent, FilterStub, SpinnerStub, IconTypeStub, SelectWorkBasketPipe],
|
declarations: [
|
||||||
|
WorkbasketDistributionTargetsListComponent,
|
||||||
|
FilterStub,
|
||||||
|
SpinnerStub,
|
||||||
|
IconTypeStub,
|
||||||
|
SelectWorkBasketPipe
|
||||||
|
],
|
||||||
providers: []
|
providers: []
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(WorkbasketDualListComponent);
|
fixture = TestBed.createComponent(WorkbasketDistributionTargetsListComponent);
|
||||||
debugElement = fixture.debugElement;
|
debugElement = fixture.debugElement;
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.distributionTargets = workbasketReadStateMock.paginatedWorkbasketsSummary.workbaskets;
|
component.distributionTargets = workbasketReadStateMock.paginatedWorkbasketsSummary.workbaskets;
|
||||||
component.distributionTargetsSelected = [];
|
component.distributionTargetsSelected = [];
|
||||||
component.side = Side.LEFT;
|
component.side = Side.AVAILABLE;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should create component', () => {
|
it('should create component', () => {
|
||||||
expect(component).toBeTruthy();
|
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();
|
fixture.detectChanges();
|
||||||
expect(component.sideNumber).toBe(0);
|
expect(component.side).toBe(Side.AVAILABLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select all distribution targets', () => {
|
it('should select all distribution targets', () => {
|
||||||
|
|
@ -67,13 +76,6 @@ describe('WorkbasketDualListComponent', () => {
|
||||||
expect(scrollingEmitSpy).toHaveBeenCalledWith(component.side);
|
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', () => {
|
it('should change toolbar state', () => {
|
||||||
expect(component.toolbarState).toBe(false);
|
expect(component.toolbarState).toBe(false);
|
||||||
component.changeToolbarState(true);
|
component.changeToolbarState(true);
|
||||||
|
|
@ -83,7 +85,7 @@ describe('WorkbasketDualListComponent', () => {
|
||||||
it('should display all available workbaskets', () => {
|
it('should display all available workbaskets', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const distributionTargetList = debugElement.nativeElement.getElementsByClassName(
|
const distributionTargetList = debugElement.nativeElement.getElementsByClassName(
|
||||||
'workbasket-list__distribution-targets'
|
'workbasket-distribution-targets__workbaskets-item'
|
||||||
);
|
);
|
||||||
expect(distributionTargetList).toHaveLength(5);
|
expect(distributionTargetList).toHaveLength(5);
|
||||||
});
|
});
|
||||||
|
|
@ -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<Side>();
|
||||||
|
@Input() allSelected;
|
||||||
|
@Output() allSelectedChange = new EventEmitter<boolean>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,77 +1,111 @@
|
||||||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
<div *ngIf="workbasket" id="wb-information" class="workbasket-distribution-targets">
|
||||||
<!-- ACTION TOOLBAR-->
|
<mat-toolbar class="distribution-targets-list__action-toolbar" >
|
||||||
<!--
|
<button mat-flat-button class="distribution-targets-list__action-button distribution-targets-list__toggle-view-button"
|
||||||
<div class="panel-heading">
|
*ngIf="!sideBySide" (click)="toggleSideBySideView()">
|
||||||
<div class="pull-right btn-group">
|
Display side-by-side
|
||||||
<button type="button" (click)="onSave()" [disabled]="action === 'COPY'" data-toggle="tooltip" title="Save"
|
<mat-icon class="distribution-targets-list__button-icon">view_week</mat-icon>
|
||||||
class="btn btn-default btn-primary">
|
|
||||||
<span class="material-icons md-20">save</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button type="button" (click)="onClear()" data-toggle="tooltip" title="Undo Changes"
|
<button mat-flat-button class="distribution-targets-list__action-button distribution-targets-list__toggle-view-button"
|
||||||
class="btn btn-default">
|
*ngIf="sideBySide" (click)="toggleSideBySideView()">
|
||||||
<span class="material-icons md-20 blue">undo</span>
|
Display in single-view
|
||||||
|
<mat-icon class="distribution-targets-list__button-icon">view_agenda</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- SIDE BY SIDE VIEW BUTTONS -->
|
||||||
|
<div class="distribution-targets-list__action-buttons" *ngIf="sideBySide">
|
||||||
|
<div class="distribution-targets-list__action-buttons--selected"
|
||||||
|
style="justify-content: flex-end; margin-right: 2%">
|
||||||
|
<button mat-flat-button color="warn"
|
||||||
|
class="distribution-targets-list__action-button distribution-targets-list-dialog__remove-button"
|
||||||
|
(click)="moveDistributionTargets(side.SELECTED)">
|
||||||
|
Remove selected distribution target
|
||||||
|
<mat-icon>remove</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="panel-header">{{workbasket.name}}
|
<span style="flex-grow: 1"> </span>
|
||||||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
<div class="distribution-targets-list__action-buttons--chooser"
|
||||||
</h4>
|
style="justify-content: flex-end;">
|
||||||
|
<button mat-flat-button color="accent"
|
||||||
|
class="distribution-targets-list__action-button distribution-targets-list-dialog__add-button"
|
||||||
|
(click)="moveDistributionTargets(side.AVAILABLE)">
|
||||||
|
Add selected distribution targets
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
-->
|
</div>
|
||||||
|
|
||||||
|
<!-- SINGLE VIEW BUTTONS WHEN DISPLAYING SELECTED DISTRIBUTION TARGETS -->
|
||||||
|
<div class="distribution-targets-list__action-buttons distribution-targets-list__action-buttons--selected"
|
||||||
|
*ngIf="!displayingDistributionTargetsPicker && !sideBySide">
|
||||||
|
<button mat-flat-button color="warn"
|
||||||
|
class="distribution-targets-list__action-button distribution-targets-list-dialog__remove-button"
|
||||||
|
(click)="moveDistributionTargets(side.SELECTED)">
|
||||||
|
Remove selected distribution target
|
||||||
|
<mat-icon>remove</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span style="flex: 1 1 auto"> </span>
|
||||||
|
|
||||||
|
<button mat-stroked-button
|
||||||
|
class="distribution-targets-list__action-button distribution-targets-list-dialog__display-button"
|
||||||
|
(click)="toggleDistributionTargetsPicker()">
|
||||||
|
Display available distribution targets
|
||||||
|
<mat-icon>launch</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SINGLE VIEW BUTTONS WHEN CHOOSING DISTRIBUTION TARGETS -->
|
||||||
|
<div class="distribution-targets-list__action-buttons distribution-targets-list__action-buttons--chooser"
|
||||||
|
*ngIf="displayingDistributionTargetsPicker && !sideBySide">
|
||||||
|
<button mat-flat-button color="accent"
|
||||||
|
class="distribution-targets-list__action-button distribution-targets-list-dialog__add-button"
|
||||||
|
(click)="moveDistributionTargets(side.AVAILABLE)">
|
||||||
|
Add selected distribution targets
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span style="flex: 1 1 auto"> </span>
|
||||||
|
|
||||||
|
<button mat-flat-button color="warn" class="distribution-targets-list-dialog__check-button"
|
||||||
|
(click)="toggleDistributionTargetsPicker()">
|
||||||
|
Close selection
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-toolbar>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="distribution-targets-list__lists"
|
||||||
|
[ngClass]="sideBySide ? 'distribution-targets-list__lists--side' : 'distribution-targets-list__lists--single'">
|
||||||
|
|
||||||
<!-- DISTRIBUTION TABLE-->
|
<!-- DISTRIBUTION TABLE-->
|
||||||
<div #panelBody class="panel-body">
|
<taskana-administration-workbasket-distribution-targets-list
|
||||||
|
[ngClass]="sideBySide ? 'distribution-targets-list__lists--left-side' : ''"
|
||||||
<!-- DISTRIBUTION LEFT LIST -->
|
header="Selected distribution targets"
|
||||||
<div class="dual-list list-left col-xs-12 col-md-5-6 container">
|
|
||||||
<taskana-administration-workbasket-dual-list #dualListLeft id="dual-list-Left" header="Available distribution targets"
|
|
||||||
[distributionTargets]="distributionTargetsLeft"
|
|
||||||
[distributionTargetsSelected]="distributionTargetsSelected"
|
|
||||||
(performDualListFilter)="performFilter($event)"
|
|
||||||
(scrolling)="onScroll($event)"
|
|
||||||
[side]="side.LEFT"
|
|
||||||
[requestInProgress]="requestInProgressLeft"
|
|
||||||
[loadingItems]="loadingItems"
|
|
||||||
[(allSelected)]="selectAllLeft"></taskana-administration-workbasket-dual-list>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- DISTRIBUTION ACTION BUTTONS -->
|
|
||||||
<div class="hidden-xs hidden-sm col-md-1 list-arrows text-center button-margin-top">
|
|
||||||
<button (click)="moveDistributionTargets(side.LEFT)"
|
|
||||||
[disabled]="requestInProgressLeft || requestInProgressRight"
|
|
||||||
class="btn btn-default move-right" data-toggle="tooltip"
|
|
||||||
title="Move to selected distribution targets">
|
|
||||||
<span class="material-icons md-20 blue">chevron_right</span>
|
|
||||||
</button>
|
|
||||||
<button (click)="moveDistributionTargets(side.RIGHT)"
|
|
||||||
[disabled]="requestInProgressLeft || requestInProgressRight"
|
|
||||||
class="btn btn-default move-left" data-toggle="tooltip"
|
|
||||||
title="Move to available distribution targets">
|
|
||||||
<span class="material-icons md-20 blue">chevron_left</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="hidden visible-xs visible-sm col-xs-12 list-arrows text-center">
|
|
||||||
<button (click)="moveDistributionTargets(side.LEFT)"
|
|
||||||
[disabled]="requestInProgressLeft || requestInProgressRight"
|
|
||||||
class="btn btn-default move-down" data-toggle="tooltip"
|
|
||||||
title="Move to selected distribution targets">
|
|
||||||
<span class="material-icons md-20 blue">expand_more</span>
|
|
||||||
</button>
|
|
||||||
<button (click)="moveDistributionTargets(side.RIGHT)"
|
|
||||||
[disabled]="requestInProgressLeft || requestInProgressRight"
|
|
||||||
class="btn btn-default move-up" data-toggle="tooltip"
|
|
||||||
title="Move to available distribution targets">
|
|
||||||
<span class="material-icons md-20 blue">expand_less</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- DISTRIBUTION RIGHT LIST -->
|
|
||||||
<div class="dual-list list-right col-xs-12 col-md-5-6 container">
|
|
||||||
<taskana-administration-workbasket-dual-list #dualListRight id="dual-list-right" header="Selected distribution targets"
|
|
||||||
[distributionTargets]="distributionTargetsRight"
|
[distributionTargets]="distributionTargetsRight"
|
||||||
[distributionTargetsSelected]="distributionTargetsSelected"
|
[distributionTargetsSelected]="distributionTargetsSelected"
|
||||||
(performDualListFilter)="performFilter($event)"
|
(performDualListFilter)="performFilter($event)"
|
||||||
[requestInProgress]="requestInProgressRight"
|
[side]="side.SELECTED"
|
||||||
[side]="side.RIGHT"
|
(scrolling)="onScroll()"
|
||||||
[(allSelected)]="selectAllRight"></taskana-administration-workbasket-dual-list>
|
[loadingItems]="loadingItems"
|
||||||
</div>
|
[(allSelected)]="selectAllLeft"
|
||||||
|
[hidden]="displayingDistributionTargetsPicker && !sideBySide"
|
||||||
|
>
|
||||||
|
|
||||||
|
</taskana-administration-workbasket-distribution-targets-list>
|
||||||
|
|
||||||
|
<taskana-administration-workbasket-distribution-targets-list
|
||||||
|
header="Available distribution targets"
|
||||||
|
[distributionTargets]="availableDistributionTargets"
|
||||||
|
[side]="side.AVAILABLE"
|
||||||
|
[distributionTargetsSelected]="distributionTargetsSelected"
|
||||||
|
(performDualListFilter)="performFilter($event)"
|
||||||
|
(scrolling)="onScroll()"
|
||||||
|
[loadingItems]="loadingItems"
|
||||||
|
[(allSelected)]="selectAllRight"
|
||||||
|
*ngIf="displayingDistributionTargetsPicker"
|
||||||
|
>
|
||||||
|
|
||||||
|
</taskana-administration-workbasket-distribution-targets-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,27 @@
|
||||||
.button-margin-top {
|
@import 'src/theme/_colors.scss';
|
||||||
margin-top: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-arrows > button {
|
.distribution-targets-list {
|
||||||
margin: 10px 0px;
|
&__action-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
&__action-button {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
&__button-icon {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
&__lists {
|
||||||
|
padding: 0 16px 16px 16px;
|
||||||
|
background-color: $light-grey;
|
||||||
|
&--side {
|
||||||
|
display: flex;
|
||||||
|
taskana-administration-workbasket-distribution-targets-list {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&--left-side {
|
||||||
|
margin-right: 1%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-md-5-6 {
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
width: 45.82%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { Side, WorkbasketDistributionTargetsComponent } from './workbasket-distribution-targets.component';
|
||||||
|
import { WorkbasketSummary } from '../../../shared/models/workbasket-summary';
|
||||||
|
import { Filter } from '../../../shared/models/filter';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { WorkbasketService } from '../../../shared/services/workbasket/workbasket.service';
|
||||||
|
import { SavingWorkbasketService } from '../../services/saving-workbaskets.service';
|
||||||
|
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||||
|
import { Actions, NgxsModule, Store } from '@ngxs/store';
|
||||||
|
import { WorkbasketState } from '../../../shared/store/workbasket-store/workbasket.state';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import {
|
||||||
|
engineConfigurationMock,
|
||||||
|
selectedWorkbasketMock,
|
||||||
|
workbasketReadStateMock
|
||||||
|
} from '../../../shared/store/mock-data/mock-store';
|
||||||
|
|
||||||
|
const routeParamsMock = { id: 'workbasket' };
|
||||||
|
const activatedRouteMock = {
|
||||||
|
firstChild: {
|
||||||
|
params: of(routeParamsMock)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@Component({ selector: 'taskana-administration-workbasket-distribution-targets-list', template: '' })
|
||||||
|
class WorkbasketDistributionTargetsListStub {
|
||||||
|
@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<Side>();
|
||||||
|
@Input() allSelected;
|
||||||
|
@Output() allSelectedChange = new EventEmitter<boolean>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const workbasketServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<WorkbasketService> => ({
|
||||||
|
getWorkBasketsSummary: jest.fn().mockReturnValue(of()),
|
||||||
|
getWorkBasketsDistributionTargets: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const savingWorkbasketServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<SavingWorkbasketService> => ({
|
||||||
|
triggeredDistributionTargetsSaving: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const notificationsServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<NotificationService> => ({
|
||||||
|
showToast: jest.fn().mockReturnValue(true)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const requestInProgressServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<RequestInProgressService> => ({
|
||||||
|
setRequestInProgress: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('WorkbasketDistributionTargetsComponent', () => {
|
||||||
|
let fixture: ComponentFixture<WorkbasketDistributionTargetsComponent>;
|
||||||
|
let debugElement: DebugElement;
|
||||||
|
let component: WorkbasketDistributionTargetsComponent;
|
||||||
|
let store: Store;
|
||||||
|
let actions$: Observable<any>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
MatIconModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatButtonModule,
|
||||||
|
NgxsModule.forRoot([WorkbasketState])
|
||||||
|
],
|
||||||
|
declarations: [WorkbasketDistributionTargetsComponent, WorkbasketDistributionTargetsListStub],
|
||||||
|
providers: [
|
||||||
|
{ provide: WorkbasketService, useClass: workbasketServiceSpy },
|
||||||
|
{ provide: SavingWorkbasketService, useClass: savingWorkbasketServiceSpy },
|
||||||
|
{ provide: NotificationService, useClass: notificationsServiceSpy },
|
||||||
|
{ provide: ActivatedRoute, useValue: activatedRouteMock },
|
||||||
|
{ provide: RequestInProgressService, useClass: requestInProgressServiceSpy }
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(WorkbasketDistributionTargetsComponent);
|
||||||
|
debugElement = fixture.debugElement;
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
store = TestBed.inject(Store);
|
||||||
|
actions$ = TestBed.inject(Actions);
|
||||||
|
store.reset({
|
||||||
|
...store.snapshot(),
|
||||||
|
engineConfiguration: engineConfigurationMock,
|
||||||
|
workbasket: workbasketReadStateMock
|
||||||
|
});
|
||||||
|
component.workbasket = selectedWorkbasketMock;
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create component', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display side-by-side view by default', () => {
|
||||||
|
expect(component.sideBySide).toBe(true);
|
||||||
|
expect(debugElement.nativeElement.querySelector('.distribution-targets-list__lists--side')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display single view when toggle view button is clicked', () => {
|
||||||
|
const toggleViewButton = debugElement.nativeElement.querySelector('.distribution-targets-list__toggle-view-button');
|
||||||
|
expect(toggleViewButton).toBeTruthy();
|
||||||
|
toggleViewButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.sideBySide).toBe(false);
|
||||||
|
expect(debugElement.nativeElement.querySelector('.distribution-targets-list__lists--side')).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get available and selected distribution targets', () => {
|
||||||
|
component.getWorkbaskets();
|
||||||
|
expect(component.availableDistributionTargets).toHaveLength(8); //mock-data has 8 entries
|
||||||
|
expect(component.distributionTargetsSelected).toHaveLength(3); //mock-data has 3 entries
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit filter model and side when performing filter', () => {
|
||||||
|
const performDualListFilterSpy = jest.spyOn(component, 'performFilter');
|
||||||
|
const filterModelMock: Filter = { filterParams: { name: '', description: '', owner: '', type: '', key: '' } };
|
||||||
|
component.performFilter({ filterBy: filterModelMock, side: component.side });
|
||||||
|
expect(performDualListFilterSpy).toHaveBeenCalledWith({ filterBy: filterModelMock, side: component.side });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should move distribution targets to selected list', () => {
|
||||||
|
component.availableDistributionTargets[0]['selected'] = true; // select first item in available array
|
||||||
|
component.distributionTargetsRight = component.distributionTargetsSelected;
|
||||||
|
component.moveDistributionTargets(Side.AVAILABLE);
|
||||||
|
expect(component.distributionTargetsSelected).toHaveLength(4); // mock-data only has 3
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset distribution targets to last state when undo is called', () => {
|
||||||
|
component.distributionTargetsClone = component.availableDistributionTargets;
|
||||||
|
component.distributionTargetsSelectedClone = component.distributionTargetsSelected;
|
||||||
|
component.availableDistributionTargets[0]['selected'] = true; // select first item in available array
|
||||||
|
component.distributionTargetsRight = component.distributionTargetsSelected;
|
||||||
|
component.moveDistributionTargets(Side.AVAILABLE);
|
||||||
|
expect(component.distributionTargetsSelected).toHaveLength(4); // mock-data only has 3
|
||||||
|
|
||||||
|
component.onClear();
|
||||||
|
expect(component.distributionTargetsSelected).toHaveLength(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core';
|
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { Observable, Subject } from 'rxjs';
|
import { Observable, Subject } from 'rxjs';
|
||||||
|
|
||||||
import { Workbasket } from 'app/shared/models/workbasket';
|
import { Workbasket } from 'app/shared/models/workbasket';
|
||||||
|
|
@ -6,53 +6,50 @@ import { WorkbasketSummary } from 'app/shared/models/workbasket-summary';
|
||||||
import { WorkbasketSummaryRepresentation } from 'app/shared/models/workbasket-summary-representation';
|
import { WorkbasketSummaryRepresentation } from 'app/shared/models/workbasket-summary-representation';
|
||||||
import { WorkbasketDistributionTargets } from 'app/shared/models/workbasket-distribution-targets';
|
import { WorkbasketDistributionTargets } from 'app/shared/models/workbasket-distribution-targets';
|
||||||
import { ACTION } from 'app/shared/models/action';
|
import { ACTION } from 'app/shared/models/action';
|
||||||
|
|
||||||
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service';
|
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service';
|
||||||
import { SavingWorkbasketService, SavingInformation } from 'app/administration/services/saving-workbaskets.service';
|
import { SavingWorkbasketService, SavingInformation } from 'app/administration/services/saving-workbaskets.service';
|
||||||
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
|
|
||||||
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
|
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
|
||||||
import { Page } from 'app/shared/models/page';
|
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 { Select, Store } from '@ngxs/store';
|
||||||
import { take, takeUntil } from 'rxjs/operators';
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
|
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
|
||||||
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||||
import {
|
import {
|
||||||
|
GetAvailableDistributionTargets,
|
||||||
GetWorkbasketDistributionTargets,
|
GetWorkbasketDistributionTargets,
|
||||||
GetWorkbasketsSummary,
|
|
||||||
UpdateWorkbasketDistributionTargets
|
UpdateWorkbasketDistributionTargets
|
||||||
} from '../../../shared/store/workbasket-store/workbasket.actions';
|
} from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||||
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
||||||
import { WorkbasketStateModel } from '../../../shared/store/workbasket-store/workbasket.state';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { ButtonAction } from '../../models/button-action';
|
||||||
|
|
||||||
export enum Side {
|
export enum Side {
|
||||||
LEFT,
|
AVAILABLE,
|
||||||
RIGHT
|
SELECTED
|
||||||
}
|
}
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'taskana-administration-workbasket-distribution-targets',
|
selector: 'taskana-administration-workbasket-distribution-targets',
|
||||||
templateUrl: './workbasket-distribution-targets.component.html',
|
templateUrl: './workbasket-distribution-targets.component.html',
|
||||||
styleUrls: ['./workbasket-distribution-targets.component.scss']
|
styleUrls: ['./workbasket-distribution-targets.component.scss']
|
||||||
})
|
})
|
||||||
export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges, OnDestroy {
|
export class WorkbasketDistributionTargetsComponent implements OnInit, OnDestroy {
|
||||||
@Input()
|
@Input()
|
||||||
workbasket: Workbasket;
|
workbasket: Workbasket;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
action: ACTION;
|
action: ACTION;
|
||||||
|
|
||||||
badgeMessage = '';
|
toolbarState = false;
|
||||||
|
sideBySide = true;
|
||||||
|
displayingDistributionTargetsPicker = true;
|
||||||
|
|
||||||
distributionTargetsSelectedResource: WorkbasketDistributionTargets;
|
distributionTargetsSelectedResource: WorkbasketDistributionTargets;
|
||||||
distributionTargetsLeft: Array<WorkbasketSummary> = [];
|
availableDistributionTargets: Array<WorkbasketSummary> = [];
|
||||||
distributionTargetsRight: Array<WorkbasketSummary> = [];
|
distributionTargetsRight: Array<WorkbasketSummary> = [];
|
||||||
distributionTargetsSelected: Array<WorkbasketSummary>;
|
distributionTargetsSelected: Array<WorkbasketSummary>;
|
||||||
distributionTargetsClone: Array<WorkbasketSummary>;
|
distributionTargetsClone: Array<WorkbasketSummary>;
|
||||||
distributionTargetsSelectedClone: Array<WorkbasketSummary>;
|
distributionTargetsSelectedClone: Array<WorkbasketSummary>;
|
||||||
|
|
||||||
requestInProgressLeft = false;
|
|
||||||
requestInProgressRight = false;
|
|
||||||
loadingItems = false;
|
loadingItems = false;
|
||||||
side = Side;
|
side = Side;
|
||||||
private initialized = false;
|
private initialized = false;
|
||||||
|
|
@ -61,56 +58,37 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
||||||
selectAllLeft = false;
|
selectAllLeft = false;
|
||||||
selectAllRight = false;
|
selectAllRight = false;
|
||||||
|
|
||||||
@ViewChild('panelBody')
|
|
||||||
panelBody: ElementRef;
|
|
||||||
|
|
||||||
@Select(WorkbasketSelectors.workbasketDistributionTargets)
|
@Select(WorkbasketSelectors.workbasketDistributionTargets)
|
||||||
workbasketDistributionTargets$: Observable<WorkbasketDistributionTargets>;
|
workbasketDistributionTargets$: Observable<WorkbasketDistributionTargets>;
|
||||||
|
|
||||||
|
@Select(WorkbasketSelectors.availableDistributionTargets)
|
||||||
|
availableDistributionTargets$: Observable<WorkbasketSummary[]>;
|
||||||
|
|
||||||
|
@Select(WorkbasketSelectors.buttonAction)
|
||||||
|
buttonAction$: Observable<ButtonAction>;
|
||||||
|
|
||||||
destroy$ = new Subject<void>();
|
destroy$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private workbasketService: WorkbasketService,
|
private workbasketService: WorkbasketService,
|
||||||
private savingWorkbaskets: SavingWorkbasketService,
|
private savingWorkbaskets: SavingWorkbasketService,
|
||||||
private requestInProgressService: RequestInProgressService,
|
|
||||||
private orientationService: OrientationService,
|
|
||||||
private notificationsService: NotificationService,
|
private notificationsService: NotificationService,
|
||||||
private store: Store
|
private store: Store,
|
||||||
|
public matDialog: MatDialog
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rework with modification based on old components,
|
||||||
|
* would be ideal to completely redo whole components using drag and drop angular components and clearer logics
|
||||||
|
*/
|
||||||
ngOnInit() {
|
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 (changes.action) {
|
|
||||||
this.setBadge();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onScroll(side: Side) {
|
|
||||||
if (side === this.side.LEFT && this.page.totalPages > TaskanaQueryParameters.page) {
|
|
||||||
this.loadingItems = true;
|
|
||||||
this.getNextPage(side);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.onRequest();
|
|
||||||
if (!this.workbasket._links.distributionTargets) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.store.dispatch(new GetWorkbasketDistributionTargets(this.workbasket._links.distributionTargets.href));
|
this.store.dispatch(new GetWorkbasketDistributionTargets(this.workbasket._links.distributionTargets.href));
|
||||||
|
this.store.dispatch(new GetAvailableDistributionTargets());
|
||||||
|
|
||||||
|
this.availableDistributionTargets$.pipe(takeUntil(this.destroy$)).subscribe((availableDistributionTargets) => {
|
||||||
|
this.availableDistributionTargets = availableDistributionTargets;
|
||||||
|
});
|
||||||
|
|
||||||
this.savingWorkbaskets
|
this.savingWorkbaskets
|
||||||
.triggeredDistributionTargetsSaving()
|
.triggeredDistributionTargetsSaving()
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
|
@ -121,13 +99,45 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.orientationService
|
this.workbasketDistributionTargets$.subscribe((workbasketDistributionTargets) => {
|
||||||
.getOrientation()
|
if (typeof workbasketDistributionTargets !== 'undefined') {
|
||||||
.pipe(takeUntil(this.destroy$))
|
this.distributionTargetsSelectedResource = { ...workbasketDistributionTargets };
|
||||||
.subscribe(() => {
|
this.distributionTargetsSelected = this.distributionTargetsSelectedResource.distributionTargets;
|
||||||
this.calculateNumberItemsList();
|
this.distributionTargetsSelectedClone = { ...this.distributionTargetsSelected };
|
||||||
|
TaskanaQueryParameters.page = 1;
|
||||||
this.getWorkbaskets();
|
this.getWorkbaskets();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
this.buttonAction$
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.pipe(filter((buttonAction) => typeof buttonAction !== 'undefined'))
|
||||||
|
.subscribe((button) => {
|
||||||
|
switch (button) {
|
||||||
|
case ButtonAction.SAVE:
|
||||||
|
this.onSave();
|
||||||
|
break;
|
||||||
|
case ButtonAction.UNDO:
|
||||||
|
this.onClear();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll() {
|
||||||
|
if (this.page.totalPages > TaskanaQueryParameters.page) {
|
||||||
|
this.loadingItems = true;
|
||||||
|
this.getNextPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeToolbarState(state: boolean) {
|
||||||
|
this.toolbarState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDistributionTargetsPicker() {
|
||||||
|
this.displayingDistributionTargetsPicker = !this.displayingDistributionTargetsPicker;
|
||||||
}
|
}
|
||||||
|
|
||||||
getWorkbaskets(side?: Side) {
|
getWorkbaskets(side?: Side) {
|
||||||
|
|
@ -136,34 +146,34 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
||||||
TaskanaQueryParameters.pageSize = this.cards + this.distributionTargetsSelected.length;
|
TaskanaQueryParameters.pageSize = this.cards + this.distributionTargetsSelected.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this into NGXS
|
|
||||||
this.workbasketService
|
this.workbasketService
|
||||||
.getWorkBasketsSummary(true)
|
.getWorkBasketsSummary(true)
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe((distributionTargetsAvailable: WorkbasketSummaryRepresentation) => {
|
.subscribe((distributionTargetsAvailable: WorkbasketSummaryRepresentation) => {
|
||||||
if (TaskanaQueryParameters.page === 1) {
|
if (TaskanaQueryParameters.page === 1) {
|
||||||
this.distributionTargetsLeft = [];
|
this.availableDistributionTargets = [];
|
||||||
this.page = distributionTargetsAvailable.page;
|
this.page = distributionTargetsAvailable.page;
|
||||||
}
|
}
|
||||||
if (side === this.side.LEFT) {
|
if (side === this.side.AVAILABLE) {
|
||||||
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
|
this.availableDistributionTargets.push(...distributionTargetsAvailable.workbaskets);
|
||||||
} else if (side === this.side.RIGHT) {
|
} else if (side === this.side.SELECTED) {
|
||||||
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
|
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
|
||||||
} else {
|
} else {
|
||||||
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
|
this.availableDistributionTargets.push(...distributionTargetsAvailable.workbaskets);
|
||||||
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
|
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
|
||||||
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable.workbaskets);
|
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable.workbaskets);
|
||||||
}
|
}
|
||||||
this.onRequest(true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNextPage(side?: Side) {
|
||||||
|
TaskanaQueryParameters.page += 1;
|
||||||
|
this.getWorkbaskets(side);
|
||||||
|
}
|
||||||
|
|
||||||
performFilter(dualListFilter: any) {
|
performFilter(dualListFilter: any) {
|
||||||
this.fillDistributionTargets(dualListFilter.side, undefined);
|
this.workbasketService
|
||||||
this.onRequest(false, dualListFilter.side);
|
.getWorkBasketsSummary(
|
||||||
this.store
|
|
||||||
.dispatch(
|
|
||||||
new GetWorkbasketsSummary(
|
|
||||||
true,
|
true,
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
|
|
@ -178,60 +188,41 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
||||||
'',
|
'',
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
)
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe((state: WorkbasketStateModel) => {
|
.subscribe((distributionTargetsAvailable: WorkbasketSummaryRepresentation) => {
|
||||||
this.fillDistributionTargets(dualListFilter.side, state.paginatedWorkbasketsSummary.workbaskets);
|
this.fillDistributionTargets(dualListFilter.side, []);
|
||||||
this.onRequest(true, dualListFilter.side);
|
|
||||||
|
if (TaskanaQueryParameters.page === 1) {
|
||||||
|
this.availableDistributionTargets = [];
|
||||||
|
this.page = distributionTargetsAvailable.page;
|
||||||
|
}
|
||||||
|
if (dualListFilter.side === this.side.AVAILABLE) {
|
||||||
|
this.availableDistributionTargets.push(...distributionTargetsAvailable.workbaskets);
|
||||||
|
} else if (dualListFilter.side === this.side.SELECTED) {
|
||||||
|
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
|
||||||
|
} else {
|
||||||
|
this.availableDistributionTargets.push(...distributionTargetsAvailable.workbaskets);
|
||||||
|
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
|
||||||
|
this.distributionTargetsClone = Object.assign([], distributionTargetsAvailable.workbaskets);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave() {
|
onSave() {
|
||||||
this.requestInProgressService.setRequestInProgress(true);
|
this.store.dispatch(
|
||||||
this.store
|
|
||||||
.dispatch(
|
|
||||||
new UpdateWorkbasketDistributionTargets(
|
new UpdateWorkbasketDistributionTargets(
|
||||||
this.distributionTargetsSelectedResource._links.self.href,
|
this.distributionTargetsSelectedResource._links.self.href,
|
||||||
this.getSeletedIds()
|
this.getSelectedIds()
|
||||||
)
|
)
|
||||||
)
|
|
||||||
.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<string, string>([['workbasketName', this.workbasket.name]])
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
this.notificationsService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_3, error);
|
|
||||||
this.requestInProgressService.setRequestInProgress(false);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveDistributionTargets(side: number) {
|
moveDistributionTargets(side: number) {
|
||||||
if (side === Side.LEFT) {
|
if (side === Side.AVAILABLE) {
|
||||||
const itemsLeft = this.distributionTargetsLeft.length;
|
const itemsLeft = this.availableDistributionTargets.length;
|
||||||
const itemsRight = this.distributionTargetsRight.length;
|
const itemsRight = this.distributionTargetsRight.length;
|
||||||
const itemsSelected = this.getSelectedItems(this.distributionTargetsLeft);
|
const itemsSelected = this.getSelectedItems(this.availableDistributionTargets);
|
||||||
this.distributionTargetsSelected = [...this.distributionTargetsSelected, ...itemsSelected];
|
this.distributionTargetsSelected = [...this.distributionTargetsSelected, ...itemsSelected];
|
||||||
this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected);
|
this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected);
|
||||||
if (
|
if (
|
||||||
|
|
@ -245,102 +236,52 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
||||||
const itemsSelected = this.getSelectedItems(this.distributionTargetsRight);
|
const itemsSelected = this.getSelectedItems(this.distributionTargetsRight);
|
||||||
this.distributionTargetsSelected = this.removeSelectedItems(this.distributionTargetsSelected, itemsSelected);
|
this.distributionTargetsSelected = this.removeSelectedItems(this.distributionTargetsSelected, itemsSelected);
|
||||||
this.distributionTargetsRight = this.removeSelectedItems(this.distributionTargetsRight, itemsSelected);
|
this.distributionTargetsRight = this.removeSelectedItems(this.distributionTargetsRight, itemsSelected);
|
||||||
this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected);
|
this.availableDistributionTargets = this.availableDistributionTargets.concat(itemsSelected);
|
||||||
this.unselectItems(itemsSelected);
|
this.unselectItems(itemsSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClear() {
|
onClear() {
|
||||||
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT);
|
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT);
|
||||||
this.distributionTargetsLeft = Object.assign([], this.distributionTargetsClone);
|
this.availableDistributionTargets = Object.assign([], this.distributionTargetsClone);
|
||||||
this.distributionTargetsRight = Object.assign([], this.distributionTargetsSelectedClone);
|
this.distributionTargetsRight = Object.assign([], this.distributionTargetsSelectedClone);
|
||||||
this.distributionTargetsSelected = 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[]) {
|
fillDistributionTargets(side: Side, workbaskets: WorkbasketSummary[]) {
|
||||||
this.distributionTargetsLeft = side === Side.LEFT ? workbaskets : this.distributionTargetsLeft;
|
this.availableDistributionTargets = side === Side.AVAILABLE ? workbaskets : this.availableDistributionTargets;
|
||||||
this.distributionTargetsRight = side === Side.RIGHT ? workbaskets : this.distributionTargetsRight;
|
this.distributionTargetsRight = side === Side.SELECTED ? 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}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedItems(originList: any): Array<any> {
|
getSelectedItems(originList: any): Array<any> {
|
||||||
return originList.filter((item: any) => item.selected === true);
|
return originList.filter((item: any) => item.selected === true);
|
||||||
}
|
}
|
||||||
|
|
||||||
unselectItems(originList: any): Array<any> {
|
unselectItems(originList: Array<any>): Array<any> {
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
return originList
|
||||||
for (const item of originList) {
|
.filter((item) => item.selected)
|
||||||
if (item.selected && item.selected === true) {
|
.map((item) => {
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
}
|
});
|
||||||
}
|
|
||||||
return originList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeSelectedItems(originList: any, selectedItemList) {
|
removeSelectedItems(originList, selectedItemList) {
|
||||||
|
const copyList = [...originList];
|
||||||
for (let index = originList.length - 1; index >= 0; index--) {
|
for (let index = originList.length - 1; index >= 0; index--) {
|
||||||
if (selectedItemList.some((itemToRemove) => originList[index].workbasketId === itemToRemove.workbasketId)) {
|
if (selectedItemList.some((itemToRemove) => originList[index].workbasketId === itemToRemove.workbasketId)) {
|
||||||
originList.splice(index, 1);
|
copyList.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return originList;
|
return copyList;
|
||||||
}
|
}
|
||||||
|
|
||||||
onRequest(finished: boolean = false, side?: Side) {
|
getSelectedIds(): Array<string> {
|
||||||
this.loadingItems = false;
|
return this.distributionTargetsSelected.map((distributionTarget) => distributionTarget.workbasketId);
|
||||||
const inProgress = !finished;
|
|
||||||
switch (side) {
|
|
||||||
case Side.LEFT:
|
|
||||||
this.requestInProgressLeft = inProgress;
|
|
||||||
break;
|
|
||||||
case Side.RIGHT:
|
|
||||||
this.requestInProgressRight = inProgress;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.requestInProgressLeft = inProgress;
|
|
||||||
this.requestInProgressRight = inProgress;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSeletedIds(): Array<string> {
|
toggleSideBySideView() {
|
||||||
const distributionTargetsSelelected: Array<string> = [];
|
this.sideBySide = !this.sideBySide;
|
||||||
this.distributionTargetsSelected.forEach((item) => {
|
this.displayingDistributionTargetsPicker = true; //always display picker when toggle from side-by-side to single
|
||||||
distributionTargetsSelelected.push(item.workbasketId);
|
|
||||||
});
|
|
||||||
return distributionTargetsSelelected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private uncheckSelectAll(side: number) {
|
|
||||||
if (side === Side.LEFT && this.selectAllLeft) {
|
|
||||||
this.selectAllLeft = false;
|
|
||||||
}
|
|
||||||
if (side === Side.RIGHT && this.selectAllRight) {
|
|
||||||
this.selectAllRight = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
<div id="dual-list-Left" class="dual-list list-left col-xs-12 col-md-5-6 container">
|
|
||||||
<!-- ACTION TOOLBAR -->
|
|
||||||
<div class="action-toolbar">
|
|
||||||
<div class="row header">
|
|
||||||
<div class="col-xs-2">
|
|
||||||
<button (click)="allSelected = !allSelected; selectAll(allSelected);"
|
|
||||||
class="btn btn-default btn-sm no-style" title="Select all">
|
|
||||||
<span class="material-icons md-20 blue ">{{allSelected ? 'check_box' : 'check_box_outline_blank'}}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-7">
|
|
||||||
<h5>{{header}}</h5>
|
|
||||||
</div>
|
|
||||||
<div class="pull-right">
|
|
||||||
<button class="btn btn-default btn-sm" type="button" id="collapsedMenufilterWb" aria-expanded="false"
|
|
||||||
(click)="changeToolbarState(!toolbarState)"
|
|
||||||
data-toggle="tooltip" title="Filter">
|
|
||||||
<span class="material-icons md-20 blue ">{{!toolbarState ? 'search' : 'expand_less'}}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div [@toggleDown]="toolbarState">
|
|
||||||
<taskana-shared-filter (performFilter)="performAvailableFilter($event)"></taskana-shared-filter>
|
|
||||||
</div>
|
|
||||||
<taskana-shared-spinner [isRunning]="requestInProgress" positionClass="centered-spinner"
|
|
||||||
class="floating"></taskana-shared-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- WORKBASKET LIST -->
|
|
||||||
<div class="workbasket-list" infiniteScroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="50" (scrolled)="onScroll()"
|
|
||||||
[scrollWindow]="false" class="infinite-scroll">
|
|
||||||
<ul class="list-group">
|
|
||||||
<li class="list-group-item workbasket-list__distribution-targets"
|
|
||||||
*ngFor="let distributionTarget of distributionTargets | selectWorkbaskets: distributionTargetsSelected: side"
|
|
||||||
[class.selected]="distributionTarget.selected" type="text"
|
|
||||||
(click)="distributionTarget.selected = !distributionTarget.selected">
|
|
||||||
<div class="row">
|
|
||||||
<dl class="col-xs-1">
|
|
||||||
<taskana-administration-icon-type [type]="distributionTarget.type"></taskana-administration-icon-type>
|
|
||||||
</dl>
|
|
||||||
<dl class="col-xs-10">
|
|
||||||
<dt>{{distributionTarget.name}},
|
|
||||||
<i>{{distributionTarget.key}} </i>
|
|
||||||
</dt>
|
|
||||||
<dd>{{distributionTarget.description}} </dd>
|
|
||||||
<dd>{{distributionTarget.owner}} </dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="list-group-item" *ngIf="loadingItems">
|
|
||||||
<taskana-shared-spinner [isRunning]="loadingItems" positionClass="centered-spinner"
|
|
||||||
class="floating"></taskana-shared-spinner>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
$selected-item: #e3f3f5;
|
|
||||||
|
|
||||||
.dual-list {
|
|
||||||
min-height: 250px;
|
|
||||||
padding: 0px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border: 1px solid #e3e3e3;
|
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
|
|
||||||
& .row {
|
|
||||||
padding: 0px 0px 0px 3px;
|
|
||||||
}
|
|
||||||
& .row:first {
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
& div.pull-right {
|
|
||||||
margin-right: 17px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .list-group {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
& > .list-group > li {
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
@media screen and (max-width: 991px) {
|
|
||||||
height: calc((100vh - 241px) / 2);
|
|
||||||
min-height: 120px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
max-height: calc(100vh - 194px);
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.infinite-scroll {
|
|
||||||
overflow-y: scroll;
|
|
||||||
height: calc(100vh - 233px);
|
|
||||||
@media screen and (max-width: 991px) {
|
|
||||||
height: calc((100vh - 315px) / 2);
|
|
||||||
min-height: 83px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin: 2px -15px 1px -15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul > li {
|
|
||||||
&:first-child.list-group-item.selected {
|
|
||||||
border-color: #ddd;
|
|
||||||
}
|
|
||||||
&.list-group-item.selected {
|
|
||||||
background-color: $selected-item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-left li {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.no-style {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row.list-group {
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group > li {
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a > label {
|
|
||||||
height: 2em;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
dd,
|
|
||||||
dt {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt > i {
|
|
||||||
font-weight: normal;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
li > div.row > dl {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.list-group-item:hover {
|
|
||||||
color: #555;
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
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 'app/shared/animations/expand.animation';
|
|
||||||
import { Side } from '../workbasket-distribution-targets/workbasket-distribution-targets.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'taskana-administration-workbasket-dual-list',
|
|
||||||
templateUrl: './workbasket-dual-list.component.html',
|
|
||||||
styleUrls: ['./workbasket-dual-list.component.scss'],
|
|
||||||
animations: [expandDown]
|
|
||||||
})
|
|
||||||
export class WorkbasketDualListComponent implements OnInit {
|
|
||||||
@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<Side>();
|
|
||||||
@Input() allSelected;
|
|
||||||
@Output() allSelectedChange = new EventEmitter<boolean>();
|
|
||||||
|
|
||||||
sideNumber = 0;
|
|
||||||
toolbarState = false;
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.sideNumber = this.side === Side.LEFT ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectAll(selected: boolean) {
|
|
||||||
this.distributionTargets.forEach((element: any) => {
|
|
||||||
element.selected = selected;
|
|
||||||
});
|
|
||||||
this.allSelectedChange.emit(this.allSelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
onScroll() {
|
|
||||||
this.scrolling.emit(this.side);
|
|
||||||
}
|
|
||||||
|
|
||||||
performAvailableFilter(filterModel: Filter) {
|
|
||||||
this.performDualListFilter.emit({ filterBy: filterModel, side: this.side });
|
|
||||||
}
|
|
||||||
|
|
||||||
changeToolbarState(state: boolean) {
|
|
||||||
this.toolbarState = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -26,10 +26,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.workbasket-information__mat-form-field {
|
.workbasket-information__mat-form-field {
|
||||||
width: 70%;
|
width: 100%;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
}
|
}
|
||||||
|
.workbasket-information__custom-fields {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,9 @@ const workbasketServiceMock = jest.fn().mockImplementation(
|
||||||
updateWorkbasket: jest.fn().mockReturnValue(of(true)),
|
updateWorkbasket: jest.fn().mockReturnValue(of(true)),
|
||||||
markWorkbasketForDeletion: jest.fn().mockReturnValue(of(true)),
|
markWorkbasketForDeletion: jest.fn().mockReturnValue(of(true)),
|
||||||
createWorkbasket: jest.fn().mockReturnValue(of({ ...selectedWorkbasketMock })),
|
createWorkbasket: jest.fn().mockReturnValue(of({ ...selectedWorkbasketMock })),
|
||||||
getWorkBasket: jest.fn().mockReturnValue(of({ ...selectedWorkbasketMock }))
|
getWorkBasket: jest.fn().mockReturnValue(of({ ...selectedWorkbasketMock })),
|
||||||
|
getWorkBasketAccessItems: jest.fn().mockReturnValue(of()),
|
||||||
|
getWorkBasketsDistributionTargets: jest.fn().mockReturnValue(of())
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,6 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<taskana-shared-filter *ngIf="showFilter" (performFilter)="filtering($event)"></taskana-shared-filter>
|
<taskana-shared-filter *ngIf="showFilter" (performFilter)="filtering($event)" component="workbasket-list" (inputComponent)="setComponent($event)"></taskana-shared-filter>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { WorkbasketListToolbarComponent } from './workbasket-list-toolbar.component';
|
import { WorkbasketListToolbarComponent } from './workbasket-list-toolbar.component';
|
||||||
import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { Actions, NgxsModule, ofActionDispatched, Store } from '@ngxs/store';
|
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 { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
import { WorkbasketState } from '../../../shared/store/workbasket-store/workbasket.state';
|
import { WorkbasketState } from '../../../shared/store/workbasket-store/workbasket.state';
|
||||||
import { WorkbasketService } from '../../../shared/services/workbasket/workbasket.service';
|
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 { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
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 getDomainFn = jest.fn().mockReturnValue(true);
|
||||||
const domainServiceMock = jest.fn().mockImplementation(
|
const domainServiceMock = jest.fn().mockImplementation(
|
||||||
|
|
@ -43,6 +44,12 @@ class FilterStub {
|
||||||
@Output() performFilter = new EventEmitter<Filter>();
|
@Output() performFilter = new EventEmitter<Filter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestInProgressServiceSpy = jest.fn().mockImplementation(
|
||||||
|
(): Partial<RequestInProgressService> => ({
|
||||||
|
setRequestInProgress: jest.fn().mockReturnValue(of())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
describe('WorkbasketListToolbarComponent', () => {
|
describe('WorkbasketListToolbarComponent', () => {
|
||||||
let fixture: ComponentFixture<WorkbasketListToolbarComponent>;
|
let fixture: ComponentFixture<WorkbasketListToolbarComponent>;
|
||||||
let debugElement: DebugElement;
|
let debugElement: DebugElement;
|
||||||
|
|
@ -62,7 +69,11 @@ describe('WorkbasketListToolbarComponent', () => {
|
||||||
MatDialogModule
|
MatDialogModule
|
||||||
],
|
],
|
||||||
declarations: [WorkbasketListToolbarComponent, ImportExportStub, SortStub, FilterStub],
|
declarations: [WorkbasketListToolbarComponent, ImportExportStub, SortStub, FilterStub],
|
||||||
providers: [{ provide: DomainService, useClass: domainServiceMock }, WorkbasketService]
|
providers: [
|
||||||
|
{ provide: DomainService, useClass: domainServiceMock },
|
||||||
|
{ provide: RequestInProgressService, useClass: requestInProgressServiceSpy },
|
||||||
|
WorkbasketService
|
||||||
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(WorkbasketListToolbarComponent);
|
fixture = TestBed.createComponent(WorkbasketListToolbarComponent);
|
||||||
|
|
@ -107,7 +118,7 @@ describe('WorkbasketListToolbarComponent', () => {
|
||||||
expect(sort).toMatchObject(mockSort);
|
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' };
|
const mockFilter: Filter = { filterParams: 'abc' };
|
||||||
let filterBy: Filter = { filterParams: 'abc' };
|
let filterBy: Filter = { filterParams: 'abc' };
|
||||||
component.performFilter.subscribe((filter: Filter) => {
|
component.performFilter.subscribe((filter: Filter) => {
|
||||||
|
|
@ -116,7 +127,7 @@ describe('WorkbasketListToolbarComponent', () => {
|
||||||
});
|
});
|
||||||
component.filtering(filterBy);
|
component.filtering(filterBy);
|
||||||
expect(filterBy).toMatchObject(mockFilter);
|
expect(filterBy).toMatchObject(mockFilter);
|
||||||
});
|
}));
|
||||||
|
|
||||||
/* HTML */
|
/* HTML */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
||||||
filterParams = { name: '', key: '', type: '', description: '', owner: '' };
|
filterParams = { name: '', key: '', type: '', description: '', owner: '' };
|
||||||
filterType = TaskanaType.WORKBASKETS;
|
filterType = TaskanaType.WORKBASKETS;
|
||||||
showFilter = false;
|
showFilter = false;
|
||||||
|
component = '';
|
||||||
|
|
||||||
@Select(WorkbasketSelectors.workbasketActiveAction)
|
@Select(WorkbasketSelectors.workbasketActiveAction)
|
||||||
workbasketActiveAction$: Observable<ACTION>;
|
workbasketActiveAction$: Observable<ACTION>;
|
||||||
|
|
@ -63,8 +64,14 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
filtering(filterBy: Filter) {
|
filtering(filterBy: Filter) {
|
||||||
|
if (this.component === 'workbasket-list') {
|
||||||
this.performFilter.emit(filterBy);
|
this.performFilter.emit(filterBy);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setComponent(component: string) {
|
||||||
|
this.component = component;
|
||||||
|
}
|
||||||
|
|
||||||
addWorkbasket() {
|
addWorkbasket() {
|
||||||
if (this.action !== ACTION.CREATE) {
|
if (this.action !== ACTION.CREATE) {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@
|
||||||
|
|
||||||
.workbasket-list {
|
.workbasket-list {
|
||||||
height: calc(100vh - 156px);
|
height: calc(100vh - 156px);
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
min-width: 500px;
|
|
||||||
}
|
}
|
||||||
.workbasket-list__workbaskets {
|
.workbasket-list__workbaskets {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|
|
||||||
|
|
@ -23,17 +23,20 @@ import { MatListModule } from '@angular/material/list';
|
||||||
import { DomainService } from '../../../shared/services/domain/domain.service';
|
import { DomainService } from '../../../shared/services/domain/domain.service';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service';
|
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 workbasketSavedTriggeredFn = jest.fn().mockReturnValue(of(1));
|
||||||
const workbasketSummaryFn = jest.fn().mockReturnValue(of({}));
|
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 getWorkbasketActionToolbarExpansionFn = jest.fn().mockReturnValue(of(false));
|
||||||
const workbasketServiceMock = jest.fn().mockImplementation(
|
const workbasketServiceMock = jest.fn().mockImplementation(
|
||||||
(): Partial<WorkbasketService> => ({
|
(): Partial<WorkbasketService> => ({
|
||||||
workbasketSavedTriggered: workbasketSavedTriggeredFn,
|
workbasketSavedTriggered: workbasketSavedTriggeredFn,
|
||||||
getWorkBasketsSummary: workbasketSummaryFn,
|
getWorkBasketsSummary: workbasketSummaryFn,
|
||||||
getWorkBasket: getWorkbasketFn,
|
getWorkBasket: getWorkbasketFn,
|
||||||
getWorkbasketActionToolbarExpansion: getWorkbasketActionToolbarExpansionFn
|
getWorkbasketActionToolbarExpansion: getWorkbasketActionToolbarExpansionFn,
|
||||||
|
getWorkBasketAccessItems: jest.fn().mockReturnValue(of({})),
|
||||||
|
getWorkBasketsDistributionTargets: jest.fn().mockReturnValue(of({}))
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -137,7 +140,7 @@ describe('WorkbasketListComponent', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
let actionDispatched = false;
|
let actionDispatched = false;
|
||||||
actions$.pipe(ofActionDispatched(SelectWorkbasket)).subscribe(() => (actionDispatched = true));
|
actions$.pipe(ofActionDispatched(SelectWorkbasket)).subscribe(() => (actionDispatched = true));
|
||||||
component.selectWorkbasket('1');
|
component.selectWorkbasket('WBI:000000000000000000000000000000000902');
|
||||||
expect(actionDispatched).toBe(true);
|
expect(actionDispatched).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
.workbasket-overview {
|
.workbasket-overview {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
taskana-administration-workbasket-list {
|
||||||
taskana-administration-workbasket-details {
|
width: 500px;
|
||||||
width: calc(100% - 500px);
|
}
|
||||||
height: calc(100% - 213px);
|
taskana-administration-workbasket-details {
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@
|
||||||
width: 350px;
|
width: 350px;
|
||||||
background-color: $dark-green;
|
background-color: $dark-green;
|
||||||
}
|
}
|
||||||
|
.mat-drawer-content {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
.sidenav__drawer-list-item {
|
.sidenav__drawer-list-item {
|
||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { TaskanaType } from 'app/shared/models/taskana-type';
|
||||||
styleUrls: ['./filter.component.scss']
|
styleUrls: ['./filter.component.scss']
|
||||||
})
|
})
|
||||||
export class FilterComponent implements OnInit {
|
export class FilterComponent implements OnInit {
|
||||||
|
@Input() component: string;
|
||||||
@Input() allTypes: Map<ICONTYPES, string> = new Map([
|
@Input() allTypes: Map<ICONTYPES, string> = new Map([
|
||||||
[ICONTYPES.ALL, 'All'],
|
[ICONTYPES.ALL, 'All'],
|
||||||
[ICONTYPES.PERSONAL, 'Personal'],
|
[ICONTYPES.PERSONAL, 'Personal'],
|
||||||
|
|
@ -29,6 +30,7 @@ export class FilterComponent implements OnInit {
|
||||||
@Input() filterType = TaskanaType.WORKBASKETS;
|
@Input() filterType = TaskanaType.WORKBASKETS;
|
||||||
|
|
||||||
@Output() performFilter = new EventEmitter<Filter>();
|
@Output() performFilter = new EventEmitter<Filter>();
|
||||||
|
@Output() inputComponent = new EventEmitter<string>();
|
||||||
|
|
||||||
filter: Filter;
|
filter: Filter;
|
||||||
filterParamKeys = [];
|
filterParamKeys = [];
|
||||||
|
|
@ -59,6 +61,7 @@ export class FilterComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
search() {
|
search() {
|
||||||
|
this.inputComponent.emit(this.component);
|
||||||
this.performFilter.emit(this.filter);
|
this.performFilter.emit(this.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
|
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
|
||||||
|
import { WorkbasketSummary } from '../models/workbasket-summary';
|
||||||
|
|
||||||
@Pipe({ name: 'selectWorkbaskets' })
|
@Pipe({ name: 'selectWorkbaskets' })
|
||||||
export class SelectWorkBasketPipe implements PipeTransform {
|
export class SelectWorkBasketPipe implements PipeTransform {
|
||||||
transform(originArray: any, selectionArray: any, arg1: any): Object[] {
|
transform(originArray: any, selectionArray: any, arg1: any): WorkbasketSummary[] {
|
||||||
let returnArray = [];
|
let returnArray = [];
|
||||||
if (!originArray || !selectionArray) {
|
if (!originArray || !selectionArray) {
|
||||||
return returnArray;
|
return returnArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let index = originArray.length - 1; index >= 0; index--) {
|
for (let index = originArray.length - 1; index >= 0; index--) {
|
||||||
if (
|
if (
|
||||||
(arg1 &&
|
(arg1 &&
|
||||||
|
|
@ -21,6 +21,7 @@ export class SelectWorkBasketPipe implements PipeTransform {
|
||||||
originArray.splice(index, 1);
|
originArray.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originArray.length > TaskanaQueryParameters.pageSize) {
|
if (originArray.length > TaskanaQueryParameters.pageSize) {
|
||||||
originArray.slice(0, TaskanaQueryParameters.pageSize);
|
originArray.slice(0, TaskanaQueryParameters.pageSize);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -288,5 +288,215 @@ export const workbasketReadStateMock = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
action: ACTION.READ,
|
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
|
workbasketAccessItems: workbasketAccessItemsMock
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,10 @@ export class GetWorkbasketDistributionTargets {
|
||||||
constructor(public url: string) {}
|
constructor(public url: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GetAvailableDistributionTargets {
|
||||||
|
static readonly type = '[Workbasket] Get available distribution targets';
|
||||||
|
}
|
||||||
|
|
||||||
export class UpdateWorkbasketDistributionTargets {
|
export class UpdateWorkbasketDistributionTargets {
|
||||||
static readonly type = '[Workbasket] Update workbasket distribution targets';
|
static readonly type = '[Workbasket] Update workbasket distribution targets';
|
||||||
constructor(public url: string, public distributionTargetsIds: string[]) {}
|
constructor(public url: string, public distributionTargetsIds: string[]) {}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,11 @@ export class WorkbasketSelectors {
|
||||||
static workbasketDistributionTargets(state: WorkbasketStateModel): WorkbasketDistributionTargets {
|
static workbasketDistributionTargets(state: WorkbasketStateModel): WorkbasketDistributionTargets {
|
||||||
return state.workbasketDistributionTargets;
|
return state.workbasketDistributionTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Selector([WorkbasketState])
|
||||||
|
static availableDistributionTargets(state: WorkbasketStateModel): WorkbasketSummary[] {
|
||||||
|
return state.workbasketAvailableDistributionTargets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkbasketAndAction {
|
export interface WorkbasketAndAction {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
CopyWorkbasket,
|
CopyWorkbasket,
|
||||||
CreateWorkbasket,
|
CreateWorkbasket,
|
||||||
DeselectWorkbasket,
|
DeselectWorkbasket,
|
||||||
|
GetAvailableDistributionTargets,
|
||||||
GetWorkbasketAccessItems,
|
GetWorkbasketAccessItems,
|
||||||
GetWorkbasketDistributionTargets,
|
GetWorkbasketDistributionTargets,
|
||||||
GetWorkbasketsSummary,
|
GetWorkbasketsSummary,
|
||||||
|
|
@ -32,6 +33,7 @@ import { WorkbasketSummary } from '../../models/workbasket-summary';
|
||||||
import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
|
import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
|
||||||
import { ButtonAction } from '../../../administration/models/button-action';
|
import { ButtonAction } from '../../../administration/models/button-action';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { RequestInProgressService } from '../../services/request-in-progress/request-in-progress.service';
|
||||||
|
|
||||||
class InitializeStore {
|
class InitializeStore {
|
||||||
static readonly type = '[Workbasket] Initializing state';
|
static readonly type = '[Workbasket] Initializing state';
|
||||||
|
|
@ -43,7 +45,8 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
||||||
private workbasketService: WorkbasketService,
|
private workbasketService: WorkbasketService,
|
||||||
private location: Location,
|
private location: Location,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute,
|
||||||
|
private requestInProgressService: RequestInProgressService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Action(InitializeStore)
|
@Action(InitializeStore)
|
||||||
|
|
@ -131,6 +134,10 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
||||||
selectedWorkbasket,
|
selectedWorkbasket,
|
||||||
action: ACTION.READ
|
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)
|
@Action(OnButtonPressed)
|
||||||
doWorkbasketDetailsAction(ctx: StateContext<WorkbasketStateModel>, action: OnButtonPressed): Observable<any> {
|
doWorkbasketDetailsAction(ctx: StateContext<WorkbasketStateModel>, action: OnButtonPressed): Observable<any> {
|
||||||
ctx.patchState({ button: action.button });
|
ctx.patchState({ button: action.button });
|
||||||
|
setTimeout(() => {
|
||||||
|
ctx.patchState({ button: undefined });
|
||||||
|
}, 500);
|
||||||
return of(null);
|
return of(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,15 +357,32 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Action(GetAvailableDistributionTargets)
|
||||||
|
getAvailableDistributionTargets(ctx: StateContext<WorkbasketStateModel>): Observable<any> {
|
||||||
|
return this.workbasketService.getWorkBasketsSummary(true).pipe(
|
||||||
|
take(1),
|
||||||
|
tap((workbasketAvailableDistributionTargets: WorkbasketSummaryRepresentation) => {
|
||||||
|
ctx.patchState({
|
||||||
|
workbasketAvailableDistributionTargets: workbasketAvailableDistributionTargets.workbaskets
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Action(UpdateWorkbasketDistributionTargets)
|
@Action(UpdateWorkbasketDistributionTargets)
|
||||||
updateWorkbasketDistributionTargets(
|
updateWorkbasketDistributionTargets(
|
||||||
ctx: StateContext<WorkbasketStateModel>,
|
ctx: StateContext<WorkbasketStateModel>,
|
||||||
action: UpdateWorkbasketDistributionTargets
|
action: UpdateWorkbasketDistributionTargets
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
|
this.requestInProgressService.setRequestInProgress(true);
|
||||||
return this.workbasketService.updateWorkBasketsDistributionTargets(action.url, action.distributionTargetsIds).pipe(
|
return this.workbasketService.updateWorkBasketsDistributionTargets(action.url, action.distributionTargetsIds).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
tap(
|
tap(
|
||||||
(updatedWorkbasketsDistributionTargets) => {
|
(updatedWorkbasketsDistributionTargets) => {
|
||||||
|
ctx.patchState({
|
||||||
|
workbasketDistributionTargets: updatedWorkbasketsDistributionTargets
|
||||||
|
});
|
||||||
|
this.requestInProgressService.setRequestInProgress(false);
|
||||||
this.notificationService.showToast(
|
this.notificationService.showToast(
|
||||||
NOTIFICATION_TYPES.SUCCESS_ALERT_8,
|
NOTIFICATION_TYPES.SUCCESS_ALERT_8,
|
||||||
new Map<string, string>([['workbasketName', ctx.getState().selectedWorkbasket.name]])
|
new Map<string, string>([['workbasketName', ctx.getState().selectedWorkbasket.name]])
|
||||||
|
|
@ -363,6 +390,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.notificationService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_3, error);
|
this.notificationService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_3, error);
|
||||||
|
this.requestInProgressService.setRequestInProgress(false);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -392,6 +420,7 @@ export interface WorkbasketStateModel {
|
||||||
action: ACTION;
|
action: ACTION;
|
||||||
workbasketAccessItems: WorkbasketAccessItemsRepresentation;
|
workbasketAccessItems: WorkbasketAccessItemsRepresentation;
|
||||||
workbasketDistributionTargets: WorkbasketDistributionTargets;
|
workbasketDistributionTargets: WorkbasketDistributionTargets;
|
||||||
|
workbasketAvailableDistributionTargets: WorkbasketSummary[];
|
||||||
selectedComponent: WorkbasketComponent;
|
selectedComponent: WorkbasketComponent;
|
||||||
button: ButtonAction | undefined;
|
button: ButtonAction | undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
Loading…
Reference in New Issue