@acorex/platform 20.7.13 → 20.7.15

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.
Files changed (49) hide show
  1. package/common/index.d.ts +10 -1
  2. package/core/index.d.ts +93 -4
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-BwBLG0Hl.mjs → acorex-platform-common-common-settings.provider-gyb6ohAE.mjs} +15 -1
  4. package/fesm2022/acorex-platform-common-common-settings.provider-gyb6ohAE.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +3 -2
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +428 -157
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  10. package/fesm2022/acorex-platform-layout-builder.mjs +90 -74
  11. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  12. package/fesm2022/acorex-platform-layout-components.mjs +954 -1223
  13. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  14. package/fesm2022/acorex-platform-layout-entity.mjs +600 -192
  15. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  16. package/fesm2022/acorex-platform-layout-views.mjs +7 -7
  17. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  18. package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-BfV3spe3.mjs → acorex-platform-layout-widgets-file-list-popup.component-B0omAUil.mjs} +18 -3
  19. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-B0omAUil.mjs.map +1 -0
  20. package/fesm2022/{acorex-platform-layout-widgets-repeater-widget-column.component-DnhR00cH.mjs → acorex-platform-layout-widgets-repeater-widget-column.component-fcCirNxz.mjs} +2 -2
  21. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-fcCirNxz.mjs.map +1 -0
  22. package/fesm2022/acorex-platform-layout-widgets.mjs +153 -37
  23. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  24. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-CJcbkSBF.mjs → acorex-platform-themes-default-entity-master-create-view.component-CCiYPMhz.mjs} +29 -26
  25. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CCiYPMhz.mjs.map +1 -0
  26. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-HBr-ZTSt.mjs → acorex-platform-themes-default-entity-master-list-view.component-BQODc73e.mjs} +2 -2
  27. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-HBr-ZTSt.mjs.map → acorex-platform-themes-default-entity-master-list-view.component-BQODc73e.mjs.map} +1 -1
  28. package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-DAFQ4UI9.mjs → acorex-platform-themes-default-entity-master-modify-view.component-CgLUnYRq.mjs} +3 -4
  29. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-CgLUnYRq.mjs.map +1 -0
  30. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-CwHHYmiK.mjs → acorex-platform-themes-default-entity-master-single-view.component-di5w_3K2.mjs} +4 -4
  31. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-di5w_3K2.mjs.map +1 -0
  32. package/fesm2022/acorex-platform-themes-default.mjs +11 -11
  33. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  34. package/fesm2022/acorex-platform-themes-shared.mjs +244 -246
  35. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  36. package/fesm2022/acorex-platform-workflow.mjs +0 -3
  37. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  38. package/layout/components/index.d.ts +159 -248
  39. package/layout/entity/index.d.ts +42 -1
  40. package/layout/widgets/index.d.ts +41 -3
  41. package/package.json +9 -9
  42. package/themes/shared/index.d.ts +1 -1
  43. package/workflow/index.d.ts +33 -30
  44. package/fesm2022/acorex-platform-common-common-settings.provider-BwBLG0Hl.mjs.map +0 -1
  45. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-BfV3spe3.mjs.map +0 -1
  46. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-DnhR00cH.mjs.map +0 -1
  47. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CJcbkSBF.mjs.map +0 -1
  48. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-DAFQ4UI9.mjs.map +0 -1
  49. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-CwHHYmiK.mjs.map +0 -1
@@ -2043,7 +2043,7 @@ class AXPEntityDetailPopoverComponent {
2043
2043
  return importantProperties;
2044
2044
  }
