UI: Add new resource type text
This commit is contained in:
parent
5a6ddce8f5
commit
b9a9348eac
@ -100,6 +100,7 @@ import { OAuth2Service } from '@core/http/oauth2.service';
|
|||||||
import { MobileAppService } from '@core/http/mobile-app.service';
|
import { MobileAppService } from '@core/http/mobile-app.service';
|
||||||
import { PlatformType } from '@shared/models/oauth2.models';
|
import { PlatformType } from '@shared/models/oauth2.models';
|
||||||
import { AiModelService } from '@core/http/ai-model.service';
|
import { AiModelService } from '@core/http/ai-model.service';
|
||||||
|
import { ResourceType } from "@shared/models/resource.models";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -297,6 +298,11 @@ export class EntityService {
|
|||||||
(id) => this.ruleChainService.getRuleChain(id, config),
|
(id) => this.ruleChainService.getRuleChain(id, config),
|
||||||
entityIds);
|
entityIds);
|
||||||
break;
|
break;
|
||||||
|
case EntityType.TB_RESOURCE:
|
||||||
|
observable = this.getEntitiesByIdsObservable(
|
||||||
|
(id) => this.resourceService.getResource(id, config),
|
||||||
|
entityIds);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return observable;
|
return observable;
|
||||||
}
|
}
|
||||||
@ -472,7 +478,7 @@ export class EntityService {
|
|||||||
break;
|
break;
|
||||||
case EntityType.TB_RESOURCE:
|
case EntityType.TB_RESOURCE:
|
||||||
pageLink.sortOrder.property = 'title';
|
pageLink.sortOrder.property = 'title';
|
||||||
entitiesObservable = this.resourceService.getTenantResources(pageLink, config);
|
entitiesObservable = this.resourceService.getTenantResources(pageLink, subType as ResourceType, config);
|
||||||
break;
|
break;
|
||||||
case EntityType.QUEUE_STATS:
|
case EntityType.QUEUE_STATS:
|
||||||
pageLink.sortOrder.property = 'createdTime';
|
pageLink.sortOrder.property = 'createdTime';
|
||||||
|
|||||||
@ -47,8 +47,12 @@ export class ResourceService {
|
|||||||
return this.http.get<PageData<ResourceInfo>>(url, defaultHttpOptionsFromConfig(config));
|
return this.http.get<PageData<ResourceInfo>>(url, defaultHttpOptionsFromConfig(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTenantResources(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
|
public getTenantResources(pageLink: PageLink, resourceType?: ResourceType, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
|
||||||
return this.http.get<PageData<ResourceInfo>>(`/api/resource/tenant${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
|
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> {
|
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';
|
} 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 { CheckConnectivityDialogComponent } from '@home/components/ai-model/check-connectivity-dialog.component';
|
||||||
import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-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({
|
@NgModule({
|
||||||
declarations:
|
declarations:
|
||||||
@ -358,6 +360,8 @@ import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialo
|
|||||||
CalculatedFieldTestArgumentsComponent,
|
CalculatedFieldTestArgumentsComponent,
|
||||||
CheckConnectivityDialogComponent,
|
CheckConnectivityDialogComponent,
|
||||||
AIModelDialogComponent,
|
AIModelDialogComponent,
|
||||||
|
ResourcesDialogComponent,
|
||||||
|
ResourcesLibraryComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -505,6 +509,8 @@ import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialo
|
|||||||
CalculatedFieldTestArgumentsComponent,
|
CalculatedFieldTestArgumentsComponent,
|
||||||
CheckConnectivityDialogComponent,
|
CheckConnectivityDialogComponent,
|
||||||
AIModelDialogComponent,
|
AIModelDialogComponent,
|
||||||
|
ResourcesDialogComponent,
|
||||||
|
ResourcesLibraryComponent,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
WidgetComponentService,
|
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.
|
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"
|
<button mat-raised-button color="primary"
|
||||||
[disabled]="(isLoading$ | async)"
|
[disabled]="(isLoading$ | async)"
|
||||||
(click)="onEntityAction($event, 'open')"
|
(click)="onEntityAction($event, 'open')"
|
||||||
@ -14,7 +14,7 @@
|
|||||||
/// limitations under the License.
|
/// 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 { Subject } from 'rxjs';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
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 {
|
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 resourceType = ResourceType;
|
||||||
readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS];
|
|
||||||
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
|
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
|
||||||
readonly maxResourceSize = getCurrentAuthState(this.store).maxResourceSize;
|
readonly maxResourceSize = getCurrentAuthState(this.store).maxResourceSize;
|
||||||
|
|
||||||
@ -49,8 +57,8 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
|
|||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
@Inject('entity') protected entityValue: Resource,
|
@Optional() @Inject('entity') protected entityValue: Resource,
|
||||||
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>,
|
@Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<Resource>,
|
||||||
public fb: FormBuilder,
|
public fb: FormBuilder,
|
||||||
protected cd: ChangeDetectorRef) {
|
protected cd: ChangeDetectorRef) {
|
||||||
super(store, fb, entityValue, entitiesTableConfigValue, cd);
|
super(store, fb, entityValue, entitiesTableConfigValue, cd);
|
||||||
@ -138,7 +146,7 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
|
|||||||
|
|
||||||
private observeResourceTypeChange(): void {
|
private observeResourceTypeChange(): void {
|
||||||
this.entityForm.get('resourceType').valueChanges.pipe(
|
this.entityForm.get('resourceType').valueChanges.pipe(
|
||||||
startWith(ResourceType.LWM2M_MODEL),
|
startWith(this.defaultResourceType || ResourceType.LWM2M_MODEL),
|
||||||
takeUntil(this.destroy$)
|
takeUntil(this.destroy$)
|
||||||
).subscribe((type: ResourceType) => this.onResourceTypeChange(type));
|
).subscribe((type: ResourceType) => this.onResourceTypeChange(type));
|
||||||
}
|
}
|
||||||
@ -70,6 +70,16 @@
|
|||||||
{{ 'rule-node-config.ai.user-prompt-blank' | translate }}
|
{{ 'rule-node-config.ai.user-prompt-blank' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</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>
|
</div>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -24,6 +24,8 @@ import { AiModel, AiRuleNodeResponseFormatTypeOnlyText, ResponseFormat } from '@
|
|||||||
import { deepTrim } from '@core/utils';
|
import { deepTrim } from '@core/utils';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { jsonRequired } from '@shared/components/json-object-edit.component';
|
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({
|
@Component({
|
||||||
selector: 'tb-external-node-ai-config',
|
selector: 'tb-external-node-ai-config',
|
||||||
@ -38,6 +40,9 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
|
|||||||
|
|
||||||
responseFormat = ResponseFormat;
|
responseFormat = ResponseFormat;
|
||||||
|
|
||||||
|
EntityType = EntityType;
|
||||||
|
ResourceType = ResourceType;
|
||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder,
|
constructor(private fb: UntypedFormBuilder,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private dialog: MatDialog) {
|
private dialog: MatDialog) {
|
||||||
@ -53,6 +58,7 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
|
|||||||
modelId: [configuration?.modelId ?? null, [Validators.required]],
|
modelId: [configuration?.modelId ?? null, [Validators.required]],
|
||||||
systemPrompt: [configuration?.systemPrompt ?? '', [Validators.maxLength(500_000), Validators.pattern(/.*\S.*/)]],
|
systemPrompt: [configuration?.systemPrompt ?? '', [Validators.maxLength(500_000), Validators.pattern(/.*\S.*/)]],
|
||||||
userPrompt: [configuration?.userPrompt ?? '', [Validators.required, 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({
|
responseFormat: this.fb.group({
|
||||||
type: [configuration?.responseFormat?.type ?? ResponseFormat.JSON, []],
|
type: [configuration?.responseFormat?.type ?? ResponseFormat.JSON, []],
|
||||||
schema: [configuration?.responseFormat?.schema ?? null, [jsonRequired]],
|
schema: [configuration?.responseFormat?.schema ?? null, [jsonRequired]],
|
||||||
@ -116,5 +122,23 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
|
|||||||
this.aiConfigForm.get(formControl).markAsDirty();
|
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 { SmsProviderComponent } from '@home/pages/admin/sms-provider.component';
|
||||||
import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dialog.component';
|
import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dialog.component';
|
||||||
import { HomeSettingsComponent } from '@home/pages/admin/home-settings.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 { ResourceTabsComponent } from '@home/pages/admin/resource/resource-tabs.component';
|
||||||
import { ResourcesTableHeaderComponent } from '@home/pages/admin/resource/resources-table-header.component';
|
import { ResourcesTableHeaderComponent } from '@home/pages/admin/resource/resources-table-header.component';
|
||||||
import { QueueComponent } from '@home/pages/admin/queue/queue.component';
|
import { QueueComponent } from '@home/pages/admin/queue/queue.component';
|
||||||
@ -49,7 +48,6 @@ import { ResourceLibraryTabsComponent } from '@home/pages/admin/resource/resourc
|
|||||||
SendTestSmsDialogComponent,
|
SendTestSmsDialogComponent,
|
||||||
SecuritySettingsComponent,
|
SecuritySettingsComponent,
|
||||||
HomeSettingsComponent,
|
HomeSettingsComponent,
|
||||||
ResourcesLibraryComponent,
|
|
||||||
ResourceTabsComponent,
|
ResourceTabsComponent,
|
||||||
ResourceLibraryTabsComponent,
|
ResourceLibraryTabsComponent,
|
||||||
ResourcesTableHeaderComponent,
|
ResourcesTableHeaderComponent,
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import { getCurrentAuthUser } from '@core/auth/auth.selectors';
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { Authority } from '@shared/models/authority.enum';
|
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 { PageLink } from '@shared/models/page/page-link';
|
||||||
import { EntityAction } from '@home/models/entity/entity-component.models';
|
import { EntityAction } from '@home/models/entity/entity-component.models';
|
||||||
import { map } from 'rxjs/operators';
|
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> {
|
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;
|
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>) {
|
constructor(protected store: Store<AppState>) {
|
||||||
|
|||||||
@ -40,6 +40,12 @@
|
|||||||
[matAutocompleteConnectedTo]="origin"
|
[matAutocompleteConnectedTo]="origin"
|
||||||
[matAutocomplete]="entityAutocomplete"
|
[matAutocomplete]="entityAutocomplete"
|
||||||
[matChipInputFor]="chipList">
|
[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-chip-grid>
|
||||||
<mat-autocomplete #entityAutocomplete="matAutocomplete"
|
<mat-autocomplete #entityAutocomplete="matAutocomplete"
|
||||||
class="tb-autocomplete"
|
class="tb-autocomplete"
|
||||||
@ -56,6 +62,11 @@
|
|||||||
<span>
|
<span>
|
||||||
{{ 'entity.no-entities-matching' | translate: {entity: searchText} }}
|
{{ 'entity.no-entities-matching' | translate: {entity: searchText} }}
|
||||||
</span>
|
</span>
|
||||||
|
@if (allowCreateNew) {
|
||||||
|
<span>
|
||||||
|
<a translate (click)="createNewEntity($event, searchText)">entity.create-new-key</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
|||||||
@ -14,7 +14,18 @@
|
|||||||
/// limitations under the License.
|
/// 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 {
|
import {
|
||||||
ControlValueAccessor,
|
ControlValueAccessor,
|
||||||
NG_VALIDATORS,
|
NG_VALIDATORS,
|
||||||
@ -93,6 +104,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@ -109,6 +121,13 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
|
|||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
inlineField: boolean;
|
inlineField: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
@coerceBoolean()
|
||||||
|
allowCreateNew: boolean;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
createNew = new EventEmitter<string>();
|
||||||
|
|
||||||
@ViewChild('entityInput') entityInput: ElementRef<HTMLInputElement>;
|
@ViewChild('entityInput') entityInput: ElementRef<HTMLInputElement>;
|
||||||
@ViewChild('entityAutocomplete') matAutocomplete: MatAutocomplete;
|
@ViewChild('entityAutocomplete') matAutocomplete: MatAutocomplete;
|
||||||
@ViewChild('chipList', {static: true}) chipList: MatChipGrid;
|
@ViewChild('chipList', {static: true}) chipList: MatChipGrid;
|
||||||
@ -136,6 +155,11 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
|
|||||||
this.entityListFormGroup.get('entities').updateValueAndValidity();
|
this.entityListFormGroup.get('entities').updateValueAndValidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createNewEntity($event: Event, searchText?: string) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
this.createNew.emit(searchText);
|
||||||
|
}
|
||||||
|
|
||||||
registerOnChange(fn: any): void {
|
registerOnChange(fn: any): void {
|
||||||
this.propagateChange = fn;
|
this.propagateChange = fn;
|
||||||
}
|
}
|
||||||
@ -201,6 +225,9 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, OnChan
|
|||||||
this.modelValue = null;
|
this.modelValue = null;
|
||||||
}
|
}
|
||||||
this.dirty = true;
|
this.dirty = true;
|
||||||
|
if (this.entityInput) {
|
||||||
|
this.entityInput.nativeElement.value = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(): ValidationErrors | null {
|
validate(): ValidationErrors | null {
|
||||||
|
|||||||
@ -24,7 +24,8 @@ export enum ResourceType {
|
|||||||
LWM2M_MODEL = 'LWM2M_MODEL',
|
LWM2M_MODEL = 'LWM2M_MODEL',
|
||||||
PKCS_12 = 'PKCS_12',
|
PKCS_12 = 'PKCS_12',
|
||||||
JKS = 'JKS',
|
JKS = 'JKS',
|
||||||
JS_MODULE = 'JS_MODULE'
|
JS_MODULE = 'JS_MODULE',
|
||||||
|
TEXT = 'TEXT',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ResourceSubType {
|
export enum ResourceSubType {
|
||||||
@ -57,7 +58,8 @@ export const ResourceTypeTranslationMap = new Map<ResourceType, string>(
|
|||||||
[ResourceType.LWM2M_MODEL, 'resource.type.lwm2m-model'],
|
[ResourceType.LWM2M_MODEL, 'resource.type.lwm2m-model'],
|
||||||
[ResourceType.PKCS_12, 'resource.type.pkcs-12'],
|
[ResourceType.PKCS_12, 'resource.type.pkcs-12'],
|
||||||
[ResourceType.JKS, 'resource.type.jks'],
|
[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;
|
title?: string;
|
||||||
resourceType: ResourceType;
|
resourceType: ResourceType;
|
||||||
resourceSubType?: ResourceSubType;
|
resourceSubType?: ResourceSubType;
|
||||||
fileName: string;
|
fileName?: string;
|
||||||
public: boolean;
|
public?: boolean;
|
||||||
publicResourceKey?: string;
|
publicResourceKey?: string;
|
||||||
readonly link?: string;
|
readonly link?: string;
|
||||||
readonly publicLink?: string;
|
readonly publicLink?: string;
|
||||||
@ -87,7 +89,7 @@ export interface TbResourceInfo<D> extends Omit<BaseData<TbResourceId>, 'name' |
|
|||||||
export type ResourceInfo = TbResourceInfo<any>;
|
export type ResourceInfo = TbResourceInfo<any>;
|
||||||
|
|
||||||
export interface Resource extends ResourceInfo {
|
export interface Resource extends ResourceInfo {
|
||||||
data: string;
|
data?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4488,7 +4488,8 @@
|
|||||||
"jks": "JKS",
|
"jks": "JKS",
|
||||||
"js-module": "JS module",
|
"js-module": "JS module",
|
||||||
"lwm2m-model": "LWM2M model",
|
"lwm2m-model": "LWM2M model",
|
||||||
"pkcs-12": "PKCS #12"
|
"pkcs-12": "PKCS #12",
|
||||||
|
"text": "Text"
|
||||||
},
|
},
|
||||||
"resource-sub-type": "Sub-type",
|
"resource-sub-type": "Sub-type",
|
||||||
"sub-type": {
|
"sub-type": {
|
||||||
@ -5467,7 +5468,8 @@
|
|||||||
"timeout-required": "Timeout is required",
|
"timeout-required": "Timeout is required",
|
||||||
"timeout-validation": "Must be from 1 second to 10 minutes.",
|
"timeout-validation": "Must be from 1 second to 10 minutes.",
|
||||||
"force-acknowledgement": "Force acknowledgement",
|
"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": {
|
"timezone": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user