Browse Source

Merge branch 'develop/3.5.2' into feature/basic-widget-config

pull/8721/head
Igor Kulikov 3 years ago
parent
commit
c6dd11f576
  1. 2
      ui-ngx/src/app/modules/common/modules-map.ts
  2. 25
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html
  3. 6
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts
  4. 25
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html
  5. 6
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts
  6. 3
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html
  7. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts
  8. 1
      ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts
  9. 116
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
  10. 3
      ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html
  11. 3
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html
  12. 67
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html
  13. 50
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.scss
  14. 21
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts
  15. 8
      ui-ngx/src/app/shared/components/notification/template-autocomplete.component.html
  16. 28
      ui-ngx/src/app/shared/components/notification/template-autocomplete.component.ts
  17. 27
      ui-ngx/src/app/shared/components/rule-chain/rule-chain-select.component.html
  18. 26
      ui-ngx/src/app/shared/components/rule-chain/rule-chain-select.component.scss
  19. 109
      ui-ngx/src/app/shared/components/rule-chain/rule-chain-select.component.ts
  20. 7
      ui-ngx/src/app/shared/shared.module.ts
  21. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
ui-ngx/src/app/modules/common/modules-map.ts

@ -300,6 +300,7 @@ import * as QueueFormComponent from '@home/components/queue/queue-form.component
import * as AssetProfileComponent from '@home/components/profile/asset-profile.component';
import * as AssetProfileDialogComponent from '@home/components/profile/asset-profile-dialog.component';
import * as AssetProfileAutocompleteComponent from '@home/components/profile/asset-profile-autocomplete.component';
import * as RuleChainSelectComponent from '@shared/components/rule-chain/rule-chain-select.component';
import { IModulesMap } from '@modules/common/modules-map.models';
@ -418,6 +419,7 @@ class ModulesMap implements IModulesMap {
'@shared/components/time/quick-time-interval.component': QuickTimeIntervalComponent,
'@shared/components/dashboard-select.component': DashboardSelectComponent,
'@shared/components/dashboard-select-panel.component': DashboardSelectPanelComponent,
'@shared/components/rule-chain/rule-chain-select.component': RuleChainSelectComponent,
'@shared/components/time/datetime-period.component': DatetimePeriodComponent,
'@shared/components/time/datetime.component': DatetimeComponent,
'@shared/components/time/timezone-select.component': TimezoneSelectComponent,

25
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html

@ -66,4 +66,29 @@
</ng-template>
</mat-expansion-panel>
</fieldset>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-column-visibility</mat-label>
<mat-select formControlName="defaultColumnVisibility">
<mat-option [value]="'visible'">
{{ 'widgets.table.column-visibility-visible' | translate }}
</mat-option>
<mat-option [value]="'hidden'">
{{ 'widgets.table.column-visibility-hidden' | translate }}
</mat-option>
<mat-option [value]="'hidden-mobile'">
{{ 'widgets.table.column-visibility-hidden-mobile' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.column-selection-to-display</mat-label>
<mat-select formControlName="columnSelectionToDisplay">
<mat-option [value]="'enabled'">
{{ 'widgets.table.column-selection-to-display-enabled' | translate }}
</mat-option>
<mat-option [value]="'disabled'">
{{ 'widgets.table.column-selection-to-display-disabled' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>

6
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts

@ -43,7 +43,9 @@ export class TimeseriesTableKeySettingsComponent extends WidgetSettingsComponent
useCellStyleFunction: false,
cellStyleFunction: '',
useCellContentFunction: false,
cellContentFunction: ''
cellContentFunction: '',
defaultColumnVisibility: 'visible',
columnSelectionToDisplay: 'enabled'
};
}
@ -53,6 +55,8 @@ export class TimeseriesTableKeySettingsComponent extends WidgetSettingsComponent
cellStyleFunction: [settings.cellStyleFunction, [Validators.required]],
useCellContentFunction: [settings.useCellContentFunction, []],
cellContentFunction: [settings.cellContentFunction, [Validators.required]],
defaultColumnVisibility: [settings.defaultColumnVisibility, []],
columnSelectionToDisplay: [settings.columnSelectionToDisplay, []],
});
}

25
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html

