UI: timewindow panel history mode redesign

This commit is contained in:
Chantsova Ekaterina 2024-06-28 18:29:47 +03:00
parent 9b7c8595ee
commit 3d7f6adbdd
7 changed files with 172 additions and 152 deletions

View File

@ -17,7 +17,7 @@
--> -->
<section class="interval-section" fxLayout="row" fxFlex> <section class="interval-section" fxLayout="row" fxFlex>
<mat-form-field fxFlex [subscriptSizing]="subscriptSizing" [appearance]="appearance"> <mat-form-field fxFlex [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timewindow.interval</mat-label> <mat-label *ngIf="displayLabel" translate>timewindow.interval</mat-label>
<mat-select [disabled]="disabled" [(ngModel)]="modelValue" (ngModelChange)="onIntervalChange()"> <mat-select [disabled]="disabled" [(ngModel)]="modelValue" (ngModelChange)="onIntervalChange()">
<mat-option *ngFor="let interval of intervals" [value]="interval"> <mat-option *ngFor="let interval of intervals" [value]="interval">
{{ timeIntervalTranslationMap.get(interval) | translate}} {{ timeIntervalTranslationMap.get(interval) | translate}}

View File

@ -18,6 +18,7 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { QuickTimeInterval, QuickTimeIntervalTranslationMap } from '@shared/models/time/time.models'; import { QuickTimeInterval, QuickTimeIntervalTranslationMap } from '@shared/models/time/time.models';
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({ @Component({
selector: 'tb-quick-time-interval', selector: 'tb-quick-time-interval',
@ -40,6 +41,10 @@ export class QuickTimeIntervalComponent implements OnInit, ControlValueAccessor
rendered = false; rendered = false;
@Input()
@coerceBoolean()
displayLabel = true;
@Input() disabled: boolean; @Input() disabled: boolean;
@Input() onlyCurrentInterval = false; @Input() onlyCurrentInterval = false;

View File

@ -22,19 +22,19 @@
</section> </section>
<section class="interval-section" fxLayout="column" fxFlex [fxShow]="advanced && (isEdit || !hideFlag)"> <section class="interval-section" fxLayout="column" fxFlex [fxShow]="advanced && (isEdit || !hideFlag)">
<section fxLayout="row wrap" fxLayoutAlign="start start" fxFlex fxLayoutGap="6px"> <section fxLayout="row wrap" fxLayoutAlign="start start" fxFlex fxLayoutGap="6px">
<mat-form-field class="number-input" [appearance]="appearance"> <mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.days</mat-label> <mat-label translate>timeinterval.days</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" min="0" [(ngModel)]="days" (ngModelChange)="onTimeInputChange('days')"/> <input matInput [disabled]="hideFlag || disabled" type="number" step="1" min="0" [(ngModel)]="days" (ngModelChange)="onTimeInputChange('days')"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="number-input" [appearance]="appearance"> <mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.hours</mat-label> <mat-label translate>timeinterval.hours</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="hours" (ngModelChange)="onTimeInputChange('hours')"/> <input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="hours" (ngModelChange)="onTimeInputChange('hours')"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="number-input" [appearance]="appearance"> <mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.minutes</mat-label> <mat-label translate>timeinterval.minutes</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="mins" (ngModelChange)="onTimeInputChange('mins')"/> <input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="mins" (ngModelChange)="onTimeInputChange('mins')"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="number-input" [appearance]="appearance"> <mat-form-field class="number-input" [subscriptSizing]="subscriptSizing" [appearance]="appearance">
<mat-label translate>timeinterval.seconds</mat-label> <mat-label translate>timeinterval.seconds</mat-label>
<input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="secs" (ngModelChange)="onTimeInputChange('secs')"/> <input matInput [disabled]="hideFlag || disabled" type="number" step="1" [(ngModel)]="secs" (ngModelChange)="onTimeInputChange('secs')"/>
</mat-form-field> </mat-form-field>

View File

@ -16,7 +16,7 @@
--> -->
<form [formGroup]="timewindowForm" class="mat-content"> <form [formGroup]="timewindowForm" class="mat-content">
<mat-tab-group [ngClass]="{'tb-headless': historyOnly}" <mat-tab-group [class.tb-headless]="historyOnly"
(selectedTabChange)="onTimewindowTypeChange()" [(selectedIndex)]="timewindow.selectedTab"> (selectedTabChange)="onTimewindowTypeChange()" [(selectedIndex)]="timewindow.selectedTab">
<mat-tab label="{{ 'timewindow.realtime' | translate }}"> <mat-tab label="{{ 'timewindow.realtime' | translate }}">
<div class="tb-flex column"> <div class="tb-flex column">
@ -24,24 +24,18 @@
<div class="tb-form-panel-title">{{ 'timewindow.time-range' | translate }}</div> <div class="tb-form-panel-title">{{ 'timewindow.time-range' | translate }}</div>
<div class="tb-flex align-center space-between"> <div class="tb-flex align-center space-between">
<ng-container formGroupName="realtime"> <ng-container formGroupName="realtime">
<tb-toggle-select *ngIf="!quickIntervalOnly" <tb-toggle-select *ngIf="!quickIntervalOnly || isEdit || (!timewindow.hideLastInterval && !timewindow.hideQuickInterval)"
[fxShow]="isEdit || (!timewindow.hideLastInterval && !timewindow.hideQuickInterval)"
appearance="fill" [options]="realtimeTimewindowOptions" formControlName="realtimeType" appearance="fill" [options]="realtimeTimewindowOptions" formControlName="realtimeType"
style="max-width: 100%"> style="max-width: 100%">
</tb-toggle-select> </tb-toggle-select>
</ng-container> </ng-container>
<tb-timezone-select fxFlex [fxShow]="!timewindow.hideTimezone" <ng-container *ngTemplateOutlet="timezoneSelection">
localBrowserTimezonePlaceholderOnEmpty="true" </ng-container>
formControlName="timezone"
subscriptSizing="dynamic"
appearance="outline">
</tb-timezone-select>
</div> </div>
<ng-container formGroupName="realtime"> <ng-container formGroupName="realtime">
<ng-container *ngIf="isEdit || !timewindow.hideLastInterval && <ng-container *ngIf="timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL">
timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL">
<tb-timeinterval <tb-timeinterval
formControlName="timewindowMs" formControlName="timewindowMs"
subscriptSizing="dynamic" subscriptSizing="dynamic"
@ -51,9 +45,9 @@
</tb-timeinterval> </tb-timeinterval>
</ng-container> </ng-container>
<ng-container *ngIf="!timewindow.hideQuickInterval && <ng-container *ngIf="timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL">
timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL">
<tb-quick-time-interval <tb-quick-time-interval
displayLabel="false"
formControlName="quickInterval" formControlName="quickInterval"
onlyCurrentInterval="true" onlyCurrentInterval="true"
subscriptSizing="dynamic" subscriptSizing="dynamic"
@ -69,148 +63,136 @@
</div> </div>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'timewindow.history' | translate }}"> <mat-tab label="{{ 'timewindow.history' | translate }}">
<section fxLayout="row"> <div class="tb-flex column">
<section *ngIf="isEdit" fxLayout="column" fxLayoutAlign="start center" <section class="tb-form-panel stroked" [fxShow]="isEdit || !timewindow.hideInterval">
style="padding-top: 8px; padding-left: 16px;"> <div class="tb-form-panel-title">{{ 'timewindow.time-range' | translate }}</div>
<label class="tb-small hide-label" translate>timewindow.hide</label> <div class="tb-flex align-center space-between">
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideInterval" <ng-container formGroupName="history">
(ngModelChange)="onHideIntervalChanged()"></mat-checkbox> <tb-toggle-select appearance="fill" [options]="historyTimewindowOptions" formControlName="historyType"
</section> style="max-width: 100%">
<section fxLayout="column" fxFlex [fxShow]="isEdit || !timewindow.hideInterval"> </tb-toggle-select>
<div formGroupName="history" class="mat-content mat-padding" style="padding-top: 8px;"> </ng-container>
<mat-radio-group formControlName="historyType">
<mat-radio-button *ngIf="forAllTimeEnabled" [value]="historyTypes.FOR_ALL_TIME" color="primary"> <ng-container *ngTemplateOutlet="timezoneSelection">
<section fxLayout="column"> </ng-container>
<span translate>timewindow.for-all-time</span>
</section>
</mat-radio-button>
<mat-radio-button [value]="historyTypes.LAST_INTERVAL" color="primary">
<section fxLayout="column">
<span translate>timewindow.last</span>
<tb-timeinterval
formControlName="timewindowMs"
predefinedName="timewindow.last"
class="history-time-input"
subscriptSizing="dynamic"
appearance="outline"
[fxShow]="timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL"
[required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL"
style="padding-top: 8px;"></tb-timeinterval>
</section>
</mat-radio-button>
<mat-radio-button [value]="historyTypes.FIXED" color="primary">
<section fxLayout="column">
<span translate>timewindow.time-period</span>
<tb-datetime-period
formControlName="fixedTimewindow"
class="history-time-input"
[fxShow]="timewindowForm.get('history.historyType').value === historyTypes.FIXED"
[required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
timewindowForm.get('history.historyType').value === historyTypes.FIXED"
style="padding-top: 8px;"></tb-datetime-period>
</section>
</mat-radio-button>
<mat-radio-button [value]="historyTypes.INTERVAL" color="primary">
<section fxLayout="column">
<span translate>timewindow.interval</span>
<tb-quick-time-interval
formControlName="quickInterval"
class="history-time-input"
subscriptSizing="dynamic"
appearance="outline"
[fxShow]="timewindowForm.get('history.historyType').value === historyTypes.INTERVAL"
[required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
timewindowForm.get('history.historyType').value === historyTypes.INTERVAL"
style="padding-top: 8px"></tb-quick-time-interval>
</section>
</mat-radio-button>
</mat-radio-group>
</div> </div>
<ng-container formGroupName="history" *ngIf="isEdit || !timewindow.hideInterval &&
timewindowForm.get('history.historyType').value !== historyTypes.FOR_ALL_TIME">
<ng-container *ngIf="timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL">
<tb-timeinterval
formControlName="timewindowMs"
subscriptSizing="dynamic"
appearance="outline"
[required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL">
</tb-timeinterval>
</ng-container>
<ng-container *ngIf="timewindowForm.get('history.historyType').value === historyTypes.FIXED">
<tb-datetime-period
formControlName="fixedTimewindow"
class="history-time-input"
[required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
timewindowForm.get('history.historyType').value === historyTypes.FIXED">
</tb-datetime-period>
</ng-container>
<ng-container *ngIf="timewindowForm.get('history.historyType').value === historyTypes.INTERVAL">
<tb-quick-time-interval
displayLabel="false"
formControlName="quickInterval"
subscriptSizing="dynamic"
appearance="outline"
[required]="timewindow.selectedTab === timewindowTypes.HISTORY &&
timewindowForm.get('history.historyType').value === historyTypes.INTERVAL">
</tb-quick-time-interval>
</ng-container>
</ng-container>
</section> </section>
</section> <ng-container *ngTemplateOutlet="aggregationConfig">
<ng-container *ngTemplateOutlet="aggregationConfig"> </ng-container>
</ng-container> </div>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
<ng-template #aggregationConfig> <ng-template #aggregationConfig>
<div *ngIf="aggregation" formGroupName="aggregation" class="tb-flex column"> <div *ngIf="aggregation" class="tb-flex column">
<section class="tb-form-panel stroked" [fxShow]="isEdit || !timewindow.hideAggregation"> <ng-container formGroupName="aggregation">
<div class="tb-form-panel-title">{{ 'aggregation.aggregation' | translate }}</div> <section class="tb-form-panel stroked" *ngIf="isEdit || !timewindow.hideAggregation">
<mat-form-field subscriptSizing="dynamic" appearance="outline"> <div class="tb-form-panel-title">{{ 'aggregation.aggregation' | translate }}</div>
<mat-select formControlName="type" style="min-width: 150px;"> <mat-form-field subscriptSizing="dynamic" appearance="outline">
<mat-option *ngFor="let aggregation of aggregations" [value]="aggregation"> <mat-select formControlName="type" style="min-width: 150px;">
{{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }} <mat-option *ngFor="let aggregation of aggregations" [value]="aggregation">
</mat-option> {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}
</mat-select> </mat-option>
</mat-form-field> </mat-select>
</section> </mat-form-field>
</section>
<section class="tb-form-panel stroked" <section class="tb-form-panel stroked"
*ngIf="timewindowForm.get('aggregation.type').value === aggregationTypes.NONE && (isEdit || !timewindow.hideAggInterval)"> *ngIf="timewindowForm.get('aggregation.type').value === aggregationTypes.NONE && (isEdit || !timewindow.hideAggInterval)">
<div class="tb-form-panel-title">{{ 'aggregation.limit' | translate }}</div> <div class="tb-form-panel-title">{{ 'aggregation.limit' | translate }}</div>
<div class="limit-slider-container" fxLayout="row" fxLayoutAlign="start center" <div class="limit-slider-container" fxLayout="row" fxLayoutAlign="start center"
fxLayout.xs="column" fxLayoutAlign.xs="stretch"> fxLayout.xs="column" fxLayoutAlign.xs="stretch">
<div fxLayout="row" fxLayoutAlign="start center" fxFlex> <div fxLayout="row" fxLayoutAlign="start center" fxFlex>
<mat-slider fxFlex <mat-slider fxFlex
discrete discrete
min="{{minDatapointsLimit()}}" showTickMarks
max="{{maxDatapointsLimit()}}"><input matSliderThumb formControlName="limit"/> step="{{datapointsLimitSliderStep()}}"
</mat-slider> min="{{minDatapointsLimit()}}"
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic" appearance="outline"> max="{{maxDatapointsLimit()}}">
<input matInput formControlName="limit" type="number" step="1" <input matSliderThumb formControlName="limit"/>
[value]="timewindowForm.get('aggregation.limit').value" </mat-slider>
min="{{minDatapointsLimit()}}" <mat-form-field class="limit-slider-value" subscriptSizing="dynamic" appearance="outline">
max="{{maxDatapointsLimit()}}"/> <input matInput formControlName="limit" type="number" step="1"
</mat-form-field> [value]="timewindowForm.get('aggregation.limit').value"
min="{{minDatapointsLimit()}}"
max="{{maxDatapointsLimit()}}"/>
</mat-form-field>
</div>
</div> </div>
</div> </section>
</ng-container>
<section class="tb-form-panel stroked" [fxShow]="(isEdit || !timewindow.hideAggInterval)
&& timewindowForm.get('aggregation.type').value !== aggregationTypes.NONE">
<div class="tb-form-panel-title">{{ 'aggregation.group-interval' | translate }}</div>
<ng-container formGroupName="realtime" *ngIf="timewindow.selectedTab === timewindowTypes.REALTIME">
<tb-timeinterval
formControlName="interval"
[isEdit]="isEdit"
[(hideFlag)]="timewindow.hideAggInterval"
[min]="minRealtimeAggInterval()" [max]="maxRealtimeAggInterval()"
useCalendarIntervals
subscriptSizing="dynamic"
appearance="outline">
</tb-timeinterval>
</ng-container>
<ng-container formGroupName="history" *ngIf="timewindow.selectedTab === timewindowTypes.HISTORY">
<tb-timeinterval
formControlName="interval"
[isEdit]="isEdit"
[(hideFlag)]="timewindow.hideAggInterval"
[min]="minHistoryAggInterval()" [max]="maxHistoryAggInterval()"
useCalendarIntervals
subscriptSizing="dynamic"
appearance="outline">
</tb-timeinterval>
</ng-container>
</section> </section>
</div> </div>
<div formGroupName="realtime" </ng-template>
*ngIf="aggregation && timewindowForm.get('aggregation.type').value !== aggregationTypes.NONE && <ng-template #timezoneSelection>
timewindow.selectedTab === timewindowTypes.REALTIME" class="mat-content mat-padding" fxLayout="column"> <ng-container *ngIf="timezone && (isEdit || !timewindow.hideTimezone)">
<tb-timeinterval <tb-timezone-select localBrowserTimezonePlaceholderOnEmpty="true"
formControlName="interval" formControlName="timezone"
[isEdit]="isEdit" subscriptSizing="dynamic"
[(hideFlag)]="timewindow.hideAggInterval" appearance="outline">
(hideFlagChange)="onHideAggIntervalChanged()" </tb-timezone-select>
[min]="minRealtimeAggInterval()" [max]="maxRealtimeAggInterval()" </ng-container>
useCalendarIntervals
subscriptSizing="dynamic"
appearance="outline"
predefinedName="aggregation.group-interval">
</tb-timeinterval>
</div>
<div formGroupName="history"
*ngIf="aggregation && timewindowForm.get('aggregation.type').value !== aggregationTypes.NONE &&
timewindow.selectedTab === timewindowTypes.HISTORY" class="mat-content mat-padding" fxLayout="column">
<tb-timeinterval
formControlName="interval"
[isEdit]="isEdit"
[(hideFlag)]="timewindow.hideAggInterval"
(hideFlagChange)="onHideAggIntervalChanged()"
[min]="minHistoryAggInterval()" [max]="maxHistoryAggInterval()"
useCalendarIntervals
subscriptSizing="dynamic"
appearance="outline"
predefinedName="aggregation.group-interval">
</tb-timeinterval>
</div>
<div *ngIf="timezone" class="mat-content mat-padding" fxLayout="row">
<section fxLayout="column" fxLayoutAlign="start center" [fxShow]="isEdit">
<label class="tb-small hide-label" translate>timewindow.hide</label>
<mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideTimezone"
(ngModelChange)="onHideTimezoneChanged()"></mat-checkbox>
</section>
<!-- <tb-timezone-select fxFlex [fxShow]="isEdit || !timewindow.hideTimezone"-->
<!-- localBrowserTimezonePlaceholderOnEmpty="true"-->
<!-- formControlName="timezone"-->
<!-- appearance="outline">-->
<!-- </tb-timezone-select>-->
</div>
</ng-template> </ng-template>
</form> </form>
<mat-divider></mat-divider>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center"> <div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<button type="button" <button type="button"
mat-button mat-button

View File

@ -22,6 +22,11 @@
max-width: 100%; max-width: 100%;
background-color: #fff; background-color: #fff;
.tb-form-panel {
padding: 12px;
gap: 12px;
}
.tb-flex { .tb-flex {
gap: 16px; gap: 16px;
} }
@ -84,6 +89,6 @@
} }
.mat-mdc-tab-body-content { .mat-mdc-tab-body-content {
padding: 16px; padding: 12px 16px;
} }
} }

View File

@ -91,11 +91,26 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
value: this.realtimeTypes.LAST_INTERVAL value: this.realtimeTypes.LAST_INTERVAL
}, },
{ {
name: this.translate.instant('timewindow.interval'), name: this.translate.instant('timewindow.relative'),
value: this.realtimeTypes.INTERVAL value: this.realtimeTypes.INTERVAL
} }
]; ];
historyTimewindowOptions: ToggleHeaderOption[] = [
{
name: this.translate.instant('timewindow.last'),
value: this.historyTypes.LAST_INTERVAL
},
{
name: this.translate.instant('timewindow.fixed'),
value: this.historyTypes.FIXED
},
{
name: this.translate.instant('timewindow.relative'),
value: this.historyTypes.INTERVAL
}
];
constructor(@Inject(TIMEWINDOW_PANEL_DATA) public data: TimewindowPanelData, constructor(@Inject(TIMEWINDOW_PANEL_DATA) public data: TimewindowPanelData,
public overlayRef: OverlayRef, public overlayRef: OverlayRef,
protected store: Store<AppState>, protected store: Store<AppState>,
@ -106,6 +121,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
super(store); super(store);
this.historyOnly = data.historyOnly; this.historyOnly = data.historyOnly;
this.forAllTimeEnabled = data.forAllTimeEnabled; this.forAllTimeEnabled = data.forAllTimeEnabled;
if (this.forAllTimeEnabled) {
this.historyTimewindowOptions.unshift({
name: this.translate.instant('timewindow.for-all-time'),
value: this.historyTypes.FOR_ALL_TIME
});
}
this.quickIntervalOnly = data.quickIntervalOnly; this.quickIntervalOnly = data.quickIntervalOnly;
this.timewindow = data.timewindow; this.timewindow = data.timewindow;
this.aggregation = data.aggregation; this.aggregation = data.aggregation;
@ -273,6 +294,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit {
return this.timeService.getMaxDatapointsLimit(); return this.timeService.getMaxDatapointsLimit();
} }
// TODO: find more accurate step size for slider
datapointsLimitSliderStep() {
return Math.round(this.maxDatapointsLimit() / 10);
}
minRealtimeAggInterval() { minRealtimeAggInterval() {
return this.timeService.minIntervalLimit(this.currentRealtimeTimewindow()); return this.timeService.minIntervalLimit(this.currentRealtimeTimewindow());
} }

View File

@ -4695,7 +4695,9 @@
"color": "Color", "color": "Color",
"displayTypePrefix": "Display Realtime/History prefix", "displayTypePrefix": "Display Realtime/History prefix",
"preview": "Preview", "preview": "Preview",
"time-range": "Time range" "time-range": "Time range",
"relative": "Relative",
"fixed": "Fixed"
}, },
"tooltip": { "tooltip": {
"trigger": "Trigger", "trigger": "Trigger",