@praxisui/dynamic-form 10.0.0-rc.31 → 10.0.0-rc.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/component-registry.json +590 -138
- package/fesm2022/praxisui-dynamic-form.mjs +786 -48
- package/package.json +8 -8
- package/types/praxisui-dynamic-form.d.ts +63 -2
|
@@ -20,10 +20,10 @@ import * as i9 from '@angular/material/snack-bar';
|
|
|
20
20
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
21
21
|
import * as i19 from '@angular/material/badge';
|
|
22
22
|
import { MatBadgeModule } from '@angular/material/badge';
|
|
23
|
-
import { map, Subscription, merge, of, timer, NEVER, EMPTY, defer, TimeoutError, Subject, firstValueFrom, BehaviorSubject, throwError, combineLatest, Observable, race, from, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs';
|
|
23
|
+
import { map, Subscription, merge, of, timer, NEVER, EMPTY, defer, TimeoutError, Subject, firstValueFrom, BehaviorSubject, throwError, combineLatest, Observable, race, from, debounceTime as debounceTime$1, takeUntil as takeUntil$1, defaultIfEmpty, switchMap as switchMap$1, catchError as catchError$1, take as take$1, finalize as finalize$1 } from 'rxjs';
|
|
24
24
|
import { filter, map as map$1, switchMap, take, timeout, throwIfEmpty, tap, catchError, ignoreElements, finalize, takeUntil, skip, debounceTime, startWith } from 'rxjs/operators';
|
|
25
25
|
import * as i1 from '@praxisui/core';
|
|
26
|
-
import { serializeEntityLookupValueForPayload, PraxisI18nService, PraxisIconButtonComponent, PraxisIconDirective, providePraxisI18nConfig, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, migrateFormLayoutRule, PraxisJsonLogicService, normalizeFormLayoutItems, deepMerge, createDefaultFormConfig, normalizeFormConfig as normalizeFormConfig$1, ensureIds, createEmptyRichContentDocument, formIdentityIssues, resolveHidden, resolveOrder, resolveSpan, projectGroupedCommandPartialRows, isPraxisRuntimeGlobalActionEffect, ASYNC_CONFIG_STORAGE, withFormConfigSections, createFieldLayoutItem, getFormLayoutFieldNames, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, ResourceDiscoveryService, GlobalActionService, SCHEMA_METADATA_CLIENT_OPTIONS, ResourceQuickConnectComponent, composeHeadersWithVersion, buildApiUrl, readStrongResourceEtag, ApiEndpoint, mapFieldDefinitionsToMetadata, reconcileFormConfig, applyLocalFormCustomizations, materializeFormLayoutFromMetadata, syncWithServerMetadata, PRAXIS_LOADING_CTX, evaluateFieldAccess, normalizeControlTypeKey, normalizePraxisEffectPolicy, buildPraxisEffectDistinctKey, getFormColumnFieldNames, resolveGroupedCommandPartialRowSpans, resolveOffset, buildSchemaIdStorageKeySegment, buildSchemaId, fetchWithETag, resolveControlTypeAlias, getTextTransformer, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, DEFAULT_SCHEMA_REQUEST_TIMEOUT_MS, normalizeReactiveDeterminations, CONNECTION_STORAGE, API_URL, PRAXIS_LOADING_RENDERER, FORM_HOOKS_PRESETS, EmptyStateCardComponent, isValidFormConfig, ComponentMetadataRegistry, FieldControlType, isRequiredGlobalActionPayloadMissing, GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, SURFACE_OPEN_I18N_NAMESPACE, getGlobalActionUiSchema, getRequiredGlobalActionPayloadKeys, hasMeaningfulGlobalActionPayloadValue, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, IconPickerService, validateGlobalActionRefs, normalizeFormMetadata, DynamicFormService, projectComponentAuthoringManifest } from '@praxisui/core';
|
|
26
|
+
import { serializeEntityLookupValueForPayload, PraxisI18nService, PraxisIconButtonComponent, PraxisIconDirective, providePraxisI18nConfig, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, migrateFormLayoutRule, PraxisJsonLogicService, normalizeFormLayoutItems, deepMerge, createDefaultFormConfig, normalizeFormConfig as normalizeFormConfig$1, ensureIds, createEmptyRichContentDocument, formIdentityIssues, resolveHidden, resolveOrder, resolveSpan, projectGroupedCommandPartialRows, isPraxisRuntimeGlobalActionEffect, ASYNC_CONFIG_STORAGE, withFormConfigSections, createFieldLayoutItem, getFormLayoutFieldNames, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, ResourceDiscoveryService, GlobalActionService, SCHEMA_METADATA_CLIENT_OPTIONS, ResourceQuickConnectComponent, composeHeadersWithVersion, buildApiUrl, readStrongResourceEtag, ApiEndpoint, mapFieldDefinitionsToMetadata, reconcileFormConfig, applyLocalFormCustomizations, materializeFormLayoutFromMetadata, syncWithServerMetadata, PRAXIS_LOADING_CTX, evaluateFieldAccess, normalizeControlTypeKey, normalizePraxisEffectPolicy, buildPraxisEffectDistinctKey, getFormColumnFieldNames, resolveGroupedCommandPartialRowSpans, resolveOffset, buildSchemaIdStorageKeySegment, buildSchemaId, fetchWithETag, resolveControlTypeAlias, getTextTransformer, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, DEFAULT_SCHEMA_REQUEST_TIMEOUT_MS, normalizeReactiveDeterminations, CONNECTION_STORAGE, API_URL, PRAXIS_LOADING_RENDERER, FORM_HOOKS_PRESETS, EmptyStateCardComponent, isValidFormConfig, ComponentMetadataRegistry, FieldControlType, isRequiredGlobalActionPayloadMissing, GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, SURFACE_OPEN_I18N_NAMESPACE, getGlobalActionUiSchema, getRequiredGlobalActionPayloadKeys, hasMeaningfulGlobalActionPayloadValue, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, IconPickerService, validateGlobalActionRefs, normalizeFormMetadata, GenericCrudService, DynamicFormService, projectComponentAuthoringManifest } from '@praxisui/core';
|
|
27
27
|
import * as i1$1 from '@praxisui/dynamic-fields';
|
|
28
28
|
import { getControlTypeCatalog, SelectOptionRegistry, ConfirmDialogComponent, DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
|
|
29
29
|
import { BaseAiAdapter, AgenticAuthoringTurnClientService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, createPraxisAssistantViewportLayout, PraxisAiAssistantShellComponent } from '@praxisui/ai';
|
|
@@ -12363,6 +12363,9 @@ class PraxisDynamicForm {
|
|
|
12363
12363
|
component: ResourceQuickConnectComponent,
|
|
12364
12364
|
inputs: {
|
|
12365
12365
|
resourcePath: this.resourcePath,
|
|
12366
|
+
intent: 'crud',
|
|
12367
|
+
endpointKey: this.apiEndpointKey ?? undefined,
|
|
12368
|
+
apiUrlEntry: this.apiUrlEntry ?? undefined,
|
|
12366
12369
|
},
|
|
12367
12370
|
},
|
|
12368
12371
|
});
|
|
@@ -13474,6 +13477,16 @@ class PraxisDynamicForm {
|
|
|
13474
13477
|
if (this.hasRenderableConfig(hostConfig)) {
|
|
13475
13478
|
await this.reconcileLoadedConfigWithServerSchema(false);
|
|
13476
13479
|
}
|
|
13480
|
+
else if (localConfig && this.hasRenderableConfig(localConfig)) {
|
|
13481
|
+
const hostTitle = hostConfig.title;
|
|
13482
|
+
this.config = normalizeFormConfig$1({
|
|
13483
|
+
...localConfig,
|
|
13484
|
+
...(hostTitle ? { title: hostTitle } : {}),
|
|
13485
|
+
...(hostConfig.actions ? { actions: hostConfig.actions } : {}),
|
|
13486
|
+
...(hostConfig.helpPresentation ? { helpPresentation: hostConfig.helpPresentation } : {}),
|
|
13487
|
+
});
|
|
13488
|
+
await this.reconcileLoadedConfigWithServerSchema();
|
|
13489
|
+
}
|
|
13477
13490
|
else {
|
|
13478
13491
|
await this.createDefaultConfig();
|
|
13479
13492
|
}
|
|
@@ -21340,34 +21353,125 @@ class PraxisDynamicForm {
|
|
|
21340
21353
|
? new MemoryCacheAdapter()
|
|
21341
21354
|
: new LocalStorageCacheAdapter({ ttlMs: 30 * 60 * 1000, maxEntries: 200 });
|
|
21342
21355
|
const client = new SchemaMetadataClient(adapter, this.schemaTransportOptions);
|
|
21343
|
-
|
|
21356
|
+
let discoveryAttempted = false;
|
|
21357
|
+
const loadDiscoverySchema = async (reason) => {
|
|
21358
|
+
discoveryAttempted = true;
|
|
21344
21359
|
if (this.isDestroyed)
|
|
21345
21360
|
throw new PraxisDynamicFormDestroyedError();
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21349
|
-
|
|
21350
|
-
|
|
21351
|
-
|
|
21361
|
+
const rp = String(this.resourcePath || '').trim();
|
|
21362
|
+
const discoveryOptions = {
|
|
21363
|
+
...(this.apiEndpointKey ? { endpointKey: this.apiEndpointKey } : {}),
|
|
21364
|
+
...(this.apiUrlEntry ? { apiUrlEntry: this.apiUrlEntry } : {}),
|
|
21365
|
+
};
|
|
21366
|
+
const canLoadViaDiscovery = !!rp &&
|
|
21367
|
+
typeof this.resourceDiscovery?.getResourceSchemaFields === 'function' &&
|
|
21368
|
+
(typeof this.resourceDiscovery.canResolveSchemas !== 'function' ||
|
|
21369
|
+
this.resourceDiscovery.canResolveSchemas(discoveryOptions));
|
|
21370
|
+
if (!canLoadViaDiscovery) {
|
|
21352
21371
|
throw reason instanceof Error
|
|
21353
21372
|
? reason
|
|
21354
21373
|
: new Error('Schema metadata unavailable and legacy crud.getSchema() not found.');
|
|
21355
21374
|
}
|
|
21356
|
-
const
|
|
21375
|
+
const inferredFilter = /(^|[-_])filter($|[-_])/i.test(this.formId || '');
|
|
21376
|
+
const useFilterSchema = this.schemaSource === 'filter' || inferredFilter;
|
|
21377
|
+
const hasDedicatedOptions = !!(discoveryOptions.endpointKey || discoveryOptions.apiUrlEntry);
|
|
21378
|
+
const discoveryCall$ = useFilterSchema && typeof this.resourceDiscovery.getResourceFilterSchemaFields === 'function'
|
|
21379
|
+
? (hasDedicatedOptions
|
|
21380
|
+
? this.resourceDiscovery.getResourceFilterSchemaFields(rp, discoveryOptions)
|
|
21381
|
+
: this.resourceDiscovery.getResourceFilterSchemaFields(rp))
|
|
21382
|
+
: (hasDedicatedOptions
|
|
21383
|
+
? this.resourceDiscovery.getResourceSchemaFields(rp, discoveryOptions)
|
|
21384
|
+
: this.resourceDiscovery.getResourceSchemaFields(rp));
|
|
21357
21385
|
controller.signal.throwIfAborted();
|
|
21358
21386
|
const cancelled$ = new Observable(subscriber => {
|
|
21359
21387
|
const abort = () => subscriber.error(controller.signal.reason ?? new DOMException('Schema read cancelled.', 'AbortError'));
|
|
21360
21388
|
controller.signal.addEventListener('abort', abort, { once: true });
|
|
21361
21389
|
return () => controller.signal.removeEventListener('abort', abort);
|
|
21362
21390
|
});
|
|
21363
|
-
let
|
|
21391
|
+
let discoveryRead$ = race(from(discoveryCall$), cancelled$).pipe(take(1));
|
|
21364
21392
|
const timeoutMs = this.schemaTransportOptions.requestTimeoutMs ?? DEFAULT_SCHEMA_REQUEST_TIMEOUT_MS;
|
|
21365
21393
|
if (timeoutMs > 0)
|
|
21366
|
-
|
|
21367
|
-
const
|
|
21394
|
+
discoveryRead$ = discoveryRead$.pipe(timeout(timeoutMs));
|
|
21395
|
+
const discoveryDefs = await firstValueFrom(discoveryRead$);
|
|
21396
|
+
controller.signal.throwIfAborted();
|
|
21397
|
+
if (this.isDestroyed)
|
|
21398
|
+
throw new PraxisDynamicFormDestroyedError();
|
|
21399
|
+
if (!Array.isArray(discoveryDefs) || discoveryDefs.length === 0) {
|
|
21400
|
+
throw reason instanceof Error
|
|
21401
|
+
? reason
|
|
21402
|
+
: new Error('Schema metadata unavailable via ResourceDiscoveryService.');
|
|
21403
|
+
}
|
|
21404
|
+
try {
|
|
21405
|
+
const schemaId = buildSchemaId(schemaContext);
|
|
21406
|
+
this.lastSchemaMeta = { schemaId, context: schemaContext };
|
|
21407
|
+
}
|
|
21408
|
+
catch { }
|
|
21409
|
+
this.schemaCache = discoveryDefs;
|
|
21410
|
+
this.emitLoadingState('schema', 'success', 'Schema carregado.');
|
|
21411
|
+
return discoveryDefs;
|
|
21412
|
+
};
|
|
21413
|
+
const loadLegacySchema = async (reason) => {
|
|
21414
|
+
if (this.isDestroyed)
|
|
21415
|
+
throw new PraxisDynamicFormDestroyedError();
|
|
21416
|
+
try {
|
|
21417
|
+
this.warnLog('[PDF] schema:metadata-fallback', reason);
|
|
21418
|
+
}
|
|
21419
|
+
catch { }
|
|
21420
|
+
const legacyGetSchema = this.crud?.getSchema;
|
|
21421
|
+
if (typeof legacyGetSchema !== 'function') {
|
|
21422
|
+
if (discoveryAttempted) {
|
|
21423
|
+
throw reason instanceof Error
|
|
21424
|
+
? reason
|
|
21425
|
+
: new Error('Schema metadata unavailable and legacy crud.getSchema() not found.');
|
|
21426
|
+
}
|
|
21427
|
+
return await loadDiscoverySchema(reason);
|
|
21428
|
+
}
|
|
21429
|
+
const isMockedLegacy = !!legacyGetSchema?.and;
|
|
21430
|
+
let legacyDefs;
|
|
21431
|
+
try {
|
|
21432
|
+
const legacyResult = legacyGetSchema.call(this.crud);
|
|
21433
|
+
controller.signal.throwIfAborted();
|
|
21434
|
+
const cancelled$ = new Observable(subscriber => {
|
|
21435
|
+
const abort = () => subscriber.error(controller.signal.reason ?? new DOMException('Schema read cancelled.', 'AbortError'));
|
|
21436
|
+
controller.signal.addEventListener('abort', abort, { once: true });
|
|
21437
|
+
return () => controller.signal.removeEventListener('abort', abort);
|
|
21438
|
+
});
|
|
21439
|
+
let legacyRead$ = race(from(legacyResult), cancelled$).pipe(take(1));
|
|
21440
|
+
const timeoutMs = this.schemaTransportOptions.requestTimeoutMs ?? DEFAULT_SCHEMA_REQUEST_TIMEOUT_MS;
|
|
21441
|
+
if (timeoutMs > 0)
|
|
21442
|
+
legacyRead$ = legacyRead$.pipe(timeout(timeoutMs));
|
|
21443
|
+
legacyDefs = await firstValueFrom(legacyRead$);
|
|
21444
|
+
}
|
|
21445
|
+
catch (legacyError) {
|
|
21446
|
+
if (this.isDestroyed || legacyError instanceof PraxisDynamicFormDestroyedError) {
|
|
21447
|
+
throw new PraxisDynamicFormDestroyedError();
|
|
21448
|
+
}
|
|
21449
|
+
if (this.isSchemaTransportInterruption(legacyError) ||
|
|
21450
|
+
controller.signal.aborted ||
|
|
21451
|
+
isMockedLegacy ||
|
|
21452
|
+
discoveryAttempted) {
|
|
21453
|
+
throw legacyError;
|
|
21454
|
+
}
|
|
21455
|
+
return await loadDiscoverySchema(legacyError);
|
|
21456
|
+
}
|
|
21368
21457
|
controller.signal.throwIfAborted();
|
|
21369
21458
|
if (this.isDestroyed)
|
|
21370
21459
|
throw new PraxisDynamicFormDestroyedError();
|
|
21460
|
+
if (!isMockedLegacy &&
|
|
21461
|
+
!discoveryAttempted &&
|
|
21462
|
+
(!Array.isArray(legacyDefs) || legacyDefs.length === 0)) {
|
|
21463
|
+
try {
|
|
21464
|
+
return await loadDiscoverySchema(reason);
|
|
21465
|
+
}
|
|
21466
|
+
catch (discoveryError) {
|
|
21467
|
+
if (this.isDestroyed || discoveryError instanceof PraxisDynamicFormDestroyedError) {
|
|
21468
|
+
throw new PraxisDynamicFormDestroyedError();
|
|
21469
|
+
}
|
|
21470
|
+
if (this.isSchemaTransportInterruption(discoveryError) || controller.signal.aborted) {
|
|
21471
|
+
throw discoveryError;
|
|
21472
|
+
}
|
|
21473
|
+
}
|
|
21474
|
+
}
|
|
21371
21475
|
try {
|
|
21372
21476
|
const schemaId = buildSchemaId(schemaContext);
|
|
21373
21477
|
this.lastSchemaMeta = { schemaId, context: schemaContext };
|
|
@@ -21384,6 +21488,14 @@ class PraxisDynamicForm {
|
|
|
21384
21488
|
if (preferLegacySchema) {
|
|
21385
21489
|
return await loadLegacySchema(new Error('Using legacy schema source (mocked crud.getSchema).'));
|
|
21386
21490
|
}
|
|
21491
|
+
const rp = String(this.resourcePath || '').trim();
|
|
21492
|
+
const isCatalogResourceKey = !!rp &&
|
|
21493
|
+
!String(this.schemaUrl || '').trim() &&
|
|
21494
|
+
!rp.includes('/') &&
|
|
21495
|
+
rp.includes('.');
|
|
21496
|
+
if (isCatalogResourceKey) {
|
|
21497
|
+
return await loadDiscoverySchema(new Error(`Resolving catalog resourceKey "${rp}" via ResourceDiscoveryService.`));
|
|
21498
|
+
}
|
|
21387
21499
|
if (!ctx.baseUrl) {
|
|
21388
21500
|
return await loadLegacySchema(new Error('Missing schema metadata base URL.'));
|
|
21389
21501
|
}
|
|
@@ -33487,6 +33599,14 @@ class PraxisFilterForm {
|
|
|
33487
33599
|
config;
|
|
33488
33600
|
formId;
|
|
33489
33601
|
resourcePath;
|
|
33602
|
+
/**
|
|
33603
|
+
* Optional logical endpoint key for multi-gateway filter schema resolution.
|
|
33604
|
+
*/
|
|
33605
|
+
endpointKey;
|
|
33606
|
+
/**
|
|
33607
|
+
* Optional explicit API URL entry override for filter schema resolution.
|
|
33608
|
+
*/
|
|
33609
|
+
apiUrlEntry;
|
|
33490
33610
|
mode;
|
|
33491
33611
|
formReady = new EventEmitter();
|
|
33492
33612
|
valueChange = new EventEmitter();
|
|
@@ -33497,26 +33617,153 @@ class PraxisFilterForm {
|
|
|
33497
33617
|
form = new FormGroup({});
|
|
33498
33618
|
valueChangesSub;
|
|
33499
33619
|
statusChangesSub;
|
|
33620
|
+
schemaSub;
|
|
33621
|
+
hydratedFieldMetadata = [];
|
|
33622
|
+
lastHydratedResourceKey = '';
|
|
33500
33623
|
columnFieldsCache = new WeakMap();
|
|
33501
33624
|
singleFieldCache = new WeakMap();
|
|
33625
|
+
crudService = (() => {
|
|
33626
|
+
try {
|
|
33627
|
+
return inject(GenericCrudService, { optional: true });
|
|
33628
|
+
}
|
|
33629
|
+
catch {
|
|
33630
|
+
return null;
|
|
33631
|
+
}
|
|
33632
|
+
})();
|
|
33633
|
+
resourceDiscovery = (() => {
|
|
33634
|
+
try {
|
|
33635
|
+
return inject(ResourceDiscoveryService, { optional: true });
|
|
33636
|
+
}
|
|
33637
|
+
catch {
|
|
33638
|
+
return null;
|
|
33639
|
+
}
|
|
33640
|
+
})();
|
|
33641
|
+
cdr = (() => {
|
|
33642
|
+
try {
|
|
33643
|
+
return inject(ChangeDetectorRef, { optional: true });
|
|
33644
|
+
}
|
|
33645
|
+
catch {
|
|
33646
|
+
return null;
|
|
33647
|
+
}
|
|
33648
|
+
})();
|
|
33502
33649
|
constructor(dynamicForm) {
|
|
33503
33650
|
this.dynamicForm = dynamicForm;
|
|
33504
33651
|
}
|
|
33505
33652
|
ngOnChanges(changes) {
|
|
33653
|
+
const hasConfiguredFields = Array.isArray(this.config?.fieldMetadata) && this.config.fieldMetadata.length > 0;
|
|
33654
|
+
if (hasConfiguredFields) {
|
|
33655
|
+
this.schemaSub?.unsubscribe();
|
|
33656
|
+
this.hydratedFieldMetadata = [];
|
|
33657
|
+
this.lastHydratedResourceKey = '';
|
|
33658
|
+
}
|
|
33506
33659
|
if ('config' in changes && this.config?.fieldMetadata) {
|
|
33507
33660
|
this.buildForm();
|
|
33508
33661
|
}
|
|
33662
|
+
if (!hasConfiguredFields &&
|
|
33663
|
+
('config' in changes ||
|
|
33664
|
+
'resourcePath' in changes ||
|
|
33665
|
+
'endpointKey' in changes ||
|
|
33666
|
+
'apiUrlEntry' in changes)) {
|
|
33667
|
+
this.loadFilterSchemaFieldsIfNeeded();
|
|
33668
|
+
}
|
|
33509
33669
|
}
|
|
33510
33670
|
ngOnDestroy() {
|
|
33671
|
+
this.schemaSub?.unsubscribe();
|
|
33511
33672
|
this.valueChangesSub?.unsubscribe();
|
|
33512
33673
|
this.statusChangesSub?.unsubscribe();
|
|
33513
33674
|
}
|
|
33675
|
+
loadFilterSchemaFieldsIfNeeded() {
|
|
33676
|
+
const rp = String(this.resourcePath ||
|
|
33677
|
+
(this.config?.resourcePath ?? '')).trim();
|
|
33678
|
+
if (!rp) {
|
|
33679
|
+
this.schemaSub?.unsubscribe();
|
|
33680
|
+
if (this.hydratedFieldMetadata.length > 0) {
|
|
33681
|
+
this.hydratedFieldMetadata = [];
|
|
33682
|
+
this.lastHydratedResourceKey = '';
|
|
33683
|
+
this.buildForm();
|
|
33684
|
+
}
|
|
33685
|
+
return;
|
|
33686
|
+
}
|
|
33687
|
+
const discoveryOptions = this.endpointKey || this.apiUrlEntry
|
|
33688
|
+
? {
|
|
33689
|
+
...(this.endpointKey ? { endpointKey: this.endpointKey } : {}),
|
|
33690
|
+
...(this.apiUrlEntry ? { apiUrlEntry: this.apiUrlEntry } : {}),
|
|
33691
|
+
}
|
|
33692
|
+
: undefined;
|
|
33693
|
+
const hydrationKey = JSON.stringify({
|
|
33694
|
+
rp,
|
|
33695
|
+
endpointKey: this.endpointKey ?? null,
|
|
33696
|
+
apiUrlEntry: this.apiUrlEntry ?? null,
|
|
33697
|
+
});
|
|
33698
|
+
if (this.lastHydratedResourceKey === hydrationKey && this.hydratedFieldMetadata.length > 0) {
|
|
33699
|
+
return;
|
|
33700
|
+
}
|
|
33701
|
+
const canLoadViaDiscovery = typeof this.resourceDiscovery?.getResourceFilterSchemaFields === 'function' &&
|
|
33702
|
+
(typeof this.resourceDiscovery.canResolveSchemas !== 'function' ||
|
|
33703
|
+
this.resourceDiscovery.canResolveSchemas(discoveryOptions));
|
|
33704
|
+
const canLoadViaCrud = typeof this.crudService?.getFilterSchema === 'function' ||
|
|
33705
|
+
typeof this.crudService?.getSchema === 'function';
|
|
33706
|
+
if (!canLoadViaCrud && !canLoadViaDiscovery) {
|
|
33707
|
+
return;
|
|
33708
|
+
}
|
|
33709
|
+
const loadFromDiscovery = () => discoveryOptions
|
|
33710
|
+
? this.resourceDiscovery.getResourceFilterSchemaFields(rp, discoveryOptions)
|
|
33711
|
+
: this.resourceDiscovery.getResourceFilterSchemaFields(rp);
|
|
33712
|
+
const loadFromCrud = () => {
|
|
33713
|
+
if (!canLoadViaCrud || !this.crudService) {
|
|
33714
|
+
return of([]);
|
|
33715
|
+
}
|
|
33716
|
+
if (discoveryOptions) {
|
|
33717
|
+
this.crudService.configure?.(rp, discoveryOptions);
|
|
33718
|
+
}
|
|
33719
|
+
else {
|
|
33720
|
+
this.crudService.configure?.(rp);
|
|
33721
|
+
}
|
|
33722
|
+
return typeof this.crudService.getFilterSchema === 'function'
|
|
33723
|
+
? this.crudService.getFilterSchema()
|
|
33724
|
+
: this.crudService.getSchema();
|
|
33725
|
+
};
|
|
33726
|
+
const isCatalogResourceKey = !rp.includes('/') && rp.includes('.');
|
|
33727
|
+
let source$;
|
|
33728
|
+
if (isCatalogResourceKey && canLoadViaDiscovery) {
|
|
33729
|
+
source$ = loadFromDiscovery().pipe(defaultIfEmpty([]), switchMap$1((defs) => Array.isArray(defs) && defs.length === 0 && canLoadViaCrud
|
|
33730
|
+
? loadFromCrud()
|
|
33731
|
+
: of(defs)), catchError$1(() => (canLoadViaCrud ? loadFromCrud() : of([]))));
|
|
33732
|
+
}
|
|
33733
|
+
else if (canLoadViaCrud) {
|
|
33734
|
+
source$ = loadFromCrud().pipe(defaultIfEmpty([]), switchMap$1((defs) => Array.isArray(defs) && defs.length === 0 && canLoadViaDiscovery
|
|
33735
|
+
? loadFromDiscovery()
|
|
33736
|
+
: of(defs)), catchError$1(() => (canLoadViaDiscovery ? loadFromDiscovery() : of([]))));
|
|
33737
|
+
}
|
|
33738
|
+
else {
|
|
33739
|
+
source$ = loadFromDiscovery();
|
|
33740
|
+
}
|
|
33741
|
+
this.schemaSub?.unsubscribe();
|
|
33742
|
+
this.schemaSub = source$
|
|
33743
|
+
.pipe(take$1(1), catchError$1(() => of([])))
|
|
33744
|
+
.subscribe((defs) => {
|
|
33745
|
+
if (!Array.isArray(defs) || defs.length === 0) {
|
|
33746
|
+
return;
|
|
33747
|
+
}
|
|
33748
|
+
if (Array.isArray(this.config?.fieldMetadata) && this.config.fieldMetadata.length > 0) {
|
|
33749
|
+
return;
|
|
33750
|
+
}
|
|
33751
|
+
const metadata = mapFieldDefinitionsToMetadata(defs);
|
|
33752
|
+
if (!metadata.length) {
|
|
33753
|
+
return;
|
|
33754
|
+
}
|
|
33755
|
+
this.hydratedFieldMetadata = metadata;
|
|
33756
|
+
this.lastHydratedResourceKey = hydrationKey;
|
|
33757
|
+
this.buildForm();
|
|
33758
|
+
this.cdr?.markForCheck();
|
|
33759
|
+
});
|
|
33760
|
+
}
|
|
33514
33761
|
buildForm() {
|
|
33515
33762
|
this.valueChangesSub?.unsubscribe();
|
|
33516
33763
|
this.statusChangesSub?.unsubscribe();
|
|
33517
33764
|
this.columnFieldsCache = new WeakMap();
|
|
33518
33765
|
this.singleFieldCache = new WeakMap();
|
|
33519
|
-
const fields = this.
|
|
33766
|
+
const fields = this.fieldMetadata;
|
|
33520
33767
|
const previous = this.form.getRawValue?.() || {};
|
|
33521
33768
|
this.form = this.dynamicForm.createFormGroupFromMetadata(fields);
|
|
33522
33769
|
// Preserve previous values only for fields that did not declare a new explicit seed.
|
|
@@ -33576,7 +33823,9 @@ class PraxisFilterForm {
|
|
|
33576
33823
|
return className || null;
|
|
33577
33824
|
}
|
|
33578
33825
|
get fieldMetadata() {
|
|
33579
|
-
return this.config?.fieldMetadata
|
|
33826
|
+
return this.config?.fieldMetadata?.length
|
|
33827
|
+
? this.config.fieldMetadata
|
|
33828
|
+
: this.hydratedFieldMetadata;
|
|
33580
33829
|
}
|
|
33581
33830
|
get hasLayout() {
|
|
33582
33831
|
return (Array.isArray(this.config?.sections) && this.config.sections.length > 0);
|
|
@@ -33592,7 +33841,7 @@ class PraxisFilterForm {
|
|
|
33592
33841
|
return `col-${i}-${col.fields.join(',')}`;
|
|
33593
33842
|
}
|
|
33594
33843
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisFilterForm, deps: [{ token: i1.DynamicFormService }], target: i0.ɵɵFactoryTarget.Component });
|
|
33595
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisFilterForm, isStandalone: true, selector: "praxis-filter-form", inputs: { config: "config", formId: "formId", resourcePath: "resourcePath", mode: "mode" }, outputs: { formReady: "formReady", valueChange: "valueChange", submit: "submit", requestSearch: "requestSearch", validityChange: "validityChange" }, usesOnChanges: true, ngImport: i0, template: "<form\n [formGroup]=\"form\"\n class=\"praxis-filter-form\"\n (change)=\"$event.stopPropagation()\"\n (ngSubmit)=\"onFormSubmit(); $event.preventDefault(); $event.stopPropagation()\"\n>\n @if (hasLayout) { @for (section of (config.sections || []); track section?.id ??\n $index) {\n <div class=\"filter-section\">\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n } @for (row of section.rows; track $index) {\n <div class=\"filter-row\">\n @for ( column of row.columns; track (column?.fields?.join(',') ?? $index)\n ) {\n <div class=\"filter-column\" [ngClass]=\"getColumnClasses(column)\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getColumnFields(column)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n </div>\n } } @else {\n <div class=\"filter-row\">\n @for (field of fieldMetadata; track field?.name ?? $index) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getSingleField(field)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n <!-- Bot\u00E3o de submit invis\u00EDvel para garantir submit ao pressionar Enter -->\n <button\n type=\"submit\"\n class=\"hidden-submit\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n ></button>\n</form>\n", styles: [":host{display:block}.praxis-filter-form{padding-top:6px}.filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px 16px}.filter-column{min-width:0}.filter-column--full{grid-column:1/-1}.hidden-submit{display:none;width:0;height:0;padding:0;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick", "canvasFocus"] }] });
|
|
33844
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisFilterForm, isStandalone: true, selector: "praxis-filter-form", inputs: { config: "config", formId: "formId", resourcePath: "resourcePath", endpointKey: "endpointKey", apiUrlEntry: "apiUrlEntry", mode: "mode" }, outputs: { formReady: "formReady", valueChange: "valueChange", submit: "submit", requestSearch: "requestSearch", validityChange: "validityChange" }, usesOnChanges: true, ngImport: i0, template: "<form\n [formGroup]=\"form\"\n class=\"praxis-filter-form\"\n (change)=\"$event.stopPropagation()\"\n (ngSubmit)=\"onFormSubmit(); $event.preventDefault(); $event.stopPropagation()\"\n>\n @if (hasLayout) { @for (section of (config.sections || []); track section?.id ??\n $index) {\n <div class=\"filter-section\">\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n } @for (row of section.rows; track $index) {\n <div class=\"filter-row\">\n @for ( column of row.columns; track (column?.fields?.join(',') ?? $index)\n ) {\n <div class=\"filter-column\" [ngClass]=\"getColumnClasses(column)\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getColumnFields(column)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n </div>\n } } @else {\n <div class=\"filter-row\">\n @for (field of fieldMetadata; track field?.name ?? $index) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getSingleField(field)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n <!-- Bot\u00E3o de submit invis\u00EDvel para garantir submit ao pressionar Enter -->\n <button\n type=\"submit\"\n class=\"hidden-submit\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n ></button>\n</form>\n", styles: [":host{display:block}.praxis-filter-form{padding-top:6px}.filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px 16px}.filter-column{min-width:0}.filter-column--full{grid-column:1/-1}.hidden-submit{display:none;width:0;height:0;padding:0;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick", "canvasFocus"] }] });
|
|
33596
33845
|
}
|
|
33597
33846
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisFilterForm, decorators: [{
|
|
33598
33847
|
type: Component,
|
|
@@ -33604,6 +33853,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
33604
33853
|
type: Input
|
|
33605
33854
|
}], resourcePath: [{
|
|
33606
33855
|
type: Input
|
|
33856
|
+
}], endpointKey: [{
|
|
33857
|
+
type: Input
|
|
33858
|
+
}], apiUrlEntry: [{
|
|
33859
|
+
type: Input
|
|
33607
33860
|
}], mode: [{
|
|
33608
33861
|
type: Input
|
|
33609
33862
|
}], formReady: [{
|
|
@@ -33709,7 +33962,19 @@ const PRAXIS_FILTER_FORM_COMPONENT_METADATA = {
|
|
|
33709
33962
|
name: 'resourcePath',
|
|
33710
33963
|
type: 'string',
|
|
33711
33964
|
label: 'Resource',
|
|
33712
|
-
description: 'Resource path used
|
|
33965
|
+
description: 'Resource path or catalog resourceKey used to resolve the filter schema.',
|
|
33966
|
+
},
|
|
33967
|
+
{
|
|
33968
|
+
name: 'endpointKey',
|
|
33969
|
+
type: 'ApiEndpoint',
|
|
33970
|
+
label: 'Endpoint Key',
|
|
33971
|
+
description: 'Canonical endpoint key used to resolve the filter schema in multi-endpoint hosts.',
|
|
33972
|
+
},
|
|
33973
|
+
{
|
|
33974
|
+
name: 'apiUrlEntry',
|
|
33975
|
+
type: 'ApiUrlEntry | null',
|
|
33976
|
+
label: 'API URL Entry',
|
|
33977
|
+
description: 'Explicit API URL entry for detached hosts that render the filter form outside the route injector.',
|
|
33713
33978
|
},
|
|
33714
33979
|
{
|
|
33715
33980
|
name: 'mode',
|
|
@@ -33767,18 +34032,89 @@ class PraxisFilterFormWidgetConfigEditor {
|
|
|
33767
34032
|
isDirty$ = new BehaviorSubject(false);
|
|
33768
34033
|
isValid$ = new BehaviorSubject(true);
|
|
33769
34034
|
isBusy$ = new BehaviorSubject(false);
|
|
34035
|
+
schemaWarning = '';
|
|
33770
34036
|
subscription = new Subscription();
|
|
34037
|
+
schemaSubscription;
|
|
34038
|
+
childBusy = false;
|
|
34039
|
+
schemaLoading = false;
|
|
34040
|
+
resourcePathDraft;
|
|
34041
|
+
crudService = (() => {
|
|
34042
|
+
try {
|
|
34043
|
+
return inject(GenericCrudService, { optional: true });
|
|
34044
|
+
}
|
|
34045
|
+
catch {
|
|
34046
|
+
return null;
|
|
34047
|
+
}
|
|
34048
|
+
})();
|
|
34049
|
+
resourceDiscovery = (() => {
|
|
34050
|
+
try {
|
|
34051
|
+
return inject(ResourceDiscoveryService, { optional: true });
|
|
34052
|
+
}
|
|
34053
|
+
catch {
|
|
34054
|
+
return null;
|
|
34055
|
+
}
|
|
34056
|
+
})();
|
|
34057
|
+
cdr = (() => {
|
|
34058
|
+
try {
|
|
34059
|
+
return inject(ChangeDetectorRef, { optional: true });
|
|
34060
|
+
}
|
|
34061
|
+
catch {
|
|
34062
|
+
return null;
|
|
34063
|
+
}
|
|
34064
|
+
})();
|
|
34065
|
+
get effectiveResourcePath() {
|
|
34066
|
+
if (this.resourcePathDraft !== undefined) {
|
|
34067
|
+
return this.resourcePathDraft;
|
|
34068
|
+
}
|
|
34069
|
+
return String(this.inputs?.resourcePath || '').trim();
|
|
34070
|
+
}
|
|
34071
|
+
get effectiveEndpointKey() {
|
|
34072
|
+
return this.buildDiscoveryOptions()?.endpointKey;
|
|
34073
|
+
}
|
|
34074
|
+
get effectiveApiUrlEntry() {
|
|
34075
|
+
return this.buildDiscoveryOptions()?.apiUrlEntry;
|
|
34076
|
+
}
|
|
34077
|
+
onResourceQuickConnect(resourcePath) {
|
|
34078
|
+
const normalized = String(resourcePath ?? '').trim();
|
|
34079
|
+
if (!normalized) {
|
|
34080
|
+
this.onResourceQuickClear();
|
|
34081
|
+
return;
|
|
34082
|
+
}
|
|
34083
|
+
this.resourcePathDraft = normalized;
|
|
34084
|
+
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
34085
|
+
if (this.formEditor) {
|
|
34086
|
+
const currentDoc = parseLegacyOrDynamicFormDocument(this.formEditor.getSettingsValue?.() ?? this.createDocumentFromInputs());
|
|
34087
|
+
this.loadFilterSchemaFieldsIfNeeded(normalized, currentDoc, true);
|
|
34088
|
+
}
|
|
34089
|
+
}
|
|
34090
|
+
onResourceQuickClear() {
|
|
34091
|
+
this.schemaSubscription?.unsubscribe();
|
|
34092
|
+
this.schemaLoading = false;
|
|
34093
|
+
this.publishBusy();
|
|
34094
|
+
this.resourcePathDraft = '';
|
|
34095
|
+
this.schemaWarning = '';
|
|
34096
|
+
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
34097
|
+
}
|
|
34098
|
+
updateDirtyState(editorDirty) {
|
|
34099
|
+
const baseline = String(this.inputs?.resourcePath || '').trim();
|
|
34100
|
+
const resourceChanged = this.resourcePathDraft !== undefined && this.resourcePathDraft !== baseline;
|
|
34101
|
+
this.isDirty$.next(editorDirty || resourceChanged);
|
|
34102
|
+
}
|
|
33771
34103
|
ngAfterViewInit() {
|
|
33772
34104
|
if (!this.formEditor) {
|
|
33773
34105
|
return;
|
|
33774
34106
|
}
|
|
33775
34107
|
this.formEditor.supportsPresentationSettings = false;
|
|
33776
|
-
this.
|
|
33777
|
-
this.subscription.add(this.formEditor.isDirty$.subscribe((value) => this.isDirty$.next(value)));
|
|
34108
|
+
this.subscription.add(this.formEditor.isDirty$.subscribe((value) => this.updateDirtyState(value)));
|
|
33778
34109
|
this.subscription.add(this.formEditor.isValid$.subscribe((value) => this.isValid$.next(value)));
|
|
33779
|
-
this.subscription.add(this.formEditor.isBusy$.subscribe((value) =>
|
|
34110
|
+
this.subscription.add(this.formEditor.isBusy$.subscribe((value) => {
|
|
34111
|
+
this.childBusy = value;
|
|
34112
|
+
this.publishBusy();
|
|
34113
|
+
}));
|
|
34114
|
+
this.initializeChildEditor();
|
|
33780
34115
|
}
|
|
33781
34116
|
ngOnDestroy() {
|
|
34117
|
+
this.schemaSubscription?.unsubscribe();
|
|
33782
34118
|
this.subscription.unsubscribe();
|
|
33783
34119
|
}
|
|
33784
34120
|
getSettingsValue() {
|
|
@@ -33788,27 +34124,146 @@ class PraxisFilterFormWidgetConfigEditor {
|
|
|
33788
34124
|
return this.buildValue(this.formEditor?.onSave?.() ?? this.formEditor?.getSettingsValue());
|
|
33789
34125
|
}
|
|
33790
34126
|
reset() {
|
|
34127
|
+
this.schemaSubscription?.unsubscribe();
|
|
34128
|
+
this.schemaLoading = false;
|
|
34129
|
+
this.resourcePathDraft = undefined;
|
|
34130
|
+
this.publishBusy();
|
|
33791
34131
|
this.formEditor?.reset?.();
|
|
34132
|
+
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
34133
|
+
}
|
|
34134
|
+
publishBusy() {
|
|
34135
|
+
this.isBusy$.next(this.childBusy || this.schemaLoading);
|
|
33792
34136
|
}
|
|
33793
34137
|
initializeChildEditor() {
|
|
33794
34138
|
const document = this.createDocumentFromInputs();
|
|
34139
|
+
this.applyBaselineDocumentToChildEditor(document, 'filter-form-widget-config-init');
|
|
34140
|
+
const resourcePath = this.effectiveResourcePath;
|
|
34141
|
+
if (resourcePath &&
|
|
34142
|
+
(!Array.isArray(document.config.fieldMetadata) || document.config.fieldMetadata.length === 0)) {
|
|
34143
|
+
this.loadFilterSchemaFieldsIfNeeded(resourcePath, document);
|
|
34144
|
+
}
|
|
34145
|
+
}
|
|
34146
|
+
applyBaselineDocumentToChildEditor(document, reason) {
|
|
33795
34147
|
const serialized = serializeDynamicFormAuthoringDocument(document);
|
|
33796
34148
|
const editor = this.formEditor;
|
|
33797
34149
|
editor.onJsonConfigChange?.(serialized);
|
|
33798
34150
|
editor.initialDocument = document;
|
|
33799
34151
|
editor.initialConfig = document.config;
|
|
33800
|
-
editor.updateDirtyState?.(
|
|
34152
|
+
editor.updateDirtyState?.(reason);
|
|
33801
34153
|
editor.isDirty$?.next(false);
|
|
33802
34154
|
editor.isValid$?.next(true);
|
|
33803
34155
|
}
|
|
34156
|
+
loadFilterSchemaFieldsIfNeeded(resourcePath, baselineDocument, forceReload = false) {
|
|
34157
|
+
const discoveryOptions = this.buildDiscoveryOptions();
|
|
34158
|
+
const canLoadViaDiscovery = typeof this.resourceDiscovery?.getResourceFilterSchemaFields === 'function' &&
|
|
34159
|
+
(typeof this.resourceDiscovery.canResolveSchemas !== 'function' ||
|
|
34160
|
+
this.resourceDiscovery.canResolveSchemas(discoveryOptions));
|
|
34161
|
+
const canLoadViaCrud = typeof this.crudService?.getFilterSchema === 'function' ||
|
|
34162
|
+
typeof this.crudService?.getSchema === 'function';
|
|
34163
|
+
if (!canLoadViaCrud && !canLoadViaDiscovery) {
|
|
34164
|
+
return;
|
|
34165
|
+
}
|
|
34166
|
+
const loadFromDiscovery = () => discoveryOptions
|
|
34167
|
+
? this.resourceDiscovery.getResourceFilterSchemaFields(resourcePath, discoveryOptions)
|
|
34168
|
+
: this.resourceDiscovery.getResourceFilterSchemaFields(resourcePath);
|
|
34169
|
+
const loadFromCrud = () => {
|
|
34170
|
+
if (!canLoadViaCrud || !this.crudService) {
|
|
34171
|
+
return of([]);
|
|
34172
|
+
}
|
|
34173
|
+
if (discoveryOptions?.endpointKey || discoveryOptions?.apiUrlEntry) {
|
|
34174
|
+
this.crudService.configure?.(resourcePath, {
|
|
34175
|
+
...(discoveryOptions.endpointKey ? { endpointKey: discoveryOptions.endpointKey } : {}),
|
|
34176
|
+
...(discoveryOptions.apiUrlEntry ? { apiUrlEntry: discoveryOptions.apiUrlEntry } : {}),
|
|
34177
|
+
});
|
|
34178
|
+
}
|
|
34179
|
+
else {
|
|
34180
|
+
this.crudService.configure?.(resourcePath);
|
|
34181
|
+
}
|
|
34182
|
+
return typeof this.crudService.getFilterSchema === 'function'
|
|
34183
|
+
? this.crudService.getFilterSchema()
|
|
34184
|
+
: this.crudService.getSchema();
|
|
34185
|
+
};
|
|
34186
|
+
const isCatalogResourceKey = !resourcePath.includes('/') && resourcePath.includes('.');
|
|
34187
|
+
let source$;
|
|
34188
|
+
if (isCatalogResourceKey && canLoadViaDiscovery) {
|
|
34189
|
+
source$ = loadFromDiscovery().pipe(defaultIfEmpty([]), switchMap$1((defs) => Array.isArray(defs) && defs.length === 0 && canLoadViaCrud
|
|
34190
|
+
? loadFromCrud()
|
|
34191
|
+
: of(defs)), catchError$1(() => (canLoadViaCrud ? loadFromCrud() : of([]))));
|
|
34192
|
+
}
|
|
34193
|
+
else if (canLoadViaCrud) {
|
|
34194
|
+
source$ = loadFromCrud().pipe(defaultIfEmpty([]), switchMap$1((defs) => Array.isArray(defs) && defs.length === 0 && canLoadViaDiscovery
|
|
34195
|
+
? loadFromDiscovery()
|
|
34196
|
+
: of(defs)), catchError$1(() => (canLoadViaDiscovery ? loadFromDiscovery() : of([]))));
|
|
34197
|
+
}
|
|
34198
|
+
else {
|
|
34199
|
+
source$ = loadFromDiscovery();
|
|
34200
|
+
}
|
|
34201
|
+
this.schemaSubscription?.unsubscribe();
|
|
34202
|
+
this.schemaWarning = '';
|
|
34203
|
+
this.schemaLoading = true;
|
|
34204
|
+
this.publishBusy();
|
|
34205
|
+
this.schemaSubscription = source$
|
|
34206
|
+
.pipe(take$1(1), catchError$1(() => of([])), finalize$1(() => {
|
|
34207
|
+
this.schemaLoading = false;
|
|
34208
|
+
this.publishBusy();
|
|
34209
|
+
this.cdr?.markForCheck();
|
|
34210
|
+
}))
|
|
34211
|
+
.subscribe((defs) => {
|
|
34212
|
+
if (!Array.isArray(defs) || defs.length === 0) {
|
|
34213
|
+
this.schemaWarning = `No filter schema fields were returned for resource "${resourcePath}".`;
|
|
34214
|
+
this.cdr?.markForCheck();
|
|
34215
|
+
return;
|
|
34216
|
+
}
|
|
34217
|
+
if ((!forceReload && this.isDirty$.value) || !this.formEditor) {
|
|
34218
|
+
return;
|
|
34219
|
+
}
|
|
34220
|
+
const fieldMetadata = mapFieldDefinitionsToMetadata(defs);
|
|
34221
|
+
if (!fieldMetadata.length) {
|
|
34222
|
+
this.schemaWarning = `No filter schema fields were returned for resource "${resourcePath}".`;
|
|
34223
|
+
this.cdr?.markForCheck();
|
|
34224
|
+
return;
|
|
34225
|
+
}
|
|
34226
|
+
const newFieldNames = new Set(fieldMetadata.map((f) => f.name));
|
|
34227
|
+
const existingSectionFields = (baselineDocument.config.sections ?? []).flatMap((section) => (section.rows ?? []).flatMap((row) => (row.columns ?? []).flatMap((col) => (col.fields ?? []).map((f) => typeof f === 'string' ? f : f?.name ?? '')))).filter(Boolean);
|
|
34228
|
+
const shouldResetSections = forceReload &&
|
|
34229
|
+
existingSectionFields.length > 0 &&
|
|
34230
|
+
!existingSectionFields.some((fieldName) => newFieldNames.has(fieldName));
|
|
34231
|
+
const nextDocument = {
|
|
34232
|
+
...baselineDocument,
|
|
34233
|
+
config: {
|
|
34234
|
+
...baselineDocument.config,
|
|
34235
|
+
...(shouldResetSections ? { sections: [] } : {}),
|
|
34236
|
+
fieldMetadata,
|
|
34237
|
+
},
|
|
34238
|
+
};
|
|
34239
|
+
this.applyBaselineDocumentToChildEditor(nextDocument, 'filter-form-widget-config-schema');
|
|
34240
|
+
this.updateDirtyState(this.formEditor.isDirty$.value);
|
|
34241
|
+
this.cdr?.markForCheck();
|
|
34242
|
+
});
|
|
34243
|
+
}
|
|
34244
|
+
buildDiscoveryOptions() {
|
|
34245
|
+
const rawEndpointKey = this.inputs?.apiEndpointKey ?? this.inputs?.endpointKey;
|
|
34246
|
+
const endpointKey = String(rawEndpointKey ?? '').trim();
|
|
34247
|
+
const apiUrlEntry = this.inputs?.apiUrlEntry;
|
|
34248
|
+
if (!endpointKey && !apiUrlEntry) {
|
|
34249
|
+
return undefined;
|
|
34250
|
+
}
|
|
34251
|
+
return {
|
|
34252
|
+
...(endpointKey ? { endpointKey } : {}),
|
|
34253
|
+
...(apiUrlEntry ? { apiUrlEntry } : {}),
|
|
34254
|
+
};
|
|
34255
|
+
}
|
|
33804
34256
|
buildValue(rawDocument) {
|
|
33805
34257
|
const document = parseLegacyOrDynamicFormDocument(rawDocument ?? this.createDocumentFromInputs());
|
|
34258
|
+
const resolvedResourcePath = this.resourcePathDraft !== undefined
|
|
34259
|
+
? this.resourcePathDraft || undefined
|
|
34260
|
+
: this.inputs?.resourcePath;
|
|
33806
34261
|
return {
|
|
33807
34262
|
inputs: {
|
|
33808
34263
|
...(this.inputs ?? {}),
|
|
33809
34264
|
config: document.config,
|
|
33810
34265
|
formId: this.inputs?.formId ?? this.widgetKey,
|
|
33811
|
-
resourcePath:
|
|
34266
|
+
resourcePath: resolvedResourcePath,
|
|
33812
34267
|
mode: document.bindings?.mode ?? this.inputs?.mode ?? 'create',
|
|
33813
34268
|
},
|
|
33814
34269
|
};
|
|
@@ -33829,25 +34284,47 @@ class PraxisFilterFormWidgetConfigEditor {
|
|
|
33829
34284
|
: undefined;
|
|
33830
34285
|
}
|
|
33831
34286
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisFilterFormWidgetConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
33832
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
34287
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisFilterFormWidgetConfigEditor, isStandalone: true, selector: "praxis-filter-form-widget-config-editor", inputs: { inputs: "inputs", widgetKey: "widgetKey" }, providers: [{ provide: SETTINGS_PANEL_DATA, useValue: null }], viewQueries: [{ propertyName: "formEditor", first: true, predicate: ["formEditor"], descendants: true }], ngImport: i0, template: `
|
|
33833
34288
|
<section data-testid="filter-form-widget-config-editor">
|
|
34289
|
+
<div class="filter-form-resource-quick-connect" data-testid="filter-form-resource-quick-connect">
|
|
34290
|
+
<praxis-resource-quick-connect
|
|
34291
|
+
[resourcePath]="effectiveResourcePath"
|
|
34292
|
+
[endpointKey]="effectiveEndpointKey"
|
|
34293
|
+
[apiUrlEntry]="effectiveApiUrlEntry"
|
|
34294
|
+
intent="filter"
|
|
34295
|
+
(resourcePathChange)="onResourceQuickConnect($event)"
|
|
34296
|
+
></praxis-resource-quick-connect>
|
|
34297
|
+
</div>
|
|
34298
|
+
@if (schemaWarning) {
|
|
34299
|
+
<p class="filter-form-editor-schema-warning" data-testid="filter-form-schema-warning" role="status">
|
|
34300
|
+
{{ schemaWarning }}
|
|
34301
|
+
</p>
|
|
34302
|
+
}
|
|
33834
34303
|
<praxis-dynamic-form-config-editor #formEditor />
|
|
33835
34304
|
</section>
|
|
33836
|
-
`, isInline: true, dependencies: [{ kind: "component", type: PraxisDynamicFormConfigEditor, selector: "praxis-dynamic-form-config-editor" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34305
|
+
`, isInline: true, styles: [".filter-form-resource-quick-connect{padding:var(--p-space-4, 16px) var(--p-space-4, 16px) var(--p-space-2, 8px)}\n"], dependencies: [{ kind: "component", type: PraxisDynamicFormConfigEditor, selector: "praxis-dynamic-form-config-editor" }, { kind: "component", type: ResourceQuickConnectComponent, selector: "praxis-resource-quick-connect", inputs: ["resourcePath", "endpointKey", "apiUrlEntry", "intent", "resources", "label", "placeholder", "hint", "disabled"], outputs: ["resourcePathChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33837
34306
|
}
|
|
33838
34307
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisFilterFormWidgetConfigEditor, decorators: [{
|
|
33839
34308
|
type: Component,
|
|
33840
|
-
args: [{
|
|
33841
|
-
selector: 'praxis-filter-form-widget-config-editor',
|
|
33842
|
-
standalone: true,
|
|
33843
|
-
imports: [PraxisDynamicFormConfigEditor],
|
|
33844
|
-
template: `
|
|
34309
|
+
args: [{ providers: [{ provide: SETTINGS_PANEL_DATA, useValue: null }], selector: 'praxis-filter-form-widget-config-editor', standalone: true, imports: [PraxisDynamicFormConfigEditor, ResourceQuickConnectComponent], template: `
|
|
33845
34310
|
<section data-testid="filter-form-widget-config-editor">
|
|
34311
|
+
<div class="filter-form-resource-quick-connect" data-testid="filter-form-resource-quick-connect">
|
|
34312
|
+
<praxis-resource-quick-connect
|
|
34313
|
+
[resourcePath]="effectiveResourcePath"
|
|
34314
|
+
[endpointKey]="effectiveEndpointKey"
|
|
34315
|
+
[apiUrlEntry]="effectiveApiUrlEntry"
|
|
34316
|
+
intent="filter"
|
|
34317
|
+
(resourcePathChange)="onResourceQuickConnect($event)"
|
|
34318
|
+
></praxis-resource-quick-connect>
|
|
34319
|
+
</div>
|
|
34320
|
+
@if (schemaWarning) {
|
|
34321
|
+
<p class="filter-form-editor-schema-warning" data-testid="filter-form-schema-warning" role="status">
|
|
34322
|
+
{{ schemaWarning }}
|
|
34323
|
+
</p>
|
|
34324
|
+
}
|
|
33846
34325
|
<praxis-dynamic-form-config-editor #formEditor />
|
|
33847
34326
|
</section>
|
|
33848
|
-
`,
|
|
33849
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
33850
|
-
}]
|
|
34327
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".filter-form-resource-quick-connect{padding:var(--p-space-4, 16px) var(--p-space-4, 16px) var(--p-space-2, 8px)}\n"] }]
|
|
33851
34328
|
}], propDecorators: { inputs: [{
|
|
33852
34329
|
type: Input
|
|
33853
34330
|
}], widgetKey: [{
|
|
@@ -33863,40 +34340,97 @@ var praxisFilterFormWidgetConfigEditor = /*#__PURE__*/Object.freeze({
|
|
|
33863
34340
|
});
|
|
33864
34341
|
|
|
33865
34342
|
class PraxisDynamicFormWidgetConfigEditor {
|
|
34343
|
+
crudService = this.optionalInject(GenericCrudService);
|
|
34344
|
+
resourceDiscovery = this.optionalInject(ResourceDiscoveryService);
|
|
34345
|
+
cdr = this.optionalInject(ChangeDetectorRef);
|
|
33866
34346
|
inputs = null;
|
|
33867
34347
|
widgetKey;
|
|
33868
34348
|
appliedValues$;
|
|
33869
34349
|
appliedSource;
|
|
34350
|
+
appliedResourcePath;
|
|
33870
34351
|
/** Runtime evidence supplied by the existing component editor context resolver. */
|
|
33871
34352
|
context = {};
|
|
33872
34353
|
formEditor;
|
|
33873
34354
|
isDirty$ = new BehaviorSubject(false);
|
|
33874
34355
|
isValid$ = new BehaviorSubject(true);
|
|
33875
34356
|
isBusy$ = new BehaviorSubject(false);
|
|
34357
|
+
schemaWarning = '';
|
|
33876
34358
|
subscription = new Subscription();
|
|
34359
|
+
schemaSubscription;
|
|
34360
|
+
childBusy = false;
|
|
34361
|
+
schemaLoading = false;
|
|
33877
34362
|
initialEditorConfig;
|
|
33878
34363
|
sourceDraft;
|
|
34364
|
+
resourcePathDraft;
|
|
34365
|
+
optionalInject(token) {
|
|
34366
|
+
try {
|
|
34367
|
+
return inject(token, { optional: true });
|
|
34368
|
+
}
|
|
34369
|
+
catch {
|
|
34370
|
+
return null;
|
|
34371
|
+
}
|
|
34372
|
+
}
|
|
33879
34373
|
get configurationSource() {
|
|
33880
34374
|
return this.sourceDraft ?? this.inputs?.configPersistenceStrategy ?? 'local-first';
|
|
33881
34375
|
}
|
|
34376
|
+
get effectiveResourcePath() {
|
|
34377
|
+
if (this.resourcePathDraft !== undefined) {
|
|
34378
|
+
return this.resourcePathDraft;
|
|
34379
|
+
}
|
|
34380
|
+
return String(this.inputs?.resourcePath ?? this.context['resourcePath'] ?? '').trim();
|
|
34381
|
+
}
|
|
34382
|
+
get effectiveEndpointKey() {
|
|
34383
|
+
return this.buildDiscoveryOptions()?.endpointKey;
|
|
34384
|
+
}
|
|
34385
|
+
get effectiveApiUrlEntry() {
|
|
34386
|
+
return this.buildDiscoveryOptions()?.apiUrlEntry;
|
|
34387
|
+
}
|
|
34388
|
+
get effectiveDiscoveryIntent() {
|
|
34389
|
+
return (this.inputs?.schemaSource ?? this.context['schemaSource']) === 'filter'
|
|
34390
|
+
? 'filter'
|
|
34391
|
+
: 'crud';
|
|
34392
|
+
}
|
|
33882
34393
|
changeConfigurationSource(value) {
|
|
33883
34394
|
if (value !== 'local-first' && value !== 'input-first')
|
|
33884
34395
|
return;
|
|
33885
34396
|
this.sourceDraft = value;
|
|
33886
34397
|
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
33887
34398
|
}
|
|
33888
|
-
|
|
33889
|
-
|
|
33890
|
-
|
|
33891
|
-
|
|
33892
|
-
|
|
33893
|
-
try {
|
|
33894
|
-
return inject(ChangeDetectorRef, { optional: true });
|
|
34399
|
+
onResourceQuickConnect(resourcePath) {
|
|
34400
|
+
const normalized = String(resourcePath ?? '').trim();
|
|
34401
|
+
if (!normalized) {
|
|
34402
|
+
this.onResourceQuickClear();
|
|
34403
|
+
return;
|
|
33895
34404
|
}
|
|
33896
|
-
|
|
33897
|
-
|
|
34405
|
+
this.resourcePathDraft = normalized;
|
|
34406
|
+
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
34407
|
+
this.loadResourceSchemaFieldsIfNeeded(true);
|
|
34408
|
+
}
|
|
34409
|
+
onResourceQuickClear() {
|
|
34410
|
+
this.schemaSubscription?.unsubscribe();
|
|
34411
|
+
this.schemaLoading = false;
|
|
34412
|
+
this.publishBusy();
|
|
34413
|
+
this.resourcePathDraft = '';
|
|
34414
|
+
this.schemaWarning = '';
|
|
34415
|
+
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
34416
|
+
}
|
|
34417
|
+
baselineResourcePath() {
|
|
34418
|
+
if (this.appliedResourcePath !== undefined) {
|
|
34419
|
+
return this.appliedResourcePath;
|
|
33898
34420
|
}
|
|
33899
|
-
|
|
34421
|
+
return String(this.inputs?.resourcePath ?? this.context['resourcePath'] ?? '').trim();
|
|
34422
|
+
}
|
|
34423
|
+
updateDirtyState(editorDirty) {
|
|
34424
|
+
const sourceChanged = this.sourceDraft !== undefined &&
|
|
34425
|
+
this.sourceDraft !==
|
|
34426
|
+
(this.appliedSource ?? this.inputs?.configPersistenceStrategy ?? 'local-first');
|
|
34427
|
+
const resourceChanged = this.resourcePathDraft !== undefined &&
|
|
34428
|
+
this.resourcePathDraft !== this.baselineResourcePath();
|
|
34429
|
+
this.isDirty$.next(editorDirty || sourceChanged || resourceChanged);
|
|
34430
|
+
}
|
|
34431
|
+
publishBusy() {
|
|
34432
|
+
this.isBusy$.next(this.childBusy || this.schemaLoading);
|
|
34433
|
+
}
|
|
33900
34434
|
ngAfterViewInit() {
|
|
33901
34435
|
if (!this.formEditor) {
|
|
33902
34436
|
return;
|
|
@@ -33909,9 +34443,14 @@ class PraxisDynamicFormWidgetConfigEditor {
|
|
|
33909
34443
|
this.initialEditorConfig = structuredClone(parseLegacyOrDynamicFormDocument(this.formEditor.getSettingsValue?.() ?? this.createDocumentFromInputs()).config);
|
|
33910
34444
|
this.subscription.add(this.formEditor.isDirty$.subscribe((value) => this.updateDirtyState(value)));
|
|
33911
34445
|
this.subscription.add(this.formEditor.isValid$.subscribe((value) => this.isValid$.next(value)));
|
|
33912
|
-
this.subscription.add(this.formEditor.isBusy$.subscribe((value) =>
|
|
34446
|
+
this.subscription.add(this.formEditor.isBusy$.subscribe((value) => {
|
|
34447
|
+
this.childBusy = value;
|
|
34448
|
+
this.publishBusy();
|
|
34449
|
+
}));
|
|
34450
|
+
this.loadResourceSchemaFieldsIfNeeded();
|
|
33913
34451
|
}
|
|
33914
34452
|
ngOnDestroy() {
|
|
34453
|
+
this.schemaSubscription?.unsubscribe();
|
|
33915
34454
|
this.subscription.unsubscribe();
|
|
33916
34455
|
}
|
|
33917
34456
|
getSettingsValue() {
|
|
@@ -33925,25 +34464,173 @@ class PraxisDynamicFormWidgetConfigEditor {
|
|
|
33925
34464
|
return;
|
|
33926
34465
|
// The owner acknowledges the current draft only after committing its inputs.
|
|
33927
34466
|
this.appliedSource = this.configurationSource;
|
|
34467
|
+
if (this.resourcePathDraft !== undefined) {
|
|
34468
|
+
this.appliedResourcePath = this.resourcePathDraft;
|
|
34469
|
+
}
|
|
33928
34470
|
this.formEditor?.acceptAppliedValue(this.formEditor.getSettingsValue());
|
|
33929
34471
|
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
33930
34472
|
}
|
|
33931
34473
|
reset() {
|
|
34474
|
+
this.schemaSubscription?.unsubscribe();
|
|
34475
|
+
this.schemaLoading = false;
|
|
34476
|
+
this.publishBusy();
|
|
33932
34477
|
this.sourceDraft = undefined;
|
|
34478
|
+
this.resourcePathDraft = undefined;
|
|
33933
34479
|
this.formEditor?.reset?.();
|
|
33934
34480
|
this.updateDirtyState(this.formEditor?.isDirty$.value ?? false);
|
|
34481
|
+
this.loadResourceSchemaFieldsIfNeeded();
|
|
33935
34482
|
}
|
|
33936
34483
|
initializeChildEditor() {
|
|
33937
34484
|
const document = this.createDocumentFromInputs();
|
|
34485
|
+
this.applyBaselineDocumentToChildEditor(document, 'widget-config-init');
|
|
34486
|
+
}
|
|
34487
|
+
applyBaselineDocumentToChildEditor(document, reason) {
|
|
33938
34488
|
const serialized = serializeDynamicFormAuthoringDocument(document);
|
|
33939
34489
|
const editor = this.formEditor;
|
|
33940
34490
|
editor.onJsonConfigChange?.(serialized);
|
|
33941
34491
|
editor.initialDocument = document;
|
|
33942
34492
|
editor.initialConfig = document.config;
|
|
33943
|
-
editor.updateDirtyState?.(
|
|
34493
|
+
editor.updateDirtyState?.(reason);
|
|
33944
34494
|
editor.isDirty$?.next(false);
|
|
33945
34495
|
editor.isValid$?.next(true);
|
|
33946
34496
|
}
|
|
34497
|
+
loadResourceSchemaFieldsIfNeeded(forceReload = false) {
|
|
34498
|
+
if (!this.formEditor) {
|
|
34499
|
+
return;
|
|
34500
|
+
}
|
|
34501
|
+
const resourcePath = this.effectiveResourcePath;
|
|
34502
|
+
const currentDoc = parseLegacyOrDynamicFormDocument(this.formEditor.getSettingsValue?.() ?? this.createDocumentFromInputs());
|
|
34503
|
+
if (!resourcePath ||
|
|
34504
|
+
(!forceReload &&
|
|
34505
|
+
Array.isArray(currentDoc.config.fieldMetadata) &&
|
|
34506
|
+
currentDoc.config.fieldMetadata.length > 0)) {
|
|
34507
|
+
return;
|
|
34508
|
+
}
|
|
34509
|
+
const isFilterSource = (this.inputs?.schemaSource ?? this.context['schemaSource']) === 'filter';
|
|
34510
|
+
const discoveryOptions = this.buildDiscoveryOptions();
|
|
34511
|
+
const discoveryMethodName = isFilterSource
|
|
34512
|
+
? 'getResourceFilterSchemaFields'
|
|
34513
|
+
: 'getResourceSchemaFields';
|
|
34514
|
+
const canLoadViaDiscovery = typeof this.resourceDiscovery?.[discoveryMethodName] === 'function' &&
|
|
34515
|
+
(typeof this.resourceDiscovery?.canResolveSchemas !== 'function' ||
|
|
34516
|
+
this.resourceDiscovery.canResolveSchemas(discoveryOptions));
|
|
34517
|
+
const canLoadViaCrud = isFilterSource
|
|
34518
|
+
? typeof this.crudService?.getFilterSchema === 'function' ||
|
|
34519
|
+
typeof this.crudService?.getSchema === 'function'
|
|
34520
|
+
: typeof this.crudService?.getSchema === 'function';
|
|
34521
|
+
if (!canLoadViaCrud && !canLoadViaDiscovery) {
|
|
34522
|
+
return;
|
|
34523
|
+
}
|
|
34524
|
+
const loadFromDiscovery = () => {
|
|
34525
|
+
if (isFilterSource) {
|
|
34526
|
+
return discoveryOptions
|
|
34527
|
+
? this.resourceDiscovery.getResourceFilterSchemaFields(resourcePath, discoveryOptions)
|
|
34528
|
+
: this.resourceDiscovery.getResourceFilterSchemaFields(resourcePath);
|
|
34529
|
+
}
|
|
34530
|
+
return discoveryOptions
|
|
34531
|
+
? this.resourceDiscovery.getResourceSchemaFields(resourcePath, discoveryOptions)
|
|
34532
|
+
: this.resourceDiscovery.getResourceSchemaFields(resourcePath);
|
|
34533
|
+
};
|
|
34534
|
+
const loadFromCrud = () => {
|
|
34535
|
+
if (!canLoadViaCrud || !this.crudService) {
|
|
34536
|
+
return of([]);
|
|
34537
|
+
}
|
|
34538
|
+
if (discoveryOptions?.endpointKey || discoveryOptions?.apiUrlEntry) {
|
|
34539
|
+
this.crudService.configure?.(resourcePath, {
|
|
34540
|
+
...(discoveryOptions.endpointKey ? { endpointKey: discoveryOptions.endpointKey } : {}),
|
|
34541
|
+
...(discoveryOptions.apiUrlEntry ? { apiUrlEntry: discoveryOptions.apiUrlEntry } : {}),
|
|
34542
|
+
});
|
|
34543
|
+
}
|
|
34544
|
+
else {
|
|
34545
|
+
this.crudService.configure?.(resourcePath);
|
|
34546
|
+
}
|
|
34547
|
+
if (isFilterSource && typeof this.crudService.getFilterSchema === 'function') {
|
|
34548
|
+
return this.crudService.getFilterSchema();
|
|
34549
|
+
}
|
|
34550
|
+
return this.crudService.getSchema();
|
|
34551
|
+
};
|
|
34552
|
+
const isCatalogResourceKey = !resourcePath.includes('/') && resourcePath.includes('.');
|
|
34553
|
+
let source$;
|
|
34554
|
+
if (isCatalogResourceKey && canLoadViaDiscovery) {
|
|
34555
|
+
source$ = loadFromDiscovery().pipe(defaultIfEmpty([]), switchMap$1((defs) => Array.isArray(defs) && defs.length === 0 && canLoadViaCrud
|
|
34556
|
+
? loadFromCrud()
|
|
34557
|
+
: of(defs)), catchError$1(() => canLoadViaCrud ? loadFromCrud() : of([])));
|
|
34558
|
+
}
|
|
34559
|
+
else if (canLoadViaCrud) {
|
|
34560
|
+
source$ = loadFromCrud().pipe(defaultIfEmpty([]), switchMap$1((defs) => Array.isArray(defs) && defs.length === 0 && canLoadViaDiscovery
|
|
34561
|
+
? loadFromDiscovery()
|
|
34562
|
+
: of(defs)), catchError$1(() => canLoadViaDiscovery ? loadFromDiscovery() : of([])));
|
|
34563
|
+
}
|
|
34564
|
+
else {
|
|
34565
|
+
source$ = loadFromDiscovery();
|
|
34566
|
+
}
|
|
34567
|
+
this.schemaSubscription?.unsubscribe();
|
|
34568
|
+
this.schemaWarning = '';
|
|
34569
|
+
this.schemaLoading = true;
|
|
34570
|
+
this.publishBusy();
|
|
34571
|
+
this.schemaSubscription = source$
|
|
34572
|
+
.pipe(take$1(1), catchError$1(() => of([])), finalize$1(() => {
|
|
34573
|
+
this.schemaLoading = false;
|
|
34574
|
+
this.publishBusy();
|
|
34575
|
+
this.cdr?.markForCheck();
|
|
34576
|
+
}))
|
|
34577
|
+
.subscribe((defs) => {
|
|
34578
|
+
if (!Array.isArray(defs) || defs.length === 0) {
|
|
34579
|
+
this.schemaWarning = `No schema fields were returned for resource "${resourcePath}".`;
|
|
34580
|
+
this.cdr?.markForCheck();
|
|
34581
|
+
return;
|
|
34582
|
+
}
|
|
34583
|
+
if ((!forceReload && this.isDirty$.value) || !this.formEditor) {
|
|
34584
|
+
return;
|
|
34585
|
+
}
|
|
34586
|
+
const fieldMetadata = mapFieldDefinitionsToMetadata(defs);
|
|
34587
|
+
if (!fieldMetadata.length) {
|
|
34588
|
+
this.schemaWarning = `No schema fields were returned for resource "${resourcePath}".`;
|
|
34589
|
+
this.cdr?.markForCheck();
|
|
34590
|
+
return;
|
|
34591
|
+
}
|
|
34592
|
+
const latestDoc = parseLegacyOrDynamicFormDocument(this.formEditor.getSettingsValue?.() ?? currentDoc);
|
|
34593
|
+
if (!forceReload &&
|
|
34594
|
+
Array.isArray(latestDoc.config.fieldMetadata) &&
|
|
34595
|
+
latestDoc.config.fieldMetadata.length > 0) {
|
|
34596
|
+
return;
|
|
34597
|
+
}
|
|
34598
|
+
const newFieldNames = new Set(fieldMetadata.map((f) => f.name));
|
|
34599
|
+
const existingSectionFields = (latestDoc.config.sections ?? []).flatMap((section) => (section.rows ?? []).flatMap((row) => (row.columns ?? []).flatMap((col) => (col.fields ?? []).map((f) => typeof f === 'string' ? f : f?.name ?? '')))).filter(Boolean);
|
|
34600
|
+
const shouldResetSections = forceReload &&
|
|
34601
|
+
existingSectionFields.length > 0 &&
|
|
34602
|
+
!existingSectionFields.some((fieldName) => newFieldNames.has(fieldName));
|
|
34603
|
+
const nextDocument = {
|
|
34604
|
+
...latestDoc,
|
|
34605
|
+
config: {
|
|
34606
|
+
...latestDoc.config,
|
|
34607
|
+
...(shouldResetSections ? { sections: [] } : {}),
|
|
34608
|
+
fieldMetadata,
|
|
34609
|
+
},
|
|
34610
|
+
};
|
|
34611
|
+
this.applyBaselineDocumentToChildEditor(nextDocument, 'widget-config-schema-hydrate');
|
|
34612
|
+
this.cdr?.detectChanges();
|
|
34613
|
+
this.initialEditorConfig = structuredClone(parseLegacyOrDynamicFormDocument(this.formEditor.getSettingsValue?.() ?? nextDocument).config);
|
|
34614
|
+
this.updateDirtyState(this.formEditor.isDirty$.value);
|
|
34615
|
+
this.cdr?.markForCheck();
|
|
34616
|
+
});
|
|
34617
|
+
}
|
|
34618
|
+
buildDiscoveryOptions() {
|
|
34619
|
+
const rawEndpointKey = this.inputs?.apiEndpointKey ??
|
|
34620
|
+
this.inputs?.endpointKey ??
|
|
34621
|
+
this.context['apiEndpointKey'] ??
|
|
34622
|
+
this.context['endpointKey'];
|
|
34623
|
+
const endpointKey = String(rawEndpointKey ?? '').trim();
|
|
34624
|
+
const apiUrlEntry = this.inputs?.apiUrlEntry ??
|
|
34625
|
+
this.context['apiUrlEntry'];
|
|
34626
|
+
if (!endpointKey && !apiUrlEntry) {
|
|
34627
|
+
return undefined;
|
|
34628
|
+
}
|
|
34629
|
+
return {
|
|
34630
|
+
...(endpointKey ? { endpointKey } : {}),
|
|
34631
|
+
...(apiUrlEntry ? { apiUrlEntry } : {}),
|
|
34632
|
+
};
|
|
34633
|
+
}
|
|
33947
34634
|
buildValue(rawDocument) {
|
|
33948
34635
|
const document = parseLegacyOrDynamicFormDocument(rawDocument ?? this.createDocumentFromInputs());
|
|
33949
34636
|
const schemaPrefs = document.contextSnapshot?.schemaPrefs;
|
|
@@ -33955,6 +34642,9 @@ class PraxisDynamicFormWidgetConfigEditor {
|
|
|
33955
34642
|
inputs: {
|
|
33956
34643
|
...(this.inputs ?? {}),
|
|
33957
34644
|
...(this.sourceDraft !== undefined ? { configPersistenceStrategy: this.sourceDraft } : {}),
|
|
34645
|
+
...(this.resourcePathDraft !== undefined
|
|
34646
|
+
? { resourcePath: this.resourcePathDraft || null }
|
|
34647
|
+
: {}),
|
|
33958
34648
|
// An explicit layout edit authors sections; merely inspecting generated layout does not.
|
|
33959
34649
|
...(layoutChanged && layoutPolicy?.source === 'schema'
|
|
33960
34650
|
? { layoutPolicy: { ...layoutPolicy, source: 'authored' } } : {}),
|
|
@@ -34002,7 +34692,7 @@ class PraxisDynamicFormWidgetConfigEditor {
|
|
|
34002
34692
|
});
|
|
34003
34693
|
}
|
|
34004
34694
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisDynamicFormWidgetConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
34005
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
34695
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisDynamicFormWidgetConfigEditor, isStandalone: true, selector: "praxis-dynamic-form-widget-config-editor", inputs: { inputs: "inputs", widgetKey: "widgetKey", appliedValues$: "appliedValues$", context: "context" }, providers: [{ provide: SETTINGS_PANEL_DATA, useValue: null }], viewQueries: [{ propertyName: "formEditor", first: true, predicate: ["formEditor"], descendants: true }], ngImport: i0, template: `
|
|
34006
34696
|
<section data-testid="dynamic-form-widget-config-editor">
|
|
34007
34697
|
<div class="configuration-source">
|
|
34008
34698
|
<mat-form-field appearance="outline" subscriptSizing="dynamic">
|
|
@@ -34014,14 +34704,33 @@ class PraxisDynamicFormWidgetConfigEditor {
|
|
|
34014
34704
|
<mat-hint>{{ formEditor.tx(configurationSource === 'input-first' ? 'config.general.configurationSource.pageHelp' : 'config.general.configurationSource.preferencesHelp') }}</mat-hint>
|
|
34015
34705
|
</mat-form-field>
|
|
34016
34706
|
</div>
|
|
34707
|
+
<div class="resource-quick-connect-host" data-testid="form-widget-resource-quick-connect">
|
|
34708
|
+
<praxis-resource-quick-connect
|
|
34709
|
+
[resourcePath]="effectiveResourcePath"
|
|
34710
|
+
[endpointKey]="effectiveEndpointKey"
|
|
34711
|
+
[apiUrlEntry]="effectiveApiUrlEntry"
|
|
34712
|
+
[intent]="effectiveDiscoveryIntent"
|
|
34713
|
+
(resourcePathChange)="onResourceQuickConnect($event)"
|
|
34714
|
+
></praxis-resource-quick-connect>
|
|
34715
|
+
</div>
|
|
34716
|
+
@if (schemaWarning) {
|
|
34717
|
+
<p class="form-widget-schema-warning" data-testid="form-widget-schema-warning" role="status">
|
|
34718
|
+
{{ schemaWarning }}
|
|
34719
|
+
</p>
|
|
34720
|
+
}
|
|
34017
34721
|
<praxis-dynamic-form-config-editor #formEditor />
|
|
34018
34722
|
<p class="widget-save-hint">{{ formEditor.tx('config.general.pageSaveHint') }}</p>
|
|
34019
34723
|
</section>
|
|
34020
|
-
`, isInline: true, styles: [".configuration-source{padding:var(--p-space-4, 16px)}.configuration-source mat-form-field{width:100%}.widget-save-hint{margin:12px 20px;color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-body-small)}\n"], dependencies: [{ kind: "component", type: PraxisDynamicFormConfigEditor, selector: "praxis-dynamic-form-config-editor" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34724
|
+
`, isInline: true, styles: [".configuration-source{padding:var(--p-space-4, 16px)}.configuration-source mat-form-field{width:100%}.resource-quick-connect-host{padding:0 var(--p-space-4, 16px) var(--p-space-3, 12px)}.form-widget-schema-warning{margin:0 16px 8px;color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-body-small)}.widget-save-hint{margin:12px 20px;color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-body-small)}\n"], dependencies: [{ kind: "component", type: PraxisDynamicFormConfigEditor, selector: "praxis-dynamic-form-config-editor" }, { kind: "component", type: ResourceQuickConnectComponent, selector: "praxis-resource-quick-connect", inputs: ["resourcePath", "endpointKey", "apiUrlEntry", "intent", "resources", "label", "placeholder", "hint", "disabled"], outputs: ["resourcePathChange"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34021
34725
|
}
|
|
34022
34726
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisDynamicFormWidgetConfigEditor, decorators: [{
|
|
34023
34727
|
type: Component,
|
|
34024
|
-
args: [{ providers: [{ provide: SETTINGS_PANEL_DATA, useValue: null }], selector: 'praxis-dynamic-form-widget-config-editor', standalone: true, imports: [
|
|
34728
|
+
args: [{ providers: [{ provide: SETTINGS_PANEL_DATA, useValue: null }], selector: 'praxis-dynamic-form-widget-config-editor', standalone: true, imports: [
|
|
34729
|
+
PraxisDynamicFormConfigEditor,
|
|
34730
|
+
ResourceQuickConnectComponent,
|
|
34731
|
+
MatFormFieldModule,
|
|
34732
|
+
MatSelectModule,
|
|
34733
|
+
], template: `
|
|
34025
34734
|
<section data-testid="dynamic-form-widget-config-editor">
|
|
34026
34735
|
<div class="configuration-source">
|
|
34027
34736
|
<mat-form-field appearance="outline" subscriptSizing="dynamic">
|
|
@@ -34033,10 +34742,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
34033
34742
|
<mat-hint>{{ formEditor.tx(configurationSource === 'input-first' ? 'config.general.configurationSource.pageHelp' : 'config.general.configurationSource.preferencesHelp') }}</mat-hint>
|
|
34034
34743
|
</mat-form-field>
|
|
34035
34744
|
</div>
|
|
34745
|
+
<div class="resource-quick-connect-host" data-testid="form-widget-resource-quick-connect">
|
|
34746
|
+
<praxis-resource-quick-connect
|
|
34747
|
+
[resourcePath]="effectiveResourcePath"
|
|
34748
|
+
[endpointKey]="effectiveEndpointKey"
|
|
34749
|
+
[apiUrlEntry]="effectiveApiUrlEntry"
|
|
34750
|
+
[intent]="effectiveDiscoveryIntent"
|
|
34751
|
+
(resourcePathChange)="onResourceQuickConnect($event)"
|
|
34752
|
+
></praxis-resource-quick-connect>
|
|
34753
|
+
</div>
|
|
34754
|
+
@if (schemaWarning) {
|
|
34755
|
+
<p class="form-widget-schema-warning" data-testid="form-widget-schema-warning" role="status">
|
|
34756
|
+
{{ schemaWarning }}
|
|
34757
|
+
</p>
|
|
34758
|
+
}
|
|
34036
34759
|
<praxis-dynamic-form-config-editor #formEditor />
|
|
34037
34760
|
<p class="widget-save-hint">{{ formEditor.tx('config.general.pageSaveHint') }}</p>
|
|
34038
34761
|
</section>
|
|
34039
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".configuration-source{padding:var(--p-space-4, 16px)}.configuration-source mat-form-field{width:100%}.widget-save-hint{margin:12px 20px;color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-body-small)}\n"] }]
|
|
34762
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".configuration-source{padding:var(--p-space-4, 16px)}.configuration-source mat-form-field{width:100%}.resource-quick-connect-host{padding:0 var(--p-space-4, 16px) var(--p-space-3, 12px)}.form-widget-schema-warning{margin:0 16px 8px;color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-body-small)}.widget-save-hint{margin:12px 20px;color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-body-small)}\n"] }]
|
|
34040
34763
|
}], propDecorators: { inputs: [{
|
|
34041
34764
|
type: Input
|
|
34042
34765
|
}], widgetKey: [{
|
|
@@ -34371,6 +35094,21 @@ const PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA = {
|
|
|
34371
35094
|
context: {
|
|
34372
35095
|
effectiveConfig: materializedInputs?.['config'],
|
|
34373
35096
|
layoutPolicy: materializedInputs?.['layoutPolicy'],
|
|
35097
|
+
...(materializedInputs?.['resourcePath'] !== undefined
|
|
35098
|
+
? { resourcePath: materializedInputs['resourcePath'] }
|
|
35099
|
+
: {}),
|
|
35100
|
+
...(materializedInputs?.['apiEndpointKey'] !== undefined
|
|
35101
|
+
? { apiEndpointKey: materializedInputs['apiEndpointKey'] }
|
|
35102
|
+
: {}),
|
|
35103
|
+
...(materializedInputs?.['endpointKey'] !== undefined
|
|
35104
|
+
? { endpointKey: materializedInputs['endpointKey'] }
|
|
35105
|
+
: {}),
|
|
35106
|
+
...(materializedInputs?.['apiUrlEntry'] !== undefined
|
|
35107
|
+
? { apiUrlEntry: materializedInputs['apiUrlEntry'] }
|
|
35108
|
+
: {}),
|
|
35109
|
+
...(materializedInputs?.['schemaSource'] !== undefined
|
|
35110
|
+
? { schemaSource: materializedInputs['schemaSource'] }
|
|
35111
|
+
: {}),
|
|
34374
35112
|
},
|
|
34375
35113
|
}),
|
|
34376
35114
|
title: 'Configurar formulário',
|