Browse Source

UI: Alarms table widget basic config.

pull/8917/head
Igor Kulikov 3 years ago
parent
commit
c7823a26af
  1. 8
      application/src/main/data/json/system/widget_bundles/alarm_widgets.json
  2. 6
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html
  3. 19
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts
  4. 23
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select-panel.component.ts
  5. 12
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html
  6. 55
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.ts
  7. 23
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss
  8. 61
      ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html
  9. 32
      ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts
  10. 100
      ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html
  11. 160
      ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts
  12. 8
      ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts
  13. 2
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html
  14. 6
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html
  15. 19
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts
  16. 2
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html
  17. 9
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html
  18. 1
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts
  19. 4
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html
  20. 8
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts
  21. 4
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html
  22. 8
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts
  23. 2
      ui-ngx/src/app/modules/home/components/widget/config/datasources.component.html
  24. 9
      ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts
  25. 8
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
  26. 5
      ui-ngx/src/app/shared/models/alarm.models.ts
  27. 54
      ui-ngx/src/styles.scss

8
application/src/main/data/json/system/widget_bundles/alarm_widgets.json

@ -3,7 +3,9 @@
"alias": "alarm_widgets", "alias": "alarm_widgets",
"title": "Alarm widgets", "title": "Alarm widgets",
"image": "data:image/png;base64,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", "image": "data:image/png;base64,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",
"description": "Visualization of alarms for devices, assets and other entities." "description": "Visualization of alarms for devices, assets and other entities.",
"externalId": null,
"name": "Alarm widgets"
}, },
"widgetTypes": [ "widgetTypes": [
{ {
@ -23,7 +25,9 @@
"dataKeySettingsSchema": "", "dataKeySettingsSchema": "",
"settingsDirective": "tb-alarms-table-widget-settings", "settingsDirective": "tb-alarms-table-widget-settings",
"dataKeySettingsDirective": "tb-alarms-table-key-settings", "dataKeySettingsDirective": "tb-alarms-table-key-settings",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayActivity\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}" "hasBasicMode": true,
"basicModeDirective": "tb-alarms-table-basic-config",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayActivity\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true,\"entitiesTitle\":null},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"warning\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false,\"configMode\":\"basic\",\"alarmFilterConfig\":null}"
} }
} }
] ]

6
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html

@ -26,10 +26,14 @@
#userAutocomplete="matAutocomplete" #userAutocomplete="matAutocomplete"
[displayWith]="displayUserFn" [displayWith]="displayUserFn"
(optionSelected)="selected($event)"> (optionSelected)="selected($event)">
<mat-option [fxHide]="!assigneeId" [value]="null"> <mat-option *ngIf="displayAssigneeNotSet" [value]="assigneeOptions.noAssignee">
<mat-icon class="unassigned-icon">account_circle</mat-icon> <mat-icon class="unassigned-icon">account_circle</mat-icon>
<span>{{ assigneeNotSetText | translate }}</span> <span>{{ assigneeNotSetText | translate }}</span>
</mat-option> </mat-option>
<mat-option *ngIf="displayAssignedToCurrentUser" [value]="assigneeOptions.currentUser">
<mat-icon class="unassigned-icon">account_circle</mat-icon>
<span>{{ assignedToCurrentUserText | translate }}</span>
</mat-option>
<mat-option *ngFor="let user of filteredUsers | async" [value]="user" <mat-option *ngFor="let user of filteredUsers | async" [value]="user"
[fxHide]="assigneeId === user.id.id"> [fxHide]="assigneeId === user.id.id">
<span class="user-avatar" [innerHTML]="getUserInitials(user)" <span class="user-avatar" [innerHTML]="getUserInitials(user)"

19
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts

