UI: General resources

This commit is contained in:
ArtemDzhereleiko 2025-09-10 09:41:47 +03:00 committed by Vladyslav_Prykhodko
parent b9a9348eac
commit 055a1ae56d
12 changed files with 77 additions and 50 deletions

View File

@ -299,9 +299,7 @@ export class EntityService {
entityIds); entityIds);
break; break;
case EntityType.TB_RESOURCE: case EntityType.TB_RESOURCE:
observable = this.getEntitiesByIdsObservable( observable = this.resourceService.getResourcesByIds(entityIds, config);
(id) => this.resourceService.getResource(id, config),
entityIds);
break; break;
} }
return observable; return observable;
@ -478,7 +476,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, subType as ResourceType, config); entitiesObservable = this.resourceService.getResources(pageLink, subType as ResourceType, null, config);
break; break;
case EntityType.QUEUE_STATS: case EntityType.QUEUE_STATS:
pageLink.sortOrder.property = 'createdTime'; pageLink.sortOrder.property = 'createdTime';

View File

@ -24,6 +24,7 @@ import { Resource, ResourceInfo, ResourceSubType, ResourceType, TBResourceScope
import { catchError, mergeMap } from 'rxjs/operators'; import { catchError, mergeMap } from 'rxjs/operators';
import { isNotEmptyStr } from '@core/utils'; import { isNotEmptyStr } from '@core/utils';
import { ResourcesService } from '@core/services/resources.service'; import { ResourcesService } from '@core/services/resources.service';
import { NotificationTarget } from "@shared/models/notification.models";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -47,12 +48,8 @@ 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, resourceType?: ResourceType, config?: RequestConfig): Observable<PageData<ResourceInfo>> { public getTenantResources(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ResourceInfo>> {
let url = `/api/resource${pageLink.toQuery()}`; return this.http.get<PageData<ResourceInfo>>(`/api/resource/tenant${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config))
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> {
@ -98,4 +95,9 @@ export class ResourceService {
return this.http.delete(`/api/resource/${resourceId}?force=${force}`, defaultHttpOptionsFromConfig(config)); return this.http.delete(`/api/resource/${resourceId}?force=${force}`, defaultHttpOptionsFromConfig(config));
} }
public getResourcesByIds(ids: string[], config?: RequestConfig): Observable<Array<ResourceInfo>> {
return this.http.get<Array<ResourceInfo>>(`/api/resource?resourceIds=${ids.join(',')}`,
defaultHttpOptionsFromConfig(config));
}
} }

View File

@ -32,8 +32,8 @@
<tb-resources-library #resourcesComponent <tb-resources-library #resourcesComponent
[standalone]="true" [standalone]="true"
[entity]="resources" [entity]="resources"
[defaultResourceType]="ResourceType.TEXT" [defaultResourceType]="ResourceType.GENERAL"
[resourceTypes]="[ResourceType.TEXT]" [resourceTypes]="[ResourceType.GENERAL]"
[isEdit]="true"> [isEdit]="true">
</tb-resources-library> </tb-resources-library>
</div> </div>

View File

@ -106,6 +106,9 @@ export class ResourcesDialogComponent extends DialogComponent<ResourcesDialogCom
map((response) => response[0]) map((response) => response[0])
).subscribe(result => this.dialogRef.close(result)); ).subscribe(result => this.dialogRef.close(result));
} else { } else {
if (resource.resourceType !== ResourceType.GENERAL) {
delete resource.descriptor;
}
this.resourceService.saveResource(resource).subscribe(result => this.dialogRef.close(result)); this.resourceService.saveResource(resource).subscribe(result => this.dialogRef.close(result));
} }
} }

View File

@ -48,14 +48,16 @@
<div class="mat-padding flex flex-col"> <div class="mat-padding flex flex-col">
<form [formGroup]="entityForm"> <form [formGroup]="entityForm">
<fieldset [disabled]="(isLoading$ | async) || !isEdit"> <fieldset [disabled]="(isLoading$ | async) || !isEdit">
<mat-form-field class="mat-block"> @if (resourceTypes.length > 1) {
<mat-label translate>resource.resource-type</mat-label> <mat-form-field class="mat-block">
<mat-select formControlName="resourceType" required> <mat-label translate>resource.resource-type</mat-label>
<mat-option *ngFor="let resourceType of resourceTypes" [value]="resourceType"> <mat-select formControlName="resourceType" required>
{{ resourceTypesTranslationMap.get(resourceType) | translate }} <mat-option *ngFor="let resourceType of resourceTypes" [value]="resourceType">
</mat-option> {{ resourceTypesTranslationMap.get(resourceType) | translate }}
</mat-select> </mat-option>
</mat-form-field> </mat-select>
</mat-form-field>
}
<mat-form-field class="mat-block" *ngIf="entityForm.get('resourceType').value !== resourceType.LWM2M_MODEL || !isAdd"> <mat-form-field class="mat-block" *ngIf="entityForm.get('resourceType').value !== resourceType.LWM2M_MODEL || !isAdd">
<mat-label translate>resource.title</mat-label> <mat-label translate>resource.title</mat-label>
<input matInput formControlName="title" required> <input matInput formControlName="title" required>
@ -66,26 +68,29 @@
{{ 'resource.title-max-length' | translate }} {{ 'resource.title-max-length' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<tb-file-input *ngIf="isAdd" @if (isAdd || ((isAdd || isEdit) && entityForm.get('resourceType').value === resourceType.GENERAL)) {
formControlName="data" <tb-file-input formControlName="data"
required required
label="{{ (entityForm.get('resourceType').value === resourceType.LWM2M_MODEL ? 'resource.resource-files' : 'resource.resource-file') | translate }}" label="{{ (entityForm.get('resourceType').value === resourceType.LWM2M_MODEL ? 'resource.resource-files' : 'resource.resource-file') | translate }}"
[readAsBinary]="true" [readAsBinary]="true"
[maxSizeByte]="maxResourceSize" [maxSizeByte]="maxResourceSize"
[allowedExtensions]="getAllowedExtensions()" [allowedExtensions]="getAllowedExtensions()"
[contentConvertFunction]="convertToBase64File" [contentConvertFunction]="convertToBase64File"
[accept]="getAcceptType()" [accept]="getAcceptType()"
[multipleFile]="entityForm.get('resourceType').value === resourceType.LWM2M_MODEL" [multipleFile]="entityForm.get('resourceType').value === resourceType.LWM2M_MODEL"
dropLabel="{{'resource.drop-resource-file-or' | translate}}" dropLabel="{{'resource.drop-resource-file-or' | translate}}"
[existingFileName]="entityForm.get('fileName')?.value" [existingFileName]="entityForm.get('fileName')?.value"
(fileNameChanged)="entityForm?.get('fileName').patchValue($event)"> (mediaTypeChanged)="mediaTypeChange($event)"
</tb-file-input> (fileNameChanged)="entityForm?.get('fileName').patchValue($event)">
<div *ngIf="!isAdd" class="flex flex-row xs:flex-col sm:gap-2 md:flex-col gt-md:gap-2"> </tb-file-input>
<mat-form-field class="flex-1"> } @else {
<mat-label translate>resource.file-name</mat-label> <div class="flex flex-row xs:flex-col sm:gap-2 md:flex-col gt-md:gap-2">
<input matInput formControlName="fileName" type="text"> <mat-form-field class="flex-1">
</mat-form-field> <mat-label translate>resource.file-name</mat-label>
</div> <input matInput formControlName="fileName" type="text">
</mat-form-field>
</div>
}
</fieldset> </fieldset>
</form> </form>
</div> </div>

View File

@ -44,7 +44,7 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
standalone = false; standalone = false;
@Input() @Input()
resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS, ResourceType.TEXT]; resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS, ResourceType.GENERAL];
@Input() @Input()
defaultResourceType = ResourceType.LWM2M_MODEL; defaultResourceType = ResourceType.LWM2M_MODEL;
@ -90,10 +90,19 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, Validators.required], resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, Validators.required],
fileName: [entity ? entity.fileName : null, Validators.required], fileName: [entity ? entity.fileName : null, Validators.required],
data: [entity ? entity.data : null, this.isAdd ? [Validators.required] : []] data: [entity ? entity.data : null, this.isAdd ? [Validators.required] : []],
descriptor: this.fb.group({
mediaType: ['']
})
}); });
} }
mediaTypeChange(mediaType: string): void {
if (this.entityForm.get('resourceType').value === ResourceType.GENERAL) {
this.entityForm.get('descriptor').get('mediaType').patchValue(mediaType);
}
}
updateForm(entity: Resource): void { updateForm(entity: Resource): void {
this.entityForm.patchValue(entity); this.entityForm.patchValue(entity);
} }

View File

@ -76,7 +76,7 @@
placeholderText="{{ 'rule-node-config.ai.ai-resources' | translate }}" placeholderText="{{ 'rule-node-config.ai.ai-resources' | translate }}"
[inlineField]="true" [inlineField]="true"
[entityType]="EntityType.TB_RESOURCE" [entityType]="EntityType.TB_RESOURCE"
[subType]="ResourceType.TEXT" [subType]="ResourceType.GENERAL"
(createNew)="createAiResources($event, 'resourceIds')" (createNew)="createAiResources($event, 'resourceIds')"
formControlName="resourceIds"> formControlName="resourceIds">
</tb-entity-list> </tb-entity-list>

View File

@ -129,7 +129,7 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent {
disableClose: true, disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
resources: {title: name, resourceType: ResourceType.TEXT}, resources: {title: name, resourceType: ResourceType.GENERAL},
isAdd: true isAdd: true
} }
}).afterClosed() }).afterClosed()

View File

@ -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, ResourceType.TEXT]; readonly resourceTypes = [ResourceType.LWM2M_MODEL, ResourceType.PKCS_12, ResourceType.JKS, ResourceType.GENERAL];
readonly resourceTypesTranslationMap = ResourceTypeTranslationMap; readonly resourceTypesTranslationMap = ResourceTypeTranslationMap;
constructor(protected store: Store<AppState>) { constructor(protected store: Store<AppState>) {

View File

@ -129,10 +129,15 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
@Output() @Output()
fileNameChanged = new EventEmitter<string|string[]>(); fileNameChanged = new EventEmitter<string|string[]>();
@Output()
mediaTypeChanged = new EventEmitter<string>();
fileName: string | string[]; fileName: string | string[];
fileContent: any; fileContent: any;
files: File[]; files: File[];
mediaType: string;
@ViewChild('flow', {static: true}) @ViewChild('flow', {static: true})
flow: FlowDirective; flow: FlowDirective;
@ -180,6 +185,7 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
this.fileContent = files[0].fileContent; this.fileContent = files[0].fileContent;
this.fileName = files[0].fileName; this.fileName = files[0].fileName;
this.files = files[0].files; this.files = files[0].files;
this.mediaType = files[0].mediaType;
this.updateModel(); this.updateModel();
} else if (files.length > 1) { } else if (files.length > 1) {
this.fileContent = files.map(content => content.fileContent); this.fileContent = files.map(content => content.fileContent);
@ -203,6 +209,7 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
let fileName = null; let fileName = null;
let fileContent = null; let fileContent = null;
let files = null; let files = null;
let mediaType = null;
if (reader.readyState === reader.DONE) { if (reader.readyState === reader.DONE) {
if (!this.workFromFileObj) { if (!this.workFromFileObj) {
fileContent = reader.result; fileContent = reader.result;
@ -211,16 +218,18 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
fileContent = this.contentConvertFunction(fileContent); fileContent = this.contentConvertFunction(fileContent);
} }
fileName = fileContent ? file.name : null; fileName = fileContent ? file.name : null;
mediaType = file?.file?.type || null;
} }
} else if (file.name || file.file){ } else if (file.name || file.file){
files = file.file; files = file.file;
fileName = file.name; fileName = file.name;
mediaType = file.file.type || null;
} }
} }
resolve({fileContent, fileName, files}); resolve({fileContent, fileName, files, mediaType});
}; };
reader.onerror = () => { reader.onerror = () => {
resolve({fileContent: null, fileName: null, files: null}); resolve({fileContent: null, fileName: null, files: null, mediaType: null});
}; };
if (this.readAsBinary) { if (this.readAsBinary) {
reader.readAsBinaryString(file.file); reader.readAsBinaryString(file.file);
@ -283,6 +292,7 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
this.propagateChange(this.files); this.propagateChange(this.files);
} else { } else {
this.propagateChange(this.fileContent); this.propagateChange(this.fileContent);
this.mediaTypeChanged.emit(this.mediaType);
this.fileNameChanged.emit(this.fileName); this.fileNameChanged.emit(this.fileName);
} }
} }

View File

@ -25,7 +25,7 @@ export enum ResourceType {
PKCS_12 = 'PKCS_12', PKCS_12 = 'PKCS_12',
JKS = 'JKS', JKS = 'JKS',
JS_MODULE = 'JS_MODULE', JS_MODULE = 'JS_MODULE',
TEXT = 'TEXT', GENERAL = 'GENERAL',
} }
export enum ResourceSubType { export enum ResourceSubType {
@ -59,7 +59,7 @@ export const ResourceTypeTranslationMap = new Map<ResourceType, string>(
[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'], [ResourceType.GENERAL, 'resource.type.general'],
] ]
); );

View File

@ -4489,7 +4489,7 @@
"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" "general": "General"
}, },
"resource-sub-type": "Sub-type", "resource-sub-type": "Sub-type",
"sub-type": { "sub-type": {