Browse Source

UI: Entity table models improvements

pull/2592/head
Igor Kulikov 7 years ago
parent
commit
c60e99fad9
  1. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts
  2. 2
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts
  3. 2
      ui-ngx/src/app/modules/home/components/entity/add-entity-dialog.component.html
  4. 33
      ui-ngx/src/app/modules/home/components/entity/add-entity-dialog.component.ts
  5. 7
      ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts
  6. 10
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.html
  7. 78
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts
  8. 4
      ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.html
  9. 55
      ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts
  10. 31
      ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts
  11. 25
      ui-ngx/src/app/modules/home/components/entity/entity.component.ts
  12. 2
      ui-ngx/src/app/modules/home/components/event/event-table-config.ts
  13. 4
      ui-ngx/src/app/modules/home/models/datasource/entity-datasource.ts
  14. 64
      ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
  15. 7
      ui-ngx/src/app/modules/home/pages/asset/asset.component.ts
  16. 2
      ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts
  17. 7
      ui-ngx/src/app/modules/home/pages/customer/customer.component.ts
  18. 13
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts
  19. 5
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts
  20. 7
      ui-ngx/src/app/modules/home/pages/device/device.component.ts
  21. 7
      ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.ts
  22. 10
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts
  23. 7
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts
  24. 7
      ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts
  25. 7
      ui-ngx/src/app/modules/home/pages/user/user.component.ts
  26. 13
      ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts
  27. 7
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts
  28. 2
      ui-ngx/src/app/shared/components/breadcrumb.component.ts
  29. 6
      ui-ngx/src/app/shared/components/breadcrumb.ts
  30. 6
      ui-ngx/src/app/shared/models/entity-type.models.ts

2
ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts

@ -72,7 +72,7 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
this.entityType = EntityType.ALARM; this.entityType = EntityType.ALARM;
this.entityTranslations = entityTypeTranslations.get(EntityType.ALARM); this.entityTranslations = entityTypeTranslations.get(EntityType.ALARM);
this.entityResources = { this.entityResources = {
} as EntityTypeResource; } as EntityTypeResource<AlarmInfo>;
this.searchStatus = defaultSearchStatus; this.searchStatus = defaultSearchStatus;
this.headerComponent = AlarmTableHeaderComponent; this.headerComponent = AlarmTableHeaderComponent;

2
ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts

@ -68,7 +68,7 @@ export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLin
search: 'audit-log.search' search: 'audit-log.search'
}; };
this.entityResources = { this.entityResources = {
} as EntityTypeResource; } as EntityTypeResource<AuditLog>;
this.entitiesFetchFunction = pageLink => this.fetchAuditLogs(pageLink); this.entitiesFetchFunction = pageLink => this.fetchAuditLogs(pageLink);

2
ui-ngx/src/app/modules/home/components/entity/add-entity-dialog.component.html

@ -19,7 +19,7 @@
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<h2 translate>{{ translations.add }}</h2> <h2 translate>{{ translations.add }}</h2>
<span fxFlex></span> <span fxFlex></span>
<div [tb-help]="resources.helpLinkId"></div> <div [tb-help]="helpLinkId()"></div>
<button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
type="button"> type="button">

33
ui-ngx/src/app/modules/home/components/entity/add-entity-dialog.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, ComponentFactoryResolver, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core'; import { Component, ComponentFactoryResolver, Inject, Injector, OnInit, SkipSelf, ViewChild } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -29,6 +29,7 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.mod
import { AddEntityDialogData } from '@home/models/entity/entity-component.models'; import { AddEntityDialogData } from '@home/models/entity/entity-component.models';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'tb-add-entity-dialog', selector: 'tb-add-entity-dialog',
@ -44,7 +45,7 @@ export class AddEntityDialogComponent extends
entitiesTableConfig: EntityTableConfig<BaseData<HasId>>; entitiesTableConfig: EntityTableConfig<BaseData<HasId>>;
translations: EntityTypeTranslation; translations: EntityTypeTranslation;
resources: EntityTypeResource; resources: EntityTypeResource<BaseData<HasId>>;
entity: BaseData<EntityId>; entity: BaseData<EntityId>;
submitted = false; submitted = false;
@ -56,6 +57,7 @@ export class AddEntityDialogComponent extends
@Inject(MAT_DIALOG_DATA) public data: AddEntityDialogData<BaseData<HasId>>, @Inject(MAT_DIALOG_DATA) public data: AddEntityDialogData<BaseData<HasId>>,
public dialogRef: MatDialogRef<AddEntityDialogComponent, BaseData<HasId>>, public dialogRef: MatDialogRef<AddEntityDialogComponent, BaseData<HasId>>,
private componentFactoryResolver: ComponentFactoryResolver, private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher) { @SkipSelf() private errorStateMatcher: ErrorStateMatcher) {
super(store, router, dialogRef); super(store, router, dialogRef);
} }
@ -68,14 +70,35 @@ export class AddEntityDialogComponent extends
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent);
const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef; const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory); const injector: Injector = Injector.create(
{
providers: [
{
provide: 'entity',
useValue: this.entity
},
{
provide: 'entitiesTableConfig',
useValue: this.entitiesTableConfig
}
],
parent: this.injector
}
);
const componentRef = viewContainerRef.createComponent(componentFactory, 0, injector);
this.entityComponent = componentRef.instance; this.entityComponent = componentRef.instance;
this.entityComponent.isEdit = true; this.entityComponent.isEdit = true;
this.entityComponent.entitiesTableConfig = this.entitiesTableConfig;
this.entityComponent.entity = this.entity;
this.detailsForm = this.entityComponent.entityNgForm; this.detailsForm = this.entityComponent.entityNgForm;
} }
helpLinkId(): string {
if (this.resources.helpLinkIdForEntity && this.entityComponent.entityForm) {
return this.resources.helpLinkIdForEntity(this.entityComponent.entityForm.getRawValue());
} else {
return this.resources.helpLinkId;
}
}
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form); const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid && this.submitted); const customErrorState = !!(control && control.invalid && this.submitted);

7
ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts

