Browse Source

UI: Add cf details page

pull/14691/head
Vladyslav_Prykhodko 9 months ago
parent
commit
9604817521
  1. 192
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html
  2. 18
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss
  3. 166
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts
  4. 4
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts
  5. 52
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
  6. 4
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts
  7. 4
      ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts
  8. 84
      ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts
  9. 8
      ui-ngx/src/app/shared/models/entity-type.models.ts
  10. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

192
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html

@ -0,0 +1,192 @@
<!--
Copyright © 2016-2025 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.
-->
<div class="tb-details-buttons xs:flex xs:flex-col" *ngIf="!standalone">
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'open')"
[class.!hidden]="isEdit || isDetailsPage">
{{'common.open-details-page' | translate }}
</button>
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'export')"
[class.!hidden]="isEdit">
{{'action.export' | translate }}
</button>
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')"
[class.!hidden]="hideDelete() || isEdit">
{{'action.delete' | translate }}
</button>
<!-- <div class="flex flex-row xs:flex-col">-->
<!-- <button mat-raised-button-->
<!-- ngxClipboard-->
<!-- (cbOnSuccess)="onAssetProfileIdCopied($event)"-->
<!-- [cbContent]="entity?.id?.id"-->
<!-- [class.!hidden]="isEdit">-->
<!-- <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>-->
<!-- <span translate>asset-profile.copyId</span>-->
<!-- </button>-->
<!-- </div>-->
</div>
<div class="mat-padding flex flex-col">
<!-- <mat-form-field class="mat-block"-->
<!-- [class.!hidden]="isEdit || !isAssignedToCustomer(entity)-->
<!-- || entity?.customerIsPublic || assetScope !== 'tenant'">-->
<!-- <mat-label translate>asset.assignedToCustomer</mat-label>-->
<!-- <input matInput disabled [ngModel]="entity?.customerTitle">-->
<!-- </mat-form-field>-->
<!-- <div class="tb-small" style="padding-bottom: 10px; padding-left: 2px;"-->
<!-- [class.!hidden]="isEdit || !entity?.customerIsPublic || (assetScope !== 'customer' && assetScope !== 'tenant')">-->
<!-- {{ 'asset.asset-public' | translate }}-->
<!-- </div>-->
<!-- <form [formGroup]="entityForm">-->
<!-- <fieldset [disabled]="(isLoading$ | async) || !isEdit">-->
<!-- <mat-form-field class="mat-block">-->
<!-- <mat-label translate>asset.name</mat-label>-->
<!-- <input matInput formControlName="name" required>-->
<!-- <mat-error *ngIf="entityForm.get('name').hasError('required')">-->
<!-- {{ 'asset.name-required' | translate }}-->
<!-- </mat-error>-->
<!-- <mat-error *ngIf="entityForm.get('name').hasError('maxlength')">-->
<!-- {{ 'asset.name-max-length' | translate }}-->
<!-- </mat-error>-->
<!-- </mat-form-field>-->
<!-- <mat-form-field class="mat-block">-->
<!-- <mat-label translate>asset.label</mat-label>-->
<!-- <input matInput formControlName="label">-->
<!-- <mat-error *ngIf="entityForm.get('label').hasError('maxlength')">-->
<!-- {{ 'asset.label-max-length' | translate }}-->
<!-- </mat-error>-->
<!-- </mat-form-field>-->
<!-- <tb-asset-profile-autocomplete-->
<!-- [selectDefaultProfile]="isAdd"-->
<!-- required-->
<!-- formControlName="assetProfileId"-->
<!-- [showDetailsPageLink]="true"-->
<!-- (assetProfileUpdated)="onAssetProfileUpdated()">-->
<!-- </tb-asset-profile-autocomplete>-->
<!-- <tb-entity-autocomplete-->
<!-- *ngIf="isAdd"-->
<!-- useFullEntityId-->
<!-- formControlName="customerId"-->
<!-- labelText="asset.assign-to-customer"-->
<!-- [entityType]="entityType.CUSTOMER">-->
<!-- </tb-entity-autocomplete>-->
<!-- <div formGroupName="additionalInfo">-->
<!-- <mat-form-field class="mat-block">-->
<!-- <mat-label translate>asset.description</mat-label>-->
<!-- <textarea matInput formControlName="description" rows="2"></textarea>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- </fieldset>-->
<!-- </form>-->
<div mat-dialog-content class="flex-1" [formGroup]="entityForm">
<div class="tb-form-panel no-border no-padding">
<div class="tb-form-panel no-gap">
<div class="tb-form-panel-title mb-4">{{ 'common.general' | translate }}</div>
<div class="flex items-center gap-2">
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<mat-label>{{ 'entity-field.title' | translate }}</mat-label>
<input matInput maxlength="255" formControlName="name" required>
@if (entityForm.get('name').errors && entityForm.get('name').touched) {
<mat-error>
@if (entityForm.get('name').hasError('required')) {
{{ 'common.hint.title-required' | translate }}
} @else if (entityForm.get('name').hasError('pattern')) {
{{ 'common.hint.title-pattern' | translate }}
} @else if (entityForm.get('name').hasError('maxlength')) {
{{ 'common.hint.title-max-length' | translate }}
}
</mat-error>
}
<mat-hint></mat-hint>
</mat-form-field>
<tb-entity-debug-settings-button
formControlName="debugSettings"
style="margin-bottom: 22px"
[entityType]="EntityType.CALCULATED_FIELD"
[additionalActionConfig]="entitiesTableConfig.additionalDebugActionConfig"
/>
</div>
<tb-entity-select formControlName="entityId" appearance="outline" [allowedEntityTypes]="calculatedFieldsEntityTypeList"></tb-entity-select>
<mat-form-field appearance="outline" subscriptSizing="dynamic">
<mat-label>{{ 'common.type' | translate }}</mat-label>
<mat-select formControlName="type">
@for (type of fieldTypes; track type) {
<mat-option [value]="type">{{ CalculatedFieldTypeTranslations.get(type).name | translate}}</mat-option>
}
</mat-select>
@if (CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint) {
<mat-hint>{{ CalculatedFieldTypeTranslations.get(entityForm.get('type').value).hint | translate }}</mat-hint>
}
</mat-form-field>
</div>
@switch (entityForm.get('type').value) {
@case (CalculatedFieldType.GEOFENCING) {
<tb-geofencing-configuration formControlName="configuration"
[class.tb-form-panel-disabled]="disabledConfiguration"
[entityId]="entityId"
[entityName]="entityName"
[ownerId]="ownerId"
[tenantId]="tenantId"
></tb-geofencing-configuration>
}
@case (CalculatedFieldType.PROPAGATION) {
<tb-propagation-configuration formControlName="configuration"
[class.tb-form-panel-disabled]="disabledConfiguration"
[entityId]="entityId"
[entityName]="entityName"
[tenantId]="tenantId"
[ownerId]="ownerId"
[testScript]="onTestScript.bind(this)"
></tb-propagation-configuration>
}
@case (CalculatedFieldType.RELATED_ENTITIES_AGGREGATION) {
<tb-related-entities-aggregation-component formControlName="configuration"
[class.tb-form-panel-disabled]="disabledConfiguration"
[entityId]="entityId"
[entityName]="entityName"
[tenantId]="tenantId"
[testScript]="onTestScript.bind(this)"
></tb-related-entities-aggregation-component>
}
@case (CalculatedFieldType.ENTITY_AGGREGATION) {
<tb-entity-aggregation-component formControlName="configuration"
[class.tb-form-panel-disabled]="disabledConfiguration"
[entityId]="entityId"
[entityName]="entityName"
[tenantId]="tenantId"
></tb-entity-aggregation-component>
}
@default {
<tb-simple-configuration formControlName="configuration"
[class.tb-form-panel-disabled]="disabledConfiguration"
[entityId]="entityId"
[entityName]="entityName"
[ownerId]="ownerId"
[tenantId]="tenantId"
[isScript]="entityForm.get('type').value === CalculatedFieldType.SCRIPT"
[testScript]="onTestScript.bind(this)"
></tb-simple-configuration>
}
}
</div>
</div>
</div>