@ -44,6 +44,7 @@ import { AlarmService } from '@core/http/alarm.service';
import { OverlayRef } from '@angular/cdk/overlay'; import { OverlayRef } from '@angular/cdk/overlay';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { AlarmAssigneeOption } from '@shared/models/alarm.models';
export const ALARM_ASSIGNEE_PANEL_DATA = new InjectionToken<any>('AlarmAssigneePanelData'); export const ALARM_ASSIGNEE_PANEL_DATA = new InjectionToken<any>('AlarmAssigneePanelData');
@ -59,15 +60,19 @@ export interface AlarmAssigneePanelData {
}) })
export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDestroy { export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDestroy {
assigneeOptions = AlarmAssigneeOption;
private dirty = false; private dirty = false;
alarmId: string; alarmId: string;
assigneeId?: string; assigneeId?: string;
assigneeOption?: AlarmAssigneeOption = null;
assigneeNotSetText = 'alarm.unassigned'; assigneeNotSetText = 'alarm.unassigned';
assignedToCurrentUserText = '';
reassigned: boolean = false; reassigned = false;
selectUserFormGroup: FormGroup; selectUserFormGroup: FormGroup;
@ -77,6 +82,14 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe
searchText = ''; searchText = '';
get displayAssigneeNotSet(): boolean {
return !!this.assigneeId;
}
get displayAssignedToCurrentUser(): boolean {
return false;
}
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
constructor(@Inject(ALARM_ASSIGNEE_PANEL_DATA) public data: AlarmAssigneePanelData, constructor(@Inject(ALARM_ASSIGNEE_PANEL_DATA) public data: AlarmAssigneePanelData,
@ -124,8 +137,8 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe
selected(event: MatAutocompleteSelectedEvent): void { selected(event: MatAutocompleteSelectedEvent): void {
this.clear(); this.clear();
const user: User = event.option.value; if (event.option.value?.id) {
if (user) { const user: User = event.option.value;
this.assign(user); this.assign(user);
} else { } else {
this.unassign(); this.unassign();

23
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select-panel.component.ts

@ -36,11 +36,14 @@ import { emptyPageData } from '@shared/models/page/page-data';
import { OverlayRef } from '@angular/cdk/overlay'; import { OverlayRef } from '@angular/cdk/overlay';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { AlarmAssigneeOption } from '@shared/models/alarm.models';
export const ALARM_ASSIGNEE_SELECT_PANEL_DATA = new InjectionToken<any>('AlarmAssigneeSelectPanelData'); export const ALARM_ASSIGNEE_SELECT_PANEL_DATA = new InjectionToken<any>('AlarmAssigneeSelectPanelData');
export interface AlarmAssigneeSelectPanelData { export interface AlarmAssigneeSelectPanelData {
assigneeId?: string; assigneeId?: string;
assigneeOption?: AlarmAssigneeOption;
userMode?: boolean;
} }
@Component({ @Component({
@ -50,11 +53,15 @@ export interface AlarmAssigneeSelectPanelData {
}) })
export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit, OnDestroy { export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit, OnDestroy {
assigneeOptions = AlarmAssigneeOption;
private dirty = false; private dirty = false;
assigneeId?: string; assigneeId?: string;
assigneeOption?: AlarmAssigneeOption;
assigneeNotSetText = 'alarm.assignee-not-set'; assigneeNotSetText = 'alarm.assignee-not-set';
assignedToCurrentUserText = this.data.userMode ? 'alarm.assigned-to-me' : 'alarm.assigned-to-current-user';
selectUserFormGroup: FormGroup; selectUserFormGroup: FormGroup;
@ -67,6 +74,15 @@ export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit
userSelected = false; userSelected = false;
result?: UserEmailInfo; result?: UserEmailInfo;
optionResult?: AlarmAssigneeOption;
get displayAssigneeNotSet(): boolean {
return this.assigneeOption !== AlarmAssigneeOption.noAssignee;
}
get displayAssignedToCurrentUser(): boolean {
return this.assigneeOption !== AlarmAssigneeOption.currentUser;
}
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
@ -77,6 +93,7 @@ export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit
private fb: FormBuilder, private fb: FormBuilder,
private utilsService: UtilsService) { private utilsService: UtilsService) {
this.assigneeId = data.assigneeId; this.assigneeId = data.assigneeId;
this.assigneeOption = data.assigneeOption;
this.selectUserFormGroup = this.fb.group({ this.selectUserFormGroup = this.fb.group({
user: [null] user: [null]
}); });
@ -112,7 +129,11 @@ export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit
selected(event: MatAutocompleteSelectedEvent): void { selected(event: MatAutocompleteSelectedEvent): void {
this.clear(); this.clear();
this.userSelected = true; this.userSelected = true;
this.result = event.option.value; if (event.option.value?.id) {
this.result = event.option.value;
} else {
this.optionResult = event.option.value;
}
this.overlayRef.dispose(); this.overlayRef.dispose();
} }

12
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html

@ -16,16 +16,16 @@
--> -->
<ng-container [formGroup]="assigneeFormGroup"> <ng-container [formGroup]="assigneeFormGroup">
<mat-form-field fxFlex class="mat-block" style="margin-bottom: 25px" <mat-form-field [ngStyle]="inline ? {width: '100%'} : {marginBottom: '25px'}"
(click)="openAlarmAssigneeSelectPanel($event)" (click)="openAlarmAssigneeSelectPanel($event)"
subscriptSizing="dynamic"> subscriptSizing="dynamic" [appearance]="inline ? 'outline' : 'fill'">
<mat-label translate>alarm.assignee</mat-label> <mat-label *ngIf="!inline" translate>alarm.assignee</mat-label>
<input matInput readonly formControlName="assignee"> <input matInput readonly formControlName="assignee">
<span *ngIf="assignee" matPrefix class="user-avatar" <span *ngIf="assignee" matPrefix class="user-avatar" [ngClass]="{'inline': inline}"
[style.backgroundColor]="getAvatarBgColor()"> [style.backgroundColor]="getAvatarBgColor()">
{{ getUserInitials() }} {{ getUserInitials() }}
</span> </span>
<mat-icon *ngIf="!assignee" matPrefix class="unassigned-icon">account_circle</mat-icon> <mat-icon *ngIf="!assignee" matPrefix class="unassigned-icon" [ngClass]="{'inline': inline}">account_circle</mat-icon>
<mat-icon *ngIf="!disabled" matSuffix>arrow_drop_down</mat-icon> <mat-icon *ngIf="!disabled" matSuffix class="drop-down-icon" [ngClass]="{'inline': inline}">arrow_drop_down</mat-icon>
</mat-form-field> </mat-form-field>
</ng-container> </ng-container>

55
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.ts

@ -30,6 +30,8 @@ import {
AlarmAssigneeSelectPanelComponent, AlarmAssigneeSelectPanelComponent,
AlarmAssigneeSelectPanelData AlarmAssigneeSelectPanelData
} from '@home/components/alarm/alarm-assignee-select-panel.component'; } from '@home/components/alarm/alarm-assignee-select-panel.component';
import { coerceBoolean } from '@shared/decorators/coercion';
import { AlarmAssigneeOption } from '@shared/models/alarm.models';
@Component({ @Component({
selector: 'tb-alarm-assignee-select', selector: 'tb-alarm-assignee-select',
@ -47,8 +49,17 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso
@Input() disabled: boolean; @Input() disabled: boolean;
@coerceBoolean()
@Input()
inline = false;
@coerceBoolean()
@Input()
userMode = false;
assigneeFormGroup: UntypedFormGroup; assigneeFormGroup: UntypedFormGroup;
assignee?: User | UserEmailInfo; assignee?: User | UserEmailInfo;
assigneeOption?: AlarmAssigneeOption;
private propagateChange = (_: any) => {}; private propagateChange = (_: any) => {};
@ -82,7 +93,15 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso
} }
} }
writeValue(userId?: UserId): void { writeValue(value?: UserId | AlarmAssigneeOption): void {
let userId: UserId;
if (value && (value as UserId).id) {
userId = value as UserId;
this.assigneeOption = null;
} else {
userId = null;
this.assigneeOption = value ? value as AlarmAssigneeOption : AlarmAssigneeOption.noAssignee;
}
const userObservable = userId ? this.userService.getUser(userId.id, {ignoreErrors: true}).pipe( const userObservable = userId ? this.userService.getUser(userId.id, {ignoreErrors: true}).pipe(
catchError(() => of(null)) catchError(() => of(null))
) : of(null); ) : of(null);
@ -92,15 +111,31 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso
}), }),
map((user) => this.getAssignee(user)) map((user) => this.getAssignee(user))
).subscribe((assignee) => { ).subscribe((assignee) => {
this.assigneeFormGroup.get('assignee').patchValue(assignee, {emitEvent: false}); if (assignee) {
this.assigneeFormGroup.get('assignee').patchValue(assignee, {emitEvent: false});
} else {
if (!this.assigneeOption) {
this.assigneeOption = AlarmAssigneeOption.noAssignee;
}
assignee = this.getAssigneeOption(this.assigneeOption);
this.assigneeFormGroup.get('assignee').patchValue(assignee, {emitEvent: false});
}
}); });
} }
private getAssignee(user?: User| UserEmailInfo): string { private getAssignee(user?: User| UserEmailInfo): string | null {
if (user) { if (user) {
return this.getUserDisplayName(user); return this.getUserDisplayName(user);
} else { } else {
return null;
}
}
private getAssigneeOption(assigneeOption: AlarmAssigneeOption): string {
if (assigneeOption === AlarmAssigneeOption.noAssignee) {
return this.translateService.instant('alarm.assignee-not-set'); return this.translateService.instant('alarm.assignee-not-set');
} else {
return this.translateService.instant(this.userMode ? 'alarm.assigned-to-me' : 'alarm.assigned-to-current-user');
} }
} }
@ -169,7 +204,9 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso
{ {
provide: ALARM_ASSIGNEE_SELECT_PANEL_DATA, provide: ALARM_ASSIGNEE_SELECT_PANEL_DATA,
useValue: { useValue: {
assigneeId: this.assignee?.id?.id assigneeId: this.assignee?.id?.id,
assigneeOption: this.assigneeOption,
userMode: this.userMode
} as AlarmAssigneeSelectPanelData } as AlarmAssigneeSelectPanelData
}, },
{ {
@ -183,8 +220,14 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso
component.onDestroy(() => { component.onDestroy(() => {
if (component.instance.userSelected) { if (component.instance.userSelected) {
this.assignee = component.instance.result; this.assignee = component.instance.result;
this.assigneeFormGroup.get('assignee').patchValue(this.getAssignee(this.assignee), {emitEvent: false}); this.assigneeOption = component.instance.optionResult;
this.propagateChange(this.assignee?.id); if (this.assignee) {
this.assigneeFormGroup.get('assignee').patchValue(this.getAssignee(this.assignee), {emitEvent: false});
this.propagateChange(this.assignee?.id);
} else if (this.assigneeOption) {
this.assigneeFormGroup.get('assignee').patchValue(this.getAssigneeOption(this.assigneeOption), {emitEvent: false});
this.propagateChange(this.assigneeOption);
}
} }
}); });
} }

23
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss

@ -19,20 +19,14 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border-radius: 50%; border-radius: 50%;
width: 28px;
height: 28px;
min-width: 28px; min-width: 28px;
min-height: 28px; min-height: 28px;
color: white; color: white;
font-size: 13px; font-size: 13px;
font-weight: 700; font-weight: 700;
margin-left: 12px;
margin-right: 20px;
} }
.unassigned-icon { .unassigned-icon {
width: 28px;
height: 28px;
font-size: 28px; font-size: 28px;
color: rgba(0, 0, 0, 0.38); color: rgba(0, 0, 0, 0.38);
overflow: visible; overflow: visible;
@ -40,3 +34,20 @@
margin-right: 20px; margin-right: 20px;
padding: 0; padding: 0;
} }
.user-avatar, .unassigned-icon {
width: 28px;
height: 28px;
margin-left: 12px;
margin-right: 20px;
&.inline {
margin-left: 0;
margin-right: 8px;
}
}
.drop-down-icon {
&.inline {
margin-right: -12px;
}
}

61
ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html

@ -48,32 +48,26 @@
</form> </form>
</ng-template> </ng-template>
<ng-template #alarmFilter> <ng-template #alarmFilter>
<div fxLayout="column" fxLayoutAlign="center" [formGroup]="alarmFilterConfigForm"> <div class="tb-widget-config-panel no-padding no-border" [formGroup]="alarmFilterConfigForm">
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start start" <div class="tb-widget-config-row space-between">
fxLayoutGap.gt-xs="8px"> <div class="fixed-title-width" translate>alarm.alarm-status-list</div>
<mat-form-field fxFlex class="mat-block" floatLabel="always"> <mat-chip-listbox multiple formControlName="statusList">
<mat-label translate>alarm.alarm-status-list</mat-label> <mat-chip-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus">
<mat-select formControlName="statusList" multiple {{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }}
placeholder="{{ !alarmFilterConfigForm.get('statusList').value?.length ? ('alarm.any-status' | translate) : '' }}"> </mat-chip-option>
<mat-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus"> </mat-chip-listbox>
{{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }} </div>
</mat-option> <div class="tb-widget-config-row space-between">
</mat-select> <div class="fixed-title-width" translate>alarm.alarm-severity-list</div>
</mat-form-field> <mat-chip-listbox multiple formControlName="severityList">
<mat-form-field fxFlex class="mat-block" floatLabel="always"> <mat-chip-option *ngFor="let alarmSeverity of alarmSeverities" [value]="alarmSeverity">
<mat-label translate>alarm.alarm-severity-list</mat-label> {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }}
<mat-select formControlName="severityList" multiple </mat-chip-option>
placeholder="{{ !alarmFilterConfigForm.get('severityList').value?.length ? ('alarm.any-severity' | translate) : '' }}"> </mat-chip-listbox>
<mat-option *ngFor="let alarmSeverity of alarmSeverities" [value]="alarmSeverity">
{{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div> </div>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start start" <div class="tb-widget-config-row tb-chips">
fxLayoutGap.gt-xs="8px"> <div class="fixed-title-width" translate>alarm.alarm-type-list</div>
<mat-form-field fxFlex class="mat-block" floatLabel="always"> <mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic">
<mat-label translate>alarm.alarm-type-list</mat-label>
<mat-chip-grid #alarmTypeChipList formControlName="typeList"> <mat-chip-grid #alarmTypeChipList formControlName="typeList">
<mat-chip-row *ngFor="let type of alarmTypeList()" <mat-chip-row *ngFor="let type of alarmTypeList()"
[removable]="true" (removed)="removeAlarmType(type)"> [removable]="true" (removed)="removeAlarmType(type)">
@ -88,16 +82,15 @@
</mat-chip-grid> </mat-chip-grid>
</mat-form-field> </mat-form-field>
</div> </div>
<div fxLayout="column" fxLayoutGap="16px"> <div class="tb-widget-config-row">
<mat-slide-toggle *ngIf="propagatedFilter" formControlName="searchPropagatedAlarms"> <div class="fixed-title-width" translate>alarm.assignee</div>
{{ 'alarm.search-propagated-alarms' | translate }} <tb-alarm-assignee-select fxFlex inline="true"
</mat-slide-toggle> [userMode]="userMode"
<mat-slide-toggle formControlName="assignedToCurrentUser"> formControlName="assigneeId">
{{ (userMode ? 'alarm.assigned-to-me' : 'alarm.assigned-to-current-user') | translate }}
</mat-slide-toggle>
<tb-alarm-assignee-select
*ngIf="!alarmFilterConfigForm.get('assignedToCurrentUser').value" formControlName="assigneeId">
</tb-alarm-assignee-select> </tb-alarm-assignee-select>
</div> </div>
<mat-slide-toggle class="mat-slide" *ngIf="propagatedFilter" formControlName="searchPropagatedAlarms">
{{ 'alarm.search-propagated-alarms' | translate }}
</mat-slide-toggle>
</div> </div>
</ng-template> </ng-template>

32
ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts

@ -34,6 +34,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal'; import { TemplatePortal } from '@angular/cdk/portal';
import { import {
AlarmAssigneeOption,
AlarmSearchStatus, AlarmSearchStatus,
alarmSearchStatusTranslations, alarmSearchStatusTranslations,
AlarmSeverity, AlarmSeverity,
@ -133,12 +134,10 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal
severityList: [null, []], severityList: [null, []],
typeList: [null, []], typeList: [null, []],
searchPropagatedAlarms: [false, []], searchPropagatedAlarms: [false, []],
assignedToCurrentUser: [false, []], assigneeId: [AlarmAssigneeOption.noAssignee, []]
assigneeId: [null, []]
}); });
this.alarmFilterConfigForm.valueChanges.subscribe( this.alarmFilterConfigForm.valueChanges.subscribe(
() => { () => {
this.updateValidators();
if (!this.buttonMode) { if (!this.buttonMode) {
this.alarmConfigUpdated(this.alarmFilterConfigForm.value); this.alarmConfigUpdated(this.alarmFilterConfigForm.value);
} }
@ -165,7 +164,6 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal
this.alarmFilterConfigForm.disable({emitEvent: false}); this.alarmFilterConfigForm.disable({emitEvent: false});
} else { } else {
this.alarmFilterConfigForm.enable({emitEvent: false}); this.alarmFilterConfigForm.enable({emitEvent: false});
this.updateValidators();
} }
} }
@ -175,16 +173,6 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal
this.updateAlarmConfigForm(alarmFilterConfig); this.updateAlarmConfigForm(alarmFilterConfig);
} }
private updateValidators() {
const assignedToCurrentUser = this.alarmFilterConfigForm.get('assignedToCurrentUser').value;
if (assignedToCurrentUser) {
this.alarmFilterConfigForm.get('assigneeId').disable({emitEvent: false});
} else {
this.alarmFilterConfigForm.get('assigneeId').enable({emitEvent: false});
}
this.alarmFilterConfigForm.get('assigneeId').updateValueAndValidity({emitEvent: false});
}
toggleAlarmFilterPanel($event: Event) { toggleAlarmFilterPanel($event: Event) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
@ -276,14 +264,20 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal
severityList: alarmFilterConfig?.severityList, severityList: alarmFilterConfig?.severityList,
typeList: alarmFilterConfig?.typeList, typeList: alarmFilterConfig?.typeList,
searchPropagatedAlarms: alarmFilterConfig?.searchPropagatedAlarms, searchPropagatedAlarms: alarmFilterConfig?.searchPropagatedAlarms,
assignedToCurrentUser: alarmFilterConfig?.assignedToCurrentUser, assigneeId: alarmFilterConfig?.assignedToCurrentUser ? AlarmAssigneeOption.currentUser :
assigneeId: alarmFilterConfig?.assigneeId (alarmFilterConfig?.assigneeId ? alarmFilterConfig?.assigneeId : AlarmAssigneeOption.noAssignee)
}, {emitEvent: false}); }, {emitEvent: false});
this.updateValidators();
} }
private alarmConfigUpdated(alarmFilterConfig: AlarmFilterConfig) { private alarmConfigUpdated(formValue: any) {
this.alarmFilterConfig = alarmFilterConfig; this.alarmFilterConfig = {
statusList: formValue.statusList,
severityList: formValue.severityList,
typeList: formValue.typeList,
searchPropagatedAlarms: formValue.searchPropagatedAlarms,
assignedToCurrentUser: formValue.assigneeId === AlarmAssigneeOption.currentUser,
assigneeId: formValue.assigneeId?.id ? formValue.assigneeId : null
};
this.updateButtonDisplayValue(); this.updateButtonDisplayValue();
this.propagateChange(this.alarmFilterConfig); this.propagateChange(this.alarmFilterConfig);
} }