@ -22,12 +22,15 @@ import { AfterViewInit } from '@angular/core';
import { POSTAL_CODE_PATTERNS } from '@home/models/contact.models'; import { POSTAL_CODE_PATTERNS } from '@home/models/contact.models';
import { HasId } from '@shared/models/base-data'; import { HasId } from '@shared/models/base-data';
import {EntityComponent} from './entity.component'; import {EntityComponent} from './entity.component';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
export abstract class ContactBasedComponent<T extends ContactBased<HasId>> extends EntityComponent<T> implements AfterViewInit { export abstract class ContactBasedComponent<T extends ContactBased<HasId>> extends EntityComponent<T> implements AfterViewInit {
protected constructor(protected store: Store<AppState>, protected constructor(protected store: Store<AppState>,
protected fb: FormBuilder) { protected fb: FormBuilder,
super(store); protected entityValue: T,
protected entitiesTableConfig: EntityTableConfig<T>) {
super(store, fb, entityValue, entitiesTableConfig);
} }
buildForm(entity: T): FormGroup { buildForm(entity: T): FormGroup {

10
ui-ngx/src/app/modules/home/components/entity/entities-table.component.html

@ -135,7 +135,7 @@
</mat-toolbar> </mat-toolbar>
<div fxFlex class="table-container"> <div fxFlex class="table-container">
<table mat-table [dataSource]="dataSource" [trackBy]="trackByEntityId" <table mat-table [dataSource]="dataSource" [trackBy]="trackByEntityId"
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> matSort [matSortActive]="pageLink.sortOrder?.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<ng-container matColumnDef="select" sticky> <ng-container matColumnDef="select" sticky>
<mat-header-cell *matHeaderCellDef style="width: 30px;"> <mat-header-cell *matHeaderCellDef style="width: 30px;">
<mat-checkbox (change)="$event ? dataSource.masterToggle() : null" <mat-checkbox (change)="$event ? dataSource.masterToggle() : null"
@ -153,8 +153,11 @@
</ng-container> </ng-container>
<ng-container [matColumnDef]="column.key" *ngFor="let column of entityColumns; trackBy: trackByColumnKey;"> <ng-container [matColumnDef]="column.key" *ngFor="let column of entityColumns; trackBy: trackByColumnKey;">
<mat-header-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}" <mat-header-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}"
*matHeaderCellDef [ngStyle]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable"> {{ column.title | translate }} </mat-header-cell> [fxHide.lt-lg]="column.mobileHide"
*matHeaderCellDef [ngStyle]="headerCellStyle(column)" mat-sort-header [disabled]="!column.sortable">
{{ column.ignoreTranslate ? column.title : (column.title | translate) }} </mat-header-cell>
<mat-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}" <mat-cell [ngClass]="{'mat-number-cell': column.isNumberColumn}"
[fxHide.lt-lg]="column.mobileHide"
*matCellDef="let entity; let row = index" *matCellDef="let entity; let row = index"
[matTooltip]="cellTooltip(entity, column, row)" [matTooltip]="cellTooltip(entity, column, row)"
matTooltipPosition="above" matTooltipPosition="above"
@ -186,8 +189,7 @@
maxWidth: (cellActionDescriptors.length * 40) + 'px', maxWidth: (cellActionDescriptors.length * 40) + 'px',
width: (cellActionDescriptors.length * 40) + 'px' }"> width: (cellActionDescriptors.length * 40) + 'px' }">
<div fxHide fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end"> <div fxHide fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end">
<button mat-icon-button [disabled]="isLoading$ | async" <button mat-icon-button [disabled]="(isLoading$ | async) || !actionDescriptor.isEnabled(entity)"
[style.visibility]="actionDescriptor.isEnabled(entity) ? 'visible' : 'hidden'"
*ngFor="let actionDescriptor of cellActionDescriptors" *ngFor="let actionDescriptor of cellActionDescriptors"
matTooltip="{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}" matTooltip="{{ actionDescriptor.nameFunction ? actionDescriptor.nameFunction(entity) : actionDescriptor.name }}"
matTooltipPosition="above" matTooltipPosition="above"

78
ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts

@ -20,8 +20,8 @@ import {
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
ElementRef, ElementRef,
Input, Input, OnChanges,
OnInit, OnInit, SimpleChanges,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -51,7 +51,7 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { AddEntityDialogComponent } from './add-entity-dialog.component'; import { AddEntityDialogComponent } from './add-entity-dialog.component';
import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models';
import { historyInterval, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; import { DAY, historyInterval, HistoryWindowType, Timewindow } from '@shared/models/time/time.models';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
import { isDefined, isUndefined } from '@core/utils'; import { isDefined, isUndefined } from '@core/utils';
@ -62,7 +62,7 @@ import { isDefined, isUndefined } from '@core/utils';
styleUrls: ['./entities-table.component.scss'], styleUrls: ['./entities-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class EntitiesTableComponent extends PageComponent implements AfterViewInit, OnInit { export class EntitiesTableComponent extends PageComponent implements AfterViewInit, OnInit, OnChanges {
@Input() @Input()
entitiesTableConfig: EntityTableConfig<BaseData<HasId>>; entitiesTableConfig: EntityTableConfig<BaseData<HasId>>;
@ -112,7 +112,27 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
} }
ngOnInit() { ngOnInit() {
this.entitiesTableConfig = this.entitiesTableConfig || this.route.snapshot.data.entitiesTableConfig; if (this.entitiesTableConfig) {
this.init(this.entitiesTableConfig);
} else {
this.init(this.route.snapshot.data.entitiesTableConfig);
}
}
ngOnChanges(changes: SimpleChanges): void {
for (const propName of Object.keys(changes)) {
const change = changes[propName];
if (!change.firstChange && change.currentValue !== change.previousValue) {
if (propName === 'entitiesTableConfig' && change.currentValue) {
this.init(change.currentValue);
}
}
}
}
private init(entitiesTableConfig: EntityTableConfig<BaseData<HasId>>) {
this.isDetailsOpen = false;
this.entitiesTableConfig = entitiesTableConfig;
if (this.entitiesTableConfig.headerComponent) { if (this.entitiesTableConfig.headerComponent) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.headerComponent); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.headerComponent);
const viewContainerRef = this.entityTableHeaderAnchor.viewContainerRef; const viewContainerRef = this.entityTableHeaderAnchor.viewContainerRef;
@ -138,17 +158,16 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
onAction: ($event, entity) => this.deleteEntity($event, entity) onAction: ($event, entity) => this.deleteEntity($event, entity)
} }
); );
this.groupActionDescriptors.push(
{
name: this.translate.instant('action.delete'),
icon: 'delete',
isEnabled: true,
onAction: ($event, entities) => this.deleteEntities($event, entities)
}
);
} }
this.groupActionDescriptors.push(
{
name: this.translate.instant('action.delete'),
icon: 'delete',
isEnabled: this.entitiesTableConfig.entitiesDeleteEnabled,
onAction: ($event, entities) => this.deleteEntities($event, entities)
}
);
const enabledGroupActionDescriptors = const enabledGroupActionDescriptors =
this.groupActionDescriptors.filter((descriptor) => descriptor.isEnabled); this.groupActionDescriptors.filter((descriptor) => descriptor.isEnabled);
@ -156,24 +175,25 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
this.columnsUpdated(); this.columnsUpdated();
const sortOrder: SortOrder = { property: this.entitiesTableConfig.defaultSortOrder.property, let sortOrder: SortOrder = null;
direction: this.entitiesTableConfig.defaultSortOrder.direction }; if (this.entitiesTableConfig.defaultSortOrder) {
sortOrder = {
property: this.entitiesTableConfig.defaultSortOrder.property,
direction: this.entitiesTableConfig.defaultSortOrder.direction
};
}
if (this.entitiesTableConfig.useTimePageLink) { if (this.entitiesTableConfig.useTimePageLink) {
this.timewindow = historyInterval(24 * 60 * 60 * 1000); this.timewindow = historyInterval(DAY);
const currentTime = Date.now(); const currentTime = Date.now();
this.pageLink = new TimePageLink(10, 0, null, sortOrder, this.pageLink = new TimePageLink(10, 0, null, sortOrder,
currentTime - this.timewindow.history.timewindowMs, currentTime); currentTime - this.timewindow.history.timewindowMs, currentTime);
} else { } else {
this.pageLink = new PageLink(10, 0, null, sortOrder); this.pageLink = new PageLink(10, 0, null, sortOrder);
} }
this.dataSource = new EntitiesDataSource<BaseData<HasId>>( this.dataSource = this.entitiesTableConfig.dataSource(() => {
this.entitiesTableConfig.entitiesFetchFunction, this.dataLoaded();
this.entitiesTableConfig.entitySelectionEnabled, });
() => {
this.dataLoaded();
}
);
if (this.entitiesTableConfig.onLoadAction) { if (this.entitiesTableConfig.onLoadAction) {
this.entitiesTableConfig.onLoadAction(this.route); this.entitiesTableConfig.onLoadAction(this.route);
} }
@ -214,8 +234,14 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn
} }
this.pageLink.page = this.paginator.pageIndex; this.pageLink.page = this.paginator.pageIndex;
this.pageLink.pageSize = this.paginator.pageSize; this.pageLink.pageSize = this.paginator.pageSize;
this.pageLink.sortOrder.property = this.sort.active; if (this.sort.active) {
this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; this.pageLink.sortOrder = {
property: this.sort.active,
direction: Direction[this.sort.direction.toUpperCase()]
};
} else {
this.pageLink.sortOrder = null;
}
if (this.entitiesTableConfig.useTimePageLink) { if (this.entitiesTableConfig.useTimePageLink) {
const timePageLink = this.pageLink as TimePageLink; const timePageLink = this.pageLink as TimePageLink;
if (this.timewindow.history.historyType === HistoryWindowType.LAST_INTERVAL) { if (this.timewindow.history.historyType === HistoryWindowType.LAST_INTERVAL) {

4
ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.html

@ -25,9 +25,9 @@
(applyDetails)="saveEntity()" (applyDetails)="saveEntity()"
[theForm]="detailsForm"> [theForm]="detailsForm">
<div class="details-buttons"> <div class="details-buttons">
<div [tb-help]="resources.helpLinkId"></div> <div [tb-help]="helpLinkId()"></div>
</div> </div>
<mat-tab-group class="tb-absolute-fill" [ngClass]="{'tb-headless': isEditValue}" fxFlex [(selectedIndex)]="selectedTab"> <mat-tab-group class="tb-absolute-fill" [ngClass]="{'tb-headless': hideDetailsTabs()}" fxFlex [(selectedIndex)]="selectedTab">
<mat-tab label="{{ 'details.details' | translate }}"> <mat-tab label="{{ 'details.details' | translate }}">
<tb-anchor #entityDetailsForm></tb-anchor> <tb-anchor #entityDetailsForm></tb-anchor>
</mat-tab> </mat-tab>

55
ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts

@ -16,10 +16,10 @@
import { import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy, ChangeDetectionStrategy, ChangeDetectorRef,
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
EventEmitter, EventEmitter, Injector,
Input, Input,
OnDestroy, OnDestroy,
OnInit, OnInit,
@ -41,6 +41,7 @@ import { EntityAction } from '@home/models/entity/entity-component.models';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { MatTab, MatTabGroup } from '@angular/material/tabs'; import { MatTab, MatTabGroup } from '@angular/material/tabs';
import { EntityTabsComponent } from '@home/components/entity/entity-tabs.component'; import { EntityTabsComponent } from '@home/components/entity/entity-tabs.component';
import { deepClone } from '@core/utils';
@Component({ @Component({
selector: 'tb-entity-details-panel', selector: 'tb-entity-details-panel',
@ -79,13 +80,16 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
@ViewChildren(MatTab) inclusiveTabs: QueryList<MatTab>; @ViewChildren(MatTab) inclusiveTabs: QueryList<MatTab>;
translations: EntityTypeTranslation; translations: EntityTypeTranslation;
resources: EntityTypeResource; resources: EntityTypeResource<BaseData<HasId>>;
entity: BaseData<HasId>; entity: BaseData<HasId>;
editingEntity: BaseData<HasId>;
private currentEntityId: HasId; private currentEntityId: HasId;
private entityActionSubscription: Subscription; private entityActionSubscription: Subscription;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private injector: Injector,
private cd: ChangeDetectorRef,
private componentFactoryResolver: ComponentFactoryResolver) { private componentFactoryResolver: ComponentFactoryResolver) {
super(store); super(store);
} }
@ -127,15 +131,32 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.entityComponent);
const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef; const viewContainerRef = this.entityDetailsFormAnchor.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory); const injector: Injector = Injector.create(
{
providers: [
{
provide: 'entity',
useValue: this.entity
},
{
provide: 'entitiesTableConfig',
useValue: this.entitiesTableConfig
}
],
parent: this.injector
}
);
const componentRef = viewContainerRef.createComponent(componentFactory, 0, injector);
this.entityComponent = componentRef.instance; this.entityComponent = componentRef.instance;
this.entityComponent.isEdit = this.isEdit; this.entityComponent.isEdit = this.isEdit;
this.entityComponent.entitiesTableConfig = this.entitiesTableConfig;
this.detailsForm = this.entityComponent.entityNgForm; this.detailsForm = this.entityComponent.entityNgForm;
this.entityActionSubscription = this.entityComponent.entityAction.subscribe((action) => { this.entityActionSubscription = this.entityComponent.entityAction.subscribe((action) => {
this.entityAction.emit(action); this.entityAction.emit(action);
}); });
this.buildEntityTabsComponent(); this.buildEntityTabsComponent();
this.entityComponent.entityForm.valueChanges.subscribe(() => {
this.cd.detectChanges();
});
} }
buildEntityTabsComponent() { buildEntityTabsComponent() {
@ -147,9 +168,14 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
this.entityTabsComponent = componentTabsRef.instance; this.entityTabsComponent = componentTabsRef.instance;
this.entityTabsComponent.isEdit = this.isEdit; this.entityTabsComponent.isEdit = this.isEdit;
this.entityTabsComponent.entitiesTableConfig = this.entitiesTableConfig; this.entityTabsComponent.entitiesTableConfig = this.entitiesTableConfig;
this.entityTabsComponent.detailsForm = this.detailsForm;
} }
} }
hideDetailsTabs(): boolean {
return this.isEditValue && this.entitiesTableConfig.hideDetailsTabsOnEdit;
}
reload(): void { reload(): void {
this.isEdit = false; this.isEdit = false;
this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe( this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe(
@ -175,13 +201,28 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit
this.entityTabsComponent.entity = this.entity; this.entityTabsComponent.entity = this.entity;
} }
} else { } else {
this.selectedTab = 0; this.editingEntity = deepClone(this.entity);
this.entityComponent.entity = this.editingEntity;
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = this.editingEntity;
}
if (this.entitiesTableConfig.hideDetailsTabsOnEdit) {
this.selectedTab = 0;
}
}
}
helpLinkId(): string {
if (this.resources.helpLinkIdForEntity && this.entityComponent.entityForm) {
return this.resources.helpLinkIdForEntity(this.entityComponent.entityForm.getRawValue());
} else {
return this.resources.helpLinkId;
} }
} }
saveEntity() { saveEntity() {
if (this.detailsForm.valid) { if (this.detailsForm.valid) {
const editingEntity = {...this.entity, ...this.entityComponent.entityFormValue()}; const editingEntity = {...this.editingEntity, ...this.entityComponent.entityFormValue()};
this.entitiesTableConfig.saveEntity(editingEntity).subscribe( this.entitiesTableConfig.saveEntity(editingEntity).subscribe(
(entity) => { (entity) => {
this.entity = entity; this.entity = entity;

31
ui-ngx/src/app/modules/home/components/entity/entity-tabs.component.ts

@ -31,9 +31,15 @@ import { AuditLogMode } from '@shared/models/audit-log.models';
import { DebugEventType, EventType } from '@shared/models/event.models'; import { DebugEventType, EventType } from '@shared/models/event.models';
import { AttributeScope, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; import { AttributeScope, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
import { NULL_UUID } from '@shared/models/id/has-uuid'; import { NULL_UUID } from '@shared/models/id/has-uuid';
import { NgForm } from '@angular/forms';
import { PageLink } from '@shared/models/page/page-link';
@Directive() @Directive()
export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends PageComponent implements OnInit, AfterViewInit { export abstract class EntityTabsComponent<T extends BaseData<HasId>,
P extends PageLink = PageLink,
L extends BaseData<HasId> = T,
C extends EntityTableConfig<T, P, L> = EntityTableConfig<T, P, L>>
extends PageComponent implements OnInit, AfterViewInit {
attributeScopes = AttributeScope; attributeScopes = AttributeScope;
latestTelemetryTypes = LatestTelemetry; latestTelemetryTypes = LatestTelemetry;
@ -54,6 +60,8 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
entityValue: T; entityValue: T;
entitiesTableConfigValue: C;
@ViewChildren(MatTab) entityTabs: QueryList<MatTab>; @ViewChildren(MatTab) entityTabs: QueryList<MatTab>;
isEditValue: boolean; isEditValue: boolean;
@ -69,7 +77,7 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
@Input() @Input()
set entity(entity: T) { set entity(entity: T) {
this.entityValue = entity; this.setEntity(entity);
} }
get entity(): T { get entity(): T {
@ -77,7 +85,16 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
} }
@Input() @Input()
entitiesTableConfig: EntityTableConfig<T>; set entitiesTableConfig(entitiesTableConfig: C) {
this.setEntitiesTableConfig(entitiesTableConfig);
}
get entitiesTableConfig(): C {
return this.entitiesTableConfigValue;
}
@Input()
detailsForm: NgForm;
private entityTabsSubject = new BehaviorSubject<Array<MatTab>>(null); private entityTabsSubject = new BehaviorSubject<Array<MatTab>>(null);
@ -100,4 +117,12 @@ export abstract class EntityTabsComponent<T extends BaseData<HasId>> extends Pag
); );
} }
protected setEntity(entity: T) {
this.entityValue = entity;
}
protected setEntitiesTableConfig(entitiesTableConfig: C) {
this.entitiesTableConfigValue = entitiesTableConfig;
}
} }

25
ui-ngx/src/app/modules/home/components/entity/entity.component.ts

@ -15,18 +15,22 @@
/// ///
import { BaseData, HasId } from '@shared/models/base-data'; import { BaseData, HasId } from '@shared/models/base-data';
import { FormGroup, NgForm } from '@angular/forms'; import { FormBuilder, FormGroup, NgForm } from '@angular/forms';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { EventEmitter, Input, OnInit, Output, ViewChild, Directive } from '@angular/core'; import { EventEmitter, Input, OnInit, Output, ViewChild, Directive } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityAction } from '@home/models/entity/entity-component.models'; import { EntityAction } from '@home/models/entity/entity-component.models';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { PageLink } from '@shared/models/page/page-link';
@Directive() @Directive()
export abstract class EntityComponent<T extends BaseData<HasId>> extends PageComponent implements OnInit { export abstract class EntityComponent<T extends BaseData<HasId>,
P extends PageLink = PageLink,
L extends BaseData<HasId> = T,
C extends EntityTableConfig<T, P, L> = EntityTableConfig<T, P, L>>
extends PageComponent implements OnInit {
entityValue: T;
entityForm: FormGroup; entityForm: FormGroup;
@ViewChild('entityNgForm', {static: true}) entityNgForm: NgForm; @ViewChild('entityNgForm', {static: true}) entityNgForm: NgForm;
@ -51,7 +55,8 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom
set entity(entity: T) { set entity(entity: T) {
this.entityValue = entity; this.entityValue = entity;
if (this.entityForm) { if (this.entityForm) {
this.entityForm.reset(); this.entityForm.reset(undefined, {emitEvent: false});
this.entityForm.markAsPristine();
this.updateForm(entity); this.updateForm(entity);
} }
} }
@ -60,18 +65,18 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom
return this.entityValue; return this.entityValue;
} }
@Input()
entitiesTableConfig: EntityTableConfig<T>;
@Output() @Output()
entityAction = new EventEmitter<EntityAction<T>>(); entityAction = new EventEmitter<EntityAction<T>>();
protected constructor(protected store: Store<AppState>) { protected constructor(protected store: Store<AppState>,
protected fb: FormBuilder,
protected entityValue: T,
protected entitiesTableConfig: C) {
super(store); super(store);
this.entityForm = this.buildForm(this.entityValue);
} }
ngOnInit() { ngOnInit() {
this.entityForm = this.buildForm(this.entityValue);
} }
onEntityAction($event: Event, action: string) { onEntityAction($event: Event, action: string) {
@ -96,7 +101,7 @@ export abstract class EntityComponent<T extends BaseData<HasId>> extends PageCom
} }
entityFormValue() { entityFormValue() {
const formValue = this.entityForm ? {...this.entityForm.value} : {}; const formValue = this.entityForm ? {...this.entityForm.getRawValue()} : {};
return this.prepareFormValue(formValue); return this.prepareFormValue(formValue);
} }

2
ui-ngx/src/app/modules/home/components/event/event-table-config.ts

@ -94,7 +94,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
noEntities: 'event.no-events-prompt' noEntities: 'event.no-events-prompt'
}; };
this.entityResources = { this.entityResources = {
} as EntityTypeResource; } as EntityTypeResource<Event>;
this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink); this.entitiesFetchFunction = pageLink => this.fetchEvents(pageLink);
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};

4
ui-ngx/src/app/modules/home/models/datasource/entity-datasource.ts

@ -36,8 +36,8 @@ export class EntitiesDataSource<T extends BaseData<HasId>, P extends PageLink =
public currentEntity: T = null; public currentEntity: T = null;
constructor(private fetchFunction: EntitiesFetchFunction<T, P>, constructor(private fetchFunction: EntitiesFetchFunction<T, P>,
private selectionEnabledFunction: EntityBooleanFunction<T>, protected selectionEnabledFunction: EntityBooleanFunction<T>,
private dataLoadedFunction: () => void) {} protected dataLoadedFunction: () => void) {}
connect(collectionViewer: CollectionViewer): Observable<T[] | ReadonlyArray<T>> { connect(collectionViewer: CollectionViewer): Observable<T[] | ReadonlyArray<T>> {
return this.entitiesSubject.asObservable(); return this.entitiesSubject.asObservable();

64
ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts

@ -14,21 +14,21 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {BaseData, HasId} from '@shared/models/base-data'; import { BaseData, HasId } from '@shared/models/base-data';
import {EntitiesFetchFunction} from '@home/models/datasource/entity-datasource'; import { EntitiesDataSource, EntitiesFetchFunction } from '@home/models/datasource/entity-datasource';
import {Observable, of} from 'rxjs'; import { Observable, of } from 'rxjs';
import {emptyPageData} from '@shared/models/page/page-data'; import { emptyPageData } from '@shared/models/page/page-data';
import {DatePipe} from '@angular/common'; import { DatePipe } from '@angular/common';
import {Direction, SortOrder} from '@shared/models/page/sort-order'; import { Direction, SortOrder } from '@shared/models/page/sort-order';
import {EntityType, EntityTypeResource, EntityTypeTranslation} from '@shared/models/entity-type.models'; import { EntityType, EntityTypeResource, EntityTypeTranslation } from '@shared/models/entity-type.models';
import {EntityComponent} from '@home/components/entity/entity.component'; import { EntityComponent } from '@home/components/entity/entity.component';
import {Type} from '@angular/core'; import { Type } from '@angular/core';
import {EntityAction} from './entity-component.models'; import { EntityAction } from './entity-component.models';
import {HasUUID} from '@shared/models/id/has-uuid'; import { HasUUID } from '@shared/models/id/has-uuid';
import {PageLink} from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import {EntitiesTableComponent} from '@home/components/entity/entities-table.component'; import { EntitiesTableComponent } from '@home/components/entity/entities-table.component';
import {EntityTableHeaderComponent} from '@home/components/entity/entity-table-header.component'; import { EntityTableHeaderComponent } from '@home/components/entity/entity-table-header.component';
import {ActivatedRoute} from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; import { EntityTabsComponent } from '../../components/entity/entity-tabs.component';
export type EntityBooleanFunction<T extends BaseData<HasId>> = (entity: T) => boolean; export type EntityBooleanFunction<T extends BaseData<HasId>> = (entity: T) => boolean;
@ -76,7 +76,9 @@ export class BaseEntityTableColumn<T extends BaseData<HasId>> {
public key: string, public key: string,
public title: string, public title: string,
public width: string = '0px', public width: string = '0px',
public sortable: boolean = true) { public sortable: boolean = true,
public ignoreTranslate: boolean = false,
public mobileHide: boolean = false) {
} }
} }
@ -120,7 +122,7 @@ export class DateEntityTableColumn<T extends BaseData<HasId>> extends EntityTabl
export type EntityColumn<T extends BaseData<HasId>> = EntityTableColumn<T> | EntityActionTableColumn<T>; export type EntityColumn<T extends BaseData<HasId>> = EntityTableColumn<T> | EntityActionTableColumn<T>;
export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = PageLink> { export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = PageLink, L extends BaseData<HasId> = T> {
constructor() {} constructor() {}
@ -137,31 +139,35 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
addEnabled = true; addEnabled = true;
entitiesDeleteEnabled = true; entitiesDeleteEnabled = true;
detailsPanelEnabled = true; detailsPanelEnabled = true;
hideDetailsTabsOnEdit = true;
actionsColumnTitle = null; actionsColumnTitle = null;
entityTranslations: EntityTypeTranslation; entityTranslations: EntityTypeTranslation;
entityResources: EntityTypeResource; entityResources: EntityTypeResource<T>;
entityComponent: Type<EntityComponent<T>>; entityComponent: Type<EntityComponent<T, P, L>>;
entityTabsComponent: Type<EntityTabsComponent<T>>; entityTabsComponent: Type<EntityTabsComponent<T, P, L>>;
addDialogStyle = {}; addDialogStyle = {};
defaultSortOrder: SortOrder = {property: 'createdTime', direction: Direction.ASC}; defaultSortOrder: SortOrder = {property: 'createdTime', direction: Direction.ASC};
columns: Array<EntityColumn<T>> = []; columns: Array<EntityColumn<L>> = [];
cellActionDescriptors: Array<CellActionDescriptor<T>> = []; cellActionDescriptors: Array<CellActionDescriptor<L>> = [];
groupActionDescriptors: Array<GroupActionDescriptor<T>> = []; groupActionDescriptors: Array<GroupActionDescriptor<L>> = [];
headerActionDescriptors: Array<HeaderActionDescriptor> = []; headerActionDescriptors: Array<HeaderActionDescriptor> = [];
addActionDescriptors: Array<HeaderActionDescriptor> = []; addActionDescriptors: Array<HeaderActionDescriptor> = [];
headerComponent: Type<EntityTableHeaderComponent<T>>; headerComponent: Type<EntityTableHeaderComponent<L>>;
addEntity: CreateEntityOperation<T> = null; addEntity: CreateEntityOperation<T> = null;
dataSource: (dataLoadedFunction: () => void) => EntitiesDataSource<L> = (dataLoadedFunction: () => void) => {
return new EntitiesDataSource(this.entitiesFetchFunction, this.entitySelectionEnabled, dataLoadedFunction);
};
detailsReadonly: EntityBooleanFunction<T> = () => false; detailsReadonly: EntityBooleanFunction<T> = () => false;
entitySelectionEnabled: EntityBooleanFunction<T> = () => true; entitySelectionEnabled: EntityBooleanFunction<L> = () => true;
deleteEnabled: EntityBooleanFunction<T> = () => true; deleteEnabled: EntityBooleanFunction<T | L> = () => true;
deleteEntityTitle: EntityStringFunction<T> = () => ''; deleteEntityTitle: EntityStringFunction<L> = () => '';
deleteEntityContent: EntityStringFunction<T> = () => ''; deleteEntityContent: EntityStringFunction<L> = () => '';
deleteEntitiesTitle: EntityCountStringFunction = () => ''; deleteEntitiesTitle: EntityCountStringFunction = () => '';
deleteEntitiesContent: EntityCountStringFunction = () => ''; deleteEntitiesContent: EntityCountStringFunction = () => '';
loadEntity: EntityByIdOperation<T> = () => of(); loadEntity: EntityByIdOperation<T> = () => of();
saveEntity: EntityTwoWayOperation<T> = (entity) => of(entity); saveEntity: EntityTwoWayOperation<T> = (entity) => of(entity);
deleteEntity: EntityIdOneWayOperation = () => of(); deleteEntity: EntityIdOneWayOperation = () => of();
entitiesFetchFunction: EntitiesFetchFunction<T, P> = () => of(emptyPageData<T>()); entitiesFetchFunction: EntitiesFetchFunction<L, P> = () => of(emptyPageData<L>());
onEntityAction: EntityActionFunction<T> = () => false; onEntityAction: EntityActionFunction<T> = () => false;
entityTitle: EntityStringFunction<T> = (entity) => entity?.name; entityTitle: EntityStringFunction<T> = (entity) => entity?.name;
} }

