Commit c60e99fad90f572b668e3fae5a153a736ba67d1c

Authored by Igor Kulikov
1 parent e160b6e0

UI: Entity table models improvements

Showing 30 changed files with 299 additions and 133 deletions
@@ -72,7 +72,7 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink> @@ -72,7 +72,7 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
72 this.entityType = EntityType.ALARM; 72 this.entityType = EntityType.ALARM;
73 this.entityTranslations = entityTypeTranslations.get(EntityType.ALARM); 73 this.entityTranslations = entityTypeTranslations.get(EntityType.ALARM);
74 this.entityResources = { 74 this.entityResources = {
75 - } as EntityTypeResource; 75 + } as EntityTypeResource<AlarmInfo>;
76 this.searchStatus = defaultSearchStatus; 76 this.searchStatus = defaultSearchStatus;
77 77
78 this.headerComponent = AlarmTableHeaderComponent; 78 this.headerComponent = AlarmTableHeaderComponent;
@@ -68,7 +68,7 @@ export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLin @@ -68,7 +68,7 @@ export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLin
68 search: 'audit-log.search' 68 search: 'audit-log.search'
69 }; 69 };
70 this.entityResources = { 70 this.entityResources = {
71 - } as EntityTypeResource; 71 + } as EntityTypeResource<AuditLog>;
72 72
73 this.entitiesFetchFunction = pageLink => this.fetchAuditLogs(pageLink); 73 this.entitiesFetchFunction = pageLink => this.fetchAuditLogs(pageLink);
74 74
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 <mat-toolbar color="primary"> 19 <mat-toolbar color="primary">
20 <h2 translate>{{ translations.add }}</h2> 20 <h2 translate>{{ translations.add }}</h2>
21 <span fxFlex></span> 21 <span fxFlex></span>
22 - <div [tb-help]="resources.helpLinkId"></div> 22 + <div [tb-help]="helpLinkId()"></div>
23 <button mat-icon-button 23 <button mat-icon-button
24 (click)="cancel()" 24 (click)="cancel()"
25 type="button"> 25 type="button">
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, ComponentFactoryResolver, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core'; 17 +import { Component, ComponentFactoryResolver, Inject, Injector, OnInit, SkipSelf, ViewChild } from '@angular/core';
18 import { ErrorStateMatcher } from '@angular/material/core'; 18 import { ErrorStateMatcher } from '@angular/material/core';
19 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; 19 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
20 import { Store } from '@ngrx/store'; 20 import { Store } from '@ngrx/store';
@@ -29,6 +29,7 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod @@ -29,6 +29,7 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod
29 import { AddEntityDialogData } from '@home/models/entity/entity-component.models'; 29 import { AddEntityDialogData } from '@home/models/entity/entity-component.models';
30 import { DialogComponent } from '@shared/components/dialog.component'; 30 import { DialogComponent } from '@shared/components/dialog.component';
31 import { Router } from '@angular/router'; 31 import { Router } from '@angular/router';
  32 +import { Observable } from 'rxjs';
32 33
33 @Component({ 34 @Component({
34 selector: 'tb-add-entity-dialog', 35 selector: 'tb-add-entity-dialog',
@@ -44,7 +45,7 @@ export class AddEntityDialogComponent extends @@ -44,7 +45,7 @@ export class AddEntityDialogComponent extends
44 45
45 entitiesTableConfig: EntityTableConfig<BaseData<HasId>>; 46 entitiesTableConfig: EntityTableConfig<BaseData<HasId>>;
46 translations: EntityTypeTranslation; 47 translations: EntityTypeTranslation;
47 - resources: EntityTypeResource; 48 + resources: EntityTypeResource<BaseData<HasId>>;
48 entity: BaseData<EntityId>; 49 entity: BaseData<EntityId>;
49 50
50 submitted = false; 51 submitted = false;
@@ -56,6 +57,7 @@ export class AddEntityDialogComponent extends @@ -56,6 +57,7 @@ export class AddEntityDialogComponent extends
56 @Inject(MAT_DIALOG_DATA) public data: AddEntityDialogData<BaseData<HasId>>, 57 @Inject(MAT_DIALOG_DATA) public data: AddEntityDialogData<BaseData<HasId>>,
57 public dialogRef: MatDialogRef<AddEntityDialogComponent, BaseData<HasId>>, 58 public dialogRef: MatDialogRef<AddEntityDialogComponent, BaseData<HasId>>,
58 private componentFactoryResolver: ComponentFactoryResolver, 59 private componentFactoryResolver: ComponentFactoryResolver,
  60 + private injector: Injector,
59 @SkipSelf() private errorStateMatcher: ErrorStateMatcher) { 61 @SkipSelf() private errorStateMatcher: ErrorStateMatcher) {
60 super(store, router, dialogRef); 62 super(store, router, dialogRef);
61 } 63 }
@@ -68,14 +70,35 @@ export class AddEntityDialogComponent extends @@ -68,14 +70,35 @@ export class AddEntityDialogComponent extends
68 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent); 70 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent);
69 const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef; 71 const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef;
70 viewContainerRef.clear(); 72 viewContainerRef.clear();
71 - const componentRef = viewContainerRef.createComponent(componentFactory); 73 + const injector: Injector = Injector.create(
  74 + {
  75 + providers: [
  76 + {
  77 + provide: 'entity',
  78 + useValue: this.entity
  79 + },
  80 + {
  81 + provide: 'entitiesTableConfig',
  82 + useValue: this.entitiesTableConfig
  83 + }
  84 + ],
  85 + parent: this.injector
  86 + }
  87 + );
  88 + const componentRef = viewContainerRef.createComponent(componentFactory, 0, injector);
72 this.entityComponent = componentRef.instance; 89 this.entityComponent = componentRef.instance;
73 this.entityComponent.isEdit = true; 90 this.entityComponent.isEdit = true;
74 - this.entityComponent.entitiesTableConfig = this.entitiesTableConfig;  
75 - this.entityComponent.entity = this.entity;  
76 this.detailsForm = this.entityComponent.entityNgForm; 91 this.detailsForm = this.entityComponent.entityNgForm;
77 } 92 }
78 93
  94 + helpLinkId(): string {
  95 + if (this.resources.helpLinkIdForEntity && this.entityComponent.entityForm) {
  96 + return this.resources.helpLinkIdForEntity(this.entityComponent.entityForm.getRawValue());
  97 + } else {
  98 + return this.resources.helpLinkId;
  99 + }
  100 + }
  101 +
