Commit bd8af1111eecf4ddd0c708278a02ba685985eda0

Authored by Igor Kulikov
1 parent ae3ea313

Implement Alarm widget

... ... @@ -259,5 +259,7 @@ export interface IWidgetSubscription {
259 259
260 260 destroy(): void;
261 261
  262 + update(): void;
  263 +
262 264 [key: string]: any;
263 265 }
... ...
... ... @@ -30,7 +30,6 @@ import { RelationTableComponent } from '@home/components/relation/relation-table
30 30 import { RelationDialogComponent } from './relation/relation-dialog.component';
31 31 import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component';
32 32 import { AlarmTableComponent } from '@home/components/alarm/alarm-table.component';
33   -import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component';
34 33 import { AttributeTableComponent } from '@home/components/attribute/attribute-table.component';
35 34 import { AddAttributeDialogComponent } from './attribute/add-attribute-dialog.component';
36 35 import { EditAttributeValuePanelComponent } from './attribute/edit-attribute-value-panel.component';
... ... @@ -64,6 +63,7 @@ import { AddWidgetToDashboardDialogComponent } from './attribute/add-widget-to-d
64 63 import { ImportDialogCsvComponent } from './import-export/import-dialog-csv.component';
65 64 import { TableColumnsAssignmentComponent } from './import-export/table-columns-assignment.component';
66 65 import { EventContentDialogComponent } from '@home/components/event/event-content-dialog.component';
  66 +import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
