From bd8af1111eecf4ddd0c708278a02ba685985eda0 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 29 Jan 2020 17:20:28 +0200 Subject: [PATCH] Implement Alarm widget --- ui-ngx/src/app/core/api/widget-api.models.ts | 2 + .../home/components/home-components.module.ts | 8 +- .../shared-home-components.module.ts | 38 ++++ .../alarm-status-filter-panel.component.html | 25 +++ .../alarm-status-filter-panel.component.scss | 36 ++++ .../alarm-status-filter-panel.component.ts | 42 +++++ .../lib/alarms-table-widget.component.ts | 171 +++++++++++++++++- .../widget/lib/table-widget.models.ts | 3 + .../widget/widget-component.service.ts | 25 ++- .../widget/widget-components.module.ts | 11 +- .../src/app/shared/models/page/page-link.ts | 16 +- ui-ngx/src/styles.scss | 5 + 12 files changed, 351 insertions(+), 31 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/shared-home-components.module.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index d9365a50cc..00d33cdde1 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -259,5 +259,7 @@ export interface IWidgetSubscription { destroy(): void; + update(): void; + [key: string]: any; } diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 5fce77ab5e..cdbd9d7763 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -30,7 +30,6 @@ import { RelationTableComponent } from '@home/components/relation/relation-table import { RelationDialogComponent } from './relation/relation-dialog.component'; import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component'; import { AlarmTableComponent } from '@home/components/alarm/alarm-table.component'; -import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component'; import { AttributeTableComponent } from '@home/components/attribute/attribute-table.component'; import { AddAttributeDialogComponent } from './attribute/add-attribute-dialog.component'; import { EditAttributeValuePanelComponent } from './attribute/edit-attribute-value-panel.component'; @@ -64,6 +63,7 @@ import { AddWidgetToDashboardDialogComponent } from './attribute/add-widget-to-d import { ImportDialogCsvComponent } from './import-export/import-dialog-csv.component'; import { TableColumnsAssignmentComponent } from './import-export/table-columns-assignment.component'; import { EventContentDialogComponent } from '@home/components/event/event-content-dialog.component'; +import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; @NgModule({ entryComponents: [ @@ -73,7 +73,6 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten EventTableHeaderComponent, RelationDialogComponent, AlarmTableHeaderComponent, - AlarmDetailsDialogComponent, AddAttributeDialogComponent, EditAttributeValuePanelComponent, AliasesEntitySelectPanelComponent, @@ -104,7 +103,6 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten RelationFiltersComponent, AlarmTableHeaderComponent, AlarmTableComponent, - AlarmDetailsDialogComponent, AttributeTableComponent, AddAttributeDialogComponent, EditAttributeValuePanelComponent, @@ -136,7 +134,8 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten ], imports: [ CommonModule, - SharedModule + SharedModule, + SharedHomeComponentsModule ], exports: [ EntitiesTableComponent, @@ -149,7 +148,6 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten RelationTableComponent, RelationFiltersComponent, AlarmTableComponent, - AlarmDetailsDialogComponent, AttributeTableComponent, AliasesEntitySelectComponent, EntityAliasesDialogComponent, diff --git a/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts b/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts new file mode 100644 index 0000000000..30616276c5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/shared-home-components.module.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@app/shared/shared.module'; +import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component'; + +@NgModule({ + entryComponents: [ + AlarmDetailsDialogComponent + ], + declarations: + [ + AlarmDetailsDialogComponent + ], + imports: [ + CommonModule, + SharedModule + ], + exports: [ + AlarmDetailsDialogComponent + ] +}) +export class SharedHomeComponentsModule { } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html new file mode 100644 index 0000000000..7c3b4e95d7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html @@ -0,0 +1,25 @@ + +
+ + + + {{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }} + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss new file mode 100644 index 0000000000..0ead8c7992 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2019 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + width: 100%; + height: 100%; + min-width: 300px; + overflow: hidden; + background: #fff; + border-radius: 4px; + box-shadow: + 0 7px 8px -4px rgba(0, 0, 0, .2), + 0 13px 19px 2px rgba(0, 0, 0, .14), + 0 5px 24px 4px rgba(0, 0, 0, .12); + + .mat-content { + overflow: hidden; + background-color: #fff; + } + + .mat-padding { + padding: 16px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts new file mode 100644 index 0000000000..a260c22b89 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts @@ -0,0 +1,42 @@ +/// +/// Copyright © 2016-2019 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, InjectionToken } from '@angular/core'; +import { IWidgetSubscription } from '@core/api/widget-api.models'; +import { AlarmSearchStatus, alarmSearchStatusTranslations } from '@shared/models/alarm.models'; + +export const ALARM_STATUS_FILTER_PANEL_DATA = new InjectionToken('AlarmStatusFilterPanelData'); + +export interface AlarmStatusFilterPanelData { + subscription: IWidgetSubscription; +} + +@Component({ + selector: 'tb-alarm-status-filter-panel', + templateUrl: './alarm-status-filter-panel.component.html', + styleUrls: ['./alarm-status-filter-panel.component.scss'] +}) +export class AlarmStatusFilterPanelComponent { + + subscription: IWidgetSubscription; + + alarmSearchStatuses = Object.keys(AlarmSearchStatus); + alarmSearchStatusTranslationMap = alarmSearchStatusTranslations; + + constructor(@Inject(ALARM_STATUS_FILTER_PANEL_DATA) public data: AlarmStatusFilterPanelData) { + this.subscription = this.data.subscription; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index 5ac66cbee0..7a143c80ac 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -39,7 +39,7 @@ import { PageLink } from '@shared/models/page/page-link'; import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order'; import { DataSource } from '@angular/cdk/typings/collections'; import { CollectionViewer, SelectionModel } from '@angular/cdk/collections'; -import { BehaviorSubject, fromEvent, merge, Observable, of } from 'rxjs'; +import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, of } from 'rxjs'; import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { entityTypeTranslations } from '@shared/models/entity-type.models'; import { catchError, debounceTime, distinctUntilChanged, map, take, tap } from 'rxjs/operators'; @@ -77,6 +77,19 @@ import { alarmStatusTranslations } from '@shared/models/alarm.models'; import { DatePipe } from '@angular/common'; +import { + ALARM_STATUS_FILTER_PANEL_DATA, + AlarmStatusFilterPanelComponent, + AlarmStatusFilterPanelData +} from '@home/components/widget/lib/alarm-status-filter-panel.component'; +import { + AlarmDetailsDialogComponent, + AlarmDetailsDialogData +} from '@home/components/alarm/alarm-details-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { DialogService } from '@core/services/dialog.service'; +import { AlarmService } from '@core/http/alarm.service'; interface AlarmsTableWidgetSettings extends TableWidgetSettings { alarmsTitle: string; @@ -172,7 +185,10 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, private utils: UtilsService, public translate: TranslateService, private domSanitizer: DomSanitizer, - private datePipe: DatePipe) { + private datePipe: DatePipe, + private dialog: MatDialog, + private dialogService: DialogService, + private alarmService: AlarmService) { super(store); const sortOrder: SortOrder = sortOrderFromString(this.defaultSortOrder); @@ -390,7 +406,36 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } private editAlarmStatusFilter($event: Event) { - // TODO: + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig(); + config.backdropClass = 'cdk-overlay-transparent-backdrop'; + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + const injectionTokens = new WeakMap([ + [ALARM_STATUS_FILTER_PANEL_DATA, { + subscription: this.subscription, + } as AlarmStatusFilterPanelData], + [OverlayRef, overlayRef] + ]); + const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens); + overlayRef.attach(new ComponentPortal(AlarmStatusFilterPanelComponent, + this.viewContainerRef, injector)); + this.ctx.detectChanges(); } private enterFilterMode() { @@ -538,35 +583,138 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, if ($event) { $event.stopPropagation(); } - // TODO: + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { + this.dialog.open + (AlarmDetailsDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + alarmId: alarm.id.id, + allowAcknowledgment: this.allowAcknowledgment, + allowClear: this.allowClear, + displayDetails: true + } + }).afterClosed().subscribe( + (res) => { + if (res) { + this.subscription.update(); + } + } + ); + } } private ackAlarm($event: Event, alarm: AlarmInfo) { if ($event) { $event.stopPropagation(); } - // TODO: + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { + this.dialogService.confirm( + this.translate.instant('alarm.aknowledge-alarm-title'), + this.translate.instant('alarm.aknowledge-alarm-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + this.alarmService.ackAlarm(alarm.id.id).subscribe(() => { + this.subscription.update(); + }); + } + } + }); + } } public ackAlarms($event: Event) { if ($event) { $event.stopPropagation(); } - // TODO: + if (this.alarmsDatasource.selection.hasValue()) { + const alarms = this.alarmsDatasource.selection.selected.filter( + (alarm) => { return alarm.id.id !== NULL_UUID } + ); + if (alarms.length) { + const title = this.translate.instant('alarm.aknowledge-alarms-title', {count: alarms.length}); + const content = this.translate.instant('alarm.aknowledge-alarms-text', {count: alarms.length}); + this.dialogService.confirm( + title, + content, + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + const tasks: Observable[] = []; + for (const alarm of alarms) { + tasks.push(this.alarmService.ackAlarm(alarm.id.id)); + } + forkJoin(tasks).subscribe(() => { + this.alarmsDatasource.clearSelection(); + this.subscription.update(); + }); + } + } + }); + } + } } private clearAlarm($event: Event, alarm: AlarmInfo) { if ($event) { $event.stopPropagation(); } - // TODO: + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { + this.dialogService.confirm( + this.translate.instant('alarm.clear-alarm-title'), + this.translate.instant('alarm.clear-alarm-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + this.alarmService.clearAlarm(alarm.id.id).subscribe(() => { + this.subscription.update(); + }); + } + } + }); + } } public clearAlarms($event: Event) { if ($event) { $event.stopPropagation(); } - // TODO: + if (this.alarmsDatasource.selection.hasValue()) { + const alarms = this.alarmsDatasource.selection.selected.filter( + (alarm) => { return alarm.id.id !== NULL_UUID } + ); + if (alarms.length) { + const title = this.translate.instant('alarm.clear-alarms-title', {count: alarms.length}); + const content = this.translate.instant('alarm.clear-alarms-text', {count: alarms.length}); + this.dialogService.confirm( + title, + content, + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + const tasks: Observable[] = []; + for (const alarm of alarms) { + tasks.push(this.alarmService.clearAlarm(alarm.id.id)); + } + forkJoin(tasks).subscribe(() => { + this.alarmsDatasource.clearSelection(); + this.subscription.update(); + }); + } + } + }); + } + } } private defaultContent(key: EntityColumn, value: any): any { @@ -722,6 +870,13 @@ class AlarmsDatasource implements DataSource { return this.selection.isSelected(alarm); } + clearSelection() { + if (this.selection.hasValue()) { + this.selection.clear(); + this.onSelectionModeChanged(false); + } + } + masterToggle() { this.alarmsSubject.pipe( tap((alarms) => { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts index 1926bb72ba..db864fddff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts @@ -234,6 +234,9 @@ export function constructTableCssString(widgetConfig: WidgetConfig): string { '.mat-table .mat-cell button.mat-icon-button mat-icon {\n'+ 'color: ' + mdDarkSecondary + ';\n'+ '}\n'+ + '.mat-table .mat-cell button.mat-icon-button[disabled][disabled] mat-icon {\n'+ + 'color: ' + mdDarkDisabled + ';\n'+ + '}\n'+ '.mat-divider {\n'+ 'border-top-color: ' + mdDarkDivider + ';\n'+ '}\n'+ diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index 75317f1108..363595badf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Inject, Injectable } from '@angular/core'; +import { Inject, Injectable, Type } from '@angular/core'; import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service'; import { WidgetService } from '@core/http/widget.service'; import { forkJoin, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs'; @@ -34,7 +34,6 @@ import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; import { isFunction, isUndefined } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.component'; -import { SharedModule } from '@shared/shared.module'; import { WidgetComponentsModule } from '@home/components/widget/widget-components.module'; import { WINDOW } from '@core/services/window.service'; @@ -43,6 +42,7 @@ import { TbFlot } from './lib/flot-widget'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { WidgetTypeId } from '@app/shared/models/id/widget-type-id'; import { TenantId } from '@app/shared/models/id/tenant-id'; +import { SharedModule } from '@shared/shared.module'; const tinycolor = tinycolor_; @@ -117,16 +117,23 @@ export class WidgetComponentService { const initSubject = new ReplaySubject(); this.init$ = initSubject.asObservable(); const loadDefaultWidgetInfoTasks = [ - this.loadWidgetResources(this.missingWidgetType, 'global-widget-missing-type'), - this.loadWidgetResources(this.errorWidgetType, 'global-widget-error-type'), + this.loadWidgetResources(this.missingWidgetType, 'global-widget-missing-type', [SharedModule]), + this.loadWidgetResources(this.errorWidgetType, 'global-widget-error-type', [SharedModule]), ]; forkJoin(loadDefaultWidgetInfoTasks).subscribe( () => { initSubject.next(); }, - () => { + (e) => { + let errorMessages = ['Failed to load default widget types!']; + if (e && e.length) { + errorMessages = errorMessages.concat(e); + } console.error('Failed to load default widget types!'); - initSubject.error('Failed to load default widget types!'); + initSubject.error({ + widgetInfo: this.errorWidgetType, + errorMessages + }); } ); return this.init$; @@ -194,7 +201,7 @@ export class WidgetComponentService { } if (widgetControllerDescriptor) { const widgetNamespace = `widget-type-${(isSystem ? 'sys-' : '')}${bundleAlias}-${widgetInfo.alias}`; - this.loadWidgetResources(widgetInfo, widgetNamespace).subscribe( + this.loadWidgetResources(widgetInfo, widgetNamespace, [WidgetComponentsModule]).subscribe( () => { if (widgetControllerDescriptor.settingsSchema) { widgetInfo.typeSettingsSchema = widgetControllerDescriptor.settingsSchema; @@ -219,7 +226,7 @@ export class WidgetComponentService { } } - private loadWidgetResources(widgetInfo: WidgetInfo, widgetNamespace: string): Observable { + private loadWidgetResources(widgetInfo: WidgetInfo, widgetNamespace: string, modules?: Type[]): Observable { this.cssParser.cssPreviewNamespace = widgetNamespace; this.cssParser.createStyleElement(widgetNamespace, widgetInfo.templateCss); const resourceTasks: Observable[] = []; @@ -236,7 +243,7 @@ export class WidgetComponentService { this.dynamicComponentFactoryService.createDynamicComponentFactory( class DynamicWidgetComponentInstance extends DynamicWidgetComponent {}, widgetInfo.templateHtml, - [SharedModule, WidgetComponentsModule] + modules ).pipe( map((factory) => { widgetInfo.componentFactory = factory; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index b965add9f2..b006b317a6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -17,25 +17,28 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@app/shared/shared.module'; -import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component'; -import { LegendComponent } from '@home/components/widget/legend.component'; import { EntitiesTableWidgetComponent } from '@home/components/widget/lib/entities-table-widget.component'; import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/display-columns-panel.component'; import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarms-table-widget.component'; +import { AlarmStatusFilterPanelComponent } from '@home/components/widget/lib/alarm-status-filter-panel.component'; +import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; @NgModule({ entryComponents: [ - DisplayColumnsPanelComponent + DisplayColumnsPanelComponent, + AlarmStatusFilterPanelComponent ], declarations: [ DisplayColumnsPanelComponent, + AlarmStatusFilterPanelComponent, EntitiesTableWidgetComponent, AlarmsTableWidgetComponent ], imports: [ CommonModule, - SharedModule + SharedModule, + SharedHomeComponentsModule ], exports: [ EntitiesTableWidgetComponent, diff --git a/ui-ngx/src/app/shared/models/page/page-link.ts b/ui-ngx/src/app/shared/models/page/page-link.ts index 8317cc325b..8cd5c7fe56 100644 --- a/ui-ngx/src/app/shared/models/page/page-link.ts +++ b/ui-ngx/src/app/shared/models/page/page-link.ts @@ -16,7 +16,7 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { emptyPageData, PageData } from '@shared/models/page/page-data'; -import { getDescendantProp } from '@core/utils'; +import { getDescendantProp, isObject } from '@core/utils'; export type PageLinkSearchFunction = (entity: T, textSearch: string) => boolean; @@ -28,10 +28,16 @@ const defaultPageLinkSearchFunction: PageLinkSearchFunction = const expected = ('' + textSearch).toLowerCase(); for (const key of Object.keys(entity)) { const val = entity[key]; - if (val !== null && val !== Object(val)) { - const actual = ('' + val).toLowerCase(); - if (actual.indexOf(expected) !== -1) { - return true; + if (val !== null) { + if (val !== Object(val)) { + const actual = ('' + val).toLowerCase(); + if (actual.indexOf(expected) !== -1) { + return true; + } + } else if (isObject(val)) { + if (defaultPageLinkSearchFunction(val, textSearch)) { + return true; + } } } } diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index a9f69e6e62..0e490633e6 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -498,6 +498,11 @@ mat-label { mat-icon { color: rgba(0, 0, 0, .54); } + &[disabled][disabled] { + mat-icon { + color: rgba(0, 0, 0, .26); + } + } } }