100
ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html

@ -0,0 +1,100 @@
<!--
Copyright © 2016-2023 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.
-->
<ng-container [formGroup]="alarmsTableWidgetConfigForm">
<tb-timewindow-config-panel formControlName="timewindowConfig">
</tb-timewindow-config-panel>
<div class="tb-widget-config-panel">
<div class="tb-widget-config-panel-title" translate>alarm.filter</div>
<tb-alarm-filter-config buttonMode="false" formControlName="alarmFilterConfig"></tb-alarm-filter-config>
</div>
<tb-datasources
[configMode]="basicMode"
hideDataKeys
formControlName="datasources">
</tb-datasources>
<tb-data-keys-panel
panelTitle="{{ 'widgets.table.columns' | translate }}"
addKeyTitle="{{ 'widgets.table.add-column' | translate }}"
keySettingsTitle="{{ 'widgets.table.column-settings' | translate }}"
removeKeyTitle="{{ 'widgets.table.remove-column' | translate }}"
noKeysText="{{ 'widgets.table.no-columns' | translate }}"
hideDataKeyColor
hideUnits
hideDecimals
[datasourceType]="alarmSource?.type"
[deviceId]="alarmSource?.deviceId"
[entityAliasId]="alarmSource?.entityAliasId"
formControlName="columns">
</tb-data-keys-panel>
<div class="tb-widget-config-panel">
<div class="tb-widget-config-panel-title" translate>widget-config.card-appearance</div>
<div class="tb-widget-config-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle">
{{ 'widget-config.card-title' | translate }}
</mat-slide-toggle>
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
<div class="tb-widget-config-row space-between same-padding">
<mat-slide-toggle class="mat-slide" formControlName="showTitleIcon">
{{ 'widget-config.card-icon' | translate }}
</mat-slide-toggle>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<tb-material-icon-select asBoxInput
[color]="alarmsTableWidgetConfigForm.get('iconColor').value"
formControlName="titleIcon">
</tb-material-icon-select>
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="iconColor">
</tb-color-input>
</div>
</div>
<div class="tb-widget-config-row space-between">
<div translate>widget-config.show-card-buttons</div>
<mat-chip-listbox multiple formControlName="cardButtons">
<mat-chip-option value="search">{{ 'action.search' | translate }}</mat-chip-option>
<mat-chip-option value="filter">{{ 'alarm.alarm-filter' | translate }}</mat-chip-option>
<mat-chip-option value="columnsToDisplay">{{ 'widgets.table.columns-to-display' | translate }}</mat-chip-option>
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</div>
<div class="tb-widget-config-row space-between same-padding">
<div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
</div>
<div class="tb-widget-config-row space-between same-padding">
<div>{{ 'widget-config.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="backgroundColor">
</tb-color-input>
</div>
</div>
</div>
<tb-widget-actions-panel
formControlName="actions">
</tb-widget-actions-panel>
</ng-container>

160
ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts

@ -0,0 +1,160 @@
///
/// Copyright © 2016-2023 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 } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { isUndefined } from '@core/utils';
import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
@Component({
selector: 'tb-alarms-table-basic-config',
templateUrl: './alarms-table-basic-config.component.html',
styleUrls: ['../basic-config.scss']
})
export class AlarmsTableBasicConfigComponent extends BasicWidgetConfigComponent {
public get alarmSource(): Datasource {
const datasources: Datasource[] = this.alarmsTableWidgetConfigForm.get('datasources').value;
if (datasources && datasources.length) {
return datasources[0];
} else {
return null;
}
}
alarmsTableWidgetConfigForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent,
private fb: UntypedFormBuilder) {
super(store, widgetConfigComponent);
}
protected configForm(): UntypedFormGroup {
return this.alarmsTableWidgetConfigForm;
}
protected onConfigSet(configData: WidgetConfigComponentData) {
this.alarmsTableWidgetConfigForm = this.fb.group({
timewindowConfig: [getTimewindowConfig(configData.config), []],
alarmFilterConfig: [configData.config.alarmFilterConfig, []],
datasources: [[configData.config.alarmSource], []],
columns: [this.getColumns(configData.config.alarmSource), []],
showTitle: [configData.config.showTitle, []],
title: [configData.config.settings?.entitiesTitle, []],
showTitleIcon: [configData.config.showTitleIcon, []],
titleIcon: [configData.config.titleIcon, []],
iconColor: [configData.config.iconColor, []],
cardButtons: [this.getCardButtons(configData.config), []],
color: [configData.config.color, []],
backgroundColor: [configData.config.backgroundColor, []],
actions: [configData.config.actions || {}, []]
});
}
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow;
this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow;
this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow;
this.widgetConfig.config.alarmFilterConfig = config.alarmFilterConfig;
this.widgetConfig.config.alarmSource = config.datasources[0];
this.setColumns(config.columns, this.widgetConfig.config.alarmSource);
this.widgetConfig.config.actions = config.actions;
this.widgetConfig.config.showTitle = config.showTitle;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
this.widgetConfig.config.settings.entitiesTitle = config.title;
this.widgetConfig.config.showTitleIcon = config.showTitleIcon;
this.widgetConfig.config.titleIcon = config.titleIcon;
this.widgetConfig.config.iconColor = config.iconColor;
this.setCardButtons(config.cardButtons, this.widgetConfig.config);
this.widgetConfig.config.color = config.color;
this.widgetConfig.config.backgroundColor = config.backgroundColor;
return this.widgetConfig;
}
protected validatorTriggers(): string[] {
return ['showTitle', 'showTitleIcon'];
}
protected updateValidators(emitEvent: boolean, trigger?: string) {
const showTitle: boolean = this.alarmsTableWidgetConfigForm.get('showTitle').value;
const showTitleIcon: boolean = this.alarmsTableWidgetConfigForm.get('showTitleIcon').value;
if (showTitle) {
this.alarmsTableWidgetConfigForm.get('title').enable();
this.alarmsTableWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false});
if (showTitleIcon) {
this.alarmsTableWidgetConfigForm.get('titleIcon').enable();
this.alarmsTableWidgetConfigForm.get('iconColor').enable();
} else {
this.alarmsTableWidgetConfigForm.get('titleIcon').disable();
this.alarmsTableWidgetConfigForm.get('iconColor').disable();
}
} else {
this.alarmsTableWidgetConfigForm.get('title').disable();
this.alarmsTableWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false});
this.alarmsTableWidgetConfigForm.get('titleIcon').disable();
this.alarmsTableWidgetConfigForm.get('iconColor').disable();
}
this.alarmsTableWidgetConfigForm.get('title').updateValueAndValidity({emitEvent});
this.alarmsTableWidgetConfigForm.get('showTitleIcon').updateValueAndValidity({emitEvent: false});
this.alarmsTableWidgetConfigForm.get('titleIcon').updateValueAndValidity({emitEvent});
this.alarmsTableWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent});
}
private getColumns(alarmSource?: Datasource): DataKey[] {
if (alarmSource) {
return alarmSource.dataKeys || [];
}
return [];
}
private setColumns(columns: DataKey[], alarmSource?: Datasource) {
if (alarmSource) {
alarmSource.dataKeys = columns;
}
}
private getCardButtons(config: WidgetConfig): string[] {
const buttons: string[] = [];
if (isUndefined(config.settings?.enableSearch) || config.settings?.enableSearch) {
buttons.push('search');
}
if (isUndefined(config.settings?.enableFilter) || config.settings?.enableFilter) {
buttons.push('filter');
}
if (isUndefined(config.settings?.enableSelectColumnDisplay) || config.settings?.enableSelectColumnDisplay) {
buttons.push('columnsToDisplay');
}
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) {
buttons.push('fullscreen');
}
return buttons;
}
private setCardButtons(buttons: string[], config: WidgetConfig) {
config.settings.enableSearch = buttons.includes('search');
config.settings.enableFilter = buttons.includes('filter');
config.settings.enableSelectColumnDisplay = buttons.includes('columnsToDisplay');
config.enableFullscreen = buttons.includes('fullscreen');
}
}