18
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.scss

@ -0,0 +1,18 @@
/**
* Copyright © 2016-2025 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 {
}

166
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts

@ -0,0 +1,166 @@
///
/// Copyright © 2016-2025 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 { ChangeDetectorRef, Component, DestroyRef, Inject, Input } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component';
import { FormBuilder, FormGroup, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { AliasEntityType, EntityType } from '@shared/models/entity-type.models';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
import { AssetInfo } from '@app/shared/models/asset.models';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import {
CalculatedFieldConfiguration,
CalculatedFieldInfo, calculatedFieldsEntityTypeList,
CalculatedFieldType, calculatedFieldTypes, CalculatedFieldTypeTranslations, OutputStrategyType
} from '@shared/models/calculated-field.models';
import { oneSpaceInsideRegex } from '@shared/models/regex.constants';
import { isDefined } from '@core/utils';
import { EntityId } from '@shared/models/id/entity-id';
import { pairwise, switchMap } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { BaseData } from '@shared/models/base-data';
import { Observable, of } from 'rxjs';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import {
CalculatedFieldsTableConfig,
CalculatedFieldsTableEntity
} from '@home/components/calculated-fields/calculated-fields-table-config';
@Component({
selector: 'tb-calculated-field',
templateUrl: './calculated-field.component.html',
styleUrls: ['./calculated-field.component.scss']
})
export class CalculatedFieldComponent extends EntityComponent<CalculatedFieldsTableEntity> {
@Input()
standalone = false;
@Input()
entityName: string;
disabledConfiguration = false;
readonly tenantId = getCurrentAuthUser(this.store).tenantId;
readonly ownerId = getCurrentAuthUser(this.store).tenantId;
readonly EntityType = EntityType;
readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList;
readonly CalculatedFieldType = CalculatedFieldType;
readonly fieldTypes = calculatedFieldTypes;
readonly CalculatedFieldTypeTranslations = CalculatedFieldTypeTranslations;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
@Inject('entity') protected entityValue: CalculatedFieldInfo,
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig,
protected fb: FormBuilder,
protected cd: ChangeDetectorRef,
private destroyRef: DestroyRef,
private calculatedFieldsService: CalculatedFieldsService) {
super(store, fb, entityValue, entitiesTableConfigValue, cd);
}
hideDelete() {
if (this.entitiesTableConfig) {
return !this.entitiesTableConfig.deleteEnabled(this.entity);
} else {
return false;
}
}
isAssignedToCustomer(entity: AssetInfo): boolean {
return entity && entity.customerId && entity.customerId.id !== NULL_UUID;
}
get entityId(): EntityId {
return this.entityForm.get('entityId').value;
}
get entitiesTableConfig(): CalculatedFieldsTableConfig {
return this.entitiesTableConfigValue;
}
changeEntity(entity: BaseData<EntityId>): void {
this.entityName = entity?.name;
}
buildForm(entity?: CalculatedFieldInfo): FormGroup {
const form = this.fb.group({
name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]],
entityId: [null],
type: [CalculatedFieldType.SIMPLE],
debugSettings: [],
configuration: this.fb.control<CalculatedFieldConfiguration>({} as CalculatedFieldConfiguration),
});
form.get('type').valueChanges.pipe(
pairwise(),
takeUntilDestroyed(this.destroyRef)
).subscribe(([prevType, nextType]) => {
if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) ||
![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) {
form.get('configuration').setValue(({} as CalculatedFieldConfiguration), {emitEvent: false});
}
});
return form;
}
updateForm(entity: CalculatedFieldInfo) {
const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.entityId, ...value } = entity ?? {};
if (configuration.type !== CalculatedFieldType.ALARM) {
if (isDefined(configuration?.output) && !configuration?.output?.strategy) {
configuration.output.strategy = {type: OutputStrategyType.RULE_CHAIN};
}
}
this.entityForm.patchValue({ configuration, type, debugSettings, entityId, ...value }, {emitEvent: false});
setTimeout(() => this.entityForm.get('type').updateValueAndValidity({onlySelf: true}));
if (!entityId) {
this.entityForm.get('configuration').disable({emitEvent: false});
this.disabledConfiguration = true;
}
}
onTestScript(expression?: string): Observable<string> {
const calculatedFieldId = this.entityId?.id;
if (calculatedFieldId) {
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true})
.pipe(
switchMap(event => {
const args = event?.arguments ? JSON.parse(event.arguments) : null;
return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), args, false, expression);
}),
takeUntilDestroyed(this.destroyRef)
)
}
return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), null, false, expression);
}
updateFormState() {
if (this.entityForm) {
if (this.isEditValue) {
this.entityForm.enable({emitEvent: false});
this.entityForm.get('entityId').disable({emitEvent: false});
} else {
this.entityForm.disable({emitEvent: false});
}
}
}
}

4
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.module.ts

@ -50,6 +50,7 @@ import {
import { import {
CalculatedFieldsFilterConfigComponent CalculatedFieldsFilterConfigComponent
} from '@home/components/calculated-fields/table-header/calculated-fields-filter-config.component'; } from '@home/components/calculated-fields/table-header/calculated-fields-filter-config.component';
import { CalculatedFieldComponent } from '@home/components/calculated-fields/calculated-field.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -57,7 +58,8 @@ import {
CalculatedFieldScriptTestDialogComponent, CalculatedFieldScriptTestDialogComponent,
CalculatedFieldTestArgumentsComponent, CalculatedFieldTestArgumentsComponent,
CalculatedFieldsHeaderComponent, CalculatedFieldsHeaderComponent,
CalculatedFieldsFilterConfigComponent CalculatedFieldsFilterConfigComponent,
CalculatedFieldComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

52
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts

@ -20,7 +20,7 @@ import {
EntityTableColumn, EntityTableColumn,
EntityTableConfig EntityTableConfig
} from '@home/models/entity/entities-table-config.models'; } from '@home/models/entity/entities-table-config.models';
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MatDialog, MatDialogRef } from '@angular/material/dialog';
@ -67,8 +67,11 @@ import { EventsDialogComponent, EventsDialogData } from '@home/dialogs/events-di
import { import {
CalculatedFieldsHeaderComponent CalculatedFieldsHeaderComponent
} from '@home/components/calculated-fields/table-header/calculated-fields-header.component'; } from '@home/components/calculated-fields/table-header/calculated-fields-header.component';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { CalculatedFieldComponent } from '@home/components/calculated-fields/calculated-field.component';
import { Router } from '@angular/router';
type CalculatedFieldsTableEntity = CalculatedField | CalculatedFieldInfo; export type CalculatedFieldsTableEntity = CalculatedField | CalculatedFieldInfo;
export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFieldsTableEntity> { export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFieldsTableEntity> {
@ -93,30 +96,33 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
private importExportService: ImportExportService, private importExportService: ImportExportService,
private entityDebugSettingsService: EntityDebugSettingsService, private entityDebugSettingsService: EntityDebugSettingsService,
private utilsService: UtilsService, private utilsService: UtilsService,
private router: Router,
public pageMode = false, public pageMode = false,
) { ) {
super(); super();
if (this.pageMode) { if (this.pageMode) {
this.headerComponent = CalculatedFieldsHeaderComponent; this.headerComponent = CalculatedFieldsHeaderComponent;
this.handleRowClick = ($event, entity) => { this.entityComponent = CalculatedFieldComponent;
this.editCalculatedField($event, entity); this.rowPointer = true;
this.rowPointer = true;
return true;
};
} }
this.tableTitle = this.pageMode ? '' : this.translate.instant('entity.type-calculated-fields'); this.tableTitle = this.pageMode ? '' : this.translate.instant('entity.type-calculated-fields');
this.detailsPanelEnabled = false; this.detailsPanelEnabled = this.pageMode;
this.entityType = EntityType.CALCULATED_FIELD; this.entityType = EntityType.CALCULATED_FIELD;
this.entityTranslations = entityTypeTranslations.get(EntityType.CALCULATED_FIELD); this.entityTranslations = entityTypeTranslations.get(EntityType.CALCULATED_FIELD);
this.entityResources = entityTypeResources.get(EntityType.CALCULATED_FIELD);
this.entitiesFetchFunction = (pageLink: PageLink) => this.fetchCalculatedFields(pageLink); this.entitiesFetchFunction = (pageLink: PageLink) => this.fetchCalculatedFields(pageLink);
this.addEntity = this.getCalculatedFieldDialog.bind(this); this.addEntity = this.getCalculatedFieldDialog.bind(this);
this.loadEntity = id => this.calculatedFieldsService.getCalculatedFieldById(id.id);
this.deleteEntityTitle = (field) => this.translate.instant('calculated-fields.delete-title', {title: field.name}); this.deleteEntityTitle = (field) => this.translate.instant('calculated-fields.delete-title', {title: field.name});
this.deleteEntityContent = () => this.translate.instant('calculated-fields.delete-text'); this.deleteEntityContent = () => this.translate.instant('calculated-fields.delete-text');
this.deleteEntitiesTitle = count => this.translate.instant('calculated-fields.delete-multiple-title', {count}); this.deleteEntitiesTitle = count => this.translate.instant('calculated-fields.delete-multiple-title', {count});
this.deleteEntitiesContent = () => this.translate.instant('calculated-fields.delete-multiple-text'); this.deleteEntitiesContent = () => this.translate.instant('calculated-fields.delete-multiple-text');
this.deleteEntity = id => this.calculatedFieldsService.deleteCalculatedField(id.id); this.deleteEntity = id => this.calculatedFieldsService.deleteCalculatedField(id.id);
this.onEntityAction = action => this.onCFAction(action);
this.addActionDescriptors = [ this.addActionDescriptors = [
{ {
name: this.translate.instant('calculated-fields.create'), name: this.translate.instant('calculated-fields.create'),
@ -172,14 +178,16 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
isEnabled: () => true, isEnabled: () => true,
iconFunction: ({ debugSettings }) => this.entityDebugSettingsService.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline', iconFunction: ({ debugSettings }) => this.entityDebugSettingsService.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline',
onAction: ($event, entity) => this.onOpenDebugConfig($event, entity), onAction: ($event, entity) => this.onOpenDebugConfig($event, entity),
}, }
{ );
if (!this.pageMode) {
this.cellActionDescriptors.push({
name: this.translate.instant('action.edit'), name: this.translate.instant('action.edit'),
icon: 'edit', icon: 'edit',
isEnabled: () => true, isEnabled: () => true,
onAction: ($event, entity) => this.editCalculatedField($event, entity), onAction: ($event, entity) => this.editCalculatedField($event, entity),
} })
); }
} }
fetchCalculatedFields(pageLink: PageLink): Observable<PageData<CalculatedFieldsTableEntity>> { fetchCalculatedFields(pageLink: PageLink): Observable<PageData<CalculatedFieldsTableEntity>> {
@ -354,7 +362,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
).subscribe(() => this.updateData()); ).subscribe(() => this.updateData());
} }
private getTestScriptDialog(calculatedField: CalculatedFieldsTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable<string> { getTestScriptDialog(calculatedField: CalculatedFieldsTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable<string> {
if ( if (
calculatedField.type === CalculatedFieldType.SCRIPT || calculatedField.type === CalculatedFieldType.SCRIPT ||
calculatedField.type === CalculatedFieldType.RELATED_ENTITIES_AGGREGATION || calculatedField.type === CalculatedFieldType.RELATED_ENTITIES_AGGREGATION ||
@ -394,4 +402,22 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
return of(null); return of(null);
} }
} }
private openCalculatedField($event: Event, entity: CalculatedFieldsTableEntity) {
$event?.stopPropagation();
const url = this.router.createUrlTree(['calculatedFields', entity.id.id]);
this.router.navigateByUrl(url);
}
private onCFAction(action: EntityAction<CalculatedFieldsTableEntity>): boolean {
switch (action.action) {
case 'open':
this.openCalculatedField(action.event, action.entity);
return true;
case 'export':
this.exportCalculatedField(action.event, action.entity);
return true;
}
return false;
}
} }

4
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts

@ -36,7 +36,7 @@ import { ImportExportService } from '@shared/import-export/import-export.service
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service'; import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { UtilsService } from "@core/services/utils.service"; import { UtilsService } from "@core/services/utils.service";
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
@Component({ @Component({
selector: 'tb-calculated-fields-table', selector: 'tb-calculated-fields-table',
@ -70,6 +70,7 @@ export class CalculatedFieldsTableComponent {
private utilsService: UtilsService, private utilsService: UtilsService,
private destroyRef: DestroyRef, private destroyRef: DestroyRef,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router
) { ) {
this.pageMode = !!this.route.snapshot.data.isPage; this.pageMode = !!this.route.snapshot.data.isPage;
effect(() => { effect(() => {
@ -88,6 +89,7 @@ export class CalculatedFieldsTableComponent {
this.importExportService, this.importExportService,
this.entityDebugSettingsService, this.entityDebugSettingsService,
this.utilsService, this.utilsService,
this.router,
this.pageMode, this.pageMode,
); );
this.cd.markForCheck(); this.cd.markForCheck();

4
ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts

@ -22,7 +22,9 @@ import { TranslateService } from '@ngx-translate/core';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
import { EntityDebugSettingPanelConfig } from '@home/components/entity/debug/entity-debug-settings.model'; import { EntityDebugSettingPanelConfig } from '@home/components/entity/debug/entity-debug-settings.model';
@Injectable() @Injectable({
providedIn: 'root'
})
export class EntityDebugSettingsService { export class EntityDebugSettingsService {
constructor( constructor(

84
ui-ngx/src/app/modules/home/pages/calculated-fields/calculated-fields-routing.module.ts

@ -14,24 +14,94 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { NgModule } from '@angular/core'; import { DestroyRef, inject, NgModule, Renderer2 } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { ActivatedRouteSnapshot, ResolveFn, Router, RouterModule, RouterStateSnapshot, Routes } from '@angular/router';
import { Authority } from '@shared/models/authority.enum'; import { Authority } from '@shared/models/authority.enum';
import { MenuId } from '@core/services/menu.models'; import { MenuId } from '@core/services/menu.models';
import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component'; import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component';
import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component';
import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard';
import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models';
import { BreadCrumbConfig } from '@shared/components/breadcrumb';
import { CalculatedFieldsTableConfig } from '@home/components/calculated-fields/calculated-fields-table-config';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { DatePipe } from '@angular/common';
import { ImportExportService } from '@shared/import-export/import-export.service';
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
import { UtilsService } from '@core/services/utils.service';
export const CalculatedFieldsTableConfigResolver: ResolveFn<CalculatedFieldsTableConfig> =
(_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot,
calculatedFieldsService = inject(CalculatedFieldsService),
translate = inject(TranslateService),
dialog = inject(MatDialog),
store = inject(Store<AppState>),
datePipe = inject(DatePipe),
destroyRef = inject(DestroyRef),
importExportService = inject(ImportExportService),
entityDebugSettingsService = inject(EntityDebugSettingsService),
utilsService = inject(UtilsService),
router = inject(Router),
) => {
return new CalculatedFieldsTableConfig(
calculatedFieldsService,
translate,
dialog,
datePipe,
null,
store,
destroyRef,
null,
null,
null,
importExportService,
entityDebugSettingsService,
utilsService,
router,
false,
);
};
const routes: Routes = [ const routes: Routes = [
{ {
path: 'calculatedFields', path: 'calculatedFields',
component: CalculatedFieldsTableComponent,
data: { data: {
auth: [Authority.TENANT_ADMIN],
title: 'entity.type-calculated-fields',
breadcrumb: { breadcrumb: {
menuId: MenuId.calculated_fields menuId: MenuId.calculated_fields
}
},
children: [
{
path: '',
component: CalculatedFieldsTableComponent,
data: {
auth: [Authority.TENANT_ADMIN],
title: 'entity.type-calculated-fields',
isPage: true,
}
}, },
isPage: true, {
} path: ':entityId',
component: EntityDetailsPageComponent,
canDeactivate: [ConfirmOnExitGuard],
data: {
breadcrumb: {
labelFunction: entityDetailsPageBreadcrumbLabelFunction,
icon: 'mdi:function-variant'
} as BreadCrumbConfig<EntityDetailsPageComponent>,
auth: [Authority.TENANT_ADMIN],
title: 'entity.type-calculated-fields',
},
resolve: {
entitiesTableConfig: CalculatedFieldsTableConfigResolver
}
}
]
} }
]; ];

8
ui-ngx/src/app/shared/models/entity-type.models.ts

@ -490,7 +490,7 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
type: 'entity.type-calculated-field', type: 'entity.type-calculated-field',
typePlural: 'entity.type-calculated-fields', typePlural: 'entity.type-calculated-fields',
list: 'calculated-fields.list', list: 'calculated-fields.list',
add: 'action.add', details: 'calculated-fields.calculated-field-details',
noEntities: 'calculated-fields.no-found', noEntities: 'calculated-fields.no-found',
search: 'action.search', search: 'action.search',
selectedEntities: 'calculated-fields.selected-fields' selectedEntities: 'calculated-fields.selected-fields'
@ -658,6 +658,12 @@ export const entityTypeResources = new Map<EntityType, EntityTypeResource<BaseDa
helpLinkId: 'aiModels' helpLinkId: 'aiModels'
} }
], ],
[
EntityType.CALCULATED_FIELD,
{
helpLinkId: 'calculatedField'
}
],
[ [
EntityType.API_KEY, EntityType.API_KEY,
{ {

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

@ -1086,7 +1086,7 @@
"filter-types": "Audit log types" "filter-types": "Audit log types"
}, },
"debug-settings": { "debug-settings": {
"label": "Debug Configuration", "label": "Debug configuration",
"on-failure": "Failures only (24/7)", "on-failure": "Failures only (24/7)",
"all-messages": "All messages ({{time}})", "all-messages": "All messages ({{time}})",
"failures": "Failures", "failures": "Failures",
@ -1115,6 +1115,7 @@
"arguments": "Arguments", "arguments": "Arguments",
"decimals-by-default": "Decimals by default", "decimals-by-default": "Decimals by default",
"debugging": "Calculated field debugging", "debugging": "Calculated field debugging",
"calculated-field-details": "Calculated field details",
"argument-name": "Argument name", "argument-name": "Argument name",
"name": "Name", "name": "Name",
"datasource": "Datasource", "datasource": "Datasource",

Loading…
Cancel
Save