@ -73,4 +73,29 @@
</ng-template>
</mat-expansion-panel>
</fieldset>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.default-column-visibility</mat-label>
<mat-select formControlName="defaultColumnVisibility">
<mat-option [value]="'visible'">
{{ 'widgets.table.column-visibility-visible' | translate }}
</mat-option>
<mat-option [value]="'hidden'">
{{ 'widgets.table.column-visibility-hidden' | translate }}
</mat-option>
<mat-option [value]="'hidden-mobile'">
{{ 'widgets.table.column-visibility-hidden-mobile' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>widgets.table.column-selection-to-display</mat-label>
<mat-select formControlName="columnSelectionToDisplay">
<mat-option [value]="'enabled'">
{{ 'widgets.table.column-selection-to-display-enabled' | translate }}
</mat-option>
<mat-option [value]="'disabled'">
{{ 'widgets.table.column-selection-to-display-disabled' | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</section>

6
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts

@ -44,7 +44,9 @@ export class TimeseriesTableLatestKeySettingsComponent extends WidgetSettingsCom
useCellStyleFunction: false,
cellStyleFunction: '',
useCellContentFunction: false,
cellContentFunction: ''
cellContentFunction: '',
defaultColumnVisibility: 'visible',
columnSelectionToDisplay: 'enabled'
};
}
@ -56,6 +58,8 @@ export class TimeseriesTableLatestKeySettingsComponent extends WidgetSettingsCom
cellStyleFunction: [settings.cellStyleFunction, [Validators.required]],
useCellContentFunction: [settings.useCellContentFunction, []],
cellContentFunction: [settings.cellContentFunction, [Validators.required]],
defaultColumnVisibility: [settings.defaultColumnVisibility, []],
columnSelectionToDisplay: [settings.columnSelectionToDisplay, []],
});
}

3
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html