8
ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts

@ -35,6 +35,9 @@ import {
} from '@home/components/widget/config/basic/cards/timeseries-table-basic-config.component'; } from '@home/components/widget/config/basic/cards/timeseries-table-basic-config.component';
import { FlotBasicConfigComponent } from '@home/components/widget/config/basic/chart/flot-basic-config.component'; import { FlotBasicConfigComponent } from '@home/components/widget/config/basic/chart/flot-basic-config.component';
import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module'; import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module';
import {
AlarmsTableBasicConfigComponent
} from '@home/components/widget/config/basic/alarm/alarms-table-basic-config.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -43,6 +46,7 @@ import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widge
EntitiesTableBasicConfigComponent, EntitiesTableBasicConfigComponent,
TimeseriesTableBasicConfigComponent, TimeseriesTableBasicConfigComponent,
FlotBasicConfigComponent, FlotBasicConfigComponent,
AlarmsTableBasicConfigComponent,
DataKeyRowComponent, DataKeyRowComponent,
DataKeysPanelComponent DataKeysPanelComponent
], ],
@ -58,6 +62,7 @@ import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widge
EntitiesTableBasicConfigComponent, EntitiesTableBasicConfigComponent,
TimeseriesTableBasicConfigComponent, TimeseriesTableBasicConfigComponent,
FlotBasicConfigComponent, FlotBasicConfigComponent,
AlarmsTableBasicConfigComponent,
DataKeyRowComponent, DataKeyRowComponent,
DataKeysPanelComponent DataKeysPanelComponent
] ]
@ -69,5 +74,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type<IBasicWidgetCo
'tb-simple-card-basic-config': SimpleCardBasicConfigComponent, 'tb-simple-card-basic-config': SimpleCardBasicConfigComponent,
'tb-entities-table-basic-config': EntitiesTableBasicConfigComponent, 'tb-entities-table-basic-config': EntitiesTableBasicConfigComponent,
'tb-timeseries-table-basic-config': TimeseriesTableBasicConfigComponent, 'tb-timeseries-table-basic-config': TimeseriesTableBasicConfigComponent,
'tb-flot-basic-config': FlotBasicConfigComponent 'tb-flot-basic-config': FlotBasicConfigComponent,
'tb-alarms-table-basic-config': AlarmsTableBasicConfigComponent
}; };

