Browse Source

UI: Add liquid level error message and improvement load and validate resources

pull/9456/head
Vladyslav_Prykhodko 3 years ago
parent
commit
8300995d80
  1. 24
      ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html
  2. 18
      ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts
  3. 7
      ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.html
  4. 26
      ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.scss
  5. 146
      ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts
  6. 19
      ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts
  7. 23
      ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html
  8. 17
      ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts
  9. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

24
ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html

@ -45,7 +45,7 @@
</tb-color-input>
</div>
</div>
<div class="tb-form-row">
<div class="tb-form-row column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitleIcon">
{{ 'widgets.liquid-level-card.icon' | translate }}
</mat-slide-toggle>
@ -89,9 +89,8 @@
<div class="tb-form-row space-between" style="width: 100%;">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.shape-attribute-name</div>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('shapeAttributeName')"
required style="flex: 1"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1"
formControlName="shapeAttributeName">
</tb-string-autocomplete>
</div>
@ -124,9 +123,8 @@
<div class="tb-form-row space-between">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.datasource-units</div>
<div fxFlex fxLayout="row" fxLayoutAlign="end center">
<tb-unit-input style="max-width: 25%;" class="flex"
<tb-unit-input required style="max-width: 25%;" class="flex"
[tagFilter]="unitsType.capacity"
[required]="isRequired('datasourceUnits')"
formControlName="datasourceUnits">
</tb-unit-input>
</div>
@ -144,14 +142,12 @@
<tb-unit-input *ngIf="levelCardWidgetConfigForm.get('widgetUnitsSource').value === DataSourceType.static; else selectUnitsAttributes"
class="flex" required
[tagFilter]="unitsType.capacity"
[required]="isRequired('units')"
formControlName="units">
</tb-unit-input>
<ng-template #selectUnitsAttributes>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('widgetUnitsAttributeName')"
required style="flex: 1"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1"
formControlName="widgetUnitsAttributeName">
</tb-string-autocomplete>
</ng-template>
@ -170,7 +166,7 @@
<mat-form-field *ngIf="levelCardWidgetConfigForm.get('volumeSource').value === DataSourceType.static; else volumeAttributes"
appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="volumeConstant" type="number"
min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}">
min="0.1" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
@ -183,14 +179,12 @@
<ng-template #volumeAttributes>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
[required]="isRequired('volumeAttributeName')"
style="flex: 1"
required style="flex: 1"
formControlName="volumeAttributeName">
</tb-string-autocomplete>
</ng-template>
<tb-unit-input [tagFilter]="unitsType.capacity"
[required]="isRequired('volumeUnits')"
style="max-width: 25%" class="flex"
required style="max-width: 25%" class="flex"
formControlName="volumeUnits">
</tb-unit-input>
</div>
@ -253,8 +247,8 @@
{{ 'widgets.liquid-level-card.level' | translate }}
</mat-slide-toggle>
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
<tb-unit-input [required]="isRequired('tooltipUnits')"
[tagFilter]="unitsType.capacity" class="flex"
<tb-unit-input required class="flex"
[tagFilter]="unitsType.capacity"
formControlName="tooltipUnits">
</tb-unit-input>
<mat-form-field style="width: 40%;" appearance="outline" class="flex number" subscriptSizing="dynamic">

18
ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts

