@acorex/platform 21.0.0-next.40 → 21.0.0-next.42

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 (22) hide show
  1. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  2. package/fesm2022/acorex-platform-layout-builder.mjs +115 -29
  3. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  4. package/fesm2022/acorex-platform-layout-designer.mjs +59 -4
  5. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  6. package/fesm2022/acorex-platform-layout-entity.mjs +995 -545
  7. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  8. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  9. package/fesm2022/{acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs → acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs} +9 -4
  10. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-layout-widgets.mjs +418 -106
  12. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-workflow.mjs +2 -1
  14. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  15. package/package.json +1 -1
  16. package/types/acorex-platform-common.d.ts +14 -10
  17. package/types/acorex-platform-layout-builder.d.ts +20 -2
  18. package/types/acorex-platform-layout-designer.d.ts +35 -3
  19. package/types/acorex-platform-layout-entity.d.ts +46 -5
  20. package/types/acorex-platform-layout-widget-core.d.ts +2 -5
  21. package/types/acorex-platform-layout-widgets.d.ts +108 -51
  22. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs.map +0 -1
@@ -2,13 +2,16 @@ import { AXToastService } from '@acorex/components/toast';
2
2
  import * as i6 from '@acorex/core/translation';
3
3
  import { AXTranslationService, AXTranslationModule, resolveMultiLanguageString } from '@acorex/core/translation';
4
4
  import * as i4$4 from '@acorex/platform/common';
5
- import { AXPSettingsService, AXPCommonSettings, AXPFilterOperatorMiddlewareService, AXPEntityCommandScope, getEntityInfo, AXPRefreshEvent, AXPReloadEvent, AXPCleanNestedFilters, AXPDefaultMultiLanguageConfigService, withDefaultMultiLanguageOnWidgetNodeTree, AXPEntityQueryType, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER, AXPMenuItemsDataSourceDefinition } from '@acorex/platform/common';
5
+ import { AXPEntityCommandScope, AXPSettingsService, AXPCommonSettings, AXPFilterOperatorMiddlewareService, getEntityInfo, AXPRefreshEvent, AXPReloadEvent, AXPCleanNestedFilters, AXPDefaultMultiLanguageConfigService, withDefaultMultiLanguageOnWidgetNodeTree, AXPEntityQueryType, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER, AXPMenuItemsDataSourceDefinition } from '@acorex/platform/common';
6
6
  import * as i0 from '@angular/core';
7
7
  import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, computed, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, ChangeDetectorRef, effect, Input, afterNextRender, untracked, ViewEncapsulation, viewChildren, linkedSignal, HostBinding, output, NgModule, makeEnvironmentProviders } from '@angular/core';
8
8
  import { Subject, takeUntil } from 'rxjs';
9
9
  import { AXPLayoutBuilderService, LayoutBuilderModule } from '@acorex/platform/layout/builder';
10
- import { AXPDeviceService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPExpressionEvaluatorService, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, objectKeyValueTransforms, AXPColumnWidthService, AXPModuleManifestRegistry, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPModuleManifestsDataSourceDefinition, AXPSystemActionType } from '@acorex/platform/core';
11
- import { merge, get, castArray, cloneDeep, set, orderBy, omit, isNil, isEmpty, isEqual } from 'lodash-es';
10
+ import * as i3 from '@acorex/platform/layout/widget-core';
11
+ import { AXPWidgetsCatalog, AXPWidgetCoreModule, AXPPageStatus, AXPWidgetRegistryService, AXPColumnWidgetComponent, AXPValueWidgetComponent, AXPWidgetGroupEnum, createBooleanProperty, AXPWidgetRendererDirective, createSelectProperty, createStringProperty, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex/platform/layout/widget-core';
12
+ import { AXPSystemActionType, AXPDeviceService, AXPExpressionEvaluatorService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, objectKeyValueTransforms, AXPColumnWidthService, AXPModuleManifestRegistry, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPModuleManifestsDataSourceDefinition } from '@acorex/platform/core';
13
+ import { cloneDeep, merge, get, castArray, set, orderBy, omit, isNil, isEmpty, isEqual as isEqual$1 } from 'lodash-es';
14
+ import { transform, isEqual } from 'lodash';
12
15
  import { AXPSessionService, AXPAuthGuard, AXPPermissionDefinitionsDataSourceDefinition } from '@acorex/platform/auth';
13
16
  import { Router, ActivatedRoute, RouterModule, ROUTES } from '@angular/router';
14
17
  import { defineCommand, AXP_COMMAND_DEFINITION_CATEGORY_ENTITY, AXPCommandService, AXPQueryService, AXPQueryExecutor, provideCommandSetups, provideQuerySetups, AXPCommandRegistry, AXPQueryRegistry } from '@acorex/platform/runtime';
@@ -18,8 +21,6 @@ import * as i4 from '@acorex/components/loading';
18
21
  import { AXLoadingModule } from '@acorex/components/loading';
19
22
  import * as i2 from '@acorex/components/popover';
20
23
  import { AXPopoverModule } from '@acorex/components/popover';
21
- import * as i3 from '@acorex/platform/layout/widget-core';
22
- import { AXPWidgetsCatalog, AXPWidgetCoreModule, AXPPageStatus, AXPWidgetRegistryService, AXPColumnWidgetComponent, AXPValueWidgetComponent, AXPWidgetGroupEnum, createBooleanProperty, AXPWidgetRendererDirective, createSelectProperty, createStringProperty, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex/platform/layout/widget-core';
23
24
  import * as i5 from '@angular/common';
24
25
  import { CommonModule, AsyncPipe } from '@angular/common';
25
26
  import { AXPThemeLayoutBlockComponent, AXPPreloadFiltersComponent, AXPStateMessageComponent, AXPColumnItemListComponent, AXPDataSelectorService, AXPPageComponentRegistryService } from '@acorex/platform/layout/components';
@@ -69,7 +70,6 @@ import * as i5$3 from '@acorex/components/label';
69
70
  import { AXLabelModule } from '@acorex/components/label';
70
71
  import * as i7 from '@acorex/components/text-box';
71
72
  import { AXTextBoxModule } from '@acorex/components/text-box';
72
- import { transform, isEqual as isEqual$1 } from 'lodash';
73
73
 
74
74
  function ensureListActions(ctx) {
75
75
  ctx.interfaces.update((i) => {
@@ -803,6 +803,308 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
803
803
  }]
804
804
  }] });
805
805
 
