TSK-492 Access validation, type ahead component created.
This commit is contained in:
parent
e2a781521e
commit
1c93702063
|
@ -38,10 +38,19 @@
|
|||
"requires": {
|
||||
"ajv": "5.5.2",
|
||||
"chokidar": "1.7.0",
|
||||
"rxjs": "5.5.6",
|
||||
"rxjs": "5.5.10",
|
||||
"source-map": "0.5.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "5.5.10",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz",
|
||||
"integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"symbol-observable": "1.0.1"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
|
@ -57,7 +66,18 @@
|
|||
"dev": true,
|
||||
"requires": {
|
||||
"@ngtools/json-schema": "1.2.0",
|
||||
"rxjs": "5.5.6"
|
||||
"rxjs": "5.5.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "5.5.10",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz",
|
||||
"integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"symbol-observable": "1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular/animations": {
|
||||
|
@ -117,7 +137,7 @@
|
|||
"postcss-url": "7.3.1",
|
||||
"raw-loader": "0.5.1",
|
||||
"resolve": "1.5.0",
|
||||
"rxjs": "5.5.6",
|
||||
"rxjs": "5.5.10",
|
||||
"sass-loader": "6.0.7",
|
||||
"semver": "5.5.0",
|
||||
"silent-error": "1.1.0",
|
||||
|
@ -176,6 +196,15 @@
|
|||
"osenv": "0.1.4"
|
||||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "5.5.10",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz",
|
||||
"integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"symbol-observable": "1.0.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
|
||||
|
@ -345,9 +374,20 @@
|
|||
"integrity": "sha512-7aVP4994Hu8vRdTTohXkfGWEwLhrdNP3EZnWyBootm5zshWqlQojUGweZe5zwewsKcixeVOiy2YtW+aI4aGSLA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"rxjs": "5.5.6",
|
||||
"rxjs": "5.5.10",
|
||||
"semver": "5.5.0",
|
||||
"semver-intersect": "1.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "5.5.10",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz",
|
||||
"integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"symbol-observable": "1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/jasmine": {
|
||||
|
@ -2096,7 +2136,7 @@
|
|||
},
|
||||
"compression": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.2.tgz",
|
||||
"resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz",
|
||||
"integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -9482,9 +9522,9 @@
|
|||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "5.5.6",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz",
|
||||
"integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==",
|
||||
"version": "5.5.9",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.9.tgz",
|
||||
"integrity": "sha512-DHG9AHmCmgaFWgjBcXp6NxFDmh3MvIA62GqTWmLnTzr/3oZ6h5hLD8NA+9j+GF0jEwklNIpI4KuuyLG8UWMEvQ==",
|
||||
"requires": {
|
||||
"symbol-observable": "1.0.1"
|
||||
}
|
||||
|
|
|
@ -23,21 +23,21 @@
|
|||
"@angular/platform-browser": "5.2.10",
|
||||
"@angular/platform-browser-dynamic": "5.2.10",
|
||||
"@angular/router": "5.2.10",
|
||||
"file-saver": "1.3.3",
|
||||
"angular-svg-icon": "5.0.0",
|
||||
"angular-tree-component": "7.1.0",
|
||||
"bootstrap": "3.3.7",
|
||||
"bootstrap-sass": "3.3.7",
|
||||
"chart.js": "2.7.1",
|
||||
"core-js": "2.5.3",
|
||||
"file-saver": "1.3.3",
|
||||
"jquery": "3.3.1",
|
||||
"magic-string": "0.22.4",
|
||||
"ng2-auto-complete": "0.12.0",
|
||||
"ng2-charts": "1.6.0",
|
||||
"ngx-bootstrap": "2.0.1",
|
||||
"node-sass": "4.7.2",
|
||||
"rxjs": "5.5.6",
|
||||
"zone.js": "0.8.20",
|
||||
"chart.js": "2.7.1",
|
||||
"ng2-charts": "1.6.0",
|
||||
"ng2-auto-complete": "0.12.0"
|
||||
"rxjs": "5.5.9",
|
||||
"zone.js": "0.8.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.7.3",
|
||||
|
|
|
@ -7,6 +7,7 @@ import { AngularSvgIconModule } from 'angular-svg-icon';
|
|||
import { AlertModule } from 'ngx-bootstrap';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import { AdministrationRoutingModule } from './administration-routing.module';
|
||||
import { TypeaheadModule } from 'ngx-bootstrap';
|
||||
|
||||
/**
|
||||
* Components
|
||||
|
@ -39,7 +40,6 @@ import { ClassificationsService } from './services/classifications/classificatio
|
|||
import { ClassificationTypesService } from './services/classification-types/classification-types.service';
|
||||
import { ClassificationCategoriesService } from './services/classification-categories-service/classification-categories.service';
|
||||
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
|
@ -47,7 +47,8 @@ const MODULES = [
|
|||
AngularSvgIconModule,
|
||||
AlertModule,
|
||||
SharedModule,
|
||||
AdministrationRoutingModule
|
||||
AdministrationRoutingModule,
|
||||
TypeaheadModule
|
||||
];
|
||||
|
||||
const DECLARATIONS = [
|
||||
|
|
|
@ -41,13 +41,13 @@
|
|||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="input-group text-align text-width ">
|
||||
<div [ngClass]="{
|
||||
'has-warning': (accessItemsClone[index].accessId !== accessItem.accessId),
|
||||
'has-error': !accessItem.accessId }">
|
||||
<input type="text" required #accessItemName="ngModel" class="form-control" name="accessItem.accessId-{{index}}" [(ngModel)]="accessItem.accessId"
|
||||
placeholder="{{accessItemName.invalid? 'Access id is required': ''}}">
|
||||
</div>
|
||||
<td class="input-group text-align text-width taskana-type-ahead" [ngClass]="{
|
||||
'has-warning': (accessItemsClone[index].accessId !== accessItem.accessId),
|
||||
'has-error': !accessItem.accessId } ">
|
||||
|
||||
<taskana-type-ahead required #accessItemName="ngModel" [(ngModel)]="accessItem.accessId" name="accessItem.accessId-{{index}}"
|
||||
[(ngModel)]="accessItem.accessIdr" placeHolderMessage="Access id is required"></taskana-type-ahead>
|
||||
|
||||
</td>
|
||||
<td [ngClass]="{'has-changes': (accessItemsClone[index].permRead !== accessItem.permRead)}">
|
||||
<input type="checkbox" disabled="disabled" name="accessItem.permRead-{{index}}" [(ngModel)]="accessItem.permRead">
|
||||
|
|
|
@ -5,9 +5,14 @@ td > input[type="checkbox"] {
|
|||
overflow-x: auto;
|
||||
}
|
||||
.text-width{
|
||||
width: 100%;
|
||||
min-width: 180px;
|
||||
}
|
||||
.required-header {
|
||||
width: 200px;
|
||||
}
|
||||
.required-header:after {
|
||||
|
||||
content:" *";
|
||||
color: red;
|
||||
}
|
||||
|
@ -17,3 +22,11 @@ td {
|
|||
border-bottom: 1px solid #f0ad4e;;
|
||||
}
|
||||
}
|
||||
.table > thead > tr > th {
|
||||
max-width: 150px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.has-warning.taskana-type-ahead {
|
||||
border-bottom: 1px solid #f0ad4e;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { SimpleChange } from '@angular/core';
|
||||
import { SimpleChange, Component, forwardRef, Input } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule, JsonpModule } from '@angular/http';
|
||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||
|
@ -26,6 +26,37 @@ import { DomainService } from 'app/services/domain/domain.service';
|
|||
import { DomainServiceMock } from 'app/services/domain/domain.service.mock';
|
||||
import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
template: 'dummydetail',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => TaskanaTypeAheadComponent),
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TaskanaTypeAheadComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
describe('AccessItemsComponent', () => {
|
||||
let component: AccessItemsComponent;
|
||||
let fixture: ComponentFixture<AccessItemsComponent>;
|
||||
|
@ -33,7 +64,7 @@ describe('AccessItemsComponent', () => {
|
|||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SpinnerComponent, AccessItemsComponent, GeneralMessageModalComponent],
|
||||
declarations: [SpinnerComponent, AccessItemsComponent, GeneralMessageModalComponent, TaskanaTypeAheadComponent],
|
||||
imports: [FormsModule, AngularSvgIconModule, HttpClientModule, HttpModule, ReactiveFormsModule],
|
||||
providers: [WorkbasketService, AlertService, ErrorModalService, SavingWorkbasketService, RequestInProgressService,
|
||||
{
|
||||
|
|
|
@ -15,9 +15,10 @@ import { AlertService } from 'app/services/alert/alert.service';
|
|||
import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service';
|
||||
import { TitlesService } from 'app/services/titles/titles.service';
|
||||
import { CustomFieldsService } from '../../../../services/custom-fields/custom-fields.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
|
||||
|
||||
declare var $: any;
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-workbasket-access-items',
|
||||
templateUrl: './access-items.component.html',
|
||||
|
@ -25,7 +26,6 @@ declare var $: any;
|
|||
})
|
||||
export class AccessItemsComponent implements OnChanges, OnDestroy {
|
||||
|
||||
|
||||
@Input()
|
||||
workbasket: Workbasket;
|
||||
@Input()
|
||||
|
@ -65,7 +65,11 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
private errorModalService: ErrorModalService,
|
||||
private savingWorkbaskets: SavingWorkbasketService,
|
||||
private requestInProgressService: RequestInProgressService,
|
||||
private customFieldService: CustomFieldsService) { }
|
||||
private customFieldService: CustomFieldsService) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (!this.initialized && changes.active && changes.active.currentValue === 'accessItems') {
|
||||
|
@ -75,6 +79,7 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
this.setBadge();
|
||||
}
|
||||
}
|
||||
|
||||
private init() {
|
||||
this.initialized = true;
|
||||
if (!this.workbasket._links.accessItems) {
|
||||
|
@ -133,6 +138,7 @@ export class AccessItemsComponent implements OnChanges, OnDestroy {
|
|||
})
|
||||
return false;
|
||||
}
|
||||
|
||||
private setBadge() {
|
||||
if (this.action === ACTION.COPY) {
|
||||
this.badgeMessage = `Copying workbasket: ${this.workbasket.key}`;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<button type="button" (click)="onClear()" class="btn btn-default">Undo</button>
|
||||
<button type="button" (click)="removeDistributionTargets()" data-toggle="tooltip" title="remove distribuition target" class="btn btn-default remove">
|
||||
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
|
||||
</button>
|
||||
</button>
|
||||
<button type="button" (click)="copyWorkbasket()" data-toggle="tooltip" title="copy" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-copy green-blue" aria-hidden="true"></span>
|
||||
</button>
|
||||
|
@ -39,9 +39,8 @@
|
|||
</div>
|
||||
<div class="form-group required">
|
||||
<label for="wb-owner" class="control-label">Owner</label>
|
||||
<input type="text" required #owner="ngModel" class="form-control" id="wb-owner" placeholder="Owner" [(ngModel)]="workbasket.owner"
|
||||
name="workbasket.owner">
|
||||
<div *ngIf="!owner.valid" class="required-text">
|
||||
<taskana-type-ahead required #owner="ngModel" name="owner" [(ngModel)]="workbasket.owner" placeHolderMessage="Owner is required"></taskana-type-ahead>
|
||||
<div *ngIf="!owner?.valid" class="required-text">
|
||||
* Owner is required
|
||||
</div>
|
||||
</div>
|
||||
|
@ -98,15 +97,18 @@
|
|||
</div>
|
||||
<div *ngIf="custom2Field.visible" class="form-group">
|
||||
<label for="wb-custom-2" class="control-label">{{custom2Field.field}}</label>
|
||||
<input type="text" class="form-control" id="wb-custom-2" [placeholder]="custom2Field.field" [(ngModel)]="workbasket.custom2" name="workbasket.custom2">
|
||||
<input type="text" class="form-control" id="wb-custom-2" [placeholder]="custom2Field.field" [(ngModel)]="workbasket.custom2"
|
||||
name="workbasket.custom2">
|
||||
</div>
|
||||
<div *ngIf="custom3Field.visible" class="form-group">
|
||||
<label for="wb-custom-3" class="control-label">{{custom3Field.field}}</label>
|
||||
<input type="text" class="form-control" id="wb-custom-3" [placeholder]="custom3Field.field" [(ngModel)]="workbasket.custom3" name="workbasket.custom3">
|
||||
<input type="text" class="form-control" id="wb-custom-3" [placeholder]="custom3Field.field" [(ngModel)]="workbasket.custom3"
|
||||
name="workbasket.custom3">
|
||||
</div>
|
||||
<div *ngIf="custom4Field.visible" class="form-group">
|
||||
<label for="wb-custom-4" class="control-label">{{custom4Field.field}}</label>
|
||||
<input type="text" class="form-control" id="wb-custom-4" [placeholder]="custom4Field.field" [(ngModel)]="workbasket.custom4" name="workbasket.custom4">
|
||||
<input type="text" class="form-control" id="wb-custom-4" [placeholder]="custom4Field.field" [(ngModel)]="workbasket.custom4"
|
||||
name="workbasket.custom4">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { WorkbasketService } from 'app/administration/services/workbasket/workbasket.service';
|
||||
import { WorkbasketInformationComponent } from './workbasket-information.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, Input, forwardRef } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { AppModule } from 'app/app.module'
|
||||
|
||||
|
@ -37,6 +37,36 @@ import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.se
|
|||
export class DummyDetailComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
template: 'dummydetail',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => TaskanaTypeAheadComponent),
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TaskanaTypeAheadComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: ':id', component: DummyDetailComponent, outlet: 'detail' },
|
||||
{ path: 'someNewId', component: DummyDetailComponent }
|
||||
|
@ -50,7 +80,8 @@ describe('InformationComponent', () => {
|
|||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [WorkbasketInformationComponent, IconTypeComponent, MapValuesPipe,
|
||||
RemoveNoneTypePipe, SpinnerComponent, GeneralMessageModalComponent, DummyDetailComponent],
|
||||
RemoveNoneTypePipe, SpinnerComponent, GeneralMessageModalComponent, DummyDetailComponent,
|
||||
TaskanaTypeAheadComponent],
|
||||
imports: [FormsModule,
|
||||
AngularSvgIconModule,
|
||||
HttpClientModule,
|
||||
|
|
|
@ -42,7 +42,6 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
custom3Field = this.customFieldsService.getCustomField('Custom 3', 'workbaskets.information.custom3');
|
||||
custom4Field = this.customFieldsService.getCustomField('Custom 4', 'workbaskets.information.custom4');
|
||||
|
||||
|
||||
private workbasketSubscription: Subscription;
|
||||
private routeSubscription: Subscription;
|
||||
|
||||
|
@ -132,7 +131,6 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
private beforeRequest() {
|
||||
this.requestInProgressService.setRequestInProgress(true);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { Component, Input, forwardRef } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed, } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Router, Routes } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
|
||||
import { AngularSvgIconModule } from 'angular-svg-icon';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
@ -58,6 +58,37 @@ export class FilterComponent {
|
|||
export class DummyDetailComponent {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
template: 'dummydetail',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: forwardRef(() => TaskanaTypeAheadComponent),
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TaskanaTypeAheadComponent implements ControlValueAccessor {
|
||||
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
writeValue(obj: any): void {
|
||||
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
describe('WorkbasketDetailsComponent', () => {
|
||||
let component: WorkbasketDetailsComponent;
|
||||
let fixture: ComponentFixture<WorkbasketDetailsComponent>;
|
||||
|
@ -77,7 +108,8 @@ describe('WorkbasketDetailsComponent', () => {
|
|||
imports: [RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule, HttpClientModule, HttpModule],
|
||||
declarations: [WorkbasketDetailsComponent, NoAccessComponent, WorkbasketInformationComponent, SpinnerComponent,
|
||||
IconTypeComponent, MapValuesPipe, RemoveNoneTypePipe, AlertComponent, GeneralMessageModalComponent, AccessItemsComponent,
|
||||
DistributionTargetsComponent, FilterComponent, DualListComponent, DummyDetailComponent, SelectWorkBasketPipe],
|
||||
DistributionTargetsComponent, FilterComponent, DualListComponent, DummyDetailComponent,
|
||||
TaskanaTypeAheadComponent, SelectWorkBasketPipe],
|
||||
providers: [WorkbasketService, MasterAndDetailService, ErrorModalService, RequestInProgressService,
|
||||
AlertService, SavingWorkbasketService, {
|
||||
provide: DomainService,
|
||||
|
|
|
@ -50,11 +50,11 @@ import { APP_BASE_HREF } from '@angular/common';
|
|||
|
||||
|
||||
const MODULES = [
|
||||
TabsModule.forRoot(),
|
||||
AlertModule.forRoot(),
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
TabsModule.forRoot(),
|
||||
AppRoutingModule,
|
||||
AlertModule.forRoot(),
|
||||
AngularSvgIconModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
|
@ -66,7 +66,7 @@ const MODULES = [
|
|||
const DECLARATIONS = [
|
||||
AppComponent,
|
||||
NavBarComponent,
|
||||
UserInformationComponent
|
||||
UserInformationComponent,
|
||||
];
|
||||
|
||||
export function startupServiceFactory(startupService: StartupService): () => Promise<any> {
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import { Observable } from 'rxjs/Observable';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CanActivate, Router } from '@angular/router';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DomainService } from 'app/services/domain/domain.service';
|
||||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { ErrorModel } from 'app/models/modal-error';
|
||||
import { TaskanaEngineService } from 'app/services/taskana-engine/taskana-engine.service';
|
||||
|
||||
@Injectable()
|
||||
export class AdminGuard implements CanActivate {
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, public router: Router) { }
|
||||
|
||||
canActivate() {
|
||||
return this.taskanaEngineService.getUserInformation().map(userInfo => {
|
||||
if (userInfo.roles.length === 0) {
|
||||
return this.navigateToWorplace();
|
||||
}
|
||||
const adminRole = userInfo.roles.find(role => {
|
||||
if (role === 'ADMIN') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (adminRole) {
|
||||
return true;
|
||||
}
|
||||
return this.navigateToWorplace();
|
||||
}).catch(() => {
|
||||
return Observable.of(this.navigateToWorplace())
|
||||
});
|
||||
}
|
||||
|
||||
navigateToWorplace(): boolean {
|
||||
this.router.navigate(['workplace']);
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -6,10 +6,12 @@ import { DomainService } from 'app/services/domain/domain.service';
|
|||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { ErrorModel } from 'app/models/modal-error';
|
||||
import { TaskanaEngineService } from 'app/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from 'app/services/window/window.service';
|
||||
|
||||
@Injectable()
|
||||
export class BusinessAdminGuard implements CanActivate {
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, public router: Router) { }
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, public router: Router,
|
||||
private window: WindowRefService) { }
|
||||
|
||||
canActivate() {
|
||||
return this.taskanaEngineService.getUserInformation().map(userInfo => {
|
||||
|
@ -17,7 +19,7 @@ export class BusinessAdminGuard implements CanActivate {
|
|||
return this.navigateToWorplace();
|
||||
}
|
||||
const adminRole = userInfo.roles.find(role => {
|
||||
if (role === 'BUSINESS_ADMIN' || role === 'ADMIN' ) {
|
||||
if (role === 'BUSINESS_ADMIN' || role === 'ADMIN') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -32,7 +34,9 @@ export class BusinessAdminGuard implements CanActivate {
|
|||
}
|
||||
|
||||
navigateToWorplace(): boolean {
|
||||
this.router.navigate(['workplace']);
|
||||
if (this.window.nativeWindow.location.href.indexOf('administration') !== -1) {
|
||||
this.router.navigate(['workplace']);
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import { DomainService } from 'app/services/domain/domain.service';
|
|||
import { ErrorModalService } from 'app/services/errorModal/error-modal.service';
|
||||
import { ErrorModel } from 'app/models/modal-error';
|
||||
import { TaskanaEngineService } from 'app/services/taskana-engine/taskana-engine.service';
|
||||
import { WindowRefService } from 'app/services/window/window.service';
|
||||
|
||||
@Injectable()
|
||||
export class MonitorGuard implements CanActivate {
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, public router: Router) { }
|
||||
constructor(private taskanaEngineService: TaskanaEngineService, public router: Router,
|
||||
private window: WindowRefService) { }
|
||||
|
||||
canActivate() {
|
||||
return this.taskanaEngineService.getUserInformation().map(userInfo => {
|
||||
|
@ -17,7 +19,7 @@ export class MonitorGuard implements CanActivate {
|
|||
return this.navigateToWorplace();
|
||||
}
|
||||
const adminRole = userInfo.roles.find(role => {
|
||||
if (role === 'MONITOR' || role === 'ADMIN' ) {
|
||||
if (role === 'MONITOR' || role === 'ADMIN') {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -32,7 +34,9 @@ export class MonitorGuard implements CanActivate {
|
|||
}
|
||||
|
||||
navigateToWorplace(): boolean {
|
||||
this.router.navigate(['workplace']);
|
||||
if (this.window.nativeWindow.location.href.indexOf('monitor') !== -1) {
|
||||
this.router.navigate(['workplace']);
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { LinksClassification } from 'app/models/links-classfication';
|
||||
|
||||
export class AccessIdDefinition {
|
||||
constructor(
|
||||
public accessId: string = undefined,
|
||||
public name: string = undefined) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { AccessIdsService } from './access-ids.service';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
describe('ValidateAccessItemsService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientModule, HttpModule],
|
||||
providers: [AccessIdsService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([AccessIdsService], (service: AccessIdsService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from 'environments/environment';
|
||||
|
||||
|
||||
import { AccessIdDefinition } from 'app/models/access-id';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@Injectable()
|
||||
export class AccessIdsService {
|
||||
|
||||
private url = environment.taskanaRestUrl + '/v1/validate-access-id';
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient) { }
|
||||
|
||||
getAccessItemsInformation(token): Observable<Array<AccessIdDefinition>> {
|
||||
if (!token) {
|
||||
return Observable.of([]);
|
||||
}
|
||||
return this.httpClient.get<Array<AccessIdDefinition>>(`${this.url}?search=${token}`);
|
||||
};
|
||||
|
||||
}
|
|
@ -6,12 +6,14 @@ import { AngularSvgIconModule } from 'angular-svg-icon';
|
|||
import { AlertModule } from 'ngx-bootstrap';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TreeModule } from 'angular-tree-component';
|
||||
import { TypeaheadModule } from 'ngx-bootstrap';
|
||||
|
||||
import { GeneralMessageModalComponent } from 'app/shared/general-message-modal/general-message-modal.component';
|
||||
import { SpinnerComponent } from 'app/shared/spinner/spinner.component';
|
||||
import { AlertComponent } from 'app/shared/alert/alert.component';
|
||||
import { MasterAndDetailComponent } from 'app/shared/master-and-detail/master-and-detail.component';
|
||||
import { TaskanaTreeComponent } from 'app/shared/tree/tree.component';
|
||||
import { TypeAheadComponent } from 'app/shared/type-ahead/type-ahead.component';
|
||||
|
||||
/**
|
||||
* Pipes
|
||||
|
@ -22,17 +24,24 @@ import { SelectWorkBasketPipe } from './pipes/selectedWorkbasket/seleted-workbas
|
|||
import { SpreadNumberPipe } from './pipes/spreadNumber/spread-number';
|
||||
import { OrderBy } from './pipes/orderBy/orderBy';
|
||||
import { MapToIterable } from './pipes/mapToIterable/mapToIterable';
|
||||
|
||||
/**
|
||||
* Services
|
||||
*/
|
||||
import { HttpClientInterceptor } from './services/httpClientInterceptor/http-client-interceptor.service';
|
||||
import { AccessIdsService } from './services/access-ids/access-ids.service';
|
||||
|
||||
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
AlertModule.forRoot(),
|
||||
TypeaheadModule.forRoot(),
|
||||
AngularSvgIconModule,
|
||||
HttpClientModule,
|
||||
RouterModule,
|
||||
TreeModule
|
||||
TreeModule,
|
||||
];
|
||||
|
||||
const DECLARATIONS = [
|
||||
|
@ -41,6 +50,7 @@ const DECLARATIONS = [
|
|||
AlertComponent,
|
||||
MasterAndDetailComponent,
|
||||
TaskanaTreeComponent,
|
||||
TypeAheadComponent,
|
||||
MapValuesPipe,
|
||||
RemoveNoneTypePipe,
|
||||
SelectWorkBasketPipe,
|
||||
|
@ -58,7 +68,8 @@ const DECLARATIONS = [
|
|||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: HttpClientInterceptor,
|
||||
multi: true
|
||||
}
|
||||
},
|
||||
AccessIdsService
|
||||
]
|
||||
})
|
||||
export class SharedModule {
|
||||
|
|
|
@ -133,4 +133,11 @@
|
|||
|
||||
.no-display{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.type-ahead-spinner {
|
||||
position: relative;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<div *ngIf="dataSource" class="custom-form-control">
|
||||
<ng-template class="wrapper-text" #customItemTemplate let-model="item" let-index="indexTemplate" let-query="query">
|
||||
<div (mousedown)="typeaheadOnSelect({'item':model})">
|
||||
<div>
|
||||
<span [innerHTML]="join(model.accessId, query)">
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span [innerHTML]="join(model.name, query)">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<div [ngClass]="{'hidden': !dataSource.selected || typing}" class="wrapper-text" (click)="setTyping(true)">
|
||||
<span>
|
||||
<label>
|
||||
{{dataSource.selected?.accessId}}
|
||||
</label>
|
||||
</span>
|
||||
<div>{{dataSource.selected?.name}}</div>
|
||||
</div>
|
||||
<div [ngClass]="{'hidden': dataSource.selected && !typing}">
|
||||
<span class="field-label-wrapper">
|
||||
<label>
|
||||
{{dataSource.selected?.name}}
|
||||
</label>
|
||||
</span>
|
||||
<div *ngIf="typeaheadLoading" class="loading">
|
||||
<taskana-spinner [isRunning]="typeaheadLoading" positionClass="type-ahead-spinner"></taskana-spinner>
|
||||
</div>
|
||||
<input #inputTypeAhead class=" form-control input-text" (blur)="typeaheadOnSelect({'item':dataSource.selected})" name="accessItem-{{index}}"
|
||||
required #accessItemName="ngModel" [(ngModel)]="value" [typeahead]="dataSource" typeaheadOptionField="name" [typeaheadItemTemplate]="customItemTemplate"
|
||||
(typeaheadOnSelect)="typeaheadOnSelect($event, index)" [typeaheadScrollable]="true" [typeaheadOptionsInScrollableView]="typeaheadOptionsInScrollableView"
|
||||
[typeaheadMinLength]="typeaheadMinLength" [typeaheadWaitMs]="typeaheadWaitMs" (typeaheadLoading)="changeTypeaheadLoading($event)"
|
||||
placeholder="{{accessItemName.invalid? placeHolderMessage: ''}}">
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,47 @@
|
|||
$blue: #2e9eca;
|
||||
$grey: #ddd;
|
||||
.wrapper-text {
|
||||
height: 47px;
|
||||
& label {
|
||||
width: 100%;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
& div {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid $grey;
|
||||
margin-top:6px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-text {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
border-radius: 0px;
|
||||
//margin-top: 18px;
|
||||
height: 22px;
|
||||
border-bottom: 1px solid $grey;
|
||||
&:focus{
|
||||
border-bottom: 1px solid $blue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.field-label-wrapper{
|
||||
position: relative;
|
||||
//left: 8px;
|
||||
box-sizing: content-box;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.form-control:focus {
|
||||
border-color: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.loading {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
import { Component, OnInit, Input, EventEmitter, Output, ViewChild, ElementRef, forwardRef } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
|
||||
|
||||
import { AccessIdsService } from 'app/shared/services/access-ids/access-ids.service';
|
||||
import { AccessItemsComponent } from 'app/administration/workbasket/details/access-items/access-items.component';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
const noop = () => {
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-type-ahead',
|
||||
templateUrl: './type-ahead.component.html',
|
||||
styleUrls: ['./type-ahead.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => TypeAheadComponent),
|
||||
multi: true
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
})
|
||||
export class TypeAheadComponent implements OnInit, ControlValueAccessor {
|
||||
|
||||
dataSource: any;
|
||||
typing = false;
|
||||
|
||||
@Input()
|
||||
placeHolderMessage;
|
||||
|
||||
@ViewChild('inputTypeAhead')
|
||||
private inputTypeAhead;
|
||||
|
||||
typeaheadLoading = false;
|
||||
typeaheadMinLength = 2;
|
||||
typeaheadWaitMs = 500;
|
||||
typeaheadOptionsInScrollableView = 6;
|
||||
|
||||
// The internal data model
|
||||
private innerValue: any = '';
|
||||
|
||||
// Placeholders for the callbacks which are later provided
|
||||
// by the Control Value Accessor
|
||||
private onTouchedCallback: () => void = noop;
|
||||
private onChangeCallback: (_: any) => void = noop;
|
||||
|
||||
// get accessor
|
||||
get value(): any {
|
||||
return this.innerValue;
|
||||
};
|
||||
|
||||
// set accessor including call the onchange callback
|
||||
set value(v: any) {
|
||||
if (v !== this.innerValue) {
|
||||
this.innerValue = v;
|
||||
this.onChangeCallback(v);
|
||||
}
|
||||
}
|
||||
|
||||
// From ControlValueAccessor interface
|
||||
writeValue(value: any) {
|
||||
if (value !== this.innerValue) {
|
||||
this.innerValue = value;
|
||||
this.initializeDataSource();
|
||||
}
|
||||
}
|
||||
|
||||
// From ControlValueAccessor interface
|
||||
registerOnChange(fn: any) {
|
||||
this.onChangeCallback = fn;
|
||||
}
|
||||
|
||||
// From ControlValueAccessor interface
|
||||
registerOnTouched(fn: any) {
|
||||
this.onTouchedCallback = fn;
|
||||
}
|
||||
|
||||
constructor(private accessIdsService: AccessIdsService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
|
||||
initializeDataSource() {
|
||||
this.dataSource = Observable.create((observer: any) => {
|
||||
observer.next(this.value);
|
||||
}).mergeMap((token: string) => this.getUsersAsObservable(token));
|
||||
this.accessIdsService.getAccessItemsInformation(this.value).subscribe(items => {
|
||||
if (items.length > 0) {
|
||||
this.dataSource.selected = items.find(item => item.accessId === this.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
getUsersAsObservable(token: string): Observable<any> {
|
||||
return this.accessIdsService.getAccessItemsInformation(token);
|
||||
}
|
||||
|
||||
typeaheadOnSelect(event: TypeaheadMatch): void {
|
||||
if (event && event.item) {
|
||||
this.value = event.item.accessId;
|
||||
this.dataSource.selected = event.item;
|
||||
}
|
||||
this.setTyping(false);
|
||||
}
|
||||
|
||||
setTyping(value) {
|
||||
if (value) {
|
||||
setTimeout(() => {
|
||||
this.inputTypeAhead.nativeElement.focus();
|
||||
}, 1)
|
||||
|
||||
}
|
||||
this.typing = value;
|
||||
}
|
||||
|
||||
changeTypeaheadLoading(e: boolean): void {
|
||||
this.typeaheadLoading = e;
|
||||
}
|
||||
|
||||
join(text: string, str: string) {
|
||||
return text.toLocaleLowerCase().split(str).join(`<strong>${str}</strong>`);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,4 +3,5 @@
|
|||
@import '../../node_modules/angular-tree-component/dist/angular-tree-component.css';
|
||||
@import 'site';
|
||||
@import 'forms';
|
||||
@import 'tree';
|
||||
@import 'tree';
|
||||
@import 'type-ahead';
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
typeahead-container >ul.dropdown-menu{
|
||||
width: 215px;
|
||||
& >li.active {
|
||||
&>a {
|
||||
background-color: $green;
|
||||
}
|
||||
&>a:hover {
|
||||
background-color: $green;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
Loading…
Reference in New Issue