UI: Fixed validation and style in Save ts rule node config

This commit is contained in:
Vladyslav_Prykhodko 2025-01-23 14:05:41 +02:00
parent a463707d5b
commit 80593f4b52
6 changed files with 31 additions and 18 deletions

View File

@ -32,6 +32,7 @@
requiredText="{{ 'rule-node-config.save-time-series.deduplication-interval-required' | translate }}" requiredText="{{ 'rule-node-config.save-time-series.deduplication-interval-required' | translate }}"
minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
[minTime]="1"
[maxTime]="maxDeduplicateTime" [maxTime]="maxDeduplicateTime"
formControlName="deduplicationIntervalSecs"> formControlName="deduplicationIntervalSecs">
</tb-time-unit-input> </tb-time-unit-input>

View File

@ -45,6 +45,7 @@
minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}" maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
[maxTime]="maxDeduplicateTime" [maxTime]="maxDeduplicateTime"
[minTime]="1"
formControlName="deduplicationIntervalSecs"> formControlName="deduplicationIntervalSecs">
</tb-time-unit-input> </tb-time-unit-input>
} }
@ -56,12 +57,18 @@
></tb-advanced-persistence-settings> ></tb-advanced-persistence-settings>
} }
</div> </div>
<section class="tb-form-panel stroked"> <section class="tb-form-panel stroked no-padding-bottom">
<mat-expansion-panel class="tb-settings"> <mat-expansion-panel class="tb-settings">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title> <mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<div tb-hint-tooltip-icon="{{ 'rule-node-config.use-server-ts-hint' | translate}}"
class="tb-form-row no-border no-padding">
<mat-slide-toggle class="mat-slide" formControlName="useServerTs">
{{ 'rule-node-config.use-server-ts' | translate }}
</mat-slide-toggle>
</div>
<tb-time-unit-input <tb-time-unit-input
required required
labelText="{{ 'rule-node-config.default-ttl' | translate }}" labelText="{{ 'rule-node-config.default-ttl' | translate }}"
@ -76,12 +83,6 @@
help help
</mat-icon> </mat-icon>
</tb-time-unit-input> </tb-time-unit-input>
<div tb-hint-tooltip-icon="{{ 'rule-node-config.use-server-ts-hint' | translate}}"
class="tb-form-row no-border no-padding">
<mat-slide-toggle class="mat-slide" formControlName="useServerTs">
{{ 'rule-node-config.use-server-ts' | translate }}
</mat-slide-toggle>
</div>
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>
</section> </section>

View File

@ -60,16 +60,16 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
if (config?.persistenceSettings) { if (config?.persistenceSettings) {
const isAdvanced = config?.persistenceSettings?.type === PersistenceType.ADVANCED; const isAdvanced = config?.persistenceSettings?.type === PersistenceType.ADVANCED;
persistenceSettings = { persistenceSettings = {
...config.persistenceSettings,
isAdvanced: isAdvanced,
type: isAdvanced ? PersistenceType.ON_EVERY_MESSAGE : config.persistenceSettings.type, type: isAdvanced ? PersistenceType.ON_EVERY_MESSAGE : config.persistenceSettings.type,
isAdvanced: isAdvanced,
deduplicationIntervalSecs: config.persistenceSettings?.deduplicationIntervalSecs ?? 60,
advanced: isAdvanced ? config.persistenceSettings : defaultAdvancedPersistenceStrategy advanced: isAdvanced ? config.persistenceSettings : defaultAdvancedPersistenceStrategy
} }
} else { } else {
persistenceSettings = { persistenceSettings = {
type: PersistenceType.ON_EVERY_MESSAGE, type: PersistenceType.ON_EVERY_MESSAGE,
isAdvanced: false, isAdvanced: false,
deduplicationIntervalSecs: 10, deduplicationIntervalSecs: 60,
advanced: defaultAdvancedPersistenceStrategy advanced: defaultAdvancedPersistenceStrategy
}; };
} }
@ -104,7 +104,7 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
isAdvanced: [config?.persistenceSettings?.isAdvanced ?? false], isAdvanced: [config?.persistenceSettings?.isAdvanced ?? false],
type: [config?.persistenceSettings?.type ?? PersistenceType.ON_EVERY_MESSAGE], type: [config?.persistenceSettings?.type ?? PersistenceType.ON_EVERY_MESSAGE],
deduplicationIntervalSecs: [ deduplicationIntervalSecs: [
{value: config?.persistenceSettings?.deduplicationIntervalSecs ?? 10, disabled: true}, {value: config?.persistenceSettings?.deduplicationIntervalSecs ?? 60, disabled: true},
[Validators.required, Validators.max(maxDeduplicateTimeSecs)] [Validators.required, Validators.max(maxDeduplicateTimeSecs)]
], ],
advanced: [{value: null, disabled: true}] advanced: [{value: null, disabled: true}]