2
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html

@ -38,7 +38,7 @@
formControlName="columns"> formControlName="columns">
</tb-data-keys-panel> </tb-data-keys-panel>
<div class="tb-widget-config-panel"> <div class="tb-widget-config-panel">
<div class="tb-widget-config-panel-title" translate>widget-config.appearance</div> <div class="tb-widget-config-panel-title" translate>widget-config.card-appearance</div>
<div class="tb-widget-config-row"> <div class="tb-widget-config-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle"> <mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle">
{{ 'widget-config.card-title' | translate }} {{ 'widget-config.card-title' | translate }}

6
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html

@ -61,6 +61,12 @@
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="tb-widget-config-row space-between">
<div translate>widget-config.show-card-buttons</div>
<mat-chip-listbox multiple formControlName="cardButtons">
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox>
</div>
<div class="tb-widget-config-row space-between same-padding"> <div class="tb-widget-config-row space-between same-padding">
<div>{{ 'widget-config.text-color' | translate }}</div> <div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">

19
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.ts

@ -23,11 +23,12 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models'
import { import {
Datasource, Datasource,
datasourcesHasAggregation, datasourcesHasAggregation,
datasourcesHasOnlyComparisonAggregation, datasourcesHasOnlyComparisonAggregation, WidgetConfig,
} from '@shared/models/widget.models'; } from '@shared/models/widget.models';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component'; import { getTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component';
import { isUndefined } from '@core/utils';
@Component({ @Component({
selector: 'tb-simple-card-basic-config', selector: 'tb-simple-card-basic-config',
@ -70,6 +71,7 @@ export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
labelPosition: [configData.config.settings?.labelPosition, []], labelPosition: [configData.config.settings?.labelPosition, []],
units: [configData.config.units, []], units: [configData.config.units, []],
decimals: [configData.config.decimals, []], decimals: [configData.config.decimals, []],
cardButtons: [this.getCardButtons(configData.config), []],
color: [configData.config.color, []], color: [configData.config.color, []],
backgroundColor: [configData.config.backgroundColor, []], backgroundColor: [configData.config.backgroundColor, []],
actions: [configData.config.actions || {}, []] actions: [configData.config.actions || {}, []]
@ -85,9 +87,10 @@ export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
this.widgetConfig.config.actions = config.actions; this.widgetConfig.config.actions = config.actions;
this.widgetConfig.config.units = config.units; this.widgetConfig.config.units = config.units;
this.widgetConfig.config.decimals = config.decimals; this.widgetConfig.config.decimals = config.decimals;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
this.setCardButtons(config.cardButtons, this.widgetConfig.config);
this.widgetConfig.config.color = config.color; this.widgetConfig.config.color = config.color;
this.widgetConfig.config.backgroundColor = config.backgroundColor; this.widgetConfig.config.backgroundColor = config.backgroundColor;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
this.widgetConfig.config.settings.labelPosition = config.labelPosition; this.widgetConfig.config.settings.labelPosition = config.labelPosition;
return this.widgetConfig; return this.widgetConfig;
} }
@ -111,4 +114,16 @@ export class SimpleCardBasicConfigComponent extends BasicWidgetConfigComponent {
} }
} }
private getCardButtons(config: WidgetConfig): string[] {
const buttons: string[] = [];
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) {
buttons.push('fullscreen');
}
return buttons;
}
private setCardButtons(buttons: string[], config: WidgetConfig) {
config.enableFullscreen = buttons.includes('fullscreen');
}
} }

