TSK-1399: Workbasket List and Details MD (#1301)
* TSK-1399: Comments in wb-list for better understanding * TSK-1399: Upgrade angular material 9, added basic selection list * TSK-1399: implement basic mat list for workbasket list * TSK-1399: Action buttons and sorting in wb-toolbar * TSK-1399: Update workbasket list item design * TSK-1399: Updated workbasket-list toolbar * TSK-1399: Import-export buttons with correct matToolTip * TSK-1399: rework pagination component * TSK-1399: Refactored the filter within filter options * TSK-1399: Refactored workbasket filter * TSK-1399: Redesigned workbasket filter * TSK-1399: Fixed wb-list-toolbar test * TSK-1399: Added todo comment so we will not forget :) * TSK-1399: fixed pagination sometimes displaying wrong numbers * TSK-1399: Partially refactored wb-information component * TSK-1399: implement new workbasket details toolbar without functionality * TSK-1399: commented old action toolbar * TSK-1399: update custom pagination component * TSK-1399: update pagination and workbasket list layout CSS * TSK-1399: Update overall layout * TSK-1399: fixed bugs regarding workbasket list * TSK-1399: added store states for selecting tabs * TSK-1399: bind workbasket details action button with workbasket information using ngxs store * TSK-1399: Update pagination to display correctly while workbaskets being loaded * TSK-1399: Implement functionality in workbasket information for action toolbar buttons * TSK-1399: fixed test for workbasket information * TSK-1399: fixed workbasket-list tests * TSK-1399: fixed tests in workbasket details, list-toolbar * TSK-1399: fixed all affected tests during MD redesign * TSK-1399: fixed workbasket information not rendering full height * TSK-1399: fixed merging issues with navbar, remove unnecessary css Co-authored-by: Sofie Hofmann <29145005+sofie29@users.noreply.github.com>
This commit is contained in:
parent
7c83c87f32
commit
356e41ea27
|
@ -321,12 +321,11 @@
|
|||
"integrity": "sha512-tphpf9QHnOPoL2Jl7KpR+R5aHNW3oifLEmRUTajJYJGvo1uzdUDE82+V9OGOinxJsYseCth9gYJhN24aYTB9NA=="
|
||||
},
|
||||
"@angular/cdk": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-8.2.3.tgz",
|
||||
"integrity": "sha512-ZwO5Sn720RA2YvBqud0JAHkZXjmjxM0yNzCO8RVtRE9i8Gl26Wk0j0nQeJkVm4zwv2QO8MwbKUKGTMt8evsokA==",
|
||||
"version": "9.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.2.4.tgz",
|
||||
"integrity": "sha512-iw2+qHMXHYVC6K/fttHeNHIieSKiTEodVutZoOEcBu9rmRTGbLB26V/CRsfIRmA1RBk+uFYWc6UQZnMC3RdnJQ==",
|
||||
"requires": {
|
||||
"parse5": "^5.0.0",
|
||||
"tslib": "^1.7.1"
|
||||
"parse5": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"@angular/cli": {
|
||||
|
@ -634,12 +633,9 @@
|
|||
"integrity": "sha512-LhjnZlC4WEsEsAJfOZLte+Lks3WBAFVeRv2lzoQNFVr/IMzBNDVfjEaaSqKF1cei3cjY39Df2nYDMJM7HfqbJA=="
|
||||
},
|
||||
"@angular/material": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-8.2.3.tgz",
|
||||
"integrity": "sha512-SOczkIaqes+r+9XF/UUiokidfFKBpHkOPIaFK857sFD0FBNPvPEpOr5oHKCG3feERRwAFqHS7Wo2ohVEWypb5A==",
|
||||
"requires": {
|
||||
"tslib": "^1.7.1"
|
||||
}
|
||||
"version": "9.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-9.2.4.tgz",
|
||||
"integrity": "sha512-LkoTXE6B0slvMhvfZDdPWaz4yaYLkaAp5VSPunI9pxGsPxzqEV9e210wC1/sjG/76Nk8Ep7/2z9XKac8Q9bMwA=="
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"version": "9.1.12",
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "9.1.12",
|
||||
"@angular/cdk": "8.2.3",
|
||||
"@angular/cdk": "9.2.4",
|
||||
"@angular/common": "9.1.12",
|
||||
"@angular/core": "9.1.12",
|
||||
"@angular/forms": "9.1.12",
|
||||
"@angular/material": "8.2.3",
|
||||
"@angular/material": "9.2.4",
|
||||
"@angular/platform-browser": "9.1.12",
|
||||
"@angular/platform-browser-dynamic": "9.1.12",
|
||||
"@angular/router": "9.1.12",
|
||||
|
|
|
@ -5,7 +5,6 @@ import { AngularSvgIconModule } from 'angular-svg-icon';
|
|||
import { AlertModule, TypeaheadModule } from 'ngx-bootstrap';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
import { TreeModule } from 'angular-tree-component';
|
||||
|
||||
import { ClassificationTypesSelectorComponent } from 'app/administration/components/classification-types-selector/classification-types-selector.component';
|
||||
import { ClassificationCategoriesService } from 'app/shared/services/classification-categories/classification-categories.service';
|
||||
|
@ -44,6 +43,9 @@ import { AdministrationOverviewComponent } from './components/administration-ove
|
|||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
|
@ -88,7 +90,10 @@ const DECLARATIONS = [
|
|||
MatTabsModule,
|
||||
MatInputModule,
|
||||
MatTooltipModule,
|
||||
MatDividerModule
|
||||
MatDividerModule,
|
||||
MatListModule,
|
||||
MatProgressBarModule,
|
||||
MatToolbarModule
|
||||
],
|
||||
providers: [
|
||||
ClassificationDefinitionService,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="classification-details">
|
||||
<taskana-shared-spinner [isRunning]="requestInProgress" class="floating"
|
||||
(spinnerIsRunning)="spinnerRunning($event)"></taskana-shared-spinner>
|
||||
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>
|
||||
|
||||
<div class="classification-details__wrapper" id="classification-details" *ngIf="classification && !spinnerIsRunning">
|
||||
|
||||
<!-- TITLE + ACTION BUTTONS -->
|
||||
|
@ -50,6 +50,7 @@
|
|||
<h6 class="classification-details__subheading" style="margin-top: 65px;"> General </h6>
|
||||
<mat-divider class="classification-details__horizontal-line"> </mat-divider>
|
||||
|
||||
<!-- KEY -->
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Key</mat-label>
|
||||
<label for="classification-key"></label>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
padding-top: 0.5rem;
|
||||
}
|
||||
.classification-details__action-toolbar {
|
||||
width: calc(100% - 450px);
|
||||
width: calc(100% - 500px);
|
||||
position: fixed;
|
||||
padding: 12px 32px 12px 24px;
|
||||
background-color: #fff;
|
||||
|
|
|
@ -30,6 +30,7 @@ import { MatMenuModule } from '@angular/material/menu';
|
|||
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';
|
||||
|
||||
@Component({ selector: 'taskana-shared-spinner', template: '' })
|
||||
class SpinnerStub {
|
||||
|
@ -128,6 +129,7 @@ describe('ClassificationDetailsComponent', () => {
|
|||
MatInputModule,
|
||||
MatOptionModule,
|
||||
MatSelectModule,
|
||||
MatProgressBarModule,
|
||||
MatMenuModule,
|
||||
BrowserAnimationsModule
|
||||
],
|
||||
|
@ -250,9 +252,6 @@ describe('ClassificationDetailsComponent', () => {
|
|||
});
|
||||
|
||||
/* HTML */
|
||||
it('should show spinner component', () => {
|
||||
expect(debugElement.nativeElement.querySelector('taskana-shared-spinner')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not show details when spinner is running', () => {
|
||||
component.spinnerIsRunning = true;
|
||||
|
|
|
@ -10,12 +10,13 @@
|
|||
</button>
|
||||
|
||||
<taskana-administration-import-export
|
||||
class="classification-list__import-export" [currentSelection]="taskanaType.CLASSIFICATIONS">
|
||||
class="classification-list__import-export" [currentSelection]="taskanaType.CLASSIFICATIONS" [parentComponent]="'classifications'">
|
||||
</taskana-administration-import-export>
|
||||
<span class="workbasket-details__spacer" style="flex: 1 1 auto"> </span>
|
||||
|
||||
<button mat-stroked-button matTooltip="Display filter options" (click)="displayFilter()">
|
||||
<mat-icon *ngIf="!showFilter">search</mat-icon>
|
||||
<mat-icon *ngIf="showFilter" color="warn">clear</mat-icon>
|
||||
<button mat-stroked-button matTooltip="Display filter options" (click)="displayFilter()" style="color: #555">
|
||||
<mat-icon *ngIf="!showFilter">filter_list</mat-icon>
|
||||
<mat-icon *ngIf="showFilter">keyboard_arrow_up</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -60,8 +61,8 @@
|
|||
|
||||
|
||||
<!-- CLASSIFICATION TREE -->
|
||||
<taskana-shared-spinner [isRunning]="requestInProgress"
|
||||
positionClass="centered-spinner-whole-screen"></taskana-shared-spinner>
|
||||
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>
|
||||
|
||||
<taskana-administration-tree *ngIf="(classifications && classifications.length) else empty_classifications"
|
||||
[filterText]="inputValue" [filterIcon]="selectedCategory"
|
||||
(switchTaskanaSpinnerEmit)="requestInProgress=$event"></taskana-administration-tree>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.classification-list {
|
||||
height: calc(100vh - 55px);
|
||||
width: 450px;
|
||||
width: 500px;
|
||||
}
|
||||
.classification-list__action-toolbar {
|
||||
padding: 0 16px;
|
||||
|
@ -40,6 +40,10 @@
|
|||
top: -2px;
|
||||
}
|
||||
|
||||
.category-filter__filter-button {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.filter__input {
|
||||
width: 100%;
|
||||
margin-right: 12px;
|
||||
|
|
|
@ -18,10 +18,12 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
|||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
|
||||
@Component({ selector: 'taskana-administration-import-export', template: '' })
|
||||
class ImportExportStub {
|
||||
@Input() currentSelection: TaskanaType;
|
||||
@Input() parentComponent: string;
|
||||
}
|
||||
|
||||
@Component({ selector: 'taskana-administration-classification-types-selector', template: '' })
|
||||
|
@ -88,7 +90,8 @@ describe('ClassificationListComponent', () => {
|
|||
MatMenuModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
BrowserAnimationsModule
|
||||
BrowserAnimationsModule,
|
||||
MatProgressBarModule
|
||||
],
|
||||
declarations: [
|
||||
ClassificationListComponent,
|
||||
|
@ -188,10 +191,6 @@ describe('ClassificationListComponent', () => {
|
|||
});
|
||||
|
||||
/* HTML: CLASSIFICATION TREE */
|
||||
it('should display spinner component', () => {
|
||||
expect(debugElement.nativeElement.querySelector('taskana-shared-spinner')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display tree component when classifications exist', () => {
|
||||
component.classifications = [{ classificationId: '1' }, { classificationId: '2' }];
|
||||
fixture.detectChanges();
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
|
||||
<button mat-stroked-button class="mr-1" matTooltip="Import classification" [ngClass]="{disabled: uploadService?.isInUse}" (click)="selectedFile.click()" title="Import">
|
||||
Import
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
</button>
|
||||
|
||||
<form class="hidden" enctype="multipart/form-data" method="post">
|
||||
<input #selectedFile type="file" accept=".json" (change)="uploadFile()" class="hide" />
|
||||
</form>
|
||||
|
||||
<button mat-stroked-button class="mr-1" matTooltip="Export classification" [matMenuTriggerFor]="menu" [ngClass]="{disabled: uploadService?.isInUse}" title="Export">
|
||||
Export
|
||||
<mat-icon>cloud_download</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item href="javascript:void(0)" (click)="export()">
|
||||
All Domains
|
||||
<div class="import-export">
|
||||
<button mat-stroked-button class="mr-1" matTooltip="Import {{parentComponent}}" [ngClass]="{disabled: uploadService?.isInUse}" (click)="selectedFile.click()" title="Import">
|
||||
Import
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
</button>
|
||||
<button mat-menu-item *ngFor="let domain of domains" href="javascript:void(0)" (click)="export(domain)">
|
||||
{{domain === '' ? 'Master' : domain}}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<form class="hidden" enctype="multipart/form-data" method="post">
|
||||
<input #selectedFile type="file" accept=".json" (change)="uploadFile()" class="hide" />
|
||||
</form>
|
||||
|
||||
<button mat-stroked-button class="mr-1" matTooltip="Export {{parentComponent}}" [matMenuTriggerFor]="menu" [ngClass]="{disabled: uploadService?.isInUse}" title="Export">
|
||||
Export
|
||||
<mat-icon>cloud_download</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item href="javascript:void(0)" (click)="export()">
|
||||
All Domains
|
||||
</button>
|
||||
<button mat-menu-item *ngFor="let domain of domains" href="javascript:void(0)" (click)="export(domain)">
|
||||
{{domain === '' ? 'Master' : domain}}
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
.import-export {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
mat-icon {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #555;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import { NotificationService } from '../../../shared/services/notifications/noti
|
|||
})
|
||||
export class ImportExportComponent implements OnInit {
|
||||
@Input() currentSelection: TaskanaType;
|
||||
@Input() parentComponent: string;
|
||||
|
||||
@ViewChild('selectedFile', { static: true })
|
||||
selectedFileInput;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<svg-icon class="{{selected? 'white': 'blue' }} small" src="./assets/icons/{{getIconPath(type)}}" data-toggle="tooltip"
|
||||
<svg-icon class="{{selected? 'white': 'grayIcon' }} {{size}}" src="./assets/icons/{{getIconPath(type)}}"
|
||||
[title]="!type? 'All' : type"></svg-icon>
|
||||
{{text}}
|
||||
|
|
Before Width: | Height: | Size: 175 B After Width: | Height: | Size: 160 B |
|
@ -0,0 +1,8 @@
|
|||
.large {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.grayIcon {
|
||||
fill: #555;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { ICONTYPES } from 'app/shared/models/icon-types';
|
||||
|
||||
@Component({
|
||||
|
@ -19,6 +19,9 @@ export class IconTypeComponent {
|
|||
@Input()
|
||||
text: string;
|
||||
|
||||
@Input()
|
||||
size = 'small';
|
||||
|
||||
public static get allTypes(): Map<string, string> {
|
||||
return new Map([
|
||||
['PERSONAL', 'Personal'],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||
<div *ngIf="workbasket" id="wb-information">
|
||||
|
||||
<!-- ACTION TOOLBAR -->
|
||||
<!--
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right btn-group">
|
||||
<button type="button" (click)="onSubmit()" [disabled]="action === 'COPY'" data-toggle="tooltip" title="Save" class="btn btn-default btn-primary">
|
||||
|
@ -14,9 +15,10 @@
|
|||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
</h4>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- ACCESS ITEMS -->
|
||||
<div class="panel-body">
|
||||
<div class="workbasket-access-items">
|
||||
<form [formGroup]="AccessItemsForm">
|
||||
<table formArrayName="accessItemsGroups" id="table-access-items" class="table table-striped table-center">
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
@import '../../../../theme/colors';
|
||||
|
||||
.workbasket-access-items {
|
||||
max-width: calc(100vw - 500px);
|
||||
}
|
||||
td > input[type='checkbox'] {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
|
|
@ -158,11 +158,8 @@ describe('WorkbasketAccessItemsComponent', () => {
|
|||
});
|
||||
|
||||
it('should undo changes when undo button is clicked', () => {
|
||||
const undoButton = debugElement.nativeElement.querySelector('button.undo-button');
|
||||
const clearSpy = jest.spyOn(component, 'clear');
|
||||
expect(undoButton.title).toMatch('Undo Changes');
|
||||
|
||||
undoButton.click();
|
||||
component.clear();
|
||||
expect(clearSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,34 +1,53 @@
|
|||
<div class="container container-scrollable" style="min-width: 70vw;">
|
||||
<taskana-shared-spinner [isRunning]="requestInProgress"></taskana-shared-spinner>
|
||||
<div id="workbasket-details" *ngIf="workbasket && !requestInProgress">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" (click)="selectTab('information')" [ngClass]="{'active':tabSelected === 'information'}">
|
||||
<a aria-controls="work baskets" role="tab" aria-expanded="true">
|
||||
Information</a>
|
||||
</li>
|
||||
<li role="presentation" (click)="selectTab('accessItems')" [ngClass]="{
|
||||
'disabled': action ==='CREATE',
|
||||
'active':tabSelected === 'accessItems'}">
|
||||
<a aria-controls="Acccess" role="tab" aria-expanded="true">
|
||||
Access</a>
|
||||
</li>
|
||||
<li role="presentation" (click)="selectTab('distributionTargets')" [ngClass]="{
|
||||
'disabled': action ==='CREATE',
|
||||
'active':tabSelected === 'distributionTargets'}">
|
||||
<a aria-controls="distribution targets" role="tab" data-toggle="tab" aria-expanded="true">
|
||||
Distribution targets</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" [ngClass]="{'active':tabSelected === 'information'}" id="work-baskets">
|
||||
<taskana-administration-workbasket-information [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-information>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="access-items" [ngClass]="{'active':tabSelected === 'accessItems'}">
|
||||
<taskana-administration-workbasket-access-items [workbasket]="workbasket" [action]="action" [active]="tabSelected"></taskana-administration-workbasket-access-items>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="distribution-targets" [ngClass]="{'active':tabSelected === 'distributionTargets'}">
|
||||
<taskana-administration-workbasket-distribution-targets [workbasket]="workbasket" [action]="action" [active]="tabSelected"></taskana-administration-workbasket-distribution-targets>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workbasket-details">
|
||||
<mat-toolbar class="workbasket-details__toolbar">
|
||||
<h4 class="workbasket-details__title">{{workbasket.name}}
|
||||
<span class="workbasket-details__title-badge" *ngIf="!workbasket.workbasketId"> {{ badgeMessage }}</span>
|
||||
</h4>
|
||||
<span class="workbasket-details__spacer"></span>
|
||||
<button mat-button class="workbasket-details__button workbasket-details__save-button" matTooltip="Save changes in current workbasket" (click)="onSubmit()">
|
||||
Save
|
||||
<mat-icon class="md-20">save</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-stroked-button class="workbasket-details__button" matTooltip="Revert changes to previous saved state" (click)="onRestore()">
|
||||
Undo Changes
|
||||
<mat-icon class="button__green-blue md-20">restore</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-stroked-button [matMenuTriggerFor]="buttonMenu" matTooltip="More actions" class="action-toolbar__button" id="action-toolbar__more-buttons">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-menu #buttonMenu="matMenu">
|
||||
<button mat-menu-item class="workbasket-details__dropdown" matTooltip="Copy current values to create new workbasket" (click)="onCopy()">
|
||||
<mat-icon class="button__green-blue">content_copy</mat-icon>
|
||||
<span>Copy</span>
|
||||
</button>
|
||||
<button mat-menu-item class="workbasket-details__dropdown" matTooltip="Remove this workbasket as distribution target" (click)="onRemoveAsDistributionTarget()">
|
||||
<mat-icon class="button__red">remove_circle_outline</mat-icon>
|
||||
<span>Remove as distribution target</span>
|
||||
</button>
|
||||
<button mat-menu-item class="workbasket-details__dropdown" matTooltip="Delete this workbasket" (click)="onRemoveWorkbasket()">
|
||||
<mat-icon class="button__red">delete</mat-icon>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
<button mat-menu-item class="workbasket-details__dropdown" style="border-bottom-style: none;" matTooltip="Close this workbasket and discard all changes" (click)="onClose()">
|
||||
<mat-icon>close</mat-icon>
|
||||
<span>Close</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</mat-toolbar>
|
||||
<mat-tab-group animationDuration="0ms" (selectedIndexChange)="selectComponent($event)">
|
||||
<mat-tab label="Information">
|
||||
<taskana-administration-workbasket-information [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-information>
|
||||
</mat-tab>
|
||||
<mat-tab label="Access">
|
||||
<taskana-administration-workbasket-access-items [workbasket]="workbasket" [action]="action" [active]="tabSelected"></taskana-administration-workbasket-access-items>
|
||||
</mat-tab>
|
||||
<mat-tab label="Distribution Targets">
|
||||
<taskana-administration-workbasket-distribution-targets [workbasket]="workbasket" [action]="action" [active]="tabSelected"></taskana-administration-workbasket-distribution-targets>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.workbasket-details {
|
||||
width: 100%;
|
||||
height: calc(100vh - 100px);
|
||||
}
|
||||
.workbasket-details__toolbar {
|
||||
background-color: white;
|
||||
padding: 16px 36px 12px 24px;
|
||||
}
|
||||
.workbasket-details__title {
|
||||
font-size: 1.5rem;
|
||||
padding: 4px;
|
||||
}
|
||||
.workbasket-details__title-badge {
|
||||
background-color: $brown;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.workbasket-details__spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.workbasket-details__button {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.workbasket-details__save-button {
|
||||
background-color: $aquamarine;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.button__green-blue {
|
||||
color: $aquamarine;
|
||||
}
|
||||
|
||||
.button__red {
|
||||
color: $invalid;
|
||||
}
|
|
@ -15,11 +15,20 @@ import { RequestInProgressService } from '../../../shared/services/request-in-pr
|
|||
import { SelectedRouteService } from '../../../shared/services/selected-route/selected-route';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { selectedWorkbasketMock } from '../../../shared/store/mock-data/mock-store';
|
||||
import { ClassificationCategoriesService } from '../../../shared/services/classification-categories/classification-categories.service';
|
||||
import {
|
||||
engineConfigurationMock,
|
||||
selectedWorkbasketMock,
|
||||
workbasketReadStateMock
|
||||
} from '../../../shared/store/mock-data/mock-store';
|
||||
import { StartupService } from '../../../shared/services/startup/startup.service';
|
||||
import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from '../../../shared/services/window/window.service';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
@Component({ selector: 'taskana-shared-spinner', template: '' })
|
||||
class SpinnerStub {
|
||||
|
@ -74,7 +83,13 @@ describe('WorkbasketDetailsComponent', () => {
|
|||
HttpClientTestingModule,
|
||||
RouterTestingModule.withRoutes([]),
|
||||
MatSnackBarModule,
|
||||
MatDialogModule
|
||||
MatDialogModule,
|
||||
MatIconModule,
|
||||
MatProgressBarModule,
|
||||
MatTabsModule,
|
||||
MatMenuModule,
|
||||
MatToolbarModule,
|
||||
BrowserAnimationsModule
|
||||
],
|
||||
declarations: [
|
||||
WorkbasketDetailsComponent,
|
||||
|
@ -100,6 +115,10 @@ describe('WorkbasketDetailsComponent', () => {
|
|||
component = fixture.debugElement.componentInstance;
|
||||
store = TestBed.inject(Store);
|
||||
actions$ = TestBed.inject(Actions);
|
||||
store.reset({
|
||||
...store.snapshot(),
|
||||
workbasket: workbasketReadStateMock
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
|
@ -107,34 +126,12 @@ describe('WorkbasketDetailsComponent', () => {
|
|||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display loading spinner while content loads', () => {
|
||||
component.requestInProgress = true;
|
||||
fixture.detectChanges();
|
||||
const spinner = debugElement.nativeElement.querySelector('taskana-shared-spinner');
|
||||
expect(spinner).toBeTruthy();
|
||||
expect(spinner.style.display).toContain('');
|
||||
});
|
||||
|
||||
it('should render workbasket-details when workbasket exists and request is not in progress', () => {
|
||||
component.workbasket = { workbasketId: '1' };
|
||||
component.requestInProgress = false;
|
||||
fixture.detectChanges();
|
||||
const workbasketDetails = debugElement.nativeElement.querySelector('#workbasket-details');
|
||||
expect(workbasketDetails).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render information, access items and distribution targets components', () => {
|
||||
it('should render information component when workbasket details is opened', () => {
|
||||
component.workbasket = { workbasketId: '1' };
|
||||
component.requestInProgress = false;
|
||||
fixture.detectChanges();
|
||||
const information = debugElement.nativeElement.querySelector('taskana-administration-workbasket-information');
|
||||
const accessItems = debugElement.nativeElement.querySelector('taskana-administration-workbasket-access-items');
|
||||
const distributionTargets = debugElement.nativeElement.querySelector(
|
||||
'taskana-administration-workbasket-distribution-targets'
|
||||
);
|
||||
expect(information).toBeTruthy();
|
||||
expect(accessItems).toBeTruthy();
|
||||
expect(distributionTargets).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render new workbasket when action is CREATE', () => {
|
||||
|
|
|
@ -1,27 +1,35 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { Workbasket } from 'app/shared/models/workbasket';
|
||||
import { ACTION } from 'app/shared/models/action';
|
||||
import { DomainService } from 'app/shared/services/domain/domain.service';
|
||||
import { ImportExportService } from 'app/administration/services/import-export.service';
|
||||
import { Select } from '@ngxs/store';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { WorkbasketAndAction, WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
||||
import { TaskanaDate } from '../../../shared/util/taskana.date';
|
||||
import { ICONTYPES } from '../../../shared/models/icon-types';
|
||||
import {
|
||||
DeselectWorkbasket,
|
||||
OnButtonPressed,
|
||||
SelectComponent
|
||||
} from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { ButtonAction } from '../../models/button-action';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-administration-workbasket-details',
|
||||
templateUrl: './workbasket-details.component.html'
|
||||
templateUrl: './workbasket-details.component.html',
|
||||
styleUrls: ['./workbasket-details.component.scss']
|
||||
})
|
||||
export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
||||
export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges {
|
||||
workbasket: Workbasket;
|
||||
workbasketCopy: Workbasket;
|
||||
selectedId: string;
|
||||
requestInProgress = false;
|
||||
action: ACTION;
|
||||
tabSelected = 'information';
|
||||
badgeMessage = '';
|
||||
|
||||
@Select(WorkbasketSelectors.selectedWorkbasket)
|
||||
selectedWorkbasket$: Observable<Workbasket>;
|
||||
|
@ -38,7 +46,8 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
|||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private domainService: DomainService,
|
||||
private importExportService: ImportExportService
|
||||
private importExportService: ImportExportService,
|
||||
private store: Store
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -47,11 +56,13 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
|||
if (this.action === ACTION.CREATE) {
|
||||
this.tabSelected = 'information';
|
||||
this.selectedId = undefined;
|
||||
this.badgeMessage = 'Creating new workbasket';
|
||||
this.initWorkbasket();
|
||||
} else if (this.action === ACTION.COPY) {
|
||||
// delete this.workbasket.key;
|
||||
this.workbasketCopy = this.workbasket;
|
||||
this.getWorkbasketInformation();
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
} else if (typeof selectedWorkbasketAndAction.selectedWorkbasket !== 'undefined') {
|
||||
this.workbasket = { ...selectedWorkbasketAndAction.selectedWorkbasket };
|
||||
this.getWorkbasketInformation(this.workbasket);
|
||||
|
@ -68,6 +79,8 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes?: SimpleChanges) {}
|
||||
|
||||
addDateToWorkbasket(workbasket: Workbasket) {
|
||||
const date = TaskanaDate.getDate();
|
||||
workbasket.created = date;
|
||||
|
@ -130,6 +143,48 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
selectComponent(index) {
|
||||
this.store.dispatch(new SelectComponent(index));
|
||||
switch (index) {
|
||||
case 0:
|
||||
this.selectTab('information');
|
||||
break;
|
||||
case 1:
|
||||
this.selectTab('access-items');
|
||||
break;
|
||||
case 2:
|
||||
this.selectTab('distribution-targets');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.store.dispatch(new OnButtonPressed(ButtonAction.SAVE));
|
||||
}
|
||||
|
||||
onRestore() {
|
||||
this.store.dispatch(new OnButtonPressed(ButtonAction.UNDO));
|
||||
}
|
||||
|
||||
onCopy() {
|
||||
this.store.dispatch(new OnButtonPressed(ButtonAction.COPY));
|
||||
}
|
||||
|
||||
onRemoveAsDistributionTarget() {
|
||||
this.store.dispatch(new OnButtonPressed(ButtonAction.REMOVE_AS_DISTRIBUTION_TARGETS));
|
||||
}
|
||||
|
||||
onRemoveWorkbasket() {
|
||||
this.store.dispatch(new OnButtonPressed(ButtonAction.DELETE));
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.store.dispatch(new OnButtonPressed(ButtonAction.CLOSE));
|
||||
this.store.dispatch(new DeselectWorkbasket());
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<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"
|
||||
|
@ -15,7 +16,7 @@
|
|||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
-->
|
||||
<!-- DISTRIBUTION TABLE-->
|
||||
<div #panelBody class="panel-body">
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<taskana-shared-spinner [isRunning]="requestInProgress" class="floating"></taskana-shared-spinner>
|
||||
<div *ngIf="workbasket" id="wb-information" class="panel panel-default">
|
||||
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>
|
||||
<div *ngIf="workbasket" id="wb-information">
|
||||
<!--
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right btn-group">
|
||||
<button type="button" (click)="onSubmit()" data-toggle="tooltip" title="Save"
|
||||
|
@ -28,36 +29,97 @@
|
|||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
</h4>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="workbasket-information-wrapper">
|
||||
<ng-form #WorkbasketForm="ngForm">
|
||||
<div class="col-md-6">
|
||||
|
||||
<!-- GENERAL FIELDS -->
|
||||
<div class="workbasket-information">
|
||||
|
||||
<h6 class="workbasket-information__subheading"> General </h6>
|
||||
<mat-divider class="workbasket-information__horizontal-line"> </mat-divider>
|
||||
|
||||
<!-- KEY -->
|
||||
<div class="form-group required">
|
||||
<label for="wb-key" class="control-label">Key</label>
|
||||
<input type="text" required maxlength="64" #key="ngModel" class="form-control" id="wb-key"
|
||||
placeholder="Key"
|
||||
[(ngModel)]="workbasket.key" name="workbasket.key" (input)="validateInputOverflow(key, 64, $event)">
|
||||
<div *ngIf="inputOverflowMap.get(key.name)" class="error">{{lengthError}}</div>
|
||||
<taskana-shared-field-error-display [displayError]="!isFieldValid('workbasket.key')"
|
||||
[validationTrigger]="this.toggleValidationMap.get('workbasket.key')"
|
||||
errorMessage="* Key is required">
|
||||
</taskana-shared-field-error-display>
|
||||
</div>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Key</mat-label>
|
||||
<label for="workbasket-key"></label>
|
||||
<input matInput required type="text" #key="ngModel" maxlength="64"
|
||||
[disabled]="action == 0 || action == 3"
|
||||
id="workbasket-key" placeholder="Key" [(ngModel)]="workbasket.key"
|
||||
name="workbasket.key" (input)="validateInputOverflow(key, 64, $event)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(key.name)" class="error">{{lengthError}}</div>
|
||||
<taskana-shared-field-error-display [displayError]="!isFieldValid('workbasket.key')"
|
||||
[validationTrigger]="this.toggleValidationMap.get('workbasket.key')"
|
||||
errorMessage="* Key is required">
|
||||
</taskana-shared-field-error-display>
|
||||
|
||||
<!-- NAME -->
|
||||
<div class="form-group required">
|
||||
<label for="wb-name" class="control-label">Name</label>
|
||||
<input type="text" required maxlength="255" #name="ngModel" class="form-control" id="wb-name"
|
||||
placeholder="Name"
|
||||
[(ngModel)]="workbasket.name" name="workbasket.name" (input)="validateInputOverflow(name, 255)">
|
||||
<div *ngIf="inputOverflowMap.get(name.name)" class="error">{{lengthError}}</div>
|
||||
<taskana-shared-field-error-display [displayError]="!isFieldValid('workbasket.name')"
|
||||
[validationTrigger]="this.toggleValidationMap.get('workbasket.name')"
|
||||
errorMessage="* Name is required">
|
||||
</taskana-shared-field-error-display>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Name</mat-label>
|
||||
<label for="workbasket-name"></label>
|
||||
<input matInput type="text" required maxlength="255" #name="ngModel"
|
||||
id="workbasket-name" placeholder="Name"
|
||||
[(ngModel)]="workbasket.name" name="workbasket.name"
|
||||
(input)="validateInputOverflow(name, 255)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(name.name)" class="error">{{lengthError}}</div>
|
||||
<taskana-shared-field-error-display [displayError]="!isFieldValid('workbasket.name')"
|
||||
[validationTrigger]="this.toggleValidationMap.get('workbasket.name')"
|
||||
errorMessage="* Name is required">
|
||||
</taskana-shared-field-error-display>
|
||||
|
||||
|
||||
|
||||
<div class="workbasket-information__domain-and-type">
|
||||
|
||||
<!-- DOMAIN -->
|
||||
<mat-form-field class="workbasket-information__mat-form-field" appearance="outline">
|
||||
<mat-label>Domain</mat-label>
|
||||
<label for="workbasket-domain"></label>
|
||||
<input matInput type="text" disabled id="workbasket-domain"
|
||||
placeholder="Domain" [(ngModel)]="workbasket.domain"
|
||||
name="classification.domain">
|
||||
</mat-form-field>
|
||||
|
||||
<!-- TYPE -->
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Type</mat-label>
|
||||
<mat-select [(value)]="this.workbasket.type">
|
||||
<mat-select-trigger>
|
||||
<taskana-administration-icon-type [type]='workbasket.type'></taskana-administration-icon-type>
|
||||
{{allTypes.get(workbasket.type)}}
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let type of allTypes | mapValues | removeEmptyType" value="{{type.key}}">
|
||||
<taskana-administration-icon-type
|
||||
[type]='type.key' [text]="type.value">
|
||||
</taskana-administration-icon-type>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- DESCRIPTION -->
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Description</mat-label>
|
||||
<label for="workbasket-description"></label>
|
||||
<textarea matInput
|
||||
cdkTextareaAutosize
|
||||
cdkAutosizeMinRows="1"
|
||||
cdkAutosizeMaxRows="5"
|
||||
maxlength="255"
|
||||
id="workbasket-description" placeholder="Description"
|
||||
[(ngModel)]="workbasket.description"
|
||||
name="workbasket.description" #description="ngModel"
|
||||
(input)="validateInputOverflow(description, 255)"></textarea>
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(description.name)" class="error">{{lengthError}}</div>
|
||||
|
||||
|
||||
<!-- OWNER -->
|
||||
<div class="input-group form-group col-xs-12 required">
|
||||
<label for="wb-owner" class="control-label ">Owner</label>
|
||||
|
@ -70,6 +132,7 @@
|
|||
width="100%" (input)="validateInputOverflow(owner, 128)">
|
||||
<div *ngIf="inputOverflowMap.get(owner.name)" class="error">{{lengthError}}</div>
|
||||
</taskana-shared-type-ahead>
|
||||
|
||||
<ng-template #ownerInput>
|
||||
<input type="text" required maxlength="128" #owner="ngModel" class="form-control" id="wb-owner"
|
||||
placeholder="Owner"
|
||||
|
@ -82,93 +145,68 @@
|
|||
</ng-template>
|
||||
</div>
|
||||
|
||||
<!-- DOMAIN -->
|
||||
<div class="form-group ">
|
||||
<label for="wb-domain" class="control-label">Domain</label>
|
||||
<input type="text" #domain="ngModel" class="form-control" disabled id="wb-domain"
|
||||
placeholder="Domain"
|
||||
[(ngModel)]="workbasket.domain" name="workbasket.domain">
|
||||
</div>
|
||||
|
||||
<!-- TYPE & DESCRIPTION-->
|
||||
<div class="row">
|
||||
<div class="form-group col-xs-4">
|
||||
<label class="control-label">Type</label>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-default" type="button" id="dropdownMenu24"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<taskana-administration-icon-type
|
||||
[type]='workbasket.type'></taskana-administration-icon-type>
|
||||
{{allTypes.get(workbasket.type)}}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu" aria-labelledby="dropdownMenu">
|
||||
<li>
|
||||
<a *ngFor="let type of allTypes | mapValues | removeEmptyType"
|
||||
(click)="selectType(type.key)">
|
||||
<taskana-administration-icon-type [type]='type.key'
|
||||
[text]="type.value"></taskana-administration-icon-type>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-xs-8">
|
||||
<label for="wb-description" class="control-label">Description</label>
|
||||
<textarea #description="ngModel" maxlength="255" class="form-control" rows="7" id="wb-description"
|
||||
placeholder="Description"
|
||||
[(ngModel)]="workbasket.description" name="workbasket.description"
|
||||
(input)="validateInputOverflow(description, 255)"></textarea>
|
||||
<div *ngIf="inputOverflowMap.get(description.name)" class="error">{{lengthError}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="wb-org-level-1" class="control-label">OrgLevel 1</label>
|
||||
<input type="text" class="form-control" id="wb-org-level-1" placeholder="OrgLevel 1"
|
||||
[(ngModel)]="workbasket.orgLevel1"
|
||||
name="workbasket.orgLevel1" maxlength="255" #orgLevel1="ngModel"
|
||||
(input)="validateInputOverflow(orgLevel1, 255)">
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel1.name)" class="error">{{lengthError}}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="wb-org-level-2" class="control-label">OrgLevel 2</label>
|
||||
<input type="text" class="form-control" id="wb-org-level-2" placeholder="OrgLevel 2"
|
||||
[(ngModel)]="workbasket.orgLevel2"
|
||||
name="workbasket.orgLevel2" maxlength="255" #orgLevel2="ngModel"
|
||||
(input)="validateInputOverflow(orgLevel2, 255)">
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel2.name)" class="error">{{lengthError}}</div>
|
||||
</div>
|
||||
<div class="form-group" style="padding-top: 18px;">
|
||||
<label for="wb-org-level-3" class="control-label">OrgLevel 3</label>
|
||||
<input type="text" class="form-control" id="wb-org-level-3" placeholder="OrgLevel 3"
|
||||
[(ngModel)]="workbasket.orgLevel3"
|
||||
name="workbasket.orgLevel3" maxlength="255" #orgLevel3="ngModel"
|
||||
(input)="validateInputOverflow(orgLevel3, 255)">
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel3.name)" class="error">{{lengthError}}</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="wb-org-level-4" class="control-label">OrgLevel 4</label>
|
||||
<input type="text" class="form-control" id="wb-org-level-4" placeholder="OrgLevel 4"
|
||||
[(ngModel)]="workbasket.orgLevel4"
|
||||
name="workbasket.orgLevel4" maxlength="255" #orgLevel4="ngModel"
|
||||
(input)="validateInputOverflow(orgLevel4, 255)">
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel4.name)" class="error">{{lengthError}}</div>
|
||||
</div>
|
||||
<ng-container *ngFor="let customField of customFields$ | async; let index = index">
|
||||
<div *ngIf="customField.visible" class="custom-fields form-group">
|
||||
<label for='wb-custom-{{index+1}}' class="control-label">{{customField.field}}</label>
|
||||
<input type="text" class="form-control" id="wb-custom-{{index+1}}"
|
||||
[placeholder]="customField.field"
|
||||
[(ngModel)]="workbasket[getWorkbasketCustomProperty(index + 1)]"
|
||||
name="workbasket[{{getWorkbasketCustomProperty(index + 1)}}]" maxlength="255" #custom="ngModel"
|
||||
(input)="validateInputOverflow(custom, 255)">
|
||||
<!-- ORGASATIONAL LEVELS -->
|
||||
<h6 class="workbasket-information__subheading" style="margin-top: 65px;"> Organisational Levels </h6>
|
||||
<mat-divider class="workbasket-information__horizontal-line"> </mat-divider>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>OrgLevel 1</mat-label>
|
||||
<input matInput type="text" #orgLevel1="ngModel" maxlength="255"
|
||||
placeholder="OrgLevel 1" [(ngModel)]="workbasket.orgLevel1"
|
||||
name="workbasket.orgLevel1" (input)="validateInputOverflow(orgLevel1, 255)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel1.name)" class="error">{{lengthError}}</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>OrgLevel 2</mat-label>
|
||||
<input matInput type="text" #orgLevel2="ngModel" maxlength="255"
|
||||
placeholder="OrgLevel 2" [(ngModel)]="workbasket.orgLevel2"
|
||||
name="workbasket.orgLevel2" (input)="validateInputOverflow(orgLevel2, 255)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel2.name)" class="error">{{lengthError}}</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>OrgLevel 3</mat-label>
|
||||
<input matInput type="text" #orgLevel3="ngModel" maxlength="255"
|
||||
placeholder="OrgLevel 3" [(ngModel)]="workbasket.orgLevel3"
|
||||
name="workbasket.orgLevel3" (input)="validateInputOverflow(orgLevel3, 255)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel3.name)" class="error">{{lengthError}}</div>
|
||||
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>OrgLevel 4</mat-label>
|
||||
<input matInput type="text" #orgLevel4="ngModel" maxlength="255"
|
||||
placeholder="OrgLevel 4" [(ngModel)]="workbasket.orgLevel4"
|
||||
name="workbasket.orgLevel4" (input)="validateInputOverflow(orgLevel4, 255)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(orgLevel4.name)" class="error">{{lengthError}}</div>
|
||||
|
||||
|
||||
<!-- CUSTOM FIELDS -->
|
||||
<h6 class="workbasket-information__subheading" style="margin-top: 65px;"> Custom Fields </h6>
|
||||
<mat-divider class="workbasket-information__horizontal-line"> </mat-divider>
|
||||
|
||||
<div *ngFor="let customField of customFields$ | async; let index = index">
|
||||
<div *ngIf="customField.visible">
|
||||
|
||||
<mat-form-field appearance="outline" class="workbasket-information__custom-fields">
|
||||
<mat-label>{{customField.field}}</mat-label>
|
||||
<label for='wb-custom-{{index+1}}'></label>
|
||||
<input matInput type="text"
|
||||
[placeholder]="customField.field"
|
||||
[(ngModel)]="workbasket[getWorkbasketCustomProperty(index + 1)]"
|
||||
id="wb-custom-{{index+1}}"
|
||||
name="workbasket[{{getWorkbasketCustomProperty(index + 1)}}]"
|
||||
maxlength="255"
|
||||
#custom="ngModel"
|
||||
(input)="validateInputOverflow(custom, 255)">
|
||||
</mat-form-field>
|
||||
<div *ngIf="inputOverflowMap.get(custom.name)" class="error">{{lengthError}}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,35 @@
|
|||
.workbasket-information-wrapper {
|
||||
height: calc(100vh - 213px);
|
||||
overflow-y: auto ;
|
||||
}
|
||||
.workbasket-information {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.workbasket-information__subheading {
|
||||
font-weight: bold;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.workbasket-information__horizontal-line {
|
||||
margin: 5px 5px 25px 5px;
|
||||
border-top-color: #555;
|
||||
border-top-width: 1.35px;
|
||||
}
|
||||
|
||||
.workbasket-information__domain-and-type {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.workbasket-information__mat-form-field {
|
||||
width: 70%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
min-width: auto;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@ import {
|
|||
import { StartupService } from '../../../shared/services/startup/startup.service';
|
||||
import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from '../../../shared/services/window/window.service';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
@Component({ selector: 'taskana-shared-spinner', template: '' })
|
||||
class SpinnerStub {
|
||||
|
@ -59,7 +64,8 @@ const workbasketServiceMock = jest.fn().mockImplementation(
|
|||
triggerWorkBasketSaved: triggerWorkbasketSavedFn,
|
||||
updateWorkbasket: 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 }))
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -103,7 +109,12 @@ describe('WorkbasketInformationComponent', () => {
|
|||
TypeaheadModule.forRoot(),
|
||||
ReactiveFormsModule,
|
||||
RouterTestingModule.withRoutes([]),
|
||||
BrowserAnimationsModule
|
||||
BrowserAnimationsModule,
|
||||
MatProgressBarModule,
|
||||
MatDividerModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatSelectModule
|
||||
],
|
||||
declarations: [
|
||||
WorkbasketInformationComponent,
|
||||
|
@ -149,7 +160,7 @@ describe('WorkbasketInformationComponent', () => {
|
|||
});
|
||||
|
||||
it('should display custom fields correctly', () => {
|
||||
const customFields = debugElement.nativeElement.getElementsByClassName('custom-fields');
|
||||
const customFields = debugElement.nativeElement.getElementsByClassName('workbasket-information__custom-fields');
|
||||
expect(customFields.length).toBe(3); //mock data has custom1->4 but engineConfig disables custom3 -> [1,2,4]
|
||||
});
|
||||
|
||||
|
@ -171,12 +182,6 @@ describe('WorkbasketInformationComponent', () => {
|
|||
expect(component.badgeMessage).toContain(`Copying workbasket: ${component.workbasket.key}`);
|
||||
});
|
||||
|
||||
it('should set type variable in selectType', () => {
|
||||
const type = ICONTYPES.GROUP;
|
||||
component.selectType(type);
|
||||
expect(component.workbasket.type).toMatch(type);
|
||||
});
|
||||
|
||||
it('should submit when validatorService is true', () => {
|
||||
const formsValidatorService = TestBed.inject(FormsValidatorService);
|
||||
component.onSubmit();
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChil
|
|||
import { Observable, Subject } from 'rxjs';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { Select, Store } from '@ngxs/store';
|
||||
import { ICONTYPES } from 'app/shared/models/icon-types';
|
||||
import { ACTION } from 'app/shared/models/action';
|
||||
import { customFieldCount, Workbasket } from 'app/shared/models/workbasket';
|
||||
import { TaskanaDate } from 'app/shared/util/taskana.date';
|
||||
|
@ -10,7 +9,7 @@ import { SavingInformation, SavingWorkbasketService } from 'app/administration/s
|
|||
import { WorkbasketService } from 'app/shared/services/workbasket/workbasket.service';
|
||||
import { RequestInProgressService } from 'app/shared/services/request-in-progress/request-in-progress.service';
|
||||
import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service';
|
||||
import { map, takeUntil } from 'rxjs/operators';
|
||||
import { filter, map, take, takeUntil } from 'rxjs/operators';
|
||||
import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors';
|
||||
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
|
||||
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||
|
@ -20,8 +19,12 @@ import {
|
|||
MarkWorkbasketForDeletion,
|
||||
RemoveDistributionTarget,
|
||||
SaveNewWorkbasket,
|
||||
SelectComponent,
|
||||
UpdateWorkbasket
|
||||
} from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { WorkbasketComponent } from '../../models/workbasket-component';
|
||||
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
||||
import { ButtonAction } from '../../models/button-action';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-administration-workbasket-information',
|
||||
|
@ -52,6 +55,12 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
@Select(EngineConfigurationSelectors.workbasketsCustomisation)
|
||||
workbasketsCustomisation$: Observable<WorkbasketsCustomisation>;
|
||||
|
||||
@Select(WorkbasketSelectors.buttonAction)
|
||||
buttonAction$: Observable<ButtonAction>;
|
||||
|
||||
@Select(WorkbasketSelectors.selectedComponent)
|
||||
selectedComponent$: Observable<WorkbasketComponent>;
|
||||
|
||||
customFields$: Observable<CustomField[]>;
|
||||
destroy$ = new Subject<void>();
|
||||
|
||||
|
@ -65,6 +74,7 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.dispatch(new SelectComponent(WorkbasketComponent.INFORMATION));
|
||||
this.allTypes = new Map([
|
||||
['PERSONAL', 'Personal'],
|
||||
['GROUP', 'Group'],
|
||||
|
@ -87,6 +97,35 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
this.validateInputOverflow = (inputFieldModel, maxLength) => {
|
||||
this.formsValidatorService.validateInputOverflow(inputFieldModel, maxLength);
|
||||
};
|
||||
this.buttonAction$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.pipe(filter((buttonAction) => typeof buttonAction !== 'undefined'))
|
||||
.subscribe((button) => {
|
||||
this.selectedComponent$
|
||||
.pipe(take(1))
|
||||
.pipe(filter((component) => component === WorkbasketComponent.INFORMATION))
|
||||
.subscribe((component) => {
|
||||
switch (button) {
|
||||
case ButtonAction.SAVE:
|
||||
this.onSave();
|
||||
break;
|
||||
case ButtonAction.UNDO:
|
||||
this.onUndo();
|
||||
break;
|
||||
case ButtonAction.COPY:
|
||||
this.copyWorkbasket();
|
||||
break;
|
||||
case ButtonAction.REMOVE_AS_DISTRIBUTION_TARGETS:
|
||||
this.removeDistributionTargets();
|
||||
break;
|
||||
case ButtonAction.DELETE:
|
||||
this.removeWorkbasket();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes?: SimpleChanges) {
|
||||
|
@ -98,10 +137,6 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
}
|
||||
}
|
||||
|
||||
selectType(type: ICONTYPES) {
|
||||
this.workbasket.type = type;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.formsValidatorService.formSubmitAttempt = true;
|
||||
this.formsValidatorService.validateFormInformation(this.workbasketForm, this.toggleValidationMap).then((value) => {
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
<li id="wb-action-toolbar" class="list-group-item tab-align">
|
||||
<div class="row">
|
||||
<div class="col-xs-7 btn-group">
|
||||
<button type="button" (click)="addWorkbasket()" data-toggle="tooltip" title="Add" class="btn btn-default">
|
||||
<span class="material-icons md-20 green-blue">add_circle_outline</span>
|
||||
</button>
|
||||
<taskana-administration-import-export class="btn-group" [currentSelection]="selectionToImport"></taskana-administration-import-export>
|
||||
</div>
|
||||
<div class="margin-right pull-right btn-group">
|
||||
<taskana-shared-sort [sortingFields]="sortingFields" (performSorting)="sorting($event)" class="btn-group" [defaultSortBy]="workbasketDefaultSortBy"></taskana-shared-sort>
|
||||
<button class="btn btn-default collapsed" type="button" id="collapsedMenufilterWb" aria-expanded="false" (click)="toolbarState=!toolbarState"
|
||||
data-toggle="tooltip" title="Filter">
|
||||
<span class="material-icons md-20 blue">{{!toolbarState? 'search' : 'expand_less'}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="workbasket-list-toolbar">
|
||||
|
||||
<div class="workbasket-list-toolbar__action-toolbar">
|
||||
|
||||
<!-- ACTION BUTTONS -->
|
||||
<button mat-flat-button class="workbasket-list-toolbar__add-button mr-1" matTooltip="Create new workbasket"
|
||||
(click)="addWorkbasket()">
|
||||
Add
|
||||
<mat-icon class="md-20">add</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- IMPORT EXPORT -->
|
||||
<taskana-administration-import-export [currentSelection]="selectionToImport" [parentComponent]="'workbaskets'"></taskana-administration-import-export>
|
||||
|
||||
<span class="workbasket-details__spacer" style="flex: 1 1 auto"> </span>
|
||||
|
||||
<!-- SORTING -->
|
||||
<taskana-shared-sort
|
||||
style="margin-right: 4px;" [sortingFields]="sortingFields" (performSorting)="sorting($event)" [defaultSortBy]="workbasketDefaultSortBy">
|
||||
</taskana-shared-sort>
|
||||
|
||||
<!-- FILTER -->
|
||||
<button mat-stroked-button class="workbasket-list-toolbar__filter-button" matTooltip="Display filter options" (click)="onClickFilter()">
|
||||
<mat-icon *ngIf="!showFilter">filter_list</mat-icon>
|
||||
<mat-icon *ngIf="showFilter">keyboard_arrow_up</mat-icon>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<div [@toggleDown]="toolbarState" class="row no-overflow">
|
||||
<taskana-shared-filter (performFilter)="filtering($event)"></taskana-shared-filter>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<taskana-shared-filter *ngIf="showFilter" (performFilter)="filtering($event)"></taskana-shared-filter>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,34 @@
|
|||
.list-group-item {
|
||||
padding: 5px 0px 2px 1px;
|
||||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.workbasket-list-toolbar {
|
||||
padding: 16px 16px 8px 16px;
|
||||
flex-wrap: wrap;
|
||||
min-height: 68px;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.workbasket-list-toolbar__action-toolbar {
|
||||
padding: 0 4px;
|
||||
display: flex;
|
||||
border: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tab-align {
|
||||
padding: 8px 12px 8px 4px;
|
||||
margin-bottom: 0px;
|
||||
|
||||
& > div {
|
||||
margin: 6px 0px;
|
||||
}
|
||||
.workbasket-list-toolbar__filter-and-sorting {
|
||||
display: flex;
|
||||
border: none;
|
||||
margin-bottom: 0;
|
||||
padding: 16px 4px 8px 4px;
|
||||
}
|
||||
|
||||
.workbasket-list-toolbar__add-button {
|
||||
background-color: $aquamarine;
|
||||
color: white;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.workbasket-list-toolbar__filter-button {
|
||||
padding: 0 5px;
|
||||
color: #555;
|
||||
height: 36px;
|
||||
}
|
||||
|
|
|
@ -7,14 +7,15 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|||
import { WorkbasketState } from '../../../shared/store/workbasket-store/workbasket.state';
|
||||
import { WorkbasketService } from '../../../shared/services/workbasket/workbasket.service';
|
||||
import { DomainService } from '../../../shared/services/domain/domain.service';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { CreateWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { Filter } from '../../../shared/models/filter';
|
||||
import { Sorting } from '../../../shared/models/sorting';
|
||||
import { ACTION } from '../../../shared/models/action';
|
||||
import { TaskanaType } from '../../../shared/models/taskana-type';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
|
||||
const getDomainFn = jest.fn().mockReturnValue(true);
|
||||
const domainServiceMock = jest.fn().mockImplementation(
|
||||
|
@ -26,6 +27,7 @@ const domainServiceMock = jest.fn().mockImplementation(
|
|||
@Component({ selector: 'taskana-administration-import-export', template: '' })
|
||||
class ImportExportStub {
|
||||
@Input() currentSelection: TaskanaType;
|
||||
@Input() parentComponent;
|
||||
}
|
||||
|
||||
@Component({ selector: 'taskana-shared-sort', template: '' })
|
||||
|
@ -52,9 +54,10 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
imports: [
|
||||
HttpClientTestingModule,
|
||||
NgxsModule.forRoot([WorkbasketState]),
|
||||
BrowserAnimationsModule,
|
||||
MatIconModule,
|
||||
MatSnackBarModule,
|
||||
MatDialogModule,
|
||||
BrowserAnimationsModule
|
||||
MatDialogModule
|
||||
],
|
||||
declarations: [WorkbasketListToolbarComponent, ImportExportStub, SortStub, FilterStub],
|
||||
providers: [{ provide: DomainService, useClass: domainServiceMock }, WorkbasketService]
|
||||
|
@ -73,6 +76,8 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
/* Typescript */
|
||||
|
||||
it('should dispatch CreateWorkbasket when addWorkbasket is called', async((done) => {
|
||||
component.action = ACTION.COPY;
|
||||
let actionDispatched = false;
|
||||
|
@ -110,4 +115,36 @@ describe('WorkbasketListToolbarComponent', () => {
|
|||
component.filtering(filterBy);
|
||||
expect(filterBy).toMatchObject(mockFilter);
|
||||
});
|
||||
|
||||
/* HTML */
|
||||
|
||||
it('should call AddWorkbasket() when add-workbasket button is clicked', async () => {
|
||||
const button = debugElement.nativeElement.querySelector('.workbasket-list-toolbar__add-button');
|
||||
expect(button).toBeTruthy();
|
||||
expect(button.textContent).toContain('add');
|
||||
expect(button.textContent).toContain('Add');
|
||||
component.addWorkbasket = jest.fn().mockImplementation();
|
||||
button.click();
|
||||
expect(component.addWorkbasket).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should display import-export component', () => {
|
||||
expect(debugElement.nativeElement.querySelector('taskana-administration-import-export')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display sort component', () => {
|
||||
expect(debugElement.nativeElement.querySelector('taskana-shared-sort')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show filter component only when filter button is clicked', () => {
|
||||
const button = debugElement.nativeElement.querySelector('.workbasket-list-toolbar__filter-button');
|
||||
expect(button).toBeTruthy();
|
||||
expect(button.textContent).toBe('filter_list');
|
||||
expect(debugElement.nativeElement.querySelector('filter')).toBeFalsy();
|
||||
button.click();
|
||||
fixture.detectChanges();
|
||||
expect(component.showFilter).toBe(true);
|
||||
expect(button.textContent).toBe('keyboard_arrow_up');
|
||||
expect(debugElement.nativeElement.querySelector('taskana-shared-filter')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import { takeUntil } from 'rxjs/operators';
|
|||
import { ACTION } from '../../../shared/models/action';
|
||||
import { CreateWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
||||
import { WorkbasketService } from '../../../shared/services/workbasket/workbasket.service';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-administration-workbasket-list-toolbar',
|
||||
|
@ -40,8 +41,8 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
|||
]);
|
||||
|
||||
filterParams = { name: '', key: '', type: '', description: '', owner: '' };
|
||||
toolbarState = false;
|
||||
filterType = TaskanaType.WORKBASKETS;
|
||||
showFilter = false;
|
||||
|
||||
@Select(WorkbasketSelectors.workbasketActiveAction)
|
||||
workbasketActiveAction$: Observable<ACTION>;
|
||||
|
@ -49,7 +50,7 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
|||
destroy$ = new Subject<void>();
|
||||
action: ACTION;
|
||||
|
||||
constructor(private store: Store) {}
|
||||
constructor(private store: Store, private workbasketService: WorkbasketService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.workbasketActiveAction$.pipe(takeUntil(this.destroy$)).subscribe((action) => {
|
||||
|
@ -71,6 +72,11 @@ export class WorkbasketListToolbarComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
onClickFilter() {
|
||||
this.showFilter = !this.showFilter;
|
||||
this.workbasketService.expandWorkbasketActionToolbar(this.showFilter);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
|
|
@ -1,43 +1,56 @@
|
|||
<div class="workbasket-list footer-space-pagination-list">
|
||||
<div #wbToolbar>
|
||||
<div class="workbasket-list">
|
||||
<!-- TOOLBAR -->
|
||||
<section #wbToolbar class="workbasket-list__toolbar">
|
||||
<taskana-administration-workbasket-list-toolbar [workbaskets]="workbasketsSummary$ | async" (performFilter)="performFilter($event)"
|
||||
(performSorting)="performSorting($event)" [workbasketDefaultSortBy]="workbasketDefaultSortBy">
|
||||
</taskana-administration-workbasket-list-toolbar>
|
||||
</div>
|
||||
<div *ngIf="((workbasketsSummary$ | async) && (workbasketsSummary$ | async)?.length > 0) else empty_workbaskets">
|
||||
<ul #wbList id="wb-list-container" class="list-group">
|
||||
<li class="list-group-item no-space">
|
||||
<div class="row"></div>
|
||||
</li>
|
||||
<li class="list-group-item" *ngFor="let workbasket of (workbasketsSummary$ | async)"
|
||||
[class.active]="workbasket.workbasketId == selectedId"
|
||||
type="text" (click)="selectWorkbasket(workbasket.workbasketId)">
|
||||
<div class="row">
|
||||
<dl class="col-xs-1">
|
||||
<taskana-administration-icon-type class="vertical-align" [type]="workbasket.type" tooltip="true" [selected]="workbasket.workbasketId === selectedId"></taskana-administration-icon-type>
|
||||
</dl>
|
||||
<dl class="col-xs-10">
|
||||
<dt data-toggle="tooltip" title="{{workbasket.name}}">{{workbasket.name}},
|
||||
<i data-toggle="tooltip" title="{{workbasket.key}}">{{workbasket.key}} </i>
|
||||
</dt>
|
||||
<dd data-toggle="tooltip" title="{{workbasket.description}}">{{workbasket.description}} </dd>
|
||||
<dd data-toggle="tooltip" title="{{workbasket.owner}}">{{workbasket.owner}} </dd>
|
||||
</dl>
|
||||
<dl *ngIf="workbasket.markedForDeletion">
|
||||
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>
|
||||
</section>
|
||||
|
||||
<!-- WORKBASKET LIST -->
|
||||
<div class="workbasket-list__workbaskets" *ngIf="((workbasketsSummary$ | async) && (workbasketsSummary$ | async)?.length > 0) else empty_workbaskets">
|
||||
<mat-selection-list #workbasket [multiple]="false">
|
||||
<mat-list-option class="workbasket-list__workbaskets-item"
|
||||
*ngFor="let workbasket of (workbasketsSummary$ | async)"
|
||||
(click)="selectWorkbasket(workbasket.workbasketId)"
|
||||
[selected]="workbasket.workbasketId == selectedId"
|
||||
[value]="workbasket.workbasketId">
|
||||
<div class="workbaskets-item__wrapper">
|
||||
|
||||
<div class="workbaskets-item__icon">
|
||||
<taskana-administration-icon-type [type]="workbasket.type" size="large" tooltip="true" [selected]="workbasket.workbasketId === selectedId"></taskana-administration-icon-type>
|
||||
</div>
|
||||
|
||||
<div class="workbaskets-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" data-toggle="tooltip" class="material-icons md-20 {{workbasket.workbasketId === selectedId ? 'white': 'red' }} ">error</span>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
</mat-list-option>
|
||||
</mat-selection-list>
|
||||
</div>
|
||||
<taskana-shared-spinner [isRunning]="requestInProgress"></taskana-shared-spinner>
|
||||
|
||||
|
||||
<!-- SPINNER and EMPTY WORKBASKET LIST -->
|
||||
<ng-template #empty_workbaskets>
|
||||
<div *ngIf="!requestInProgress" class="col-xs-12 container-no-items center-block">
|
||||
<div *ngIf="!requestInProgress" class="workbasket-list__no-items">
|
||||
<h3 class="grey">There are no workbaskets</h3>
|
||||
<svg-icon class="img-responsive empty-icon" src="./assets/icons/wb-empty.svg"></svg-icon>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<!-- PAGINATION -->
|
||||
|
||||
<taskana-shared-pagination
|
||||
[page]="(workbasketsSummaryRepresentation$ | async) ? (workbasketsSummaryRepresentation$ | async)?.page : (workbasketsSummaryRepresentation$ | async)"
|
||||
[type]="type"
|
||||
|
|
|
@ -1,41 +1,39 @@
|
|||
@import 'src/theme/_colors.scss';
|
||||
|
||||
|
||||
.workbasket-list {
|
||||
min-width: 30vw;
|
||||
height: calc(100vh - 156px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
min-width: 500px;
|
||||
}
|
||||
.row.list-group {
|
||||
margin-left: 2px;
|
||||
.workbasket-list__workbaskets {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.list-group > li {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
.workbasket-list__workbaskets-item {
|
||||
}
|
||||
|
||||
a > label {
|
||||
height: 2em;
|
||||
width: 100%;
|
||||
.mat-list-item {
|
||||
height: 90px !important;
|
||||
}
|
||||
dd,
|
||||
dt {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
.mat-list-single-selected-option {
|
||||
background-color: $green !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
dt > i {
|
||||
font-weight: normal;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
.workbaskets-item__wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
li > div.row > dl {
|
||||
margin-bottom: 0px;
|
||||
.workbaskets-item__icon {
|
||||
padding: 32px 32px 32px 16px;
|
||||
}
|
||||
li > div.row > dl:first-child {
|
||||
margin-left: 15px;
|
||||
.workbaskets-item__info {
|
||||
display: block;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.no-space {
|
||||
border-top: none;
|
||||
padding: 0px;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
.workbasket-list__no-items {
|
||||
text-align: center;
|
||||
padding-top: 150px;
|
||||
}
|
||||
|
|
|
@ -16,15 +16,23 @@ import { Sorting } from '../../../shared/models/sorting';
|
|||
import { Filter } from '../../../shared/models/filter';
|
||||
import { ICONTYPES } from '../../../shared/models/icon-types';
|
||||
import { Page } from '../../../shared/models/page';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatListModule, MatSelectionList } from '@angular/material/list';
|
||||
import { DomainService } from '../../../shared/services/domain/domain.service';
|
||||
|
||||
const workbasketSavedTriggeredFn = jest.fn().mockReturnValue(of(1));
|
||||
const workbasketSummaryFn = jest.fn().mockReturnValue(of({}));
|
||||
const getWorkbasketFn = jest.fn().mockReturnValue(of({ workbasketId: '1' }));
|
||||
const getWorkbasketActionToolbarExpansionFn = jest.fn().mockReturnValue(of(false));
|
||||
const workbasketServiceMock = jest.fn().mockImplementation(
|
||||
(): Partial<WorkbasketService> => ({
|
||||
workbasketSavedTriggered: workbasketSavedTriggeredFn,
|
||||
getWorkBasketsSummary: workbasketSummaryFn,
|
||||
getWorkBasket: getWorkbasketFn
|
||||
getWorkBasket: getWorkbasketFn,
|
||||
getWorkbasketActionToolbarExpansion: getWorkbasketActionToolbarExpansionFn
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -43,6 +51,13 @@ const importExportServiceMock = jest.fn().mockImplementation(
|
|||
})
|
||||
);
|
||||
|
||||
const domainServiceSpy = jest.fn().mockImplementation(
|
||||
(): Partial<DomainService> => ({
|
||||
getSelectedDomainValue: jest.fn().mockReturnValue(of()),
|
||||
getSelectedDomain: jest.fn().mockReturnValue(of())
|
||||
})
|
||||
);
|
||||
|
||||
@Component({ selector: 'taskana-administration-workbasket-list-toolbar', template: '' })
|
||||
class WorkbasketListToolbarStub {
|
||||
@Input() workbaskets: Array<WorkbasketSummary>;
|
||||
|
@ -82,7 +97,15 @@ describe('WorkbasketListComponent', () => {
|
|||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NgxsModule.forRoot([WorkbasketState]), MatSnackBarModule, MatDialogModule],
|
||||
imports: [
|
||||
NgxsModule.forRoot([WorkbasketState]),
|
||||
MatSnackBarModule,
|
||||
MatDialogModule,
|
||||
FormsModule,
|
||||
MatProgressBarModule,
|
||||
MatSelectModule,
|
||||
MatListModule
|
||||
],
|
||||
declarations: [
|
||||
WorkbasketListComponent,
|
||||
WorkbasketListToolbarStub,
|
||||
|
@ -94,7 +117,8 @@ describe('WorkbasketListComponent', () => {
|
|||
providers: [
|
||||
{ provide: WorkbasketService, useClass: workbasketServiceMock },
|
||||
{ provide: OrientationService, useClass: orientationServiceMock },
|
||||
{ provide: ImportExportService, useClass: importExportServiceMock }
|
||||
{ provide: ImportExportService, useClass: importExportServiceMock },
|
||||
{ provide: DomainService, useClass: domainServiceSpy }
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import {
|
|||
} from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
||||
import { Workbasket } from '../../../shared/models/workbasket';
|
||||
import { MatSelectionList } from '@angular/material/list';
|
||||
import { DomainService } from '../../../shared/services/domain/domain.service';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-administration-workbasket-list',
|
||||
|
@ -51,11 +53,14 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
|||
|
||||
destroy$ = new Subject<void>();
|
||||
|
||||
@ViewChild('workbasket') workbasketList: MatSelectionList;
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private workbasketService: WorkbasketService,
|
||||
private orientationService: OrientationService,
|
||||
private importExportService: ImportExportService,
|
||||
private domainService: DomainService,
|
||||
private ngxsActions$: Actions
|
||||
) {
|
||||
this.ngxsActions$.pipe(ofActionDispatched(GetWorkbasketsSummary), takeUntil(this.destroy$)).subscribe(() => {
|
||||
|
@ -100,6 +105,23 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
|||
.subscribe((value: Boolean) => {
|
||||
this.refreshWorkbasketList();
|
||||
});
|
||||
|
||||
this.domainService
|
||||
.getSelectedDomain()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((domain) => {
|
||||
this.performRequest();
|
||||
});
|
||||
|
||||
this.workbasketService
|
||||
.getWorkbasketActionToolbarExpansion()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((value) => {
|
||||
this.requestInProgress = true;
|
||||
setTimeout(() => {
|
||||
this.refreshWorkbasketList();
|
||||
}, 1);
|
||||
});
|
||||
}
|
||||
|
||||
selectWorkbasket(id: string) {
|
||||
|
@ -128,30 +150,34 @@ export class WorkbasketListComponent implements OnInit, OnDestroy {
|
|||
refreshWorkbasketList() {
|
||||
this.cards = this.orientationService.calculateNumberItemsList(
|
||||
window.innerHeight,
|
||||
72,
|
||||
170 + this.toolbarElement.nativeElement.offsetHeight,
|
||||
92,
|
||||
200 + this.toolbarElement.nativeElement.offsetHeight,
|
||||
false
|
||||
);
|
||||
this.performRequest();
|
||||
}
|
||||
|
||||
performRequest(): void {
|
||||
this.store.dispatch(
|
||||
new GetWorkbasketsSummary(
|
||||
true,
|
||||
this.sort.sortBy,
|
||||
this.sort.sortDirection,
|
||||
'',
|
||||
this.filterBy.filterParams.name,
|
||||
this.filterBy.filterParams.description,
|
||||
'',
|
||||
this.filterBy.filterParams.owner,
|
||||
this.filterBy.filterParams.type,
|
||||
'',
|
||||
this.filterBy.filterParams.key,
|
||||
''
|
||||
this.store
|
||||
.dispatch(
|
||||
new GetWorkbasketsSummary(
|
||||
true,
|
||||
this.sort.sortBy,
|
||||
this.sort.sortDirection,
|
||||
'',
|
||||
this.filterBy.filterParams.name,
|
||||
this.filterBy.filterParams.description,
|
||||
'',
|
||||
this.filterBy.filterParams.owner,
|
||||
this.filterBy.filterParams.type,
|
||||
'',
|
||||
this.filterBy.filterParams.key,
|
||||
''
|
||||
)
|
||||
)
|
||||
);
|
||||
.subscribe(() => {
|
||||
this.requestInProgress = false;
|
||||
});
|
||||
TaskanaQueryParameters.pageSize = this.cards;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
<div class="workbasket-overview">
|
||||
<div class="vertical-right-divider">
|
||||
<taskana-administration-workbasket-list></taskana-administration-workbasket-list>
|
||||
</div>
|
||||
<div *ngIf="showDetail; else showEmptyPage">
|
||||
<taskana-administration-workbasket-details></taskana-administration-workbasket-details>
|
||||
</div>
|
||||
<taskana-administration-workbasket-list></taskana-administration-workbasket-list>
|
||||
<div class="vertical-right-divider"></div>
|
||||
<taskana-administration-workbasket-details
|
||||
*ngIf="showDetail; else showEmptyPage"></taskana-administration-workbasket-details>
|
||||
|
||||
<ng-template #showEmptyPage>
|
||||
<div class="hidden-xs hidden-sm col-md-8 container-no-items">
|
||||
<div class="center-block no-detail">
|
||||
<h3 class="grey">Select a workbasket</h3>
|
||||
<svg-icon class="img-responsive empty-icon" src="./assets/icons/wb-empty.svg"></svg-icon>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #showEmptyPage>
|
||||
<div class="hidden-xs hidden-sm col-md-8 container-no-items">
|
||||
<div class="center-block no-detail">
|
||||
<h3 class="grey">Select a workbasket</h3>
|
||||
<svg-icon class="img-responsive empty-icon" src="./assets/icons/wb-empty.svg"></svg-icon>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
.workbasket-overview {
|
||||
width: 100%;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { SelectedRouteService } from '../../../shared/services/selected-route/se
|
|||
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { CreateWorkbasket, SelectWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { ClassificationCategoriesService } from '../../../shared/services/classification-categories/classification-categories.service';
|
||||
import { StartupService } from '../../../shared/services/startup/startup.service';
|
||||
import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from '../../../shared/services/window/window.service';
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export enum ButtonAction {
|
||||
SAVE,
|
||||
UNDO,
|
||||
COPY,
|
||||
REMOVE_AS_DISTRIBUTION_TARGETS,
|
||||
DELETE,
|
||||
CLOSE
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export enum WorkbasketComponent {
|
||||
INFORMATION,
|
||||
ACCESS_ITEMS,
|
||||
DISTRIBUTION_TARGETS
|
||||
}
|
|
@ -6,9 +6,7 @@
|
|||
<mat-icon>exit_to_app</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<a class="sidenav__drawer-user-info">
|
||||
<taskana-shared-user-information></taskana-shared-user-information>
|
||||
</a>
|
||||
<taskana-shared-user-information class="sidenav__drawer-user-info"></taskana-shared-user-information>
|
||||
<taskana-sidenav-list></taskana-sidenav-list>
|
||||
<div class="sidenav__drawer-version">
|
||||
<p> Taskana version: {{version}} </p>
|
||||
|
@ -16,14 +14,13 @@
|
|||
</mat-sidenav>
|
||||
<mat-sidenav-content>
|
||||
<taskana-shared-nav-bar></taskana-shared-nav-bar>
|
||||
<div (window:resize)="onResize()" class="">
|
||||
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>
|
||||
<div (window:resize)="onResize()">
|
||||
<div class="taskana-main">
|
||||
<router-outlet></router-outlet>
|
||||
<taskana-shared-spinner [isRunning]="requestInProgress" isModal=true></taskana-shared-spinner>
|
||||
<taskana-shared-progress-bar [hidden]="currentProgressValue === 0" currentValue={{currentProgressValue}}>
|
||||
</taskana-shared-progress-bar>
|
||||
</div>
|
||||
<taskana-shared-type-ahead></taskana-shared-type-ahead>
|
||||
</div>
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
</mat-sidenav-container>
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
box-shadow: none;
|
||||
width: 350px;
|
||||
background-color: $dark-green;
|
||||
box-shadow: 3px 0px 10px -1px $dark-green;
|
||||
}
|
||||
|
||||
.sidenav__drawer-list-item {
|
||||
|
@ -24,14 +23,15 @@
|
|||
|
||||
.sidenav__drawer-logout {
|
||||
text-align: end;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidenav__drawer-version {
|
||||
color: $grey;
|
||||
color: white;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 16px;
|
||||
font-size: 12px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.sidenav__drawer-user-info {
|
||||
|
@ -40,14 +40,9 @@
|
|||
margin-left: -16px;
|
||||
}
|
||||
|
||||
.mat-icon-button {
|
||||
outline: none;
|
||||
mat-sidenav-content {
|
||||
height: unset;
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
color: white;
|
||||
}
|
||||
|
||||
::ng-deep .mat-drawer-inner-container {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Component, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { NavigationStart, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { MatSidenav } from '@angular/material';
|
||||
import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service';
|
||||
import { SidenavService } from './shared/services/sidenav/sidenav.service';
|
||||
import { RequestInProgressService } from './shared/services/request-in-progress/request-in-progress.service';
|
||||
|
@ -12,6 +11,7 @@ import { ErrorModel } from './shared/models/error-model';
|
|||
import { TaskanaEngineService } from './shared/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from 'app/shared/services/window/window.service';
|
||||
import { environment } from 'environments/environment';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-root',
|
||||
|
|
|
@ -13,14 +13,6 @@ import { TabsModule } from 'ngx-bootstrap/tabs';
|
|||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TreeModule } from 'angular-tree-component';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import {
|
||||
MatButtonModule,
|
||||
MatSidenavModule,
|
||||
MatCheckboxModule,
|
||||
MatGridListModule,
|
||||
MatListModule,
|
||||
MatIconModule
|
||||
} from '@angular/material';
|
||||
|
||||
/**
|
||||
* Services
|
||||
|
@ -59,6 +51,14 @@ import { UserGuard } from './shared/guards/user.guard';
|
|||
import { ClassificationCategoriesService } from './shared/services/classification-categories/classification-categories.service';
|
||||
import { environment } from '../environments/environment';
|
||||
import { STATES } from './shared/store';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
|
||||
const MODULES = [
|
||||
TabsModule.forRoot(),
|
||||
|
@ -90,7 +90,7 @@ export function startupServiceFactory(startupService: StartupService): () => Pro
|
|||
|
||||
@NgModule({
|
||||
declarations: DECLARATIONS,
|
||||
imports: MODULES,
|
||||
imports: [MODULES, MatSidenavModule, MatIconModule, MatToolbarModule, MatProgressBarModule],
|
||||
providers: [
|
||||
WindowRefService,
|
||||
DomainService,
|
||||
|
|
|
@ -1,49 +1,68 @@
|
|||
<div class="list-group-search">
|
||||
<div *ngIf="filterTypeIsWorkbasket(); else tasktype">
|
||||
<div class="row">
|
||||
<div class="dropdown col-xs-2">
|
||||
<button class="btn btn-default btn-sm" data-toggle="dropdown" type="button" id="dropdownMenufilter" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<taskana-administration-icon-type [type]="filter.filterParams?.type"></taskana-administration-icon-type>
|
||||
<div>
|
||||
|
||||
<!-- WORKBASKET FILTER -->
|
||||
<div class="filter" *ngIf="filterTypeIsWorkbasket(); else tasktype">
|
||||
|
||||
<!-- TEXT INPUT -->
|
||||
<div class="filter__text-input">
|
||||
<div class="filter__name-and-key-input">
|
||||
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
|
||||
<mat-label>Filter by name</mat-label>
|
||||
<input matInput [(ngModel)]="filter.filterParams.name" matTooltip="Type to filter by name" (keyup.enter)="search()">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
|
||||
<mat-label>Filter by key</mat-label>
|
||||
<input matInput [(ngModel)]="filter.filterParams.key" matTooltip="Type to filter by key" (keyup.enter)="search()">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
|
||||
<mat-label>Filter by description</mat-label>
|
||||
<input matInput [(ngModel)]="filter.filterParams.description" matTooltip="Type to filter by description" (keyup.enter)="search()">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
|
||||
<mat-label>Filter by owner</mat-label>
|
||||
<input matInput [(ngModel)]="filter.filterParams.owner" matTooltip="Type to filter by owner" (keyup.enter)="search()">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- SEARCH AND CLEAR BUTTON -->
|
||||
<div class="filter__action-buttons">
|
||||
|
||||
<!-- TYPE FILTER -->
|
||||
<button mat-stroked-button [matMenuTriggerFor]="menu" matTooltip="Filter workbaskets by type">
|
||||
Filter by type
|
||||
<mat-icon *ngIf="filter.filterParams?.type == ''" style="color: #555">filter_list</mat-icon>
|
||||
<taskana-administration-icon-type *ngIf="filter.filterParams?.type != ''" [type]="filter.filterParams?.type"> </taskana-administration-icon-type>
|
||||
</button>
|
||||
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item *ngFor="let type of allTypes | mapValues" (click)="selectType(type.key); search()">
|
||||
<taskana-administration-icon-type [type]='type.key' [text]="type.value"></taskana-administration-icon-type>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-users" role="menu">
|
||||
<li>
|
||||
<a *ngFor="let type of allTypes | mapValues" type="button" (click)="selectType(type.key); search()"
|
||||
data-toggle="tooltip" [title]="type.value">
|
||||
<taskana-administration-icon-type [type]='type.key' [text]="type.value"></taskana-administration-icon-type>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<input type="text" [(ngModel)]="filter.filterParams.name" (keyup.enter)="search()" class="form-control input-sm"
|
||||
placeholder="Filter name">
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<input type="text" [(ngModel)]="filter.filterParams.key" (keyup.enter)="search()" class="form-control input-sm" placeholder="Filter key">
|
||||
</div>
|
||||
<button (click)="clear(); search()" type="button" class="btn btn-default btn-sm pull-right margin-right"
|
||||
data-toggle="tooltip" title="Clear">
|
||||
<span class="material-icons md-20 blue">clear</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-8 col-xs-offset-2">
|
||||
<input type="text" [(ngModel)]="filter.filterParams.description" (keyup.enter)="search()" class="form-control input-sm"
|
||||
placeholder="Filter description">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-8 col-xs-offset-2">
|
||||
<input type="text" [(ngModel)]="filter.filterParams.owner" (keyup.enter)="search()" class="form-control input-sm"
|
||||
placeholder="Filter owner">
|
||||
</div>
|
||||
<button (click)="search()" type="button" class="btn btn-default btn-sm pull-right margin-right" data-toggle="tooltip"
|
||||
title="Search">
|
||||
<span class="material-icons md-20 blue ">search</span>
|
||||
</mat-menu>
|
||||
|
||||
<!-- CLEAR BUTTON -->
|
||||
<button mat-stroked-button (click)="clear(); search()" matTooltip="Clear workbasket filter">
|
||||
Reset filter
|
||||
<mat-icon style="color: #555">undo</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- SEARCH BUTTON -->
|
||||
<button mat-stroked-button (click)="search()" matTooltip="Search by given filter" class="filter__search-button">
|
||||
Apply filter
|
||||
<mat-icon>search</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- TASK FILTER -->
|
||||
<ng-template #tasktype>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
|
|
|
@ -1,3 +1,37 @@
|
|||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.filter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.filter__text-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter__action-buttons {
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.filter__search-button {
|
||||
background: $aquamarine;
|
||||
color: white;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.filter__name-and-key-input{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.dropdown-menu-users {
|
||||
& > li {
|
||||
margin-bottom: 5px;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { ICONTYPES } from 'app/shared/models/icon-types';
|
||||
import { Filter } from 'app/shared/models/filter';
|
||||
import { TaskanaType } from 'app/shared/models/taskana-type';
|
||||
|
@ -9,12 +9,12 @@ import { TaskanaType } from 'app/shared/models/taskana-type';
|
|||
styleUrls: ['./filter.component.scss']
|
||||
})
|
||||
export class FilterComponent implements OnInit {
|
||||
@Input() allTypes: Map<string, string> = new Map([
|
||||
['ALL', 'All'],
|
||||
['PERSONAL', 'Personal'],
|
||||
['GROUP', 'Group'],
|
||||
['CLEARANCE', 'Clearance'],
|
||||
['TOPIC', 'Topic']
|
||||
@Input() allTypes: Map<ICONTYPES, string> = new Map([
|
||||
[ICONTYPES.ALL, 'All'],
|
||||
[ICONTYPES.PERSONAL, 'Personal'],
|
||||
[ICONTYPES.GROUP, 'Group'],
|
||||
[ICONTYPES.CLEARANCE, 'Clearance'],
|
||||
[ICONTYPES.TOPIC, 'Topic']
|
||||
]);
|
||||
|
||||
@Input() allStates: Map<string, string> = new Map([
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<nav class="navbar navbar__inverse">
|
||||
<mat-toolbar class="navbar">
|
||||
<div class="navbar__button">
|
||||
<button mat-icon-button class="navbar_button-toggle" (click)="toggleSidenav()">
|
||||
<mat-icon>menu</mat-icon>
|
||||
|
@ -6,6 +6,6 @@
|
|||
</div>
|
||||
<div class="navbar__logo">
|
||||
<svg-icon class="navbar__logo-icon" src="./assets/icons/logo-copy.svg"></svg-icon>
|
||||
<h2 class="navbar__title">{{title}}</h2>
|
||||
<div class="navbar__title">/ {{title}}</div>
|
||||
</div>
|
||||
</nav>
|
||||
</mat-toolbar>
|
||||
|
|
|
@ -1,58 +1,33 @@
|
|||
@import '../../../../theme/variables';
|
||||
|
||||
.navbar.main:before {
|
||||
@include degraded-bar(right, 100%, 3px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
height: 52px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.navbar__inverse {
|
||||
border: none;
|
||||
background-color: $dark-green;
|
||||
box-shadow: 0px 1px 5px -1px black;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navbar__buttom {
|
||||
flex-grow: 1;
|
||||
display: flex !important;
|
||||
order: 1;
|
||||
margin-top: -10px;
|
||||
max-height: 52px;
|
||||
overflow-x: hidden;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.navbar__logo {
|
||||
display: flex !important;
|
||||
flex-grow: 6;
|
||||
position: relative;
|
||||
left: 40%;
|
||||
margin-top: -3px;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
h2.navbar__title {
|
||||
display: flex !important;
|
||||
flex-grow: 5;
|
||||
color: white;
|
||||
margin-top: 10px;
|
||||
order: 3;
|
||||
margin-left: 2%;
|
||||
font-size: large;
|
||||
@media only screen and (max-width: 700px) {
|
||||
display: none !important;
|
||||
}
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
svg-icon.navbar__logo-icon {
|
||||
float: left;
|
||||
width: 150px;
|
||||
height: 50px;
|
||||
padding: 4px 10px 0 0;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.navbar__title {
|
||||
padding-top: 9px;
|
||||
color: white;
|
||||
font-size: 1.25rem;
|
||||
@media only screen and (max-width: 700px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-icon-button {
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { NavBarComponent } from './nav-bar.component';
|
||||
import { SelectedRouteService } from '../../../shared/services/selected-route/selected-route';
|
||||
import { MatIconModule } from '@angular/material';
|
||||
import { SidenavService } from '../../../shared/services/sidenav/sidenav.service';
|
||||
import { SelectedRouteService } from '../../services/selected-route/selected-route';
|
||||
import { SidenavService } from '../../services/sidenav/sidenav.service';
|
||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
|
||||
const SidenavServiceSpy = jest.fn().mockImplementation(
|
||||
(): Partial<SidenavService> => ({
|
||||
|
@ -30,7 +31,7 @@ describe('NavBarComponent', () => {
|
|||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [NavBarComponent],
|
||||
imports: [MatIconModule, HttpClientTestingModule, AngularSvgIconModule],
|
||||
imports: [MatIconModule, HttpClientTestingModule, AngularSvgIconModule, MatToolbarModule],
|
||||
providers: [
|
||||
{ provide: SidenavService, useClass: SidenavServiceSpy },
|
||||
{ provide: SelectedRouteService, useClass: SelectedRouteServiceSpy }
|
||||
|
|
|
@ -1,21 +1,11 @@
|
|||
<ul id="wb-pagination" class="pagination vertical-center">
|
||||
<li>
|
||||
<a *ngIf="hasItems" (click)="changeToPage(1)" aria-label="First">
|
||||
First</a>
|
||||
</li>
|
||||
<li *ngFor="let pageNumber of page?.totalPages | spreadNumber: page?.number: maxPagesAvailable">
|
||||
<a *ngIf="pageNumber + 1 !== page?.number" (click)="changeToPage(pageNumber+1)">{{pageNumber + 1}}</a>
|
||||
<a *ngIf="pageNumber + 1 === page?.number" class="pagination">
|
||||
<input [(ngModel)]="pageSelected" (keyup.enter)="changeToPage(pageSelected)" type="number" (blur)="changeToPage(pageSelected)" >
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a *ngIf="hasItems" (click)="changeToPage(page?.totalPages)" aria-label="Last">Last</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="footer pull-right" [hidden]="numberOfItems === 0">
|
||||
<i [innerHTML]="getPagesTextToShow()"></i>
|
||||
</span>
|
||||
<span class="footer pull-right" [hidden]="numberOfItems !== 0 && page?.totalElements !== 0">
|
||||
<i>Loading...</i>
|
||||
</span>
|
||||
<div class="pagination">
|
||||
<mat-paginator [length]="page?.totalElements" [pageIndex]="pageSelected - 1 " hidePageSize="true" [pageSize]="page?.size" (page)="changeToPage($event)" showFirstLastButtons="true"></mat-paginator>
|
||||
<div class="pagination__go-to">
|
||||
<div class="pagination__go-to-label">Page: </div>
|
||||
<mat-form-field>
|
||||
<mat-select [(ngModel)]="pageSelected" (selectionChange)="goToPage(pageSelected)">
|
||||
<mat-option *ngFor="let pageNumber of pageNumbers" [value]="pageNumber">{{ pageNumber }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,47 +1,27 @@
|
|||
$blue: #66afe9;
|
||||
.pagination {
|
||||
margin: 15px 0 0 0;
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
a.pagination {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
margin: 0 10px;
|
||||
padding: 0;
|
||||
background-color: aliceblue;
|
||||
> input {
|
||||
margin: 1px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
background-color: aliceblue;
|
||||
border: none;
|
||||
outline: none;
|
||||
&:focus {
|
||||
border-color: $blue;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
}
|
||||
mat-paginator {
|
||||
background-color: #fafafa;
|
||||
width: 70%;
|
||||
font-feature-settings: "tnum";
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
//Original
|
||||
.pagination__go-to {
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
mat-form-field {
|
||||
width: 56px;
|
||||
margin: 6px 4px 0 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.pagination__go-to-label {
|
||||
margin: 0 4px;
|
||||
font-size: 12px;
|
||||
color: rgba(0,0,0,.54);
|
||||
}
|
||||
}
|
||||
|
||||
ul.pagination {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin: 0px 5px 0 0;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
// small "hack" to remove the arrows in the input fields of type numbers
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
|
|
@ -1,60 +1,78 @@
|
|||
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { Page } from 'app/shared/models/page';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-shared-pagination',
|
||||
templateUrl: './pagination.component.html',
|
||||
styleUrls: ['./pagination.component.scss']
|
||||
})
|
||||
export class PaginationComponent implements OnChanges {
|
||||
export class PaginationComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
page: Page;
|
||||
|
||||
@Input()
|
||||
type: String;
|
||||
|
||||
@Output()
|
||||
workbasketsResourceChange = new EventEmitter<Page>();
|
||||
@Input()
|
||||
numberOfItems: number;
|
||||
|
||||
@Output()
|
||||
changePage = new EventEmitter<number>();
|
||||
|
||||
@Input()
|
||||
numberOfItems: number;
|
||||
@ViewChild(MatPaginator, { static: true })
|
||||
paginator: MatPaginator;
|
||||
|
||||
hasItems = true;
|
||||
previousPageSelected = 1;
|
||||
pageSelected = 1;
|
||||
maxPagesAvailable = 8;
|
||||
pageNumbers: number[];
|
||||
|
||||
ngOnInit() {
|
||||
// Custom label: EG. "1-7 of 21 workbaskets"
|
||||
// return `${start} - ${end} of ${length} workbaskets`;
|
||||
|
||||
this.paginator._intl.itemsPerPageLabel = 'Per page';
|
||||
this.paginator._intl.getRangeLabel = (page: number, pageSize: number, length: number) => {
|
||||
page += 1;
|
||||
const start = pageSize * (page - 1) + 1;
|
||||
const end = pageSize * page < length ? pageSize * page : length;
|
||||
if (length === 0) {
|
||||
return 'loading...';
|
||||
} else {
|
||||
return `${start} - ${end} of ${length}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.page && changes.page.currentValue) {
|
||||
this.pageSelected = changes.page.currentValue.number;
|
||||
}
|
||||
this.hasItems = this.numberOfItems > 0;
|
||||
}
|
||||
|
||||
changeToPage(p) {
|
||||
let page = p;
|
||||
if (page < 1) {
|
||||
this.pageSelected = 1;
|
||||
page = this.pageSelected;
|
||||
}
|
||||
if (page > this.page.totalPages) {
|
||||
page = this.page.totalPages;
|
||||
}
|
||||
if (this.previousPageSelected !== page) {
|
||||
this.changePage.emit(page);
|
||||
this.previousPageSelected = page;
|
||||
this.pageSelected = page;
|
||||
if (changes.page) {
|
||||
this.updateGoto();
|
||||
}
|
||||
}
|
||||
|
||||
getPagesTextToShow(): string {
|
||||
if (!this.page) {
|
||||
return '';
|
||||
changeToPage(event) {
|
||||
let currentPageIndex = event.pageIndex;
|
||||
if (currentPageIndex > event.previousPageIndex) {
|
||||
this.pageSelected += 1;
|
||||
} else {
|
||||
this.pageSelected -= 1;
|
||||
}
|
||||
const text = `${this.numberOfItems}`;
|
||||
return `${text} of ${this.page.totalElements} ${this.type}`;
|
||||
this.changePage.emit(currentPageIndex + 1);
|
||||
}
|
||||
|
||||
updateGoto() {
|
||||
this.pageNumbers = [];
|
||||
for (let i = 1; i <= this.page?.totalPages; i++) {
|
||||
this.pageNumbers.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
goToPage(page: number) {
|
||||
this.paginator.pageIndex = page - 1;
|
||||
this.pageSelected = page;
|
||||
this.changePage.emit(page);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<mat-nav-list>
|
||||
<a mat-list-item class="list-item list-item__admin" [routerLink]=[workbasketsUrl] [routerLinkActive]="['active']"
|
||||
<mat-nav-list class="navlist">
|
||||
<a mat-list-item class="navlist__item navlist__admin" [routerLink]=[administrationsUrl] [routerLinkActive]="['active']"
|
||||
*ngIf="administrationAccess" (click)="toggleSidenav()">Administration</a>
|
||||
<a mat-list-item class="list-item list-item__admin-workbaskets" [routerLink]=[workbasketsUrl]
|
||||
[routerLinkActive]="['active']" *ngIf="administrationAccess" (click)="toggleSidenav()">Workbaskets</a>
|
||||
<a mat-list-item class="list-item list-item__admin-classifications" [routerLink]=[classificationUrl]
|
||||
[routerLinkActive]="['active']" *ngIf="administrationAccess" (click)="toggleSidenav()">Classifications</a>
|
||||
<a mat-list-item class="list-item list-item__admin-acces-items" [routerLink]=[accessUrl]
|
||||
[routerLinkActive]="['active']" (click)="toggleSidenav()" *ngIf="administrationAccess">Access Items</a>
|
||||
<a mat-list-item class="list-item list-item__monitor" [routerLink]=[monitorUrl] [routerLinkActive]="['active']"
|
||||
<div class="navlist__admin-item">
|
||||
<a mat-list-item class="navlist__item navlist__admin-workbaskets" [routerLink]=[workbasketsUrl]
|
||||
[routerLinkActive]="['active']" *ngIf="administrationAccess" (click)="toggleSidenav()">Workbaskets</a>
|
||||
<a mat-list-item class="navlist__item navlist__admin-classifications" [routerLink]=[classificationUrl]
|
||||
[routerLinkActive]="['active']" *ngIf="administrationAccess" (click)="toggleSidenav()">Classifications</a>
|
||||
<a mat-list-item class="navlist__item navlist__admin-access-items" [routerLink]=[accessUrl]
|
||||
[routerLinkActive]="['active']" (click)="toggleSidenav()" *ngIf="administrationAccess">Access Items</a>
|
||||
</div>
|
||||
<a mat-list-item class="navlist__item navlist__monitor" [routerLink]=[monitorUrl] [routerLinkActive]="['active']"
|
||||
*ngIf="monitorAccess" (click)="toggleSidenav()">Monitor</a>
|
||||
<a mat-list-item class="list-item list-item__workplace" [routerLink]=[workplaceUrl] [routerLinkActive]="['active']"
|
||||
<a mat-list-item class="navlist__item navlist__workplace" [routerLink]=[workplaceUrl] [routerLinkActive]="['active']"
|
||||
*ngIf="workplaceAccess" (click)="toggleSidenav()">Workplace</a>
|
||||
<a mat-list-item class="list-item list-item__history" [routerLink]=[historyUrl] [routerLinkActive]="['active']"
|
||||
<a mat-list-item class="navlist__item navlist__history" [routerLink]=[historyUrl] [routerLinkActive]="['active']"
|
||||
*ngIf="historyAccess" (click)="toggleSidenav()">History</a>
|
||||
</mat-nav-list>
|
||||
</mat-nav-list>
|
||||
|
|
|
@ -1,33 +1,17 @@
|
|||
@import '../../../../theme/variables';
|
||||
|
||||
.list-item {
|
||||
color: $grey;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
color: $grey;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
color: $grey;
|
||||
}
|
||||
|
||||
.list-item__admin-workbaskets {
|
||||
color: $grey;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.list-item__admin-classifications {
|
||||
color: $grey;
|
||||
.navlist__admin-item {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.list-item__admin-acces-items {
|
||||
color: $grey;
|
||||
margin-left: 30px;
|
||||
.navlist__item {
|
||||
color: #ddd;
|
||||
|
||||
}
|
||||
|
||||
.active {
|
||||
color: white !important;
|
||||
color: $aquamarine;
|
||||
border-left: 5px solid $aquamarine;
|
||||
}
|
||||
|
||||
::ng-deep .mat-drawer-container {
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Component, DebugElement } from '@angular/core';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { SidenavListComponent } from './sidenav-list.component';
|
||||
import { SidenavService } from '../../../shared/services/sidenav/sidenav.service';
|
||||
import {
|
||||
MatButtonModule,
|
||||
MatSidenavModule,
|
||||
MatCheckboxModule,
|
||||
MatGridListModule,
|
||||
MatListModule,
|
||||
MatIconModule
|
||||
} from '@angular/material';
|
||||
import { SidenavService } from '../../services/sidenav/sidenav.service';
|
||||
|
||||
import { BrowserModule, By } from '@angular/platform-browser';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
@ -17,6 +10,12 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||
import { TaskanaEngineService } from '../../services/taskana-engine/taskana-engine.service';
|
||||
import { TaskanaEngineServiceMock } from '../../services/taskana-engine/taskana-engine.mock.service';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
const SidenavServiceSpy = jest.fn().mockImplementation(
|
||||
(): Partial<SidenavService> => ({
|
||||
|
@ -75,7 +74,7 @@ describe('SidenavListComponent', () => {
|
|||
component.workplaceAccess = true;
|
||||
component.historyAccess = true;
|
||||
fixture.detectChanges();
|
||||
const menuList = debugElement.queryAll(By.css('.list-item'));
|
||||
const menuList = debugElement.queryAll(By.css('.navlist__item'));
|
||||
expect(menuList.length).toBe(7);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
@ -86,14 +85,14 @@ describe('SidenavListComponent', () => {
|
|||
component.workplaceAccess = false;
|
||||
component.historyAccess = false;
|
||||
fixture.detectChanges();
|
||||
const menuList = debugElement.queryAll(By.css('.list-item'));
|
||||
const menuList = debugElement.queryAll(By.css('.navlist__item'));
|
||||
expect(menuList.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should toggle sidenav when link clicked', () => {
|
||||
component.toggle = true;
|
||||
fixture.detectChanges();
|
||||
const button = debugElement.query(By.css('.list-item__admin-workbaskets')).nativeElement;
|
||||
const button = debugElement.query(By.css('.navlist__admin-workbaskets')).nativeElement;
|
||||
expect(button).toBeTruthy();
|
||||
button.click();
|
||||
expect(component.toggle).toBe(false);
|
||||
|
|
|
@ -19,14 +19,13 @@ export class SidenavListComponent implements OnInit {
|
|||
accessUrl = 'taskana/administration/access-items-management';
|
||||
classificationUrl = 'taskana/administration/classifications';
|
||||
workbasketsUrl = 'taskana/administration/workbaskets';
|
||||
administrationsUrl = 'taskana/administration';
|
||||
|
||||
administrationAccess = false;
|
||||
monitorAccess = false;
|
||||
workplaceAccess = false;
|
||||
historyAccess = false;
|
||||
|
||||
admin_url_list: any[];
|
||||
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, private sidenavService: SidenavService) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
|
|
@ -1,36 +1,60 @@
|
|||
<div class="dropdown">
|
||||
<button class="btn btn-default" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<span class="material-icons md-20 blue {{sort.sortDirection === 'asc'? '' : 'flip' }}"
|
||||
data-toggle="tooltip"
|
||||
title="{{sort.sortDirection === 'asc'? 'A-Z' : 'Z-A' }}">sort</span>
|
||||
<div class="sort">
|
||||
<button mat-stroked-button style="color: #555;" [matMenuTriggerFor]="sortMenu" matTooltip="Sort workbaskets">
|
||||
<mat-icon>sort</mat-icon>
|
||||
|
||||
<mat-menu #sortMenu="matMenu">
|
||||
<button mat-menu-item [matMenuTriggerFor]="sortDirection">Sort direction</button>
|
||||
<button mat-menu-item [matMenuTriggerFor]="sortValue">Sort value</button>
|
||||
</mat-menu>
|
||||
|
||||
<!-- SORT DIRECTION -->
|
||||
<mat-menu #sortDirection="matMenu">
|
||||
<!-- ASCENDING ORDER BUTTON -->
|
||||
<button mat-menu-item (click)="changeOrder('asc')">
|
||||
<span *ngIf="sort.sortDirection === 'asc'; else coloredAsc">
|
||||
<mat-icon class="sort__selected-value"> arrow_upward </mat-icon>
|
||||
<span class="sort__selected-value"> Ascending </span>
|
||||
</span>
|
||||
<ng-template #coloredAsc>
|
||||
<mat-icon> arrow_upward </mat-icon>
|
||||
<span> Ascending </span>
|
||||
</ng-template>
|
||||
</button>
|
||||
|
||||
<!-- DESCENDING ORDER BUTTON -->
|
||||
<button mat-menu-item (click)="changeOrder('desc')">
|
||||
<span *ngIf="sort.sortDirection === 'desc'; else coloredDesc">
|
||||
<mat-icon class="sort__selected-value"> arrow_downward </mat-icon>
|
||||
<span class="sort__selected-value"> Descending </span>
|
||||
</span>
|
||||
<ng-template #coloredDesc>
|
||||
<mat-icon> arrow_downward </mat-icon>
|
||||
<span> Descending </span>
|
||||
</ng-template>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<!-- SORT VALUE -->
|
||||
<mat-menu #sortValue="matMenu">
|
||||
<button mat-menu-item *ngFor="let sortingField of sortingFields | mapValues"
|
||||
(click)="changeSortBy(sortingField.key)">
|
||||
<span *ngIf="sortingField.value.toLowerCase() === sort.sortBy; else coloredValue" class="sort__selected-value">
|
||||
{{sortingField.value}}
|
||||
</span>
|
||||
<ng-template #coloredValue>
|
||||
<span>{{sortingField.value}}</span>
|
||||
</ng-template>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-{{menuPosition}} sortby-dropdown popup"
|
||||
aria-labelledby="sortingDropdown">
|
||||
<div>
|
||||
<div class="col-xs-6">
|
||||
<h5>Sort By</h5>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button id="sort-by-direction-asc" type="button" (click)="changeOrder('asc')"
|
||||
data-toggle="tooltip"
|
||||
title="A-Z"
|
||||
class="btn btn-default {{sort.sortDirection === 'asc'? 'selected' : '' }}">
|
||||
<span class="material-icons md-20 blue ">sort</span>
|
||||
</button>
|
||||
<button id="sort-by-direction-desc" type="button" (click)="changeOrder('desc')"
|
||||
data-toggle="tooltip"
|
||||
title="Z-A"
|
||||
class="btn btn-default {{sort.sortDirection === 'desc'? 'selected' : '' }}">
|
||||
<span class="material-icons md-20 blue flip">sort</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div role="separator" class="divider"></div>
|
||||
<mat-radio-group name="sort" color="accent" class="radio-group">
|
||||
<mat-radio-button *ngFor="let sortingField of sortingFields | mapValues"
|
||||
name="sort" id="sort-by-{{sortingField.key}}" [checked]="sortingField.key === defaultSortBy"
|
||||
(change)="changeSortBy(sortingField.key)" [value]="sortingField.value"> {{ sortingField.value }}</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- with radio buttons
|
||||
<mat-radio-group name="sort" class="radio-group">
|
||||
<mat-radio-button *ngFor="let sortingField of sortingFields | mapValues"
|
||||
name="sort" [checked]="sortingField.key === defaultSortBy"
|
||||
(change)="changeSortBy(sortingField.key)" [value]="sortingField.value"> {{ sortingField.value }}</mat-radio-button>
|
||||
</mat-radio-group> -->
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
.sortby-dropdown {
|
||||
min-width: 200px;
|
||||
}
|
||||
@import 'src/theme/_colors.scss';
|
||||
|
||||
.bold-blue {
|
||||
color: #337ab7;
|
||||
.sort__selected-value {
|
||||
color: $aquamarine;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0rem 2.5rem;
|
||||
}
|
||||
|
|
|
@ -31,4 +31,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { MatSidenav } from '@angular/material';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
|
@ -19,6 +19,7 @@ import { WorkbasketRepresentation } from '../../models/workbasket-representation
|
|||
export class WorkbasketService {
|
||||
public workBasketSelected = new Subject<string>();
|
||||
public workBasketSaved = new Subject<number>();
|
||||
public workbasketActionToolbarExpanded = new Subject<boolean>();
|
||||
private workbasketSummaryRef: Observable<WorkbasketSummaryRepresentation> = new Observable();
|
||||
|
||||
constructor(private httpClient: HttpClient, private domainService: DomainService) {}
|
||||
|
@ -146,6 +147,14 @@ export class WorkbasketService {
|
|||
return this.workBasketSelected.asObservable();
|
||||
}
|
||||
|
||||
expandWorkbasketActionToolbar(value: boolean) {
|
||||
this.workbasketActionToolbarExpanded.next(value);
|
||||
}
|
||||
|
||||
getWorkbasketActionToolbarExpansion(): Observable<boolean> {
|
||||
return this.workbasketActionToolbarExpanded.asObservable();
|
||||
}
|
||||
|
||||
triggerWorkBasketSaved() {
|
||||
this.workBasketSaved.next(Date.now());
|
||||
}
|
||||
|
|
|
@ -54,6 +54,11 @@ import { ToastComponent } from './components/toast/toast.component';
|
|||
import { DialogPopUpComponent } from './components/popup/dialog-pop-up.component';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
|
@ -99,7 +104,17 @@ const DECLARATIONS = [
|
|||
|
||||
@NgModule({
|
||||
declarations: DECLARATIONS,
|
||||
imports: [MODULES, MatRadioModule, MatFormFieldModule, MatInputModule],
|
||||
imports: [
|
||||
MODULES,
|
||||
MatRadioModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatMenuModule,
|
||||
MatTooltipModule,
|
||||
MatPaginatorModule,
|
||||
MatSelectModule
|
||||
],
|
||||
exports: DECLARATIONS,
|
||||
providers: [
|
||||
{
|
||||
|
|
|
@ -3,6 +3,8 @@ import { TaskanaQueryParameters } from '../../util/query-parameters';
|
|||
import { Direction } from '../../models/sorting';
|
||||
import { ACTION } from '../../models/action';
|
||||
import { WorkbasketAccessItems } from '../../models/workbasket-access-items';
|
||||
import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
|
||||
import { ButtonAction } from '../../../administration/models/button-action';
|
||||
|
||||
// Workbasket List
|
||||
export class GetWorkbasketsSummary {
|
||||
|
@ -43,6 +45,17 @@ export class SetActiveAction {
|
|||
constructor(public action: ACTION) {}
|
||||
}
|
||||
|
||||
//Workbasket Details
|
||||
export class SelectComponent {
|
||||
static readonly type = '[Workbasket] Select component';
|
||||
constructor(public component: WorkbasketComponent) {}
|
||||
}
|
||||
|
||||
export class OnButtonPressed {
|
||||
static readonly type = '[Workbasket] Button pressed';
|
||||
constructor(public button: ButtonAction) {}
|
||||
}
|
||||
|
||||
// Workbasket Information
|
||||
export class SaveNewWorkbasket {
|
||||
static readonly type = '[Workbasket] Save new workbasket';
|
||||
|
|
|
@ -6,6 +6,8 @@ import { ACTION } from '../../models/action';
|
|||
import { WorkbasketAccessItemsRepresentation } from '../../models/workbasket-access-items-representation';
|
||||
import { WorkbasketDistributionTargets } from '../../models/workbasket-distribution-targets';
|
||||
import { Workbasket } from '../../models/workbasket';
|
||||
import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
|
||||
import { ButtonAction } from '../../../administration/models/button-action';
|
||||
|
||||
export class WorkbasketSelectors {
|
||||
// Workbasket
|
||||
|
@ -37,6 +39,16 @@ export class WorkbasketSelectors {
|
|||
};
|
||||
}
|
||||
|
||||
@Selector([WorkbasketState])
|
||||
static selectedComponent(state: WorkbasketStateModel): WorkbasketComponent {
|
||||
return state.selectedComponent;
|
||||
}
|
||||
|
||||
@Selector([WorkbasketState])
|
||||
static buttonAction(state: WorkbasketStateModel): ButtonAction {
|
||||
return state.button;
|
||||
}
|
||||
|
||||
// Workbasket Access Items
|
||||
@Selector([WorkbasketState])
|
||||
static workbasketAccessItems(state: WorkbasketStateModel): WorkbasketAccessItemsRepresentation {
|
||||
|
|
|
@ -12,8 +12,10 @@ import {
|
|||
GetWorkbasketDistributionTargets,
|
||||
GetWorkbasketsSummary,
|
||||
MarkWorkbasketForDeletion,
|
||||
OnButtonPressed,
|
||||
RemoveDistributionTarget,
|
||||
SaveNewWorkbasket,
|
||||
SelectComponent,
|
||||
SelectWorkbasket,
|
||||
SetActiveAction,
|
||||
UpdateWorkbasket,
|
||||
|
@ -27,6 +29,8 @@ import { NotificationService } from '../../services/notifications/notification.s
|
|||
import { WorkbasketAccessItemsRepresentation } from '../../models/workbasket-access-items-representation';
|
||||
import { WorkbasketDistributionTargets } from '../../models/workbasket-distribution-targets';
|
||||
import { WorkbasketSummary } from '../../models/workbasket-summary';
|
||||
import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
|
||||
import { ButtonAction } from '../../../administration/models/button-action';
|
||||
|
||||
class InitializeStore {
|
||||
static readonly type = '[Workbasket] Initializing state';
|
||||
|
@ -42,6 +46,9 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
|
||||
@Action(GetWorkbasketsSummary)
|
||||
getWorkbasketsSummary(ctx: StateContext<WorkbasketStateModel>, action: GetWorkbasketsSummary): Observable<any> {
|
||||
ctx.patchState({
|
||||
paginatedWorkbasketsSummary: undefined
|
||||
});
|
||||
return this.workbasketService
|
||||
.getWorkBasketsSummary(
|
||||
action.forceRequest,
|
||||
|
@ -110,8 +117,31 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
return of(null);
|
||||
}
|
||||
|
||||
@Action(SelectComponent)
|
||||
selectComponent(ctx: StateContext<WorkbasketStateModel>, action: SelectComponent): Observable<any> {
|
||||
switch (action.component) {
|
||||
case WorkbasketComponent.INFORMATION:
|
||||
ctx.patchState({ selectedComponent: WorkbasketComponent.INFORMATION });
|
||||
break;
|
||||
case WorkbasketComponent.ACCESS_ITEMS:
|
||||
ctx.patchState({ selectedComponent: WorkbasketComponent.ACCESS_ITEMS });
|
||||
break;
|
||||
case WorkbasketComponent.DISTRIBUTION_TARGETS:
|
||||
ctx.patchState({ selectedComponent: WorkbasketComponent.DISTRIBUTION_TARGETS });
|
||||
break;
|
||||
}
|
||||
return of(null);
|
||||
}
|
||||
|
||||
@Action(OnButtonPressed)
|
||||
doWorkbasketDetailsAction(ctx: StateContext<WorkbasketStateModel>, action: OnButtonPressed): Observable<any> {
|
||||
ctx.patchState({ button: action.button });
|
||||
return of(null);
|
||||
}
|
||||
|
||||
@Action(SaveNewWorkbasket)
|
||||
saveNewWorkbasket(ctx: StateContext<WorkbasketStateModel>, action: SaveNewWorkbasket): Observable<any> {
|
||||
ctx.dispatch(new OnButtonPressed(undefined));
|
||||
return this.workbasketService.createWorkbasket(action.workbasket).pipe(
|
||||
take(1),
|
||||
tap(
|
||||
|
@ -134,6 +164,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
@Action(CopyWorkbasket)
|
||||
copyWorkbasket(ctx: StateContext<WorkbasketStateModel>, action: CopyWorkbasket): Observable<any> {
|
||||
this.location.go(this.location.path().replace(/(workbaskets).*/g, 'workbaskets/(detail:new-workbasket)'));
|
||||
ctx.dispatch(new OnButtonPressed(undefined));
|
||||
ctx.patchState({
|
||||
action: ACTION.COPY
|
||||
});
|
||||
|
@ -142,6 +173,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
|
||||
@Action(UpdateWorkbasket)
|
||||
updateWorkbasket(ctx: StateContext<WorkbasketStateModel>, action: UpdateWorkbasket): Observable<any> {
|
||||
ctx.dispatch(new OnButtonPressed(undefined));
|
||||
return this.workbasketService.updateWorkbasket(action.url, action.workbasket).pipe(
|
||||
take(1),
|
||||
tap(
|
||||
|
@ -156,7 +188,6 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
paginatedWorkbasketSummary.workbaskets,
|
||||
action.workbasket
|
||||
);
|
||||
|
||||
ctx.patchState({
|
||||
selectedWorkbasket: updatedWorkbasket,
|
||||
paginatedWorkbasketsSummary: paginatedWorkbasketSummary
|
||||
|
@ -171,6 +202,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
|
||||
@Action(RemoveDistributionTarget)
|
||||
removeDistributionTarget(ctx: StateContext<WorkbasketStateModel>, action: RemoveDistributionTarget): Observable<any> {
|
||||
ctx.dispatch(new OnButtonPressed(undefined));
|
||||
return this.workbasketService.removeDistributionTarget(action.url).pipe(
|
||||
take(1),
|
||||
tap(
|
||||
|
@ -193,6 +225,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
|
||||
@Action(MarkWorkbasketForDeletion)
|
||||
deleteWorkbasket(ctx: StateContext<WorkbasketStateModel>, action: MarkWorkbasketForDeletion): Observable<any> {
|
||||
ctx.dispatch(new OnButtonPressed(undefined));
|
||||
return this.workbasketService.markWorkbasketForDeletion(action.url).pipe(
|
||||
take(1),
|
||||
tap((response) => {
|
||||
|
@ -308,4 +341,6 @@ export interface WorkbasketStateModel {
|
|||
action: ACTION;
|
||||
workbasketAccessItems: WorkbasketAccessItemsRepresentation;
|
||||
workbasketDistributionTargets: WorkbasketDistributionTargets;
|
||||
selectedComponent: WorkbasketComponent;
|
||||
button: ButtonAction | undefined;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue