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