Commit c60e99fad90f572b668e3fae5a153a736ba67d1c
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, |