67 67
68 68 @NgModule({
69 69 entryComponents: [
... ... @@ -73,7 +73,6 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten
73 73 EventTableHeaderComponent,
74 74 RelationDialogComponent,
75 75 AlarmTableHeaderComponent,
76   - AlarmDetailsDialogComponent,
77 76 AddAttributeDialogComponent,
78 77 EditAttributeValuePanelComponent,
79 78 AliasesEntitySelectPanelComponent,
... ... @@ -104,7 +103,6 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten
104 103 RelationFiltersComponent,
105 104 AlarmTableHeaderComponent,
106 105 AlarmTableComponent,
107   - AlarmDetailsDialogComponent,
108 106 AttributeTableComponent,
109 107 AddAttributeDialogComponent,
110 108 EditAttributeValuePanelComponent,
... ... @@ -136,7 +134,8 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten
136 134 ],
137 135 imports: [
138 136 CommonModule,
139   - SharedModule
  137 + SharedModule,
  138 + SharedHomeComponentsModule
140 139 ],
141 140 exports: [
142 141 EntitiesTableComponent,
... ... @@ -149,7 +148,6 @@ import { EventContentDialogComponent } from '@home/components/event/event-conten
149 148 RelationTableComponent,
150 149 RelationFiltersComponent,
151 150 AlarmTableComponent,
152   - AlarmDetailsDialogComponent,
153 151 AttributeTableComponent,
154 152 AliasesEntitySelectComponent,
155 153 EntityAliasesDialogComponent,
... ...
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { NgModule } from '@angular/core';
  18 +import { CommonModule } from '@angular/common';
  19 +import { SharedModule } from '@app/shared/shared.module';
  20 +import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component';
  21 +
  22 +@NgModule({
  23 + entryComponents: [
  24 + AlarmDetailsDialogComponent
  25 + ],
  26 + declarations:
  27 + [
  28 + AlarmDetailsDialogComponent
  29 + ],
  30 + imports: [
  31 + CommonModule,
  32 + SharedModule
  33 + ],
  34 + exports: [
  35 + AlarmDetailsDialogComponent
  36 + ]
  37 +})
  38 +export class SharedHomeComponentsModule { }
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div fxLayout="column" class="mat-content mat-padding">
  19 + <label class="tb-title" translate>alarm.alarm-status-filter</label>
  20 + <mat-radio-group [(ngModel)]="subscription.alarmSearchStatus" fxLayout="column" fxLayoutGap="16px">
  21 + <mat-radio-button *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus" color="primary">
  22 + {{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }}
  23 + </mat-radio-button>
  24 + </mat-radio-group>
  25 +</div>
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +:host {
  17 + width: 100%;
  18 + height: 100%;
  19 + min-width: 300px;
  20 + overflow: hidden;
  21 + background: #fff;
  22 + border-radius: 4px;
  23 + box-shadow:
  24 + 0 7px 8px -4px rgba(0, 0, 0, .2),
  25 + 0 13px 19px 2px rgba(0, 0, 0, .14),
  26 + 0 5px 24px 4px rgba(0, 0, 0, .12);
  27 +
  28 + .mat-content {
  29 + overflow: hidden;
  30 + background-color: #fff;
  31 + }
  32 +
  33 + .mat-padding {
  34 + padding: 16px;
  35 + }
  36 +}
... ...
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component, Inject, InjectionToken } from '@angular/core';
  18 +import { IWidgetSubscription } from '@core/api/widget-api.models';
  19 +import { AlarmSearchStatus, alarmSearchStatusTranslations } from '@shared/models/alarm.models';
  20 +
  21 +export const ALARM_STATUS_FILTER_PANEL_DATA = new InjectionToken<any>('AlarmStatusFilterPanelData');
  22 +
  23 +export interface AlarmStatusFilterPanelData {
  24 + subscription: IWidgetSubscription;
  25 +}
  26 +
  27 +@Component({
  28 + selector: 'tb-alarm-status-filter-panel',
  29 + templateUrl: './alarm-status-filter-panel.component.html',
  30 + styleUrls: ['./alarm-status-filter-panel.component.scss']
  31 +})
  32 +export class AlarmStatusFilterPanelComponent {
  33 +
  34 + subscription: IWidgetSubscription;
  35 +
  36 + alarmSearchStatuses = Object.keys(AlarmSearchStatus);
  37 + alarmSearchStatusTranslationMap = alarmSearchStatusTranslations;
  38 +
  39 + constructor(@Inject(ALARM_STATUS_FILTER_PANEL_DATA) public data: AlarmStatusFilterPanelData) {
  40 + this.subscription = this.data.subscription;
  41 + }
  42 +}
... ...
... ... @@ -39,7 +39,7 @@ import { PageLink } from '@shared/models/page/page-link';
39 39 import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order';
40 40 import { DataSource } from '@angular/cdk/typings/collections';
41 41 import { CollectionViewer, SelectionModel } from '@angular/cdk/collections';
42   -import { BehaviorSubject, fromEvent, merge, Observable, of } from 'rxjs';
  42 +import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, of } from 'rxjs';
43 43 import { emptyPageData, PageData } from '@shared/models/page/page-data';
44 44 import { entityTypeTranslations } from '@shared/models/entity-type.models';
45 45 import { catchError, debounceTime, distinctUntilChanged, map, take, tap } from 'rxjs/operators';
... ... @@ -77,6 +77,19 @@ import {
77 77 alarmStatusTranslations
78 78 } from '@shared/models/alarm.models';
79 79 import { DatePipe } from '@angular/common';
  80 +import {
  81 + ALARM_STATUS_FILTER_PANEL_DATA,
  82 + AlarmStatusFilterPanelComponent,
  83 + AlarmStatusFilterPanelData
  84 +} from '@home/components/widget/lib/alarm-status-filter-panel.component';
  85 +import {
  86 + AlarmDetailsDialogComponent,
  87 + AlarmDetailsDialogData
  88 +} from '@home/components/alarm/alarm-details-dialog.component';
  89 +import { MatDialog } from '@angular/material/dialog';
  90 +import { NULL_UUID } from '@shared/models/id/has-uuid';
  91 +import { DialogService } from '@core/services/dialog.service';
  92 +import { AlarmService } from '@core/http/alarm.service';
80 93
81 94 interface AlarmsTableWidgetSettings extends TableWidgetSettings {
82 95 alarmsTitle: string;
... ... @@ -172,7 +185,10 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
172 185 private utils: UtilsService,
173 186 public translate: TranslateService,
174 187 private domSanitizer: DomSanitizer,
175   - private datePipe: DatePipe) {
  188 + private datePipe: DatePipe,
  189 + private dialog: MatDialog,
  190 + private dialogService: DialogService,
  191 + private alarmService: AlarmService) {
176 192 super(store);
177 193
178 194 const sortOrder: SortOrder = sortOrderFromString(this.defaultSortOrder);
... ... @@ -390,7 +406,36 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
390 406 }
391 407
392 408 private editAlarmStatusFilter($event: Event) {
393   - // TODO:
  409 + if ($event) {
  410 + $event.stopPropagation();
  411 + }
  412 + const target = $event.target || $event.srcElement || $event.currentTarget;
  413 + const config = new OverlayConfig();
  414 + config.backdropClass = 'cdk-overlay-transparent-backdrop';
  415 + config.hasBackdrop = true;
  416 + const connectedPosition: ConnectedPosition = {
  417 + originX: 'end',
  418 + originY: 'bottom',
  419 + overlayX: 'end',
  420 + overlayY: 'top'
  421 + };
  422 + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
  423 + .withPositions([connectedPosition]);
  424 +
  425 + const overlayRef = this.overlay.create(config);
  426 + overlayRef.backdropClick().subscribe(() => {
  427 + overlayRef.dispose();
  428 + });
  429 + const injectionTokens = new WeakMap<any, any>([
  430 + [ALARM_STATUS_FILTER_PANEL_DATA, {
  431 + subscription: this.subscription,
  432 + } as AlarmStatusFilterPanelData],
  433 + [OverlayRef, overlayRef]
  434 + ]);
  435 + const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens);
  436 + overlayRef.attach(new ComponentPortal(AlarmStatusFilterPanelComponent,
  437 + this.viewContainerRef, injector));
  438 + this.ctx.detectChanges();
394 439 }
395 440
396 441 private enterFilterMode() {
... ... @@ -538,35 +583,138 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
538 583 if ($event) {
539 584 $event.stopPropagation();
540 585 }
541   - // TODO:
  586 + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) {
  587 + this.dialog.open<AlarmDetailsDialogComponent, AlarmDetailsDialogData, boolean>
  588 + (AlarmDetailsDialogComponent,
  589 + {
  590 + disableClose: true,
  591 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  592 + data: {
  593 + alarmId: alarm.id.id,
  594 + allowAcknowledgment: this.allowAcknowledgment,
  595 + allowClear: this.allowClear,
  596 + displayDetails: true
  597 + }
  598 + }).afterClosed().subscribe(
  599 + (res) => {
  600 + if (res) {
  601 + this.subscription.update();
  602 + }
  603 + }
  604 + );
  605 + }