806
+ // #region Master
807
+ function entityMasterCreateAction() {
808
+ return {
809
+ title: '@general:actions.create.title',
810
+ command: {
811
+ name: 'Entity:Create',
812
+ },
813
+ priority: 'primary',
814
+ type: AXPSystemActionType.Create,
815
+ scope: AXPEntityCommandScope.TypeLevel,
816
+ };
817
+ }
818
+ function entityMasterEditAction() {
819
+ return {
820
+ title: '@general:actions.edit.title',
821
+ command: 'Entity:Update',
822
+ priority: 'secondary',
823
+ type: AXPSystemActionType.Update,
824
+ scope: AXPEntityCommandScope.Individual,
825
+ default: true,
826
+ };
827
+ }
828
+ function entityMasterBulkDeleteAction() {
829
+ return {
830
+ title: '@general:actions.delete-items.title',
831
+ command: 'delete-entity',
832
+ priority: 'primary',
833
+ type: AXPSystemActionType.Delete,
834
+ scope: AXPEntityCommandScope.Selected,
835
+ order: 100,
836
+ };
837
+ }
838
+ function entityMasterViewAction() {
839
+ return {
840
+ title: '@general:actions.view.title',
841
+ command: 'open-entity',
842
+ priority: 'secondary',
843
+ type: AXPSystemActionType.View,
844
+ scope: AXPEntityCommandScope.Individual,
845
+ default: true,
846
+ };
847
+ }
848
+ function entityMasterDeleteAction() {
849
+ return {
850
+ title: '@general:actions.delete.title',
851
+ command: 'delete-entity',
852
+ priority: 'secondary',
853
+ type: AXPSystemActionType.Delete,
854
+ scope: AXPEntityCommandScope.Individual,
855
+ order: 100,
856
+ };
857
+ }
858
+ function entityMasterCrudActions(options) {
859
+ const opts = {
860
+ create: true,
861
+ delete: true,
862
+ view: true,
863
+ edit: false,
864
+ ...options,
865
+ };
866
+ const actions = [];
867
+ if (opts.create) {
868
+ actions.push(entityMasterCreateAction());
869
+ }
870
+ if (opts.delete) {
871
+ actions.push(entityMasterBulkDeleteAction());
872
+ actions.push(entityMasterDeleteAction());
873
+ }
874
+ if (opts.view) {
875
+ actions.push(entityMasterViewAction());
876
+ }
877
+ if (opts.edit) {
878
+ actions.push(entityMasterEditAction());
879
+ }
880
+ return actions;
881
+ }
882
+ function entityMasterRecordActions() {
883
+ return [entityMasterDeleteAction()];
884
+ }
885
+ // #endregion
886
+ // #region Details
887
+ function entityDetailsCreateActions(parentId) {
888
+ return {
889
+ title: '@general:actions.create.title',
890
+ command: {
891
+ name: 'Entity:Create',
892
+ options: {
893
+ process: {
894
+ redirect: false,
895
+ canCreateNewOne: true,
896
+ data: {
897
+ [parentId]: '{{context.eval("id")}}',
898
+ },
899
+ },
900
+ },
901
+ },
902
+ priority: 'primary',
903
+ type: AXPSystemActionType.Create,
904
+ scope: AXPEntityCommandScope.TypeLevel,
905
+ };
906
+ }
907
+ /**
908
+ * Type-level Create with empty `process.data`; the FK is supplied from `relatedEntity.persistence.foreignKeyField`
909
+ * when the related list is built (see `mergeForeignKeyFieldIntoCreateActions`).
910
+ */
911
+ function entityDetailsCreateActionsDeferredParent() {
912
+ return {
913
+ title: '@general:actions.create.title',
914
+ command: {
915
+ name: 'Entity:Create',
916
+ options: {
917
+ process: {
918
+ redirect: false,
919
+ canCreateNewOne: true,
920
+ data: {},
921
+ },
922
+ },
923
+ },
924
+ priority: 'primary',
925
+ type: AXPSystemActionType.Create,
926
+ scope: AXPEntityCommandScope.TypeLevel,
927
+ };
928
+ }
929
+ /**
930
+ * Ensures each type-level `Entity:Create` action includes `process.data[foreignKeyField]` bound to the parent row id.
931
+ */
932
+ function mergeForeignKeyFieldIntoCreateActions(foreignKeyField, actions) {
933
+ if (!foreignKeyField || !actions?.length) {
934
+ return actions ?? [];
935
+ }
936
+ return actions.map((a) => {
937
+ const cmd = a.command;
938
+ if (typeof cmd !== 'object' ||
939
+ !cmd ||
940
+ cmd.name !== 'Entity:Create' ||
941
+ a.scope !== AXPEntityCommandScope.TypeLevel) {
942
+ return a;
943
+ }
944
+ const opts = (cmd.options ?? {});
945
+ const proc = (opts['process'] ?? {});
946
+ const data = { ...(proc['data'] ?? {}) };
947
+ if (data[foreignKeyField] === undefined || data[foreignKeyField] === null || data[foreignKeyField] === '') {
948
+ data[foreignKeyField] = '{{ context.eval("id") }}';
949
+ }
950
+ return {
951
+ ...a,
952
+ command: {
953
+ ...cmd,
954
+ options: { ...opts, process: { ...proc, data } },
955
+ },
956
+ };
957
+ });
958
+ }
959
+ /** Property names hidden on nested Create from a related list (`excludeProperties` plus `foreignKeyField` when set). */
960
+ function collectNestedCreateHiddenProperties(relatedEntity) {
961
+ const fk = relatedEntity.persistence?.foreignKeyField;
962
+ const merged = [...(relatedEntity.excludeProperties ?? [])];
963
+ if (fk) {
964
+ merged.push(fk);
965
+ }
966
+ const unique = [...new Set(merged)];
967
+ return unique.length ? unique : undefined;
968
+ }
969
+ function entityDetailsSimpleCondition(fk) {
970
+ return {
971
+ name: fk,
972
+ operator: { type: 'equal' },
973
+ value: '{{context.eval("id")}}',
974
+ };
975
+ }
976
+ function entityDetailsReferenceCondition(type) {
977
+ return [
978
+ {
979
+ name: 'reference.id',
980
+ operator: { type: 'equal' },
981
+ value: '{{context.eval("id")}}',
982
+ },
983
+ {
984
+ name: 'reference.type',
985
+ operator: { type: 'equal' },
986
+ value: type,
987
+ },
988
+ ];
989
+ }
990
+ function entityDetailsEditAction() {
991
+ return {
992
+ title: '@general:actions.edit.title',
993
+ // command: 'quick-modify-entity',
994
+ command: 'Entity:Update',
995
+ priority: 'secondary',
996
+ type: AXPSystemActionType.Update,
997
+ default: true,
998
+ scope: AXPEntityCommandScope.Individual,
999
+ };
1000
+ }
1001
+ function entityDetailsNewEditAction() {
1002
+ return {
1003
+ title: 'New Edit',
1004
+ command: {
1005
+ name: 'Entity:Update',
1006
+ },
1007
+ priority: 'secondary',
1008
+ type: AXPSystemActionType.Update,
1009
+ default: true,
1010
+ scope: AXPEntityCommandScope.Individual,
1011
+ };
1012
+ }
1013
+ function entityOverrideDetailsViewAction() {
1014
+ return {
1015
+ title: '@general:actions.view.title',
1016
+ command: 'open-entity',
1017
+ priority: 'secondary',
1018
+ hidden: true,
1019
+ type: AXPSystemActionType.View,
1020
+ scope: AXPEntityCommandScope.Individual,
1021
+ };
1022
+ }
1023
+ function entityDetailsCrudActions(parentId, options) {
1024
+ const opts = {
1025
+ create: true,
1026
+ delete: true,
1027
+ view: true,
1028
+ edit: true,
1029
+ ...options,
1030
+ };
1031
+ const actions = [];
1032
+ if (opts.create) {
1033
+ if (parentId) {
1034
+ actions.push(entityDetailsCreateActions(parentId));
1035
+ }
1036
+ else {
1037
+ actions.push(entityDetailsCreateActionsDeferredParent());
1038
+ }
1039
+ }
1040
+ if (opts.edit) {
1041
+ actions.push(entityDetailsEditAction());
1042
+ }
1043
+ if (opts.view) {
1044
+ actions.push(entityOverrideDetailsViewAction());
1045
+ }
1046
+ return actions;
1047
+ }
1048
+ function entityDetailsReferenceCreateActions(type) {
1049
+ return [
1050
+ {
1051
+ title: '@general:actions.create.title',
1052
+ command: {
1053
+ name: 'Entity:Create',
1054
+ options: {
1055
+ process: {
1056
+ redirect: false,
1057
+ canCreateNewOne: true,
1058
+ data: {
1059
+ reference: {
1060
+ id: '{{context.eval("id")}}',
1061
+ type: type,
1062
+ },
1063
+ },
1064
+ },
1065
+ },
1066
+ },
1067
+ priority: 'primary',
1068
+ type: AXPSystemActionType.Create,
1069
+ scope: AXPEntityCommandScope.TypeLevel,
1070
+ },
1071
+ entityDetailsEditAction(),
1072
+ entityOverrideDetailsViewAction(),
1073
+ ];
1074
+ }
1075
+ /**
1076
+ * Computes a diff between two plain objects with array-aware semantics.
1077
+ * - For arrays of objects with an id field, computes added/removed by id.
1078
+ * - For arrays of primitives or objects without id, uses deep equality.
1079
+ * - For scalars/objects, reports oldValue/newValue when changed.
1080
+ */
1081
+ function detectEntityChanges(oldObj, newObj) {
1082
+ return transform(newObj, (result, value, key) => {
1083
+ if (!isEqual(value, oldObj[key])) {
1084
+ const oldValue = oldObj[key];
1085
+ if (Array.isArray(value) || Array.isArray(oldValue)) {
1086
+ const oldArray = Array.isArray(oldValue) ? oldValue : [];
1087
+ const newArray = Array.isArray(value) ? value : [];
1088
+ const hasId = newArray.length > 0 && typeof newArray[0] === 'object' && newArray[0] !== null && 'id' in newArray[0];
1089
+ if (hasId) {
1090
+ const added = newArray.filter((item) => !oldArray.some((oldItem) => oldItem.id === item.id));
1091
+ const removed = oldArray.filter((item) => !newArray.some((newItem) => newItem.id === item.id));
1092
+ result[key] = { oldValue, newValue: value, added, removed };
1093
+ }
1094
+ else {
1095
+ const added = newArray.filter((item) => !oldArray.some((oldItem) => isEqual(item, oldItem)));
1096
+ const removed = oldArray.filter((item) => !newArray.some((newItem) => isEqual(item, newItem)));
1097
+ result[key] = { oldValue, newValue: value, added, removed };
1098
+ }
1099
+ }
1100
+ else {
1101
+ result[key] = { oldValue, newValue: value };
1102
+ }
1103
+ }
1104
+ }, {});
1105
+ }
1106
+ //#endregion
1107
+
806
1108
  /**
807
1109
  * Maps entity property `description` (or any i18n key / multi-language payload) to `form-field`
808
1110
  * widget options. Returns `undefined` when there is nothing to show.
@@ -818,6 +1120,88 @@ function hintFormFieldOptionsFromDescription(description) {
818
1120
  }
819
1121
  //#endregion
820
1122
 
1123
+ //#endregion
1124
+ //#region ---- Helpers ----
1125
+ function isRelatedEntityStringColumns(columns) {
1126
+ return columns.length === 0 || typeof columns[0] === 'string';
1127
+ }
1128
+ /**
1129
+ * Property names from `AXPRelatedEntity.columns` (no expression evaluation).
1130
+ * Empty array means caller can treat as “no name filter” (show all allowed by entity).
1131
+ */
1132
+ function getRelatedEntityColumnNames(columns) {
1133
+ if (!columns?.length) {
1134
+ return [];
1135
+ }
1136
+ if (isRelatedEntityStringColumns(columns)) {
1137
+ return [...columns];
1138
+ }
1139
+ return columns.map((c) => c.name);
1140
+ }
1141
+ function isTruthyVisible(value) {
1142
+ if (value === true) {
1143
+ return true;
1144
+ }
1145
+ if (value === false || value === null || value === undefined) {
1146
+ return false;
1147
+ }
1148
+ if (typeof value === 'string') {
1149
+ const t = value.trim().toLowerCase();
1150
+ return t !== '' && t !== 'false' && t !== '0';
1151
+ }
1152
+ if (typeof value === 'number') {
1153
+ return value !== 0 && !Number.isNaN(value);
1154
+ }
1155
+ return Boolean(value);
1156
+ }
1157
+ //#endregion
1158
+ //#region ---- Public API ----
1159
+ /**
1160
+ * Resolves `AXPRelatedEntity.columns` for `entity-list`:
1161
+ * - `string[]` → `includeColumns` only (legacy).
1162
+ * - `AXPEntityTableColumn[]` → evaluates `options.visible` when it is a string (parent detail context),
1163
+ * drops hidden / invisible columns, and returns `relatedTableColumns` + matching `includeColumns`.
1164
+ */
1165
+ async function resolveRelatedEntityColumns(columns, expressionEvaluator, scope) {
1166
+ if (!columns?.length) {
1167
+ return {};
1168
+ }
1169
+ if (isRelatedEntityStringColumns(columns)) {
1170
+ return { includeColumns: getRelatedEntityColumnNames(columns) };
1171
+ }
1172
+ const relatedTableColumns = [];
1173
+ for (const raw of columns) {
1174
+ const col = cloneDeep(raw);
1175
+ if (col.hidden === true) {
1176
+ continue;
1177
+ }
1178
+ let visible = col.options?.visible;
1179
+ if (visible === undefined) {
1180
+ visible = true;
1181
+ }
1182
+ else if (typeof visible === 'string') {
1183
+ const evaluated = await expressionEvaluator.evaluate(visible, scope);
1184
+ visible = isTruthyVisible(evaluated);
1185
+ }
1186
+ if (visible === false) {
1187
+ continue;
1188
+ }
1189
+ col.options = { ...col.options, visible: true };
1190
+ relatedTableColumns.push(col);
1191
+ }
1192
+ return {
1193
+ relatedTableColumns,
1194
+ includeColumns: relatedTableColumns.map((c) => c.name),
1195
+ };
1196
+ }
1197
+ //#endregion
1198
+
1199
+ /** Registered widget name for entity create/update step wizard (dialog footer uses `widget:${name}.next`). */
1200
+ const ENTITY_FORM_STEP_WIZARD_NAME = 'entityFormStepWizard';
1201
+ /** Footer command: validate, persist main entity, merge context, advance wizard. */
1202
+ const ENTITY_FORM_ACTION_FIRST_STEP_CONTINUE = 'entity-form-first-step-continue';
1203
+ /** Footer command: close dialog (data already saved per step). */
1204
+ const ENTITY_FORM_ACTION_DONE = 'entity-form-done';
821
1205
  //#endregion