@ -23,6 +23,9 @@
<mat-checkbox formControlName="enableSearch">
{{ 'widgets.table.enable-search' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="enableSelectColumnDisplay">
{{ 'widgets.table.enable-select-column-display' | translate }}
</mat-checkbox>
</section>
<section fxLayout="column" fxFlex>
<mat-checkbox formControlName="enableStickyHeader">

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts

@ -41,6 +41,7 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon
protected defaultSettings(): WidgetSettings {
return {
enableSearch: true,
enableSelectColumnDisplay: true,
enableStickyHeader: true,
enableStickyAction: true,
reserveSpaceForHiddenAction: 'true',
@ -59,6 +60,7 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon
protected onSettingsSet(settings: WidgetSettings) {
this.timeseriesTableWidgetSettingsForm = this.fb.group({
enableSearch: [settings.enableSearch, []],
enableSelectColumnDisplay: [settings.enableSelectColumnDisplay, []],
enableStickyHeader: [settings.enableStickyHeader, []],
enableStickyAction: [settings.enableStickyAction, []],
reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []],

1
ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts

@ -31,6 +31,7 @@ type ColumnSelectionOptions = 'enabled' | 'disabled';
export interface TableWidgetSettings {
enableSearch: boolean;
enableSelectColumnDisplay: boolean;
enableStickyAction: boolean;
enableStickyHeader: boolean;
displayPagination: boolean;

116
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts

@ -19,9 +19,12 @@ import {
ChangeDetectorRef,
Component,
ElementRef,
Injector,
Input,
OnDestroy,
OnInit,
QueryList,
StaticProvider,
ViewChild,
ViewChildren,
ViewContainerRef
@ -64,8 +67,9 @@ import {
CellStyleInfo,
checkHasActions,
constructTableCssString,
DisplayColumn,
getCellContentInfo,
getCellStyleInfo,
getCellStyleInfo, getColumnDefaultVisibility, getColumnSelectionAvailability,
getRowStyleInfo,
getTableCellButtonActions,
noDataMessage,
@ -75,12 +79,17 @@ import {
TableWidgetDataKeySettings,
TableWidgetSettings
} from '@home/components/widget/lib/table-widget.models';
import { Overlay } from '@angular/cdk/overlay';
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { SubscriptionEntityInfo } from '@core/api/widget-api.models';
import { DatePipe } from '@angular/common';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ResizeObserver } from '@juggle/resize-observer';
import { hidePageSizePixelValue } from '@shared/models/constants';
import {
DISPLAY_COLUMNS_PANEL_DATA,
DisplayColumnsPanelComponent
} from '@home/components/widget/lib/display-columns-panel.component';
import { ComponentPortal } from '@angular/cdk/portal';
export interface TimeseriesTableWidgetSettings extends TableWidgetSettings {
showTimestamp: boolean;
@ -105,6 +114,8 @@ interface TimeseriesHeader {
dataKey: DataKey;
sortable: boolean;
show: boolean;
columnDefaultVisibility?: boolean;
columnSelectionAvailability?: boolean;
styleInfo: CellStyleInfo;
contentInfo: CellContentInfo;
order?: number;
@ -131,7 +142,7 @@ interface TimeseriesTableSource {
templateUrl: './timeseries-table-widget.component.html',
styleUrls: ['./timeseries-table-widget.component.scss', './table-widget.scss']
})
export class TimeseriesTableWidgetComponent extends PageComponent implements OnInit, AfterViewInit {
export class TimeseriesTableWidgetComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy {
@Input()
ctx: WidgetContext;
@ -169,6 +180,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
private useEntityLabel = false;
private dateFormatFilter: string;
private displayedColumns: Array<DisplayColumn[]> = [];
private rowStylesInfo: RowStyleInfo;
private subscriptions: Subscription[] = [];
@ -184,6 +197,15 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
}
};
private columnDisplayAction: WidgetAction = {
name: 'entity.columns-to-display',
show: true,
icon: 'view_column',
onAction: ($event) => {
this.editColumnsToDisplay($event);
}
};
constructor(protected store: Store<AppState>,
private elementRef: ElementRef,
private overlay: Overlay,
@ -275,11 +297,12 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
}
private initialize() {
this.ctx.widgetActions = [this.searchAction ];
this.ctx.widgetActions = [this.searchAction, this.columnDisplayAction];
this.setCellButtonAction = !!this.ctx.actionsApi.getActionDescriptors('actionCellButton').length;
this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true;
this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true;
@ -365,9 +388,82 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
this.sources.push(source);
}
}
this.prepareDisplayedColumn();
this.sources[this.sourceIndex].displayedColumns =
this.displayedColumns[this.sourceIndex].filter(value => value.display).map(value => value.def);
this.updateActiveEntityInfo();
}
private editColumnsToDisplay($event: Event) {
if ($event) {
$event.stopPropagation();
}
const target = $event.target || $event.currentTarget;
const config = new OverlayConfig();
config.backdropClass = 'cdk-overlay-transparent-backdrop';
config.hasBackdrop = true;
const connectedPosition: ConnectedPosition = {
originX: 'end',
originY: 'bottom',
overlayX: 'end',
overlayY: 'top'
};
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
.withPositions([connectedPosition]);
const overlayRef = this.overlay.create(config);
overlayRef.backdropClick().subscribe(() => {
overlayRef.dispose();
});
const source = this.sources[this.sourceIndex];
this.prepareDisplayedColumn();
const providers: StaticProvider[] = [
{
provide: DISPLAY_COLUMNS_PANEL_DATA,
useValue: {
columns: this.displayedColumns[this.sourceIndex],
columnsUpdated: (newColumns) => {
source.displayedColumns = newColumns.filter(value => value.display).map(value => value.def);
this.clearCache();
}
}
},
{
provide: OverlayRef,
useValue: overlayRef
}
];
const injector = Injector.create({parent: this.viewContainerRef.injector, providers});
overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent,
this.viewContainerRef, injector));
this.ctx.detectChanges();
}
private prepareDisplayedColumn() {
if (!this.displayedColumns[this.sourceIndex]) {
this.displayedColumns[this.sourceIndex] = this.sources[this.sourceIndex].displayedColumns.map(value => {
let title = '';
const header = this.sources[this.sourceIndex].header.find(column => column.index.toString() === value);
if (value === '0') {
title = 'Timestamp';
} else if (value === 'actions') {
title = 'Actions';
} else {
title = header.dataKey.name;
}
return {
title,
def: value,
display: header?.columnDefaultVisibility ?? true,
selectable: header?.columnSelectionAvailability ?? true
};
});
}
}
private prepareHeader(datasource: Datasource): TimeseriesHeader[] {
const dataKeys = datasource.dataKeys;
const latestDataKeys = datasource.latestDataKeys;
@ -377,6 +473,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
const keySettings: TableWidgetDataKeySettings = dataKey.settings;
const styleInfo = getCellStyleInfo(keySettings, 'value, rowData, ctx');
const contentInfo = getCellContentInfo(keySettings, 'value, rowData, ctx');
const columnDefaultVisibility = getColumnDefaultVisibility(keySettings, this.ctx);
const columnSelectionAvailability = getColumnSelectionAvailability(keySettings);
contentInfo.units = dataKey.units;
contentInfo.decimals = dataKey.decimals;
header.push({
@ -386,6 +484,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
styleInfo,
contentInfo,
show: true,
columnDefaultVisibility,
columnSelectionAvailability,
order: index + 2
});
});
@ -396,6 +496,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
const keySettings: TimeseriesWidgetLatestDataKeySettings = dataKey.settings;
const styleInfo = getCellStyleInfo(keySettings, 'value, rowData, ctx');
const contentInfo = getCellContentInfo(keySettings, 'value, rowData, ctx');
const columnDefaultVisibility = getColumnDefaultVisibility(keySettings, this.ctx);
const columnSelectionAvailability = getColumnSelectionAvailability(keySettings);
contentInfo.units = dataKey.units;
contentInfo.decimals = dataKey.decimals;
header.push({
@ -405,13 +507,13 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
styleInfo,
contentInfo,
show: isDefinedAndNotNull(keySettings.show) ? keySettings.show : true,
columnDefaultVisibility,
columnSelectionAvailability,
order: isDefinedAndNotNull(keySettings.order) ? keySettings.order : (index + 2)
});
});
}
header = header.sort((a, b) => {
return a.order - b.order;
});
header = header.sort((a, b) => a.order - b.order);
return header;
}