542 606 }
543 607
544 608 private ackAlarm($event: Event, alarm: AlarmInfo) {
545 609 if ($event) {
546 610 $event.stopPropagation();
547 611 }
548   - // TODO:
  612 + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) {
  613 + this.dialogService.confirm(
  614 + this.translate.instant('alarm.aknowledge-alarm-title'),
  615 + this.translate.instant('alarm.aknowledge-alarm-text'),
  616 + this.translate.instant('action.no'),
  617 + this.translate.instant('action.yes')
  618 + ).subscribe((res) => {
  619 + if (res) {
  620 + if (res) {
  621 + this.alarmService.ackAlarm(alarm.id.id).subscribe(() => {
  622 + this.subscription.update();
  623 + });
  624 + }
  625 + }
  626 + });
  627 + }
549 628 }
550 629
551 630 public ackAlarms($event: Event) {
552 631 if ($event) {
553 632 $event.stopPropagation();
554 633 }
555   - // TODO:
  634 + if (this.alarmsDatasource.selection.hasValue()) {
  635 + const alarms = this.alarmsDatasource.selection.selected.filter(
  636 + (alarm) => { return alarm.id.id !== NULL_UUID }
  637 + );
  638 + if (alarms.length) {
  639 + const title = this.translate.instant('alarm.aknowledge-alarms-title', {count: alarms.length});
  640 + const content = this.translate.instant('alarm.aknowledge-alarms-text', {count: alarms.length});
  641 + this.dialogService.confirm(
  642 + title,
  643 + content,
  644 + this.translate.instant('action.no'),
  645 + this.translate.instant('action.yes')
  646 + ).subscribe((res) => {
  647 + if (res) {
  648 + if (res) {
  649 + const tasks: Observable<void>[] = [];
  650 + for (const alarm of alarms) {
  651 + tasks.push(this.alarmService.ackAlarm(alarm.id.id));
  652 + }
  653 + forkJoin(tasks).subscribe(() => {
  654 + this.alarmsDatasource.clearSelection();
  655 + this.subscription.update();
  656 + });
  657 + }
  658 + }
  659 + });
  660 + }
  661 + }
556 662 }
557 663
558 664 private clearAlarm($event: Event, alarm: AlarmInfo) {
559 665 if ($event) {
560 666 $event.stopPropagation();
561 667 }
562   - // TODO:
  668 + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) {
  669 + this.dialogService.confirm(
  670 + this.translate.instant('alarm.clear-alarm-title'),
  671 + this.translate.instant('alarm.clear-alarm-text'),
  672 + this.translate.instant('action.no'),
  673 + this.translate.instant('action.yes')
  674 + ).subscribe((res) => {
  675 + if (res) {
  676 + if (res) {
  677 + this.alarmService.clearAlarm(alarm.id.id).subscribe(() => {
  678 + this.subscription.update();
  679 + });
  680 + }
  681 + }
  682 + });
  683 + }