822
1206
  class AXPEntityFormBuilderService {
823
1207
  constructor() {
@@ -825,11 +1209,12 @@ class AXPEntityFormBuilderService {
825
1209
  this.entityRegistry = inject(AXPEntityDefinitionRegistryService);
826
1210
  this.layoutBuilder = inject(AXPLayoutBuilderService);
827
1211
  this.deviceService = inject(AXPDeviceService);
1212
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
828
1213
  }
829
1214
  //#endregion
830
1215
  //#region ---- Public API ----
831
1216
  entity(fullName) {
832
- return new InterfaceSelector(this.entityRegistry, this.layoutBuilder, this.deviceService, fullName, this);
1217
+ return new InterfaceSelector(this.entityRegistry, this.layoutBuilder, this.deviceService, this.expressionEvaluator, fullName, this);
833
1218
  }
834
1219
  /**
835
1220
  * Fetches a record by ID for the specified entity.
@@ -863,15 +1248,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
863
1248
  }] });
864
1249
  //#region ---- Builders ----
865
1250
  class InterfaceSelector {
866
- constructor(entityRegistry, layoutBuilder, deviceService, fullName, formBuilderService) {
1251
+ constructor(entityRegistry, layoutBuilder, deviceService, expressionEvaluator, fullName, formBuilderService) {
867
1252
  this.entityRegistry = entityRegistry;
868
1253
  this.layoutBuilder = layoutBuilder;
869
1254
  this.deviceService = deviceService;
1255
+ this.expressionEvaluator = expressionEvaluator;
870
1256
  this.fullName = fullName;
871
1257
  this.formBuilderService = formBuilderService;
872
1258
  }
873
1259
  create(initialData) {
874
- const filter = new PropertyFilter(this.entityRegistry, this.layoutBuilder, this.deviceService, this.fullName, 'create', undefined, this.formBuilderService);
1260
+ const filter = new PropertyFilter(this.entityRegistry, this.layoutBuilder, this.deviceService, this.expressionEvaluator, this.fullName, 'create', undefined, this.formBuilderService);
875
1261
  if (initialData) {
876
1262
  filter.context(initialData);
877
1263
  }
@@ -887,7 +1273,7 @@ class InterfaceSelector {
887
1273
  initialData = data;
888
1274
  recordId = data['id'] || data['_id'];
889
1275
  }
890
- const filter = new PropertyFilter(this.entityRegistry, this.layoutBuilder, this.deviceService, this.fullName, 'update', recordId, this.formBuilderService);
1276
+ const filter = new PropertyFilter(this.entityRegistry, this.layoutBuilder, this.deviceService, this.expressionEvaluator, this.fullName, 'update', recordId, this.formBuilderService);
891
1277
  if (Object.keys(initialData).length > 0) {
892
1278
  filter.context(initialData);
893
1279
  }
@@ -903,7 +1289,7 @@ class InterfaceSelector {
903
1289
  initialData = data;
904
1290
  recordId = data['id'] || data['_id'];
905
1291
  }
906
- const filter = new PropertyFilter(this.entityRegistry, this.layoutBuilder, this.deviceService, this.fullName, 'single', recordId, this.formBuilderService);
1292
+ const filter = new PropertyFilter(this.entityRegistry, this.layoutBuilder, this.deviceService, this.expressionEvaluator, this.fullName, 'single', recordId, this.formBuilderService);
907
1293
  if (Object.keys(initialData).length > 0) {
908
1294
  filter.context(initialData);
909
1295
  }
@@ -911,10 +1297,11 @@ class InterfaceSelector {
911
1297
  }
912
1298
  }
913
1299
  class PropertyFilter {
914
- constructor(entityRegistry, layoutBuilder, deviceService, fullName, kind, recordId, formBuilderService) {
1300
+ constructor(entityRegistry, layoutBuilder, deviceService, expressionEvaluator, fullName, kind, recordId, formBuilderService) {
915
1301
  this.entityRegistry = entityRegistry;
916
1302
  this.layoutBuilder = layoutBuilder;
917
1303
  this.deviceService = deviceService;
1304
+ this.expressionEvaluator = expressionEvaluator;
918
1305
  this.fullName = fullName;
919
1306
  this.kind = kind;
920
1307
  this.formBuilderService = formBuilderService;
@@ -971,11 +1358,10 @@ class PropertyFilter {
971
1358
  return this;
972
1359
  }
973
1360
  async build() {
974
- const dialog = await this.buildDialog();
1361
+ const dialog = await this.buildDialog({});
975
1362
  return dialog.build();
976
1363
  }
977
1364
  async show() {
978
- const dialog = await this.buildDialog();
979
1365
  // Context: always load by key when we have a record id (authoritative full row), then merge any
980
1366
  // caller/list row context. Skipping fetch when initialContext was non-empty left dialogs without
981
1367
  // fields not present on the passed row (e.g. Entity:View single() with partial list payload).
@@ -1000,14 +1386,17 @@ class PropertyFilter {
1000
1386
  }
1001
1387
  }
1002
1388
  const effectiveContext = merge({}, this.initialContext, baseContext);
1389
+ const dialog = await this.buildDialog(effectiveContext);
1003
1390
  dialog.setContext(effectiveContext);
1004
1391
  return await dialog.show();
1005
1392
  }
1006
1393
  /**
1007
1394
  * Builds the dialog node structure without showing it.
1008
1395
  * This method is shared by both build() and show() methods.
1396
+ *
1397
+ * @param modelForRelatedEval Merged root model used to evaluate related list filters (show() passes full row).
1009
1398
  */
1010
- async buildDialog() {
1399
+ async buildDialog(modelForRelatedEval) {
1011
1400
  const { moduleName, entityName } = parseEntityFullName(this.fullName);
1012
1401
  const entity = await this.entityRegistry.resolve(moduleName, entityName);
1013
1402
  // Select the appropriate interface based on kind
@@ -1025,7 +1414,9 @@ class PropertyFilter {
1025
1414
  // Collect all groups for title lookup (main + merged)
1026
1415
  const allGroups = [...(entity.groups ?? [])];
1027
1416
  // Process merge-detail related entities
1028
- const mergeDetailEntities = (entity.relatedEntities ?? []).filter((re) => !re.hidden && re.layout?.type === 'merge-detail');
1417
+ const mergeDetailEntities = (entity.relatedEntities ?? []).filter((re) => !re.hidden &&
1418
+ re.layout?.type === 'merge-detail' &&
1419
+ isRelatedEntityIncludedOnMasterForm(re, this.kind, 'merge-detail'));
1029
1420
  // Build merged properties map by section
1030
1421
  const mergedPropsBySection = new Map();
1031
1422
  // Initialize with main entity properties
@@ -1198,118 +1589,270 @@ class PropertyFilter {
1198
1589
  return sectionProps.length > 0 || extraFields.length > 0;
1199
1590
  });
1200
1591
  const sectionScaleFactors = normalizeLayoutFillRowGaps(renderedSections, (s) => s.layout, (s) => s.id);
1592
+ const filterEvalRoot = merge({}, this.initialContext, modelForRelatedEval);
1593
+ const listRelatedEntities = collectListRelatedEntitiesForFormWizard(entity, this.kind);
1594
+ const useListWizard = listRelatedEntities.length > 0 && (this.kind === 'create' || this.kind === 'update');
1595
+ const listWizardSteps = [];
1596
+ for (const re of listRelatedEntities) {
1597
+ const [rm, en] = re.entity.split('.');
1598
+ const relDef = await this.entityRegistry.resolve(rm, en);
1599
+ listWizardSteps.push({
1600
+ stepId: buildEntityFormRelatedStepId(re),
1601
+ title: (re.title ?? relDef?.title ?? re.entity),
1602
+ rel: re,
1603
+ });
1604
+ }
1605
+ const prebuiltRelatedListConfigs = useListWizard
1606
+ ? await Promise.all(listWizardSteps.map((s) => this.buildRelatedEntityListWidgetOptions(s.rel, filterEvalRoot)))
1607
+ : [];
1608
+ const mainFormGridArgs = {
1609
+ entity,
1610
+ finalSections,
1611
+ mergedPropsBySection,
1612
+ allGroups,
1613
+ extraFieldsByGroup: this.extraFieldsByGroup,
1614
+ sectionScaleFactors,
1615
+ };
1201
1616
  const dialog = this.layoutBuilder.create().dialog((d) => {
1202
1617
  d.setTitle(title);
1203
- d.setSize(this.externalSize ?? calculatedSize);
1618
+ d.setSize(this.externalSize ?? (useListWizard ? 'lg' : calculatedSize));
1204
1619
  d.setCloseButton(true);
1205
1620
  d.content((layout) => {
1206
- // Default mode: 'view' for single, 'edit' for create/update
1207
1621
  const defaultMode = this.kind === 'single' ? 'view' : 'edit';
1208
1622
  layout.mode(this.externalMode ?? defaultMode);
1209
- // Wrap content in a grid container to support colSpan layouts
1210
- layout.grid((grid) => {
1211
- // Configure grid with 12 columns (like Bootstrap)
1212
- grid.setColumns(12);
1213
- grid.setGap('1rem');
1214
- for (const section of finalSections) {
1215
- const groupId = section.id;
1216
- const sectionProps = mergedPropsBySection.get(groupId) ?? [];
1217
- const extraFields = this.extraFieldsByGroup.get(groupId) ?? [];
1218
- if (sectionProps.length === 0 && extraFields.length === 0) {
1219
- continue;
1220
- }
1221
- const sectionLayout = section.layout ?? undefined;
1222
- grid.item(sectionLayout, (fs) => {
1223
- const sectionVisible = section.layout?.visible;
1224
- if (sectionVisible !== undefined && sectionVisible !== null) {
1225
- fs.visible(sectionVisible);
1226
- }
1227
- fs.setLook('fieldset');
1228
- fs.setTitle((getGroupTitleFromList(allGroups, groupId) || groupId));
1229
- fs.setCols(12);
1230
- // Section label visibility (fieldset title/legend)
1231
- // Prefer current interface (create/update/single) section layout, fallback to master.single section layout.
1232
- const sectionLabelVisible = section?.layout?.label?.visible ??
1233
- entity?.interfaces?.master?.single?.sections?.find((s) => s?.id === groupId)?.layout?.label
1234
- ?.visible;
1235
- if (sectionLabelVisible === false) {
1236
- // Different parts of the system refer to this concept with different keys.
1237
- // - Fieldset view renderer currently uses `showTitle`
1238
- // - Fieldset designer/property system uses `showHeader`
1239
- fs.setOptions({ showTitle: false, showHeader: false });
1240
- }
1241
- // Sort properties by order within section
1242
- const orderedProps = [...sectionProps].sort((a, b) => {
1243
- const aOrder = a.__order ?? Infinity;
1244
- const bOrder = b.__order ?? Infinity;
1245
- return aOrder - bOrder;
1623
+ if (useListWizard) {
1624
+ layout.stepWizard((w) => {
1625
+ w.name(ENTITY_FORM_STEP_WIZARD_NAME);
1626
+ w.setLook('circular');
1627
+ w.setShowActions(false);
1628
+ const mainStepTitle = (entity.formats?.individual ||
1629
+ entity.title ||
1630
+ `${entity.module}.${entity.name}`);
1631
+ w.step('main', mainStepTitle, (step) => {
1632
+ step.content((inner) => {
1633
+ inner.grid((grid) => this.fillMainEntityFormGrid(grid, mainFormGridArgs));
1246
1634
  });
1247
- applySectionScaleToProperties(orderedProps, sectionScaleFactors.get(groupId));
1248
- normalizeLayoutFillRowGaps(orderedProps, (p) => p.__layout);
1249
- for (const prop of orderedProps) {
1250
- const mergedProp = prop;
1251
- const dataPath = mergedProp.__dataPath;
1252
- const fieldPath = dataPath ? `${dataPath}.${prop.name}` : prop.name;
1253
- fs.formField(prop.title, (field) => {
1254
- field.path(fieldPath);
1255
- if (prop.schema?.visible !== undefined) {
1256
- field.visible(prop.schema?.visible);
1257
- }
1258
- if (prop.schema?.readonly !== undefined) {
1259
- field.readonly(prop.schema.readonly);
1260
- }
1261
- if (prop.schema?.defaultValue !== undefined) {
1262
- field.defaultValue(prop.schema.defaultValue);
1263
- }
1264
- const fieldLayout = toFieldLayout(mergedProp.__layout);
1265
- if (fieldLayout) {
1266
- field.layout(fieldLayout);
1267
- }
1268
- if (fieldLayout?.label?.visible === false) {
1269
- field.setShowLabel(false);
1270
- }
1271
- const hintOpts = hintFormFieldOptionsFromDescription(prop.description);
1272
- if (hintOpts) {
1273
- field.setOptions(hintOpts);
1274
- }
1275
- const widgetType = prop.schema?.interface?.type || '';
1276
- const widgetOptions = buildWidgetOptions(prop);
1277
- const extendedProperties = buildWidgetExtendedProperties(prop);
1278
- const finalExtendedProps = dataPath
1279
- ? prefixExpressions(extendedProperties, dataPath)
1280
- : extendedProperties;
1281
- field.customWidget(widgetType, { ...widgetOptions, ...finalExtendedProps });
1282
- });
1283
- }
1284
- for (const extra of extraFields) {
1285
- const label = extra.path?.split('.').slice(-1)[0] || extra.path;
1286
- fs.formField(label, (f) => {
1287
- const legacy = toCompatFormFieldBuilder(f);
1288
- legacy.path(extra.path);
1289
- extra.delegate?.(legacy);
1635
+ });
1636
+ listWizardSteps.forEach((step, idx) => {
1637
+ const built = prebuiltRelatedListConfigs[idx];
1638
+ w.step(step.stepId, step.title, (st) => {
1639
+ st.content((inner) => {
1640
+ // Step content uses root LayoutBuilder (no customWidget); wrap in flex like other roots.
1641
+ inner.flex((flex) => {
1642
+ flex.customWidget(AXPWidgetsCatalog.entityList, (iw) => {
1643
+ iw.name(built.name);
1644
+ if (built.defaultValue) {
1645
+ iw.defaultValue(built.defaultValue);
1646
+ }
1647
+ iw.options(built.options);
1648
+ });
1649
+ });
1290
1650
  });
1291
- }
1651
+ });
1292
1652
  });
1293
- }
1653
+ });
1654
+ }
1655
+ else {
1656
+ layout.grid((grid) => this.fillMainEntityFormGrid(grid, mainFormGridArgs));
1657
+ }
1658
+ });
1659
+ if (this.externalActionsDelegate && !useListWizard) {
1660
+ d.setActions(this.externalActionsDelegate);
1661
+ }
1662
+ else if (useListWizard) {
1663
+ d.setActions((ab) => configureEntityFormWizardFooterActions(ab, ENTITY_FORM_STEP_WIZARD_NAME, this.kind === 'create'
1664
+ ? '{{ ((typeof context.id === "string" && context.id.length > 0) || (typeof context.id === "number" && !isNaN(context.id)) || (typeof context._id === "string" && context._id.length > 0) || (typeof context._id === "number" && !isNaN(context._id))) ? "@general:entity-form.update-and-continue.title" : "@general:entity-form.create-and-continue.title" }}'
1665
+ : '@general:entity-form.save-and-continue.title'));
1666
+ }
1667
+ else if (this.kind === 'single') {
1668
+ d.setActions((a) => a.submit('@general:actions.close.title'));
1669
+ }
1670
+ else {
1671
+ d.setActions((a) => a.submit(this.kind === 'create' ? '@general:actions.create.title' : '@general:actions.apply.title'));
1672
+ }
1673
+ if (useListWizard && this.onActionHandler) {
1674
+ d.onAction(this.createWizardOnActionWrapper(this.onActionHandler));
1675
+ }
1676
+ else if (this.onActionHandler) {
1677
+ d.onAction(this.onActionHandler);
1678
+ }
1679
+ else if (this.kind === 'single') {
1680
+ d.onAction(async () => ({ success: true, skipValidate: true }));
1681
+ }
1682
+ });
1683
+ return dialog;
1684
+ }
1685
+ /**
1686
+ * Renders main entity fieldsets and fields into a grid container (step 1 or non-wizard form).
1687
+ */
1688
+ fillMainEntityFormGrid(grid, args) {
1689
+ grid.setColumns(12);
1690
+ grid.setGap('1rem');
1691
+ for (const section of args.finalSections) {
1692
+ const groupId = section.id;
1693
+ const sectionProps = args.mergedPropsBySection.get(groupId) ?? [];
1694
+ const extraFields = args.extraFieldsByGroup.get(groupId) ?? [];
1695
+ if (sectionProps.length === 0 && extraFields.length === 0) {
1696
+ continue;
1697
+ }
1698
+ const sectionLayout = section.layout ?? undefined;
1699
+ grid.item(sectionLayout, (fs) => {
1700
+ const sectionVisible = section.layout?.visible;
1701
+ if (sectionVisible !== undefined && sectionVisible !== null) {
1702
+ fs.visible(sectionVisible);
1703
+ }
1704
+ fs.setLook('fieldset');
1705
+ fs.setTitle((getGroupTitleFromList(args.allGroups, groupId) || groupId));
1706
+ fs.setCols(12);
1707
+ const sectionLabelVisible = section.layout?.label?.visible ??
1708
+ args.entity.interfaces?.master?.single?.sections?.find((s) => s?.id === groupId)?.layout?.label?.visible;
1709
+ if (sectionLabelVisible === false) {
1710
+ const legendHidden = { showTitle: false, showHeader: false };
1711
+ fs.setOptions(legendHidden);
1712
+ }
1713
+ const orderedProps = [...sectionProps].sort((a, b) => {
1714
+ const aOrder = a.__order ?? Infinity;
1715
+ const bOrder = b.__order ?? Infinity;
1716
+ return aOrder - bOrder;
1294
1717
  });
1718
+ applySectionScaleToProperties(orderedProps, args.sectionScaleFactors.get(groupId));
1719
+ normalizeLayoutFillRowGaps(orderedProps, (p) => p.__layout);
1720
+ for (const prop of orderedProps) {
1721
+ const mergedProp = prop;
1722
+ const dataPath = mergedProp.__dataPath;
1723
+ const fieldPath = dataPath ? `${dataPath}.${prop.name}` : prop.name;
1724
+ fs.formField(prop.title, (field) => {
1725
+ field.path(fieldPath);
1726
+ if (prop.schema?.visible !== undefined) {
1727
+ field.visible(prop.schema?.visible);
1728
+ }
1729
+ if (prop.schema?.readonly !== undefined) {
1730
+ field.readonly(prop.schema.readonly);
1731
+ }
1732
+ if (prop.schema?.defaultValue !== undefined) {
1733
+ field.defaultValue(prop.schema.defaultValue);
1734
+ }
1735
+ const fieldLayout = toFieldLayout(mergedProp.__layout);
1736
+ if (fieldLayout) {
1737
+ field.layout(fieldLayout);
1738
+ }
1739
+ if (fieldLayout?.label?.visible === false) {
1740
+ field.setShowLabel(false);
1741
+ }
1742
+ const hintOpts = hintFormFieldOptionsFromDescription(prop.description);
1743
+ if (hintOpts) {
1744
+ field.setOptions(hintOpts);
1745
+ }
1746
+ const widgetType = prop.schema?.interface?.type || '';
1747
+ const widgetOptions = buildWidgetOptions(prop);
1748
+ const extendedProperties = buildWidgetExtendedProperties(prop);
1749
+ const finalExtendedProps = dataPath ? prefixExpressions(extendedProperties, dataPath) : extendedProperties;
1750
+ field.customWidget(widgetType, { ...widgetOptions, ...finalExtendedProps });
1751
+ });
1752
+ }
1753
+ for (const extra of extraFields) {
1754
+ const label = extra.path?.split('.').slice(-1)[0] || extra.path;
1755
+ fs.formField(label, (f) => {
1756
+ const legacy = toCompatFormFieldBuilder(f);
1757
+ legacy.path(extra.path);
1758
+ extra.delegate?.(legacy);
1759
+ });
1760
+ }
1295
1761
  });
1296
- // Actions: external if provided, otherwise default based on kind
1297
- if (this.externalActionsDelegate) {
1298
- d.setActions(this.externalActionsDelegate);
1299
- }
1300
- else if (this.kind === 'single') {
1301
- // For single (read-only view), only show cancel/close button
1302
- d.setActions((a) => a.cancel());
1762
+ }
1763
+ }
1764
+ async buildRelatedEntityListWidgetOptions(relatedEntity, filterEvalRoot) {
1765
+ const evaluateExpressions = async (actionData) => {
1766
+ const scope = {
1767
+ context: {
1768
+ eval: (path) => get(filterEvalRoot, path),
1769
+ },
1770
+ };
1771
+ return await this.expressionEvaluator.evaluate(actionData, scope);
1772
+ };
1773
+ const syncRelatedFromRoot = this.kind === 'create';
1774
+ const foreignKeyField = relatedEntity.persistence?.foreignKeyField;
1775
+ const filters = syncRelatedFromRoot
1776
+ ? []
1777
+ : await Promise.all(relatedEntity.conditions?.map(async (c) => {
1778
+ const value = await evaluateExpressions(c.value);
1779
+ return {
1780
+ field: c.name,
1781
+ operator: c.operator,
1782
+ value,
1783
+ hidden: true,
1784
+ };
1785
+ }) ?? []);
1786
+ const rawOrEvaluatedActions = syncRelatedFromRoot
1787
+ ? (relatedEntity.actions ?? [])
1788
+ : await this.evaluateRelatedEntityActionsForFormWizard(relatedEntity.actions, filterEvalRoot);
1789
+ const evaluatedActions = mergeForeignKeyFieldIntoCreateActions(foreignKeyField, rawOrEvaluatedActions);
1790
+ const columnScope = {
1791
+ context: {
1792
+ eval: (path) => get(filterEvalRoot, path),
1793
+ },
1794
+ };
1795
+ const { includeColumns, relatedTableColumns } = await resolveRelatedEntityColumns(relatedEntity.columns, this.expressionEvaluator, columnScope);
1796
+ const nestedCreateHiddenProperties = collectNestedCreateHiddenProperties(relatedEntity);
1797
+ return {
1798
+ name: `${relatedEntity.entity}-entity-form-step`,
1799
+ defaultValue: {
1800
+ toolbar: {
1801
+ filters,
1802
+ },
1803
+ },
1804
+ options: {
1805
+ entity: relatedEntity.entity,
1806
+ showEntityActions: true,
1807
+ showToolbar: false,
1808
+ actions: evaluatedActions,
1809
+ maxHeight: '400px',
1810
+ includeColumns,
1811
+ relatedTableColumns,
1812
+ customFilterDefinitions: relatedEntity.customFilterDefinitions,
1813
+ ...(foreignKeyField ? { foreignKeyField } : {}),
1814
+ ...(nestedCreateHiddenProperties?.length ? { nestedCreateHiddenProperties } : {}),
1815
+ ...(syncRelatedFromRoot && relatedEntity.conditions?.length
1816
+ ? {
1817
+ relatedFilterConditionSpecs: cloneDeep(relatedEntity.conditions),
1818
+ syncRelatedListFiltersFromDialogContext: true,
1819
+ }
1820
+ : {}),
1821
+ },
1822
+ };
1823
+ }
1824
+ async evaluateRelatedEntityActionsForFormWizard(actions, filterEvalRoot) {
1825
+ const list = actions ?? [];
1826
+ const scope = {
1827
+ context: {
1828
+ eval: (path) => get(filterEvalRoot, path),
1829
+ },
1830
+ };
1831
+ return Promise.all(list.map(async (action) => {
1832
+ if (action.scope === AXPEntityCommandScope.Individual) {
1833
+ return action;
1303
1834
  }
1304
- else {
1305
- // For create/update, show cancel + submit
1306
- d.setActions((a) => a.cancel().submit());
1835
+ return (await this.expressionEvaluator.evaluate(action, scope));
1836
+ }));
1837
+ }
1838
+ createWizardOnActionWrapper(userHandler) {
1839
+ return async (ref) => {
1840
+ const action = ref.action();
1841
+ if (action === ENTITY_FORM_ACTION_DONE) {
1842
+ return { success: true, data: ref.context(), skipValidate: true };
1307
1843
  }
1308
- if (this.onActionHandler) {
1309
- d.onAction(this.onActionHandler);
1844
+ if (action === ENTITY_FORM_ACTION_FIRST_STEP_CONTINUE) {
1845
+ const out = (await userHandler(ref));
1846
+ if (out?.success === false) {
1847
+ return out;
1848
+ }
1849
+ const item = out?.data?.item ?? out?.data ?? {};
1850
+ ref.patchContext?.(merge({}, ref.context(), item));
1851
+ await ref.invokeWidget?.(ENTITY_FORM_STEP_WIZARD_NAME, 'next', { setLoading: ref.setLoading });
1852
+ return merge({}, out, { keepDialogOpen: true });
1310
1853
  }
1311
- });
1312
- return dialog;
1854
+ return await userHandler(ref);
1855
+ };
1313
1856
  }
1314
1857
  computeAllowedNames(allNames) {
1315
1858
  if (this.includeList && this.includeList.size > 0) {
@@ -1591,6 +2134,62 @@ function buildWidgetExtendedProperties(prop) {
1591
2134
  }
1592
2135
  return extended;
1593
2136
  }
2137
+ function isRelatedEntityIncludedOnMasterForm(related, kind, placementKind) {
2138
+ if (kind !== 'create' && kind !== 'update') {
2139
+ return true;
2140
+ }
2141
+ const defaultInclusive = placementKind === 'merge-detail';
2142
+ if (kind === 'create') {
2143
+ const flag = related.appearOn?.create;
2144
+ return defaultInclusive ? flag !== false : flag === true;
2145
+ }
2146
+ const flag = related.appearOn?.update;
2147
+ return defaultInclusive ? flag !== false : flag === true;
2148
+ }
2149
+ function collectListRelatedEntitiesForFormWizard(entity, kind) {
2150
+ return (entity.relatedEntities ?? [])
2151
+ .filter((re) => !re.hidden &&
2152
+ (re.layout?.type === 'tab-list' || re.layout?.type === 'page-list') &&
2153
+ isRelatedEntityIncludedOnMasterForm(re, kind, 'list'))
2154
+ .sort((a, b) => (a.layout?.order ?? 0) - (b.layout?.order ?? 0));
2155
+ }
2156
+ function buildEntityFormRelatedStepId(re) {
2157
+ const safe = re.entity.replace(/[^a-zA-Z0-9._-]/g, '_');
2158
+ return `related-${safe}-${re.layout?.order ?? 0}`;
2159
+ }
2160
+ function configureEntityFormWizardFooterActions(ab, wizardName, firstStepContinueTitleKey) {
2161
+ ab.custom({
2162
+ title: '@general:actions.previous.title',
2163
+ command: `widget:${wizardName}.previous`,
2164
+ icon: 'fa-regular fa-arrow-left',
2165
+ color: 'primary',
2166
+ position: 'suffix',
2167
+ disabled: '{{api.getStatus().isFirst}}',
2168
+ });
2169
+ ab.custom({
2170
+ title: firstStepContinueTitleKey,
2171
+ command: { name: ENTITY_FORM_ACTION_FIRST_STEP_CONTINUE },
2172
+ color: 'primary',
2173
+ position: 'suffix',
2174
+ hidden: '{{!api.getStatus().isFirst}}',
2175
+ predicateApiWidgetName: wizardName,
2176
+ });
2177
+ ab.custom({
2178
+ title: '@general:actions.next.title',
2179
+ command: `widget:${wizardName}.next`,
2180
+ color: 'primary',
2181
+ position: 'suffix',
2182
+ hidden: '{{api.getStatus().isFirst || api.getStatus().isLast}}',
2183
+ });
2184
+ ab.custom({
2185
+ title: '@general:entity-form.done.title',
2186
+ command: { name: ENTITY_FORM_ACTION_DONE },
2187
+ color: 'primary',
2188
+ position: 'suffix',
2189
+ hidden: '{{!api.getStatus().isLast}}',
2190
+ predicateApiWidgetName: wizardName,
2191
+ });
2192
+ }
1594
2193
  function getGroupTitle(entity, groupId) {
1595
2194
  const g = (entity.groups || []).find((x) => x.id === groupId);
1596
2195
  return g?.title;
@@ -1779,6 +2378,11 @@ var openEntityDetails_command = /*#__PURE__*/Object.freeze({
1779
2378
  AXPOpenEntityDetailsCommand: AXPOpenEntityDetailsCommand
1780
2379
  });
1781
2380
 
2381
+ /** Matches persisted PK checks used for choose update vs create (strict types only). */
2382
+ function hasPersistedRootId(context) {
2383
+ const raw = context?.id ?? context?._id;
2384
+ return (typeof raw === 'string' && raw.length > 0) || (typeof raw === 'number' && !Number.isNaN(raw));
2385
+ }
1782
2386
  class AXPCreateEntityCommand {
1783
2387
  constructor() {
1784
2388
  this.entityForm = inject(AXPEntityFormBuilderService);
@@ -1812,7 +2416,6 @@ class AXPCreateEntityCommand {
1812
2416
  const entityRef = await this.entityService.resolve(moduleName, entityName);
1813
2417
  let chain = this.entityForm.entity(`${moduleName}.${entityName}`).create(data);
1814
2418
  chain.actions((actions) => {
1815
- actions.cancel('@general:actions.cancel.title');
1816
2419
  actions.submit('@general:actions.create.title');
1817
2420
  });
1818
2421
  if (excludeProperties && excludeProperties.length > 0) {
@@ -1837,25 +2440,31 @@ class AXPCreateEntityCommand {
1837
2440
  }
1838
2441
  const result = await chain
1839
2442
  .onAction(async (dialogRef) => {
1840
- if (dialogRef.action() === 'cancel') {
1841
- return { success: false };
1842
- }
1843
2443
  const createFn = entityRef.commands?.create?.execute;
1844
- if (!createFn) {
1845
- const msg = await this.translationService.translateAsync('@general:messages.entity.create-command-unavailable');
2444
+ const updateFn = entityRef.commands?.update?.execute;
2445
+ const context = dialogRef.context();
2446
+ console.log('context', context);
2447
+ const hasPersistedId = hasPersistedRootId(context);
2448
+ const persistFn = hasPersistedId ? updateFn : createFn;
2449
+ const missingPersistHandlerMsg = hasPersistedId
2450
+ ? await this.translationService.translateAsync('@general:messages.entity.update-command-unavailable')
2451
+ : await this.translationService.translateAsync('@general:messages.entity.create-command-unavailable');
2452
+ const failedMsgKey = hasPersistedId
2453
+ ? '@general:messages.entity.update-failed'
2454
+ : '@general:messages.entity.create-failed';
2455
+ if (!persistFn) {
1846
2456
  if (enableOperationToasts) {
1847
2457
  this.toastService.show({
1848
2458
  color: 'danger',
1849
2459
  title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
1850
- content: msg,
2460
+ content: missingPersistHandlerMsg,
1851
2461
  });
1852
2462
  }
1853
- throw new Error(msg);
2463
+ throw new Error(missingPersistHandlerMsg);
1854
2464
  }
1855
2465
  dialogRef.setLoading(true);
1856
2466
  try {
1857
- const context = dialogRef.context();
1858
- const result = await createFn(context);
2467
+ const result = await persistFn(context);
1859
2468
  if (result) {
1860
2469
  return {
1861
2470
  success: true,
@@ -1869,13 +2478,13 @@ class AXPCreateEntityCommand {
1869
2478
  return {
1870
2479
  success: false,
1871
2480
  message: {
1872
- text: await this.translationService.translateAsync('@general:messages.entity.create-failed'),
2481
+ text: await this.translationService.translateAsync(failedMsgKey),
1873
2482
  },
1874
2483
  };
1875
2484
  }
1876
2485
  }
1877
2486
  catch (e) {
1878
- const errorMsg = e.message ?? (await this.translationService.translateAsync('@general:messages.entity.create-failed'));
2487
+ const errorMsg = e.message ?? (await this.translationService.translateAsync(failedMsgKey));
1879
2488
  if (enableOperationToasts) {
1880
2489
  this.toastService.show({
1881
2490
  color: 'danger',
@@ -2013,9 +2622,7 @@ class AXPUpdateEntityCommand {
2013
2622
  };
2014
2623
  }
2015
2624
  const entityRef = await this.entityService.resolve(moduleName, entityName);
2016
- let dialogRef;
2017
2625
  try {
2018
- // Validate data type
2019
2626
  if (!data || (typeof data !== 'string' && typeof data !== 'object')) {
2020
2627
  return {
2021
2628
  success: false,
@@ -2024,10 +2631,8 @@ class AXPUpdateEntityCommand {
2024
2631
  },
2025
2632
  };
2026
2633
  }
2027
- // Pass data directly to update() - it handles string | object internally
2028
2634
  let chain = this.entityForm.entity(`${moduleName}.${entityName}`).update(data);
2029
2635
  chain.actions((actions) => {
2030
- actions.cancel('@general:actions.cancel.title');
2031
2636
  actions.submit('@general:actions.apply.title');
2032
2637
  });
2033
2638
  if (excludeProperties && excludeProperties.length > 0) {
@@ -2048,19 +2653,8 @@ class AXPUpdateEntityCommand {
2048
2653
  if (finalSize) {
2049
2654
  chain.size(finalSize);
2050
2655
  }
2051
- dialogRef = await chain.show();
2052
- if (dialogRef.action() === 'cancel') {
2053
- dialogRef.close();
2054
- return {
2055
- success: false,
2056
- // message: {
2057
- // text: await this.translationService.translateAsync('@general:messages.generic.cancel.description'),
2058
- // },
2059
- };
2060
- }
2061
- else if (dialogRef.action() === 'submit') {
2062
- dialogRef.setLoading(true);
2063
- const context = dialogRef.context();
2656
+ return (await chain
2657
+ .onAction(async (dialogRef) => {
2064
2658
  const updateFn = entityRef.commands?.update?.execute;
2065
2659
  if (!updateFn) {
2066
2660
  const msg = await this.translationService.translateAsync('@general:messages.entity.update-command-unavailable');
@@ -2071,35 +2665,44 @@ class AXPUpdateEntityCommand {
2071
2665
  content: msg,
2072
2666
  });
2073
2667
  }
2074
- return {
2075
- success: false,
2076
- message: { text: msg },
2077
- };
2668
+ throw new Error(msg);
2078
2669
  }
2079
- const result = await updateFn(context);
2080
- if (result) {
2081
- dialogRef.close();
2670
+ dialogRef.setLoading(true);
2671
+ try {
2672
+ const context = dialogRef.context();
2673
+ const result = await updateFn(context);
2674
+ if (result) {
2675
+ return {
2676
+ success: true,
2677
+ data: result.data ?? result,
2678
+ message: {
2679
+ text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
2680
+ },
2681
+ };
2682
+ }
2082
2683
  return {
2083
- success: true,
2084
- data: result,
2684
+ success: false,
2085
2685
  message: {
2086
- text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
2686
+ text: await this.translationService.translateAsync('@general:messages.entity.command-no-result'),
2087
2687
  },
2088
2688
  };
2089
2689
  }
2090
- return (result ?? {
2091
- success: false,
2092
- message: {
2093
- text: await this.translationService.translateAsync('@general:messages.entity.command-no-result'),
2094
- },
2095
- });
2096
- }
2097
- return {
2098
- success: false,
2099
- message: {
2100
- text: await this.translationService.translateAsync('@general:messages.entity.invalid-action'),
2101
- },
2102
- };
2690
+ catch (e) {
2691
+ const errorMsg = e.message ?? (await this.translationService.translateAsync('@general:messages.entity.update-failed'));
2692
+ if (enableOperationToasts) {
2693
+ this.toastService.show({
2694
+ color: 'danger',
2695
+ title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
2696
+ content: errorMsg,
2697
+ });
2698
+ }
2699
+ throw e;
2700
+ }
2701
+ finally {
2702
+ dialogRef.setLoading(false);
2703
+ }
2704
+ })
2705
+ .show());
2103
2706
  }
2104
2707
  catch (error) {
2105
2708
  const text = error instanceof Error
@@ -2117,11 +2720,6 @@ class AXPUpdateEntityCommand {
2117
2720
  message: { text },
2118
2721
  };
2119
2722
  }
2120
- finally {
2121
- if (dialogRef) {
2122
- dialogRef.setLoading(false);
2123
- }
2124
- }
2125
2723
  }
2126
2724
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPUpdateEntityCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2127
2725
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPUpdateEntityCommand, providedIn: 'root' }); }
@@ -3326,82 +3924,6 @@ class AXPEntityListViewColumnViewModel {
3326
3924
  }
3327
3925
  }
3328
3926
 
3329
- //#endregion
3330
- //#region ---- Helpers ----
3331
- function isRelatedEntityStringColumns(columns) {
3332
- return columns.length === 0 || typeof columns[0] === 'string';
3333
- }
3334
- /**
3335
- * Property names from `AXPRelatedEntity.columns` (no expression evaluation).
3336
- * Empty array means caller can treat as “no name filter” (show all allowed by entity).
3337
- */
3338
- function getRelatedEntityColumnNames(columns) {
3339
- if (!columns?.length) {
3340
- return [];
3341
- }
3342
- if (isRelatedEntityStringColumns(columns)) {
3343
- return [...columns];
3344
- }
3345
- return columns.map((c) => c.name);
3346
- }
3347
- function isTruthyVisible(value) {
3348
- if (value === true) {
3349
- return true;
3350
- }
3351
- if (value === false || value === null || value === undefined) {
3352
- return false;
3353
- }
3354
- if (typeof value === 'string') {
3355
- const t = value.trim().toLowerCase();
3356
- return t !== '' && t !== 'false' && t !== '0';
3357
- }
3358
- if (typeof value === 'number') {
3359
- return value !== 0 && !Number.isNaN(value);
3360
- }
3361
- return Boolean(value);
3362
- }
3363
- //#endregion
3364
- //#region ---- Public API ----
3365
- /**
3366
- * Resolves `AXPRelatedEntity.columns` for `entity-list`:
3367
- * - `string[]` → `includeColumns` only (legacy).
3368
- * - `AXPEntityTableColumn[]` → evaluates `options.visible` when it is a string (parent detail context),
3369
- * drops hidden / invisible columns, and returns `relatedTableColumns` + matching `includeColumns`.
3370
- */
3371
- async function resolveRelatedEntityColumns(columns, expressionEvaluator, scope) {
3372
- if (!columns?.length) {
3373
- return {};
3374
- }
3375
- if (isRelatedEntityStringColumns(columns)) {
3376
- return { includeColumns: getRelatedEntityColumnNames(columns) };
3377
- }
3378
- const relatedTableColumns = [];
3379
- for (const raw of columns) {
3380
- const col = cloneDeep(raw);
3381
- if (col.hidden === true) {
3382
- continue;
3383
- }
3384
- let visible = col.options?.visible;
3385
- if (visible === undefined) {
3386
- visible = true;
3387
- }
3388
- else if (typeof visible === 'string') {
3389
- const evaluated = await expressionEvaluator.evaluate(visible, scope);
3390
- visible = isTruthyVisible(evaluated);
3391
- }
3392
- if (visible === false) {
3393
- continue;
3394
- }
3395
- col.options = { ...col.options, visible: true };
3396
- relatedTableColumns.push(col);
3397
- }
3398
- return {
3399
- relatedTableColumns,
3400
- includeColumns: relatedTableColumns.map((c) => c.name),
3401
- };
3402
- }
3403
- //#endregion
3404
-
3405
3927
  class AXPEntityDetailListViewModel {
3406
3928
  constructor(injector, detailEntityConfig, parent) {
3407
3929
  this.injector = injector;
@@ -4302,7 +4824,6 @@ class AXPEntityMasterListViewModel {
4302
4824
  width: c.width,
4303
4825
  }));
4304
4826
  set(newSettings, `list.views.${this.view().name}.columns`, updatedColumns);
4305
- console.log(newSettings, `list.views.${this.view().name}.columns`);
4306
4827
  return newSettings;
4307
4828
  });
4308
4829
  break;
@@ -10441,6 +10962,42 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
10441
10962
  }));
10442
10963
  return actions;
10443
10964
  }, ...(ngDevMode ? [{ debugName: "secondaryActions" }] : /* istanbul ignore next */ []));
10965
+ /** Keeps related-list filters in sync with dialog context for create wizards (main id appears after first-step save). */
10966
+ this.#relatedFilterSyncEffect = effect(() => {
10967
+ const opts = this.options();
10968
+ if (!opts['syncRelatedListFiltersFromDialogContext']) {
10969
+ return;
10970
+ }
10971
+ const specs = opts['relatedFilterConditionSpecs'];
10972
+ if (!specs?.length) {
10973
+ return;
10974
+ }
10975
+ this.contextService.data();
10976
+ if (!this.fullPath()) {
10977
+ return;
10978
+ }
10979
+ untracked(() => void this.applyRelatedFiltersFromContextAndDatasource(specs));
10980
+ }, ...(ngDevMode ? [{ debugName: "#relatedFilterSyncEffect" }] : /* istanbul ignore next */ []));
10981
+ /** Patches data-list `refresh` so the grid footer / toolbar refresh keeps parent-scoped filters on the data source. */
10982
+ this.#patchDataListRefreshEffect = effect(() => {
10983
+ const inst = this.listWidget()?.instance;
10984
+ const opts = this.options();
10985
+ if (!inst?.refresh || inst.__axpEntityListRefreshPatched || !opts['syncRelatedListFiltersFromDialogContext']) {
10986
+ return;
10987
+ }
10988
+ inst.__axpEntityListRefreshPatched = true;
10989
+ const originalRefresh = inst.refresh.bind(inst);
10990
+ inst.refresh = () => {
10991
+ void (async () => {
10992
+ const o = this.options();
10993
+ const specs = o['relatedFilterConditionSpecs'];
10994
+ if (o['syncRelatedListFiltersFromDialogContext'] && specs?.length) {
10995
+ await this.applyRelatedFiltersFromContextAndDatasource(specs);
10996
+ }
10997
+ originalRefresh();
10998
+ })();
10999
+ };
11000
+ }, ...(ngDevMode ? [{ debugName: "#patchDataListRefreshEffect" }] : /* istanbul ignore next */ []));
10444
11001
  //#region ---- Query Change Handler ----
10445
11002
  this.queries = undefined;
10446
11003
  this.#effect = effect(() => {
@@ -10491,12 +11048,32 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
10491
11048
  const commandData = action?.scope == AXPEntityCommandScope.Selected
10492
11049
  ? this.selectedItems()
10493
11050
  : action?.options?.['process']?.data || null;
10494
- const evaluatedToolbarOptions = await this.evaluateToolbarExpressions(action?.options, commandData);
10495
- const relatedExcludes = this.options()['excludeProperties'];
10496
- const exclusions = relatedExcludes?.filter(Boolean);
10497
- const options = exclusions?.length && (command === 'Entity:Create' || command === 'Entity:Update')
10498
- ? { ...evaluatedToolbarOptions, excludeProperties: exclusions }
10499
- : evaluatedToolbarOptions;
11051
+ let options = await this.evaluateToolbarExpressions(action?.options, commandData);
11052
+ const listOpts = this.options();
11053
+ const foreignKeyFieldName = listOpts['foreignKeyField'];
11054
+ const createExcludes = listOpts['nestedCreateHiddenProperties'] ?? [];
11055
+ if (command === 'Entity:Create' && foreignKeyFieldName) {
11056
+ const root = this.contextService.snapshot();
11057
+ const idVal = get(root, 'id');
11058
+ options = merge({}, options);
11059
+ options['process'] = merge({}, options['process'], {
11060
+ data: merge({}, options['process']?.data ?? {}, {
11061
+ ...(idVal !== undefined && idVal !== null && idVal !== '' ? { [foreignKeyFieldName]: idVal } : {}),
11062
+ }),
11063
+ });
11064
+ }
11065
+ if (command === 'Entity:Create' && createExcludes.length) {
11066
+ const existing = (options['excludeProperties'] ?? []).slice();
11067
+ options = merge({}, options);
11068
+ options['excludeProperties'] = [...new Set([...createExcludes, ...existing])];
11069
+ }
11070
+ const relatedExcludes = listOpts['excludeProperties'];
11071
+ const exclusionsFromList = relatedExcludes?.filter(Boolean) ?? [];
11072
+ if (exclusionsFromList.length && (command === 'Entity:Create' || command === 'Entity:Update')) {
11073
+ const existing = (options['excludeProperties'] ?? []).slice();
11074
+ options = merge({}, options);
11075
+ options['excludeProperties'] = [...new Set([...existing, ...exclusionsFromList])];
11076
+ }
10500
11077
  if (this.commandService.exists(command)) {
10501
11078
  await this.commandService.execute(command, {
10502
11079
  __context__: {
@@ -10536,13 +11113,88 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
10536
11113
  if (!opts) {
10537
11114
  return {};
10538
11115
  }
11116
+ const root = this.contextService.snapshot();
10539
11117
  const scope = {
10540
11118
  context: {
10541
- eval: (path) => get(expressionData, path),
11119
+ eval: (path) => get(root, path) ?? get(expressionData, path),
10542
11120
  },
10543
11121
  };
10544
11122
  return (await this.expressionEvaluator.evaluate(opts, scope));
10545
11123
  }
11124
+ /**
11125
+ * Re-evaluates related-entity list filters from the live dialog form context (e.g. after create saves the main row id).
11126
+ */
11127
+ async applyRelatedFiltersFromContext(specs) {
11128
+ const root = this.contextService.snapshot();
11129
+ const scope = {
11130
+ context: {
11131
+ eval: (path) => get(root, path),
11132
+ },
11133
+ };
11134
+ const filters = await Promise.all(specs.map(async (c) => ({
11135
+ field: c.name,
11136
+ operator: c.operator,
11137
+ value: await this.expressionEvaluator.evaluate(c.value, scope),
11138
+ hidden: true,
11139
+ })));
11140
+ if (!this.fullPath()) {
11141
+ return;
11142
+ }
11143
+ const current = this.getValue();
11144
+ if (isEqual$1(current?.toolbar?.filters, filters)) {
11145
+ return;
11146
+ }
11147
+ this.setValue({
11148
+ ...current,
11149
+ toolbar: { ...(current?.toolbar ?? {}), filters },
11150
+ });
11151
+ }
11152
+ /** Keeps related-list filters in sync with dialog context for create wizards (main id appears after first-step save). */
11153
+ #relatedFilterSyncEffect;
11154
+ /**
11155
+ * Pushes current toolbar filters to the embedded list data source when both exist.
11156
+ * Returns false while data-list is still mounting (see deferred listNode.set in ngOnInit).
11157
+ */
11158
+ pushToolbarFiltersToDataSource() {
11159
+ const listInstance = this.listWidget()?.instance;
11160
+ const dataSource = listInstance?.options?.()?.['dataSource'];
11161
+ const toolbar = this.getValue()?.toolbar;
11162
+ if (!dataSource?.filter || !toolbar?.filters?.length) {
11163
+ return false;
11164
+ }
11165
+ dataSource.filter({ filters: toolbar.filters });
11166
+ return true;
11167
+ }
11168
+ /**
11169
+ * Writes toolbar filters from specs and pushes them onto the data source so refresh/reload keeps the parent scope.
11170
+ */
11171
+ async applyRelatedFiltersFromContextAndDatasource(specs) {
11172
+ await this.applyRelatedFiltersFromContext(specs);
11173
+ if (this.pushToolbarFiltersToDataSource()) {
11174
+ return;
11175
+ }
11176
+ const opts = this.options();
11177
+ if (!opts['syncRelatedListFiltersFromDialogContext']) {
11178
+ return;
11179
+ }
11180
+ /** Data-list is created in ngOnInit via deferred listNode.set; retry briefly until instance exposes dataSource. */
11181
+ const maxAttempts = 40;
11182
+ const delayMs = 50;
11183
+ for (let i = 0; i < maxAttempts; i++) {
11184
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
11185
+ if (this.pushToolbarFiltersToDataSource()) {
11186
+ return;
11187
+ }
11188
+ }
11189
+ }
11190
+ /**
11191
+ * Refreshes the embedded data list (toolbar / workflow). In wizard mode, `refresh` is patched to re-apply scoped filters first.
11192
+ */
11193
+ refreshGridWithParentScopedFilters() {
11194
+ this.listWidget()?.instance?.call('refresh');
11195
+ }
11196
+ /** Patches data-list `refresh` so the grid footer / toolbar refresh keeps parent-scoped filters on the data source. */
11197
+ #patchDataListRefreshEffect;
10546
11198
  #effect;
10547
11199
  /**
10548
11200
  * Validates that all required dependencies are available
@@ -10669,6 +11321,9 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
10669
11321
  columns: this.getValue()?.toolbar?.columns,
10670
11322
  },
10671
11323
  });
11324
+ queueMicrotask(() => {
11325
+ void this.pushToolbarFiltersToDataSource();
11326
+ });
10672
11327
  }, 100);
10673
11328
  }
10674
11329
  async ngAfterViewInit() {
@@ -10677,7 +11332,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
10677
11332
  .pipe(takeUntil(this.destroyed))
10678
11333
  .subscribe((event) => {
10679
11334
  if (event.payload.entity == this.entitySource()) {
10680
- this.listWidget()?.instance.call('refresh');
11335
+ this.refreshGridWithParentScopedFilters();
10681
11336
  }
10682
11337
  });
10683
11338
  this.eventService
@@ -10685,7 +11340,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
10685
11340
  .pipe(takeUntil(this.destroyed))
10686
11341
  .subscribe((e) => {
10687
11342
  if (e.data.name == `${this.entity()?.module}.${this.entity()?.name}`) {
10688
- this.listWidget()?.instance.call('refresh');
11343
+ this.refreshGridWithParentScopedFilters();
10689
11344
  }
10690
11345
  });
10691
11346
  const listWidget = (await this.layoutService.waitForWidget(`${this.entitySource()}-tab-list_table`, 500));
@@ -12262,7 +12917,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
12262
12917
  const prevValue = this.previousValue();
12263
12918
  const isInitialized = this.initialized();
12264
12919
  // Check if value has actually changed
12265
- const valueChanged = !isEqual(currentValue, prevValue);
12920
+ const valueChanged = !isEqual$1(currentValue, prevValue);
12266
12921
  // Determine if we should update:
12267
12922
  // 1. First initialization with a value
12268
12923
  // 2. Value becomes empty after initialization
@@ -12413,67 +13068,53 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
12413
13068
  }
12414
13069
  }
12415
13070
  /**
12416
- * Update expose data for empty state
13071
+ * Writes expose targets into context using per-path updates.
13072
+ * Avoids `contextService.patch()` with nested objects: patch shallow-merges top-level keys only,
13073
+ * so e.g. `{ person: { educationLevel: { id, title } } }` would replace the entire `person`
13074
+ * object and drop sibling fields like `person.educationLevelId`, causing a value/effect loop.
12417
13075
  */
12418
13076
  expoesItems() {
12419
13077
  const exposeValue = castArray(this.expose());
12420
- const itemToExpose = {};
12421
13078
  const items = this.selectedItems();
12422
13079
  const isEmpty = !items || items.length === 0;
12423
- // If items are empty, group expose configs by parent path and set parent to null
12424
13080
  if (isEmpty) {
12425
13081
  const parentPaths = new Set();
12426
13082
  exposeValue.forEach((i) => {
12427
13083
  if (typeof i === 'string') {
12428
- // For string expose, the path itself is the target
12429
13084
  const pathParts = i.split('.');
12430
13085
  if (pathParts.length > 1) {
12431
- // Extract parent path (everything except the last part)
12432
- const parentPath = pathParts.slice(0, -1).join('.');
12433
- parentPaths.add(parentPath);
13086
+ parentPaths.add(pathParts.slice(0, -1).join('.'));
12434
13087
  }
12435
13088
  else {
12436
- // Single level path, set directly to null
12437
- setSmart(itemToExpose, i, null);
13089
+ this.contextService.update(i, null);
12438
13090
  }
12439
13091
  }
12440
13092
  else {
12441
- // For object expose, extract parent path from target
12442
13093
  const pathParts = i.target.split('.');
12443
- if (pathParts.length > 1) {
12444
- // Extract parent path (everything except the last part)
12445
- const parentPath = pathParts.slice(0, -1).join('.');
12446
- parentPaths.add(parentPath);
12447
- }
12448
- else {
12449
- // Single level path, set directly to null
12450
- setSmart(itemToExpose, i.target, null);
12451
- }
12452
- }
12453
- });
12454
- // Set all parent paths to null
12455
- parentPaths.forEach((parentPath) => {
12456
- setSmart(itemToExpose, parentPath, null);
12457
- });
12458
- }
12459
- else {
12460
- // Normal processing when items exist
12461
- exposeValue.forEach((i) => {
12462
- if (typeof i == 'string') {
12463
- const values = items.map((item) => set({}, i, get(item, i)));
12464
- setSmart(itemToExpose, i, this.singleOrMultiple(values));
12465
- }
12466
- else {
12467
- // extract data from item by source path and set context by target path
12468
- const values = this.multiple()
12469
- ? items.map((item) => set({}, i.source, get(item, i.source)))
12470
- : items.map((item) => get(item, i.source));
12471
- setSmart(itemToExpose, i.target, this.singleOrMultiple(values));
13094
+ if (pathParts.length > 1) {
13095
+ parentPaths.add(pathParts.slice(0, -1).join('.'));
13096
+ }
13097
+ else {
13098
+ this.contextService.update(i.target, null);
13099
+ }
12472
13100
  }
12473
13101
  });
13102
+ parentPaths.forEach((parentPath) => {
13103
+ this.contextService.update(parentPath, null);
13104
+ });
13105
+ return;
12474
13106
  }
12475
- setTimeout(() => {
12476
- this.contextService.patch(itemToExpose, true);
13107
+ exposeValue.forEach((i) => {
13108
+ if (typeof i === 'string') {
13109
+ const values = items.map((item) => set({}, i, get(item, i)));
13110
+ this.contextService.update(i, this.singleOrMultiple(values));
13111
+ }
13112
+ else {
13113
+ const values = this.multiple()
13114
+ ? items.map((item) => set({}, i.source, get(item, i.source)))
13115
+ : items.map((item) => get(item, i.source));
13116
+ this.contextService.update(i.target, this.singleOrMultiple(values));
13117
+ }
12477
13118
  });
12478
13119
  }
12479
13120
  singleOrMultiple(values) {
@@ -16676,16 +17317,34 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
16676
17317
  evaluatedOptions = action.options;
16677
17318
  }
16678
17319
  }
16679
- const actionData = action.scope == AXPEntityCommandScope.Selected
17320
+ let actionData = action.scope == AXPEntityCommandScope.Selected
16680
17321
  ? executeContext
16681
17322
  : evaluatedOptions?.['process']?.data || null;
16682
- //TODO: This is a temporary solution to exclude properties from the related entity.
16683
- // We need to find a better way to handle this.
16684
- const excludeForCreateOrUpdate = (commandName === 'Entity:Create' || commandName === 'Entity:Update') &&
16685
- relatedEntity.excludeProperties?.length;
16686
- const mergedOptions = excludeForCreateOrUpdate
16687
- ? { ...evaluatedOptions, excludeProperties: relatedEntity.excludeProperties }
16688
- : evaluatedOptions;
17323
+ const foreignKeyField = relatedEntity.persistence?.foreignKeyField;
17324
+ if (commandName === 'Entity:Create' && foreignKeyField && context.context) {
17325
+ const parentId = get(context.context, 'id');
17326
+ actionData = {
17327
+ ...(typeof actionData === 'object' && actionData !== null ? actionData : {}),
17328
+ ...(parentId !== undefined && parentId !== null && parentId !== ''
17329
+ ? { [foreignKeyField]: parentId }
17330
+ : {}),
17331
+ };
17332
+ }
17333
+ const createExcludes = [
17334
+ ...new Set([
17335
+ ...(relatedEntity.excludeProperties ?? []),
17336
+ ...(commandName === 'Entity:Create' && foreignKeyField ? [foreignKeyField] : []),
17337
+ ]),
17338
+ ];
17339
+ const excludeForCreate = commandName === 'Entity:Create' && createExcludes.length > 0;
17340
+ const excludeForUpdate = commandName === 'Entity:Update' &&
17341
+ relatedEntity.excludeProperties &&
17342
+ relatedEntity.excludeProperties.length > 0;
17343
+ const mergedOptions = excludeForCreate
17344
+ ? { ...evaluatedOptions, excludeProperties: createExcludes }
17345
+ : excludeForUpdate
17346
+ ? { ...evaluatedOptions, excludeProperties: relatedEntity.excludeProperties }
17347
+ : evaluatedOptions;
16689
17348
  if (context.commandService.exists(commandName)) {
16690
17349
  // check options for evaluation
16691
17350
  await context.commandService.execute(commandName, {
@@ -18443,6 +19102,7 @@ const AXPCrudModifier = {
18443
19102
  if (!command?.create) {
18444
19103
  command.create = {
18445
19104
  execute: async (data) => {
19105
+ console.log('create', ctx.module.get() + '.' + ctx.name.get(), data);
18446
19106
  const res = await dataService.insertOne(data);
18447
19107
  return { id: res };
18448
19108
  },
@@ -18458,6 +19118,7 @@ const AXPCrudModifier = {
18458
19118
  if (!command?.update) {
18459
19119
  command.update = {
18460
19120
  execute: async (data) => {
19121
+ console.log('update', ctx.module.get() + '.' + ctx.name.get(), data);
18461
19122
  return await dataService.updateOne(data.id, data);
18462
19123
  },
18463
19124
  };
@@ -18472,7 +19133,6 @@ const AXPCrudModifier = {
18472
19133
  queries.byKey = {
18473
19134
  execute: async (id) => {
18474
19135
  const data = await dataService.getOne(id);
18475
- // debugger;
18476
19136
  return data;
18477
19137
  },
18478
19138
  type: AXPEntityQueryType.Single,
@@ -18481,6 +19141,7 @@ const AXPCrudModifier = {
18481
19141
  if (!queries?.list) {
18482
19142
  queries.list = {
18483
19143
  execute: async (e) => {
19144
+ console.log('query', ctx.module.get() + '.' + ctx.name.get(), e);
18484
19145
  return await dataService.query(e);
18485
19146
  },
18486
19147
  type: AXPEntityQueryType.List,
@@ -19076,10 +19737,34 @@ class AXPShowListViewAction extends AXPWorkflowAction {
19076
19737
  this.sessionService = inject(AXPSessionService);
19077
19738
  }
19078
19739
  async execute(context) {
19079
- const [moduleName, entityName] = context.getVariable('entity').split(".");
19740
+ const entity = context.getVariable('entity');
19741
+ const [moduleName, entityName] = entity.split('.');
19080
19742
  const newPayload = {
19081
19743
  commands: `/${this.sessionService.application?.name}/m/${moduleName}/e/${entityName}/list`,
19082
19744
  };
19745
+ const conditions = context.getVariable('conditions');
19746
+ if (Array.isArray(conditions) && conditions.length > 0) {
19747
+ const filterQueries = conditions
19748
+ .map((c) => {
19749
+ const field = c.name ?? c.field;
19750
+ if (!field || c.value === undefined || c.value === null || c.value === '') {
19751
+ return null;
19752
+ }
19753
+ return {
19754
+ field,
19755
+ operator: c.operator ?? { type: 'equal' },
19756
+ value: c.value,
19757
+ };
19758
+ })
19759
+ .filter((entry) => entry !== null);
19760
+ if (filterQueries.length > 0) {
19761
+ newPayload.extras = {
19762
+ queryParams: {
19763
+ filters: JSON.stringify(filterQueries),
19764
+ },
19765
+ };
19766
+ }
19767
+ }
19083
19768
  context.setVariable('payload', newPayload);
19084
19769
  this.navigation.execute(context);
19085
19770
  }
@@ -19646,244 +20331,9 @@ var getEntityDetails_query = /*#__PURE__*/Object.freeze({
19646
20331
  AXPGetEntityDetailsQuery: AXPGetEntityDetailsQuery
19647
20332
  });
19648
20333
 
19649
- // #region Master
19650
- function entityMasterCreateAction() {
19651
- return {
19652
- title: '@general:actions.create.title',
19653
- command: {
19654
- name: 'Entity:Create',
19655
- },
19656
- priority: 'primary',
19657
- type: AXPSystemActionType.Create,
19658
- scope: AXPEntityCommandScope.TypeLevel,
19659
- };
19660
- }
19661
- function entityMasterEditAction() {
19662
- return {
19663
- title: '@general:actions.edit.title',
19664
- command: 'Entity:Update',
19665
- priority: 'secondary',
19666
- type: AXPSystemActionType.Update,
19667
- scope: AXPEntityCommandScope.Individual,
19668
- default: true,
19669
- };
19670
- }
19671
- function entityMasterBulkDeleteAction() {
19672
- return {
19673
- title: '@general:actions.delete-items.title',
19674
- command: 'delete-entity',
19675
- priority: 'primary',
19676
- type: AXPSystemActionType.Delete,
19677
- scope: AXPEntityCommandScope.Selected,
19678
- order: 100,
19679
- };
19680
- }
19681
- function entityMasterViewAction() {
19682
- return {
19683
- title: '@general:actions.view.title',
19684
- command: 'open-entity',
19685
- priority: 'secondary',
19686
- type: AXPSystemActionType.View,
19687
- scope: AXPEntityCommandScope.Individual,
19688
- default: true,
19689
- };
19690
- }
19691
- function entityMasterDeleteAction() {
19692
- return {
19693
- title: '@general:actions.delete.title',
19694
- command: 'delete-entity',
19695
- priority: 'secondary',
19696
- type: AXPSystemActionType.Delete,
19697
- scope: AXPEntityCommandScope.Individual,
19698
- order: 100,
19699
- };
19700
- }
19701
- function entityMasterCrudActions(options) {
19702
- const opts = {
19703
- create: true,
19704
- delete: true,
19705
- view: true,
19706
- edit: false,
19707
- ...options,
19708
- };
19709
- const actions = [];
19710
- if (opts.create) {
19711
- actions.push(entityMasterCreateAction());
19712
- }
19713
- if (opts.delete) {
19714
- actions.push(entityMasterBulkDeleteAction());
19715
- actions.push(entityMasterDeleteAction());
19716
- }
19717
- if (opts.view) {
19718
- actions.push(entityMasterViewAction());
19719
- }
19720
- if (opts.edit) {
19721
- actions.push(entityMasterEditAction());
19722
- }
19723
- return actions;
19724
- }
19725
- function entityMasterRecordActions() {
19726
- return [entityMasterDeleteAction()];
19727
- }
19728
- // #endregion
19729
- // #region Details
19730
- function entityDetailsCreateActions(parentId) {
19731
- return {
19732
- title: '@general:actions.create.title',
19733
- command: {
19734
- name: 'Entity:Create',
19735
- options: {
19736
- process: {
19737
- redirect: false,
19738
- canCreateNewOne: true,
19739
- data: {
19740
- [parentId]: '{{context.eval("id")}}',
19741
- },
19742
- },
19743
- },
19744
- },
19745
- priority: 'primary',
19746
- type: AXPSystemActionType.Create,
19747
- scope: AXPEntityCommandScope.TypeLevel,
19748
- };
19749
- }
19750
- function entityDetailsSimpleCondition(fk) {
19751
- return {
19752
- name: fk,
19753
- operator: { type: 'equal' },
19754
- value: '{{context.eval("id")}}',
19755
- };
19756
- }
19757
- function entityDetailsReferenceCondition(type) {
19758
- return [
19759
- {
19760
- name: 'reference.id',
19761
- operator: { type: 'equal' },
19762
- value: '{{context.eval("id")}}',
19763
- },
19764
- {
19765
- name: 'reference.type',
19766
- operator: { type: 'equal' },
19767
- value: type,
19768
- },
19769
- ];
19770
- }
19771
- function entityDetailsEditAction() {
19772
- return {
19773
- title: '@general:actions.edit.title',
19774
- // command: 'quick-modify-entity',
19775
- command: 'Entity:Update',
19776
- priority: 'secondary',
19777
- type: AXPSystemActionType.Update,
19778
- default: true,
19779
- scope: AXPEntityCommandScope.Individual,
19780
- };
19781
- }
19782
- function entityDetailsNewEditAction() {
19783
- return {
19784
- title: 'New Edit',
19785
- command: {
19786
- name: 'Entity:Update',
19787
- },
19788
- priority: 'secondary',
19789
- type: AXPSystemActionType.Update,
19790
- default: true,
19791
- scope: AXPEntityCommandScope.Individual,
19792
- };
19793
- }
19794
- function entityOverrideDetailsViewAction() {
19795
- return {
19796
- title: '@general:actions.view.title',
19797
- command: 'open-entity',
19798
- priority: 'secondary',
19799
- hidden: true,
19800
- type: AXPSystemActionType.View,
19801
- scope: AXPEntityCommandScope.Individual,
19802
- };
19803
- }
19804
- function entityDetailsCrudActions(parentId, options) {
19805
- const opts = {
19806
- create: true,
19807
- delete: true,
19808
- view: true,
19809
- edit: true,
19810
- ...options,
19811
- };
19812
- const actions = [];
19813
- if (opts.create) {
19814
- actions.push(entityDetailsCreateActions(parentId));
19815
- }
19816
- if (opts.edit) {
19817
- actions.push(entityDetailsEditAction());
19818
- }
19819
- if (opts.view) {
19820
- actions.push(entityOverrideDetailsViewAction());
19821
- }
19822
- return actions;
19823
- }
19824
- function entityDetailsReferenceCreateActions(type) {
19825
- return [
19826
- {
19827
- title: '@general:actions.create.title',
19828
- command: {
19829
- name: 'Entity:Create',
19830
- options: {
19831
- process: {
19832
- redirect: false,
19833
- canCreateNewOne: true,
19834
- data: {
19835
- reference: {
19836
- id: '{{context.eval("id")}}',
19837
- type: type,
19838
- },
19839
- },
19840
- },
19841
- },
19842
- },
19843
- priority: 'primary',
19844
- type: AXPSystemActionType.Create,
19845
- scope: AXPEntityCommandScope.TypeLevel,
19846
- },
19847
- entityDetailsEditAction(),
19848
- entityOverrideDetailsViewAction(),
19849
- ];
19850
- }
19851
- /**
19852
- * Computes a diff between two plain objects with array-aware semantics.
19853
- * - For arrays of objects with an id field, computes added/removed by id.
19854
- * - For arrays of primitives or objects without id, uses deep equality.
19855
- * - For scalars/objects, reports oldValue/newValue when changed.
19856
- */
19857
- function detectEntityChanges(oldObj, newObj) {
19858
- return transform(newObj, (result, value, key) => {
19859
- if (!isEqual$1(value, oldObj[key])) {
19860
- const oldValue = oldObj[key];
19861
- if (Array.isArray(value) || Array.isArray(oldValue)) {
19862
- const oldArray = Array.isArray(oldValue) ? oldValue : [];
19863
- const newArray = Array.isArray(value) ? value : [];
19864
- const hasId = newArray.length > 0 && typeof newArray[0] === 'object' && newArray[0] !== null && 'id' in newArray[0];
19865
- if (hasId) {
19866
- const added = newArray.filter((item) => !oldArray.some((oldItem) => oldItem.id === item.id));
19867
- const removed = oldArray.filter((item) => !newArray.some((newItem) => newItem.id === item.id));
19868
- result[key] = { oldValue, newValue: value, added, removed };
19869
- }
19870
- else {
19871
- const added = newArray.filter((item) => !oldArray.some((oldItem) => isEqual$1(item, oldItem)));
19872
- const removed = oldArray.filter((item) => !newArray.some((newItem) => isEqual$1(item, newItem)));
19873
- result[key] = { oldValue, newValue: value, added, removed };
19874
- }
19875
- }
19876
- else {
19877
- result[key] = { oldValue, newValue: value };
19878
- }
19879
- }
19880
- }, {});
19881
- }
19882
- //#endregion
19883
-
19884
20334
  /**
19885
20335
  * Generated bundle index. Do not edit.
19886
20336
  */
19887
20337
 
19888
- export { AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, AXPGetEntityDetailsQuery, AXPLayoutOrderingConfigService, AXPLookupWidget, AXPLookupWidgetColumnComponent, AXPLookupWidgetEditComponent, AXPLookupWidgetViewComponent, AXPMiddlewareAbortError, AXPMiddlewareEntityStorageService, AXPModifyEntitySectionWorkflow, AXPMultiSourceDefinitionProviderContext, AXPMultiSourceDefinitionProviderService, AXPMultiSourceFederatedSearchService, AXPMultiSourceSelectorComponent, AXPMultiSourceSelectorService, AXPMultiSourceSelectorWidget, AXPMultiSourceSelectorWidgetColumnComponent, AXPMultiSourceSelectorWidgetEditComponent, AXPMultiSourceSelectorWidgetViewComponent, AXPMultiSourceType, AXPOpenEntityDetailsCommand, AXPQuickEntityModifyPopupAction, AXPQuickModifyEntityWorkflow, AXPRelatedColumnEnrichmentService, AXPRelatedColumnMetadataResolver, AXPSelectorStructureWidget, AXPSelectorStructureWidgetColumnComponent, AXPSelectorStructureWidgetEditComponent, AXPSelectorStructureWidgetViewComponent, AXPShowDetailViewAction, AXPShowDetailsViewWorkflow, AXPShowListViewAction, AXPShowListViewWorkflow, AXPTruncatedBreadcrumbComponent, AXPUpdateEntityCommand, AXPViewEntityDetailsCommand, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXP_DATA_SEEDER_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_DEFINITION_LOADER, AXP_ENTITY_MODIFIER, AXP_ENTITY_STORAGE_BACKEND, AXP_ENTITY_STORAGE_MIDDLEWARE, AXP_MULTI_SOURCE_DEFINITION_PROVIDER, DEFAULT_COLUMN_ORDER, DEFAULT_PAIR_SPAN_RULES, DEFAULT_PROPERTY_ORDER, DEFAULT_SECTION_ORDER, EntityBuilder, EntityDataAccessor, actionExists, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, cloneLayoutArrays, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, defaultMultiLanguageMiddleware, defaultMultiLanguageMiddlewareProvider, detectEntityChanges, ensureLayoutPropertyView, ensureLayoutSection, ensureListActions, entityDetailsCreateActions, entityDetailsCrudActions, entityDetailsEditAction, entityDetailsNewEditAction, entityDetailsReferenceCondition, entityDetailsReferenceCreateActions, entityDetailsSimpleCondition, entityMasterBulkDeleteAction, entityMasterCreateAction, entityMasterCrudActions, entityMasterDeleteAction, entityMasterEditAction, entityMasterRecordActions, entityMasterViewAction, entityOverrideDetailsViewAction, eventDispatchMiddleware, getMasterInterfacePropertySortKey, isAXPMiddlewareAbortError, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, provideEntity, resolveEntityPluginDetailPageOrder, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider };
20338
+ export { AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, AXPGetEntityDetailsQuery, AXPLayoutOrderingConfigService, AXPLookupWidget, AXPLookupWidgetColumnComponent, AXPLookupWidgetEditComponent, AXPLookupWidgetViewComponent, AXPMiddlewareAbortError, AXPMiddlewareEntityStorageService, AXPModifyEntitySectionWorkflow, AXPMultiSourceDefinitionProviderContext, AXPMultiSourceDefinitionProviderService, AXPMultiSourceFederatedSearchService, AXPMultiSourceSelectorComponent, AXPMultiSourceSelectorService, AXPMultiSourceSelectorWidget, AXPMultiSourceSelectorWidgetColumnComponent, AXPMultiSourceSelectorWidgetEditComponent, AXPMultiSourceSelectorWidgetViewComponent, AXPMultiSourceType, AXPOpenEntityDetailsCommand, AXPQuickEntityModifyPopupAction, AXPQuickModifyEntityWorkflow, AXPRelatedColumnEnrichmentService, AXPRelatedColumnMetadataResolver, AXPSelectorStructureWidget, AXPSelectorStructureWidgetColumnComponent, AXPSelectorStructureWidgetEditComponent, AXPSelectorStructureWidgetViewComponent, AXPShowDetailViewAction, AXPShowDetailsViewWorkflow, AXPShowListViewAction, AXPShowListViewWorkflow, AXPTruncatedBreadcrumbComponent, AXPUpdateEntityCommand, AXPViewEntityDetailsCommand, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXP_DATA_SEEDER_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_DEFINITION_LOADER, AXP_ENTITY_MODIFIER, AXP_ENTITY_STORAGE_BACKEND, AXP_ENTITY_STORAGE_MIDDLEWARE, AXP_MULTI_SOURCE_DEFINITION_PROVIDER, DEFAULT_COLUMN_ORDER, DEFAULT_PAIR_SPAN_RULES, DEFAULT_PROPERTY_ORDER, DEFAULT_SECTION_ORDER, EntityBuilder, EntityDataAccessor, actionExists, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, cloneLayoutArrays, collectNestedCreateHiddenProperties, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, defaultMultiLanguageMiddleware, defaultMultiLanguageMiddlewareProvider, detectEntityChanges, ensureLayoutPropertyView, ensureLayoutSection, ensureListActions, entityDetailsCreateActions, entityDetailsCreateActionsDeferredParent, entityDetailsCrudActions, entityDetailsEditAction, entityDetailsNewEditAction, entityDetailsReferenceCondition, entityDetailsReferenceCreateActions, entityDetailsSimpleCondition, entityMasterBulkDeleteAction, entityMasterCreateAction, entityMasterCrudActions, entityMasterDeleteAction, entityMasterEditAction, entityMasterRecordActions, entityMasterViewAction, entityOverrideDetailsViewAction, eventDispatchMiddleware, getMasterInterfacePropertySortKey, isAXPMiddlewareAbortError, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, mergeForeignKeyFieldIntoCreateActions, provideEntity, resolveEntityPluginDetailPageOrder, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider };
19889
20339
  //# sourceMappingURL=acorex-platform-layout-entity.mjs.map