79 isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { 102 isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
80 const originalErrorState = this.errorStateMatcher.isErrorState(control, form); 103 const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
81 const customErrorState = !!(control && control.invalid && this.submitted); 104 const customErrorState = !!(control && control.invalid && this.submitted);
@@ -22,12 +22,15 @@ import { AfterViewInit } from '@angular/core'; @@ -22,12 +22,15 @@ import { AfterViewInit } from '@angular/core';
22 import { POSTAL_CODE_PATTERNS } from '@home/models/contact.models'; 22 import { POSTAL_CODE_PATTERNS } from '@home/models/contact.models';
23 import { HasId } from '@shared/models/base-data'; 23 import { HasId } from '@shared/models/base-data';
24 import {EntityComponent} from './entity.component'; 24 import {EntityComponent} from './entity.component';
  25 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
25 26
26 export abstract class ContactBasedComponent<T extends ContactBased<HasId>> extends EntityComponent<T> implements AfterViewInit { 27 export abstract class ContactBasedComponent<T extends ContactBased<HasId>> extends EntityComponent<T> implements AfterViewInit {
27 28
28 protected constructor(protected store: Store<AppState>, 29 protected constructor(protected store: Store<AppState>,
29 - protected fb: FormBuilder) {  
30 - super(store); 30 + protected fb: FormBuilder,
  31 + protected entityValue: T,
  32 + protected entitiesTableConfig: EntityTableConfig<T>) {
  33 + super(store, fb, entityValue, entitiesTableConfig);
31 } 34 }
32 35
33 buildForm(entity: T): FormGroup { 36 buildForm(entity: T): FormGroup {
@@ -135,7 +135,7 @@ @@ -135,7 +135,7 @@
135 </mat-toolbar> 135 </mat-toolbar>
136 <div fxFlex class="table-container"> 136 <div fxFlex class="table-container">
137 <table mat-table [dataSource]="dataSource" [trackBy]="trackByEntityId" 137 <table mat-table [dataSource]="dataSource" [trackBy]="trackByEntityId"
138 - matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> 138 + matSort [matSortActive]="pageLink.sortOrder?.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
139 <ng-container matColumnDef="select" sticky> 139 <ng-container matColumnDef="select" sticky>
140 <mat-header-cell *matHeaderCellDef style="width: 30px;"> 140 <mat-header-cell *matHeaderCellDef style="width: 30px;">
141 <mat-checkbox (change)="$event ? dataSource.masterToggle() : null" 141 <mat-checkbox (change)="$event ? dataSource.masterToggle() : null"
@@ -153,8 +153,11 @@ @@ -153,8 +153,11 @@
153 </ng-container> 153 </ng-container>
154 <ng-container [matColumnDef]="column.key" *ngFor="let column of entityColumns; trackBy: trackByColumnKey;"> 154 <ng-container [matColumnDef]="column.key" *ngFor="let column of entityColumns; trackBy: trackByColumnKey;">
155 <mat-header-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}" 155 <mat-header-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}"
156 - *matHeaderCellDef [ngStyle]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable"> {{ column.title | translate }} </mat-header-cell> 156 + [fxHide.lt-lg]="column.mobileHide"
  157 + *matHeaderCellDef [ngStyle]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable">
  158 + {{ column.ignoreTranslate ? column.title : (column.title | translate) }} </mat-header-cell>
157 <mat-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}" 159 <mat-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}"
  160 + [fxHide.lt-lg]="column.mobileHide"
158 *matCellDef="let entity; let row = index" 161 *matCellDef="let entity; let row = index"
159 [matTooltip]="cellTooltip(entity, column, row)" 162 [matTooltip]="cellTooltip(entity, column, row)"
160 matTooltipPosition="above" 163 matTooltipPosition="above"
@@ -186,8 +189,7 @@ @@ -186,8 +189,7 @@
186 maxWidth: (cellActionDescriptors.length * 40) + 'px', 189 maxWidth: (cellActionDescriptors.length * 40) + 'px',
187 width: (cellActionDescriptors.length * 40) + 'px' }"> 190 width: (cellActionDescriptors.length * 40) + 'px' }">
188 <div fxHide fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end"> 191 <div fxHide fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end">
189 - <button mat-icon-button [disabled]="isLoading$ | async"  
190 - [style.visibility]="actionDescriptor.isEnabled(entity) ? 'visible' : 'hidden'" 192 + <button mat-icon-button [disabled]="(isLoading$ | async) || !actionDescriptor.isEnabled(entity)"
191 *ngFor="let actionDescriptor of cellActionDescriptors" 193 *ngFor="let actionDescriptor of cellActionDescriptors"
192 matTooltip="{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}" 194 matTooltip="{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}"
193 matTooltipPosition="above" 195 matTooltipPosition="above"
@@ -20,8 +20,8 @@ import { @@ -20,8 +20,8 @@ import {
20 Component, 20 Component,
21 ComponentFactoryResolver, 21 ComponentFactoryResolver,
22 ElementRef, 22 ElementRef,
23 - Input,  
24 - OnInit, 23 + Input, OnChanges,
  24 + OnInit, SimpleChanges,
25 ViewChild 25 ViewChild
26 } from '@angular/core'; 26 } from '@angular/core';
27 import { PageComponent } from '@shared/components/page.component'; 27 import { PageComponent } from '@shared/components/page.component';
@@ -51,7 +51,7 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models'; @@ -51,7 +51,7 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models';
51 import { DialogService } from '@core/services/dialog.service'; 51 import { DialogService } from '@core/services/dialog.service';
52 import { AddEntityDialogComponent } from './add-entity-dialog.component'; 52 import { AddEntityDialogComponent } from './add-entity-dialog.component';
53 import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; 53 import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models';
54 -import { historyInterval, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; 54 +import { DAY, historyInterval, HistoryWindowType, Timewindow } from '@shared/models/time/time.models';
55 import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; 55 import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
56 import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; 56 import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
57 import { isDefined, isUndefined } from '@core/utils'; 57 import { isDefined, isUndefined } from '@core/utils';
@@ -62,7 +62,7 @@ import { isDefined, isUndefined } from '@core/utils'; @@ -62,7 +62,7 @@ import { isDefined, isUndefined } from '@core/utils';
62 styleUrls: ['./entities-table.component.scss'], 62 styleUrls: ['./entities-table.component.scss'],
63 changeDetection: ChangeDetectionStrategy.OnPush 63 changeDetection: ChangeDetectionStrategy.OnPush
64 }) 64 })
65 -export class EntitiesTableComponent extends PageComponent implements AfterViewInit, OnInit { 65 +export class EntitiesTableComponent extends PageComponent implements AfterViewInit, OnInit, OnChanges {
66 66
67 @Input() 67 @Input()
68 entitiesTableConfig: EntityTableConfig<BaseData<HasId>>; 68 entitiesTableConfig: EntityTableConfig<BaseData<HasId>>;
@@ -112,7 +112,27 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn @@ -112,7 +112,27 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
112 } 112 }
113 113
114 ngOnInit() { 114 ngOnInit() {
115 - this.entitiesTableConfig = this.entitiesTableConfig || this.route.snapshot.data.entitiesTableConfig; 115 + if (this.entitiesTableConfig) {
  116 + this.init(this.entitiesTableConfig);
  117 + } else {
  118 + this.init(this.route.snapshot.data.entitiesTableConfig);
  119 + }
  120 + }
  121 +
  122 + ngOnChanges(changes: SimpleChanges): void {
  123 + for (const propName of Object.keys(changes)) {
  124 + const change = changes[propName];
  125 + if (!change.firstChange && change.currentValue !== change.previousValue) {
  126 + if (propName === 'entitiesTableConfig' && change.currentValue) {
  127 + this.init(change.currentValue);
  128 + }
  129 + }
  130 + }
  131 + }
  132 +
  133 + private init(entitiesTableConfig: EntityTableConfig<BaseData<HasId>>) {
  134 + this.isDetailsOpen = false;
  135 + this.entitiesTableConfig = entitiesTableConfig;
116 if (this.entitiesTableConfig.headerComponent) { 136 if (this.entitiesTableConfig.headerComponent) {
117 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.headerComponent); 137 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.headerComponent);
118 const viewContainerRef = this.entityTableHeaderAnchor.viewContainerRef; 138 const viewContainerRef = this.entityTableHeaderAnchor.viewContainerRef;
@@ -138,17 +158,16 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn @@ -138,17 +158,16 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
138 onAction: ($event, entity) => this.deleteEntity($event, entity) 158 onAction: ($event, entity) => this.deleteEntity($event, entity)
139 } 159 }
140 ); 160 );
  161 + this.groupActionDescriptors.push(
  162 + {
  163 + name: this.translate.instant('action.delete'),
  164 + icon: 'delete',
  165 + isEnabled: true,
  166 + onAction: ($event, entities) => this.deleteEntities($event, entities)
  167 + }
  168 + );
141 } 169 }
142 170
143 - this.groupActionDescriptors.push(  
144 - {  
145 - name: this.translate.instant('action.delete'),  
146 - icon: 'delete',  
147 - isEnabled: this.entitiesTableConfig.entitiesDeleteEnabled,  
148 - onAction: ($event, entities) => this.deleteEntities($event, entities)  
149 - }  
150 - );  
151 -  
152 const enabledGroupActionDescriptors = 171 const enabledGroupActionDescriptors =
153 this.groupActionDescriptors.filter((descriptor) => descriptor.isEnabled); 172 this.groupActionDescriptors.filter((descriptor) => descriptor.isEnabled);
154 173
@@ -156,24 +175,25 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn @@ -156,24 +175,25 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
156 175
157 this.columnsUpdated(); 176 this.columnsUpdated();
158 177
159 - const sortOrder: SortOrder = { property: this.entitiesTableConfig.defaultSortOrder.property,  
160 - direction: this.entitiesTableConfig.defaultSortOrder.direction }; 178 + let sortOrder: SortOrder = null;
  179 + if (this.entitiesTableConfig.defaultSortOrder) {
  180 + sortOrder = {
  181 + property: this.entitiesTableConfig.defaultSortOrder.property,
  182 + direction: this.entitiesTableConfig.defaultSortOrder.direction
  183 + };
  184 + }
161 185
162 if (this.entitiesTableConfig.useTimePageLink) { 186 if (this.entitiesTableConfig.useTimePageLink) {
163 - this.timewindow = historyInterval(24 * 60 * 60 * 1000); 187 + this.timewindow = historyInterval(DAY);
164 const currentTime = Date.now(); 188 const currentTime = Date.now();
165 this.pageLink = new TimePageLink(10, 0, null, sortOrder, 189 this.pageLink = new TimePageLink(10, 0, null, sortOrder,
166 currentTime - this.timewindow.history.timewindowMs, currentTime); 190 currentTime - this.timewindow.history.timewindowMs, currentTime);
167 } else { 191 } else {
168 this.pageLink = new PageLink(10, 0, null, sortOrder); 192 this.pageLink = new PageLink(10, 0, null, sortOrder);
169 } 193 }
170 - this.dataSource = new EntitiesDataSource<BaseData<HasId>>(  
171 - this.entitiesTableConfig.entitiesFetchFunction,  
172 - this.entitiesTableConfig.entitySelectionEnabled,  
173 - () => {  
174 - this.dataLoaded();  
175 - }  
176 - ); 194 + this.dataSource = this.entitiesTableConfig.dataSource(() => {
  195 + this.dataLoaded();
  196 + });
