TSK-1476: Rework workbasket access items (#1360)

* TSK-1476: Rework Workbasket Access Items

* TSK-1476: Rework Workbasket Access Items

* TSK-1476: update
This commit is contained in:
Franzi321 2020-12-18 15:27:59 +01:00 committed by GitHub
parent 55a575474d
commit f42f79ef69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 336 additions and 171 deletions

View File

@ -60,6 +60,11 @@ describe('AccessItemsManagementComponent', () => {
let store: Store;
let actions$: Observable<any>;
@Component({ selector: 'taskana-shared-spinner', template: '' })
class TaskanaSharedSpinnerStub {
@Input() isRunning: boolean;
}
@Component({ selector: 'taskana-shared-sort', template: '' })
class TaskanaSharedSortStub {
@Input() sortingFields: Map<WorkbasketAccessItemQuerySortParameter, string>;
@ -91,7 +96,12 @@ describe('AccessItemsManagementComponent', () => {
MatListModule,
MatExpansionModule
],
declarations: [AccessItemsManagementComponent, TypeAheadComponent, TaskanaSharedSortStub],
declarations: [
AccessItemsManagementComponent,
TypeAheadComponent,
TaskanaSharedSortStub,
TaskanaSharedSpinnerStub
],
providers: [
{ provide: FormsValidatorService, useClass: formValidatorServiceSpy },
{ provide: NotificationService, useClass: notificationServiceSpy },
@ -148,10 +158,8 @@ describe('AccessItemsManagementComponent', () => {
const groups = store.selectSnapshot((state) => state.accessItemsManagement);
expect(selectedAccessId).not.toBeNull();
expect(groups).not.toBeNull();
expect(app.accessItemsForm).not.toBeNull();
app.onSelectAccessId(null);
expect(app.accessItemsForm).toBeNull();
expect(groups).toMatchObject({});
});
it('should dispatch GetAccessItems action in searchForAccessItemsWorkbaskets', async((done) => {

View File

@ -1,10 +1,24 @@
<div *ngIf="workbasket" id="wb-information">
<!-- ACCESS ITEMS -->
<div class="workbasket-access-items">
<form [formGroup]="AccessItemsForm">
<table formArrayName="accessItemsGroups" id="table-access-items" class="workbasket-access-items__table table-striped">
<div class="workbasket-access-items"
[ngStyle]="{'width': expanded ? 'calc(' + 100 + 'vw - 500px)' : 'calc(' + 100 + 'vw - 250px)'}">
<div class=" workbasket-access-items__buttons">
<button mat-stroked-button matTooltip="Add new access" type="button"
class="workbasket-access-items__buttons-add-access" (click)="addAccessItem()" data-toggle="tooltip"
title="Add new access">
Add new access
<mat-icon color="green-blue" aria-label="add new access">add</mat-icon>
</button>
<button mat-stroked-button matTooltip="Delete selected access" type="button"
class="workbasket-access-items__buttons-delete-access" (click)="deleteAccessItems()"
data-toggle="tooltip" title="delete selected access">
Delete selected access
<mat-icon color="green-blue" aria-label="delete access">delete</mat-icon>
</button>
</div>
<form [formGroup]="AccessItemsForm" class="workbasket-access-items__form">
<table formArrayName="accessItemsGroups" id="table-access-items" class="workbasket-access-items__table">
<!-- TITLE ROW -->
<thead>
<tr>
@ -22,86 +36,98 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let accessItem of accessItemsGroups.controls; let index = index;" [formGroupName]="index">
<tr *ngFor="let accessItem of accessItemsGroups?.controls; let index = index;"
[formGroupName]="index">
<!-- REMOVE BUTTON -->
<td>
<button mat-button type="button" style="padding: 3px;" (click)="remove(index)" title="Remove">
<span class="material-icons md-24 red">clear</span>
</button>
<input class="workbasket-access-items__select-row" type="checkbox" aria-label="selectRow"
aria-labelledby="selectRow" (change)="selectRow($event, index)">
</td>
<!-- ACCESS ID -->
<td *ngIf="(accessItemsCustomization$ | async)?.accessId.lookupField else accessIdInput"
class="workbasket-access-items__typeahead"
[ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
class="workbasket-access-items__typeahead" [ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
'has-error': !accessItem.value.accessId }">
<taskana-shared-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required"
[validationValue]="toggleValidationAccessIdMap.get(index)"
[displayError]="!isFieldValid('accessItem.value.accessId', index)"
(selectedItem)="accessItemSelected($event, index)"
(inputField)="focusNewInput($event)">
</taskana-shared-type-ahead>
</td>
<taskana-shared-type-ahead formControlName="accessId"
placeHolderMessage="* Access id is required"
[validationValue]="toggleValidationAccessIdMap.get(index)"
[displayError]="!isFieldValid('accessItem.value.accessId', index)"
(selectedItem)="accessItemSelected($event, index)">
</taskana-shared-type-ahead>
</td>
<ng-template #accessIdInput>
<td>
<div [ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
'has-error': !accessItem.value.accessId && formsValidatorService.formSubmitAttempt}">
<input matInput type="text" formControlName="accessId"
placeholder="{{accessItem.invalid ? '* Access id is required': ''}}"
[@validation]="toggleValidationAccessIdMap.get(index)" #htmlInputElement>
<input matInput type="text" formControlName="accessId"
placeholder="{{accessItem.invalid ? '* Access id is required': ''}}"
[@validation]="toggleValidationAccessIdMap.get(index)" #htmlInputElement>
</div>
</td>
</ng-template>
<!-- SELECT ALL -->
<td>
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-00" (change)="checkAll(index, $event)" aria-label="checkAll" aria-labelledby="checkAll">
</td>
<input class="workbasket-access-items__check-all" type="checkbox" id="checkbox-{{index}}-00"
(change)="checkAll(index, $event)" aria-label="checkAll" aria-labelledby="checkAll">
</td>
<!-- READ -->
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permRead !== accessItem.value.permRead)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-0" formControlName="permRead" aria-label="permRead" aria-labelledby="permRead">
<!-- READ -->
<td
[ngClass]="{ 'has-changes': (accessItemsClone[index].permRead !== accessItem.value.permRead)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox"
id="checkbox-{{index}}-0" formControlName="permRead" aria-label="permRead"
aria-labelledby="permRead" (change)="checkboxClicked(this.index, $event)">
</td>
<!-- OPEN -->
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permOpen !== accessItem.value.permOpen)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-1" formControlName="permOpen" aria-label="permOpen" aria-labelledby="permOpen">
<td
[ngClass]="{ 'has-changes': (accessItemsClone[index].permOpen !== accessItem.value.permOpen)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox"
id="checkbox-{{index}}-1" formControlName="permOpen" aria-label="permOpen"
aria-labelledby="permOpen" (change)="checkboxClicked(this.index, $event)">
</td>
<!-- APPEND -->
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permAppend !== accessItem.value.permAppend)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-2" formControlName="permAppend" aria-label="permAppend" aria-labelledby="permAppend">
<td
[ngClass]="{ 'has-changes': (accessItemsClone[index].permAppend !== accessItem.value.permAppend)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox"
id="checkbox-{{index}}-2" formControlName="permAppend" aria-label="permAppend"
aria-labelledby="permAppend" (change)="checkboxClicked(this.index, $event)">
</td>
<!-- TRANSFER -->
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permTransfer !== accessItem.value.permTransfer)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-3" formControlName="permTransfer" aria-label="permTransfer" aria-labelledby="permTransfer">
<td
[ngClass]="{ 'has-changes': (accessItemsClone[index].permTransfer !== accessItem.value.permTransfer)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox"
id="checkbox-{{index}}-3" formControlName="permTransfer" aria-label="permTransfer"
aria-labelledby="permTransfer" (change)="checkboxClicked(this.index, $event)">
</td>
<!-- DISTRIBUTE -->
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permDistribute !== accessItem.value.permDistribute)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-4" formControlName="permDistribute" aria-label="permDistribute" aria-labelledby="permDistribute">
<td
[ngClass]="{ 'has-changes': (accessItemsClone[index].permDistribute !== accessItem.value.permDistribute)}">
<input class="workbasket-access-items__permission-checkbox" type="checkbox"
id="checkbox-{{index}}-4" formControlName="permDistribute" aria-label="permDistribute"
aria-labelledby="permDistribute" (change)="checkboxClicked(this.index, $event)">
</td>
<!-- CUSTOM FIELDS -->
<ng-container *ngFor="let customField of customFields$ | async; let customIndex = index">
<td *ngIf="customField.visible" [ngClass]="{ 'has-changes': accessItemsClone[index][getAccessItemCustomProperty(customIndex + 1)] !== accessItem.value[getAccessItemCustomProperty(customIndex+1)] }">
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-{{customIndex + 5}}" formControlName="permCustom{{customIndex+1}}" aria-label="customField" aria-labelledby="customField">
<td *ngIf="customField.visible"
[ngClass]="{ 'has-changes': accessItemsClone[index][getAccessItemCustomProperty(customIndex + 1)] !== accessItem.value[getAccessItemCustomProperty(customIndex+1)] }">
<input class="workbasket-access-items__permission-checkbox" type="checkbox"
id="checkbox-{{index}}-{{customIndex + 5}}"
formControlName="permCustom{{customIndex+1}}" aria-label="customField"
aria-labelledby="customField" (change)="checkboxClicked(this.index, $event)">
</td>
</ng-container>
</tr>
</tbody>
</table>
</form>
<!-- ADD ACCESS ITEM -->
<button mat-stroked-button type="button" class="workbasket-access-items__add-access" (click)="addAccessItem()" data-toggle="tooltip" title="Add new access">
<span class="material-icons md-20 green-blue">add</span>
<span>Add new access</span>
</button>
</div>
</div>

View File

@ -1,22 +1,94 @@
@import '../../../../theme/colors';
@import '~@angular/material/theming';
.workbasket-access-items {
max-width: calc(100vw - 500px);
}
.workbasket-access-items__typeahead {
text-align: left;
min-width: 180px;
width: calc(100% + 20px);
}
td > input[type='checkbox'] {
margin-top: 0;
display: block;
}
.panel-body {
height: calc(100vh - 213px);
overflow-y: auto;
overflow-x: auto;
padding-top: 0;
display: inline-block;
&__typeahead {
text-align: left;
min-width: 180px;
width: calc(100% + 20px);
line-height: 20px;
padding-bottom: 0% !important;
margin-top: 5px;
}
&__table {
margin-top: 20px;
margin-left: auto;
margin-right: auto;
width: 98%;
text-align: center;
& th {
padding: 0.25rem;
position: sticky;
top: 0;
z-index: 3;
background: white;
}
& td {
padding-left: 0.5rem;
vertical-align: initial;
border-top: 1px solid #dee2e6;
}
& tr:first-child > td {
border-top: 2px solid #dddddd;
}
& td > input[type='checkbox'] {
margin-top: 0;
display: block;
}
& tr:nth-child(odd) > td {
background-color: rgba(0, 0, 0, 0.05);
}
& input[type='checkbox']:checked {
filter: hue-rotate(320deg) brightness(1);
}
}
&__buttons-add-access {
color: white;
border: none;
background: $aquamarine;
margin: 10px 3px 0px 6px;
outline: none;
}
&__buttons-delete-access {
color: red;
margin: 2px 0px 0px 3px;
outline: none;
}
&__permission-checkbox,
&__check-all,
&__select-row {
display: inline-block;
height: 1rem;
line-height: 0;
margin: auto;
order: 0;
position: relative;
white-space: nowrap;
width: 1rem;
flex-shrink: 0;
cursor: pointer;
@supports (-moz-appearance: none) {
//bigger checkboxes for firefox because firefox renders differently
width: 1.25rem;
height: 1.25rem;
}
}
}
.required-header {
@ -27,55 +99,17 @@ td > input[type='checkbox'] {
color: red;
}
th {
padding: 0.25rem;
position: sticky;
top: 0;
z-index: 3;
background: white;
}
.workbasket-access-items__table {
margin-top: 20px;
}
.workbasket-access-items__table thead th {
vertical-align: bottom;
border-bottom: 2px solid #dee2e6;
::ng-deep .workbasket-access-items__form .mat-form-field-wrapper {
padding-bottom: 0em;
height: 58px;
}
.workbasket-access-items__table td,
.table th {
padding: 0.5rem;
vertical-align: middle;
border-top: 1px solid #dee2e6;
::ng-deep .workbasket-access-items__typeahead .typeahead__form .mat-form-field-infix {
padding: 0 0 0 0;
position: initial;
font-size: medium;
}
.workbasket-access-items__table > thead > tr > th {
max-width: 150px;
border-bottom: none;
}
.workbasket-access-items__permission-checkbox {
display: inline-block;
height: 1rem;
line-height: 0;
margin: auto;
order: 0;
position: relative;
white-space: nowrap;
width: 1rem;
flex-shrink: 0;
cursor: pointer;
@supports (-moz-appearance: none) {
//bigger checkboxes for firefox because firefox renders differently
width: 1.25rem;
height: 1.25rem;
}
}
input[type='checkbox']:checked {
filter: hue-rotate(320deg) brightness(1);
}
.workbasket-access-items__add-access {
margin: 16px 0 16px 16px;
::ng-deep .mat-form-field-appearance-outline .mat-form-field-flex {
margin-top: 0.7em;
}

View File

@ -1,6 +1,6 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { WorkbasketAccessItemsComponent } from './workbasket-access-items.component';
import { DebugElement } from '@angular/core';
import { Component, DebugElement, Input } from '@angular/core';
import { Actions, NgxsModule, ofActionDispatched, Store } from '@ngxs/store';
import { Observable, of } from 'rxjs';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@ -32,11 +32,20 @@ import {
} from '../../../shared/store/workbasket-store/workbasket.actions';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ACTION } from '../../../shared/models/action';
import { WorkbasketAccessItems } from '../../../shared/models/workbasket-access-items';
import { MatSelectModule } from '@angular/material/select';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
@Component({ selector: 'taskana-shared-spinner', template: '' })
class SpinnerStub {
@Input() isRunning: boolean;
@Input() positionClass: string;
}
const savingWorkbasketServiceSpy = jest.fn().mockImplementation(
(): Partial<SavingWorkbasketService> => ({
@ -91,9 +100,11 @@ describe('WorkbasketAccessItemsComponent', () => {
MatInputModule,
MatSelectModule,
MatAutocompleteModule,
MatProgressBarModule
MatProgressBarModule,
MatCheckboxModule,
MatIconModule
],
declarations: [WorkbasketAccessItemsComponent, TypeAheadComponent],
declarations: [WorkbasketAccessItemsComponent, TypeAheadComponent, SpinnerStub],
providers: [
{ provide: SavingWorkbasketService, useClass: savingWorkbasketServiceSpy },
{ provide: RequestInProgressService, useClass: requestInProgressServiceSpy },
@ -152,7 +163,9 @@ describe('WorkbasketAccessItemsComponent', () => {
it('should add accessItems when add access item button is clicked', () => {
fixture.detectChanges();
const addAccessItemButton = debugElement.nativeElement.querySelector('button.workbasket-access-items__add-access');
const addAccessItemButton = debugElement.nativeElement.querySelector(
'button.workbasket-access-items__buttons-add-access'
);
const clearSpy = jest.spyOn(component, 'addAccessItem');
expect(addAccessItemButton.title).toMatch('Add new access');
@ -172,7 +185,6 @@ describe('WorkbasketAccessItemsComponent', () => {
const checkAllSpy = jest.spyOn(component, 'checkAll');
const checkAllButton = debugElement.nativeElement.querySelector('#checkbox-0-00');
expect(checkAllButton).toBeTruthy();
checkAllButton.click();
expect(checkAllSpy).toHaveBeenCalled();
});

View File

@ -7,7 +7,8 @@ import {
OnInit,
QueryList,
SimpleChanges,
ViewChildren
ViewChildren,
HostListener
} from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Actions, ofActionCompleted, Select, Store } from '@ngxs/store';
@ -51,12 +52,18 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
@Input()
action: ACTION;
@Input()
expanded: boolean;
@ViewChildren('htmlInputElement')
inputs: QueryList<ElementRef>;
badgeMessage = '';
selectedRows: number[] = [];
workbasketClone: Workbasket;
customFields$: Observable<CustomField[]>;
customFields: CustomField[];
accessItemsRepresentation: WorkbasketAccessItemsRepresentation;
accessItemsClone: Array<WorkbasketAccessItems>;
@ -99,6 +106,9 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
ngOnInit() {
this.init();
this.customFields$ = this.accessItemsCustomization$.pipe(getCustomFields(customFieldCount));
this.customFields$.subscribe((v) => {
this.customFields = v;
});
this.accessItemsRepresentation$.subscribe((accessItemsRepresentation) => {
if (typeof accessItemsRepresentation !== 'undefined') {
this.accessItemsRepresentation = accessItemsRepresentation;
@ -134,10 +144,42 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
});
}
ngAfterViewChecked() {
let elementIndex = 0;
let isTrue = true;
if (this.accessItemsGroups.controls) {
this.accessItemsGroups.controls.forEach((element) => {
for (let i in element.value) {
if (i.startsWith('perm')) {
if (this.accessItemsGroups.controls[elementIndex].value[i] === false) {
isTrue = false;
break;
}
}
}
if (isTrue) {
const checkbox = document.getElementById(`checkbox-${elementIndex}-00`) as HTMLInputElement;
if (checkbox) {
checkbox.checked = true;
elementIndex++;
}
}
});
}
}
ngOnChanges(changes?: SimpleChanges) {
console.log('change');
if (changes.action) {
this.setBadge();
}
if (this.workbasketClone) {
if (this.workbasketClone.workbasketId != this.workbasket.workbasketId) {
this.init();
}
}
this.workbasketClone = this.workbasket;
//var offsetWidth = document.getElementById('container').offsetWidth;
}
init() {
@ -205,8 +247,8 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
workbasketAccessItems.permRead = true;
const newForm = this.formBuilder.group(workbasketAccessItems);
newForm.controls.accessId.setValidators(Validators.required);
this.accessItemsGroups.push(newForm);
this.accessItemsClone.push(workbasketAccessItems);
this.accessItemsGroups.insert(0, newForm);
this.accessItemsClone.unshift(workbasketAccessItems);
this.added = true;
}
@ -219,11 +261,6 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
this.notificationsService.showToast(NOTIFICATION_TYPES.INFO_ALERT);
}
remove(index: number) {
this.accessItemsGroups.removeAt(index);
this.accessItemsClone.splice(index, 1);
}
isFieldValid(field: string, index: number): boolean {
return this.formsValidatorService.isFieldValid(this.accessItemsGroups[index], field);
}
@ -255,7 +292,7 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
}
accessItemSelected(accessItem: AccessIdDefinition, row: number) {
this.accessItemsGroups.controls[row].get('accessId').setValue(accessItem.accessId);
this.accessItemsGroups.controls[row].get('accessId').setValue(accessItem?.accessId);
this.accessItemsGroups.controls[row].get('accessName').setValue(accessItem.name);
}
@ -273,6 +310,33 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
});
}
checkboxClicked(index: number, value: any) {
if (value.currentTarget.checked) {
let isTrue = true;
let numbers = [];
const notVisibleFields = this.customFields.filter((v) => v.visible === false);
notVisibleFields.forEach((element) => {
const num = element.field.toString().replace('Custom ', 'permCustom');
numbers.push(num);
});
for (let i in this.accessItemsGroups.controls[index].value) {
if (i.startsWith('perm')) {
if (this.accessItemsGroups.controls[index].value[i] === false && !numbers.includes(i)) {
isTrue = false;
break;
}
}
}
if (isTrue) {
const checkbox = document.getElementById(`checkbox-${index}-00`) as HTMLInputElement;
checkbox.checked = true;
}
} else {
const checkbox = document.getElementById(`checkbox-${index}-00`) as HTMLInputElement;
checkbox.checked = false;
}
}
setBadge() {
if (this.action === ACTION.COPY) {
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
@ -296,12 +360,27 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
return `permCustom${customNumber}`;
}
focusNewInput(input: ElementRef) {
if (this.added) {
input.nativeElement.focus();
selectRow(value: any, index: number) {
if (value.target.checked) {
this.selectedRows.push(index);
} else {
this.selectedRows = this.selectedRows.filter(function (number) {
return number != index;
});
}
}
deleteAccessItems() {
this.selectedRows.sort(function (a, b) {
return b - a;
});
this.selectedRows.forEach((element) => {
this.accessItemsGroups.removeAt(element);
this.accessItemsClone.splice(element, 1);
});
this.selectedRows = [];
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();

View File

@ -4,49 +4,59 @@
<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()">
<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()">
<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">
<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()">
<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()">
<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()">
<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()">
<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)" [selectedIndex]="selectedTab$ | async">
<mat-tab-group animationDuration="0ms" (selectedIndexChange)="selectComponent($event)"
[selectedIndex]="selectedTab$ | async">
<mat-tab label="Information">
<taskana-administration-workbasket-information [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-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"></taskana-administration-workbasket-access-items>
<taskana-administration-workbasket-access-items [workbasket]="workbasket" [action]="action" [expanded]="expanded">
</taskana-administration-workbasket-access-items>
</mat-tab>
<mat-tab label="Distribution Targets">
<taskana-administration-workbasket-distribution-targets [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-distribution-targets>
<taskana-administration-workbasket-distribution-targets [workbasket]="workbasket" [action]="action">
</taskana-administration-workbasket-distribution-targets>
</mat-tab>
</mat-tab-group>
</div>

View File

@ -39,6 +39,7 @@ class WorkbasketAccessItemsStub {
@Input() workbasket: Workbasket;
@Input() action: ACTION;
@Input() active: string;
@Input() expanded: boolean;
}
@Component({ selector: 'taskana-administration-workbasket-distribution-targets', template: '' })

View File

@ -1,4 +1,4 @@
import { Component, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { Component, Input, 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';
@ -45,6 +45,8 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges
destroy$ = new Subject<void>();
@Input() expanded: boolean;
constructor(
private location: Location,
private route: ActivatedRoute,

View File

@ -12,7 +12,7 @@
</div>
<div class="workbasket-overview__details" *ngIf="showDetail; else showEmptyPage">
<taskana-administration-workbasket-details></taskana-administration-workbasket-details>
<taskana-administration-workbasket-details [expanded]="expanded"></taskana-administration-workbasket-details>
</div>
<ng-template #showEmptyPage>

View File

@ -53,8 +53,9 @@ class WorkbasketListStub {
}
@Component({ selector: 'taskana-administration-workbasket-details', template: '' })
class WorkbasketDetailsStub {}
class WorkbasketDetailsStub {
@Input() expanded: boolean;
}
@Component({ selector: 'svg-icon', template: '' })
class SvgIconStub {}

View File

@ -1,7 +1,10 @@
<div *ngIf="dataSource" class="typeahead">
<form>
<mat-form-field class="typeahead__form" appearance="outline">
<mat-label>{{dataSource.selected?.name ? 'Owner: ' + dataSource.selected?.name : placeHolderMessage}}</mat-label>
<mat-label *ngIf="!isFirst">
{{dataSource.selected?.name ? 'Name: ' + dataSource.selected?.name : placeHolderMessage}}</mat-label>
<mat-label *ngIf="isFirst">
{{dataSource.selected?.name}}</mat-label>
<input #inputTypeAhead [required]="isRequired" class="typeahead__form-input align" matInput type="text"
[matAutocomplete]="auto" placeholder="{{placeHolderMessage}}" [(ngModel)]="value" name="accessId"
(ngModelChange)="initializeDataSource()" />

View File

@ -1,17 +1,8 @@
import {
Component,
Input,
ViewChild,
forwardRef,
Output,
EventEmitter,
ElementRef,
AfterViewInit
} from '@angular/core';
import { Component, Input, ViewChild, forwardRef, Output, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs';
import { AccessIdsService } from 'app/shared/services/access-ids/access-ids.service';
import { ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { highlight } from 'app/shared/animations/validation.animation';
import { mergeMap } from 'rxjs/operators';
import { AccessIdDefinition } from 'app/shared/models/access-id';
@ -29,10 +20,10 @@ import { AccessIdDefinition } from 'app/shared/models/access-id';
}
]
})
export class TypeAheadComponent implements AfterViewInit, ControlValueAccessor {
export class TypeAheadComponent implements ControlValueAccessor {
dataSource: any;
typing = false;
isFirst = false;
items = [];
@Input()
@ -56,12 +47,7 @@ export class TypeAheadComponent implements AfterViewInit, ControlValueAccessor {
@Output()
selectedItem = new EventEmitter<AccessIdDefinition>();
@Output()
inputField = new EventEmitter<ElementRef>();
@ViewChild('inputTypeAhead')
private inputTypeAhead;
typeaheadLoading = false;
typeaheadMinLength = 3;
typeaheadWaitMs = 500;
@ -91,6 +77,9 @@ export class TypeAheadComponent implements AfterViewInit, ControlValueAccessor {
writeValue(value: any) {
if (value !== this.innerValue) {
this.innerValue = value;
if (this.value) {
this.isFirst = true;
}
this.initializeDataSource();
}
}
@ -107,16 +96,16 @@ export class TypeAheadComponent implements AfterViewInit, ControlValueAccessor {
constructor(private accessIdsService: AccessIdsService) {}
ngAfterViewInit() {
this.inputField.emit(this.inputTypeAhead);
}
initializeDataSource() {
this.dataSource = new Observable((observer: any) => {
observer.next(this.value);
}).pipe(mergeMap((token: string) => this.getUsersAsObservable(token)));
this.accessIdsService.searchForAccessId(this.value).subscribe((items) => {
this.items = items;
if (this.isFirst) {
this.dataSource.selected = this.items.find((item) => item.accessId.toLowerCase() === this.value.toLowerCase());
this.selectedItem.emit(this.dataSource.selected);
}
});
}

View File

@ -19,7 +19,7 @@
"lookupField": true
},
"custom3": {
"field": "",
"field": "Custom 3",
"visible": false
},
"custom9": {
@ -27,15 +27,15 @@
"visible": true
},
"custom10": {
"field": "",
"field": "Custom 10",
"visible": false
},
"custom11": {
"field": "",
"field": "Custom 11",
"visible": false
},
"custom12": {
"field": "",
"field": "Custom 12",
"visible": false
}
}