From 15b3b3574fd40070969f0d3c96aa2a5f1e5feb21 Mon Sep 17 00:00:00 2001 From: operramon Date: Thu, 9 Jul 2026 17:32:39 +0200 Subject: [PATCH 1/2] [sc-15049] Support key value field filtering in filter assistant --- libs/common/common.babel | 235 ++++++++++++++++++ libs/common/src/assets/i18n/ca.json | 10 + libs/common/src/assets/i18n/en.json | 10 + libs/common/src/assets/i18n/es.json | 10 + libs/common/src/assets/i18n/fr.json | 10 + .../add-filter-modal.component.html | 75 +++++- .../add-filter-modal.component.ts | 105 +++++++- .../filter-expression-modal.component.html | 27 ++ .../filter-expression-modal.component.ts | 90 ++++++- .../filter-value/filter-value.component.html | 35 ++- .../src/lib/db/search/search.models.ts | 30 +++ 11 files changed, 619 insertions(+), 18 deletions(-) diff --git a/libs/common/common.babel b/libs/common/common.babel index 15bf139410..64d24b338e 100644 --- a/libs/common/common.babel +++ b/libs/common/common.babel @@ -49037,6 +49037,75 @@ + + key_value_contains + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + key_value_eq + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + key_value_gte_lte + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + keyword @@ -49481,6 +49550,172 @@ + + key-value-filter + + + contains + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + equal + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + field + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + gte + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + lte + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + schema_id + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + + + + + key-value-filters + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + kind-filter diff --git a/libs/common/src/assets/i18n/ca.json b/libs/common/src/assets/i18n/ca.json index b7eb37aa77..28b60342c1 100644 --- a/libs/common/src/assets/i18n/ca.json +++ b/libs/common/src/assets/i18n/ca.json @@ -2026,6 +2026,9 @@ "search.configuration.filter-expression-modal.filter-type.field": "Camp", "search.configuration.filter-expression-modal.filter-type.field_mimetype": "Tipus MIME del camp", "search.configuration.filter-expression-modal.filter-type.generated": "Generador", + "search.configuration.filter-expression-modal.filter-type.key_value_contains": "Clau-valor (conté)", + "search.configuration.filter-expression-modal.filter-type.key_value_eq": "Clau-valor (valor exacte)", + "search.configuration.filter-expression-modal.filter-type.key_value_gte_lte": "Clau-valor (major/menor)", "search.configuration.filter-expression-modal.filter-type.keyword": "Paraula clau", "search.configuration.filter-expression-modal.filter-type.kind": "Tipus de paràgraf", "search.configuration.filter-expression-modal.filter-type.label": "Etiqueta", @@ -2045,6 +2048,13 @@ "search.configuration.filter-expression-modal.generated-filter.data-augmentation": "Augment de dades", "search.configuration.filter-expression-modal.generated-filter.field-prefix": "Prefix del camp", "search.configuration.filter-expression-modal.generated-filter.generator": "Generador", + "search.configuration.filter-expression-modal.key-value-filter.contains": "Conté", + "search.configuration.filter-expression-modal.key-value-filter.equal": "Igual a", + "search.configuration.filter-expression-modal.key-value-filter.field": "Camp", + "search.configuration.filter-expression-modal.key-value-filter.gte": "Més gran o igual a", + "search.configuration.filter-expression-modal.key-value-filter.lte": "Més petit o igual a", + "search.configuration.filter-expression-modal.key-value-filter.schema_id": "Esquema estructurat", + "search.configuration.filter-expression-modal.key-value-filters": "Filtres clau-valor", "search.configuration.filter-expression-modal.kind-filter.kind": "Tipus", "search.configuration.filter-expression-modal.label-filter.label": "Etiqueta", "search.configuration.filter-expression-modal.label-filter.labelset": "Conjunt d'etiquetes", diff --git a/libs/common/src/assets/i18n/en.json b/libs/common/src/assets/i18n/en.json index 79eb5f7b7b..808e9ef675 100644 --- a/libs/common/src/assets/i18n/en.json +++ b/libs/common/src/assets/i18n/en.json @@ -2026,6 +2026,9 @@ "search.configuration.filter-expression-modal.filter-type.field": "Field", "search.configuration.filter-expression-modal.filter-type.field_mimetype": "Field MIME type ", "search.configuration.filter-expression-modal.filter-type.generated": "Generator", + "search.configuration.filter-expression-modal.filter-type.key_value_contains": "Key-value (contains)", + "search.configuration.filter-expression-modal.filter-type.key_value_eq": "Key-value (exact value)", + "search.configuration.filter-expression-modal.filter-type.key_value_gte_lte": "Key-value (greater/less)", "search.configuration.filter-expression-modal.filter-type.keyword": "Keyword", "search.configuration.filter-expression-modal.filter-type.kind": "Paragraph kind", "search.configuration.filter-expression-modal.filter-type.label": "Label", @@ -2045,6 +2048,13 @@ "search.configuration.filter-expression-modal.generated-filter.data-augmentation": "Data augmentation", "search.configuration.filter-expression-modal.generated-filter.field-prefix": "Field prefix", "search.configuration.filter-expression-modal.generated-filter.generator": "Generator", + "search.configuration.filter-expression-modal.key-value-filter.contains": "Contains", + "search.configuration.filter-expression-modal.key-value-filter.equal": "Equal to", + "search.configuration.filter-expression-modal.key-value-filter.field": "Field", + "search.configuration.filter-expression-modal.key-value-filter.gte": "Greater than or equal to", + "search.configuration.filter-expression-modal.key-value-filter.lte": "Less than or equal to", + "search.configuration.filter-expression-modal.key-value-filter.schema_id": "Structured schema", + "search.configuration.filter-expression-modal.key-value-filters": "Key-value filters", "search.configuration.filter-expression-modal.kind-filter.kind": "Kind", "search.configuration.filter-expression-modal.label-filter.label": "Label", "search.configuration.filter-expression-modal.label-filter.labelset": "Label set", diff --git a/libs/common/src/assets/i18n/es.json b/libs/common/src/assets/i18n/es.json index fbcf773fc0..77149ce47d 100644 --- a/libs/common/src/assets/i18n/es.json +++ b/libs/common/src/assets/i18n/es.json @@ -2026,6 +2026,9 @@ "search.configuration.filter-expression-modal.filter-type.field": "Campo", "search.configuration.filter-expression-modal.filter-type.field_mimetype": "Tipo MIME del campo", "search.configuration.filter-expression-modal.filter-type.generated": "Generador", + "search.configuration.filter-expression-modal.filter-type.key_value_contains": "Clave-valor (contiene)", + "search.configuration.filter-expression-modal.filter-type.key_value_eq": "Clave-valor (valor exacto)", + "search.configuration.filter-expression-modal.filter-type.key_value_gte_lte": "Clave-valor (mayor/menor)", "search.configuration.filter-expression-modal.filter-type.keyword": "Palabra clave", "search.configuration.filter-expression-modal.filter-type.kind": "Tipo de párrafo", "search.configuration.filter-expression-modal.filter-type.label": "Etiqueta", @@ -2045,6 +2048,13 @@ "search.configuration.filter-expression-modal.generated-filter.data-augmentation": "Aumento de datos", "search.configuration.filter-expression-modal.generated-filter.field-prefix": "Prefijo de campo", "search.configuration.filter-expression-modal.generated-filter.generator": "Generador", + "search.configuration.filter-expression-modal.key-value-filter.contains": "Contiene", + "search.configuration.filter-expression-modal.key-value-filter.equal": "Igual a", + "search.configuration.filter-expression-modal.key-value-filter.field": "Campo", + "search.configuration.filter-expression-modal.key-value-filter.gte": "Mayor o igual a", + "search.configuration.filter-expression-modal.key-value-filter.lte": "Menor o igual a", + "search.configuration.filter-expression-modal.key-value-filter.schema_id": "Esquema estructurado", + "search.configuration.filter-expression-modal.key-value-filters": "Filtros clave-valor", "search.configuration.filter-expression-modal.kind-filter.kind": "Tipo", "search.configuration.filter-expression-modal.label-filter.label": "Etiqueta", "search.configuration.filter-expression-modal.label-filter.labelset": "Conjunto de etiquetas", diff --git a/libs/common/src/assets/i18n/fr.json b/libs/common/src/assets/i18n/fr.json index d737354592..27b480f332 100644 --- a/libs/common/src/assets/i18n/fr.json +++ b/libs/common/src/assets/i18n/fr.json @@ -2026,6 +2026,9 @@ "search.configuration.filter-expression-modal.filter-type.field": "Champ", "search.configuration.filter-expression-modal.filter-type.field_mimetype": "Type MIME du champ", "search.configuration.filter-expression-modal.filter-type.generated": "Générateur", + "search.configuration.filter-expression-modal.filter-type.key_value_contains": "Clé-valeur (contient)", + "search.configuration.filter-expression-modal.filter-type.key_value_eq": "Clé-valeur (valeur exact)", + "search.configuration.filter-expression-modal.filter-type.key_value_gte_lte": "Clé-valeur (supérieur/inférieur)", "search.configuration.filter-expression-modal.filter-type.keyword": "Mot-clé", "search.configuration.filter-expression-modal.filter-type.kind": "Type de paragraphe", "search.configuration.filter-expression-modal.filter-type.label": "Étiquette", @@ -2045,6 +2048,13 @@ "search.configuration.filter-expression-modal.generated-filter.data-augmentation": "Augmentation des données", "search.configuration.filter-expression-modal.generated-filter.field-prefix": "Préfixe de champ", "search.configuration.filter-expression-modal.generated-filter.generator": "Générateur", + "search.configuration.filter-expression-modal.key-value-filter.contains": "Contient", + "search.configuration.filter-expression-modal.key-value-filter.equal": "Égal à", + "search.configuration.filter-expression-modal.key-value-filter.field": "Champ", + "search.configuration.filter-expression-modal.key-value-filter.gte": "Supérieur ou égal à", + "search.configuration.filter-expression-modal.key-value-filter.lte": "Inférieur ou égal à", + "search.configuration.filter-expression-modal.key-value-filter.schema_id": "Schéma structuré", + "search.configuration.filter-expression-modal.key-value-filters": "Filtres clé-valeur", "search.configuration.filter-expression-modal.kind-filter.kind": "Type", "search.configuration.filter-expression-modal.label-filter.label": "Étiquette", "search.configuration.filter-expression-modal.label-filter.labelset": "Jeu d'étiquettes", diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html index 5c910ae948..8cd75c53f2 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html @@ -32,18 +32,25 @@ {{ 'search.configuration.filter-expression-modal.filters' | translate }} - @if ((target | async) === 'field') { + @let targetValue = target | async; + @if (targetValue === 'field') { @for (filter of fieldFilters; track filter) { {{ 'search.configuration.filter-expression-modal.filter-type.' + filter | translate }} } - } @else { + } @else if (targetValue === 'paragraph') { @for (filter of paragraphFilters; track filter) { {{ 'search.configuration.filter-expression-modal.filter-type.' + filter | translate }} } + } @else { + @for (filter of keyValueFilters; track filter) { + + {{ 'search.configuration.filter-expression-modal.filter-type.' + filter | translate }} + + } } } @@ -234,7 +241,71 @@ {{ 'search.configuration.filter-expression-modal.origin-filter.value' | translate }} } + @case ('key_value_eq') { + + @if (selectedSchemaKey) { + @let type = (schemaField | async)?.type; + @if (type === 'text' || type === 'date') { + + {{ 'search.configuration.filter-expression-modal.key-value-filter.equal' | translate }} + + } @else if (type === 'boolean') { + + True + False + + } @else if (type === 'float' || type === 'integer') { + + {{ 'search.configuration.filter-expression-modal.key-value-filter.equal' | translate }} + + } + } + } + @case ('key_value_gte_lte') { + + @if (selectedSchemaKey) { + + {{ 'search.configuration.filter-expression-modal.key-value-filter.gte' | translate }} + + + {{ 'search.configuration.filter-expression-modal.key-value-filter.lte' | translate }} + + } + } + @case ('key_value_contains') { + + @if (selectedSchemaKey) { + + {{ 'search.configuration.filter-expression-modal.key-value-filter.contains' | translate }} + + } + } } + + + @for (schema of kvSchemas | async; track schema.id) { + {{ schema.id }} + } + + @if (selectedSchema) { + + } + } diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts index 2c62bd63b2..d1d51addd9 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts @@ -12,11 +12,12 @@ import { } from '@guillotinaweb/pastanaga-angular'; import { TranslateModule } from '@ngx-translate/core'; import { LabelsService } from '@flaps/core'; -import { FIELD_TYPE, TypeParagraph } from '@nuclia/core'; +import { FIELD_TYPE, KVFieldType, TypeParagraph } from '@nuclia/core'; import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { NerService } from '../../../../entities'; -import { BehaviorSubject, defer, filter, map, merge, of, switchMap } from 'rxjs'; +import { BehaviorSubject, combineLatest, defer, filter, map, merge, of, switchMap, take, tap } from 'rxjs'; import { AnyFilterExpression, FilterTarget } from '../filter-expression-modal.component'; +import { KvSchemasService } from '../../../../knowledge-box-settings/kv-schemas/kv-schemas.service'; @Component({ imports: [ @@ -72,6 +73,13 @@ export class AddFilterModalComponent { paragraphFilters = ['label', 'kind']; + keyValueFilters = ['key_value_eq', 'key_value_gte_lte', 'key_value_contains']; + + allowedFieldTypes: { [key: string]: string[] } = { + key_value_eq: ['text', 'boolean', 'integer', 'float', 'date'], + key_value_gte_lte: ['integer', 'float'], + }; + prop = new FormControl('', { nonNullable: true, validators: [Validators.required] }); forms: { [key: string]: FormGroup } = { label: new FormGroup({ @@ -136,6 +144,22 @@ export class AddFilterModalComponent { field: new FormControl('', { nonNullable: true, validators: [Validators.required] }), value: new FormControl('', { nonNullable: true }), }), + key_value_eq: new FormGroup({ + schema_id: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + key: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + eq: new FormControl('', { validators: [Validators.required] }), + }), + key_value_gte_lte: new FormGroup({ + schema_id: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + key: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + gte: new FormControl('', { nonNullable: true }), + lte: new FormControl('', { nonNullable: true }), + }), + key_value_contains: new FormGroup({ + schema_id: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + key: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + contains: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + }), }; familyOptions = this.nerService.entities.pipe( @@ -157,6 +181,55 @@ export class AddFilterModalComponent { ), ); + kvSchemas = this.kvSchemaService.schemas$; + selectedProp$ = merge( + defer(() => of(this.prop.value)), + this.prop.valueChanges, + ); + selectedSchema$ = this.selectedProp$.pipe( + switchMap((prop) => + merge( + defer(() => of(this.selectedSchema)), + this.forms[prop].controls['schema_id'].valueChanges, + ), + ), + ); + selectedSchemaKey$ = this.selectedProp$.pipe( + switchMap((prop) => + merge( + defer(() => of(this.selectedSchemaKey)), + this.forms[prop].controls['key'].valueChanges, + ), + ), + ); + schemaKeyOptions = this.selectedSchema$.pipe( + filter((schema) => schema), + switchMap((schema) => + this.kvSchemas.pipe( + take(1), + map((schemas) => + (schemas.find((s) => s.id === schema)?.fields || []) + .filter((field) => { + if (this.prop.value === 'key_value_contains') { + return field.range || field.repeated; + } + return this.allowedFieldTypes[this.prop.value]?.includes(field.type) && !field.range && !field.repeated; + }) + .map( + (field) => new OptionModel({ id: field.key, value: field.key, label: `${field.key} (${field.type})` }), + ), + ), + ), + ), + ); + schemaField = combineLatest([this.selectedSchemaKey$, this.kvSchemas]).pipe( + map(([key, schemas]) => + (schemas.find((s) => s.id === this.selectedSchema)?.fields || []).find((field) => field.key === key), + ), + tap((field) => (this.schemaFieldType = field?.type)), + ); + schemaFieldType?: KVFieldType; + get selectedLabelset() { return this.forms['label'].controls['labelset'].value; } @@ -165,6 +238,13 @@ export class AddFilterModalComponent { return this.forms['entity'].controls['subtype'].value; } + get selectedSchema() { + return this.forms[this.prop.value].controls['schema_id'].value; + } + get selectedSchemaKey() { + return this.forms[this.prop.value].controls['key'].value; + } + get invalid() { return this.prop.invalid || this.forms[this.prop.value]?.invalid; } @@ -176,6 +256,7 @@ export class AddFilterModalComponent { >, private labelsService: LabelsService, private nerService: NerService, + private kvSchemaService: KvSchemasService, ) { this.target.next(this.modal.config.data?.target || 'field'); const expression = this.modal.config.data?.expression; @@ -187,6 +268,11 @@ export class AddFilterModalComponent { this.resourceSelection = expression.prop === 'resource' && !!expression.slug ? 'slug' : 'id'; this.prop.patchValue(expression.prop); this.forms[expression.prop].patchValue(expression); + + // Edge case: pastanaga inputs do not support boolean values + if ('eq' in expression && typeof expression.eq === 'boolean') { + this.forms['key_value_eq'].controls['eq'].patchValue(expression.eq ? 'true' : 'false'); + } } } } @@ -199,6 +285,14 @@ export class AddFilterModalComponent { this.forms['entity'].controls['value'].reset(); } + resetKey() { + this.forms[this.prop.value].controls['key'].reset(); + } + + resetValue(key: string) { + this.forms[this.prop.value].reset({ schema_id: this.selectedSchema, key }); + } + submit() { let expression: AnyFilterExpression; switch (this.prop.value) { @@ -213,7 +307,12 @@ export class AddFilterModalComponent { break; default: { const filterParams = Object.fromEntries( - Object.entries(this.forms[this.prop.value].value).filter(([, value]) => !!value), + Object.entries(this.forms[this.prop.value].value) + .filter(([, value]) => value !== '') + .map(([key, value]) => + // Edge case: boolean values should be converted to real booleans + key === 'eq' && this.schemaFieldType === 'boolean' ? [key, value === 'true'] : [key, value], + ), ) as any; expression = { prop: this.prop.value, diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html index 325aa2fc62..248440685e 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html @@ -78,6 +78,33 @@ } + @if ((hasKvSchemas | async) || filterExpression.key_value) { + + + + @if (filterExpression.key_value) { + + } + @if (!filterExpression.key_value) { +
+ + {{ 'search.configuration.filter-expression-modal.add-filter' | translate }} + +
+ } +
+
+
+ } diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.ts b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.ts index 92823cbe82..78f0a075b6 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.ts +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.ts @@ -16,24 +16,46 @@ import { } from '@guillotinaweb/pastanaga-angular'; import { TranslateModule } from '@ngx-translate/core'; import { InfoCardComponent, SisModalService } from '@nuclia/sistema'; -import { And, FieldFilterExpression, FilterExpression, Not, Or, ParagraphFilterExpression } from '@nuclia/core'; +import { + And, + FieldFilterExpression, + FilterExpression, + KeyValueContainsFilter, + KeyValueEqualFilter, + KeyValueFilterExpression, + KeyValueRangeFilter, + Not, + Or, + ParagraphFilterExpression, +} from '@nuclia/core'; import { AddFilterModalComponent } from './add-filter-modal/add-filter-modal.component'; import { filter } from 'rxjs'; import { FilterValueComponent } from './filter-value/filter-value.component'; +import { KvSchemasService } from '../../../knowledge-box-settings/kv-schemas/kv-schemas.service'; export type AnyFilterExpression = | And | Or | Not | FieldFilterExpression - | ParagraphFilterExpression; + | ParagraphFilterExpression + | KeyValueFilterExpressionWithProps; export type NonOperatorFilterExpression = Exclude< AnyFilterExpression, And | Or | Not >; -export type FilterTarget = 'field' | 'paragraph'; +export type FilterTarget = 'field' | 'paragraph' | 'key-value'; + +// Key-value filters do not have the prop property. We add fake props for consistency. +export type KeyValueFilterExpressionWithProps = + | And + | Or + | Not + | (KeyValueEqualFilter & { prop: 'key_value_eq' }) + | (KeyValueRangeFilter & { prop: 'key_value_gte_lte' }) + | (KeyValueContainsFilter & { prop: 'key_value_contains' }); @Component({ imports: [ @@ -61,6 +83,7 @@ export class FilterExpressionModalComponent { dataAugmentation = false; useKbData = true; help?: string; + hasKvSchemas = this.kvSchemasService.schemas$.pipe(filter((schemas) => schemas.length > 0)); @ViewChildren(AccordionItemComponent) accordionItems: AccordionItemComponent[] = []; @@ -70,10 +93,14 @@ export class FilterExpressionModalComponent { string >, private modalService: SisModalService, + private kvSchemasService: KvSchemasService, private cdr: ChangeDetectorRef, ) { try { this.filterExpression = JSON.parse(this.modal.config.data?.filterExpression || '{}'); + if (this.filterExpression.key_value) { + this.filterExpression.key_value = this.addFakeProps(this.filterExpression.key_value); + } } catch { // Invalid filter expression } @@ -83,12 +110,14 @@ export class FilterExpressionModalComponent { } get invalidExpession() { - return ( - (!this.filterExpression.field && !this.filterExpression.paragraph) || - (this.filterExpression.field && this.hasEmptyExpressions([this.filterExpression.field as AnyFilterExpression])) || - (this.filterExpression.paragraph && - this.hasEmptyExpressions([this.filterExpression.paragraph as AnyFilterExpression])) - ); + const fieldExpression = this.filterExpression.field; + const paragraphExpression = this.filterExpression.paragraph; + const keyValueExpression = this.filterExpression.key_value; + const hasEmptyExpressions = + (fieldExpression && this.hasEmptyExpressions([fieldExpression as AnyFilterExpression])) || + (paragraphExpression && this.hasEmptyExpressions([paragraphExpression as AnyFilterExpression])) || + (keyValueExpression && this.hasEmptyExpressions([keyValueExpression as AnyFilterExpression])); + return (!fieldExpression && !paragraphExpression && !keyValueExpression) || hasEmptyExpressions; } add(target: FilterTarget, parent?: AnyFilterExpression) { @@ -109,8 +138,10 @@ export class FilterExpressionModalComponent { } } else if (target === 'field') { this.filterExpression.field = result; - } else { + } else if (target === 'paragraph') { this.filterExpression.paragraph = result; + } else { + this.filterExpression.key_value = result; } this.updateMainOperator(); this.cdr.markForCheck(); @@ -158,8 +189,10 @@ export class FilterExpressionModalComponent { } } else if (target === 'field') { this.filterExpression.field = undefined; - } else { + } else if (target === 'paragraph') { this.filterExpression.paragraph = undefined; + } else { + this.filterExpression.key_value = undefined; } this.updateMainOperator(); this.cdr.markForCheck(); @@ -167,6 +200,11 @@ export class FilterExpressionModalComponent { } submit() { + if (this.filterExpression.key_value) { + this.filterExpression.key_value = this.removeFakeProps( + this.filterExpression.key_value as KeyValueFilterExpressionWithProps, + ); + } this.modal.close(JSON.stringify(this.filterExpression, undefined, 2)); } @@ -185,8 +223,10 @@ export class FilterExpressionModalComponent { } } else if (target === 'field') { this.filterExpression.field = expression as FieldFilterExpression; - } else { + } else if (target === 'paragraph') { this.filterExpression.paragraph = expression as ParagraphFilterExpression; + } else { + this.filterExpression.key_value = expression as KeyValueFilterExpressionWithProps; } } @@ -224,4 +264,30 @@ export class FilterExpressionModalComponent { } }); } + + private addFakeProps(expression: KeyValueFilterExpression): KeyValueFilterExpressionWithProps { + if ('and' in expression) { + return { and: expression.and.map((item) => this.addFakeProps(item)) }; + } else if ('or' in expression) { + return { or: expression.or.map((item) => this.addFakeProps(item)) }; + } else if ('not' in expression) { + return { not: this.addFakeProps(expression.not) }; + } + return { + ...expression, + prop: 'eq' in expression ? 'key_value_eq' : 'contains' in expression ? 'key_value_contains' : 'key_value_gte_lte', + }; + } + + private removeFakeProps(expression: KeyValueFilterExpressionWithProps): KeyValueFilterExpression { + if ('and' in expression) { + return { and: expression.and.map((item) => this.removeFakeProps(item)) }; + } else if ('or' in expression) { + return { or: expression.or.map((item) => this.removeFakeProps(item)) }; + } else if ('not' in expression) { + return { not: this.removeFakeProps(expression.not) }; + } + const { prop, ...rest } = expression; + return rest; + } } diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-value/filter-value.component.html b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-value/filter-value.component.html index e2a1248e08..ee0ffa6e65 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-value/filter-value.component.html +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-value/filter-value.component.html @@ -86,7 +86,9 @@ @case ('origin_path') { @if (filter.prefix) { - {{ ('search.configuration.filter-expression-modal.origin-filter.prefix' | translate) + ': ' + filter.prefix }} + {{ + ('search.configuration.filter-expression-modal.origin-filter.prefix' | translate) + ': ' + filter.prefix + }} } } @@ -106,6 +108,37 @@ } } + @case ('key_value_eq') { + + {{ filter.key }} + = {{ filter.eq }} + + } + @case ('key_value_gte_lte') { + @let hasGte = filter.gte !== undefined; + @if (hasGte) { + + {{ filter.key }} + ≥ {{ filter.gte }} + + } + @let hasLte = filter.lte !== undefined; + @if (hasLte) { + + {{ filter.key }} + ≤ {{ filter.lte }} + + } + } + @case ('key_value_contains') { + + {{ filter.key }} + + {{ ('search.configuration.filter-expression-modal.key-value-filter.contains' | translate).toLowerCase() }} + + {{ filter.contains }} + + } @default { {{ filter.prop }} } diff --git a/libs/sdk-core/src/lib/db/search/search.models.ts b/libs/sdk-core/src/lib/db/search/search.models.ts index 17aea4eec9..9d8c791163 100644 --- a/libs/sdk-core/src/lib/db/search/search.models.ts +++ b/libs/sdk-core/src/lib/db/search/search.models.ts @@ -259,10 +259,32 @@ export interface KindFilter { kind: TypeParagraph; } +interface KeyValueFilter { + schema_id: string; + key: string; +} + +export interface KeyValueEqualFilter extends KeyValueFilter { + eq?: boolean | number | string; +} + +export interface KeyValueRangeFilter extends KeyValueFilter { + gte?: number | string; + lte?: number | string; +} + +export interface KeyValueContainsFilter extends KeyValueFilter { + eq?: boolean | number | string; + contains?: number | string; +} + +export interface KeyValueEqualFilter {} + export interface FilterExpression { field?: FieldFilterExpression; paragraph?: ParagraphFilterExpression; operator?: 'and' | 'or'; + key_value?: KeyValueFilterExpression; } export type FieldFilterExpression = @@ -310,6 +332,14 @@ export type ResourceFilterExpression = | OriginCollaboratorFilter | StatusFilter; +export type KeyValueFilterExpression = + | And + | Or + | Not + | KeyValueEqualFilter + | KeyValueRangeFilter + | KeyValueContainsFilter; + export enum SHORT_FIELD_TYPE { text = 't', file = 'f', From fc4150aeca50e03b1788b8fbf640e3d5ddf0ff3e Mon Sep 17 00:00:00 2001 From: operramon Date: Mon, 13 Jul 2026 12:28:41 +0200 Subject: [PATCH 2/2] Multiple improvements --- libs/common/common.babel | 23 +++++++++++ libs/common/src/assets/i18n/ca.json | 3 +- libs/common/src/assets/i18n/en.json | 3 +- libs/common/src/assets/i18n/es.json | 3 +- libs/common/src/assets/i18n/fr.json | 3 +- .../kv-schemas/kv-schemas.config.ts | 3 +- .../kv-schemas/kv-schemas.service.ts | 17 +++++--- .../schema-form/schema-form.component.html | 3 +- .../schema-form/schema-form.component.ts | 9 +++- .../edit-resource.component.html | 6 ++- .../add-filter-modal.component.html | 39 +++++++++++++----- .../add-filter-modal.component.ts | 2 +- .../filter-expression-modal.component.html | 41 +++++++++++-------- .../filter-expression-modal.component.ts | 13 ++++-- .../filter-value/filter-value.component.html | 28 +++++++++---- .../task-settings.component.html | 12 +++--- .../task-forms/ask/ask.component.html | 30 +++++++------- .../task-forms/ask/ask.component.ts | 8 +++- 18 files changed, 172 insertions(+), 74 deletions(-) diff --git a/libs/common/common.babel b/libs/common/common.babel index 64d24b338e..b6ef183f0c 100644 --- a/libs/common/common.babel +++ b/libs/common/common.babel @@ -24921,6 +24921,29 @@ + + invalid-id + + + + + ca-ES + false + + + en-US + false + + + es-ES + false + + + fr-FR + false + + + no-fields diff --git a/libs/common/src/assets/i18n/ca.json b/libs/common/src/assets/i18n/ca.json index 28b60342c1..70b90c9313 100644 --- a/libs/common/src/assets/i18n/ca.json +++ b/libs/common/src/assets/i18n/ca.json @@ -1040,6 +1040,7 @@ "kb.kv-schemas.form.field-type": "Tipus", "kb.kv-schemas.form.fields": "Camps", "kb.kv-schemas.form.fields-subtitle": "Defineix els camps inclosos en aquest esquema. Cada camp necessita una clau, un tipus i una descripció.", + "kb.kv-schemas.form.invalid-id": "El nom del camp només ha de contenir lletres, xifres i _", "kb.kv-schemas.form.no-fields": "Afegeix com a mínim un camp.", "kb.kv-schemas.form.schema-description": "Descripció", "kb.kv-schemas.form.schema-name": "Nom de l'esquema", @@ -2065,7 +2066,7 @@ "search.configuration.filter-expression-modal.mime-filter.subtype": "Subtipus", "search.configuration.filter-expression-modal.mime-filter.subtype-help": "exemple: plain, pdf, jpg", "search.configuration.filter-expression-modal.mime-filter.type": "Tipus principal", - "search.configuration.filter-expression-modal.operator": "Lògica entre filtres de camp i paràgraf", + "search.configuration.filter-expression-modal.operator": "Lògica entre filtres de camp, filtres de paràgraf i filtres de valor-clau", "search.configuration.filter-expression-modal.origin-filter.collaborator": "Col·laborador", "search.configuration.filter-expression-modal.origin-filter.field": "Camp", "search.configuration.filter-expression-modal.origin-filter.prefix": "Prefix", diff --git a/libs/common/src/assets/i18n/en.json b/libs/common/src/assets/i18n/en.json index 808e9ef675..574c8fb0a1 100644 --- a/libs/common/src/assets/i18n/en.json +++ b/libs/common/src/assets/i18n/en.json @@ -1040,6 +1040,7 @@ "kb.kv-schemas.form.field-type": "Type", "kb.kv-schemas.form.fields": "Fields", "kb.kv-schemas.form.fields-subtitle": "Define the fields included in this schema. Each field needs a key, type, and description.", + "kb.kv-schemas.form.invalid-id": "The field name must only contain letters, figures and _", "kb.kv-schemas.form.no-fields": "Add at least one field.", "kb.kv-schemas.form.schema-description": "Description", "kb.kv-schemas.form.schema-name": "Schema name", @@ -2065,7 +2066,7 @@ "search.configuration.filter-expression-modal.mime-filter.subtype": "Subtype", "search.configuration.filter-expression-modal.mime-filter.subtype-help": "e.g: plain, pdf, jpg", "search.configuration.filter-expression-modal.mime-filter.type": "Main type", - "search.configuration.filter-expression-modal.operator": "Logic between field and paragraph filters", + "search.configuration.filter-expression-modal.operator": "Logic between field filters, paragraph filters and key-value filters", "search.configuration.filter-expression-modal.origin-filter.collaborator": "Collaborator", "search.configuration.filter-expression-modal.origin-filter.field": "Field", "search.configuration.filter-expression-modal.origin-filter.prefix": "Prefix", diff --git a/libs/common/src/assets/i18n/es.json b/libs/common/src/assets/i18n/es.json index 77149ce47d..bad998e47b 100644 --- a/libs/common/src/assets/i18n/es.json +++ b/libs/common/src/assets/i18n/es.json @@ -1040,6 +1040,7 @@ "kb.kv-schemas.form.field-type": "Tipo", "kb.kv-schemas.form.fields": "Campos", "kb.kv-schemas.form.fields-subtitle": "Defina los campos incluidos en este esquema. Cada campo necesita una clave, un tipo y una descripción.", + "kb.kv-schemas.form.invalid-id": "El nombre del campo solo debe contener letras, cifras y _", "kb.kv-schemas.form.no-fields": "Agregue al menos un campo.", "kb.kv-schemas.form.schema-description": "Descripción", "kb.kv-schemas.form.schema-name": "Nombre del esquema", @@ -2065,7 +2066,7 @@ "search.configuration.filter-expression-modal.mime-filter.subtype": "Subtipo", "search.configuration.filter-expression-modal.mime-filter.subtype-help": "ejemplo.: plain, pdf, jpg", "search.configuration.filter-expression-modal.mime-filter.type": "Tipo principal", - "search.configuration.filter-expression-modal.operator": "Lógica entre filtros de campo y de párrafo", + "search.configuration.filter-expression-modal.operator": "Lógica entre filtros de campo, filtros de párrafo y filtros clave-valor", "search.configuration.filter-expression-modal.origin-filter.collaborator": "Colaborador", "search.configuration.filter-expression-modal.origin-filter.field": "Campo", "search.configuration.filter-expression-modal.origin-filter.prefix": "Prefijo", diff --git a/libs/common/src/assets/i18n/fr.json b/libs/common/src/assets/i18n/fr.json index 27b480f332..31d3d98ccf 100644 --- a/libs/common/src/assets/i18n/fr.json +++ b/libs/common/src/assets/i18n/fr.json @@ -1040,6 +1040,7 @@ "kb.kv-schemas.form.field-type": "Taper", "kb.kv-schemas.form.fields": "Des champs", "kb.kv-schemas.form.fields-subtitle": "Définissez les champs inclus dans ce schéma. Chaque champ doit comporter une clé, un type et une description.", + "kb.kv-schemas.form.invalid-id": "Le nom du champ ne doit contenir que des lettres, des chiffres et des _", "kb.kv-schemas.form.no-fields": "Ajoutez au moins un champ.", "kb.kv-schemas.form.schema-description": "Description", "kb.kv-schemas.form.schema-name": "Nom du schema", @@ -2065,7 +2066,7 @@ "search.configuration.filter-expression-modal.mime-filter.subtype": "Sous-type", "search.configuration.filter-expression-modal.mime-filter.subtype-help": "exemple: plain, pdf, jpg", "search.configuration.filter-expression-modal.mime-filter.type": "Type principal", - "search.configuration.filter-expression-modal.operator": "Logique entre les filtres de champ et de paragraphe", + "search.configuration.filter-expression-modal.operator": "Logique entre les filtres de champ, les filtres de paragraphe et les filtres clé-valeur", "search.configuration.filter-expression-modal.origin-filter.collaborator": "Collaborateur", "search.configuration.filter-expression-modal.origin-filter.field": "Champ", "search.configuration.filter-expression-modal.origin-filter.prefix": "Préfixe", diff --git a/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.config.ts b/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.config.ts index e089573b9f..5daaeb0f25 100644 --- a/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.config.ts +++ b/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.config.ts @@ -17,7 +17,8 @@ export const KV_SCHEMA_FORM_CONFIG = { rangeEnabledTypes: ['integer', 'float', 'date'] as const satisfies readonly KVFieldType[], repeatedEnabledTypes: ['text'] as const satisfies readonly KVFieldType[], defaultFieldType: 'text' as const satisfies KVFieldType, - identifierPattern: /^[^/.]{1,64}$/, + identifierPattern: /^[0-9a-zA-Z_]{1,64}$/, + fieldIdentifierPattern: /^[^/.]{1,64}$/, } as const; export type KvSchemaFormColumn = (typeof KV_SCHEMA_FORM_CONFIG.columns)[number]; diff --git a/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.service.ts b/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.service.ts index 9fc45eaffe..9c097d526b 100644 --- a/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.service.ts +++ b/libs/common/src/lib/knowledge-box-settings/kv-schemas/kv-schemas.service.ts @@ -13,11 +13,18 @@ export class KvSchemasService { private _currentKb = this.sdk.currentKb; private _initialData = this._currentKb.pipe( switchMap((kb) => - kb.getKVSchemas().pipe( - catchError(() => { - this.toaster.error('kb.kv-schemas.error.load'); - return of({ schemas: {} }); - }), + this.sdk.isArag.pipe( + take(1), + switchMap((isArag) => + isArag + ? of({ schemas: {} }) + : kb.getKVSchemas().pipe( + catchError(() => { + this.toaster.error('kb.kv-schemas.error.load'); + return of({ schemas: {} }); + }), + ), + ), ), ), ); diff --git a/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.html b/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.html index 3e46ffba99..205f9e0330 100644 --- a/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.html +++ b/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.html @@ -14,7 +14,8 @@

{{ 'kb.kv-schemas.form.title' | translate }}

+ [readonly]="isEditMode()" + [errorMessages]="errorMessages"> {{ 'kb.kv-schemas.form.schema-name' | translate }} diff --git a/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.ts b/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.ts index 66037b4f39..88a98c5d6e 100644 --- a/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.ts +++ b/libs/common/src/lib/knowledge-box-settings/kv-schemas/schema-form/schema-form.component.ts @@ -48,6 +48,10 @@ export class SchemaFormComponent { isEditMode = computed(() => !!this.schema()); + errorMessages = { + pattern: 'kb.kv-schemas.form.invalid-id', + }; + constructor() { effect(() => { const s = this.schema(); @@ -79,7 +83,10 @@ export class SchemaFormComponent { buildFieldGroup(field?: KVSchemaField): FormGroup { return new FormGroup({ - key: new FormControl(field?.key ?? '', [Validators.required, Validators.pattern(this.config.identifierPattern)]), + key: new FormControl(field?.key ?? '', [ + Validators.required, + Validators.pattern(this.config.fieldIdentifierPattern), + ]), type: new FormControl(field?.type ?? this.config.defaultFieldType, Validators.required), description: new FormControl(field?.description ?? ''), required: new FormControl(field?.required ?? true), diff --git a/libs/common/src/lib/resources/edit-resource/edit-resource.component.html b/libs/common/src/lib/resources/edit-resource/edit-resource.component.html index 1da53fb002..77b221b977 100644 --- a/libs/common/src/lib/resources/edit-resource/edit-resource.component.html +++ b/libs/common/src/lib/resources/edit-resource/edit-resource.component.html @@ -141,7 +141,11 @@ - {{ 'resource.field-' + field.field_type | translate }} + @if (field.field_type === 'key_value') { + {{ field.field_id }} + } @else { + {{ 'resource.field-' + field.field_type | translate }} + } @if (field.hasError) { diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html index 8cd75c53f2..411b336556 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.html @@ -245,7 +245,7 @@ @if (selectedSchemaKey) { @let type = (schemaField | async)?.type; - @if (type === 'text' || type === 'date') { + @if (type === 'text') { {{ 'search.configuration.filter-expression-modal.key-value-filter.equal' | translate }} @@ -262,22 +262,39 @@ formControlName="eq"> {{ 'search.configuration.filter-expression-modal.key-value-filter.equal' | translate }} + } @else if (type === 'date') { + } } } @case ('key_value_gte_lte') { @if (selectedSchemaKey) { - - {{ 'search.configuration.filter-expression-modal.key-value-filter.gte' | translate }} - - - {{ 'search.configuration.filter-expression-modal.key-value-filter.lte' | translate }} - + @let type = (schemaField | async)?.type; + @if (type === 'float' || type === 'integer') { + + {{ 'search.configuration.filter-expression-modal.key-value-filter.gte' | translate }} + + + {{ 'search.configuration.filter-expression-modal.key-value-filter.lte' | translate }} + + } @else if (type === 'date') { + + + } } } @case ('key_value_contains') { diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts index d1d51addd9..254779e019 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/add-filter-modal/add-filter-modal.component.ts @@ -77,7 +77,7 @@ export class AddFilterModalComponent { allowedFieldTypes: { [key: string]: string[] } = { key_value_eq: ['text', 'boolean', 'integer', 'float', 'date'], - key_value_gte_lte: ['integer', 'float'], + key_value_gte_lte: ['integer', 'float', 'date'], }; prop = new FormControl('', { nonNullable: true, validators: [Validators.required] }); diff --git a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html index 248440685e..2ad98b34c0 100644 --- a/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html +++ b/libs/common/src/lib/search-widget/search-configuration/filter-expression-modal/filter-expression-modal.component.html @@ -35,22 +35,8 @@ @if (!dataAugmentation) { - @if (filterExpression.field && filterExpression.paragraph) { -
- - AND - OR - - - - - {{ 'search.configuration.filter-expression-modal.operator' | translate }} - - -
+ @if (showFilterLogic) { + } } - @if ((hasKvSchemas | async) || filterExpression.key_value) { + @if (((hasKvSchemas | async) || filterExpression.key_value) && !dataAugmentation) { + @if (showFilterLogic) { + + } + +
+ + AND + OR + + + + + {{ 'search.configuration.filter-expression-modal.operator' | translate }} + + +
+
+ } @case ('key_value_gte_lte') { - @let hasGte = filter.gte !== undefined; - @if (hasGte) { + @if (filter.gte !== undefined) { - {{ filter.key }} - ≥ {{ filter.gte }} + {{ filter.key }} ≥ + @if (typeof filter.gte === 'string') { + + } @else { + {{ filter.gte }} + } } - @let hasLte = filter.lte !== undefined; - @if (hasLte) { + @if (filter.lte !== undefined) { - {{ filter.key }} - ≤ {{ filter.lte }} + {{ filter.key }} ≤ + @if (typeof filter.lte === 'string') { + + } @else { + {{ filter.lte }} + } } } diff --git a/libs/common/src/lib/tasks-automation/task-details/task-settings/task-settings.component.html b/libs/common/src/lib/tasks-automation/task-details/task-settings/task-settings.component.html index ce7bcc06ca..cdf11f0e8d 100644 --- a/libs/common/src/lib/tasks-automation/task-details/task-settings/task-settings.component.html +++ b/libs/common/src/lib/tasks-automation/task-details/task-settings/task-settings.component.html @@ -128,11 +128,13 @@
{{ askOperation.user_prompt || askOperation.question }}
}
-
-
- {{ 'tasks-automation.form.field-name.label' | translate }} -
{{ askOperation.destination }}
-
+ @if (!askOperation.store_as_key_value) { +
+
+ {{ 'tasks-automation.form.field-name.label' | translate }} +
{{ askOperation.destination }}
+
+ } } diff --git a/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.html b/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.html index 9b51b51954..fbcaee09fb 100644 --- a/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.html +++ b/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.html @@ -100,20 +100,22 @@ - -
- {{ 'tasks-automation.generator.field-name.info' | translate }} -
- - {{ 'tasks-automation.form.field-name.label' | translate }} - + @if (!this.storeAsKeyValue) { + +
+ {{ 'tasks-automation.generator.field-name.info' | translate }} +
+ + {{ 'tasks-automation.form.field-name.label' | translate }} + +
-
- + + }
diff --git a/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.ts b/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.ts index 0c9bec7f98..fa68fd5862 100644 --- a/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.ts +++ b/libs/common/src/lib/tasks-automation/task-forms/ask/ask.component.ts @@ -15,7 +15,7 @@ import { KVSchema, KVSchemaField, TaskApplyTo, TaskName } from '@nuclia/core'; import { filter, map, take } from 'rxjs'; import { KvSchemasService } from '../../../knowledge-box-settings/kv-schemas/kv-schemas.service'; import { JSONSchema4, JSONSchema4TypeName } from 'json-schema'; -import { NavigationService } from '@flaps/core'; +import { NavigationService, STFUtils } from '@flaps/core'; import { RouterModule } from '@angular/router'; @Component({ @@ -110,6 +110,12 @@ export class AskComponent extends TaskRouteDirective { this.askForm.controls.fieldType.valueChanges.subscribe((fieldType) => { this.askForm.controls.kv_schema_id.setValidators(fieldType === 'keyValue' ? Validators.required : []); this.askForm.controls.kv_schema_id.updateValueAndValidity(); + + // When generating key-value fields, the backend ignores the destination field, but a value must still be provided + // therefore, we hide the destination from the user and generate a random value + if (fieldType === 'keyValue' && !this.askForm.controls.destination.value) { + this.askForm.controls.destination.patchValue(`field_${STFUtils.generateRandomSlugSuffix()}`); + } }); this.askOperation