7
ui-ngx/src/app/modules/home/pages/asset/asset.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component'; import { EntityComponent } from '../../components/entity/entity.component';
@ -24,6 +24,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid';
import { ActionNotificationShow } from '@core/notification/notification.actions'; import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { AssetInfo } from '@app/shared/models/asset.models'; import { AssetInfo } from '@app/shared/models/asset.models';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-asset', selector: 'tb-asset',
@ -38,8 +39,10 @@ export class AssetComponent extends EntityComponent<AssetInfo> {
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
@Inject('entity') protected entityValue: AssetInfo,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<AssetInfo>,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store, fb, entityValue, entitiesTableConfig);
} }
ngOnInit() { ngOnInit() {

2
ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts

@ -124,7 +124,7 @@ const routes: Routes = [
breadcrumb: { breadcrumb: {
labelFunction: dashboardBreadcumbLabelFunction, labelFunction: dashboardBreadcumbLabelFunction,
icon: 'dashboard' icon: 'dashboard'
} as BreadCrumbConfig, } as BreadCrumbConfig<DashboardPageComponent>,
auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
title: 'customer.dashboard', title: 'customer.dashboard',
widgetEditMode: false widgetEditMode: false

7
ui-ngx/src/app/modules/home/pages/customer/customer.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@ -22,6 +22,7 @@ import { Customer } from '@shared/models/customer.model';
import { ActionNotificationShow } from '@app/core/notification/notification.actions'; import { ActionNotificationShow } from '@app/core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ContactBasedComponent } from '../../components/entity/contact-based.component'; import { ContactBasedComponent } from '../../components/entity/contact-based.component';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-customer', selector: 'tb-customer',
@ -33,8 +34,10 @@ export class CustomerComponent extends ContactBasedComponent<Customer> {
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
@Inject('entity') protected entityValue: Customer,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<Customer>,
protected fb: FormBuilder) { protected fb: FormBuilder) {
super(store, fb); super(store, fb, entityValue, entitiesTableConfig);
} }
hideDelete() { hideDelete() {

13
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component'; import { EntityComponent } from '../../components/entity/entity.component';
@ -28,6 +28,7 @@ import {
isPublicDashboard isPublicDashboard
} from '@shared/models/dashboard.models'; } from '@shared/models/dashboard.models';
import { DashboardService } from '@core/http/dashboard.service'; import { DashboardService } from '@core/http/dashboard.service';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-dashboard-form', selector: 'tb-dashboard-form',
@ -45,8 +46,10 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> {
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
private dashboardService: DashboardService, private dashboardService: DashboardService,
@Inject('entity') protected entityValue: Dashboard,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<Dashboard>,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store, fb, entityValue, entitiesTableConfig);
} }
ngOnInit() { ngOnInit() {
@ -103,7 +106,9 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> {
} }
private updateFields(entity: Dashboard): void { private updateFields(entity: Dashboard): void {
this.assignedCustomersText = getDashboardAssignedCustomersText(entity); if (entity) {
this.publicLink = this.dashboardService.getPublicDashboardLink(entity); this.assignedCustomersText = getDashboardAssignedCustomersText(entity);
this.publicLink = this.dashboardService.getPublicDashboardLink(entity);
}
} }
} }