177 if (this.entitiesTableConfig.onLoadAction) { 197 if (this.entitiesTableConfig.onLoadAction) {
178 this.entitiesTableConfig.onLoadAction(this.route); 198 this.entitiesTableConfig.onLoadAction(this.route);
179 } 199 }
@@ -214,8 +234,14 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn @@ -214,8 +234,14 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
214 } 234 }
215 this.pageLink.page = this.paginator.pageIndex; 235 this.pageLink.page = this.paginator.pageIndex;
216 this.pageLink.pageSize = this.paginator.pageSize; 236 this.pageLink.pageSize = this.paginator.pageSize;
217 - this.pageLink.sortOrder.property = this.sort.active;  
218 - this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; 237 + if (this.sort.active) {
  238 + this.pageLink.sortOrder = {
  239 + property: this.sort.active,
  240 + direction: Direction[this.sort.direction.toUpperCase()]
  241 + };
  242 + } else {
  243 + this.pageLink.sortOrder = null;
  244 + }
219 if (this.entitiesTableConfig.useTimePageLink) { 245 if (this.entitiesTableConfig.useTimePageLink) {
220 const timePageLink = this.pageLink as TimePageLink; 246 const timePageLink = this.pageLink as TimePageLink;
221 if (this.timewindow.history.historyType === HistoryWindowType.LAST_INTERVAL) { 247 if (this.timewindow.history.historyType === HistoryWindowType.LAST_INTERVAL) {
@@ -25,9 +25,9 @@ @@ -25,9 +25,9 @@
25 (applyDetails)="saveEntity()" 25 (applyDetails)="saveEntity()"
26 [theForm]="detailsForm"> 26 [theForm]="detailsForm">
27 <div class="details-buttons"> 27 <div class="details-buttons">
28 - <div [tb-help]="resources.helpLinkId"></div> 28 + <div [tb-help]="helpLinkId()"></div>
29 </div> 29 </div>
30 - <mat-tab-group class="tb-absolute-fill" [ngClass]="{'tb-headless': isEditValue}" fxFlex [(selectedIndex)]="selectedTab"> 30 + <mat-tab-group class="tb-absolute-fill" [ngClass]="{'tb-headless': hideDetailsTabs()}" fxFlex [(selectedIndex)]="selectedTab">
31 <mat-tab label="{{ 'details.details' | translate }}"> 31 <mat-tab label="{{ 'details.details' | translate }}">
32 <tb-anchor #entityDetailsForm></tb-anchor> 32 <tb-anchor #entityDetailsForm></tb-anchor>
33 </mat-tab> 33 </mat-tab>
@@ -16,10 +16,10 @@ @@ -16,10 +16,10 @@
16 16
17 import { 17 import {
18 AfterViewInit, 18 AfterViewInit,
19 - ChangeDetectionStrategy, 19 + ChangeDetectionStrategy, ChangeDetectorRef,
20 Component, 20 Component,
21 ComponentFactoryResolver, 21 ComponentFactoryResolver,
22 - EventEmitter, 22 + EventEmitter, Injector,
23 Input, 23 Input,
24 OnDestroy, 24 OnDestroy,
25 OnInit, 25 OnInit,
@@ -41,6 +41,7 @@ import { EntityAction } from '@home/models/entity/entity-component.models'; @@ -41,6 +41,7 @@ import { EntityAction } from '@home/models/entity/entity-component.models';
41 import { Subscription } from 'rxjs'; 41 import { Subscription } from 'rxjs';
42 import { MatTab, MatTabGroup } from '@angular/material/tabs'; 42 import { MatTab, MatTabGroup } from '@angular/material/tabs';
43 import { EntityTabsComponent } from '@home/components/entity/entity-tabs.component'; 43 import { EntityTabsComponent } from '@home/components/entity/entity-tabs.component';
  44 +import { deepClone } from '@core/utils';
44 45
45 @Component({ 46 @Component({
46 selector: 'tb-entity-details-panel', 47 selector: 'tb-entity-details-panel',
@@ -79,13 +80,16 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit @@ -79,13 +80,16 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
79 @ViewChildren(MatTab) inclusiveTabs: QueryList<MatTab>; 80 @ViewChildren(MatTab) inclusiveTabs: QueryList<MatTab>;
80 81
81 translations: EntityTypeTranslation; 82 translations: EntityTypeTranslation;
82 - resources: EntityTypeResource; 83 + resources: EntityTypeResource<BaseData<HasId>>;
83 entity: BaseData<HasId>; 84 entity: BaseData<HasId>;
  85 + editingEntity: BaseData<HasId>;
84 86
85 private currentEntityId: HasId; 87 private currentEntityId: HasId;
86 private entityActionSubscription: Subscription; 88 private entityActionSubscription: Subscription;
87 89
88 constructor(protected store: Store<AppState>, 90 constructor(protected store: Store<AppState>,
  91 + private injector: Injector,
  92 + private cd: ChangeDetectorRef,
89 private componentFactoryResolver: ComponentFactoryResolver) { 93 private componentFactoryResolver: ComponentFactoryResolver) {
90 super(store); 94 super(store);
91 } 95 }
@@ -127,15 +131,32 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit @@ -127,15 +131,32 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
127 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent); 131 const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent);
128 const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef; 132 const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef;
129 viewContainerRef.clear(); 133 viewContainerRef.clear();
130 - const componentRef = viewContainerRef.createComponent(componentFactory); 134 + const injector: Injector = Injector.create(
  135 + {
  136 + providers: [
  137 + {
  138 + provide: 'entity',
  139 + useValue: this.entity
  140 + },
  141 + {
  142 + provide: 'entitiesTableConfig',
  143 + useValue: this.entitiesTableConfig
  144 + }
  145 + ],
  146 + parent: this.injector
  147 + }
  148 + );
  149 + const componentRef = viewContainerRef.createComponent(componentFactory, 0, injector);
131 this.entityComponent = componentRef.instance; 150 this.entityComponent = componentRef.instance;
132 this.entityComponent.isEdit = this.isEdit; 151 this.entityComponent.isEdit = this.isEdit;
133 - this.entityComponent.entitiesTableConfig = this.entitiesTableConfig;  
134 this.detailsForm = this.entityComponent.entityNgForm; 152 this.detailsForm = this.entityComponent.entityNgForm;
135 this.entityActionSubscription = this.entityComponent.entityAction.subscribe((action) => { 153 this.entityActionSubscription = this.entityComponent.entityAction.subscribe((action) => {
136 this.entityAction.emit(action); 154 this.entityAction.emit(action);
137 }); 155 });
138 this.buildEntityTabsComponent(); 156 this.buildEntityTabsComponent();
  157 + this.entityComponent.entityForm.valueChanges.subscribe(() => {
  158 + this.cd.detectChanges();
  159 + });
139 } 160 }
140 161
141 buildEntityTabsComponent() { 162 buildEntityTabsComponent() {
@@ -147,9 +168,14 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit @@ -147,9 +168,14 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
147 this.entityTabsComponent = componentTabsRef.instance; 168 this.entityTabsComponent = componentTabsRef.instance;
148 this.entityTabsComponent.isEdit = this.isEdit; 169 this.entityTabsComponent.isEdit = this.isEdit;
149 this.entityTabsComponent.entitiesTableConfig = this.entitiesTableConfig; 170 this.entityTabsComponent.entitiesTableConfig = this.entitiesTableConfig;
  171 + this.entityTabsComponent.detailsForm = this.detailsForm;
150 } 172 }
151 } 173 }
152 174
  175 + hideDetailsTabs(): boolean {
  176 + return this.isEditValue && this.entitiesTableConfig.hideDetailsTabsOnEdit;
  177 + }
  178 +
153 reload(): void { 179 reload(): void {
154 this.isEdit = false; 180 this.isEdit = false;
155 this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe( 181 this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe(
@@ -175,13 +201,28 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit @@ -175,13 +201,28 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
175 this.entityTabsComponent.entity = this.entity; 201 this.entityTabsComponent.entity = this.entity;
176 } 202 }
177 } else { 203 } else {
178 - this.selectedTab = 0; 204 + this.editingEntity = deepClone(this.entity);
  205 + this.entityComponent.entity = this.editingEntity;
  206 + if (this.entityTabsComponent) {
  207 + this.entityTabsComponent.entity = this.editingEntity;
  208 + }
  209 + if (this.entitiesTableConfig.hideDetailsTabsOnEdit) {
  210 + this.selectedTab = 0;
  211 + }
  212 + }
  213 + }
  214 +
  215 + helpLinkId(): string {
  216 + if (this.resources.helpLinkIdForEntity && this.entityComponent.entityForm) {
  217 + return this.resources.helpLinkIdForEntity(this.entityComponent.entityForm.getRawValue());
  218 + } else {
  219 + return this.resources.helpLinkId;
179 } 220 }
180 } 221 }
181 222
182 saveEntity() { 223 saveEntity() {
183 if (this.detailsForm.valid) { 224 if (this.detailsForm.valid) {
184 - const editingEntity = {...this.entity, ...this.entityComponent.entityFormValue()}; 225 + const editingEntity = {...this.editingEntity, ...this.entityComponent.entityFormValue()};
185 this.entitiesTableConfig.saveEntity(editingEntity).subscribe( 226 this.entitiesTableConfig.saveEntity(editingEntity).subscribe(
186 (entity) => { 227 (entity) => {
187 this.entity = entity; 228 this.entity = entity;
@@ -31,9 +31,15 @@ import { AuditLogMode } from '@shared/models/audit-log.models'; @@ -31,9 +31,15 @@ import { AuditLogMode } from '@shared/models/audit-log.models';
31 import { DebugEventType, EventType } from '@shared/models/event.models'; 31 import { DebugEventType, EventType } from '@shared/models/event.models';
32 import { AttributeScope, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; 32 import { AttributeScope, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
33 import { NULL_UUID } from '@shared/models/id/has-uuid'; 33 import { NULL_UUID } from '@shared/models/id/has-uuid';
  34 +import { NgForm } from '@angular/forms';
  35 +import { PageLink } from '@shared/models/page/page-link';
34 36
35 @Directive() 37 @Directive()
36 -export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends PageComponent implements OnInit, AfterViewInit { 38 +export abstract class EntityTabsComponent<T extends BaseData<HasId>,
  39 + P extends PageLink = PageLink,
  40 + L extends BaseData<HasId> = T,
  41 + C extends EntityTableConfig<T, P, L> = EntityTableConfig<T, P, L>>
  42 + extends PageComponent implements OnInit, AfterViewInit {
37 43
38 attributeScopes = AttributeScope; 44 attributeScopes = AttributeScope;
39 latestTelemetryTypes = LatestTelemetry; 45 latestTelemetryTypes = LatestTelemetry;
@@ -54,6 +60,8 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag @@ -54,6 +60,8 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
54 60
55 entityValue: T; 61 entityValue: T;
56 62
  63 + entitiesTableConfigValue: C;
  64 +
57 @ViewChildren(MatTab) entityTabs: QueryList<MatTab>; 65 @ViewChildren(MatTab) entityTabs: QueryList<MatTab>;
58 66
59 isEditValue: boolean; 67 isEditValue: boolean;
@@ -69,7 +77,7 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag @@ -69,7 +77,7 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
69 77
70 @Input() 78 @Input()
71 set entity(entity: T) { 79 set entity(entity: T) {
72 - this.entityValue = entity; 80 + this.setEntity(entity);
73 } 81 }
74 82
75 get entity(): T { 83 get entity(): T {
@@ -77,7 +85,16 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag @@ -77,7 +85,16 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
77 } 85 }
78 86
79 @Input() 87 @Input()
80 - entitiesTableConfig: EntityTableConfig<T>; 88 + set entitiesTableConfig(entitiesTableConfig: C) {
  89 + this.setEntitiesTableConfig(entitiesTableConfig);
  90 + }
  91 +
  92 + get entitiesTableConfig(): C {
  93 + return this.entitiesTableConfigValue;
  94 + }
  95 +
  96 + @Input()
  97 + detailsForm: NgForm;
81 98
82 private entityTabsSubject = new BehaviorSubject<Array<MatTab>>(null); 99 private entityTabsSubject = new BehaviorSubject<Array<MatTab>>(null);
83 100
@@ -100,4 +117,12 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag @@ -100,4 +117,12 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
100 ); 117 );
101 } 118 }
102 119
  120 + protected setEntity(entity: T) {
  121 + this.entityValue = entity;
  122 + }
  123 +
  124 + protected setEntitiesTableConfig(entitiesTableConfig: C) {
  125 + this.entitiesTableConfigValue = entitiesTableConfig;
  126 + }
  127 +
103 } 128 }
@@ -15,18 +15,22 @@ @@ -15,18 +15,22 @@
15 /// 15 ///
16 16
17 import { BaseData, HasId } from '@shared/models/base-data'; 17 import { BaseData, HasId } from '@shared/models/base-data';
18 -import { FormGroup, NgForm } from '@angular/forms'; 18 +import { FormBuilder, FormGroup, NgForm } from '@angular/forms';
19 import { PageComponent } from '@shared/components/page.component'; 19 import { PageComponent } from '@shared/components/page.component';
20 import { EventEmitter, Input, OnInit, Output, ViewChild, Directive } from '@angular/core'; 20 import { EventEmitter, Input, OnInit, Output, ViewChild, Directive } from '@angular/core';
21 import { Store } from '@ngrx/store'; 21 import { Store } from '@ngrx/store';
22 import { AppState } from '@core/core.state'; 22 import { AppState } from '@core/core.state';
23 import { EntityAction } from '@home/models/entity/entity-component.models'; 23 import { EntityAction } from '@home/models/entity/entity-component.models';
24 import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; 24 import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
  25 +import { PageLink } from '@shared/models/page/page-link';
25 26
26 @Directive() 27 @Directive()
27 -export abstract class EntityComponent<T extends BaseData<HasId>> extends PageComponent implements OnInit { 28 +export abstract class EntityComponent<T extends BaseData<HasId>,
  29 + P extends PageLink = PageLink,
  30 + L extends BaseData<HasId> = T,
  31 + C extends EntityTableConfig<T, P, L> = EntityTableConfig<T, P, L>>
  32 + extends PageComponent implements OnInit {
28 33
29 - entityValue: T;  
30 entityForm: FormGroup; 34 entityForm: FormGroup;
31 35
32 @ViewChild('entityNgForm', {static: true}) entityNgForm: NgForm; 36 @ViewChild('entityNgForm', {static: true}) entityNgForm: NgForm;
@@ -51,7 +55,8 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom @@ -51,7 +55,8 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom
51 set entity(entity: T) { 55 set entity(entity: T) {
52 this.entityValue = entity; 56 this.entityValue = entity;
53 if (this.entityForm) { 57 if (this.entityForm) {
54 - this.entityForm.reset(); 58 + this.entityForm.reset(undefined, {emitEvent: false});
  59 + this.entityForm.markAsPristine();
55 this.updateForm(entity); 60 this.updateForm(entity);
56 } 61 }
57 } 62 }
@@ -60,18 +65,18 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom @@ -60,18 +65,18 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom
60 return this.entityValue; 65 return this.entityValue;
61 } 66 }
62 67
63 - @Input()  
64 - entitiesTableConfig: EntityTableConfig<T>;  
65 -  
66 @Output() 68 @Output()
67 entityAction = new EventEmitter<EntityAction<T>>(); 69 entityAction = new EventEmitter<EntityAction<T>>();
68 70
69 - protected constructor(protected store: Store<AppState>) { 71 + protected constructor(protected store: Store<AppState>,
  72 + protected fb: FormBuilder,
  73 + protected entityValue: T,
  74 + protected entitiesTableConfig: C) {
70 super(store); 75 super(store);
  76 + this.entityForm = this.buildForm(this.entityValue);
71 } 77 }
72 78
73 ngOnInit() { 79 ngOnInit() {
74 - this.entityForm = this.buildForm(this.entityValue);  
75 } 80 }
76 81
77 onEntityAction($event: Event, action: string) { 82 onEntityAction($event: Event, action: string) {
@@ -96,7 +101,7 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom @@ -96,7 +101,7 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom
96 } 101 }
97 102
98 entityFormValue() { 103 entityFormValue() {
99 - const formValue = this.entityForm ? {...this.entityForm.value} : {}; 104 + const formValue = this.entityForm ? {...this.entityForm.getRawValue()} : {};
100 return this.prepareFormValue(formValue); 105 return this.prepareFormValue(formValue);
101 } 106 }
102 107
@@ -94,7 +94,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { @@ -94,7 +94,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
94 noEntities: 'event.no-events-prompt' 94 noEntities: 'event.no-events-prompt'
95 }; 95 };
96 this.entityResources = { 96 this.entityResources = {
97 - } as EntityTypeResource; 97 + } as EntityTypeResource<Event>;
98 this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink); 98 this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink);
99 99
100 this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; 100 this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
@@ -36,8 +36,8 @@ export class EntitiesDataSource<T extends BaseData<HasId>, P extends PageLink = @@ -36,8 +36,8 @@ export class EntitiesDataSource<T extends BaseData<HasId>, P extends PageLink =
36 public currentEntity: T = null; 36 public currentEntity: T = null;
37 37
38 constructor(private fetchFunction: EntitiesFetchFunction<T, P>, 38 constructor(private fetchFunction: EntitiesFetchFunction<T, P>,
39 - private selectionEnabledFunction: EntityBooleanFunction<T>,  
40 - private dataLoadedFunction: () => void) {} 39 + protected selectionEnabledFunction: EntityBooleanFunction<T>,
  40 + protected dataLoadedFunction: () => void) {}
41 41
42 connect(collectionViewer: CollectionViewer): Observable<T[] | ReadonlyArray<T>> { 42 connect(collectionViewer: CollectionViewer): Observable<T[] | ReadonlyArray<T>> {
43 return this.entitiesSubject.asObservable(); 43 return this.entitiesSubject.asObservable();
@@ -14,21 +14,21 @@ @@ -14,21 +14,21 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import {BaseData, HasId} from '@shared/models/base-data';  
18 -import {EntitiesFetchFunction} from '@home/models/datasource/entity-datasource';  
19 -import {Observable, of} from 'rxjs';  
20 -import {emptyPageData} from '@shared/models/page/page-data';  
21 -import {DatePipe} from '@angular/common';  
22 -import {Direction, SortOrder} from '@shared/models/page/sort-order';  
23 -import {EntityType, EntityTypeResource, EntityTypeTranslation} from '@shared/models/entity-type.models';  
24 -import {EntityComponent} from '@home/components/entity/entity.component';  
25 -import {Type} from '@angular/core';  
26 -import {EntityAction} from './entity-component.models';  
27 -import {HasUUID} from '@shared/models/id/has-uuid';  
28 -import {PageLink} from '@shared/models/page/page-link';  
29 -import {EntitiesTableComponent} from '@home/components/entity/entities-table.component';  
30 -import {EntityTableHeaderComponent} from '@home/components/entity/entity-table-header.component';  
31 -import {ActivatedRoute} from '@angular/router'; 17 +import { BaseData, HasId } from '@shared/models/base-data';
  18 +import { EntitiesDataSource, EntitiesFetchFunction } from '@home/models/datasource/entity-datasource';
  19 +import { Observable, of } from 'rxjs';
  20 +import { emptyPageData } from '@shared/models/page/page-data';
  21 +import { DatePipe } from '@angular/common';
  22 +import { Direction, SortOrder } from '@shared/models/page/sort-order';
  23 +import { EntityType, EntityTypeResource, EntityTypeTranslation } from '@shared/models/entity-type.models';
  24 +import { EntityComponent } from '@home/components/entity/entity.component';
  25 +import { Type } from '@angular/core';
  26 +import { EntityAction } from './entity-component.models';
  27 +import { HasUUID } from '@shared/models/id/has-uuid';
  28 +import { PageLink } from '@shared/models/page/page-link';
  29 +import { EntitiesTableComponent } from '@home/components/entity/entities-table.component';
  30 +import { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component';
  31 +import { ActivatedRoute } from '@angular/router';
32 import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; 32 import { EntityTabsComponent } from '../../components/entity/entity-tabs.component';
33 33
34 export type EntityBooleanFunction<T extends BaseData<HasId>> = (entity: T) => boolean; 34 export type EntityBooleanFunction<T extends BaseData<HasId>> = (entity: T) => boolean;
@@ -76,7 +76,9 @@ export class BaseEntityTableColumn<T extends BaseData<HasId>> { @@ -76,7 +76,9 @@ export class BaseEntityTableColumn<T extends BaseData<HasId>> {
76 public key: string, 76 public key: string,
77 public title: string, 77 public title: string,
78 public width: string = '0px', 78 public width: string = '0px',
79 - public sortable: boolean = true) { 79 + public sortable: boolean = true,
  80 + public ignoreTranslate: boolean = false,
  81 + public mobileHide: boolean = false) {
80 } 82 }
81 } 83 }
82 84
@@ -120,7 +122,7 @@ export class DateEntityTableColumn<T extends BaseData<HasId>> extends EntityTabl @@ -120,7 +122,7 @@ export class DateEntityTableColumn<T extends BaseData<HasId>> extends EntityTabl
120 122
121 export type EntityColumn<T extends BaseData<HasId>> = EntityTableColumn<T> | EntityActionTableColumn<T>; 123 export type EntityColumn<T extends BaseData<HasId>> = EntityTableColumn<T> | EntityActionTableColumn<T>;
122 124
123 -export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = PageLink> { 125 +export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = PageLink, L extends BaseData<HasId> = T> {
124 126
125 constructor() {} 127 constructor() {}
126 128
@@ -137,31 +139,35 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P @@ -137,31 +139,35 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
137 addEnabled = true; 139 addEnabled = true;
138 entitiesDeleteEnabled = true; 140 entitiesDeleteEnabled = true;
139 detailsPanelEnabled = true; 141 detailsPanelEnabled = true;
  142 + hideDetailsTabsOnEdit = true;
140 actionsColumnTitle = null; 143 actionsColumnTitle = null;
141 entityTranslations: EntityTypeTranslation; 144 entityTranslations: EntityTypeTranslation;
142 - entityResources: EntityTypeResource;  
143 - entityComponent: Type<EntityComponent<T>>;  
144 - entityTabsComponent: Type<EntityTabsComponent<T>>; 145 + entityResources: EntityTypeResource<T>;
  146 + entityComponent: Type<EntityComponent<T, P, L>>;
  147 + entityTabsComponent: Type<EntityTabsComponent<T, P, L>>;
145 addDialogStyle = {}; 148 addDialogStyle = {};
146 defaultSortOrder: SortOrder = {property: 'createdTime', direction: Direction.ASC}; 149 defaultSortOrder: SortOrder = {property: 'createdTime', direction: Direction.ASC};
147 - columns: Array<EntityColumn<T>> = [];  
148 - cellActionDescriptors: Array<CellActionDescriptor<T>> = [];  
149 - groupActionDescriptors: Array<GroupActionDescriptor<T>> = []; 150 + columns: Array<EntityColumn<L>> = [];
  151 + cellActionDescriptors: Array<CellActionDescriptor<L>> = [];
  152 + groupActionDescriptors: Array<GroupActionDescriptor<L>> = [];
150 headerActionDescriptors: Array<HeaderActionDescriptor> = []; 153 headerActionDescriptors: Array<HeaderActionDescriptor> = [];
151 addActionDescriptors: Array<HeaderActionDescriptor> = []; 154 addActionDescriptors: Array<HeaderActionDescriptor> = [];
152 - headerComponent: Type<EntityTableHeaderComponent<T>>; 155 + headerComponent: Type<EntityTableHeaderComponent<L>>;
153 addEntity: CreateEntityOperation<T> = null; 156 addEntity: CreateEntityOperation<T> = null;
  157 + dataSource: (dataLoadedFunction: () => void) => EntitiesDataSource<L> = (dataLoadedFunction: () => void) => {
  158 + return new EntitiesDataSource(this.entitiesFetchFunction, this.entitySelectionEnabled, dataLoadedFunction);
  159 + };
154 detailsReadonly: EntityBooleanFunction<T> = () => false; 160 detailsReadonly: EntityBooleanFunction<T> = () => false;
155 - entitySelectionEnabled: EntityBooleanFunction<T> = () => true;  
156 - deleteEnabled: EntityBooleanFunction<T> = () => true;  
157 - deleteEntityTitle: EntityStringFunction<T> = () => '';  
158 - deleteEntityContent: EntityStringFunction<T> = () => ''; 161 + entitySelectionEnabled: EntityBooleanFunction<L> = () => true;
  162 + deleteEnabled: EntityBooleanFunction<T | L> = () => true;
  163 + deleteEntityTitle: EntityStringFunction<L> = () => '';
  164 + deleteEntityContent: EntityStringFunction<L> = () => '';
159 deleteEntitiesTitle: EntityCountStringFunction = () => ''; 165 deleteEntitiesTitle: EntityCountStringFunction = () => '';
160 deleteEntitiesContent: EntityCountStringFunction = () => ''; 166 deleteEntitiesContent: EntityCountStringFunction = () => '';
161 loadEntity: EntityByIdOperation<T> = () => of(); 167 loadEntity: EntityByIdOperation<T> = () => of();
162 saveEntity: EntityTwoWayOperation<T> = (entity) => of(entity); 168 saveEntity: EntityTwoWayOperation<T> = (entity) => of(entity);
163 deleteEntity: EntityIdOneWayOperation = () => of(); 169 deleteEntity: EntityIdOneWayOperation = () => of();
164 - entitiesFetchFunction: EntitiesFetchFunction<T, P> = () => of(emptyPageData<T>()); 170 + entitiesFetchFunction: EntitiesFetchFunction<L, P> = () => of(emptyPageData<L>());
165 onEntityAction: EntityActionFunction<T> = () => false; 171 onEntityAction: EntityActionFunction<T> = () => false;
166 entityTitle: EntityStringFunction<T> = (entity) => entity?.name; 172 entityTitle: EntityStringFunction<T> = (entity) => entity?.name;
167 } 173 }
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { Store } from '@ngrx/store'; 18 import { Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { EntityComponent } from '../../components/entity/entity.component'; 20 import { EntityComponent } from '../../components/entity/entity.component';
@@ -24,6 +24,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; @@ -24,6 +24,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid';
24 import { ActionNotificationShow } from '@core/notification/notification.actions'; 24 import { ActionNotificationShow } from '@core/notification/notification.actions';
25 import { TranslateService } from '@ngx-translate/core'; 25 import { TranslateService } from '@ngx-translate/core';
26 import { AssetInfo } from '@app/shared/models/asset.models'; 26 import { AssetInfo } from '@app/shared/models/asset.models';
  27 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
27 28
28 @Component({ 29 @Component({
29 selector: 'tb-asset', 30 selector: 'tb-asset',
@@ -38,8 +39,10 @@ export class AssetComponent extends EntityComponent<AssetInfo> { @@ -38,8 +39,10 @@ export class AssetComponent extends EntityComponent<AssetInfo> {
38 39
39 constructor(protected store: Store<AppState>, 40 constructor(protected store: Store<AppState>,
40 protected translate: TranslateService, 41 protected translate: TranslateService,
  42 + @Inject('entity') protected entityValue: AssetInfo,
  43 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<AssetInfo>,
41 public fb: FormBuilder) { 44 public fb: FormBuilder) {
42 - super(store); 45 + super(store, fb, entityValue, entitiesTableConfig);
43 } 46 }
44 47
45 ngOnInit() { 48 ngOnInit() {
@@ -124,7 +124,7 @@ const routes: Routes = [ @@ -124,7 +124,7 @@ const routes: Routes = [
124 breadcrumb: { 124 breadcrumb: {
125 labelFunction: dashboardBreadcumbLabelFunction, 125 labelFunction: dashboardBreadcumbLabelFunction,
126 icon: 'dashboard' 126 icon: 'dashboard'
127 - } as BreadCrumbConfig, 127 + } as BreadCrumbConfig<DashboardPageComponent>,
128 auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], 128 auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
129 title: 'customer.dashboard', 129 title: 'customer.dashboard',
130 widgetEditMode: false 130 widgetEditMode: false
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { Store } from '@ngrx/store'; 18 import { Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 20 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@@ -22,6 +22,7 @@ import { Customer } from '@shared/models/customer.model'; @@ -22,6 +22,7 @@ import { Customer } from '@shared/models/customer.model';
22 import { ActionNotificationShow } from '@app/core/notification/notification.actions'; 22 import { ActionNotificationShow } from '@app/core/notification/notification.actions';
23 import { TranslateService } from '@ngx-translate/core'; 23 import { TranslateService } from '@ngx-translate/core';
24 import { ContactBasedComponent } from '../../components/entity/contact-based.component'; 24 import { ContactBasedComponent } from '../../components/entity/contact-based.component';
  25 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
25 26
26 @Component({ 27 @Component({
27 selector: 'tb-customer', 28 selector: 'tb-customer',
@@ -33,8 +34,10 @@ export class CustomerComponent extends ContactBasedComponent<Customer> { @@ -33,8 +34,10 @@ export class CustomerComponent extends ContactBasedComponent<Customer> {
33 34
34 constructor(protected store: Store<AppState>, 35 constructor(protected store: Store<AppState>,
35 protected translate: TranslateService, 36 protected translate: TranslateService,
  37 + @Inject('entity') protected entityValue: Customer,
  38 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<Customer>,
36 protected fb: FormBuilder) { 39 protected fb: FormBuilder) {
37 - super(store, fb); 40 + super(store, fb, entityValue, entitiesTableConfig);
38 } 41 }
39 42
40 hideDelete() { 43 hideDelete() {
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { Store } from '@ngrx/store'; 18 import { Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { EntityComponent } from '../../components/entity/entity.component'; 20 import { EntityComponent } from '../../components/entity/entity.component';
@@ -28,6 +28,7 @@ import { @@ -28,6 +28,7 @@ import {
28 isPublicDashboard 28 isPublicDashboard
29 } from '@shared/models/dashboard.models'; 29 } from '@shared/models/dashboard.models';
30 import { DashboardService } from '@core/http/dashboard.service'; 30 import { DashboardService } from '@core/http/dashboard.service';
  31 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
31 32
32 @Component({ 33 @Component({
33 selector: 'tb-dashboard-form', 34 selector: 'tb-dashboard-form',
@@ -45,8 +46,10 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> { @@ -45,8 +46,10 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> {
45 constructor(protected store: Store<AppState>, 46 constructor(protected store: Store<AppState>,
46 protected translate: TranslateService, 47 protected translate: TranslateService,
47 private dashboardService: DashboardService, 48 private dashboardService: DashboardService,
  49 + @Inject('entity') protected entityValue: Dashboard,
  50 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<Dashboard>,
48 public fb: FormBuilder) { 51 public fb: FormBuilder) {
49 - super(store); 52 + super(store, fb, entityValue, entitiesTableConfig);
50 } 53 }
51 54
52 ngOnInit() { 55 ngOnInit() {
@@ -103,7 +106,9 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> { @@ -103,7 +106,9 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> {
103 } 106 }
104 107
105 private updateFields(entity: Dashboard): void { 108 private updateFields(entity: Dashboard): void {
106 - this.assignedCustomersText = getDashboardAssignedCustomersText(entity);  
107 - this.publicLink = this.dashboardService.getPublicDashboardLink(entity); 109 + if (entity) {
  110 + this.assignedCustomersText = getDashboardAssignedCustomersText(entity);
  111 + this.publicLink = this.dashboardService.getPublicDashboardLink(entity);
  112 + }
108 } 113 }
109 } 114 }
@@ -43,7 +43,8 @@ export class DashboardResolver implements Resolve<Dashboard> { @@ -43,7 +43,8 @@ export class DashboardResolver implements Resolve<Dashboard> {
43 } 43 }
44 } 44 }
45 45
46 -export const dashboardBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => component.dashboard.title); 46 +export const dashboardBreadcumbLabelFunction: BreadCrumbLabelFunction<DashboardPageComponent>
  47 + = ((route, translate, component) => component.dashboard.title);
47 48
48 const routes: Routes = [ 49 const routes: Routes = [
49 { 50 {
@@ -74,7 +75,7 @@ const routes: Routes = [ @@ -74,7 +75,7 @@ const routes: Routes = [
74 breadcrumb: { 75 breadcrumb: {
75 labelFunction: dashboardBreadcumbLabelFunction, 76 labelFunction: dashboardBreadcumbLabelFunction,
76 icon: 'dashboard' 77 icon: 'dashboard'
77 - } as BreadCrumbConfig, 78 + } as BreadCrumbConfig<DashboardPageComponent>,
78 auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], 79 auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
79 title: 'dashboard.dashboard', 80 title: 'dashboard.dashboard',
80 widgetEditMode: false 81 widgetEditMode: false
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { Store } from '@ngrx/store'; 18 import { Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { EntityComponent } from '../../components/entity/entity.component'; 20 import { EntityComponent } from '../../components/entity/entity.component';
@@ -26,6 +26,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' @@ -26,6 +26,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions'
26 import { TranslateService } from '@ngx-translate/core'; 26 import { TranslateService } from '@ngx-translate/core';
27 import { DeviceService } from '@core/http/device.service'; 27 import { DeviceService } from '@core/http/device.service';
28 import { ClipboardService } from 'ngx-clipboard'; 28 import { ClipboardService } from 'ngx-clipboard';
  29 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
29 30
30 @Component({ 31 @Component({
31 selector: 'tb-device', 32 selector: 'tb-device',
@@ -42,8 +43,10 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { @@ -42,8 +43,10 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
42 protected translate: TranslateService, 43 protected translate: TranslateService,
43 private deviceService: DeviceService, 44 private deviceService: DeviceService,
44 private clipboardService: ClipboardService, 45 private clipboardService: ClipboardService,
  46 + @Inject('entity') protected entityValue: DeviceInfo,
  47 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<DeviceInfo>,
45 public fb: FormBuilder) { 48 public fb: FormBuilder) {
46 - super(store); 49 + super(store, fb, entityValue, entitiesTableConfig);
47 } 50 }
48 51
49 ngOnInit() { 52 ngOnInit() {
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { Store } from '@ngrx/store'; 18 import { Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { EntityComponent } from '../../components/entity/entity.component'; 20 import { EntityComponent } from '../../components/entity/entity.component';
@@ -27,6 +27,7 @@ import { EntityViewInfo } from '@app/shared/models/entity-view.models'; @@ -27,6 +27,7 @@ import { EntityViewInfo } from '@app/shared/models/entity-view.models';
27 import { Observable } from 'rxjs'; 27 import { Observable } from 'rxjs';
28 import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; 28 import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
29 import { EntityId } from '@app/shared/models/id/entity-id'; 29 import { EntityId } from '@app/shared/models/id/entity-id';
  30 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
30 31
31 @Component({ 32 @Component({
32 selector: 'tb-entity-view', 33 selector: 'tb-entity-view',
@@ -50,8 +51,10 @@ export class EntityViewComponent extends EntityComponent<EntityViewInfo> { @@ -50,8 +51,10 @@ export class EntityViewComponent extends EntityComponent<EntityViewInfo> {
50 51
51 constructor(protected store: Store<AppState>, 52 constructor(protected store: Store<AppState>,
52 protected translate: TranslateService, 53 protected translate: TranslateService,
  54 + @Inject('entity') protected entityValue: EntityViewInfo,
  55 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<EntityViewInfo>,
53 public fb: FormBuilder) { 56 public fb: FormBuilder) {
54 - super(store); 57 + super(store, fb, entityValue, entitiesTableConfig);
55 } 58 }
56 59
57 ngOnInit() { 60 ngOnInit() {
@@ -126,7 +126,8 @@ export class RuleChainImportGuard implements CanActivate { @@ -126,7 +126,8 @@ export class RuleChainImportGuard implements CanActivate {
126 126
127 } 127 }
128 128
129 -export const ruleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => { 129 +export const ruleChainBreadcumbLabelFunction: BreadCrumbLabelFunction<RuleChainPageComponent>
  130 + = ((route, translate, component) => {
130 let label: string = component.ruleChain.name; 131 let label: string = component.ruleChain.name;
131 if (component.ruleChain.root) { 132 if (component.ruleChain.root) {
132 label += ` (${translate.instant('rulechain.root')})`; 133 label += ` (${translate.instant('rulechain.root')})`;
@@ -134,7 +135,8 @@ export const ruleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, @@ -134,7 +135,8 @@ export const ruleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route,
134 return label; 135 return label;
135 }); 136 });
136 137
137 -export const importRuleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => { 138 +export const importRuleChainBreadcumbLabelFunction: BreadCrumbLabelFunction<RuleChainPageComponent> =
  139 + ((route, translate, component) => {
138 return `${translate.instant('rulechain.import')}: ${component.ruleChain.name}`; 140 return `${translate.instant('rulechain.import')}: ${component.ruleChain.name}`;
139 }); 141 });
140 142
@@ -167,7 +169,7 @@ const routes: Routes = [ @@ -167,7 +169,7 @@ const routes: Routes = [
167 breadcrumb: { 169 breadcrumb: {
168 labelFunction: ruleChainBreadcumbLabelFunction, 170 labelFunction: ruleChainBreadcumbLabelFunction,
169 icon: 'settings_ethernet' 171 icon: 'settings_ethernet'
170 - } as BreadCrumbConfig, 172 + } as BreadCrumbConfig<RuleChainPageComponent>,
171 auth: [Authority.TENANT_ADMIN], 173 auth: [Authority.TENANT_ADMIN],
172 title: 'rulechain.rulechain', 174 title: 'rulechain.rulechain',
173 import: false 175 import: false
@@ -187,7 +189,7 @@ const routes: Routes = [ @@ -187,7 +189,7 @@ const routes: Routes = [
187 breadcrumb: { 189 breadcrumb: {
188 labelFunction: importRuleChainBreadcumbLabelFunction, 190 labelFunction: importRuleChainBreadcumbLabelFunction,
189 icon: 'settings_ethernet' 191 icon: 'settings_ethernet'
190 - } as BreadCrumbConfig, 192 + } as BreadCrumbConfig<RuleChainPageComponent>,
191 auth: [Authority.TENANT_ADMIN], 193 auth: [Authority.TENANT_ADMIN],
192 title: 'rulechain.rulechain', 194 title: 'rulechain.rulechain',
193 import: true 195 import: true
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { Store } from '@ngrx/store'; 18 import { Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { EntityComponent } from '../../components/entity/entity.component'; 20 import { EntityComponent } from '../../components/entity/entity.component';
@@ -22,6 +22,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @@ -22,6 +22,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
22 import { ActionNotificationShow } from '@core/notification/notification.actions'; 22 import { ActionNotificationShow } from '@core/notification/notification.actions';
23 import { TranslateService } from '@ngx-translate/core'; 23 import { TranslateService } from '@ngx-translate/core';
24 import { RuleChain } from '@shared/models/rule-chain.models'; 24 import { RuleChain } from '@shared/models/rule-chain.models';
  25 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
25 26
26 @Component({ 27 @Component({
27 selector: 'tb-rulechain', 28 selector: 'tb-rulechain',
@@ -32,8 +33,10 @@ export class RuleChainComponent extends EntityComponent<RuleChain> { @@ -32,8 +33,10 @@ export class RuleChainComponent extends EntityComponent<RuleChain> {
32 33
33 constructor(protected store: Store<AppState>, 34 constructor(protected store: Store<AppState>,
34 protected translate: TranslateService, 35 protected translate: TranslateService,
  36 + @Inject('entity') protected entityValue: RuleChain,
  37 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<RuleChain>,
35 public fb: FormBuilder) { 38 public fb: FormBuilder) {
36 - super(store); 39 + super(store, fb, entityValue, entitiesTableConfig);
37 } 40 }
38 41
39 hideDelete() { 42 hideDelete() {
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { Store } from '@ngrx/store'; 18 import { Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 20 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@@ -23,6 +23,7 @@ import {Tenant} from '@app/shared/models/tenant.model'; @@ -23,6 +23,7 @@ import {Tenant} from '@app/shared/models/tenant.model';
23 import {ActionNotificationShow} from '@app/core/notification/notification.actions'; 23 import {ActionNotificationShow} from '@app/core/notification/notification.actions';
24 import {TranslateService} from '@ngx-translate/core'; 24 import {TranslateService} from '@ngx-translate/core';
25 import {ContactBasedComponent} from '../../components/entity/contact-based.component'; 25 import {ContactBasedComponent} from '../../components/entity/contact-based.component';
  26 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
26 27
27 @Component({ 28 @Component({
28 selector: 'tb-tenant', 29 selector: 'tb-tenant',
@@ -32,8 +33,10 @@ export class TenantComponent extends ContactBasedComponent<Tenant> { @@ -32,8 +33,10 @@ export class TenantComponent extends ContactBasedComponent<Tenant> {
32 33
33 constructor(protected store: Store<AppState>, 34 constructor(protected store: Store<AppState>,
34 protected translate: TranslateService, 35 protected translate: TranslateService,
  36 + @Inject('entity') protected entityValue: Tenant,
  37 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<Tenant>,
35 protected fb: FormBuilder) { 38 protected fb: FormBuilder) {
36 - super(store, fb); 39 + super(store, fb, entityValue, entitiesTableConfig);
37 } 40 }
38 41
39 hideDelete() { 42 hideDelete() {
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component } from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import { select, Store } from '@ngrx/store'; 18 import { select, Store } from '@ngrx/store';
19 import { AppState } from '@core/core.state'; 19 import { AppState } from '@core/core.state';
20 import { EntityComponent } from '../../components/entity/entity.component'; 20 import { EntityComponent } from '../../components/entity/entity.component';
@@ -24,6 +24,7 @@ import { selectAuth } from '@core/auth/auth.selectors'; @@ -24,6 +24,7 @@ import { selectAuth } from '@core/auth/auth.selectors';
24 import { map } from 'rxjs/operators'; 24 import { map } from 'rxjs/operators';
25 import { Authority } from '@shared/models/authority.enum'; 25 import { Authority } from '@shared/models/authority.enum';
26 import { isUndefined } from '@core/utils'; 26 import { isUndefined } from '@core/utils';
  27 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
27 28
28 @Component({ 29 @Component({
29 selector: 'tb-user', 30 selector: 'tb-user',
@@ -40,8 +41,10 @@ export class UserComponent extends EntityComponent<User> { @@ -40,8 +41,10 @@ export class UserComponent extends EntityComponent<User> {
40 ); 41 );
41 42
42 constructor(protected store: Store<AppState>, 43 constructor(protected store: Store<AppState>,
  44 + @Inject('entity') protected entityValue: User,
  45 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<User>,
43 public fb: FormBuilder) { 46 public fb: FormBuilder) {
44 - super(store); 47 + super(store, fb, entityValue, entitiesTableConfig);
45 } 48 }
46 49
47 hideDelete() { 50 hideDelete() {
@@ -114,10 +114,11 @@ export class WidgetEditorAddDataResolver implements Resolve<WidgetEditorData> { @@ -114,10 +114,11 @@ export class WidgetEditorAddDataResolver implements Resolve<WidgetEditorData> {
114 } 114 }
115 } 115 }
116 116
117 -export const widgetTypesBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate) => route.data.widgetsBundle.title); 117 +export const widgetTypesBreadcumbLabelFunction: BreadCrumbLabelFunction<any> = ((route, translate) =>
  118 + route.data.widgetsBundle.title);
118 119
119 -export const widgetEditorBreadcumbLabelFunction: BreadCrumbLabelFunction =  
120 - ((route, translate, component) => component ? (component as WidgetEditorComponent).widget.widgetName : ''); 120 +export const widgetEditorBreadcumbLabelFunction: BreadCrumbLabelFunction<WidgetEditorComponent> =
  121 + ((route, translate, component) => component ? component.widget.widgetName : '');
121 122
122 export const routes: Routes = [ 123 export const routes: Routes = [
123 { 124 {
@@ -146,7 +147,7 @@ export const routes: Routes = [ @@ -146,7 +147,7 @@ export const routes: Routes = [
146 breadcrumb: { 147 breadcrumb: {
147 labelFunction: widgetTypesBreadcumbLabelFunction, 148 labelFunction: widgetTypesBreadcumbLabelFunction,
148 icon: 'now_widgets' 149 icon: 'now_widgets'
149 - } as BreadCrumbConfig 150 + } as BreadCrumbConfig<any>
150 }, 151 },
151 resolve: { 152 resolve: {
152 widgetsBundle: WidgetsBundleResolver 153 widgetsBundle: WidgetsBundleResolver
@@ -173,7 +174,7 @@ export const routes: Routes = [ @@ -173,7 +174,7 @@ export const routes: Routes = [
173 breadcrumb: { 174 breadcrumb: {
174 labelFunction: widgetEditorBreadcumbLabelFunction, 175 labelFunction: widgetEditorBreadcumbLabelFunction,
175 icon: 'insert_chart' 176 icon: 'insert_chart'
176 - } as BreadCrumbConfig 177 + } as BreadCrumbConfig<WidgetEditorComponent>
177 }, 178 },
178 resolve: { 179 resolve: {
179 widgetEditorData: WidgetEditorDataResolver 180 widgetEditorData: WidgetEditorDataResolver
@@ -189,7 +190,7 @@ export const routes: Routes = [ @@ -189,7 +190,7 @@ export const routes: Routes = [
189 breadcrumb: { 190 breadcrumb: {
190 labelFunction: widgetEditorBreadcumbLabelFunction, 191 labelFunction: widgetEditorBreadcumbLabelFunction,
191 icon: 'insert_chart' 192 icon: 'insert_chart'
192 - } as BreadCrumbConfig 193 + } as BreadCrumbConfig<WidgetEditorComponent>
193 }, 194 },
194 resolve: { 195 resolve: {
195 widgetEditorData: WidgetEditorAddDataResolver 196 widgetEditorData: WidgetEditorAddDataResolver
@@ -14,12 +14,13 @@ @@ -14,12 +14,13 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import {Component} from '@angular/core'; 17 +import { Component, Inject } from '@angular/core';
18 import {Store} from '@ngrx/store'; 18 import {Store} from '@ngrx/store';
19 import {AppState} from '@core/core.state'; 19 import {AppState} from '@core/core.state';
20 import {EntityComponent} from '../../components/entity/entity.component'; 20 import {EntityComponent} from '../../components/entity/entity.component';
21 import {FormBuilder, FormGroup, Validators} from '@angular/forms'; 21 import {FormBuilder, FormGroup, Validators} from '@angular/forms';
22 import {WidgetsBundle} from '@shared/models/widgets-bundle.model'; 22 import {WidgetsBundle} from '@shared/models/widgets-bundle.model';
  23 +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
23 24
24 @Component({ 25 @Component({
25 selector: 'tb-widgets-bundle', 26 selector: 'tb-widgets-bundle',
@@ -29,8 +30,10 @@ import {WidgetsBundle} from '@shared/models/widgets-bundle.model'; @@ -29,8 +30,10 @@ import {WidgetsBundle} from '@shared/models/widgets-bundle.model';
29 export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> { 30 export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> {
30 31
31 constructor(protected store: Store<AppState>, 32 constructor(protected store: Store<AppState>,
  33 + @Inject('entity') protected entityValue: WidgetsBundle,
  34 + @Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<WidgetsBundle>,
32 public fb: FormBuilder) { 35 public fb: FormBuilder) {
33 - super(store); 36 + super(store, fb, entityValue, entitiesTableConfig);
34 } 37 }
35 38
36 hideDelete() { 39 hideDelete() {
@@ -65,7 +65,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { @@ -65,7 +65,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy {
65 buildBreadCrumbs(route: ActivatedRouteSnapshot, breadcrumbs: Array<BreadCrumb> = []): Array<BreadCrumb> { 65 buildBreadCrumbs(route: ActivatedRouteSnapshot, breadcrumbs: Array<BreadCrumb> = []): Array<BreadCrumb> {
66 let newBreadcrumbs = breadcrumbs; 66 let newBreadcrumbs = breadcrumbs;
67 if (route.routeConfig && route.routeConfig.data) { 67 if (route.routeConfig && route.routeConfig.data) {
68 - const breadcrumbConfig = route.routeConfig.data.breadcrumb as BreadCrumbConfig; 68 + const breadcrumbConfig = route.routeConfig.data.breadcrumb as BreadCrumbConfig<any>;
69 if (breadcrumbConfig && !breadcrumbConfig.skip) { 69 if (breadcrumbConfig && !breadcrumbConfig.skip) {
70 let label; 70 let label;
71 let labelFunction; 71 let labelFunction;
@@ -27,10 +27,10 @@ export interface BreadCrumb { @@ -27,10 +27,10 @@ export interface BreadCrumb {
27 queryParams: Params; 27 queryParams: Params;
28 } 28 }
29 29
30 -export type BreadCrumbLabelFunction = (route: ActivatedRouteSnapshot, translate: TranslateService, component: any) => string; 30 +export type BreadCrumbLabelFunction<C> = (route: ActivatedRouteSnapshot, translate: TranslateService, component: C) => string;
31 31
32 -export interface BreadCrumbConfig {  
33 - labelFunction: BreadCrumbLabelFunction; 32 +export interface BreadCrumbConfig<C> {
  33 + labelFunction: BreadCrumbLabelFunction<C>;
34 label: string; 34 label: string;
35 icon: string; 35 icon: string;
36 skip: boolean; 36 skip: boolean;
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 /// 15 ///
16 16
17 import {TenantId} from './id/tenant-id'; 17 import {TenantId} from './id/tenant-id';
  18 +import { BaseData, HasId } from '@shared/models/base-data';
18 19
19 /// 20 ///
20 /// Copyright © 2016-2019 The Thingsboard Authors 21 /// Copyright © 2016-2019 The Thingsboard Authors
@@ -63,8 +64,9 @@ export interface EntityTypeTranslation { @@ -63,8 +64,9 @@ export interface EntityTypeTranslation {
63 search?: string; 64 search?: string;
64 } 65 }
65 66
66 -export interface EntityTypeResource { 67 +export interface EntityTypeResource<T> {
67 helpLinkId: string; 68 helpLinkId: string;
  69 + helpLinkIdForEntity?(entity: T): string;
68 } 70 }
69 71
70 export const entityTypeTranslations = new Map<EntityType | AliasEntityType, EntityTypeTranslation>( 72 export const entityTypeTranslations = new Map<EntityType | AliasEntityType, EntityTypeTranslation>(
@@ -223,7 +225,7 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti @@ -223,7 +225,7 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
223 ] 225 ]
224 ); 226 );
225 227
226 -export const entityTypeResources = new Map<EntityType, EntityTypeResource>( 228 +export const entityTypeResources = new Map<EntityType, EntityTypeResource<BaseData<HasId>>>(
227 [ 229 [
228 [ 230 [
229 EntityType.TENANT, 231 EntityType.TENANT,