563 684 }
564 685
565 686 public clearAlarms($event: Event) {
566 687 if ($event) {
567 688 $event.stopPropagation();
568 689 }
569   - // TODO:
  690 + if (this.alarmsDatasource.selection.hasValue()) {
  691 + const alarms = this.alarmsDatasource.selection.selected.filter(
  692 + (alarm) => { return alarm.id.id !== NULL_UUID }
  693 + );
  694 + if (alarms.length) {
  695 + const title = this.translate.instant('alarm.clear-alarms-title', {count: alarms.length});
  696 + const content = this.translate.instant('alarm.clear-alarms-text', {count: alarms.length});
  697 + this.dialogService.confirm(
  698 + title,
  699 + content,
  700 + this.translate.instant('action.no'),
  701 + this.translate.instant('action.yes')
  702 + ).subscribe((res) => {
  703 + if (res) {
  704 + if (res) {
  705 + const tasks: Observable<void>[] = [];
  706 + for (const alarm of alarms) {
  707 + tasks.push(this.alarmService.clearAlarm(alarm.id.id));
  708 + }
  709 + forkJoin(tasks).subscribe(() => {
  710 + this.alarmsDatasource.clearSelection();
  711 + this.subscription.update();
  712 + });
  713 + }
  714 + }
  715 + });
  716 + }
  717 + }
570 718 }
571 719
572 720 private defaultContent(key: EntityColumn, value: any): any {
... ... @@ -722,6 +870,13 @@ class AlarmsDatasource implements DataSource<AlarmInfo> {
722 870 return this.selection.isSelected(alarm);
723 871 }
724 872
  873 + clearSelection() {
  874 + if (this.selection.hasValue()) {
  875 + this.selection.clear();
  876 + this.onSelectionModeChanged(false);
  877 + }
  878 + }
  879 +
725 880 masterToggle() {
726 881 this.alarmsSubject.pipe(
727 882 tap((alarms) => {
... ...
... ... @@ -234,6 +234,9 @@ export function constructTableCssString(widgetConfig: WidgetConfig): string {
234 234 '.mat-table .mat-cell button.mat-icon-button mat-icon {\n'+
235 235 'color: ' + mdDarkSecondary + ';\n'+
236 236 '}\n'+
  237 + '.mat-table .mat-cell button.mat-icon-button[disabled][disabled] mat-icon {\n'+
  238 + 'color: ' + mdDarkDisabled + ';\n'+
  239 + '}\n'+
237 240 '.mat-divider {\n'+
238 241 'border-top-color: ' + mdDarkDivider + ';\n'+
239 242 '}\n'+
... ...
... ... @@ -14,7 +14,7 @@
14 14 /// limitations under the License.
15 15 ///
16 16
17   -import { Inject, Injectable } from '@angular/core';
  17 +import { Inject, Injectable, Type } from '@angular/core';
18 18 import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service';
19 19 import { WidgetService } from '@core/http/widget.service';
20 20 import { forkJoin, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs';
... ... @@ -34,7 +34,6 @@ import { catchError, map, mergeMap, switchMap } from 'rxjs/operators';
34 34 import { isFunction, isUndefined } from '@core/utils';
35 35 import { TranslateService } from '@ngx-translate/core';
36 36 import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.component';
37   -import { SharedModule } from '@shared/shared.module';
38 37 import { WidgetComponentsModule } from '@home/components/widget/widget-components.module';
39 38 import { WINDOW } from '@core/services/window.service';
40 39
... ... @@ -43,6 +42,7 @@ import { TbFlot } from './lib/flot-widget';
43 42 import { NULL_UUID } from '@shared/models/id/has-uuid';
44 43 import { WidgetTypeId } from '@app/shared/models/id/widget-type-id';
45 44 import { TenantId } from '@app/shared/models/id/tenant-id';
  45 +import { SharedModule } from '@shared/shared.module';
46 46
47 47 const tinycolor = tinycolor_;
48 48
... ... @@ -117,16 +117,23 @@ export class WidgetComponentService {
117 117 const initSubject = new ReplaySubject();
118 118 this.init$ = initSubject.asObservable();
119 119 const loadDefaultWidgetInfoTasks = [
120   - this.loadWidgetResources(this.missingWidgetType, 'global-widget-missing-type'),
121   - this.loadWidgetResources(this.errorWidgetType, 'global-widget-error-type'),
  120 + this.loadWidgetResources(this.missingWidgetType, 'global-widget-missing-type', [SharedModule]),
  121 + this.loadWidgetResources(this.errorWidgetType, 'global-widget-error-type', [SharedModule]),
122 122 ];
123 123 forkJoin(loadDefaultWidgetInfoTasks).subscribe(
124 124 () => {
125 125 initSubject.next();
126 126 },
127   - () => {
  127 + (e) => {
  128 + let errorMessages = ['Failed to load default widget types!'];
  129 + if (e && e.length) {
  130 + errorMessages = errorMessages.concat(e);
  131 + }
128 132 console.error('Failed to load default widget types!');
129   - initSubject.error('Failed to load default widget types!');
  133 + initSubject.error({
  134 + widgetInfo: this.errorWidgetType,
  135 + errorMessages
  136 + });
130 137 }
131 138 );
132 139 return this.init$;
... ... @@ -194,7 +201,7 @@ export class WidgetComponentService {
194 201 }
195 202 if (widgetControllerDescriptor) {
196 203 const widgetNamespace = `widget-type-${(isSystem ? 'sys-' : '')}${bundleAlias}-${widgetInfo.alias}`;
197   - this.loadWidgetResources(widgetInfo, widgetNamespace).subscribe(
  204 + this.loadWidgetResources(widgetInfo, widgetNamespace, [WidgetComponentsModule]).subscribe(
198 205 () => {
199 206 if (widgetControllerDescriptor.settingsSchema) {
200 207 widgetInfo.typeSettingsSchema = widgetControllerDescriptor.settingsSchema;
... ... @@ -219,7 +226,7 @@ export class WidgetComponentService {
219 226 }
220 227 }
221 228
222   - private loadWidgetResources(widgetInfo: WidgetInfo, widgetNamespace: string): Observable<any> {
  229 + private loadWidgetResources(widgetInfo: WidgetInfo, widgetNamespace: string, modules?: Type<any>[]): Observable<any> {
223 230 this.cssParser.cssPreviewNamespace = widgetNamespace;
224 231 this.cssParser.createStyleElement(widgetNamespace, widgetInfo.templateCss);
225 232 const resourceTasks: Observable<string>[] = [];
... ... @@ -236,7 +243,7 @@ export class WidgetComponentService {
236 243 this.dynamicComponentFactoryService.createDynamicComponentFactory(
237 244 class DynamicWidgetComponentInstance extends DynamicWidgetComponent {},
238 245 widgetInfo.templateHtml,
239   - [SharedModule, WidgetComponentsModule]
  246 + modules
240 247 ).pipe(
241 248 map((factory) => {
242 249 widgetInfo.componentFactory = factory;
... ...
... ... @@ -17,25 +17,28 @@
17 17 import { NgModule } from '@angular/core';
18 18 import { CommonModule } from '@angular/common';
19 19 import { SharedModule } from '@app/shared/shared.module';
20   -import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component';
21   -import { LegendComponent } from '@home/components/widget/legend.component';
22 20 import { EntitiesTableWidgetComponent } from '@home/components/widget/lib/entities-table-widget.component';
23 21 import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/display-columns-panel.component';
24 22 import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarms-table-widget.component';
  23 +import { AlarmStatusFilterPanelComponent } from '@home/components/widget/lib/alarm-status-filter-panel.component';
  24 +import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
25 25
26 26 @NgModule({
27 27 entryComponents: [
28   - DisplayColumnsPanelComponent
  28 + DisplayColumnsPanelComponent,
  29 + AlarmStatusFilterPanelComponent
29 30 ],
30 31 declarations:
31 32 [
32 33 DisplayColumnsPanelComponent,
  34 + AlarmStatusFilterPanelComponent,
33 35 EntitiesTableWidgetComponent,
34 36 AlarmsTableWidgetComponent
35 37 ],
36 38 imports: [
37 39 CommonModule,
38   - SharedModule
  40 + SharedModule,
  41 + SharedHomeComponentsModule
39 42 ],
40 43 exports: [
41 44 EntitiesTableWidgetComponent,
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 import { Direction, SortOrder } from '@shared/models/page/sort-order';
18 18 import { emptyPageData, PageData } from '@shared/models/page/page-data';
19   -import { getDescendantProp } from '@core/utils';
  19 +import { getDescendantProp, isObject } from '@core/utils';
20 20
21 21 export type PageLinkSearchFunction<T> = (entity: T, textSearch: string) => boolean;
22 22
... ... @@ -28,10 +28,16 @@ const defaultPageLinkSearchFunction: PageLinkSearchFunction<any> =
28 28 const expected = ('' + textSearch).toLowerCase();
29 29 for (const key of Object.keys(entity)) {
30 30 const val = entity[key];
31   - if (val !== null && val !== Object(val)) {
32   - const actual = ('' + val).toLowerCase();
33   - if (actual.indexOf(expected) !== -1) {
34   - return true;
  31 + if (val !== null) {
  32 + if (val !== Object(val)) {
  33 + const actual = ('' + val).toLowerCase();
  34 + if (actual.indexOf(expected) !== -1) {
  35 + return true;
  36 + }
  37 + } else if (isObject(val)) {
  38 + if (defaultPageLinkSearchFunction(val, textSearch)) {
  39 + return true;
  40 + }
35 41 }
36 42 }
37 43 }
... ...
... ... @@ -498,6 +498,11 @@ mat-label {
498 498 mat-icon {
499 499 color: rgba(0, 0, 0, .54);
500 500 }
  501 + &[disabled][disabled] {
  502 + mat-icon {
  503 + color: rgba(0, 0, 0, .26);
  504 + }
  505 + }
501 506 }
502 507 }
503 508
... ...