Timewindow: start adding ability to customize interval options
This commit is contained in:
parent
5f2530bcca
commit
aff0adbfa7
@ -337,6 +337,7 @@ import * as TimezonePanelComponent from '@shared/components/time/timezone-panel.
|
||||
import * as DatapointsLimitComponent from '@shared/components/time/datapoints-limit.component';
|
||||
import * as AggregationTypeSelectComponent from '@shared/components/aggregation/aggregation-type-select.component';
|
||||
import * as AggregationOptionsConfigComponent from '@shared/components/aggregation/aggregation-options-config-panel.component';
|
||||
import * as IntervalOptionsConfigPanelComponent from '@shared/components/time/interval-options-config-panel.component';
|
||||
|
||||
import { IModulesMap } from '@modules/common/modules-map.models';
|
||||
import { Observable, map, of } from 'rxjs';
|
||||
@ -476,6 +477,7 @@ class ModulesMap implements IModulesMap {
|
||||
'@shared/components/time/datapoints-limit.component': DatapointsLimitComponent,
|
||||
'@shared/components/aggregation/aggregation-type-select.component': AggregationTypeSelectComponent,
|
||||
'@shared/components/aggregation/aggregation-options-config-panel.component': AggregationOptionsConfigComponent,
|
||||
'@shared/components/time/interval-options-config-panel.component': IntervalOptionsConfigPanelComponent,
|
||||
'@shared/components/value-input.component': ValueInputComponent,
|
||||
'@shared/components/dashboard-autocomplete.component': DashboardAutocompleteComponent,
|
||||
'@shared/components/entity/entity-subtype-autocomplete.component': EntitySubTypeAutocompleteComponent,
|
||||
|
||||
@ -26,10 +26,4 @@
|
||||
.mat-mdc-select-value {
|
||||
min-width: 100px;
|
||||
}
|
||||
.mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper:has(.mat-mdc-form-field-icon-suffix:empty) {
|
||||
padding-right: 12px;
|
||||
}
|
||||
.mat-mdc-form-field-icon-suffix:empty {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<form [formGroup]="intervalOptionsConfigForm" class="tb-interval-options-form tb-form-panel no-border">
|
||||
<div class="tb-form-panel-title">{{ 'timewindow.edit-intervals-list' | translate }}</div>
|
||||
<div class="tb-form-hint tb-primary-fill">{{ 'timewindow.edit-intervals-list-hint' | translate }}</div>
|
||||
<div class="tb-form-table no-gap no-padding">
|
||||
<div class="tb-form-table-header">
|
||||
<div class="tb-form-table-header-cell">{{"timewindow.interval" | translate }}</div>
|
||||
</div>
|
||||
<div class="tb-form-table-body">
|
||||
<mat-selection-list formControlName="allowedIntervals">
|
||||
<mat-list-option *ngFor="let interval of intervals" [value]="interval.value" togglePosition="before">
|
||||
{{ interval.name | translate:interval.translateParams }}
|
||||
</mat-list-option>
|
||||
</mat-selection-list>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-flex flex-end no-gap">
|
||||
<button type="button"
|
||||
mat-button
|
||||
(click)="cancel()">
|
||||
{{ 'action.cancel' | translate }}
|
||||
</button>
|
||||
<button type="button"
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
(click)="update()"
|
||||
[disabled]="intervalOptionsConfigForm.invalid || !intervalOptionsConfigForm.dirty">
|
||||
{{ 'action.apply' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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 {
|
||||
.tb-interval-options-form {
|
||||
height: 100%;
|
||||
max-width: 600px;
|
||||
|
||||
.tb-form-table {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tb-form-table-body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tb-form-hint {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mdc-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.mat-mdc-list-item.mdc-list-item--with-leading-checkbox.mdc-list-item--with-one-line {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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, Input, OnInit } from '@angular/core';
|
||||
import { HistoryWindowType, RealtimeWindowType, TimewindowType } from '@shared/models/time/time.models';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-interval-options-config-panel',
|
||||
templateUrl: './interval-options-config-panel.component.html',
|
||||
styleUrls: ['./interval-options-config-panel.component.scss']
|
||||
})
|
||||
export class IntervalOptionsConfigPanelComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
allowedIntervals: Array<any>;
|
||||
|
||||
@Input()
|
||||
intervalType: RealtimeWindowType | HistoryWindowType;
|
||||
|
||||
@Input()
|
||||
timewindowType: TimewindowType;
|
||||
|
||||
@Input()
|
||||
onClose: (result: Array<any> | null) => void;
|
||||
|
||||
@Input()
|
||||
popoverComponent: TbPopoverComponent;
|
||||
|
||||
intervalOptionsConfigForm: FormGroup;
|
||||
|
||||
intervals = [];
|
||||
|
||||
constructor(private fb: FormBuilder) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.intervalOptionsConfigForm = this.fb.group({
|
||||
allowedIntervals: [this.allowedIntervals]
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.onClose) {
|
||||
this.onClose([]);
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
if (this.onClose) {
|
||||
this.onClose(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -23,5 +23,8 @@
|
||||
{{ timeIntervalTranslationMap.get(interval) | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<ng-container matSuffix>
|
||||
<ng-content select="[matSuffix]"></ng-content>
|
||||
</ng-container>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
|
||||
@ -25,6 +25,9 @@
|
||||
{{ interval.name | translate:interval.translateParams }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<ng-container matSuffix>
|
||||
<ng-content select="[matSuffix]"></ng-content>
|
||||
</ng-container>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
<section class="tb-form-row column-xs no-border no-padding tb-standard-fields advanced-input"
|
||||
|
||||
@ -65,6 +65,12 @@
|
||||
[disabledAdvanced]="timewindowForm.get('realtime.disableCustomInterval').value"
|
||||
[required]="timewindowForm.get('selectedTab').value === timewindowTypes.REALTIME &&
|
||||
timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL">
|
||||
<button *ngIf="!(timewindowForm.get('realtime.hideLastInterval').value ||
|
||||
timewindowForm.get('realtime.hideInterval').value)"
|
||||
matSuffix mat-icon-button type="button" class="tb-mat-24"
|
||||
(click)="configureRealtimeLastIntervalOptions($event)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
</tb-timeinterval>
|
||||
</div>
|
||||
</ng-container>
|
||||
@ -85,6 +91,12 @@
|
||||
appearance="outline"
|
||||
[required]="timewindowForm.get('selectedTab').value === timewindowTypes.REALTIME &&
|
||||
timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL">
|
||||
<button *ngIf="!(timewindowForm.get('realtime.hideQuickInterval').value ||
|
||||
timewindowForm.get('realtime.hideInterval').value)"
|
||||
matSuffix mat-icon-button type="button" class="tb-mat-24"
|
||||
(click)="configureRealtimeQuickIntervalOptions($event)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
</tb-quick-time-interval>
|
||||
</div>
|
||||
</section>
|
||||
@ -120,6 +132,12 @@
|
||||
[disabledAdvanced]="timewindowForm.get('history.disableCustomInterval').value"
|
||||
[required]="timewindowForm.get('selectedTab').value === timewindowTypes.HISTORY &&
|
||||
timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL">
|
||||
<button *ngIf="!(timewindowForm.get('history.hideLastInterval').value ||
|
||||
timewindowForm.get('history.hideInterval').value)"
|
||||
matSuffix mat-icon-button type="button" class="tb-mat-24"
|
||||
(click)="configureHistoryLastIntervalOptions($event)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
</tb-timeinterval>
|
||||
</div>
|
||||
</ng-container>
|
||||
@ -153,6 +171,12 @@
|
||||
appearance="outline"
|
||||
[required]="timewindowForm.get('selectedTab').value === timewindowTypes.HISTORY &&
|
||||
timewindowForm.get('history.historyType').value === historyTypes.INTERVAL">
|
||||
<button *ngIf="!(timewindowForm.get('history.hideQuickInterval').value ||
|
||||
timewindowForm.get('history.hideInterval').value)"
|
||||
matSuffix mat-icon-button type="button" class="tb-mat-24"
|
||||
(click)="configureHistoryQuickIntervalOptions($event)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
</tb-quick-time-interval>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -41,6 +41,7 @@ import { TbPopoverService } from '@shared/components/popover.service';
|
||||
import {
|
||||
AggregationOptionsConfigPanelComponent
|
||||
} from '@shared/components/aggregation/aggregation-options-config-panel.component';
|
||||
import { IntervalOptionsConfigPanelComponent } from '@shared/components/time/interval-options-config-panel.component';
|
||||
|
||||
export interface TimewindowConfigDialogData {
|
||||
quickIntervalOnly: boolean;
|
||||
@ -456,4 +457,50 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
|
||||
configureRealtimeLastIntervalOptions($event: Event) {
|
||||
const resFn = (res) => {};
|
||||
this.openIntervalOptionsConfig($event, [], resFn, RealtimeWindowType.LAST_INTERVAL);
|
||||
}
|
||||
|
||||
configureRealtimeQuickIntervalOptions($event: Event) {
|
||||
const resFn = (res) => {};
|
||||
this.openIntervalOptionsConfig($event, [], resFn, RealtimeWindowType.INTERVAL, TimewindowType.REALTIME);
|
||||
}
|
||||
|
||||
configureHistoryLastIntervalOptions($event: Event) {
|
||||
const resFn = (res) => {};
|
||||
this.openIntervalOptionsConfig($event, [], resFn, HistoryWindowType.LAST_INTERVAL);
|
||||
}
|
||||
|
||||
configureHistoryQuickIntervalOptions($event: Event) {
|
||||
const resFn = (res) => {};
|
||||
this.openIntervalOptionsConfig($event, [], resFn, HistoryWindowType.INTERVAL, TimewindowType.HISTORY);
|
||||
}
|
||||
|
||||
private openIntervalOptionsConfig($event: Event, allowedIntervals: Array<any>, resFn: (res) => void,
|
||||
intervalType: RealtimeWindowType | HistoryWindowType, timewindowType?: TimewindowType) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
const trigger = ($event.target || $event.srcElement || $event.currentTarget) as Element;
|
||||
if (this.popoverService.hasPopover(trigger)) {
|
||||
this.popoverService.hidePopover(trigger);
|
||||
} else {
|
||||
const intervalsConfigPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
||||
this.viewContainerRef, IntervalOptionsConfigPanelComponent, ['left', 'leftTop', 'leftBottom'], true, null,
|
||||
{
|
||||
allowedIntervals: deepClone(allowedIntervals),
|
||||
intervalType: intervalType,
|
||||
timewindowType: timewindowType,
|
||||
onClose: (result: Array<any> | null) => {
|
||||
intervalsConfigPopover.hide();
|
||||
resFn(result);
|
||||
}
|
||||
},
|
||||
{maxHeight: '90vh', height: '100%'},
|
||||
{}, {}, true, () => {}, {padding: 0});
|
||||
intervalsConfigPopover.tbComponentRef.instance.popoverComponent = intervalsConfigPopover;
|
||||
}
|
||||
this.cd.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,6 +220,7 @@ import { SvgXmlComponent } from '@shared/components/svg-xml.component';
|
||||
import { DatapointsLimitComponent } from '@shared/components/time/datapoints-limit.component';
|
||||
import { AggregationTypeSelectComponent } from '@shared/components/aggregation/aggregation-type-select.component';
|
||||
import { AggregationOptionsConfigPanelComponent } from '@shared/components/aggregation/aggregation-options-config-panel.component';
|
||||
import { IntervalOptionsConfigPanelComponent } from '@shared/components/time/interval-options-config-panel.component';
|
||||
|
||||
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
|
||||
return markedOptionsService;
|
||||
@ -311,6 +312,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
||||
DatapointsLimitComponent,
|
||||
AggregationTypeSelectComponent,
|
||||
AggregationOptionsConfigPanelComponent,
|
||||
IntervalOptionsConfigPanelComponent,
|
||||
DashboardSelectComponent,
|
||||
DashboardSelectPanelComponent,
|
||||
DatetimePeriodComponent,
|
||||
@ -520,6 +522,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
||||
DatapointsLimitComponent,
|
||||
AggregationTypeSelectComponent,
|
||||
AggregationOptionsConfigPanelComponent,
|
||||
IntervalOptionsConfigPanelComponent,
|
||||
DashboardSelectComponent,
|
||||
DatetimePeriodComponent,
|
||||
DatetimeComponent,
|
||||
|
||||
@ -4545,7 +4545,9 @@
|
||||
"disable-custom-interval": "Disable custom interval selection",
|
||||
"edit-aggregation-functions-list": "Edit aggregation functions list",
|
||||
"edit-aggregation-functions-list-hint": "List of available options can be specified.",
|
||||
"allowed-aggregation-functions": "Allowed aggregation functions"
|
||||
"allowed-aggregation-functions": "Allowed aggregation functions",
|
||||
"edit-intervals-list": "Edit intervals list",
|
||||
"edit-intervals-list-hint": "List of available interval options can be specified. It is possible to configure the grouping intervals list and default grouping interval."
|
||||
},
|
||||
"tooltip": {
|
||||
"trigger": "Trigger",
|
||||
|
||||
@ -381,6 +381,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
&.mat-mdc-form-field-has-icon-suffix {
|
||||
.mat-mdc-form-field-icon-suffix:empty {
|
||||
padding: 0;
|
||||
}
|
||||
.mat-mdc-text-field-wrapper:has(.mat-mdc-form-field-icon-suffix:empty) {
|
||||
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
||||
padding-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mat-mdc-text-field-wrapper {
|
||||
&.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) {
|
||||
&:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(.mdc-text-field--invalid):not(:hover) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user