2045
2045
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityDetailPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2046
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, breadcrumb: { classPropertyName: "breadcrumb", publicName: "breadcrumb", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[400px]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold ax-text-on-lightest-surface\">\n @if (entityDetails()?.entityData?.[textField()]) {\n {{ entityDetails()?.entityData[textField()] }}\n } @else {\n {{ item()?.[textField()] }}\n }\n </h3>\n @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-py-8\">\n <ax-loading>Loading details...</ax-loading>\n </div>\n } @else if (entityDetails()) {\n <div class=\"ax-space-y-3 ax-mb-4\">\n <!-- Important Entity Data -->\n @if (entityDetails()?.entityData) {\n <axp-widgets-container [context]=\"entityDetails()?.entityData\">\n <div class=\"ax-space-y-2\">\n @for (item of getEntityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-flex ax-justify-between ax-items-center\">\n <span class=\"ax-text-sm ax-font-medium\">{{ item.title | translate | async }}:</span>\n <div class=\"ax-flex-1 ax-ml-2 ax-max-w-48\">\n <ng-container axp-widget-renderer [node]=\"item.node\" [mode]=\"'view'\"></ng-container>\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n }\n </div>\n <div class=\"ax-flex ax-gap-2 ax-justify-end ax-sm\">\n <ax-button\n [color]=\"'primary'\"\n [look]=\"'solid'\"\n [text]=\"'@general:actions.open-details.title' | translate | async\"\n (click)=\"navigateToDetails()\"\n >\n </ax-button>\n </div>\n }\n </div>\n</ax-popover>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "forceDisableActionSheetStyle", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i3$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2046
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, breadcrumb: { classPropertyName: "breadcrumb", publicName: "breadcrumb", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[400px]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold ax-text-on-lightest-surface\">\n @if (entityDetails()?.entityData?.[textField()]) {\n {{ entityDetails()?.entityData[textField()] }}\n } @else {\n {{ item()?.[textField()] }}\n }\n </h3>\n @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-py-8\">\n <ax-loading>Loading details...</ax-loading>\n </div>\n } @else if (entityDetails()) {\n <div class=\"ax-space-y-3 ax-mb-4\">\n <!-- Important Entity Data -->\n @if (entityDetails()?.entityData) {\n <axp-widgets-container [context]=\"entityDetails()?.entityData\">\n <div class=\"ax-space-y-2\">\n @for (item of getEntityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-flex ax-justify-between ax-items-center\">\n <span class=\"ax-text-sm ax-font-medium\">{{ item.title | translate | async }}:</span>\n <div class=\"ax-flex-1 ax-ml-2 ax-max-w-48\">\n <ng-container axp-widget-renderer [node]=\"item.node\" [mode]=\"'view'\"></ng-container>\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n }\n </div>\n <div class=\"ax-flex ax-gap-2 ax-justify-end ax-sm\">\n <ax-button\n [color]=\"'primary'\"\n [look]=\"'solid'\"\n [text]=\"'@general:actions.open-details.title' | translate | async\"\n (click)=\"navigateToDetails()\"\n >\n </ax-button>\n </div>\n }\n </div>\n</ax-popover>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i3$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2047
2047
  }
2048
2048
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityDetailPopoverComponent, decorators: [{
2049
2049
  type: Component,
@@ -3864,7 +3864,7 @@ class AXPEntityPerformDeleteAction extends AXPWorkflowAction {
3864
3864
  for await (const entity of entities) {
3865
3865
  const [moduleName, entityName] = entity.source.split('.');
3866
3866
  const entityDefinition = await this.entityRegistery.resolve(moduleName, entityName);
3867
- if (entityDefinition.parentKey && data[entityDefinition.parentKey]) {
3867
+ if (data != null && entityDefinition.parentKey && data[entityDefinition.parentKey]) {
3868
3868
  context.setVariable('meta', { ...meta, refreshTargetId: data[entityDefinition.parentKey] });
3869
3869
  }
3870
3870
  entityDefinitions[entity.source] = entityDefinition;
@@ -6446,7 +6446,6 @@ const AXPCrudModifier = {
6446
6446
  if (!queries?.list) {
6447
6447
  queries.list = {
6448
6448
  execute: async (e) => {
6449
- console.log({ e });
6450
6449
  return await dataService.query(e);
6451
6450
  },
6452
6451
  type: AXPEntityQueryType.List,
@@ -6531,12 +6530,28 @@ function isAXPMiddlewareAbortError(error) {
6531
6530
  const AXP_ENTITY_STORAGE_BACKEND = new InjectionToken('AXP_ENTITY_STORAGE_BACKEND');
6532
6531
  const AXP_ENTITY_STORAGE_MIDDLEWARE = new InjectionToken('AXP_ENTITY_STORAGE_MIDDLEWARE');
6533
6532
 
6533
+ /** In-flight request deduplication - parallel identical read requests share one execution */
6534
+ function getDedupKey(op, entityName, init) {
6535
+ if (op === 'getOne' && init?.id !== undefined) {
6536
+ return `getOne:${entityName}:${String(init.id)}`;
6537
+ }
6538
+ if (op === 'getAll') {
6539
+ return `getAll:${entityName}`;
6540
+ }
6541
+ if (op === 'query' && init?.request) {
6542
+ const r = init.request;
6543
+ return `query:${entityName}:${r.skip ?? 0}:${r.take ?? 0}:${JSON.stringify(r.filter ?? null)}:${JSON.stringify(r.sort ?? null)}:${JSON.stringify(r.params ?? null)}`;
6544
+ }
6545
+ return '';
6546
+ }
6534
6547
  class AXPMiddlewareEntityStorageService extends AXPEntityStorageService {
6535
6548
  constructor() {
6536
6549
  super(...arguments);
6537
6550
  this.backend = inject(AXP_ENTITY_STORAGE_BACKEND);
6538
6551
  this.allMiddlewares = (inject(AXP_ENTITY_STORAGE_MIDDLEWARE, { optional: true }) || []).slice();
6539
6552
  this.injector = inject(EnvironmentInjector);
6553
+ /** In-flight read requests - identical parallel requests share one execution */
6554
+ this.inFlight = new Map();
6540
6555
  }
6541
6556
  get dbName() {
6542
6557
  return this.backend.dbName;
@@ -6595,6 +6610,19 @@ class AXPMiddlewareEntityStorageService extends AXPEntityStorageService {
6595
6610
  return this.run(ctx, () => this.backend.initial(ctx.entityName, ctx.data, options));
6596
6611
  }
6597
6612
  async getOne(entityName, id) {
6613
+ const key = getDedupKey('getOne', entityName, { id });
6614
+ if (key) {
6615
+ const existing = this.inFlight.get(key);
6616
+ if (existing) {
6617
+ return existing;
6618
+ }
6619
+ const promise = this.runWithDedup(key, () => {
6620
+ const ctx = this.createCtx('getOne', entityName, { id });
6621
+ return this.run(ctx, () => this.backend.getOne(ctx.entityName, id));
6622
+ });
6623
+ this.inFlight.set(key, promise);
6624
+ return promise;
6625
+ }
6598
6626
  const ctx = this.createCtx('getOne', entityName, { id });
6599
6627
  return this.run(ctx, () => this.backend.getOne(ctx.entityName, id));
6600
6628
  }
@@ -6611,13 +6639,47 @@ class AXPMiddlewareEntityStorageService extends AXPEntityStorageService {
6611
6639
  return this.run(ctx, () => this.backend.insertOne(ctx.entityName, ctx.data));
6612
6640
  }
6613
6641
  async getAll(entityName) {
6642
+ const key = getDedupKey('getAll', entityName);
6643
+ if (key) {
6644
+ const existing = this.inFlight.get(key);
6645
+ if (existing) {
6646
+ return existing;
6647
+ }
6648
+ const promise = this.runWithDedup(key, () => {
6649
+ const ctx = this.createCtx('getAll', entityName);
6650
+ return this.run(ctx, () => this.backend.getAll(ctx.entityName));
6651
+ });
6652
+ this.inFlight.set(key, promise);
6653
+ return promise;
6654
+ }
6614
6655
  const ctx = this.createCtx('getAll', entityName);
6615
6656
  return this.run(ctx, () => this.backend.getAll(ctx.entityName));
6616
6657
  }
6617
6658
  async query(entityName, request) {
6659
+ const key = getDedupKey('query', entityName, { request });
6660
+ if (key) {
6661
+ const existing = this.inFlight.get(key);
6662
+ if (existing) {
6663
+ return existing;
6664
+ }
6665
+ const promise = this.runWithDedup(key, () => {
6666
+ const ctx = this.createCtx('query', entityName, { request });
6667
+ return this.run(ctx, () => this.backend.query(ctx.entityName, request));
6668
+ });
6669
+ this.inFlight.set(key, promise);
6670
+ return promise;
6671
+ }
6618
6672
  const ctx = this.createCtx('query', entityName, { request });
6619
6673
  return this.run(ctx, () => this.backend.query(ctx.entityName, request));
6620
6674
  }
6675
+ async runWithDedup(key, fn) {
6676
+ try {
6677
+ return await fn();
6678
+ }
6679
+ finally {
6680
+ this.inFlight.delete(key);
6681
+ }
6682
+ }
6621
6683
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPMiddlewareEntityStorageService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
6622
6684
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPMiddlewareEntityStorageService }); }
6623
6685
  }
@@ -6728,6 +6790,42 @@ class AXPCategoryTreeService {
6728
6790
  return null;
6729
6791
  }
6730
6792
  }
6793
+ /**
6794
+ * Fetches a single category item by ID. Uses byKey when available (full item with parent),
6795
+ * otherwise falls back to list query with filter so ancestor chains can be built.
6796
+ */
6797
+ async fetchItemById(id, treeData, config) {
6798
+ const byKey = treeData.categoryEntityDef?.queries?.byKey?.execute;
6799
+ if (typeof byKey === 'function') {
6800
+ try {
6801
+ const item = await byKey(id);
6802
+ return item ?? null;
6803
+ }
6804
+ catch (error) {
6805
+ console.error('Error fetching category by key:', error);
6806
+ return null;
6807
+ }
6808
+ }
6809
+ if (!treeData.categoryEntityQueryFunc)
6810
+ return null;
6811
+ try {
6812
+ const valueField = config.valueField ?? 'id';
6813
+ const event = {
6814
+ ...treeData.basicQueryEvent,
6815
+ filter: {
6816
+ field: valueField,
6817
+ value: id,
6818
+ operator: { type: 'equal' },
6819
+ },
6820
+ };
6821
+ const res = await treeData.categoryEntityQueryFunc(event);
6822
+ return res?.items?.[0] ?? null;
6823
+ }
6824
+ catch (error) {
6825
+ console.error('Error fetching category by id:', error);
6826
+ return null;
6827
+ }
6828
+ }
6731
6829
  /**
6732
6830
  * Load children for a given node
6733
6831
  */
@@ -6812,8 +6910,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
6812
6910
  }] });
6813
6911
 
6814
6912
  class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6913
+ //#endregion
6914
+ //#region ---- Lifecycle Methods ----
6815
6915
  constructor() {
6816
- super(...arguments);
6916
+ super();
6817
6917
  //#region ---- Services & Dependencies ----
6818
6918
  this.categoryTreeService = inject(AXPCategoryTreeService);
6819
6919
  this.translationService = inject(AXTranslationService);
@@ -6826,6 +6926,8 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6826
6926
  this.valueField = signal('id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
6827
6927
  this.allowMultiple = signal(false, ...(ngDevMode ? [{ debugName: "allowMultiple" }] : []));
6828
6928
  this.selectedValues = signal([], ...(ngDevMode ? [{ debugName: "selectedValues" }] : []));
6929
+ /** Optional input for when popup passes selected values via inputs instead of data */
6930
+ this.selectedValuesInput = signal([], ...(ngDevMode ? [{ debugName: "selectedValuesInput" }] : []));
6829
6931
  this.searchPlaceholder = signal('@general:terms.interface.category.search.placeholder', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
6830
6932
  this.excludedNodeId = signal(undefined, ...(ngDevMode ? [{ debugName: "excludedNodeId" }] : [])); // Node ID to disable
6831
6933
  this.searchWithChildren = signal(true, ...(ngDevMode ? [{ debugName: "searchWithChildren" }] : [])); // Include children of search results
@@ -6863,6 +6965,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6863
6965
  this.relevantNodeIds = new Set(); // For search filtering
6864
6966
  this.expandedNodesBeforeSearch = []; // Store expanded nodes before search to restore after
6865
6967
  this.nodesExpandedDuringSearch = []; // Track nodes we expanded during search
6968
+ this.initialExpandSyncDone = false;
6866
6969
  /** Datasource callback for tree-view component. */
6867
6970
  this.datasource = async (id) => {
6868
6971
  if (!this.treeData || !this.treeConfig) {
@@ -6954,9 +7057,22 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6954
7057
  }
6955
7058
  return childNodes;
6956
7059
  };
7060
+ // When popup sets data after ngOnInit, run initial expand/sync when we get selected ids and tree is ready
7061
+ effect(() => {
7062
+ if (this.initialExpandSyncDone)
7063
+ return;
7064
+ const loading = this.loading();
7065
+ const fromData = this.selectedValues().filter((id) => id && id !== 'all');
7066
+ const fromInput = (this.selectedValuesInput() ?? []).filter((id) => id && id !== 'all');
7067
+ const ids = fromData.length > 0 ? fromData : fromInput;
7068
+ if (loading || ids.length === 0 || !this.treeData || !this.treeConfig)
7069
+ return;
7070
+ if (this.selectedNodeIds().length > 0)
7071
+ return; // already set in initializeTree
7072
+ this.initialExpandSyncDone = true;
7073
+ this.runInitialExpandAndSync(ids);
7074
+ });
6957
7075
  }
6958
- //#endregion
6959
- //#region ---- Lifecycle Methods ----
6960
7076
  ngOnInit() {
6961
7077
  super.ngOnInit();
6962
7078
  this.initializeTree();
@@ -6984,15 +7100,14 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6984
7100
  if (this.treeData.categoryEntityDef?.parentKey) {
6985
7101
  this.treeConfig.parentKey = this.treeData.categoryEntityDef.parentKey;
6986
7102
  }
6987
- // CRITICAL FIX: Parse initial selected values and set selectedNodeIds BEFORE tree renders.
6988
- // This allows the datasource callback to mark nodes as selected when they load.
6989
- // Previously, we tried to sync selection while loading was true, but the tree
6990
- // component doesn't exist until loading is false, causing sync methods to fail silently.
6991
- const initialSelectedIds = this.selectedValues().filter((id) => id && id !== 'all');
7103
+ // Resolve initial selected IDs from data (popup) or input binding
7104
+ const fromData = this.selectedValues().filter((id) => id && id !== 'all');
7105
+ const fromInput = (this.selectedValuesInput() ?? []).filter((id) => id && id !== 'all');
7106
+ const initialSelectedIds = fromData.length > 0 ? fromData : fromInput;
6992
7107
  if (initialSelectedIds.length > 0) {
6993
- // Load missing node data into cache first (needed for path calculation and node display)
6994
7108
  await this.loadMissingNodeData(initialSelectedIds);
6995
- // Set selectedNodeIds so datasource callback can use it to mark nodes as selected
7109
+ // Re-fetch any node that has no parent info (batch query may return minimal fields)
7110
+ await this.ensureParentDataInCache(initialSelectedIds);
6996
7111
  this.selectedNodeIds.set(initialSelectedIds);
6997
7112
  }
6998
7113
  }
@@ -7003,27 +7118,56 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
7003
7118
  // Now let tree render by setting loading to false
7004
7119
  this.loading.set(false);
7005
7120
  }
7006
- // AFTER tree renders, perform selection sync operations.
7007
- // This must happen after loading.set(false) so the tree component exists in DOM.
7121
+ // AFTER tree renders: wait for tree to exist, then expand path and sync selection
7008
7122
  const selectedIds = this.selectedNodeIds();
7009
7123
  if (selectedIds.length > 0) {
7010
- try {
7011
- // Wait for tree to render and stabilize
7012
- await new Promise((resolve) => setTimeout(resolve, 150));
7013
- // Build ancestor chains for path expansion (so selected nodes become visible)
7014
- const ancestorChains = await this.buildAncestorChains(selectedIds);
7015
- // Expand ancestor nodes to make selected nodes visible in tree
7016
- await this.expandAncestorNodesInOrder(ancestorChains);
7017
- // Wait for tree to process expansions
7018
- await new Promise((resolve) => setTimeout(resolve, 100));
7019
- // Final sync to ensure all nodes are properly selected in tree component
7020
- await this.syncSelectionWithTree(selectedIds);
7021
- }
7022
- catch (error) {
7023
- console.error('Error syncing selection after tree render:', error);
7024
- }
7124
+ this.initialExpandSyncDone = true;
7125
+ this.runExpandAndSyncWhenTreeReady(selectedIds);
7025
7126
  }
7026
7127
  }
7128
+ /**
7129
+ * Called when popup data arrives after ngOnInit: load data, set selection, expand and sync.
7130
+ */
7131
+ runInitialExpandAndSync(ids) {
7132
+ this.selectedNodeIds.set(ids);
7133
+ Promise.resolve()
7134
+ .then(() => this.loadMissingNodeData(ids))
7135
+ .then(() => this.ensureParentDataInCache(ids))
7136
+ .then(() => {
7137
+ this.runExpandAndSyncWhenTreeReady(ids);
7138
+ })
7139
+ .catch((err) => console.error('Error in runInitialExpandAndSync:', err));
7140
+ }
7141
+ /**
7142
+ * Runs expand path + sync selection once the tree viewChild is available.
7143
+ * Uses retry loop so we don't run before the view has rendered the tree.
7144
+ */
7145
+ runExpandAndSyncWhenTreeReady(selectedIds) {
7146
+ const maxWaitMs = 3500;
7147
+ const pollMs = 80;
7148
+ const start = Date.now();
7149
+ const run = async () => {
7150
+ while (Date.now() - start < maxWaitMs) {
7151
+ const treeComponent = this.tree();
7152
+ if (treeComponent) {
7153
+ try {
7154
+ await new Promise((resolve) => setTimeout(resolve, 320));
7155
+ const ancestorChains = await this.buildAncestorChains(selectedIds);
7156
+ await this.expandAncestorNodesInOrder(ancestorChains);
7157
+ await new Promise((resolve) => setTimeout(resolve, 120));
7158
+ await this.syncSelectionWithTree(selectedIds);
7159
+ }
7160
+ catch (error) {
7161
+ console.error('Error syncing selection after tree render:', error);
7162
+ }
7163
+ this.changeDetectorRef.markForCheck();
7164
+ return;
7165
+ }
7166
+ await new Promise((resolve) => setTimeout(resolve, pollMs));
7167
+ }
7168
+ };
7169
+ run();
7170
+ }
7027
7171
  //#endregion
7028
7172
  //#region ---- Public Methods ----
7029
7173
  /**
@@ -7298,29 +7442,13 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
7298
7442
  }
7299
7443
  }
7300
7444
  /**
7301
- * Fetches a single item by ID from the server
7445
+ * Fetches a single item by ID. Uses category tree service so byKey is used when available
7446
+ * (returns full item with parent/parentId for building ancestor chains).
7302
7447
  */
7303
7448
  async fetchItemById(id) {
7304
- if (!this.treeData?.categoryEntityQueryFunc) {
7305
- return null;
7306
- }
7307
- try {
7308
- const valueField = this.treeConfig?.valueField || 'id';
7309
- const event = {
7310
- ...this.treeData.basicQueryEvent,
7311
- filter: {
7312
- field: valueField,
7313
- value: id,
7314
- operator: { type: 'equal' },
7315
- },
7316
- };
7317
- const res = await this.treeData.categoryEntityQueryFunc(event);
7318
- return res?.items?.[0] ?? null;
7319
- }
7320
- catch (error) {
7321
- console.error('Error fetching item by ID:', error);
7449
+ if (!this.treeData || !this.treeConfig)
7322
7450
  return null;
7323
- }
7451
+ return this.categoryTreeService.fetchItemById(id, this.treeData, this.treeConfig);
7324
7452
  }
7325
7453
  /**
7326
7454
  * Sorts parent IDs by their depth (root first, leaves last)
@@ -7357,7 +7485,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
7357
7485
  break;
7358
7486
  }
7359
7487
  itemCache.set(currentId, item);
7360
- currentId = item[parentKey] ? String(item[parentKey]) : null;
7488
+ currentId = this.getParentIdFromNodeData(item);
7361
7489
  }
7362
7490
  if (missingIds.size === 0 || !missingIds.has(parentId)) {
7363
7491
  depthMap.set(parentId, depth);
@@ -7387,7 +7515,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
7387
7515
  depth++;
7388
7516
  const item = this.nodeDataCache.get(currentId) || itemCache.get(currentId);
7389
7517
  if (item) {
7390
- currentId = item[parentKey] ? String(item[parentKey]) : null;
7518
+ currentId = this.getParentIdFromNodeData(item);
7391
7519
  }
7392
7520
  else {
7393
7521
  break;
@@ -7836,35 +7964,33 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
7836
7964
  */
7837
7965
  async buildAncestorChains(selectedIds) {
7838
7966
  const ancestorChains = new Map();
7839
- if (!this.treeData || !this.treeConfig) {
7967
+ if (!this.treeData || !this.treeConfig)
7840
7968
  return ancestorChains;
7841
- }
7842
- const parentKey = this.treeData.categoryEntityDef?.parentKey;
7843
- if (!parentKey) {
7844
- // No parent key means flat structure
7969
+ if (!this.treeData.categoryEntityDef?.parentKey)
7845
7970
  return ancestorChains;
7846
- }
7847
7971
  for (const nodeId of selectedIds) {
7848
7972
  const chain = [];
7849
- let currentData = this.nodeDataCache.get(nodeId);
7850
- if (!currentData) {
7973
+ let currentData = this.nodeDataCache.get(nodeId) ?? null;
7974
+ if (!currentData)
7851
7975
  continue;
7976
+ if (!this.getParentIdFromNodeData(currentData)) {
7977
+ const refetched = await this.fetchItemById(nodeId);
7978
+ if (refetched) {
7979
+ currentData = refetched;
7980
+ this.nodeDataCache.set(nodeId, refetched);
7981
+ }
7852
7982
  }
7853
- // Traverse up to root, collecting ancestor IDs
7854
7983
  const visited = new Set();
7855
7984
  while (currentData) {
7856
- const parentId = currentData[parentKey];
7857
- if (!parentId || parentId === 'all' || visited.has(String(parentId))) {
7985
+ const parentId = this.getParentIdFromNodeData(currentData);
7986
+ if (!parentId || visited.has(parentId))
7858
7987
  break;
7859
- }
7860
- const parentIdStr = String(parentId);
7861
- visited.add(parentIdStr);
7862
- chain.unshift(parentIdStr); // Add to beginning (root first)
7863
- // Load parent data if not cached
7864
- if (!this.nodeDataCache.has(parentIdStr)) {
7865
- const parentData = await this.fetchItemById(parentIdStr);
7988
+ visited.add(parentId);
7989
+ chain.unshift(parentId);
7990
+ if (!this.nodeDataCache.has(parentId)) {
7991
+ const parentData = await this.fetchItemById(parentId);
7866
7992
  if (parentData) {
7867
- this.nodeDataCache.set(parentIdStr, parentData);
7993
+ this.nodeDataCache.set(parentId, parentData);
7868
7994
  currentData = parentData;
7869
7995
  }
7870
7996
  else {
@@ -7872,7 +7998,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
7872
7998
  }
7873
7999
  }
7874
8000
  else {
7875
- currentData = this.nodeDataCache.get(parentIdStr);
8001
+ currentData = this.nodeDataCache.get(parentId) ?? null;
7876
8002
  }
7877
8003
  }
7878
8004
  ancestorChains.set(nodeId, chain);
@@ -7905,30 +8031,41 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
7905
8031
  const sortedAncestors = Array.from(allAncestors).sort((a, b) => {
7906
8032
  return (ancestorDepths.get(a) ?? 0) - (ancestorDepths.get(b) ?? 0);
7907
8033
  });
7908
- // First expand root node if not expanded
7909
8034
  try {
7910
8035
  if (!treeComponent.isNodeExpanded('all')) {
7911
8036
  await treeComponent.expandNode('all');
7912
- await new Promise((resolve) => setTimeout(resolve, 50));
8037
+ await this.waitForNodeExpanded(treeComponent, 'all');
7913
8038
  }
7914
8039
  }
7915
8040
  catch {
7916
8041
  // Root might not exist
7917
8042
  }
7918
- // Expand ancestors in order - this loads them into the tree via datasource callback
7919
8043
  for (const ancestorId of sortedAncestors) {
7920
8044
  try {
7921
8045
  if (!treeComponent.isNodeExpanded(ancestorId)) {
7922
8046
  await treeComponent.expandNode(ancestorId);
7923
- // Small delay to allow tree to process the expansion
7924
- await new Promise((resolve) => setTimeout(resolve, 30));
8047
+ await this.waitForNodeExpanded(treeComponent, ancestorId);
7925
8048
  }
7926
8049
  }
7927
8050
  catch {
7928
- // Ancestor might not be in tree yet, continue
8051
+ // Ancestor might not be in tree yet
7929
8052
  }
7930
8053
  }
7931
8054
  }
8055
+ /** Waits until the node is expanded (tree has loaded children). Polls so deep paths load. */
8056
+ async waitForNodeExpanded(treeComponent, nodeId, maxWaitMs = 2800, pollMs = 60) {
8057
+ const deadline = Date.now() + maxWaitMs;
8058
+ while (Date.now() < deadline) {
8059
+ try {
8060
+ if (treeComponent.isNodeExpanded(nodeId))
8061
+ return;
8062
+ }
8063
+ catch {
8064
+ // Node may not exist yet
8065
+ }
8066
+ await new Promise((r) => setTimeout(r, pollMs));
8067
+ }
8068
+ }
7932
8069
  /**
7933
8070
  * Syncs selection state with the tree component.
7934
8071
  * Selects leaf nodes and manually updates parent states (indeterminate/selected).
@@ -8014,15 +8151,13 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8014
8151
  if (!nodeData) {
8015
8152
  continue;
8016
8153
  }
8017
- // Traverse up the parent chain
8018
8154
  let currentData = nodeData;
8019
8155
  while (currentData) {
8020
- const parentId = currentData[parentKey];
8021
- if (!parentId || parentId === 'all') {
8156
+ const parentId = this.getParentIdFromNodeData(currentData);
8157
+ if (!parentId)
8022
8158
  break;
8023
- }
8024
- parentsToUpdate.add(String(parentId));
8025
- currentData = this.nodeDataCache.get(String(parentId));
8159
+ parentsToUpdate.add(parentId);
8160
+ currentData = this.nodeDataCache.get(parentId);
8026
8161
  }
8027
8162
  }
8028
8163
  // Sort parents by depth (deepest first so we update bottom-up)
@@ -8114,44 +8249,57 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8114
8249
  if (!this.treeData || !this.treeConfig || selectedIds.length === 0) {
8115
8250
  return;
8116
8251
  }
8117
- // Find IDs that are selected but not in cache
8118
8252
  const missingIds = selectedIds.filter((id) => !this.nodeDataCache.has(id));
8119
- if (missingIds.length === 0) {
8253
+ if (missingIds.length === 0)
8120
8254
  return;
8121
- }
8122
8255
  try {
8256
+ // Prefer fetching each id via service (uses byKey when available = full item with parent)
8123
8257
  const valueField = this.treeConfig.valueField || 'id';
8124
- const categoryEntityQueryFunc = this.treeData.categoryEntityQueryFunc;
8125
- if (!categoryEntityQueryFunc) {
8126
- return;
8127
- }
8128
- // Query for missing nodes by their IDs
8129
- const event = {
8130
- ...this.treeData.basicQueryEvent,
8131
- filter: {
8132
- filters: missingIds.map((id) => ({
8133
- field: valueField,
8134
- value: id,
8135
- operator: { type: 'equal' },
8136
- })),
8137
- logic: 'or',
8138
- },
8139
- };
8140
- const res = await categoryEntityQueryFunc(event);
8141
- if (res?.items) {
8142
- // Cache the fetched node data
8143
- res.items.forEach((item) => {
8144
- const itemId = String(item[valueField] ?? '');
8145
- if (itemId) {
8146
- this.nodeDataCache.set(itemId, item);
8147
- }
8148
- });
8258
+ for (const id of missingIds) {
8259
+ const item = await this.fetchItemById(id);
8260
+ if (item)
8261
+ this.nodeDataCache.set(String(item[valueField] ?? id), item);
8149
8262
  }
8150
8263
  }
8151
8264
  catch (error) {
8152
8265
  console.error('Error loading missing node data:', error);
8153
8266
  }
8154
8267
  }
8268
+ /**
8269
+ * For each selected id, if cached item has no parent (e.g. batch query returned minimal fields),
8270
+ * re-fetch by id so we have parent/parentId for building ancestor chains.
8271
+ */
8272
+ async ensureParentDataInCache(selectedIds) {
8273
+ if (!this.treeConfig)
8274
+ return;
8275
+ for (const id of selectedIds) {
8276
+ const cached = this.nodeDataCache.get(id);
8277
+ if (cached && this.getParentIdFromNodeData(cached) === null) {
8278
+ const full = await this.fetchItemById(id);
8279
+ if (full)
8280
+ this.nodeDataCache.set(id, full);
8281
+ }
8282
+ }
8283
+ }
8284
+ /**
8285
+ * Resolves parent ID from node data: supports nested `parent` object or flat parentId/parentKey.
8286
+ */
8287
+ getParentIdFromNodeData(item) {
8288
+ if (!item || !this.treeConfig)
8289
+ return null;
8290
+ const valueField = this.treeConfig.valueField || 'id';
8291
+ const parentKey = this.treeData?.categoryEntityDef?.parentKey || 'parentId';
8292
+ const parent = item['parent'];
8293
+ if (parent && typeof parent === 'object') {
8294
+ const id = String(parent[valueField] ?? parent['id'] ?? '');
8295
+ return id && id !== 'all' ? id : null;
8296
+ }
8297
+ const parentIdValue = item[parentKey] ?? item['parentId'];
8298
+ if (parentIdValue == null || parentIdValue === '')
8299
+ return null;
8300
+ const id = String(parentIdValue);
8301
+ return id && id !== 'all' ? id : null;
8302
+ }
8155
8303
  /**
8156
8304
  * Marks nodes as selected in the tree structure based on selectedNodeIds.
8157
8305
  * This ensures pre-selected nodes appear selected when the tree is rendered.
@@ -8337,29 +8485,18 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8337
8485
  while (currentId && currentData && !visitedIds.has(currentId)) {
8338
8486
  visitedIds.add(currentId);
8339
8487
  const title = this.getNodeTitle(currentData);
8340
- if (title) {
8488
+ if (title)
8341
8489
  path.unshift(title);
8342
- }
8343
- const parentId = currentData[parentKey];
8344
- if (!parentId || parentId === 'all' || parentId === currentId) {
8490
+ const parentId = this.getParentIdFromNodeData(currentData);
8491
+ if (!parentId || parentId === currentId)
8345
8492
  break;
8346
- }
8347
- let parentData = this.nodeDataCache.get(String(parentId));
8493
+ let parentData = this.nodeDataCache.get(parentId);
8348
8494
  if (!parentData && this.treeData.categoryEntityQueryFunc) {
8349
8495
  try {
8350
- const valueField = this.treeConfig.valueField || 'id';
8351
- const event = {
8352
- ...this.treeData.basicQueryEvent,
8353
- filter: {
8354
- field: valueField,
8355
- value: parentId,
8356
- operator: { type: 'equal' },
8357
- },
8358
- };
8359
- const res = await this.treeData.categoryEntityQueryFunc(event);
8360
- if (res?.items && res.items.length > 0) {
8361
- parentData = res.items[0];
8362
- this.nodeDataCache.set(String(parentId), parentData);
8496
+ const fetched = await this.fetchItemById(parentId);
8497
+ if (fetched) {
8498
+ parentData = fetched;
8499
+ this.nodeDataCache.set(parentId, fetched);
8363
8500
  }
8364
8501
  }
8365
8502
  catch (error) {
@@ -8367,10 +8504,9 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8367
8504
  break;
8368
8505
  }
8369
8506
  }
8370
- if (!parentData) {
8507
+ if (!parentData)
8371
8508
  break;
8372
- }
8373
- currentId = String(parentId);
8509
+ currentId = parentId;
8374
8510
  currentData = parentData;
8375
8511
  }
8376
8512
  return path.length > 0 ? path : this.getNodeTitle(nodeData) ? [this.getNodeTitle(nodeData)] : [];
@@ -8434,7 +8570,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8434
8570
  }));
8435
8571
  return items.filter((item) => item != null);
8436
8572
  }
8437
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityCategoryTreeSelectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
8573
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityCategoryTreeSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8438
8574
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPEntityCategoryTreeSelectorComponent, isStandalone: true, selector: "axp-entity-category-tree-selector", viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
8439
8575
  <div class="ax-flex ax-flex-col ax-h-full">
8440
8576
  @if (loading()) {
@@ -8703,7 +8839,211 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
8703
8839
  FormsModule,
8704
8840
  ],
8705
8841
  }]
8706
- }], propDecorators: { tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }] } });
8842
+ }], ctorParameters: () => [], propDecorators: { tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }] } });
8843
+
8844
+ /**
8845
+ * Batch-resolves category data for entity-category-widget-column.
8846
+ * Only fetches when the column is visible. Returns full path for hierarchical display (e.g. "… > Sales").
8847
+ */
8848
+ class AXPEntityCategoryBatchResolverService {
8849
+ constructor() {
8850
+ this.entityResolver = inject(AXPEntityDefinitionRegistryService);
8851
+ this.entityStorage = inject(AXPEntityStorageService);
8852
+ this.cache = new Map();
8853
+ this.pendingByEntity = new Map();
8854
+ this.flushScheduled = false;
8855
+ }
8856
+ async resolve(entityKey, ids, valueField, textField, parentKey) {
8857
+ const uniqueIds = Array.from(new Set(ids.filter((id) => id != null && id !== '')));
8858
+ if (uniqueIds.length === 0)
8859
+ return new Map();
8860
+ const cached = this.getFromCache(entityKey, uniqueIds, valueField);
8861
+ if (cached.size === uniqueIds.length)
8862
+ return cached;
8863
+ return new Promise((resolve, reject) => {
8864
+ const list = this.pendingByEntity.get(entityKey) ?? [];
8865
+ list.push({
8866
+ ids: uniqueIds,
8867
+ valueField,
8868
+ textField,
8869
+ parentKey,
8870
+ resolve: (m) => {
8871
+ const subset = new Map();
8872
+ for (const id of uniqueIds) {
8873
+ const r = m.get(id);
8874
+ if (r)
8875
+ subset.set(id, r);
8876
+ }
8877
+ resolve(subset);
8878
+ },
8879
+ reject,
8880
+ });
8881
+ this.pendingByEntity.set(entityKey, list);
8882
+ this.scheduleFlush();
8883
+ });
8884
+ }
8885
+ getFromCache(entityKey, ids, valueField) {
8886
+ const entityCache = this.cache.get(entityKey);
8887
+ if (!entityCache)
8888
+ return new Map();
8889
+ const result = new Map();
8890
+ for (const id of ids) {
8891
+ const cached = entityCache.get(id);
8892
+ if (cached)
8893
+ result.set(id, cached);
8894
+ }
8895
+ return result;
8896
+ }
8897
+ setCache(entityKey, items, valueField) {
8898
+ let entityCache = this.cache.get(entityKey);
8899
+ if (!entityCache) {
8900
+ entityCache = new Map();
8901
+ this.cache.set(entityKey, entityCache);
8902
+ }
8903
+ for (const item of items) {
8904
+ entityCache.set(item.id, item);
8905
+ }
8906
+ }
8907
+ scheduleFlush() {
8908
+ if (this.flushScheduled)
8909
+ return;
8910
+ this.flushScheduled = true;
8911
+ queueMicrotask(() => {
8912
+ this.flushScheduled = false;
8913
+ this.flushAll();
8914
+ });
8915
+ }
8916
+ async flushAll() {
8917
+ const entities = Array.from(this.pendingByEntity.keys());
8918
+ for (const entityKey of entities) {
8919
+ await this.flush(entityKey);
8920
+ }
8921
+ }
8922
+ async flush(entityKey) {
8923
+ const list = this.pendingByEntity.get(entityKey);
8924
+ if (!list?.length)
8925
+ return;
8926
+ this.pendingByEntity.delete(entityKey);
8927
+ const first = list[0];
8928
+ const { valueField, textField, parentKey } = first;
8929
+ const allIds = Array.from(new Set(list.flatMap((r) => r.ids)));
8930
+ try {
8931
+ const result = await this.fetchAndResolve(entityKey, allIds, valueField, textField, parentKey);
8932
+ for (const req of list) {
8933
+ const subset = new Map();
8934
+ for (const id of req.ids) {
8935
+ const r = result.get(id);
8936
+ if (r)
8937
+ subset.set(id, r);
8938
+ }
8939
+ req.resolve(subset);
8940
+ }
8941
+ }
8942
+ catch (err) {
8943
+ for (const req of list)
8944
+ req.reject(err);
8945
+ }
8946
+ }
8947
+ async fetchAndResolve(entityKey, ids, valueField, textField, parentKey) {
8948
+ const [moduleName, entityName] = entityKey.split('.');
8949
+ if (!moduleName || !entityName)
8950
+ return new Map();
8951
+ try {
8952
+ let items;
8953
+ let allItems;
8954
+ if (parentKey) {
8955
+ // Use storage directly to fetch ALL categories. The entity's list execute adds
8956
+ // a default filter (parentId isEmpty) that returns only roots - we need the full tree.
8957
+ const allResult = await this.entityStorage.query(entityKey, {
8958
+ skip: 0,
8959
+ take: 10000,
8960
+ });
8961
+ allItems = allResult?.items ?? [];
8962
+ items = allItems.filter((c) => ids.includes(String(get(c, valueField))));
8963
+ }
8964
+ else {
8965
+ const entityDef = await this.entityResolver.resolve(moduleName, entityName);
8966
+ const listExecute = entityDef?.queries?.list?.execute;
8967
+ if (!listExecute || typeof listExecute !== 'function')
8968
+ return new Map();
8969
+ const result = await listExecute({
8970
+ skip: 0,
8971
+ take: ids.length + 100,
8972
+ filter: { field: valueField, operator: { type: 'in' }, value: ids },
8973
+ });
8974
+ items = result?.items ?? [];
8975
+ allItems = items;
8976
+ }
8977
+ const pathMap = parentKey
8978
+ ? this.buildPathMap(allItems, valueField, textField, parentKey)
8979
+ : null;
8980
+ const result = new Map();
8981
+ for (const item of items) {
8982
+ const id = String(get(item, valueField));
8983
+ const title = get(item, textField);
8984
+ const path = pathMap
8985
+ ? pathMap.get(id) ?? (title != null ? [String(title)] : [])
8986
+ : title != null && title !== ''
8987
+ ? [String(title)]
8988
+ : [];
8989
+ result.set(id, {
8990
+ id,
8991
+ title: title != null ? String(title) : '',
8992
+ path,
8993
+ });
8994
+ }
8995
+ this.setCache(entityKey, Array.from(result.values()), valueField);
8996
+ return result;
8997
+ }
8998
+ catch (error) {
8999
+ console.error('[EntityCategoryBatchResolver] Failed:', error);
9000
+ return new Map();
9001
+ }
9002
+ }
9003
+ buildPathMap(items, valueField, textField, parentKey) {
9004
+ const map = new Map();
9005
+ for (const item of items) {
9006
+ map.set(String(get(item, valueField)), item);
9007
+ }
9008
+ const pathMap = new Map();
9009
+ const visited = new Set();
9010
+ const buildPath = (id) => {
9011
+ if (visited.has(id))
9012
+ return pathMap.get(id) ?? [];
9013
+ const item = map.get(id);
9014
+ if (!item)
9015
+ return [];
9016
+ const parentId = get(item, parentKey);
9017
+ const title = String(get(item, textField) ?? '');
9018
+ if (parentId == null ||
9019
+ parentId === '' ||
9020
+ parentId === 'all' ||
9021
+ String(parentId) === id) {
9022
+ const path = title ? [title] : [];
9023
+ pathMap.set(id, path);
9024
+ visited.add(id);
9025
+ return path;
9026
+ }
9027
+ const parentPath = buildPath(String(parentId));
9028
+ const path = title ? [...parentPath, title] : parentPath;
9029
+ pathMap.set(id, path);
9030
+ visited.add(id);
9031
+ return path;
9032
+ };
9033
+ for (const item of items) {
9034
+ const id = String(get(item, valueField));
9035
+ if (!pathMap.has(id))
9036
+ buildPath(id);
9037
+ }
9038
+ return pathMap;
9039
+ }
9040
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityCategoryBatchResolverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
9041
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityCategoryBatchResolverService, providedIn: 'root' }); }
9042
+ }
9043
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityCategoryBatchResolverService, decorators: [{
9044
+ type: Injectable,
9045
+ args: [{ providedIn: 'root' }]
9046
+ }] });
8707
9047
 
8708
9048
  class AXPEntityCategoryWidgetColumnComponent extends AXPColumnWidgetComponent {
8709
9049
  constructor() {
@@ -8712,6 +9052,7 @@ class AXPEntityCategoryWidgetColumnComponent extends AXPColumnWidgetComponent {
8712
9052
  this.entityDetailPopoverService = inject(AXPEntityDetailPopoverService);
8713
9053
  this.formatService = inject(AXFormatService);
8714
9054
  this.entityResolver = inject(AXPEntityDefinitionRegistryService);
9055
+ this.categoryBatchResolver = inject(AXPEntityCategoryBatchResolverService);
8715
9056
  //#endregion
8716
9057
  //#region ---- Inputs ----
8717
9058
  this.rawValueSignal = signal(null, ...(ngDevMode ? [{ debugName: "rawValueSignal" }] : []));
@@ -8763,7 +9104,7 @@ class AXPEntityCategoryWidgetColumnComponent extends AXPColumnWidgetComponent {
8763
9104
  }
8764
9105
  else {
8765
9106
  const items = castArray(value);
8766
- const itemsWithPaths = await Promise.all(items.map((item) => this.extractItemWithPath(item)));
9107
+ const itemsWithPaths = await this.resolveItemsWithPaths(items);
8767
9108
  this.displayItems.set(itemsWithPaths.filter((c) => c != null));
8768
9109
  }
8769
9110
  }, ...(ngDevMode ? [{ debugName: "efDisplay" }] : []));
@@ -8852,55 +9193,109 @@ class AXPEntityCategoryWidgetColumnComponent extends AXPColumnWidgetComponent {
8852
9193
  }
8853
9194
  //#endregion
8854
9195
  //#region ---- Private Methods ----
8855
- async extractItemWithPath(item) {
8856
- if (isNil(item)) {
8857
- return null;
9196
+ async resolveItemsWithPaths(items) {
9197
+ const entityKey = this.entity();
9198
+ const valueField = this.valueField();
9199
+ const textField = this.displayField();
9200
+ const parentKey = this.entityDef()?.parentKey ?? (entityKey?.endsWith('Category') ? 'parentId' : undefined);
9201
+ // When record has categories: [{id, parentId, title}] from middleware, build path from chain
9202
+ const hasFullChain = items.some((item) => item &&
9203
+ typeof item === 'object' &&
9204
+ get(item, 'parentId') != null &&
9205
+ get(item, valueField) != null);
9206
+ if (hasFullChain && items.length > 0) {
9207
+ return items
9208
+ .filter((item) => item != null)
9209
+ .map((item) => this.buildItemWithPathFromChain(item, items, valueField, textField, parentKey ?? 'parentId'));
9210
+ }
9211
+ const needsResolve = items.filter((item) => {
9212
+ if (isNil(item))
9213
+ return false;
9214
+ if (typeof item !== 'object')
9215
+ return true;
9216
+ const idVal = get(item, valueField);
9217
+ const hasPath = item.path && Array.isArray(item.path) && item.path.length > 0;
9218
+ return !hasPath && idVal != null && idVal !== '';
9219
+ });
9220
+ const idsToResolve = needsResolve.map((item) => typeof item === 'object' ? String(get(item, valueField)) : String(item));
9221
+ let resolvedMap = new Map();
9222
+ if (idsToResolve.length > 0 && entityKey) {
9223
+ resolvedMap = await this.categoryBatchResolver.resolve(entityKey, idsToResolve, valueField, textField, parentKey);
9224
+ }
9225
+ return Promise.all(items.map((item) => this.extractItemWithPath(item, resolvedMap)));
9226
+ }
9227
+ /**
9228
+ * Build path from categories array when record has full chain (id, parentId, title).
9229
+ * Used when middleware enriches categories on create/update.
9230
+ */
9231
+ buildItemWithPathFromChain(item, allItems, valueField, textField, parentKey) {
9232
+ const map = new Map();
9233
+ for (const c of allItems) {
9234
+ const id = get(c, valueField);
9235
+ if (id != null)
9236
+ map.set(String(id), c);
9237
+ }
9238
+ const path = [];
9239
+ let current = item;
9240
+ const visited = new Set();
9241
+ while (current) {
9242
+ const id = String(get(current, valueField) ?? '');
9243
+ if (!id || visited.has(id))
9244
+ break;
9245
+ visited.add(id);
9246
+ const title = String(get(current, textField) ?? '');
9247
+ if (title)
9248
+ path.unshift(title);
9249
+ const parentId = get(current, parentKey);
9250
+ if (parentId == null || parentId === '' || String(parentId) === id)
9251
+ break;
9252
+ current = map.get(String(parentId));
8858
9253
  }
9254
+ const pathArray = path.length > 0 ? path : get(item, textField) ? [String(get(item, textField))] : [];
9255
+ return {
9256
+ [valueField]: get(item, valueField),
9257
+ [textField]: get(item, textField),
9258
+ path: pathArray,
9259
+ };
9260
+ }
9261
+ async extractItemWithPath(item, resolvedMap) {
9262
+ if (isNil(item))
9263
+ return null;
9264
+ const valueField = this.valueField();
9265
+ const textField = this.displayField();
8859
9266
  let itemObj;
8860
9267
  if (typeof item === 'object') {
8861
9268
  itemObj = item;
8862
- // If the provided object lacks a computed path, try to enrich it by fetching the full record
8863
- // so we have access to parent information for accurate path computation.
8864
- if (!itemObj.path || !Array.isArray(itemObj.path) || itemObj.path.length === 0) {
8865
- const byKey = this.entityDef()?.queries?.byKey?.execute;
8866
- const idVal = get(itemObj, this.valueField());
8867
- if (byKey && idVal != null && idVal !== '') {
8868
- try {
8869
- const fetched = await byKey(idVal);
8870
- if (fetched) {
8871
- itemObj = fetched;
8872
- }
8873
- }
8874
- catch (error) {
8875
- console.error('Error fetching full item for path calculation:', error);
8876
- }
9269
+ const idVal = get(itemObj, valueField);
9270
+ const hasPath = itemObj.path && Array.isArray(itemObj.path) && itemObj.path.length > 0;
9271
+ if (!hasPath && resolvedMap && idVal != null && idVal !== '') {
9272
+ const r = resolvedMap.get(String(idVal));
9273
+ if (r) {
9274
+ itemObj = {
9275
+ [valueField]: r.id,
9276
+ [textField]: r.title,
9277
+ path: r.path,
9278
+ };
8877
9279
  }
8878
9280
  }
8879
9281
  }
8880
9282
  else {
8881
- // If item is just an ID, fetch the full item
8882
- const byKey = this.entityDef()?.queries?.byKey?.execute;
8883
- if (byKey) {
8884
- try {
8885
- itemObj = await byKey(item);
9283
+ if (resolvedMap) {
9284
+ const r = resolvedMap.get(String(item));
9285
+ if (r) {
9286
+ itemObj = { [valueField]: r.id, [textField]: r.title, path: r.path };
8886
9287
  }
8887
- catch (error) {
8888
- console.error('Error fetching item:', error);
8889
- return null;
9288
+ else {
9289
+ itemObj = { [valueField]: item, [textField]: item, path: [String(item)] };
8890
9290
  }
8891
9291
  }
8892
9292
  else {
8893
- itemObj = {
8894
- [this.valueField()]: item,
8895
- [this.textField()]: item,
8896
- };
9293
+ itemObj = { [valueField]: item, [textField]: item, path: [String(item)] };
8897
9294
  }
8898
9295
  }
8899
- // If item already has a path array, return it as is
8900
9296
  if (itemObj.path && Array.isArray(itemObj.path) && itemObj.path.length > 0) {
8901
9297
  return itemObj;
8902
9298
  }
8903
- // Calculate path for the item
8904
9299
  return await this.calculateItemPath(itemObj);
8905
9300
  }
8906
9301
  /**
@@ -9460,6 +9855,7 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
9460
9855
  selectedValues: signal(selectedIds),
9461
9856
  searchPlaceholder: signal(this.searchPlaceholderText()),
9462
9857
  excludedNodeId: signal(excludedNodeId),
9858
+ selectedValuesInput: signal(selectedIds),
9463
9859
  },
9464
9860
  });
9465
9861
  if (result?.data?.selected && Array.isArray(result.data.selected)) {
@@ -10208,6 +10604,11 @@ class AXPEntityListTableService {
10208
10604
  parentField: entity.parentKey,
10209
10605
  fetchDataMode: 'manual',
10210
10606
  minHeight: 250,
10607
+ // ⏳ Loading Configuration
10608
+ loading: {
10609
+ enabled: true,
10610
+ animation: true,
10611
+ },
10211
10612
  // 🎪 Events
10212
10613
  ...this.createDefaultEvents(entity, allActions),
10213
10614
  };
@@ -10744,25 +11145,27 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
10744
11145
  };
10745
11146
  const listOptions = await this.entityListTableService.convertEntityToListOptions(resolvedEntity, options, this.allActions());
10746
11147
  const toolbarOptions = await this.entityListToolbarService.convertEntityToolbarOptions(resolvedEntity, options);
10747
- this.listNode.set({
10748
- type: AXPWidgetsCatalog.dataList,
10749
- options: listOptions,
10750
- path: `table`,
10751
- name: 'table',
10752
- mode: 'view',
10753
- defaultValue: this.getValue()?.table,
10754
- });
10755
- this.toolbarNode.set({
10756
- type: AXPWidgetsCatalog.listToolbar,
10757
- path: `toolbar`,
10758
- options: toolbarOptions,
10759
- mode: 'view',
10760
- defaultValue: {
10761
- filters: this.getValue()?.toolbar?.filters,
10762
- sorts: this.getValue()?.toolbar?.sorts,
10763
- columns: this.getValue()?.toolbar?.columns,
10764
- },
10765
- });
11148
+ setTimeout(() => {
11149
+ this.listNode.set({
11150
+ type: AXPWidgetsCatalog.dataList,
11151
+ options: listOptions,
11152
+ path: `table`,
11153
+ name: 'table',
11154
+ mode: 'view',
11155
+ defaultValue: this.getValue()?.table,
11156
+ });
11157
+ this.toolbarNode.set({
11158
+ type: AXPWidgetsCatalog.listToolbar,
11159
+ path: `toolbar`,
11160
+ options: toolbarOptions,
11161
+ mode: 'view',
11162
+ defaultValue: {
11163
+ filters: this.getValue()?.toolbar?.filters,
11164
+ sorts: this.getValue()?.toolbar?.sorts,
11165
+ columns: this.getValue()?.toolbar?.columns,
11166
+ },
11167
+ });
11168
+ }, 100);
10766
11169
  }
10767
11170
  async ngAfterViewInit() {
10768
11171
  this.workflow.events$
@@ -12565,7 +12968,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
12565
12968
  return get(item, this.displayField()) ?? '';
12566
12969
  }
12567
12970
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLookupWidgetColumnComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
12568
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPLookupWidgetColumnComponent, isStandalone: true, selector: "ng-component", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-flex ax-gap-1 ax-items-center\">\n @if (visibleItems().length > 0) {\n @for (item of visibleItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"handleItemClick($index)\">\n {{ getDisplayText(item) }}\n </span>\n @if ($index < visibleItems().length - 1) { <span class=\"ax-text-muted\">\u2022</span>\n }\n }\n } @else {\n <span class=\"ax-text-muted\">---</span>\n }\n\n @if (hasMoreItems()) {\n <span\n class=\"ax-absolute ax-flex ax-items-center ax-end-0 ax-px-1 ax-cursor-pointer ax-h-full hover:ax-primary-lighter\"\n (click)=\"showMoreItems()\" #moreButton>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </span>\n }\n</div>\n\n<!-- More Items Popover -->\n<ax-popover [openOn]=\"'manual'\" #morePopover (openChange)=\"onMorePopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[280px]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold\">All {{ allItems().length }} Items</h3>\n </div>\n <div class=\"ax-max-h-64 ax-flex ax-flex-col ax-gap-3\">\n @for (item of allItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"showItemDetail(item, $index)\">\n {{ getDisplayText(item) }}\n </span>\n }\n </div>\n </div>\n</ax-popover>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "forceDisableActionSheetStyle", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12971
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPLookupWidgetColumnComponent, isStandalone: true, selector: "ng-component", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-flex ax-gap-1 ax-items-center\">\n @if (visibleItems().length > 0) {\n @for (item of visibleItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"handleItemClick($index)\">\n {{ getDisplayText(item) }}\n </span>\n @if ($index < visibleItems().length - 1) { <span class=\"ax-text-muted\">\u2022</span>\n }\n }\n } @else {\n <span class=\"ax-text-muted\">---</span>\n }\n\n @if (hasMoreItems()) {\n <span\n class=\"ax-absolute ax-flex ax-items-center ax-end-0 ax-px-1 ax-cursor-pointer ax-h-full hover:ax-primary-lighter\"\n (click)=\"showMoreItems()\" #moreButton>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </span>\n }\n</div>\n\n<!-- More Items Popover -->\n<ax-popover [openOn]=\"'manual'\" #morePopover (openChange)=\"onMorePopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[280px]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold\">All {{ allItems().length }} Items</h3>\n </div>\n <div class=\"ax-max-h-64 ax-flex ax-flex-col ax-gap-3\">\n @for (item of allItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"showItemDetail(item, $index)\">\n {{ getDisplayText(item) }}\n </span>\n }\n </div>\n </div>\n</ax-popover>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12569
12972
  }
12570
12973
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLookupWidgetColumnComponent, decorators: [{
12571
12974
  type: Component,
@@ -14193,6 +14596,8 @@ class AXPShowFileUploaderPopupAction extends AXPWorkflowAction {
14193
14596
  const maxFileSize = context.getVariable('options.maxFileSize');
14194
14597
  const files = context.getVariable('options.files');
14195
14598
  const fileEditable = context.getVariable('options.fileEditable');
14599
+ const enableTitleDescription = context.getVariable('options.enableTitleDescription');
14600
+ const showEditDialogAfterSelect = context.getVariable('options.showEditDialogAfterSelect');
14196
14601
  //check is correct file
14197
14602
  const correctFiles = files.filter((item) => item != undefined);
14198
14603
  // *********** show file list ***********
@@ -14202,9 +14607,10 @@ class AXPShowFileUploaderPopupAction extends AXPWorkflowAction {
14202
14607
  multiple: multiple,
14203
14608
  accept: accept,
14204
14609
  fileEditable: fileEditable,
14610
+ enableTitleDescription: enableTitleDescription ?? false,
14611
+ showEditDialogAfterSelect: showEditDialogAfterSelect ?? false,
14205
14612
  // maxFileSize: maxFileSize,
14206
14613
  });
14207
- debugger;
14208
14614
  // Handle case when result is undefined or empty array
14209
14615
  if (!res) {
14210
14616
  context.setOutput('result', false);
@@ -14213,7 +14619,6 @@ class AXPShowFileUploaderPopupAction extends AXPWorkflowAction {
14213
14619
  // *********** save files to entity ***********
14214
14620
  const [module, entity] = context.getVariable('entity').split('.');
14215
14621
  const entityDefinition = await this.entityRegistryService.resolve(module, entity);
14216
- debugger;
14217
14622
  if (entityDefinition) {
14218
14623
  const updateExec = entityDefinition.commands?.update?.execute;
14219
14624
  const entityData = await updateExec({
@@ -14221,7 +14626,10 @@ class AXPShowFileUploaderPopupAction extends AXPWorkflowAction {
14221
14626
  [key]: res,
14222
14627
  });
14223
14628
  context.setOutput('result', true);
14224
- context.setVariable('data', cloneDeep(entityData));
14629
+ // Merge full file list (name, title, description) from popup into returned data so
14630
+ // workflow output includes title/description when enableTitleDescription is used
14631
+ const dataWithFiles = { ...entityData, [key]: res };
14632
+ context.setVariable('data', cloneDeep(dataWithFiles));
14225
14633
  }
14226
14634
  }
14227
14635
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPShowFileUploaderPopupAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }