Showing
12 changed files
with
351 additions
and
31 deletions
... | ... | @@ -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 { } | ... | ... |
ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html
0 → 100644
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> | ... | ... |
ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss
0 → 100644
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 | } | ... | ... |