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": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"src/theme/_main.scss"
|
||||
"src/theme/_main.scss",
|
||||
"src/theme/custom-theme-material.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/jquery/dist/jquery.min.js",
|
||||
|
|
|
@ -19,11 +19,15 @@ import { WorkbasketListToolbarComponent } from './components/workbasket-list-too
|
|||
import { WorkbasketDetailsComponent } from './components/workbasket-details/workbasket-details.component';
|
||||
import { WorkbasketInformationComponent } from './components/workbasket-information/workbasket-information.component';
|
||||
import { WorkbasketDistributionTargetsComponent } from './components/workbasket-distribution-targets/workbasket-distribution-targets.component';
|
||||
import { WorkbasketDualListComponent } from './components/workbasket-dual-list/workbasket-dual-list.component';
|
||||
import { WorkbasketDistributionTargetsListComponent } from './components/workbasket-distribution-targets-list/workbasket-distribution-targets-list.component';
|
||||
import { WorkbasketAccessItemsComponent } from './components/workbasket-access-items/workbasket-access-items.component';
|
||||
import { ClassificationListComponent } from './components/classification-list/classification-list.component';
|
||||
import { ClassificationDetailsComponent } from './components/classification-details/classification-details.component';
|
||||
import { ImportExportComponent } from './components/import-export/import-export.component';
|
||||
import { AdministrationOverviewComponent } from './components/administration-overview/administration-overview.component';
|
||||
|
||||
import { ClassificationOverviewComponent } from './components/classification-overview/classification-overview.component';
|
||||
import { WorkbasketOverviewComponent } from './components/workbasket-overview/workbasket-overview.component';
|
||||
/**
|
||||
* Services
|
||||
*/
|
||||
|
@ -31,15 +35,16 @@ import { SavingWorkbasketService } from './services/saving-workbaskets.service';
|
|||
import { ClassificationDefinitionService } from './services/classification-definition.service';
|
||||
import { WorkbasketDefinitionService } from './services/workbasket-definition.service';
|
||||
import { ImportExportService } from './services/import-export.service';
|
||||
import { ClassificationOverviewComponent } from './components/classification-overview/classification-overview.component';
|
||||
import { WorkbasketOverviewComponent } from './components/workbasket-overview/workbasket-overview.component';
|
||||
|
||||
/**
|
||||
* Material Design
|
||||
*/
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { AdministrationOverviewComponent } from './components/administration-overview/administration-overview.component';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
|
@ -72,7 +77,7 @@ const DECLARATIONS = [
|
|||
WorkbasketDetailsComponent,
|
||||
WorkbasketInformationComponent,
|
||||
WorkbasketDistributionTargetsComponent,
|
||||
WorkbasketDualListComponent,
|
||||
WorkbasketDistributionTargetsListComponent,
|
||||
ClassificationOverviewComponent,
|
||||
ClassificationListComponent,
|
||||
ClassificationTypesSelectorComponent,
|
||||
|
@ -111,6 +116,7 @@ const DECLARATIONS = [
|
|||
SavingWorkbasketService,
|
||||
ClassificationCategoriesService,
|
||||
ImportExportService
|
||||
]
|
||||
],
|
||||
entryComponents: []
|
||||
})
|
||||
export class AdministrationModule {}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="classification-details__wrapper" id="classification-details" *ngIf="classification && !requestInProgress">
|
||||
|
||||
<!-- 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}}]
|
||||
<span *ngIf="isCreatingNewClassification" class="badge warning"> {{badgeMessage$ | async}}</span>
|
||||
</h4>
|
||||
|
@ -39,7 +39,7 @@
|
|||
</mat-menu>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</mat-toolbar>
|
||||
|
||||
<!-- DETAILED FIELDS -->
|
||||
<div class="panel-body" style="padding: 0">
|
||||
|
@ -80,17 +80,19 @@
|
|||
|
||||
<!-- SERVICE LEVEL AND PRIORITY-->
|
||||
<div class="classification-details__service-and-priority">
|
||||
<mat-form-field appearance="outline" class="classification-details__mat-form-field">
|
||||
<mat-label> Service Level </mat-label>
|
||||
<label for="classification-service-level"></label>
|
||||
<input matInput type="text" required maxlength="255"
|
||||
id="classification-service-level" placeholder="Service Level"
|
||||
[(ngModel)]="classification.serviceLevel" name="classification.serviceLevel"
|
||||
#serviceLevel="ngModel" (input)="validateInputOverflow(serviceLevel, 255)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(serviceLevel.name)" class="error">{{lengthError}}</div>
|
||||
<div class="classification-details__service">
|
||||
<mat-form-field appearance="outline" class="classification-details__mat-form-field">
|
||||
<mat-label> Service Level </mat-label>
|
||||
<label for="classification-service-level"></label>
|
||||
<input matInput type="text" required maxlength="255"
|
||||
id="classification-service-level" placeholder="Service Level"
|
||||
[(ngModel)]="classification.serviceLevel" name="classification.serviceLevel"
|
||||
#serviceLevel="ngModel" (input)="validateInputOverflow(serviceLevel, 255)">
|
||||
</mat-form-field>
|
||||
<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 don't understand all methods in the number-picker component.
|
||||
<taskana-shared-number-picker [(ngModel)]="classification.priority"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.classification-details {
|
||||
width: 100%;
|
||||
height: calc(100vh - 100px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
@ -12,12 +11,12 @@
|
|||
|
||||
/* ACTION TOOLBAR */
|
||||
.classification-details__headline {
|
||||
padding-top: 0.5rem;
|
||||
font-size: 1.5rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.classification-details__action-toolbar {
|
||||
width: calc(100% - 500px);
|
||||
position: fixed;
|
||||
padding: 12px 32px 12px 24px;
|
||||
padding: 8px 30px 12px 24px;
|
||||
height: 68px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
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 {
|
||||
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 {
|
||||
width: 70%;
|
||||
width: 100%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import { MatInputModule } from '@angular/material/input';
|
|||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
|
||||
@Component({ selector: 'taskana-shared-field-error-display', template: '' })
|
||||
class FieldErrorDisplayStub {
|
||||
|
@ -120,6 +121,7 @@ describe('ClassificationDetailsComponent', () => {
|
|||
NgxsModule.forRoot([ClassificationState, EngineConfigurationState]),
|
||||
FormsModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatDividerModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
.classification-list {
|
||||
height: calc(100vh - 55px);
|
||||
width: 500px;
|
||||
}
|
||||
.classification-list__action-toolbar {
|
||||
padding: 0 16px;
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
.classification-overview {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
align-items: stretch;
|
||||
}
|
||||
taskana-administration-classification-details {
|
||||
width: 100%;
|
||||
taskana-administration-classification-list {
|
||||
width: 500px;
|
||||
}
|
||||
taskana-administration-classification-details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.workbasket-details {
|
||||
width: 100%;
|
||||
height: calc(100vh - 100px);
|
||||
}
|
||||
.workbasket-details__toolbar {
|
||||
background-color: white;
|
||||
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 { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { WorkbasketDualListComponent } from './workbasket-dual-list.component';
|
||||
import { WorkbasketDistributionTargetsListComponent } from './workbasket-distribution-targets-list.component';
|
||||
import { Filter } from '../../../shared/models/filter';
|
||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
import { ICONTYPES } from '../../../shared/models/icon-types';
|
||||
|
@ -8,6 +8,9 @@ import { SelectWorkBasketPipe } from '../../../shared/pipes/select-workbaskets.p
|
|||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { workbasketReadStateMock } from '../../../shared/store/mock-data/mock-store';
|
||||
import { Side } from '../workbasket-distribution-targets/workbasket-distribution-targets.component';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
|
||||
@Component({ selector: 'taskana-shared-filter', template: '' })
|
||||
class FilterStub {
|
||||
|
@ -25,33 +28,39 @@ class IconTypeStub {
|
|||
@Input() text: string;
|
||||
}
|
||||
|
||||
describe('WorkbasketDualListComponent', () => {
|
||||
let fixture: ComponentFixture<WorkbasketDualListComponent>;
|
||||
describe('WorkbasketDistributionTargetsListComponent', () => {
|
||||
let fixture: ComponentFixture<WorkbasketDistributionTargetsListComponent>;
|
||||
let debugElement: DebugElement;
|
||||
let component: WorkbasketDualListComponent;
|
||||
let component: WorkbasketDistributionTargetsListComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [InfiniteScrollModule, BrowserAnimationsModule],
|
||||
declarations: [WorkbasketDualListComponent, FilterStub, SpinnerStub, IconTypeStub, SelectWorkBasketPipe],
|
||||
imports: [MatIconModule, MatToolbarModule, MatListModule, InfiniteScrollModule, BrowserAnimationsModule],
|
||||
declarations: [
|
||||
WorkbasketDistributionTargetsListComponent,
|
||||
FilterStub,
|
||||
SpinnerStub,
|
||||
IconTypeStub,
|
||||
SelectWorkBasketPipe
|
||||
],
|
||||
providers: []
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WorkbasketDualListComponent);
|
||||
fixture = TestBed.createComponent(WorkbasketDistributionTargetsListComponent);
|
||||
debugElement = fixture.debugElement;
|
||||
component = fixture.componentInstance;
|
||||
component.distributionTargets = workbasketReadStateMock.paginatedWorkbasketsSummary.workbaskets;
|
||||
component.distributionTargetsSelected = [];
|
||||
component.side = Side.LEFT;
|
||||
component.side = Side.AVAILABLE;
|
||||
}));
|
||||
|
||||
it('should create component', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set sideNumber to 0 when side is Side.LEFT', () => {
|
||||
it('should set sideNumber to 0 when side is Side.AVAILABLE', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.sideNumber).toBe(0);
|
||||
expect(component.side).toBe(Side.AVAILABLE);
|
||||
});
|
||||
|
||||
it('should select all distribution targets', () => {
|
||||
|
@ -67,13 +76,6 @@ describe('WorkbasketDualListComponent', () => {
|
|||
expect(scrollingEmitSpy).toHaveBeenCalledWith(component.side);
|
||||
});
|
||||
|
||||
it('should emit filter model and side when performing filter', () => {
|
||||
const performDualListFilterSpy = jest.spyOn(component.performDualListFilter, 'emit');
|
||||
const filterModelMock: Filter = { filterParams: 'filter' };
|
||||
component.performAvailableFilter(filterModelMock);
|
||||
expect(performDualListFilterSpy).toHaveBeenCalledWith({ filterBy: filterModelMock, side: component.side });
|
||||
});
|
||||
|
||||
it('should change toolbar state', () => {
|
||||
expect(component.toolbarState).toBe(false);
|
||||
component.changeToolbarState(true);
|
||||
|
@ -83,7 +85,7 @@ describe('WorkbasketDualListComponent', () => {
|
|||
it('should display all available workbaskets', () => {
|
||||
fixture.detectChanges();
|
||||
const distributionTargetList = debugElement.nativeElement.getElementsByClassName(
|
||||
'workbasket-list__distribution-targets'
|
||||
'workbasket-distribution-targets__workbaskets-item'
|
||||
);
|
||||
expect(distributionTargetList).toHaveLength(5);
|
||||
});
|
|
@ -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">
|
||||
<!-- ACTION TOOLBAR-->
|
||||
<!--
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right btn-group">
|
||||
<button type="button" (click)="onSave()" [disabled]="action === 'COPY'" data-toggle="tooltip" title="Save"
|
||||
class="btn btn-default btn-primary">
|
||||
<span class="material-icons md-20">save</span>
|
||||
</button>
|
||||
<button type="button" (click)="onClear()" data-toggle="tooltip" title="Undo Changes"
|
||||
class="btn btn-default">
|
||||
<span class="material-icons md-20 blue">undo</span>
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="panel-header">{{workbasket.name}}
|
||||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
</h4>
|
||||
<div *ngIf="workbasket" id="wb-information" class="workbasket-distribution-targets">
|
||||
<mat-toolbar class="distribution-targets-list__action-toolbar" >
|
||||
<button mat-flat-button class="distribution-targets-list__action-button distribution-targets-list__toggle-view-button"
|
||||
*ngIf="!sideBySide" (click)="toggleSideBySideView()">
|
||||
Display side-by-side
|
||||
<mat-icon class="distribution-targets-list__button-icon">view_week</mat-icon>
|
||||
</button>
|
||||
<button mat-flat-button class="distribution-targets-list__action-button distribution-targets-list__toggle-view-button"
|
||||
*ngIf="sideBySide" (click)="toggleSideBySideView()">
|
||||
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>
|
||||
</div>
|
||||
<span style="flex-grow: 1"> </span>
|
||||
<div class="distribution-targets-list__action-buttons--chooser"
|
||||
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>
|
||||
-->
|
||||
<!-- DISTRIBUTION TABLE-->
|
||||
<div #panelBody class="panel-body">
|
||||
|
||||
<!-- DISTRIBUTION LEFT LIST -->
|
||||
<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>
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
<span style="flex: 1 1 auto"> </span>
|
||||
|
||||
<!-- 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"
|
||||
[distributionTargetsSelected]="distributionTargetsSelected"
|
||||
(performDualListFilter)="performFilter($event)"
|
||||
[requestInProgress]="requestInProgressRight"
|
||||
[side]="side.RIGHT"
|
||||
[(allSelected)]="selectAllRight"></taskana-administration-workbasket-dual-list>
|
||||
</div>
|
||||
<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-->
|
||||
<taskana-administration-workbasket-distribution-targets-list
|
||||
[ngClass]="sideBySide ? 'distribution-targets-list__lists--left-side' : ''"
|
||||
header="Selected distribution targets"
|
||||
[distributionTargets]="distributionTargetsRight"
|
||||
[distributionTargetsSelected]="distributionTargetsSelected"
|
||||
(performDualListFilter)="performFilter($event)"
|
||||
[side]="side.SELECTED"
|
||||
(scrolling)="onScroll()"
|
||||
[loadingItems]="loadingItems"
|
||||
[(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>
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
.button-margin-top {
|
||||
margin-top: 100px;
|
||||
}
|
||||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.list-arrows > button {
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
.col-md-5-6 {
|
||||
@media (min-width: 992px) {
|
||||
width: 45.82%;
|
||||
.distribution-targets-list {
|
||||
&__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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 { 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 { WorkbasketDistributionTargets } from 'app/shared/models/workbasket-distribution-targets';
|
||||
import { ACTION } from 'app/shared/models/action';
|
||||
|
||||
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.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 { Page } from 'app/shared/models/page';
|
||||
import { OrientationService } from 'app/shared/services/orientation/orientation.service';
|
||||
import { Orientation } from 'app/shared/models/orientation';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { take, takeUntil } from 'rxjs/operators';
|
||||
import { filter, takeUntil } from 'rxjs/operators';
|
||||
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
|
||||
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||
import {
|
||||
GetAvailableDistributionTargets,
|
||||
GetWorkbasketDistributionTargets,
|
||||
GetWorkbasketsSummary,
|
||||
UpdateWorkbasketDistributionTargets
|
||||
} from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
||||
import { WorkbasketStateModel } from '../../../shared/store/workbasket-store/workbasket.state';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ButtonAction } from '../../models/button-action';
|
||||
|
||||
export enum Side {
|
||||
LEFT,
|
||||
RIGHT
|
||||
AVAILABLE,
|
||||
SELECTED
|
||||
}
|
||||
@Component({
|
||||
selector: 'taskana-administration-workbasket-distribution-targets',
|
||||
templateUrl: './workbasket-distribution-targets.component.html',
|
||||
styleUrls: ['./workbasket-distribution-targets.component.scss']
|
||||
})
|
||||
export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
export class WorkbasketDistributionTargetsComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
workbasket: Workbasket;
|
||||
|
||||
@Input()
|
||||
action: ACTION;
|
||||
|
||||
badgeMessage = '';
|
||||
toolbarState = false;
|
||||
sideBySide = true;
|
||||
displayingDistributionTargetsPicker = true;
|
||||
|
||||
distributionTargetsSelectedResource: WorkbasketDistributionTargets;
|
||||
distributionTargetsLeft: Array<WorkbasketSummary> = [];
|
||||
availableDistributionTargets: Array<WorkbasketSummary> = [];
|
||||
distributionTargetsRight: Array<WorkbasketSummary> = [];
|
||||
distributionTargetsSelected: Array<WorkbasketSummary>;
|
||||
distributionTargetsClone: Array<WorkbasketSummary>;
|
||||
distributionTargetsSelectedClone: Array<WorkbasketSummary>;
|
||||
|
||||
requestInProgressLeft = false;
|
||||
requestInProgressRight = false;
|
||||
loadingItems = false;
|
||||
side = Side;
|
||||
private initialized = false;
|
||||
|
@ -61,56 +58,37 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
|||
selectAllLeft = false;
|
||||
selectAllRight = false;
|
||||
|
||||
@ViewChild('panelBody')
|
||||
panelBody: ElementRef;
|
||||
|
||||
@Select(WorkbasketSelectors.workbasketDistributionTargets)
|
||||
workbasketDistributionTargets$: Observable<WorkbasketDistributionTargets>;
|
||||
|
||||
@Select(WorkbasketSelectors.availableDistributionTargets)
|
||||
availableDistributionTargets$: Observable<WorkbasketSummary[]>;
|
||||
|
||||
@Select(WorkbasketSelectors.buttonAction)
|
||||
buttonAction$: Observable<ButtonAction>;
|
||||
|
||||
destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private workbasketService: WorkbasketService,
|
||||
private savingWorkbaskets: SavingWorkbasketService,
|
||||
private requestInProgressService: RequestInProgressService,
|
||||
private orientationService: OrientationService,
|
||||
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() {
|
||||
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 GetAvailableDistributionTargets());
|
||||
|
||||
this.availableDistributionTargets$.pipe(takeUntil(this.destroy$)).subscribe((availableDistributionTargets) => {
|
||||
this.availableDistributionTargets = availableDistributionTargets;
|
||||
});
|
||||
|
||||
this.savingWorkbaskets
|
||||
.triggeredDistributionTargetsSaving()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
|
@ -121,117 +99,130 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
|||
}
|
||||
});
|
||||
|
||||
this.orientationService
|
||||
.getOrientation()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => {
|
||||
this.calculateNumberItemsList();
|
||||
this.workbasketDistributionTargets$.subscribe((workbasketDistributionTargets) => {
|
||||
if (typeof workbasketDistributionTargets !== 'undefined') {
|
||||
this.distributionTargetsSelectedResource = { ...workbasketDistributionTargets };
|
||||
this.distributionTargetsSelected = this.distributionTargetsSelectedResource.distributionTargets;
|
||||
this.distributionTargetsSelectedClone = { ...this.distributionTargetsSelected };
|
||||
TaskanaQueryParameters.page = 1;
|
||||
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) {
|
||||
if (this.distributionTargetsSelected && !this.initialized) {
|
||||
this.initialized = true;
|
||||
TaskanaQueryParameters.pageSize = this.cards + this.distributionTargetsSelected.length;
|
||||
}
|
||||
|
||||
// TODO: Implement this into NGXS
|
||||
this.workbasketService
|
||||
.getWorkBasketsSummary(true)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((distributionTargetsAvailable: WorkbasketSummaryRepresentation) => {
|
||||
if (TaskanaQueryParameters.page === 1) {
|
||||
this.distributionTargetsLeft = [];
|
||||
this.availableDistributionTargets = [];
|
||||
this.page = distributionTargetsAvailable.page;
|
||||
}
|
||||
if (side === this.side.LEFT) {
|
||||
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
|
||||
} else if (side === this.side.RIGHT) {
|
||||
if (side === this.side.AVAILABLE) {
|
||||
this.availableDistributionTargets.push(...distributionTargetsAvailable.workbaskets);
|
||||
} else if (side === this.side.SELECTED) {
|
||||
this.distributionTargetsRight = Object.assign([], distributionTargetsAvailable.workbaskets);
|
||||
} else {
|
||||
this.distributionTargetsLeft.push(...distributionTargetsAvailable.workbaskets);
|
||||
this.availableDistributionTargets.push(...distributionTargetsAvailable.workbaskets);
|
||||
this.distributionTargetsRight = 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) {
|
||||
this.fillDistributionTargets(dualListFilter.side, undefined);
|
||||
this.onRequest(false, dualListFilter.side);
|
||||
this.store
|
||||
.dispatch(
|
||||
new GetWorkbasketsSummary(
|
||||
true,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
dualListFilter.filterBy.filterParams.name,
|
||||
dualListFilter.filterBy.filterParams.description,
|
||||
'',
|
||||
dualListFilter.filterBy.filterParams.owner,
|
||||
dualListFilter.filterBy.filterParams.type,
|
||||
'',
|
||||
dualListFilter.filterBy.filterParams.key,
|
||||
'',
|
||||
true
|
||||
)
|
||||
this.workbasketService
|
||||
.getWorkBasketsSummary(
|
||||
true,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
dualListFilter.filterBy.filterParams.name,
|
||||
dualListFilter.filterBy.filterParams.description,
|
||||
'',
|
||||
dualListFilter.filterBy.filterParams.owner,
|
||||
dualListFilter.filterBy.filterParams.type,
|
||||
'',
|
||||
dualListFilter.filterBy.filterParams.key,
|
||||
'',
|
||||
true
|
||||
)
|
||||
.subscribe((state: WorkbasketStateModel) => {
|
||||
this.fillDistributionTargets(dualListFilter.side, state.paginatedWorkbasketsSummary.workbaskets);
|
||||
this.onRequest(true, dualListFilter.side);
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((distributionTargetsAvailable: WorkbasketSummaryRepresentation) => {
|
||||
this.fillDistributionTargets(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() {
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
this.store
|
||||
.dispatch(
|
||||
new UpdateWorkbasketDistributionTargets(
|
||||
this.distributionTargetsSelectedResource._links.self.href,
|
||||
this.getSeletedIds()
|
||||
)
|
||||
this.store.dispatch(
|
||||
new UpdateWorkbasketDistributionTargets(
|
||||
this.distributionTargetsSelectedResource._links.self.href,
|
||||
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;
|
||||
}
|
||||
|
||||
moveDistributionTargets(side: number) {
|
||||
if (side === Side.LEFT) {
|
||||
const itemsLeft = this.distributionTargetsLeft.length;
|
||||
if (side === Side.AVAILABLE) {
|
||||
const itemsLeft = this.availableDistributionTargets.length;
|
||||
const itemsRight = this.distributionTargetsRight.length;
|
||||
const itemsSelected = this.getSelectedItems(this.distributionTargetsLeft);
|
||||
const itemsSelected = this.getSelectedItems(this.availableDistributionTargets);
|
||||
this.distributionTargetsSelected = [...this.distributionTargetsSelected, ...itemsSelected];
|
||||
this.distributionTargetsRight = this.distributionTargetsRight.concat(itemsSelected);
|
||||
if (
|
||||
|
@ -245,102 +236,52 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
|
|||
const itemsSelected = this.getSelectedItems(this.distributionTargetsRight);
|
||||
this.distributionTargetsSelected = this.removeSelectedItems(this.distributionTargetsSelected, itemsSelected);
|
||||
this.distributionTargetsRight = this.removeSelectedItems(this.distributionTargetsRight, itemsSelected);
|
||||
this.distributionTargetsLeft = this.distributionTargetsLeft.concat(itemsSelected);
|
||||
this.availableDistributionTargets = this.availableDistributionTargets.concat(itemsSelected);
|
||||
this.unselectItems(itemsSelected);
|
||||
}
|
||||
}
|
||||
|
||||
onClear() {
|
||||
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.distributionTargetsSelected = Object.assign([], this.distributionTargetsSelectedClone);
|
||||
}
|
||||
|
||||
calculateNumberItemsList() {
|
||||
if (this.panelBody) {
|
||||
const cardHeight = 72;
|
||||
const unusedHeight = 100;
|
||||
this.cards =
|
||||
this.orientationService.calculateNumberItemsList(
|
||||
this.panelBody.nativeElement.offsetHeight,
|
||||
cardHeight,
|
||||
unusedHeight,
|
||||
true
|
||||
) + 1; // TODO: warum +1
|
||||
}
|
||||
}
|
||||
|
||||
fillDistributionTargets(side: Side, workbaskets: WorkbasketSummary[]) {
|
||||
this.distributionTargetsLeft = side === Side.LEFT ? workbaskets : this.distributionTargetsLeft;
|
||||
this.distributionTargetsRight = side === Side.RIGHT ? workbaskets : this.distributionTargetsRight;
|
||||
}
|
||||
|
||||
getNextPage(side: Side) {
|
||||
TaskanaQueryParameters.page += 1;
|
||||
this.getWorkbaskets(side);
|
||||
}
|
||||
|
||||
setBadge() {
|
||||
if (this.action === ACTION.COPY) {
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
}
|
||||
this.availableDistributionTargets = side === Side.AVAILABLE ? workbaskets : this.availableDistributionTargets;
|
||||
this.distributionTargetsRight = side === Side.SELECTED ? workbaskets : this.distributionTargetsRight;
|
||||
}
|
||||
|
||||
getSelectedItems(originList: any): Array<any> {
|
||||
return originList.filter((item: any) => item.selected === true);
|
||||
}
|
||||
|
||||
unselectItems(originList: any): Array<any> {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const item of originList) {
|
||||
if (item.selected && item.selected === true) {
|
||||
unselectItems(originList: Array<any>): Array<any> {
|
||||
return originList
|
||||
.filter((item) => item.selected)
|
||||
.map((item) => {
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
return originList;
|
||||
});
|
||||
}
|
||||
|
||||
removeSelectedItems(originList: any, selectedItemList) {
|
||||
removeSelectedItems(originList, selectedItemList) {
|
||||
const copyList = [...originList];
|
||||
for (let index = originList.length - 1; index >= 0; index--) {
|
||||
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) {
|
||||
this.loadingItems = false;
|
||||
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;
|
||||
}
|
||||
getSelectedIds(): Array<string> {
|
||||
return this.distributionTargetsSelected.map((distributionTarget) => distributionTarget.workbasketId);
|
||||
}
|
||||
|
||||
getSeletedIds(): Array<string> {
|
||||
const distributionTargetsSelelected: Array<string> = [];
|
||||
this.distributionTargetsSelected.forEach((item) => {
|
||||
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;
|
||||
}
|
||||
toggleSideBySideView() {
|
||||
this.sideBySide = !this.sideBySide;
|
||||
this.displayingDistributionTargetsPicker = true; //always display picker when toggle from side-by-side to single
|
||||
}
|
||||
|
||||
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 {
|
||||
width: 70%;
|
||||
width: 100%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
min-width: auto;
|
||||
}
|
||||
.workbasket-information__custom-fields {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,9 @@ const workbasketServiceMock = jest.fn().mockImplementation(
|
|||
updateWorkbasket: jest.fn().mockReturnValue(of(true)),
|
||||
markWorkbasketForDeletion: jest.fn().mockReturnValue(of(true)),
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
import { WorkbasketListToolbarComponent } from './workbasket-list-toolbar.component';
|
||||
import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { Actions, NgxsModule, ofActionDispatched, Store } from '@ngxs/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { WorkbasketState } from '../../../shared/store/workbasket-store/workbasket.state';
|
||||
import { WorkbasketService } from '../../../shared/services/workbasket/workbasket.service';
|
||||
|
@ -17,6 +17,7 @@ import { MatIconModule } from '@angular/material/icon';
|
|||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service';
|
||||
|
||||
const getDomainFn = jest.fn().mockReturnValue(true);
|
||||
const domainServiceMock = jest.fn().mockImplementation(
|
||||
|
@ -43,6 +44,12 @@ class FilterStub {
|
|||
@Output() performFilter = new EventEmitter<Filter>();
|
||||
}
|
||||
|
||||
const requestInProgressServiceSpy = jest.fn().mockImplementation(
|
||||
(): Partial<RequestInProgressService> => ({
|
||||
setRequestInProgress: jest.fn().mockReturnValue(of())
|
||||
})
|
||||
);
|
||||
|
||||
describe('WorkbasketListToolbarComponent', () => {
|
||||
let fixture: ComponentFixture<WorkbasketListToolbarComponent>;
|
||||
let debugElement: DebugElement;
|
||||
|
@ -62,7 +69,11 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
MatDialogModule
|
||||
],
|
||||
declarations: [WorkbasketListToolbarComponent, ImportExportStub, SortStub, FilterStub],
|
||||
providers: [{ provide: DomainService, useClass: domainServiceMock }, WorkbasketService]
|
||||
providers: [
|
||||
{ provide: DomainService, useClass: domainServiceMock },
|
||||
{ provide: RequestInProgressService, useClass: requestInProgressServiceSpy },
|
||||
WorkbasketService
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WorkbasketListToolbarComponent);
|
||||
|
@ -107,7 +118,7 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
expect(sort).toMatchObject(mockSort);
|
||||
});
|
||||
|
||||
it('should emit value when filtering is called', (done) => {
|
||||
it('should emit value when filtering is called', async((done) => {
|
||||
const mockFilter: Filter = { filterParams: 'abc' };
|
||||
let filterBy: Filter = { filterParams: 'abc' };
|
||||
component.performFilter.subscribe((filter: Filter) => {
|
||||
|
@ -116,7 +127,7 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
});
|
||||
component.filtering(filterBy);
|
||||
expect(filterBy).toMatchObject(mockFilter);
|
||||
});
|
||||
}));
|
||||
|
||||
/* HTML */
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
|||
filterParams = { name: '', key: '', type: '', description: '', owner: '' };
|
||||
filterType = TaskanaType.WORKBASKETS;
|
||||
showFilter = false;
|
||||
component = '';
|
||||
|
||||
@Select(WorkbasketSelectors.workbasketActiveAction)
|
||||
workbasketActiveAction$: Observable<ACTION>;
|
||||
|
@ -63,7 +64,13 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
|||
}
|
||||
|
||||
filtering(filterBy: Filter) {
|
||||
this.performFilter.emit(filterBy);
|
||||
if (this.component === 'workbasket-list') {
|
||||
this.performFilter.emit(filterBy);
|
||||
}
|
||||
}
|
||||
|
||||
setComponent(component: string) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
addWorkbasket() {
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
.workbasket-list {
|
||||
height: calc(100vh - 156px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
min-width: 500px;
|
||||
}
|
||||
.workbasket-list__workbaskets {
|
||||
overflow-y: hidden;
|
||||
|
|
|
@ -23,17 +23,20 @@ import { MatListModule } from '@angular/material/list';
|
|||
import { DomainService } from '../../../shared/services/domain/domain.service';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { RequestInProgressService } from '../../../shared/services/request-in-progress/request-in-progress.service';
|
||||
import { selectedWorkbasketMock } from '../../../shared/store/mock-data/mock-store';
|
||||
|
||||
const workbasketSavedTriggeredFn = jest.fn().mockReturnValue(of(1));
|
||||
const workbasketSummaryFn = jest.fn().mockReturnValue(of({}));
|
||||
const getWorkbasketFn = jest.fn().mockReturnValue(of({ workbasketId: '1' }));
|
||||
const getWorkbasketFn = jest.fn().mockReturnValue(of(selectedWorkbasketMock));
|
||||
const getWorkbasketActionToolbarExpansionFn = jest.fn().mockReturnValue(of(false));
|
||||
const workbasketServiceMock = jest.fn().mockImplementation(
|
||||
(): Partial<WorkbasketService> => ({
|
||||
workbasketSavedTriggered: workbasketSavedTriggeredFn,
|
||||
getWorkBasketsSummary: workbasketSummaryFn,
|
||||
getWorkBasket: getWorkbasketFn,
|
||||
getWorkbasketActionToolbarExpansion: getWorkbasketActionToolbarExpansionFn
|
||||
getWorkbasketActionToolbarExpansion: getWorkbasketActionToolbarExpansionFn,
|
||||
getWorkBasketAccessItems: jest.fn().mockReturnValue(of({})),
|
||||
getWorkBasketsDistributionTargets: jest.fn().mockReturnValue(of({}))
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -137,7 +140,7 @@ describe('WorkbasketListComponent', () => {
|
|||
fixture.detectChanges();
|
||||
let actionDispatched = false;
|
||||
actions$.pipe(ofActionDispatched(SelectWorkbasket)).subscribe(() => (actionDispatched = true));
|
||||
component.selectWorkbasket('1');
|
||||
component.selectWorkbasket('WBI:000000000000000000000000000000000902');
|
||||
expect(actionDispatched).toBe(true);
|
||||
}));
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
.workbasket-overview {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
taskana-administration-workbasket-details {
|
||||
width: calc(100% - 500px);
|
||||
height: calc(100% - 213px);
|
||||
taskana-administration-workbasket-list {
|
||||
width: 500px;
|
||||
}
|
||||
taskana-administration-workbasket-details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
width: 350px;
|
||||
background-color: $dark-green;
|
||||
}
|
||||
|
||||
.mat-drawer-content {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.sidenav__drawer-list-item {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { TaskanaType } from 'app/shared/models/taskana-type';
|
|||
styleUrls: ['./filter.component.scss']
|
||||
})
|
||||
export class FilterComponent implements OnInit {
|
||||
@Input() component: string;
|
||||
@Input() allTypes: Map<ICONTYPES, string> = new Map([
|
||||
[ICONTYPES.ALL, 'All'],
|
||||
[ICONTYPES.PERSONAL, 'Personal'],
|
||||
|
@ -29,6 +30,7 @@ export class FilterComponent implements OnInit {
|
|||
@Input() filterType = TaskanaType.WORKBASKETS;
|
||||
|
||||
@Output() performFilter = new EventEmitter<Filter>();
|
||||
@Output() inputComponent = new EventEmitter<string>();
|
||||
|
||||
filter: Filter;
|
||||
filterParamKeys = [];
|
||||
|
@ -59,6 +61,7 @@ export class FilterComponent implements OnInit {
|
|||
}
|
||||
|
||||
search() {
|
||||
this.inputComponent.emit(this.component);
|
||||
this.performFilter.emit(this.filter);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { TaskanaQueryParameters } from 'app/shared/util/query-parameters';
|
||||
import { WorkbasketSummary } from '../models/workbasket-summary';
|
||||
|
||||
@Pipe({ name: 'selectWorkbaskets' })
|
||||
export class SelectWorkBasketPipe implements PipeTransform {
|
||||
transform(originArray: any, selectionArray: any, arg1: any): Object[] {
|
||||
transform(originArray: any, selectionArray: any, arg1: any): WorkbasketSummary[] {
|
||||
let returnArray = [];
|
||||
if (!originArray || !selectionArray) {
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
for (let index = originArray.length - 1; index >= 0; index--) {
|
||||
if (
|
||||
(arg1 &&
|
||||
|
@ -21,6 +21,7 @@ export class SelectWorkBasketPipe implements PipeTransform {
|
|||
originArray.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (originArray.length > TaskanaQueryParameters.pageSize) {
|
||||
originArray.slice(0, TaskanaQueryParameters.pageSize);
|
||||
}
|
||||
|
|
|
@ -288,5 +288,215 @@ export const workbasketReadStateMock = {
|
|||
}
|
||||
},
|
||||
action: ACTION.READ,
|
||||
workbasketDistributionTargets: {
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
'http://localhost:8080/taskana/api/v1/workbaskets/WBI:000000000000000000000000000000000900/distribution-targets'
|
||||
}
|
||||
},
|
||||
distributionTargets: [
|
||||
{
|
||||
workbasketId: 'WBI:100000000000000000000000000000000001',
|
||||
key: 'GPK_KSC',
|
||||
name: 'Gruppenpostkorb KSC',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'GROUP',
|
||||
description: 'Gruppenpostkorb KSC',
|
||||
owner: 'owner0815',
|
||||
custom1: 'ABCQVW',
|
||||
custom2: '',
|
||||
custom3: 'xyz4',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:100000000000000000000000000000000002',
|
||||
key: 'GPK_KSC_1',
|
||||
name: 'Gruppenpostkorb KSC 1',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'GROUP',
|
||||
description: 'Gruppenpostkorb KSC 1',
|
||||
owner: '',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:100000000000000000000000000000000003',
|
||||
key: 'GPK_KSC_2',
|
||||
name: 'Gruppenpostkorb KSC 2',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'GROUP',
|
||||
description: 'Gruppenpostkorb KSC 2',
|
||||
owner: '',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
}
|
||||
]
|
||||
},
|
||||
workbasketAvailableDistributionTargets: [
|
||||
{
|
||||
workbasketId: 'WBI:100000000000000000000000000000000001',
|
||||
key: 'GPK_KSC',
|
||||
name: 'Gruppenpostkorb KSC',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'GROUP',
|
||||
description: 'Gruppenpostkorb KSC',
|
||||
owner: 'owner0815',
|
||||
custom1: 'ABCQVW',
|
||||
custom2: '',
|
||||
custom3: 'xyz4',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:100000000000000000000000000000000002',
|
||||
key: 'GPK_KSC_1',
|
||||
name: 'Gruppenpostkorb KSC 1',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'GROUP',
|
||||
description: 'Gruppenpostkorb KSC 1',
|
||||
owner: '',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:100000000000000000000000000000000003',
|
||||
key: 'GPK_KSC_2',
|
||||
name: 'Gruppenpostkorb KSC 2',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'GROUP',
|
||||
description: 'Gruppenpostkorb KSC 2',
|
||||
owner: '',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:000000000000000000000000000000000900',
|
||||
key: 'sort001',
|
||||
name: 'basxet0',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'TOPIC',
|
||||
description: 'Lorem ipsum dolor sit amet.',
|
||||
owner: 'Max',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:000000000000000000000000000000000901',
|
||||
key: 'Sort002',
|
||||
name: 'Basxet1',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'TOPIC',
|
||||
description: 'Lorem ipsum dolor sit amet.',
|
||||
owner: 'Max',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:000000000000000000000000000000000902',
|
||||
key: 'sOrt003',
|
||||
name: 'bAsxet2',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'TOPIC',
|
||||
description: 'Lorem ipsum dolor sit amet.',
|
||||
owner: 'Max',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:000000000000000000000000000000000903',
|
||||
key: 'soRt004',
|
||||
name: 'baSxet3',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'TOPIC',
|
||||
description: 'Lorem ipsum dolor sit amet.',
|
||||
owner: 'Max',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
},
|
||||
{
|
||||
workbasketId: 'WBI:000000000000000000000000000000000904',
|
||||
key: 'sorT005',
|
||||
name: 'basXet4',
|
||||
domain: 'DOMAIN_A',
|
||||
type: 'TOPIC',
|
||||
description: 'Lorem ipsum dolor sit amet.',
|
||||
owner: 'Max',
|
||||
custom1: '',
|
||||
custom2: '',
|
||||
custom3: '',
|
||||
custom4: '',
|
||||
orgLevel1: '',
|
||||
orgLevel2: '',
|
||||
orgLevel3: '',
|
||||
orgLevel4: '',
|
||||
markedForDeletion: false
|
||||
}
|
||||
],
|
||||
workbasketAccessItems: workbasketAccessItemsMock
|
||||
};
|
||||
|
|
|
@ -99,6 +99,10 @@ export class GetWorkbasketDistributionTargets {
|
|||
constructor(public url: string) {}
|
||||
}
|
||||
|
||||
export class GetAvailableDistributionTargets {
|
||||
static readonly type = '[Workbasket] Get available distribution targets';
|
||||
}
|
||||
|
||||
export class UpdateWorkbasketDistributionTargets {
|
||||
static readonly type = '[Workbasket] Update workbasket distribution targets';
|
||||
constructor(public url: string, public distributionTargetsIds: string[]) {}
|
||||
|
|
|
@ -60,6 +60,11 @@ export class WorkbasketSelectors {
|
|||
static workbasketDistributionTargets(state: WorkbasketStateModel): WorkbasketDistributionTargets {
|
||||
return state.workbasketDistributionTargets;
|
||||
}
|
||||
|
||||
@Selector([WorkbasketState])
|
||||
static availableDistributionTargets(state: WorkbasketStateModel): WorkbasketSummary[] {
|
||||
return state.workbasketAvailableDistributionTargets;
|
||||
}
|
||||
}
|
||||
|
||||
export interface WorkbasketAndAction {
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
CopyWorkbasket,
|
||||
CreateWorkbasket,
|
||||
DeselectWorkbasket,
|
||||
GetAvailableDistributionTargets,
|
||||
GetWorkbasketAccessItems,
|
||||
GetWorkbasketDistributionTargets,
|
||||
GetWorkbasketsSummary,
|
||||
|
@ -32,6 +33,7 @@ import { WorkbasketSummary } from '../../models/workbasket-summary';
|
|||
import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
|
||||
import { ButtonAction } from '../../../administration/models/button-action';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { RequestInProgressService } from '../../services/request-in-progress/request-in-progress.service';
|
||||
|
||||
class InitializeStore {
|
||||
static readonly type = '[Workbasket] Initializing state';
|
||||
|
@ -43,7 +45,8 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
private workbasketService: WorkbasketService,
|
||||
private location: Location,
|
||||
private notificationService: NotificationService,
|
||||
private route: ActivatedRoute
|
||||
private route: ActivatedRoute,
|
||||
private requestInProgressService: RequestInProgressService
|
||||
) {}
|
||||
|
||||
@Action(InitializeStore)
|
||||
|
@ -131,6 +134,10 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
selectedWorkbasket,
|
||||
action: ACTION.READ
|
||||
});
|
||||
ctx.dispatch(new GetWorkbasketAccessItems(ctx.getState().selectedWorkbasket._links.accessItems.href));
|
||||
ctx.dispatch(
|
||||
new GetWorkbasketDistributionTargets(ctx.getState().selectedWorkbasket._links.distributionTargets.href)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -186,6 +193,9 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
@Action(OnButtonPressed)
|
||||
doWorkbasketDetailsAction(ctx: StateContext<WorkbasketStateModel>, action: OnButtonPressed): Observable<any> {
|
||||
ctx.patchState({ button: action.button });
|
||||
setTimeout(() => {
|
||||
ctx.patchState({ button: undefined });
|
||||
}, 500);
|
||||
return of(null);
|
||||
}
|
||||
|
||||
|
@ -347,15 +357,32 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
);
|
||||
}
|
||||
|
||||
@Action(GetAvailableDistributionTargets)
|
||||
getAvailableDistributionTargets(ctx: StateContext<WorkbasketStateModel>): Observable<any> {
|
||||
return this.workbasketService.getWorkBasketsSummary(true).pipe(
|
||||
take(1),
|
||||
tap((workbasketAvailableDistributionTargets: WorkbasketSummaryRepresentation) => {
|
||||
ctx.patchState({
|
||||
workbasketAvailableDistributionTargets: workbasketAvailableDistributionTargets.workbaskets
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(UpdateWorkbasketDistributionTargets)
|
||||
updateWorkbasketDistributionTargets(
|
||||
ctx: StateContext<WorkbasketStateModel>,
|
||||
action: UpdateWorkbasketDistributionTargets
|
||||
): Observable<any> {
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
return this.workbasketService.updateWorkBasketsDistributionTargets(action.url, action.distributionTargetsIds).pipe(
|
||||
take(1),
|
||||
tap(
|
||||
(updatedWorkbasketsDistributionTargets) => {
|
||||
ctx.patchState({
|
||||
workbasketDistributionTargets: updatedWorkbasketsDistributionTargets
|
||||
});
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
this.notificationService.showToast(
|
||||
NOTIFICATION_TYPES.SUCCESS_ALERT_8,
|
||||
new Map<string, string>([['workbasketName', ctx.getState().selectedWorkbasket.name]])
|
||||
|
@ -363,6 +390,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
},
|
||||
(error) => {
|
||||
this.notificationService.triggerError(NOTIFICATION_TYPES.SAVE_ERR_3, error);
|
||||
this.requestInProgressService.setRequestInProgress(false);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -392,6 +420,7 @@ export interface WorkbasketStateModel {
|
|||
action: ACTION;
|
||||
workbasketAccessItems: WorkbasketAccessItemsRepresentation;
|
||||
workbasketDistributionTargets: WorkbasketDistributionTargets;
|
||||
workbasketAvailableDistributionTargets: WorkbasketSummary[];
|
||||
selectedComponent: WorkbasketComponent;
|
||||
button: ButtonAction | undefined;
|
||||
}
|
||||
|
|
|
@ -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