3
ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html

@ -59,8 +59,7 @@
</mat-error>
</mat-form-field>
<tb-template-autocomplete
required
allowCreate
required allowCreate allowEdit
formControlName="templateId"
[notificationTypes]="ruleNotificationForm.get('triggerType').value">
</tb-template-autocomplete>

3
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html

@ -49,8 +49,7 @@
</div>
<div *ngIf="notificationRequestForm.get('useTemplate').value; else scratchTemplate">
<tb-template-autocomplete
required
allowCreate
required allowCreate allowEdit
formControlName="templateId"
[notificationTypes]="notificationType.GENERAL">
</tb-template-autocomplete>

67
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html

@ -15,55 +15,48 @@
limitations under the License.
-->
<div class="mat-content" fxFlex tb-fullscreen [fullscreen]="isFullscreen" tb-hotkeys [hotkeys]="hotKeys"
[cheatSheet]="cheatSheetComponent"
fxLayout="column" class="tb-rulechain">
<div class="mat-content tb-rulechain" fxFlex tb-fullscreen [fullscreen]="isFullscreen" tb-hotkeys [hotkeys]="hotKeys"
[cheatSheet]="cheatSheetComponent" fxLayout="column">
<tb-hotkeys-cheatsheet #cheatSheetComponent></tb-hotkeys-cheatsheet>
<section class="tb-rulechain-container" fxFlex fxLayout="column">
<div class="tb-rulechain-layout" fxFlex fxLayout="row">
<section fxLayout="row"
class="tb-header-buttons tb-library-open" [fxShow]="!isLibraryOpen">
<button color="primary"
mat-mini-fab
class="tb-btn-header tb-btn-open-library"
(click)="isLibraryOpen = true"
matTooltip="{{ 'rulenode.open-node-library' | translate }}"
matTooltipPosition="above">
<mat-icon>menu</mat-icon>
</button>
</section>
<mat-drawer-container style="width: 100%; height: 100%;">
<mat-drawer class="tb-rulechain-library mat-elevation-z4"
disableClose="true"
mode="side"
[opened]="isLibraryOpen"
#drawer
opened
position="start"
fxLayout="column">
<mat-toolbar color="primary" class="tb-dark">
<tb-rule-chain-select
fxFlex
*ngIf="!isImport"
[ruleChainType]="ruleChainType"
[disabled]="isDirtyValue"
[(ngModel)]="ruleChain.id.id"
(ngModelChange)="currentRuleChainIdChanged(ruleChain.id?.id)">
</tb-rule-chain-select>
</mat-toolbar>
<mat-toolbar>
<div class="mat-toolbar-tools">
<button mat-icon-button class="tb-small"
matTooltip="{{'rulenode.search' | translate}}"
matTooltipPosition="above">
<mat-icon>search</mat-icon>
</button>
<mat-form-field fxFlex class="tb-appearance-transparent">
<button mat-icon-button matPrefix class="tb-small"
matTooltip="{{'rulenode.search' | translate}}"
matTooltipPosition="above">
<mat-icon>search</mat-icon>
</button>
<input #ruleNodeSearchInput matInput
[(ngModel)]="ruleNodeTypeSearch"
placeholder="{{'rulenode.search' | translate}}"/>
<button mat-icon-button matSuffix class="tb-small"
[fxShow]="ruleNodeTypeSearch !== ''"
(click)="ruleNodeTypeSearch = ''; updateRuleChainLibrary()"
matTooltip="{{'action.clear-search' | translate}}"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<button mat-icon-button class="tb-small"
[fxShow]="ruleNodeTypeSearch !== ''"
(click)="ruleNodeTypeSearch = ''; updateRuleChainLibrary()"
matTooltip="{{'action.clear-search' | translate}}"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
</button>
<button mat-icon-button class="tb-small"
(click)="isLibraryOpen = false"
matTooltip="{{'action.close' | translate}}"
matTooltipPosition="above">
<mat-icon>chevron_left</mat-icon>
</button>
</div>
</mat-toolbar>
<div class="tb-rulechain-library-panel-group">
@ -164,6 +157,14 @@
</tb-details-panel>
</mat-drawer>
<mat-drawer-content class="tb-rulechain-graph-content">
<button color="primary"
mat-mini-fab
class="tb-library-node-btn"
(click)="drawer.toggle();"
matTooltip="{{ (drawer.opened ? 'rulenode.close-node-library' : 'rulenode.open-node-library') | translate }}"
matTooltipPosition="above">
<mat-icon class="tb-library-node-btn-icon" [ngClass]="{'tb-library-node-btn-icon-toggled' : drawer.opened}">chevron_right</mat-icon>
</button>
<button #versionControlButton
*ngIf="!isImport"
color="primary"