5
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts

@ -43,7 +43,8 @@ export class DashboardResolver implements Resolve<Dashboard> {
} }
} }
export const dashboardBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => component.dashboard.title); export const dashboardBreadcumbLabelFunction: BreadCrumbLabelFunction<DashboardPageComponent>
= ((route, translate, component) => component.dashboard.title);
const routes: Routes = [ const routes: Routes = [
{ {
@ -74,7 +75,7 @@ const routes: Routes = [
breadcrumb: { breadcrumb: {
labelFunction: dashboardBreadcumbLabelFunction, labelFunction: dashboardBreadcumbLabelFunction,
icon: 'dashboard' icon: 'dashboard'
} as BreadCrumbConfig, } as BreadCrumbConfig<DashboardPageComponent>,
auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER],
title: 'dashboard.dashboard', title: 'dashboard.dashboard',
widgetEditMode: false widgetEditMode: false

7
ui-ngx/src/app/modules/home/pages/device/device.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component'; import { EntityComponent } from '../../components/entity/entity.component';
@ -26,6 +26,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions'
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DeviceService } from '@core/http/device.service'; import { DeviceService } from '@core/http/device.service';
import { ClipboardService } from 'ngx-clipboard'; import { ClipboardService } from 'ngx-clipboard';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-device', selector: 'tb-device',
@ -42,8 +43,10 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
protected translate: TranslateService, protected translate: TranslateService,
private deviceService: DeviceService, private deviceService: DeviceService,
private clipboardService: ClipboardService, private clipboardService: ClipboardService,
@Inject('entity') protected entityValue: DeviceInfo,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<DeviceInfo>,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store, fb, entityValue, entitiesTableConfig);
} }
ngOnInit() { ngOnInit() {

7
ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component'; import { EntityComponent } from '../../components/entity/entity.component';
@ -27,6 +27,7 @@ import { EntityViewInfo } from '@app/shared/models/entity-view.models';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { EntityId } from '@app/shared/models/id/entity-id'; import { EntityId } from '@app/shared/models/id/entity-id';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-entity-view', selector: 'tb-entity-view',
@ -50,8 +51,10 @@ export class EntityViewComponent extends EntityComponent<EntityViewInfo> {
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
@Inject('entity') protected entityValue: EntityViewInfo,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<EntityViewInfo>,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store, fb, entityValue, entitiesTableConfig);
} }
ngOnInit() { ngOnInit() {

10
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-routing.module.ts

@ -126,7 +126,8 @@ export class RuleChainImportGuard implements CanActivate {
} }
export const ruleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => { export const ruleChainBreadcumbLabelFunction: BreadCrumbLabelFunction<RuleChainPageComponent>
= ((route, translate, component) => {
let label: string = component.ruleChain.name; let label: string = component.ruleChain.name;
if (component.ruleChain.root) { if (component.ruleChain.root) {
label += ` (${translate.instant('rulechain.root')})`; label += ` (${translate.instant('rulechain.root')})`;
@ -134,7 +135,8 @@ export const ruleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route,
return label; return label;
}); });
export const importRuleChainBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => { export const importRuleChainBreadcumbLabelFunction: BreadCrumbLabelFunction<RuleChainPageComponent> =
((route, translate, component) => {
return `${translate.instant('rulechain.import')}: ${component.ruleChain.name}`; return `${translate.instant('rulechain.import')}: ${component.ruleChain.name}`;
}); });
@ -167,7 +169,7 @@ const routes: Routes = [
breadcrumb: { breadcrumb: {
labelFunction: ruleChainBreadcumbLabelFunction, labelFunction: ruleChainBreadcumbLabelFunction,
icon: 'settings_ethernet' icon: 'settings_ethernet'
} as BreadCrumbConfig, } as BreadCrumbConfig<RuleChainPageComponent>,
auth: [Authority.TENANT_ADMIN], auth: [Authority.TENANT_ADMIN],
title: 'rulechain.rulechain', title: 'rulechain.rulechain',
import: false import: false
@ -187,7 +189,7 @@ const routes: Routes = [
breadcrumb: { breadcrumb: {
labelFunction: importRuleChainBreadcumbLabelFunction, labelFunction: importRuleChainBreadcumbLabelFunction,
icon: 'settings_ethernet' icon: 'settings_ethernet'
} as BreadCrumbConfig, } as BreadCrumbConfig<RuleChainPageComponent>,
auth: [Authority.TENANT_ADMIN], auth: [Authority.TENANT_ADMIN],
title: 'rulechain.rulechain', title: 'rulechain.rulechain',
import: true import: true

7
ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component'; import { EntityComponent } from '../../components/entity/entity.component';
@ -22,6 +22,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActionNotificationShow } from '@core/notification/notification.actions'; import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { RuleChain } from '@shared/models/rule-chain.models'; import { RuleChain } from '@shared/models/rule-chain.models';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-rulechain', selector: 'tb-rulechain',
@ -32,8 +33,10 @@ export class RuleChainComponent extends EntityComponent<RuleChain> {
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
@Inject('entity') protected entityValue: RuleChain,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<RuleChain>,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store, fb, entityValue, entitiesTableConfig);
} }
hideDelete() { hideDelete() {

7
ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@ -23,6 +23,7 @@ import {Tenant} from '@app/shared/models/tenant.model';
import {ActionNotificationShow} from '@app/core/notification/notification.actions'; import {ActionNotificationShow} from '@app/core/notification/notification.actions';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {ContactBasedComponent} from '../../components/entity/contact-based.component'; import {ContactBasedComponent} from '../../components/entity/contact-based.component';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-tenant', selector: 'tb-tenant',
@ -32,8 +33,10 @@ export class TenantComponent extends ContactBasedComponent<Tenant> {
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
@Inject('entity') protected entityValue: Tenant,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<Tenant>,
protected fb: FormBuilder) { protected fb: FormBuilder) {
super(store, fb); super(store, fb, entityValue, entitiesTableConfig);
} }
hideDelete() { hideDelete() {

7
ui-ngx/src/app/modules/home/pages/user/user.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component'; import { EntityComponent } from '../../components/entity/entity.component';
@ -24,6 +24,7 @@ import { selectAuth } from '@core/auth/auth.selectors';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Authority } from '@shared/models/authority.enum'; import { Authority } from '@shared/models/authority.enum';
import { isUndefined } from '@core/utils'; import { isUndefined } from '@core/utils';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-user', selector: 'tb-user',
@ -40,8 +41,10 @@ export class UserComponent extends EntityComponent<User> {
); );
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
@Inject('entity') protected entityValue: User,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<User>,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store, fb, entityValue, entitiesTableConfig);
} }
hideDelete() { hideDelete() {

13
ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts

@ -114,10 +114,11 @@ export class WidgetEditorAddDataResolver implements Resolve<WidgetEditorData> {
} }
} }
export const widgetTypesBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate) => route.data.widgetsBundle.title); export const widgetTypesBreadcumbLabelFunction: BreadCrumbLabelFunction<any> = ((route, translate) =>
route.data.widgetsBundle.title);
export const widgetEditorBreadcumbLabelFunction: BreadCrumbLabelFunction = export const widgetEditorBreadcumbLabelFunction: BreadCrumbLabelFunction<WidgetEditorComponent> =
((route, translate, component) => component ? (component as WidgetEditorComponent).widget.widgetName : ''); ((route, translate, component) => component ? component.widget.widgetName : '');
export const routes: Routes = [ export const routes: Routes = [
{ {
@ -146,7 +147,7 @@ export const routes: Routes = [
breadcrumb: { breadcrumb: {
labelFunction: widgetTypesBreadcumbLabelFunction, labelFunction: widgetTypesBreadcumbLabelFunction,
icon: 'now_widgets' icon: 'now_widgets'
} as BreadCrumbConfig } as BreadCrumbConfig<any>
}, },
resolve: { resolve: {
widgetsBundle: WidgetsBundleResolver widgetsBundle: WidgetsBundleResolver
@ -173,7 +174,7 @@ export const routes: Routes = [
breadcrumb: { breadcrumb: {
labelFunction: widgetEditorBreadcumbLabelFunction, labelFunction: widgetEditorBreadcumbLabelFunction,
icon: 'insert_chart' icon: 'insert_chart'
} as BreadCrumbConfig } as BreadCrumbConfig<WidgetEditorComponent>
}, },
resolve: { resolve: {
widgetEditorData: WidgetEditorDataResolver widgetEditorData: WidgetEditorDataResolver
@ -189,7 +190,7 @@ export const routes: Routes = [
breadcrumb: { breadcrumb: {
labelFunction: widgetEditorBreadcumbLabelFunction, labelFunction: widgetEditorBreadcumbLabelFunction,
icon: 'insert_chart' icon: 'insert_chart'
} as BreadCrumbConfig } as BreadCrumbConfig<WidgetEditorComponent>
}, },
resolve: { resolve: {
widgetEditorData: WidgetEditorAddDataResolver widgetEditorData: WidgetEditorAddDataResolver

7
ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts

@ -14,12 +14,13 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {Component} from '@angular/core'; import { Component, Inject } from '@angular/core';
import {Store} from '@ngrx/store'; import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state'; import {AppState} from '@core/core.state';
import {EntityComponent} from '../../components/entity/entity.component'; import {EntityComponent} from '../../components/entity/entity.component';
import {FormBuilder, FormGroup, Validators} from '@angular/forms'; import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {WidgetsBundle} from '@shared/models/widgets-bundle.model'; import {WidgetsBundle} from '@shared/models/widgets-bundle.model';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({ @Component({
selector: 'tb-widgets-bundle', selector: 'tb-widgets-bundle',
@ -29,8 +30,10 @@ import {WidgetsBundle} from '@shared/models/widgets-bundle.model';
export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> { export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> {
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
@Inject('entity') protected entityValue: WidgetsBundle,
@Inject('entitiesTableConfig') protected entitiesTableConfig: EntityTableConfig<WidgetsBundle>,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store, fb, entityValue, entitiesTableConfig);
} }
hideDelete() { hideDelete() {

2
ui-ngx/src/app/shared/components/breadcrumb.component.ts

@ -65,7 +65,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy {
buildBreadCrumbs(route: ActivatedRouteSnapshot, breadcrumbs: Array<BreadCrumb> = []): Array<BreadCrumb> { buildBreadCrumbs(route: ActivatedRouteSnapshot, breadcrumbs: Array<BreadCrumb> = []): Array<BreadCrumb> {
let newBreadcrumbs = breadcrumbs; let newBreadcrumbs = breadcrumbs;
if (route.routeConfig && route.routeConfig.data) { if (route.routeConfig && route.routeConfig.data) {
const breadcrumbConfig = route.routeConfig.data.breadcrumb as BreadCrumbConfig; const breadcrumbConfig = route.routeConfig.data.breadcrumb as BreadCrumbConfig<any>;
if (breadcrumbConfig && !breadcrumbConfig.skip) { if (breadcrumbConfig && !breadcrumbConfig.skip) {
let label; let label;
let labelFunction; let labelFunction;

6
ui-ngx/src/app/shared/components/breadcrumb.ts

@ -27,10 +27,10 @@ export interface BreadCrumb {
queryParams: Params; queryParams: Params;
} }
export type BreadCrumbLabelFunction = (route: ActivatedRouteSnapshot, translate: TranslateService, component: any) => string; export type BreadCrumbLabelFunction<C> = (route: ActivatedRouteSnapshot, translate: TranslateService, component: C) => string;
export interface BreadCrumbConfig { export interface BreadCrumbConfig<C> {
labelFunction: BreadCrumbLabelFunction; labelFunction: BreadCrumbLabelFunction<C>;
label: string; label: string;
icon: string; icon: string;
skip: boolean; skip: boolean;

6
ui-ngx/src/app/shared/models/entity-type.models.ts

@ -15,6 +15,7 @@
/// ///
import {TenantId} from './id/tenant-id'; import {TenantId} from './id/tenant-id';
import { BaseData, HasId } from '@shared/models/base-data';
/// ///
/// Copyright © 2016-2019 The Thingsboard Authors /// Copyright © 2016-2019 The Thingsboard Authors
@ -63,8 +64,9 @@ export interface EntityTypeTranslation {
search?: string; search?: string;
} }
export interface EntityTypeResource { export interface EntityTypeResource<T> {
helpLinkId: string; helpLinkId: string;
helpLinkIdForEntity?(entity: T): string;
} }
export const entityTypeTranslations = new Map<EntityType | AliasEntityType, EntityTypeTranslation>( export const entityTypeTranslations = new Map<EntityType | AliasEntityType, EntityTypeTranslation>(
@ -223,7 +225,7 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
] ]
); );
export const entityTypeResources = new Map<EntityType, EntityTypeResource>( export const entityTypeResources = new Map<EntityType, EntityTypeResource<BaseData<HasId>>>(
[ [
[ [
EntityType.TENANT, EntityType.TENANT,

Loading…
Cancel
Save