@ -86,13 +86,11 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
public get volumeInput(): boolean {
const datasourceUnits = this.levelCardWidgetConfigForm.get('datasourceUnits').value;
const layout: LevelCardLayout = this.levelCardWidgetConfigForm.get('layout').value;
const units = this.levelCardWidgetConfigForm.get('units').value;
if (layout === LevelCardLayout.absolute) {
return true;
}
return !(datasourceUnits === CapacityUnits.percent && units === CapacityUnits.percent);
return datasourceUnits !== CapacityUnits.percent;
}
public get displayTimewindowConfig(): boolean {
@ -194,7 +192,7 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
iconColor: [configData.config.iconColor, []],
volumeSource: [settings.volumeSource, []],
volumeConstant: [settings.volumeConstant, [Validators.required]],
volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]],
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]],
volumeUnits: [settings.volumeUnits, [Validators.required]],
volumeFont: [settings.volumeFont, []],
@ -295,12 +293,8 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
protected validatorTriggers(): string[] {
return [
'showTooltip', 'showTooltipLevel',
'tankSelectionType', 'datasourceUnits',
'showTitleIcon', 'volumeSource',
'showTooltipDate', 'units',
'layout', 'showTitle',
'widgetUnitsSource'
'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits', 'showTitleIcon', 'volumeSource',
'showTooltipDate', 'layout', 'showTitle', 'widgetUnitsSource'
];
}
@ -399,10 +393,6 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
return createShapeLayout(svg, layout, this.sanitizer);
}
public isRequired(formControlName: string): boolean {
return this.levelCardWidgetConfigForm.get(formControlName)?.hasValidator(Validators.required);
}
public fetchOptions(searchText: string): Observable<Array<string>> {
if (this.keySearchText !== searchText || !this.lastFetchedKeys) {
this.keySearchText = searchText;

7
ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.html

@ -19,4 +19,11 @@
<div class="tb-liquid-level-overlay" [style]="overlayStyle"></div>
<ng-container *ngTemplateOutlet="widgetTitlePanel"></ng-container>
<div #liquidLevelContent class="tb-liquid-level-content"></div>
<div *ngIf="errorsMsg.length" class="tb-liquid-level-error">
<div class="tb-liquid-level-error-container">
<div *ngFor="let error of errorsMsg">
{{ error }}
</div>
</div>
</div>
</div>

26
ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.scss

@ -43,4 +43,30 @@
display: flex;
justify-content: center;
}
.tb-liquid-level-error {
position: absolute;
display: flex;
inset: 0;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
&-container {
border-radius: 4px;
margin: 16px;
padding: 8px 12px;
background: #848484;
display: flex;
flex-direction: column;
gap: 8px;
& > * {
font-weight: 500;
letter-spacing: .25px;
text-align: center;
color: #fff;
}
}
}
}

146
ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts

@ -25,7 +25,7 @@ import {
ViewEncapsulation
} from '@angular/core';
import { WidgetContext } from '@home/models/widget-component.models';
import { isDefined, isDefinedAndNotNull, isNumber, isString } from '@core/utils';
import { isDefined, isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, isNumeric, isUndefinedOrNull } from '@core/utils';
import {
CapacityUnits,
ConversionType,
@ -42,7 +42,7 @@ import {
SvgLimits,
svgMapping
} from '@home/components/widget/lib/indicator/liquid-level-widget.models';
import { Observable, of } from 'rxjs';
import { forkJoin, Observable, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { EntityId } from '@shared/models/id/entity-id';
import {
@ -56,6 +56,7 @@ import {
} from '@shared/models/widget-settings.models';
import { ResourcesService } from '@core/services/resources.service';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { TranslateService } from '@ngx-translate/core';
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance;
@Component({
@ -80,6 +81,8 @@ export class LiquidLevelWidgetComponent implements OnInit {
hasCardClickAction = false;
errorsMsg: string[] = [];
private svgParams: SvgInfo;
private svg: JQuery<SVGElement>;
@ -101,7 +104,11 @@ export class LiquidLevelWidgetComponent implements OnInit {
private tooltipContent: string;
private widgetUnits: string;
constructor(private cd: ChangeDetectorRef) {
private capacityUnits = Object.values(CapacityUnits);
constructor(private cd: ChangeDetectorRef,
private resourcesService: ResourcesService,
private translate: TranslateService) {
}
ngOnInit(): void {
@ -117,14 +124,14 @@ export class LiquidLevelWidgetComponent implements OnInit {
this.getData().subscribe(data => {
if (data) {
const { svg, volume, units } = data;
if (svg && isString(svg) && this.liquidLevelContent.nativeElement) {
if (svg && isNotEmptyStr(svg) && this.liquidLevelContent.nativeElement) {
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement);
jQueryContainerElement.html(svg);
this.svg = jQueryContainerElement.find('svg');
this.createSVG();
this.createValueElement();
if (this.settings.showTooltip) {
if (this.settings.showTooltip && (this.settings.showTooltipLevel || this.settings.showTooltipDate)) {
this.createTooltip();
}
}
@ -163,17 +170,13 @@ export class LiquidLevelWidgetComponent implements OnInit {
this.shape = shape;
this.svgParams = svgMapping.get(shape);
if (this.svgParams) {
return this.loadSVG(this.svgParams.svg).pipe(
switchMap( svg =>
this.getSecondaryResources(entityId).pipe(
map(({ volume, units }) =>
({ svg, volume, units })
)
)
)
return forkJoin([
this.resourcesService.loadJsonResource<string>(this.svgParams.svg),
this.getTankersParams(entityId)
]).pipe(
map(params => ({svg: params[0], ...params[1]}))
);
}
return of(null);
})
);
@ -207,11 +210,11 @@ export class LiquidLevelWidgetComponent implements OnInit {
private updateData(ignoreAnimation?: boolean) {
const data = this.ctx.data[0]?.data[0];
if (data && isDefinedAndNotNull(data[1])) {
const percentage = isNumber(data[1]) ? this.convertInputData(data[1]) : 0;
const percentage = isNumeric(data[1]) ? this.convertInputData(data[1]) : 0;
this.updateSvg(percentage, ignoreAnimation);
this.updateValueElement(data[1], percentage);
if (this.settings.showTooltip) {
if (this.settings.showTooltip && (this.settings.showTooltipLevel || this.settings.showTooltipDate)) {
this.updateTooltip(data);
}
}
@ -268,13 +271,13 @@ export class LiquidLevelWidgetComponent implements OnInit {
const percentageOverlay = jQueryContainerElement.find('.percentage-overlay');
const absoluteOverlay = jQueryContainerElement.find('.absolute-overlay');
if (this.settings.layout === LevelCardLayout.absolute) {
if (this.settings.layout === LevelCardLayout.absolute && !this.errorsMsg.length) {
this.overlayContainer = absoluteOverlay;
percentageOverlay.css('visibility', 'hidden');
if (!this.settings.showBackgroundOverlay) {
absoluteOverlay.css('visibility', 'hidden');
}
} else if (this.settings.layout === LevelCardLayout.percentage) {
} else if (this.settings.layout === LevelCardLayout.percentage && !this.errorsMsg.length) {
this.overlayContainer = percentageOverlay;
absoluteOverlay.css('visibility', 'hidden');
if (!this.settings.showBackgroundOverlay) {
@ -290,51 +293,82 @@ export class LiquidLevelWidgetComponent implements OnInit {
}
}
private loadSVG(url: string): Observable<string> {
const resourcesService = this.ctx.$injector.get(ResourcesService);
return resourcesService.loadJsonResource(url);
}
private getShape(entityId: EntityId): Observable<Shapes> {
if (this.settings.tankSelectionType === LiquidWidgetDataSourceType.attribute && entityId.id !== NULL_UUID) {
return this.ctx.attributeService.getEntityAttributes(entityId, null,
[this.settings.shapeAttributeName]).pipe(
map(attributes =>
extractValue(attributes, this.settings.shapeAttributeName, this.settings.selectedShape)
)
);
return this.ctx.attributeService.getEntityAttributes(entityId, null, [this.settings.shapeAttributeName])
.pipe(map(attributes => {
const shape = extractValue<Shapes>(attributes, this.settings.shapeAttributeName);
if (!shape || !svgMapping.has(shape)) {
this.createdErrorMgs(this.settings.shapeAttributeName, isUndefinedOrNull(shape) || isEmptyStr(shape));
return this.settings.selectedShape;
}
return shape;
}
));
}
return of(this.settings.selectedShape);
}
private getSecondaryResources(entityId: EntityId): Observable<{ volume: number; units: string }> {
const attributeNames = this.prepareAttributeNames();
private getTankersParams(entityId: EntityId): Observable<{ volume: number; units: string }> {
const isVolumeStatic = this.settings.layout !== LevelCardLayout.absolute
&& this.settings.datasourceUnits === CapacityUnits.percent
|| this.settings.volumeSource === LiquidWidgetDataSourceType.static;
const isUnitStatic = this.settings.layout !== LevelCardLayout.absolute ||
this.settings.widgetUnitsSource === LiquidWidgetDataSourceType.static;
const attributeKeys: string[] = [];
if (!attributeNames.length || entityId.id === NULL_UUID) {
if (!isVolumeStatic) {
attributeKeys.push(this.settings.volumeAttributeName);
}
if (!isUnitStatic) {
attributeKeys.push(this.settings.widgetUnitsAttributeName);
}
if (!attributeKeys.length || entityId.id === NULL_UUID) {
return of({
volume: this.settings.volumeConstant,
units: this.settings.units
});
}
return this.ctx.attributeService.getEntityAttributes(entityId, null, attributeNames).pipe(
map(attributes => ({
volume: extractValue(attributes, this.settings.volumeAttributeName, this.settings.volumeConstant),
units: extractValue(attributes, this.settings.widgetUnitsAttributeName, this.settings.units)
}))
return this.ctx.attributeService.getEntityAttributes(entityId, null, attributeKeys).pipe(
map(attributes => {
let volume = isVolumeStatic ? this.settings.volumeConstant : extractValue<number>(attributes, this.settings.volumeAttributeName);
let units = isUnitStatic ? this.settings.units : extractValue<string>(attributes, this.settings.widgetUnitsAttributeName);
if (!isVolumeStatic && (!volume || !isNumeric(volume) || volume < 0.1)) {
this.createdErrorMgs(this.settings.volumeAttributeName, isUndefinedOrNull(volume) || isEmptyStr(volume));
volume = this.settings.volumeConstant;
}
if (!isUnitStatic) {
if (isNotEmptyStr(units)) {
const normalizeUnits = units.normalize().trim();
units = this.capacityUnits.find(unit => unit.normalize() === normalizeUnits);
}
if (isUndefinedOrNull(units) || !isNotEmptyStr(units)) {
this.createdErrorMgs(this.settings.widgetUnitsAttributeName, isUndefinedOrNull(units) || isEmptyStr(units));
units = this.settings.units;
}
}
return {
volume,
units
};
})
);
}
private prepareAttributeNames(): string[] {
const names = [];
if (this.settings.volumeSource !== LiquidWidgetDataSourceType.static) {
names.push(this.settings.volumeAttributeName);
}
if (this.settings.widgetUnitsSource !== LiquidWidgetDataSourceType.static) {
names.push(this.settings.widgetUnitsAttributeName);
private createdErrorMgs(attributeName: string, isEmpty = false) {
if (isEmpty) {
this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-not-set', {attributeName}));
} else {
this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-invalid', {attributeName}));
}
return names;
this.cd.markForCheck();
}
private updateSvg(percentage: number, ignoreAnimation?: boolean) {
@ -373,12 +407,14 @@ export class LiquidLevelWidgetComponent implements OnInit {
const surfacePositionAttr = this.shape !== Shapes.vCylinder ? 'y' : 'cy';
const animationSpeed = 500;
const levelColor = this.errorsMsg.length ? 'transparent' : this.liquidColor.color;
if (ignoreAnimation) {
fill.css({y : newY});
} else {
fill.animate({y : newY}, animationSpeed);
}
fill.attr('fill', this.liquidColor.color);
fill.attr('fill', levelColor);
surfaces.each((index, element) => {
const $element = $(element);
@ -388,7 +424,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
$element.animate({[surfacePositionAttr]: newY}, animationSpeed);
}
if ($element.hasClass('tb-liquid')) {
$element.attr('fill', this.liquidColor.color);
$element.attr('fill', levelColor);
}
});
}
@ -399,12 +435,14 @@ export class LiquidLevelWidgetComponent implements OnInit {
const shapeFill = jQueryContainerElement.find('.tb-shape-fill');
this.tankColor.update(value);
const shapeColor = this.errorsMsg.length ? '#CACACA' : this.tankColor.color;
shapeStrokes.each((index, element) => {
$(element).attr('stroke', this.tankColor.color);
$(element).attr('stroke', shapeColor);
});
shapeFill.each((index, element) => {
$(element).attr('fill', this.tankColor.color);
$(element).attr('fill', shapeColor);
});
}
@ -414,7 +452,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement);
let value = 'N/A';
if (isNumber(data)) {
if (isNumeric(data)) {
value = convertLiters(this.convertOutputData(percentage), this.widgetUnits as CapacityUnits, ConversionType.from)
.toFixed(this.settings.decimals || 0);
}
@ -452,7 +490,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
const contentValue = value || [0, ''];
let tooltipValue: string | number = 'N/A';
if (isNumber(contentValue[1])) {
if (isNumeric(contentValue[1])) {
tooltipValue = this.convertTooltipData(contentValue[1]);
}
@ -470,7 +508,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
if (this.settings.showTooltipLevel) {
const levelValue = typeof tooltipValue == 'number'
? `${tooltipValue.toFixed(this.settings.tooltipLevelDecimals)}${this.settings.tooltipUnits}`
? `${tooltipValue.toFixed(this.settings.tooltipLevelDecimals)}&nbsp;${this.settings.tooltipUnits}`
: 'N/A';
content += this.createTooltipContent(
this.ctx.translate.instant('widgets.liquid-level-card.level'),
@ -493,7 +531,7 @@ export class LiquidLevelWidgetComponent implements OnInit {
}
private createTooltipContent(labelText: string, contentValue: string, textStyle: string): string {
return `<div style="display: flex; justify-content: space-between; gap: 8px">
return `<div style="display: flex; justify-content: space-between; gap: 8px; align-items: baseline">
<label style="color: rgba(0, 0, 0, 0.38); font-size: 12px; font-weight: 400; white-space: nowrap;">
${labelText}
</label>

19
ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts

@ -25,7 +25,7 @@ import {
lastUpdateAgoDateFormat
} from '@shared/models/widget-settings.models';
import { DataKey, WidgetConfig } from '@shared/models/widget.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { AttributeData, DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { forkJoin, Observable, of } from 'rxjs';
import { singleEntityFilterFromDeviceId } from '@shared/models/query/query.models';
import { EntityType } from '@shared/models/entity-type.models';
@ -375,9 +375,8 @@ export const convertLiters = (value: number, units: CapacityUnits, conversionTyp
return conversionType === ConversionType.to ? value / factor : value * factor;
};
export const extractValue = (attributes: any[], attributeName: string, defaultValue: any): any => {
const index = attributes.findIndex(attr => attr.key === attributeName);
return index !== -1 ? attributes[index].value : defaultValue;
export const extractValue = <T>(attributes: Array<AttributeData>, attributeName: string): T | undefined => {
return attributes.find(attr => attr.key === attributeName)?.value;
};
export const valueContainerStyleDefaults = cssTextFromInlineStyle({
@ -413,13 +412,13 @@ export const createAbsoluteLayout = (values?: {inputValue: number | string; volu
<div style="border-top: 1px solid rgba(0, 0, 0, 0.38); height: 1px; width: 100%; margin: 3px 0"></div>
<label style="${volumeTextStyle}">${volume}</label>
</div>
<label><b>${displayUnits}</b></label>
<label style="${valueTextStyle}">${displayUnits}</label>
</div>`;
};
export const createPercentLayout = (value: number | string = 50, valueTextStyle: string = valueTextStyleDefaults): string =>
`<div xmlns="http://www.w3.org/1999/xhtml" style="${valueContainerStyleDefaults}">
<label><span style="${valueTextStyle}">${value}</span> ${CapacityUnits.percent}</label>
<label style="${valueTextStyle}">${value} ${CapacityUnits.percent}</label>
</div>`;
export const optionsFilter = (searchText: string): ((key: DataKey) => boolean) =>
@ -534,15 +533,11 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => {
}
if (layout === LevelCardLayout.simple) {
if (formGroup.get('decimals')) {
formGroup.get('decimals').disable({emitEvent: false});
}
formGroup.get('decimals')?.disable({emitEvent: false});
formGroup.get('valueFont').disable({emitEvent: false});
formGroup.get('valueColor').disable({emitEvent: false});
} else {
if (formGroup.get('decimals')) {
formGroup.get('decimals').enable({emitEvent: false});
}
formGroup.get('decimals')?.enable({emitEvent: false});
formGroup.get('valueFont').enable({emitEvent: false});
formGroup.get('valueColor').enable({emitEvent: false});
}

23
ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html

@ -44,9 +44,8 @@
<div class="tb-form-row space-between" style="width: 100%;">
<div class="fixed-title-width" translate>widgets.liquid-level-card.shape-attribute-name</div>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('shapeAttributeName')"
required style="flex: 1"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1"
formControlName="shapeAttributeName">
</tb-string-autocomplete>
</div>
@ -82,9 +81,8 @@
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.units</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.datasource-units</div>
<tb-unit-input style="max-width: 30%;" class="flex"
<tb-unit-input required style="max-width: 30%;" class="flex"
[tagFilter]="unitsType.capacity"
[required]="isRequired('datasourceUnits')"
formControlName="datasourceUnits">
</tb-unit-input>
</div>
@ -99,16 +97,14 @@
</mat-select>
</mat-form-field>
<tb-unit-input *ngIf="levelCardWidgetSettingsForm.get('widgetUnitsSource').value !== DataSourceType.attribute; else selectUnitsAttributes"
class="flex"
required class="flex"
[tagFilter]="unitsType.capacity"
[required]="isRequired('units')"
formControlName="units">
</tb-unit-input>
<ng-template #selectUnitsAttributes>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('widgetUnitsAttributeName')"
required style="flex: 1"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1"
formControlName="widgetUnitsAttributeName">
</tb-string-autocomplete>
</ng-template>
@ -127,7 +123,7 @@
<mat-form-field *ngIf="levelCardWidgetSettingsForm.get('volumeSource').value === DataSourceType.static; else volumeAttributes"
appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="volumeConstant" type="number"
min="0" step="1" placeholder="{{ 'widget-config.set' | translate }}">
min="0.1" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
@ -139,15 +135,13 @@
</mat-form-field>
<ng-template #volumeAttributes>
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)"
[required]="isRequired('volumeAttributeName')"
required style="flex: 1"
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate"
style="flex: 1"
formControlName="volumeAttributeName">
</tb-string-autocomplete>
</ng-template>
<tb-unit-input [tagFilter]="unitsType.capacity"
[required]="isRequired('volumeUnits')"
style="max-width: 25%" class="flex"
required style="max-width: 25%" class="flex"
formControlName="volumeUnits">
</tb-unit-input>
</div>
@ -219,8 +213,7 @@
</mat-slide-toggle>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-unit-input [tagFilter]="unitsType.capacity"
[required]="isRequired('tooltipUnits')"
class="flex" formControlName="tooltipUnits">
required class="flex" formControlName="tooltipUnits">
</tb-unit-input>
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic">
<input matInput formControlName="tooltipLevelDecimals" type="number" min="0"

17
ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts

@ -68,13 +68,11 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon
public get volumeInput(): boolean {
const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value;
const layout: LevelCardLayout = this.levelCardWidgetSettingsForm.get('layout').value;
const widgetUnits = this.levelCardWidgetSettingsForm.get('units').value;
if (layout === LevelCardLayout.absolute) {
return true;
}
return !(datasourceUnits === CapacityUnits.percent && widgetUnits === CapacityUnits.percent);
return datasourceUnits !== CapacityUnits.percent;
}
private get datasource(): Datasource {
@ -158,7 +156,7 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon
layout: [settings.layout, []],
volumeSource: [settings.volumeSource, []],
volumeConstant: [settings.volumeConstant, [Validators.required]],
volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]],
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]],
volumeUnits: [settings.volumeUnits, [Validators.required]],
volumeFont: [settings.volumeFont, []],
@ -196,11 +194,8 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon
protected validatorTriggers(): string[] {
return [
'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel',
'tankSelectionType', 'datasourceUnits',
'showTooltipDate', 'units',
'layout', 'volumeSource',
'widgetUnitsSource'
'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits',
'showTooltipDate', 'layout', 'volumeSource', 'widgetUnitsSource'
];
}
@ -228,10 +223,6 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon
return createShapeLayout(svg, layout, this.sanitizer);
}
public isRequired(formControlName: string): boolean {
return this.levelCardWidgetSettingsForm.get(formControlName)?.hasValidator(Validators.required);
}
private _valuePreviewFn(): string {
const units: string = this.widgetConfig.config.units;
const decimals: number = this.widgetConfig.config.decimals;

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

@ -6108,7 +6108,9 @@
"color-and-font": "Color and font",
"shape-attribute-name": "Attribute name",
"total-volume-required": "Total volume is required.",
"attribute-name-required": "Attribute name is required."
"attribute-name-required": "Attribute name is required.",
"attribute-key-not-set": "Attribute '{{attributeName}}' key not set",
"attribute-key-invalid": "Attribute '{{attributeName}}' key is invalid"
},
"aggregated-value-card": {
"subtitle": "Subtitle",

Loading…
Cancel
Save