View File

@ -16,7 +16,7 @@
import { DAY, SECOND } from '@shared/models/time/time.models'; import { DAY, SECOND } from '@shared/models/time/time.models';
export const maxDeduplicateTimeSecs = DAY / SECOND + 1; export const maxDeduplicateTimeSecs = DAY / SECOND;
export interface TimeseriesNodeConfiguration { export interface TimeseriesNodeConfiguration {
persistenceSettings: PersistenceSettings; persistenceSettings: PersistenceSettings;

View File

@ -33,7 +33,7 @@
{{ maxErrorText }} {{ maxErrorText }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="max-w-33% flex-full"> <mat-form-field class="h-fit max-w-33% flex-full">
<mat-label translate>rule-node-config.units</mat-label> <mat-label translate>rule-node-config.units</mat-label>
<mat-select formControlName="timeUnit"> <mat-select formControlName="timeUnit">
@for (timeUnit of timeUnits; track timeUnit) { @for (timeUnit of timeUnits; track timeUnit) {

View File

@ -22,7 +22,8 @@ import {
NG_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
ValidationErrors, ValidationErrors,
Validator, Validators Validator,
Validators
} from '@angular/forms'; } from '@angular/forms';
import { TimeUnit, timeUnitTranslations } from '../rule-node-config.models'; import { TimeUnit, timeUnitTranslations } from '../rule-node-config.models';
import { isDefinedAndNotNull, isNumeric } from '@core/utils'; import { isDefinedAndNotNull, isNumeric } from '@core/utils';
@ -60,6 +61,10 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator,
@Input() @Input()
requiredText: string; requiredText: string;
@Input()
@coerceNumber()
minTime = 0;
@Input() @Input()
minErrorText: string; minErrorText: string;
@ -75,7 +80,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator,
timeUnitTranslations = timeUnitTranslations; timeUnitTranslations = timeUnitTranslations;
timeInputForm = this.fb.group({ timeInputForm = this.fb.group({
time: [0, Validators.min(0)], time: [0],
timeUnit: [TimeUnit.SECONDS] timeUnit: [TimeUnit.SECONDS]
}); });
@ -97,7 +102,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator,
ngOnInit() { ngOnInit() {
if(this.required || this.maxTime) { if(this.required || this.maxTime) {
const timeControl = this.timeInputForm.get('time'); const timeControl = this.timeInputForm.get('time');
const validators = []; const validators = [Validators.pattern(/^\d*$/)];
if (this.required) { if (this.required) {
validators.push(Validators.required); validators.push(Validators.required);
} }
@ -106,6 +111,9 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator,
Validators.max(Math.floor(this.maxTime / this.timeIntervalsInSec.get(this.timeInputForm.get('timeUnit').value)))(control) Validators.max(Math.floor(this.maxTime / this.timeIntervalsInSec.get(this.timeInputForm.get('timeUnit').value)))(control)
); );
} }
if (isDefinedAndNotNull(this.minTime)) {
validators.push(Validators.min(this.minTime));
}
timeControl.setValidators(validators); timeControl.setValidators(validators);
timeControl.updateValueAndValidity({ emitEvent: false }); timeControl.updateValueAndValidity({ emitEvent: false });
@ -137,6 +145,9 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator,
this.timeInputForm.disable({emitEvent: false}); this.timeInputForm.disable({emitEvent: false});
} else { } else {
this.timeInputForm.enable({emitEvent: false}); this.timeInputForm.enable({emitEvent: false});
if(this.timeInputForm.invalid) {
setTimeout(() => this.updatedModel(this.timeInputForm.value, true))
}
} }
} }
@ -161,9 +172,9 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator,
}; };
} }
private updatedModel(value: Partial<TimeUnitInputModel>) { private updatedModel(value: Partial<TimeUnitInputModel>, forceUpdated = false) {
const time = value.time * this.timeIntervalsInSec.get(value.timeUnit); const time = value.time * this.timeIntervalsInSec.get(value.timeUnit);
if (this.modelValue !== time) { if (this.modelValue !== time || forceUpdated) {
this.modelValue = time; this.modelValue = time;
this.propagateChange(time); this.propagateChange(time);
} }