UI: Add new resource type text
This commit is contained in:
		
							parent
							
								
									ec30bb0578
								
							
						
					
					
						commit
						c3d40a61e8
					
				@ -100,6 +100,7 @@ import { OAuth2Service } from '@core/http/oauth2.service';
 | 
			
		||||
import { MobileAppService } from '@core/http/mobile-app.service';
 | 
			
		||||
import { PlatformType } from '@shared/models/oauth2.models';
 | 
			
		||||
import { AiModelService } from '@core/http/ai-model.service';
 | 
			
		||||
import { ResourceType } from "@shared/models/resource.models";
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -297,6 +298,11 @@ export class EntityService {
 | 
			
		||||
          (id) => this.ruleChainService.getRuleChain(id, config),
 | 
			
		||||
          entityIds);
 | 
			
		||||
        break;
 | 
			
		||||
      case EntityType.TB_RESOURCE:
 | 
			
		||||
        observable = this.getEntitiesByIdsObservable(
 | 
			
		||||
          (id) => this.resourceService.getResource(id, config),
 | 
			
		||||
          entityIds);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return observable;
 | 
			
		||||
  }
 | 
			
		||||
@ -472,7 +478,7 @@ export class EntityService {
 | 
			
		||||
        break;
 | 
			
		||||
      case EntityType.TB_RESOURCE:
 | 
			
		||||
        pageLink.sortOrder.property = 'title';
 | 
			
		||||
        entitiesObservable = this.resourceService.getTenantResources(pageLink, config);
 | 
			
		||||
        entitiesObservable = this.resourceService.getTenantResources(pageLink, subType as ResourceType, config);
 | 
			
		||||
        break;
 | 
			
		||||
      case EntityType.QUEUE_STATS:
 | 
			
		||||
        pageLink.sortOrder.property = 'createdTime';
 | 
			
		||||
 | 
			
		||||
@ -47,8 +47,12 @@ export class ResourceService {
 | 
			
		||||
    return this.http.get<PageData<ResourceInfo>>(url, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getTenantResources(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
 | 
			
		||||
    return this.http.get<PageData<ResourceInfo>>(`/api/resource/tenant${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  public getTenantResources(pageLink: PageLink, resourceType?: ResourceType, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
 | 
			
		||||
    let url = `/api/resource${pageLink.toQuery()}`;
 | 
			
		||||
    if (isNotEmptyStr(resourceType)) {
 | 
			
		||||
      url += `&resourceType=${resourceType}`;
 | 
			
		||||
    }
 | 
			
		||||
    return this.http.get<PageData<ResourceInfo>>(url, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getResource(resourceId: string, config?: RequestConfig): Observable<Resource> {
 | 
			
		||||
 | 
			
		||||
@ -205,6 +205,8 @@ import {
 | 
			
		||||
} from '@home/components/calculated-fields/components/test-arguments/calculated-field-test-arguments.component';
 | 
			
		||||
import { CheckConnectivityDialogComponent } from '@home/components/ai-model/check-connectivity-dialog.component';
 | 
			
		||||
import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialog.component';
 | 
			
		||||
import { ResourcesDialogComponent } from "@home/components/resources/resources-dialog.component";
 | 
			
		||||
import { ResourcesLibraryComponent } from "@home/components/resources/resources-library.component";
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations:
 | 
			
		||||
@ -358,6 +360,8 @@ import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialo
 | 
			
		||||
      CalculatedFieldTestArgumentsComponent,
 | 
			
		||||
      CheckConnectivityDialogComponent,
 | 
			
		||||
      AIModelDialogComponent,
 | 
			
		||||
      ResourcesDialogComponent,
 | 
			
		||||
      ResourcesLibraryComponent,
 | 
			
		||||
    ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonModule,
 | 
			
		||||
@ -505,6 +509,8 @@ import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialo
 | 
			
		||||
    CalculatedFieldTestArgumentsComponent,
 | 
			
		||||
    CheckConnectivityDialogComponent,
 | 
			
		||||
    AIModelDialogComponent,
 | 
			
		||||
    ResourcesDialogComponent,
 | 
			
		||||
    ResourcesLibraryComponent,
 | 
			
		||||
  ],
 | 
			
		||||
  providers: [
 | 
			
		||||
    WidgetComponentService,
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,54 @@
 | 
			
		||||
<!--
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<form (ngSubmit)="save()" style="width: 600px;">
 | 
			
		||||
  <mat-toolbar color="primary">
 | 
			
		||||
    <h2>{{ 'resource.add' | translate }}</h2>
 | 
			
		||||
    <span class="flex-1"></span>
 | 
			
		||||
    <button mat-icon-button
 | 
			
		||||
            (click)="cancel()"
 | 
			
		||||
            type="button">
 | 
			
		||||
      <mat-icon class="material-icons">close</mat-icon>
 | 
			
		||||
    </button>
 | 
			
		||||
  </mat-toolbar>
 | 
			
		||||
  <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
 | 
			
		||||
  </mat-progress-bar>
 | 
			
		||||
  <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
 | 
			
		||||
  <div mat-dialog-content>
 | 
			
		||||
    <tb-resources-library #resourcesComponent
 | 
			
		||||
                          [standalone]="true"
 | 
			
		||||
                          [entity]="resources"
 | 
			
		||||
                          [defaultResourceType]="ResourceType.TEXT"
 | 
			
		||||
                          [resourceTypes]="[ResourceType.TEXT]"
 | 
			
		||||
                          [isEdit]="true">
 | 
			
		||||
    </tb-resources-library>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div mat-dialog-actions class="flex items-center justify-end">
 | 
			
		||||
    <button mat-button color="primary"
 | 
			
		||||
            type="button"
 | 
			
		||||
            cdkFocusInitial
 | 
			
		||||
            [disabled]="(isLoading$ | async)"
 | 
			
		||||
            (click)="cancel()">
 | 
			
		||||
      {{ 'action.cancel' | translate }}
 | 
			
		||||
    </button>
 | 
			
		||||
    <button mat-raised-button color="primary"
 | 
			
		||||
            type="submit"
 | 
			
		||||
            [disabled]="(isLoading$ | async) || resourcesComponent.entityForm?.invalid || !resourcesComponent.entityForm?.dirty">
 | 
			
		||||
      {{ (isAdd  ? 'action.add' : 'action.save') | translate }}
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
</form>
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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 ::ng-deep {
 | 
			
		||||
  .mat-mdc-dialog-content {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    padding: 0 !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,113 @@
 | 
			
		||||
///
 | 
			
		||||
/// 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 { AfterViewInit, Component, Inject, SkipSelf, ViewChild } from '@angular/core';
 | 
			
		||||
import { DialogComponent } from '@shared/components/dialog.component';
 | 
			
		||||
import { Store } from '@ngrx/store';
 | 
			
		||||
import { AppState } from '@core/core.state';
 | 
			
		||||
import { Router } from '@angular/router';
 | 
			
		||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
 | 
			
		||||
import { FormGroupDirective, NgForm, UntypedFormControl } from '@angular/forms';
 | 
			
		||||
import { EntityType } from '@shared/models/entity-type.models';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
import { ResourcesLibraryComponent } from "@home/components/resources/resources-library.component";
 | 
			
		||||
import { ErrorStateMatcher } from "@angular/material/core";
 | 
			
		||||
import { Resource, ResourceType } from "@shared/models/resource.models";
 | 
			
		||||
import { ResourceService } from "@core/http/resource.service";
 | 
			
		||||
 | 
			
		||||
export interface ResourcesDialogData {
 | 
			
		||||
  resources?: Resource;
 | 
			
		||||
  isAdd?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-resources-dialog',
 | 
			
		||||
  templateUrl: './resources-dialog.component.html',
 | 
			
		||||
  providers: [{provide: ErrorStateMatcher, useExisting: ResourcesDialogComponent}],
 | 
			
		||||
  styleUrls: ['./resources-dialog.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class ResourcesDialogComponent extends DialogComponent<ResourcesDialogComponent, Resource> implements ErrorStateMatcher, AfterViewInit {
 | 
			
		||||
 | 
			
		||||
  readonly entityType = EntityType;
 | 
			
		||||
 | 
			
		||||
  ResourceType = ResourceType;
 | 
			
		||||
 | 
			
		||||
  isAdd = false;
 | 
			
		||||
 | 
			
		||||
  submitted = false;
 | 
			
		||||
 | 
			
		||||
  resources: Resource;
 | 
			
		||||
 | 
			
		||||
  @ViewChild('resourcesComponent', {static: true}) resourcesComponent: ResourcesLibraryComponent;
 | 
			
		||||
 | 
			
		||||
  constructor(protected store: Store<AppState>,
 | 
			
		||||
              protected router: Router,
 | 
			
		||||
              protected dialogRef: MatDialogRef<ResourcesDialogComponent, Resource>,
 | 
			
		||||
              @Inject(MAT_DIALOG_DATA) public data: ResourcesDialogData,
 | 
			
		||||
              @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
 | 
			
		||||
              private resourceService: ResourceService) {
 | 
			
		||||
    super(store, router, dialogRef);
 | 
			
		||||
 | 
			
		||||
    if (this.data.isAdd) {
 | 
			
		||||
      this.isAdd = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.data.resources) {
 | 
			
		||||
      this.resources = this.data.resources;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngAfterViewInit(): void {
 | 
			
		||||
    if (this.isAdd) {
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        this.resourcesComponent.entityForm.markAsDirty();
 | 
			
		||||
      }, 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
 | 
			
		||||
    const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
 | 
			
		||||
    const customErrorState = !!(control && control.invalid && this.submitted);
 | 
			
		||||
    return originalErrorState || customErrorState;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cancel(): void {
 | 
			
		||||
    this.dialogRef.close(null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  save(): void {
 | 
			
		||||
    this.submitted = true;
 | 
			
		||||
    if (this.resourcesComponent.entityForm.valid) {
 | 
			
		||||
      const resource = {...this.resourcesComponent.entityFormValue()};
 | 
			
		||||
      if (Array.isArray(resource.data)) {
 | 
			
		||||
        const resources = [];
 | 
			
		||||
        resource.data.forEach((data, index) => {
 | 
			
		||||
          resources.push({
 | 
			
		||||
            resourceType: resource.resourceType,
 | 
			
		||||
            data,
 | 
			
		||||
            fileName: resource.fileName[index],
 | 
			
		||||
            title: resource.title
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
        this.resourceService.saveResources(resources, {resendRequest: true}).pipe(
 | 
			
		||||
          map((response) => response[0])
 | 
			
		||||
        ).subscribe(result => this.dialogRef.close(result));
 | 
			
		||||
      } else {
 | 
			
		||||
        this.resourceService.saveResource(resource).subscribe(result => this.dialogRef.close(result));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
    limitations under the License.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<div class="tb-details-buttons xs:flex xs:flex-col">
 | 
			
		||||
<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')"
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
/// limitations under the License.
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from '@angular/core';
 | 
			
		||||
import { ChangeDetectorRef, Component, Inject, Input, OnDestroy, OnInit, Optional } from '@angular/core';
 | 
			
		||||
import { Subject } from 'rxjs';
 | 
			
		||||
import { Store } from '@ngrx/store';
 | 
			
		||||
import { AppState } from '@core/core.state';
 | 
			
		||||
@ -40,8 +40,16 @@ import { getCurrentAuthState } from '@core/auth/auth.selectors';
 | 
			
		||||
})
 | 
			
		||||
export class ResourcesLibraryComponent extends EntityComponent<Resource> implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  standalone = false;
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS, ResourceType.TEXT];
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  defaultResourceType = ResourceType.LWM2M_MODEL;
 | 
			
		||||
 | 
			
		||||
  readonly resourceType = ResourceType;
 | 
			
		||||
  readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS];
 | 
			
		||||
  readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
 | 
			
		||||
  readonly maxResourceSize = getCurrentAuthState(this.store).maxResourceSize;
 | 
			
		||||
 | 
			
		||||
@ -49,8 +57,8 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
 | 
			
		||||
 | 
			
		||||
  constructor(protected store: Store<AppState>,
 | 
			
		||||
              protected translate: TranslateService,
 | 
			
		||||
              @Inject('entity') protected entityValue: Resource,
 | 
			
		||||
              @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>,
 | 
			
		||||
              @Optional() @Inject('entity') protected entityValue: Resource,
 | 
			
		||||
              @Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>,
 | 
			
		||||
              public fb: FormBuilder,
 | 
			
		||||
              protected cd: ChangeDetectorRef) {
 | 
			
		||||
    super(store, fb, entityValue, entitiesTableConfigValue, cd);
 | 
			
		||||
@ -138,7 +146,7 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
 | 
			
		||||
 | 
			
		||||
  private observeResourceTypeChange(): void {
 | 
			
		||||
    this.entityForm.get('resourceType').valueChanges.pipe(
 | 
			
		||||
      startWith(ResourceType.LWM2M_MODEL),
 | 
			
		||||
      startWith(this.defaultResourceType || ResourceType.LWM2M_MODEL),
 | 
			
		||||
      takeUntil(this.destroy$)
 | 
			
		||||
    ).subscribe((type: ResourceType) => this.onResourceTypeChange(type));
 | 
			
		||||
  }
 | 
			
		||||
@ -70,6 +70,16 @@
 | 
			
		||||
            {{ 'rule-node-config.ai.user-prompt-blank' | translate }}
 | 
			
		||||
          </mat-error>
 | 
			
		||||
        </mat-form-field>
 | 
			
		||||
        <tb-entity-list
 | 
			
		||||
          class="flex-1"
 | 
			
		||||
          allowCreateNew
 | 
			
		||||
          placeholderText="{{ 'rule-node-config.ai.ai-resources' | translate }}"
 | 
			
		||||
          [inlineField]="true"
 | 
			
		||||
          [entityType]="EntityType.TB_RESOURCE"
 | 
			
		||||
          [subType]="ResourceType.TEXT"
 | 
			
		||||
          (createNew)="createAiResources($event, 'resourceIds')"
 | 
			
		||||
          formControlName="resourceIds">
 | 
			
		||||
        </tb-entity-list>
 | 
			
		||||
      </div>
 | 
			
		||||
    </mat-expansion-panel>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,8 @@ import { AiModel, AiRuleNodeResponseFormatTypeOnlyText, ResponseFormat } from '@
 | 
			
		||||
import { deepTrim } from '@core/utils';
 | 
			
		||||
import { TranslateService } from '@ngx-translate/core';
 | 
			
		||||
import { jsonRequired } from '@shared/components/json-object-edit.component';
 | 
			
		||||
import { Resource, ResourceType } from "@shared/models/resource.models";
 | 
			
		||||
import { ResourcesDialogComponent, ResourcesDialogData } from "@home/components/resources/resources-dialog.component";
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-external-node-ai-config',
 | 
			
		||||
@ -38,6 +40,9 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
 | 
			
		||||
 | 
			
		||||
  responseFormat = ResponseFormat;
 | 
			
		||||
 | 
			
		||||
  EntityType = EntityType;
 | 
			
		||||
  ResourceType = ResourceType;
 | 
			
		||||
 | 
			
		||||
  constructor(private fb: UntypedFormBuilder,
 | 
			
		||||
              private translate: TranslateService,
 | 
			
		||||
              private dialog: MatDialog) {
 | 
			
		||||
@ -53,6 +58,7 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
 | 
			
		||||
      modelId: [configuration?.modelId ?? null, [Validators.required]],
 | 
			
		||||
      systemPrompt: [configuration?.systemPrompt ?? '', [Validators.maxLength(500_000), Validators.pattern(/.*\S.*/)]],
 | 
			
		||||
      userPrompt: [configuration?.userPrompt ?? '', [Validators.required, Validators.maxLength(500_000), Validators.pattern(/.*\S.*/)]],
 | 
			
		||||
      resourceIds: [configuration?.resourceIds ?? []],
 | 
			
		||||
      responseFormat: this.fb.group({
 | 
			
		||||
        type: [configuration?.responseFormat?.type ?? ResponseFormat.JSON, []],
 | 
			
		||||
        schema: [configuration?.responseFormat?.schema ?? null, [jsonRequired]],
 | 
			
		||||
@ -116,5 +122,23 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
 | 
			
		||||
          this.aiConfigForm.get(formControl).markAsDirty();
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  createAiResources(name: string, formControl: string) {
 | 
			
		||||
    this.dialog.open<ResourcesDialogComponent, ResourcesDialogData, Resource>(ResourcesDialogComponent, {
 | 
			
		||||
      disableClose: true,
 | 
			
		||||
      panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
 | 
			
		||||
      data: {
 | 
			
		||||
        resources: {title: name, resourceType: ResourceType.TEXT},
 | 
			
		||||
        isAdd: true
 | 
			
		||||
      }
 | 
			
		||||
    }).afterClosed()
 | 
			
		||||
      .subscribe((resource) => {
 | 
			
		||||
        if (resource) {
 | 
			
		||||
          const resourceIds = [...(this.aiConfigForm.get(formControl).value || []), resource.id.id];
 | 
			
		||||
          this.aiConfigForm.get(formControl).patchValue(resourceIds);
 | 
			
		||||
          this.aiConfigForm.get(formControl).markAsDirty();
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,6 @@ import { HomeComponentsModule } from '@modules/home/components/home-components.m
 | 
			
		||||
import { SmsProviderComponent } from '@home/pages/admin/sms-provider.component';
 | 
			
		||||
import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dialog.component';
 | 
			
		||||
import { HomeSettingsComponent } from '@home/pages/admin/home-settings.component';
 | 
			
		||||
import { ResourcesLibraryComponent } from '@home/pages/admin/resource/resources-library.component';
 | 
			
		||||
import { ResourceTabsComponent } from '@home/pages/admin/resource/resource-tabs.component';
 | 
			
		||||
import { ResourcesTableHeaderComponent } from '@home/pages/admin/resource/resources-table-header.component';
 | 
			
		||||
import { QueueComponent } from '@home/pages/admin/queue/queue.component';
 | 
			
		||||
@ -49,7 +48,6 @@ import { ResourceLibraryTabsComponent } from '@home/pages/admin/resource/resourc
 | 
			
		||||
      SendTestSmsDialogComponent,
 | 
			
		||||
      SecuritySettingsComponent,
 | 
			
		||||
      HomeSettingsComponent,
 | 
			
		||||
      ResourcesLibraryComponent,
 | 
			
		||||
      ResourceTabsComponent,
 | 
			
		||||
      ResourceLibraryTabsComponent,
 | 
			
		||||
      ResourcesTableHeaderComponent,
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ import { getCurrentAuthUser } from '@core/auth/auth.selectors';
 | 
			
		||||
import { Store } from '@ngrx/store';
 | 
			
		||||
import { AppState } from '@core/core.state';
 | 
			
		||||
import { Authority } from '@shared/models/authority.enum';
 | 
			
		||||
import { ResourcesLibraryComponent } from '@home/pages/admin/resource/resources-library.component';
 | 
			
		||||
import { ResourcesLibraryComponent } from '@home/components/resources/resources-library.component';
 | 
			
		||||
import { PageLink } from '@shared/models/page/page-link';
 | 
			
		||||
import { EntityAction } from '@home/models/entity/entity-component.models';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ import { PageLink } from '@shared/models/page/page-link';
 | 
			
		||||
})
 | 
			
		||||
export class ResourcesTableHeaderComponent extends EntityTableHeaderComponent<Resource, PageLink, ResourceInfo> {
 | 
			
		||||
 | 
			
		||||
  readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS];
 | 
			
		||||
  readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS, ResourceType.TEXT];
 | 
			
		||||
  readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
 | 
			
		||||
 | 
			
		||||
  constructor(protected store: Store<AppState>) {
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,12 @@
 | 
			
		||||
           [matAutocompleteConnectedTo]="origin"
 | 
			
		||||
           [matAutocomplete]="entityAutocomplete"
 | 
			
		||||
           [matChipInputFor]="chipList">
 | 
			
		||||
    <button mat-button color="primary" matSuffix
 | 
			
		||||
            type="button"
 | 
			
		||||
            *ngIf="allowCreateNew && !disabled"
 | 
			
		||||
            (click)="createNewEntity($event)">
 | 
			
		||||
      <span style="white-space: nowrap">{{ 'entity.create-new' | translate }}</span>
 | 
			
		||||
    </button>
 | 
			
		||||
  </mat-chip-grid>
 | 
			
		||||
  <mat-autocomplete #entityAutocomplete="matAutocomplete"
 | 
			
		||||
                    class="tb-autocomplete"
 | 
			
		||||
@ -56,6 +62,11 @@
 | 
			
		||||
          <span>
 | 
			
		||||
          {{ 'entity.no-entities-matching' | translate: {entity: searchText} }}
 | 
			
		||||
          </span>
 | 
			
		||||
          @if (allowCreateNew) {
 | 
			
		||||
            <span>
 | 
			
		||||
              <a translate (click)="createNewEntity($event, searchText)">entity.create-new-key</a>
 | 
			
		||||
            </span>
 | 
			
		||||
          }
 | 
			
		||||
        </ng-template>
 | 
			
		||||
      </div>
 | 
			
		||||
    </mat-option>
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,18 @@
 | 
			
		||||
/// limitations under the License.
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
import { Component, ElementRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  Component,
 | 
			
		||||
  ElementRef,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnChanges,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
  SimpleChanges,
 | 
			
		||||
  ViewChild
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  ControlValueAccessor,
 | 
			
		||||
  NG_VALIDATORS,
 | 
			
		||||
@ -93,6 +104,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  @coerceBoolean()
 | 
			
		||||
  disabled: boolean;
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
@ -109,6 +121,13 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
 | 
			
		||||
  @coerceBoolean()
 | 
			
		||||
  inlineField: boolean;
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  @coerceBoolean()
 | 
			
		||||
  allowCreateNew: boolean;
 | 
			
		||||
 | 
			
		||||
  @Output()
 | 
			
		||||
  createNew = new EventEmitter<string>();
 | 
			
		||||
 | 
			
		||||
  @ViewChild('entityInput') entityInput: ElementRef<HTMLInputElement>;
 | 
			
		||||
  @ViewChild('entityAutocomplete') matAutocomplete: MatAutocomplete;
 | 
			
		||||
  @ViewChild('chipList', {static: true}) chipList: MatChipGrid;
 | 
			
		||||
@ -136,6 +155,11 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
 | 
			
		||||
    this.entityListFormGroup.get('entities').updateValueAndValidity();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createNewEntity($event: Event, searchText?: string) {
 | 
			
		||||
    $event.stopPropagation();
 | 
			
		||||
    this.createNew.emit(searchText);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerOnChange(fn: any): void {
 | 
			
		||||
    this.propagateChange = fn;
 | 
			
		||||
  }
 | 
			
		||||
@ -201,6 +225,9 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
 | 
			
		||||
      this.modelValue = null;
 | 
			
		||||
    }
 | 
			
		||||
    this.dirty = true;
 | 
			
		||||
    if (this.entityInput) {
 | 
			
		||||
      this.entityInput.nativeElement.value = '';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate(): ValidationErrors | null {
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,8 @@ export enum ResourceType {
 | 
			
		||||
  LWM2M_MODEL = 'LWM2M_MODEL',
 | 
			
		||||
  PKCS_12 = 'PKCS_12',
 | 
			
		||||
  JKS = 'JKS',
 | 
			
		||||
  JS_MODULE = 'JS_MODULE'
 | 
			
		||||
  JS_MODULE = 'JS_MODULE',
 | 
			
		||||
  TEXT = 'TEXT',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum ResourceSubType {
 | 
			
		||||
@ -57,7 +58,8 @@ export const ResourceTypeTranslationMap = new Map<ResourceType, string>(
 | 
			
		||||
    [ResourceType.LWM2M_MODEL, 'resource.type.lwm2m-model'],
 | 
			
		||||
    [ResourceType.PKCS_12, 'resource.type.pkcs-12'],
 | 
			
		||||
    [ResourceType.JKS, 'resource.type.jks'],
 | 
			
		||||
    [ResourceType.JS_MODULE, 'resource.type.js-module']
 | 
			
		||||
    [ResourceType.JS_MODULE, 'resource.type.js-module'],
 | 
			
		||||
    [ResourceType.TEXT, 'resource.type.text'],
 | 
			
		||||
  ]
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@ -76,8 +78,8 @@ export interface TbResourceInfo<D> extends Omit<BaseData<TbResourceId>, 'name' |
 | 
			
		||||
  title?: string;
 | 
			
		||||
  resourceType: ResourceType;
 | 
			
		||||
  resourceSubType?: ResourceSubType;
 | 
			
		||||
  fileName: string;
 | 
			
		||||
  public: boolean;
 | 
			
		||||
  fileName?: string;
 | 
			
		||||
  public?: boolean;
 | 
			
		||||
  publicResourceKey?: string;
 | 
			
		||||
  readonly link?: string;
 | 
			
		||||
  readonly publicLink?: string;
 | 
			
		||||
@ -87,7 +89,7 @@ export interface TbResourceInfo<D> extends Omit<BaseData<TbResourceId>, 'name' |
 | 
			
		||||
export type ResourceInfo = TbResourceInfo<any>;
 | 
			
		||||
 | 
			
		||||
export interface Resource extends ResourceInfo {
 | 
			
		||||
  data: string;
 | 
			
		||||
  data?: string;
 | 
			
		||||
  name?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4488,7 +4488,8 @@
 | 
			
		||||
            "jks": "JKS",
 | 
			
		||||
            "js-module": "JS module",
 | 
			
		||||
            "lwm2m-model": "LWM2M model",
 | 
			
		||||
            "pkcs-12": "PKCS #12"
 | 
			
		||||
            "pkcs-12": "PKCS #12",
 | 
			
		||||
            "text": "Text"
 | 
			
		||||
        },
 | 
			
		||||
        "resource-sub-type": "Sub-type",
 | 
			
		||||
        "sub-type": {
 | 
			
		||||
@ -5467,7 +5468,8 @@
 | 
			
		||||
            "timeout-required": "Timeout is required",
 | 
			
		||||
            "timeout-validation": "Must be from 1 second to 10 minutes.",
 | 
			
		||||
            "force-acknowledgement": "Force acknowledgement",
 | 
			
		||||
            "force-acknowledgement-hint": "If enabled, the incoming message is acknowledged immediately. The model's response is then enqueued as a separate, new message."
 | 
			
		||||
            "force-acknowledgement-hint": "If enabled, the incoming message is acknowledged immediately. The model's response is then enqueued as a separate, new message.",
 | 
			
		||||
            "ai-resources": "AI resources"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "timezone": {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user