2
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.html

@ -38,7 +38,7 @@
formControlName="columns"> formControlName="columns">
</tb-data-keys-panel> </tb-data-keys-panel>
<div class="tb-widget-config-panel"> <div class="tb-widget-config-panel">
<div class="tb-widget-config-panel-title" translate>widget-config.appearance</div> <div class="tb-widget-config-panel-title" translate>widget-config.card-appearance</div>
<div class="tb-widget-config-row"> <div class="tb-widget-config-row">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle"> <mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle">
{{ 'widget-config.card-title' | translate }} {{ 'widget-config.card-title' | translate }}

9
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html

@ -68,6 +68,15 @@
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> <mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option>
</mat-chip-listbox> </mat-chip-listbox>
</div> </div>
<div class="tb-widget-config-row space-between same-padding">
<div>{{ 'widget-config.text-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">
<mat-divider vertical></mat-divider>
<tb-color-input asBoxInput
formControlName="color">
</tb-color-input>
</div>
</div>
<div class="tb-widget-config-row space-between same-padding"> <div class="tb-widget-config-row space-between same-padding">
<div>{{ 'widget-config.background-color' | translate }}</div> <div>{{ 'widget-config.background-color' | translate }}</div>
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px">

1
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts

@ -94,6 +94,7 @@ export class FlotBasicConfigComponent extends BasicWidgetConfigComponent {
this.widgetConfig.config.iconColor = config.iconColor; this.widgetConfig.config.iconColor = config.iconColor;
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; this.widgetConfig.config.settings = this.widgetConfig.config.settings || {};
this.setCardButtons(config.cardButtons, this.widgetConfig.config); this.setCardButtons(config.cardButtons, this.widgetConfig.config);
this.widgetConfig.config.color = config.color;
this.widgetConfig.config.backgroundColor = config.backgroundColor; this.widgetConfig.config.backgroundColor = config.backgroundColor;
this.widgetConfig.config.settings.grid = this.widgetConfig.config.settings.grid || {}; this.widgetConfig.config.settings.grid = this.widgetConfig.config.settings.grid || {};
this.widgetConfig.config.settings.grid.verticalLines = config.verticalLines; this.widgetConfig.config.settings.grid.verticalLines = config.verticalLines;

4
ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html

@ -147,12 +147,12 @@
formControlName="color"> formControlName="color">
</tb-color-input> </tb-color-input>
</div> </div>
<div class="tb-units-field"> <div *ngIf="!hideUnits" class="tb-units-field">
<tb-widget-units *ngIf="displayUnitsOrDigits" <tb-widget-units *ngIf="displayUnitsOrDigits"
formControlName="units"> formControlName="units">
</tb-widget-units> </tb-widget-units>
</div> </div>
<div class="tb-decimals-field"> <div *ngIf="!hideDecimals" class="tb-decimals-field">
<mat-form-field *ngIf="displayUnitsOrDigits" appearance="outline" class="tb-inline-field number" subscriptSizing="dynamic"> <mat-form-field *ngIf="displayUnitsOrDigits" appearance="outline" class="tb-inline-field number" subscriptSizing="dynamic">
<input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> <input matInput formControlName="decimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field> </mat-form-field>

8
ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts

@ -145,6 +145,14 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
return this.dataKeysPanelComponent.hideDataKeyColor; return this.dataKeysPanelComponent.hideDataKeyColor;
} }
get hideUnits(): boolean {
return this.dataKeysPanelComponent.hideUnits;
}
get hideDecimals(): boolean {
return this.dataKeysPanelComponent.hideDecimals;
}
get widgetType(): widgetType { get widgetType(): widgetType {
return this.widgetConfigComponent.widgetType; return this.widgetConfigComponent.widgetType;
} }

4
ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html

@ -23,8 +23,8 @@
<div class="tb-data-keys-header-cell" fxFlex translate>datakey.key</div> <div class="tb-data-keys-header-cell" fxFlex translate>datakey.key</div>
<div class="tb-data-keys-header-cell" fxFlex translate>datakey.label</div> <div class="tb-data-keys-header-cell" fxFlex translate>datakey.label</div>
<div *ngIf="!hideDataKeyColor" class="tb-data-keys-header-cell tb-color-header" translate>datakey.color</div> <div *ngIf="!hideDataKeyColor" class="tb-data-keys-header-cell tb-color-header" translate>datakey.color</div>
<div class="tb-data-keys-header-cell tb-units-header" translate>widget-config.units-short</div> <div *ngIf="!hideUnits" class="tb-data-keys-header-cell tb-units-header" translate>widget-config.units-short</div>
<div class="tb-data-keys-header-cell tb-decimals-header" translate>widget-config.decimals-short</div> <div *ngIf="!hideDecimals" class="tb-data-keys-header-cell tb-decimals-header" translate>widget-config.decimals-short</div>
<div class="tb-data-keys-header-cell tb-actions-header"></div> <div class="tb-data-keys-header-cell tb-actions-header"></div>
</div> </div>
<div *ngIf="keysFormArray().controls.length; else noKeys" class="tb-data-keys-body tb-drop-list" <div *ngIf="keysFormArray().controls.length; else noKeys" class="tb-data-keys-body tb-drop-list"

8
ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts

@ -100,6 +100,14 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
@coerceBoolean() @coerceBoolean()
hideDataKeyColor = false; hideDataKeyColor = false;
@Input()
@coerceBoolean()
hideUnits = false;
@Input()
@coerceBoolean()
hideDecimals = false;
@Input() @Input()
@coerceBoolean() @coerceBoolean()
hideSourceSelection = false; hideSourceSelection = false;

2
ui-ngx/src/app/modules/home/components/widget/config/datasources.component.html

@ -18,7 +18,7 @@
<div [formGroup]="datasourcesFormGroup" class="tb-widget-config-panel tb-datasources" [ngClass]="{'no-padding-bottom': !showAddDatasource}"> <div [formGroup]="datasourcesFormGroup" class="tb-widget-config-panel tb-datasources" [ngClass]="{'no-padding-bottom': !showAddDatasource}">
<div fxLayout="column"> <div fxLayout="column">
<div fxLayout="row" fxLayoutAlign="space-between center"> <div fxLayout="row" fxLayoutAlign="space-between center">
<div class="tb-widget-config-panel-title">{{ (singleDatasource ? 'widget-config.datasource' : 'widget-config.datasources') | translate }}</div> <div class="tb-widget-config-panel-title">{{ (singleDatasource ? (isAlarmSource ? 'widget-config.alarm-source' : 'widget-config.datasource') : 'widget-config.datasources') | translate }}</div>
<div fxFlex fxLayout="row" fxLayoutAlign="center center" *ngIf="timeseriesKeyError"> <div fxFlex fxLayout="row" fxLayoutAlign="center center" *ngIf="timeseriesKeyError">
<mat-error >{{ 'widget-config.timeseries-key-error' | translate }}</mat-error> <mat-error >{{ 'widget-config.timeseries-key-error' | translate }}</mat-error>
</div> </div>

9
ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts

@ -64,12 +64,17 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid
datasourceType = DatasourceType; datasourceType = DatasourceType;
public get isAlarmSource(): boolean {
return this.widgetConfigComponent.widgetType === widgetType.alarm;
}
public get basicMode(): boolean { public get basicMode(): boolean {
return !this.widgetConfigComponent.widgetEditMode && this.configMode === WidgetConfigMode.basic; return !this.widgetConfigComponent.widgetEditMode && this.configMode === WidgetConfigMode.basic;
} }
public get maxDatasources(): number { public get maxDatasources(): number {
return this.forceSingleDatasource ? 1 : this.widgetConfigComponent.modelValue?.typeParameters?.maxDatasources; return (this.forceSingleDatasource || this.isAlarmSource) ? 1 : this.widgetConfigComponent.modelValue?.typeParameters?.maxDatasources;
} }
public get singleDatasource(): boolean { public get singleDatasource(): boolean {
@ -266,7 +271,7 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid
private configModeChanged() { private configModeChanged() {
if (this.basicMode) { if (this.basicMode) {
let datasourcesMode = this.detectDatasourcesMode(this.datasourcesFormGroup.get('datasources').value); const datasourcesMode = this.detectDatasourcesMode(this.datasourcesFormGroup.get('datasources').value);
this.datasourcesModeChange(datasourcesMode); this.datasourcesModeChange(datasourcesMode);
} }
} }

8
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts

@ -138,6 +138,8 @@ import {
AlarmFilterConfigComponent, AlarmFilterConfigComponent,
AlarmFilterConfigData AlarmFilterConfigData
} from '@home/components/alarm/alarm-filter-config.component'; } from '@home/components/alarm/alarm-filter-config.component';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { UserId } from '@shared/models/id/user-id';
interface AlarmsTableWidgetSettings extends TableWidgetSettings { interface AlarmsTableWidgetSettings extends TableWidgetSettings {
alarmsTitle: string; alarmsTitle: string;
@ -606,6 +608,9 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
overlayRef.backdropClick().subscribe(() => { overlayRef.backdropClick().subscribe(() => {
overlayRef.dispose(); overlayRef.dispose();
}); });
const authUser = getCurrentAuthUser(this.store);
const assignedToCurrentUser = isDefinedAndNotNull(this.pageLink.assigneeId) && this.pageLink.assigneeId.id === authUser.userId;
const assigneeId = assignedToCurrentUser ? null : this.pageLink.assigneeId;
const providers: StaticProvider[] = [ const providers: StaticProvider[] = [
{ {
provide: ALARM_FILTER_CONFIG_DATA, provide: ALARM_FILTER_CONFIG_DATA,
@ -617,7 +622,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
severityList: deepClone(this.pageLink.severityList), severityList: deepClone(this.pageLink.severityList),
typeList: deepClone(this.pageLink.typeList), typeList: deepClone(this.pageLink.typeList),
searchPropagatedAlarms: this.pageLink.searchPropagatedAlarms, searchPropagatedAlarms: this.pageLink.searchPropagatedAlarms,
assignedToCurrentUser: isDefinedAndNotNull(this.pageLink.assigneeId) assignedToCurrentUser,
assigneeId
} }
} as AlarmFilterConfigData } as AlarmFilterConfigData
}, },

5
ui-ngx/src/app/shared/models/alarm.models.ts

@ -145,6 +145,11 @@ export interface AlarmAssignee {
email: string; email: string;
} }
export enum AlarmAssigneeOption {
noAssignee = 'noAssignee',
currentUser = 'currentUser'
}
export interface AlarmDataInfo extends AlarmInfo { export interface AlarmDataInfo extends AlarmInfo {
actionCellButtons?: TableCellButtonActionDescriptor[]; actionCellButtons?: TableCellButtonActionDescriptor[];
hasActions?: boolean; hasActions?: boolean;

54
ui-ngx/src/styles.scss

@ -1209,6 +1209,10 @@ mat-label {
border: 1px solid rgba(0, 0, 0, 0.12); border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 6px; border-radius: 6px;
} }
&.no-border {
box-shadow: none;
border-radius: 0;
}
&.tb-slide-toggle { &.tb-slide-toggle {
padding: 0; padding: 0;
gap: 0; gap: 0;
@ -1297,7 +1301,9 @@ mat-label {
color: #808080; color: #808080;
} }
.tb-widget-config-row { .tb-widget-config-row {
height: 56px; height: 100%;
padding-top: 7px;
padding-bottom: 7px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -1314,6 +1320,8 @@ mat-label {
} }
.mat-divider-vertical { .mat-divider-vertical {
height: 56px; height: 56px;
margin-top: -7px;
margin-bottom: -7px;
} }
.mat-mdc-form-field { .mat-mdc-form-field {
width: 106px; width: 106px;
@ -1324,6 +1332,29 @@ mat-label {
.fixed-title-width { .fixed-title-width {
min-width: 200px; min-width: 200px;
} }
.mat-slide:only-child {
margin: 8px 0;
}
&.tb-chips {
.mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field {
.mat-mdc-text-field-wrapper {
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
.mat-mdc-form-field-infix {
padding-top: 4px;
padding-bottom: 4px;
.mdc-evolution-chip-set {
min-height: 32px;
.mdc-evolution-chip {
height: 24px;
}
}
}
}
}
}
}
} }
.tb-widget-config-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field { .tb-widget-config-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field {
@ -1361,7 +1392,12 @@ mat-label {
} }
} }
.mat-mdc-form-field-icon-suffix { .mat-mdc-form-field-icon-suffix {
button.mat-mdc-icon-button { height: 40px;
font-size: 14px;
line-height: 40px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.38);
> button.mat-mdc-icon-button {
width: 40px; width: 40px;
height: 40px; height: 40px;
padding: 8px; padding: 8px;
@ -1371,6 +1407,12 @@ mat-label {
font-size: 20px; font-size: 20px;
} }
} }
> .mat-icon {
width: 20px;
height: 20px;
padding: 10px;
font-size: 20px;
}
} }
} }
} }
@ -1394,14 +1436,6 @@ mat-label {
} }
} }
} }
.mat-mdc-form-field-flex {
.mat-mdc-form-field-icon-suffix {
font-size: 14px;
line-height: 20px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.38);
}
}
} }
} }

Loading…
Cancel
Save