50
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.scss

@ -54,20 +54,43 @@
z-index: 2;
}
section.tb-header-buttons.tb-library-open {
.tb-library-node-btn {
width: 20px;
height: 90px;
position: absolute;
top: 0;
left: 0;
z-index: 2;
pointer-events: none;
.mdc-fab.tb-btn-open-library {
top: 0;
top: 50%;
z-index: 1000;
opacity: 0.5;
border-radius: 0 15px 15px 0 !important;
transform: translateY(-50%);
&-icon {
transition: transform 0.4s !important;
&-toggled {
transform: rotateZ(180deg);
}
}
&:before {
width: 20px;
height: 30px;
top: -28px;
left: 0;
content: "";
pointer-events: none;
border-radius: 0 0 0 15px;
box-shadow: -10px 0px 0 0px #305680;
border: none;
}
&:after {
position: absolute;
width: 20px;
height: 30px;
bottom: -28px;
left: 0;
width: 36px;
height: 36px;
margin: 4px 0 0 4px;
line-height: 36px;
opacity: .5;
content: "";
pointer-events: none;
border-top-left-radius: 15px;
box-shadow: -10px 0px 0px 0px #305680;
border: none;
}
}
@ -77,8 +100,7 @@
min-width: 250px;
.mat-toolbar {
height: 48px;
min-height: 48px;
height: min-content;
padding: 0;
.mat-toolbar-tools {

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

@ -15,7 +15,9 @@
///
import {
AfterViewChecked,
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
@ -90,6 +92,7 @@ import { MatMiniFabButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
import { VersionControlComponent } from '@home/components/vc/version-control.component';
import { ComponentClusteringMode } from '@shared/models/component-descriptor.models';
import { MatDrawer } from '@angular/material/sidenav';
import Timeout = NodeJS.Timeout;
@Component({
@ -99,7 +102,7 @@ import Timeout = NodeJS.Timeout;
encapsulation: ViewEncapsulation.None
})
export class RuleChainPageComponent extends PageComponent
implements AfterViewInit, OnInit, OnDestroy, HasDirtyFlag, ISearchableComponent {
implements AfterViewInit, OnInit, OnDestroy, HasDirtyFlag, ISearchableComponent, AfterViewChecked {
get isDirty(): boolean {
return this.isDirtyValue || this.isImport;
@ -121,6 +124,8 @@ export class RuleChainPageComponent extends PageComponent
@ViewChild('ruleChainMenuTrigger', {static: true}) ruleChainMenuTrigger: MatMenuTrigger;
@ViewChild('drawer') drawer: MatDrawer;
eventTypes = EventType;
debugEventTypes = DebugEventType;
@ -159,7 +164,6 @@ export class RuleChainPageComponent extends PageComponent
hotKeys: Hotkey[] = [];
enableHotKeys = true;
isLibraryOpen = true;
ruleNodeSearch = '';
ruleNodeTypeSearch = '';
@ -266,6 +270,7 @@ export class RuleChainPageComponent extends PageComponent
private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
private changeDetector: ChangeDetectorRef,
public dialog: MatDialog,
public dialogService: DialogService,
public fb: UntypedFormBuilder) {
@ -281,6 +286,10 @@ export class RuleChainPageComponent extends PageComponent
ngOnInit() {
}
ngAfterViewChecked(){
this.changeDetector.detectChanges();
}
ngAfterViewInit() {
fromEvent(this.ruleNodeSearchInputField.nativeElement, 'keyup')
.pipe(
@ -299,6 +308,14 @@ export class RuleChainPageComponent extends PageComponent
this.rxSubscription.unsubscribe();
}
currentRuleChainIdChanged(ruleChainId: string) {
if (this.ruleChainType === RuleChainType.CORE) {
this.router.navigateByUrl(`ruleChains/${ruleChainId}`);
} else {
this.router.navigateByUrl(`edgeManagement/ruleChains/${ruleChainId}`);
}
}
onSearchTextUpdated(searchText: string) {
this.ruleNodeSearch = searchText;
this.updateRuleNodesHighlight();

8
ui-ngx/src/app/shared/components/notification/template-autocomplete.component.html

@ -29,6 +29,14 @@
(click)="clear()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<button *ngIf="selectTemplateFormGroup.get('templateName').value && !disabled && allowEdit"
type="button"
matSuffix mat-icon-button aria-label="Edit"
matTooltip="{{ 'notification.edit-notification-template' | translate }}"
matTooltipPosition="above"
(click)="editTemplate($event)">
<mat-icon class="material-icons">edit</mat-icon>
</button>
<button #createTemplateButton
mat-button color="primary" matSuffix
*ngIf="allowCreate && !selectTemplateFormGroup.get('templateName').value && !disabled"

28
ui-ngx/src/app/shared/components/notification/template-autocomplete.component.ts

@ -17,7 +17,7 @@
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
import { catchError, debounceTime, map, share, switchMap, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
@ -66,6 +66,10 @@ export class TemplateAutocompleteComponent implements ControlValueAccessor, OnIn
@coerceBoolean()
allowCreate = false;
@Input()
@coerceBoolean()
allowEdit = false;
@Input()
disabled: boolean;
@ -196,18 +200,34 @@ export class TemplateAutocompleteComponent implements ControlValueAccessor, OnIn
}, 0);
}
editTemplate($event: Event) {
if ($event) {
$event.stopPropagation();
}
this.notificationService.getNotificationTemplateById(this.modelValue.id).subscribe(
(template) => {
this.openNotificationTemplateDialog({template});
}
);
}
createTemplate($event: Event, button: MatButton) {
if ($event) {
$event.stopPropagation();
}
button._elementRef.nativeElement.blur();
this.openNotificationTemplateDialog({
isAdd: true,
predefinedType: this.notificationTypes
});
}
private openNotificationTemplateDialog(dialogData?: TemplateNotificationDialogData) {
this.dialog.open<TemplateNotificationDialogComponent, TemplateNotificationDialogData,
NotificationTemplate>(TemplateNotificationDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
predefinedType: this.notificationTypes
}
data: dialogData
}).afterClosed()
.subscribe((res) => {
if (res) {

27
ui-ngx/src/app/shared/components/rule-chain/rule-chain-select.component.html

@ -0,0 +1,27 @@
<!--
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.
-->
<mat-select fxFlex
class="tb-rule-chain-select"
[required]="required"
[disabled]="disabled"
[(ngModel)]="ruleChainId"
(ngModelChange)="ruleChainIdChanged()">
<mat-option *ngFor="let ruleChain of ruleChains$ | async" [value]="ruleChain.id.id">
{{ruleChain.name}}
</mat-option>
</mat-select>

26
ui-ngx/src/app/shared/components/rule-chain/rule-chain-select.component.scss

@ -0,0 +1,26 @@
/**
* 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.
*/
:host {
min-width: 52px;
width: 100%;
padding: 0 6px;
.tb-rule-chain-select {
display: flex;
height: 48px;
min-height: 100%;
pointer-events: all;
}
}

109
ui-ngx/src/app/shared/components/rule-chain/rule-chain-select.component.ts

@ -0,0 +1,109 @@
///
/// 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, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs';
import { PageLink } from '@shared/models/page/page-link';
import { map, share } from 'rxjs/operators';
import { PageData } from '@shared/models/page/page-data';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { TooltipPosition } from '@angular/material/tooltip';
import { RuleChain, RuleChainType } from '@shared/models/rule-chain.models';
import { RuleChainService } from '@core/http/rule-chain.service';
import { isDefinedAndNotNull } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion';
import { Direction } from '@shared/models/page/sort-order';
@Component({
selector: 'tb-rule-chain-select',
templateUrl: './rule-chain-select.component.html',
styleUrls: ['./rule-chain-select.component.scss'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RuleChainSelectComponent),
multi: true
}]
})
export class RuleChainSelectComponent implements ControlValueAccessor, OnInit {
@Input()
tooltipPosition: TooltipPosition = 'above';
@Input()
@coerceBoolean()
required: boolean;
@Input()
@coerceBoolean()
disabled: boolean;
@Input()
ruleChainType: RuleChainType = RuleChainType.CORE;
ruleChains$: Observable<Array<RuleChain>>;
ruleChainId: string | null;
private propagateChange = (v: any) => { };
constructor(private ruleChainService: RuleChainService) {
}
ngOnInit() {
const pageLink = new PageLink(100, 0, null, {
property: 'name',
direction: Direction.ASC
});
this.ruleChains$ = this.getRuleChains(pageLink).pipe(
map((pageData) => pageData.data),
share()
);
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
writeValue(value: string | null): void {
if (isDefinedAndNotNull(value)) {
this.ruleChainId = value;
}
}
ruleChainIdChanged() {
this.updateView();
}
private updateView() {
this.propagateChange(this.ruleChainId);
}
private getRuleChains(pageLink: PageLink): Observable<PageData<RuleChain>> {
return this.ruleChainService.getRuleChains(pageLink, this.ruleChainType, {ignoreLoading: true});
}
}

7
ui-ngx/src/app/shared/shared.module.ts

@ -191,6 +191,7 @@ import {
import { ColorPickerComponent } from '@shared/components/color-picker/color-picker.component';
import { ShortNumberPipe } from '@shared/pipe/short-number.pipe';
import { ToggleHeaderComponent } from '@shared/components/toggle-header.component';
import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-chain-select.component';
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
return markedOptionsService;
@ -360,7 +361,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
GtMdLgLayoutGapDirective,
GtMdLgShowHideDirective,
ColorPickerComponent,
ToggleHeaderComponent
ToggleHeaderComponent,
RuleChainSelectComponent
],
imports: [
CommonModule,
@ -586,7 +588,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
GtMdLgLayoutGapDirective,
GtMdLgShowHideDirective,
ColorPickerComponent,
ToggleHeaderComponent
ToggleHeaderComponent,
RuleChainSelectComponent
]
})
export class SharedModule { }

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -3349,6 +3349,7 @@
"events": "Events",
"search": "Search nodes",
"open-node-library": "Open node library",
"close-node-library": "Close node library",
"add": "Add rule node",
"name": "Name",
"name-required": "Name is required.",

Loading…
Cancel
Save